iThemes Security (formerly Better WP Security) - Version 7.7.0

Version Description

  • Important: iThemes Security requires PHP 5.6 or greater and WordPress 5.2 or greater.
  • New Feature: Save Time Securing WordPress With User Groups!
  • New Feature: Simplified connection flow when setting up iThemes Sync.
  • Enhancement: Add a warning if a WordPress Salt is set to an invalid value.
  • Enhancement: Include child log items in the logs list table. These are helpful for debugging issues.
  • Enhancement: Improve performance of the logs page on sites with large number of log items.
  • Enhancement: Check tables exist after completing a DB upgrade.
  • Tweak: When logging $_SERVER, only log a snapshot of available properties.
  • Bug Fix: The "Mulisite Tweaks -> Hide Updates" setting prevented auto-updates from running with WP Cron.
  • Bug Fix: Backup event was not added when the WP Cron Scheduler was reset manually.
  • Bug Fix: Admin Notices Popover was not being hidden when clicking outside the Popover on WP 5.3.
  • Bug Fix: New Password Requirements for already created accounts were not enforced until the second login.
  • Bug Fix: Update admin notices styling to be compatible with WordPress 5.4.
  • Bug Fix: Periodically clear expired opaque tokens.
  • Bug Fix: Don't block registration page when "wp-signup.php" is the Hide Backend register slug.
  • Bug Fix: Users with weak passwords would not be forced to change their password if the strong password requirement had been enabled after their password strength was checked.
  • Bug Fix: Remove "get_magic_quotes()" call that existed for backwards compatibility with PHP versions 5.3 and earlier. This function call was causing a warning on PHP 7.4.
  • Bug Fix: Warning when loading the settings page on PHP 7.4.
  • Bug Fix: Warning when loading the debug page on PHP 7.4.
Download this release

Release Info

Developer TimothyBlynJacobs
Plugin Icon 128x128 iThemes Security (formerly Better WP Security)
Version 7.7.0
Comparing to
See all releases

Code changes from version 7.6.1 to 7.7.0

Files changed (310) hide show
  1. better-wp-security.php +17 -3
  2. composer.json +34 -0
  3. core/Contracts/Runnable.php +12 -0
  4. core/Contracts/index.php +1 -0
  5. core/Exception/Exception.php +7 -0
  6. core/Exception/Invalid_Argument_Exception.php +7 -0
  7. core/Exception/Unsatisfied_Module_Dependencies_Exception.php +7 -0
  8. core/Exception/WP_Error.php +36 -0
  9. core/Exception/index.php +1 -0
  10. core/admin-pages/css/style.css +48 -7
  11. core/admin-pages/init.php +4 -2
  12. core/admin-pages/js/form-user-groups.js +103 -0
  13. core/admin-pages/js/lib/index.php +1 -0
  14. core/admin-pages/js/lib/jquery.multiselect.css +148 -0
  15. core/admin-pages/js/lib/jquery.multiselect.js +936 -0
  16. core/admin-pages/js/script.js +0 -1135
  17. core/admin-pages/js/util.js +40 -14
  18. core/admin-pages/logs-list-table.php +5 -48
  19. core/admin-pages/module-settings.php +8 -1
  20. core/admin-pages/page-debug.php +15 -8
  21. core/admin-pages/page-settings.php +37 -17
  22. core/composer.json +31 -0
  23. core/core.php +244 -56
  24. core/history.txt +38 -0
  25. core/img/bb-ad.jpg +0 -0
  26. core/lib.php +234 -19
  27. core/lib/class-itsec-lib-canonical-roles.php +68 -14
  28. core/lib/class-itsec-lib-directory.php +28 -8
  29. core/lib/class-itsec-lib-feature-flags.php +63 -7
  30. core/lib/class-itsec-lib-fingerprinting.php +11 -4
  31. core/lib/class-itsec-lib-login-interstitial.php +3 -1
  32. core/lib/class-itsec-lib-password-requirements.php +11 -3
  33. core/lib/class-itsec-lib-remote-messages.php +59 -21
  34. core/lib/class-itsec-lib-rest.php +85 -0
  35. core/lib/class-itsec-mutex.php +153 -0
  36. core/lib/class-itsec-scheduler.php +11 -0
  37. core/lib/form.php +50 -5
  38. core/lib/lock.php +0 -47
  39. core/lib/log-util.php +47 -16
  40. core/lib/log.php +2 -0
  41. core/lib/login-interstitial/class-itsec-login-interstitial-session.php +42 -1
  42. core/lib/mail-templates/small-code.html +1 -1
  43. core/lib/schema.php +59 -3
  44. core/lib/settings.php +20 -1
  45. core/lib/validator.php +167 -127
  46. core/modules.php +172 -53
  47. core/modules/404-detection/class-itsec-four-oh-four.php +1 -1
  48. core/modules/404-detection/labels.php +5 -0
  49. core/modules/admin-user/labels.php +5 -0
  50. core/modules/away-mode/labels.php +5 -0
  51. core/modules/backup/activate.php +2 -1
  52. core/modules/backup/labels.php +5 -0
  53. core/modules/backup/settings.php +2 -1
  54. core/modules/ban-users/labels.php +5 -0
  55. core/modules/ban-users/lists/user-agents.php +1543 -0
  56. core/modules/brute-force/class-itsec-brute-force.php +1 -1
  57. core/modules/brute-force/labels.php +5 -0
  58. core/modules/content-directory/labels.php +5 -0
  59. core/modules/core/entries/admin-notices/components/admin-bar/index.js +17 -4
  60. core/modules/core/entries/admin-notices/components/admin-bar/style.scss +2 -0
  61. core/modules/core/entries/admin-notices/components/panel/style.scss +2 -2
  62. core/modules/core/entries/admin-notices/components/toolbar/index.js +16 -3
  63. core/modules/core/entries/admin-notices/store/controls.js +1 -1
  64. core/modules/core/labels.php +5 -0
  65. core/modules/core/notices.php +73 -0
  66. core/modules/database-prefix/labels.php +5 -0
  67. core/modules/feature-flags/labels.php +5 -0
  68. core/modules/feature-flags/settings-page.php +19 -6
  69. core/modules/file-change/labels.php +5 -0
  70. core/modules/file-permissions/labels.php +5 -0
  71. core/modules/global/active.php +14 -0
  72. core/modules/global/labels.php +5 -0
  73. core/modules/global/settings-page.php +13 -0
  74. core/modules/global/settings.php +4 -1
  75. core/modules/global/validator.php +14 -0
  76. core/modules/hide-backend/class-itsec-hide-backend.php +25 -14
  77. core/modules/hide-backend/labels.php +5 -0
  78. core/modules/hide-backend/settings.php +1 -0
  79. core/modules/ipcheck/labels.php +5 -0
  80. core/modules/malware/labels.php +5 -0
  81. core/modules/malware/settings-page.php +27 -13
  82. core/modules/multisite-tweaks/class-itsec-multisite-tweaks.php +9 -9
  83. core/modules/multisite-tweaks/labels.php +5 -0
  84. core/modules/multisite-tweaks/settings-page.php +10 -11
  85. core/modules/notification-center/class-notification-center.php +8 -0
  86. core/modules/notification-center/labels.php +5 -0
  87. core/modules/notification-center/settings.php +2 -1
  88. core/modules/password-requirements/class-itsec-password-requirements.php +26 -4
  89. core/modules/password-requirements/labels.php +5 -0
  90. core/modules/password-requirements/settings.php +10 -1
  91. core/modules/password-requirements/validator.php +2 -1
  92. core/modules/privacy/labels.php +5 -0
  93. core/modules/salts/labels.php +5 -0
  94. core/modules/salts/settings-page.php +29 -10
  95. core/modules/salts/utilities.php +45 -25
  96. core/modules/security-check-pro/labels.php +5 -0
  97. core/modules/security-check-pro/utility.php +14 -6
  98. core/modules/security-check/labels.php +5 -0
  99. core/modules/security-check/scanner.php +3 -1
  100. core/modules/ssl/labels.php +5 -0
  101. core/modules/strong-passwords/active.php +3 -1
  102. core/modules/strong-passwords/class-itsec-strong-passwords.php +43 -30
  103. core/modules/strong-passwords/container.php +9 -0
  104. core/modules/strong-passwords/labels.php +5 -0
  105. core/modules/strong-passwords/setup.php +62 -85
  106. core/modules/sync-connect/active.php +6 -0
  107. core/modules/sync-connect/class-itsec-sync-connect-interstitial.php +136 -0
  108. core/modules/sync-connect/class-itsec-sync-connect.php +183 -0
  109. core/modules/sync-connect/css/connect.css +84 -0
  110. core/modules/sync-connect/css/index.php +1 -0
  111. core/modules/sync-connect/img/index.php +1 -0
  112. core/modules/sync-connect/img/sync-logo.svg +98 -0
  113. core/modules/sync-connect/includes/index.php +1 -0
  114. core/modules/sync-connect/includes/upgrader-skin.php +23 -0
  115. core/modules/sync-connect/index.php +1 -0
  116. core/modules/sync-connect/labels.php +5 -0
  117. core/modules/sync-connect/templates/connect-prompt.php +9 -0
  118. core/modules/sync-connect/templates/connect-unauthorized.php +11 -0
  119. core/modules/sync-connect/templates/fallback.php +11 -0
  120. core/modules/sync-connect/templates/index.php +1 -0
  121. core/modules/sync-connect/templates/or.php +3 -0
  122. core/modules/sync-connect/templates/prompt-link.php +9 -0
  123. core/modules/system-tweaks/labels.php +5 -0
  124. core/modules/user-groups/All_Users.php +17 -0
  125. core/modules/user-groups/Everybody_Else.php +38 -0
  126. core/modules/user-groups/Match/Default_Matcher.php +33 -0
  127. core/modules/user-groups/Match/Match_Target.php +66 -0
  128. core/modules/user-groups/Match/Matchable_Not_Found.php +9 -0
  129. core/modules/user-groups/Match/Matcher.php +16 -0
  130. core/modules/user-groups/Match/index.php +1 -0
  131. core/modules/user-groups/Matchable.php +29 -0
  132. core/modules/user-groups/Matchables_Source.php +77 -0
  133. core/modules/user-groups/Module/Module.php +95 -0
  134. core/modules/user-groups/Module/Settings.php +149 -0
  135. core/modules/user-groups/Module/Validator.php +19 -0
  136. core/modules/user-groups/Module/index.php +1 -0
  137. core/modules/user-groups/REST/Matchables.php +133 -0
  138. core/modules/user-groups/REST/REST.php +28 -0
  139. core/modules/user-groups/REST/Settings.php +355 -0
  140. core/modules/user-groups/REST/User_Groups.php +467 -0
  141. core/modules/user-groups/REST/index.php +1 -0
  142. core/modules/user-groups/Repository/DB_Repository.php +139 -0
  143. core/modules/user-groups/Repository/Decorator.php +35 -0
  144. core/modules/user-groups/Repository/Eager_Loading_Decorator.php +68 -0
  145. core/modules/user-groups/Repository/In_Memory_Repository.php +72 -0
  146. core/modules/user-groups/Repository/Object_Caching_Decorator.php +118 -0
  147. core/modules/user-groups/Repository/Repository.php +62 -0
  148. core/modules/user-groups/Repository/User_Group_Not_Found.php +9 -0
  149. core/modules/user-groups/Repository/index.php +1 -0
  150. core/modules/user-groups/Settings/Settings_Proxy.php +46 -0
  151. core/modules/user-groups/Settings/Settings_Registration.php +87 -0
  152. core/modules/user-groups/Settings/Settings_Registry.php +73 -0
  153. core/modules/user-groups/Settings/index.php +1 -0
  154. core/modules/user-groups/Upgrader.php +76 -0
  155. core/modules/user-groups/User_Group.php +436 -0
  156. core/modules/user-groups/container.php +90 -0
  157. core/modules/user-groups/entries/api.js +3 -0
  158. core/modules/user-groups/entries/index.php +1 -0
  159. core/modules/user-groups/entries/settings.js +37 -0
  160. core/modules/user-groups/entries/settings/app.js +26 -0
  161. core/modules/user-groups/entries/settings/components/edit-group-fields/index.js +17 -0
  162. core/modules/user-groups/entries/settings/components/edit-group-fields/index.php +1 -0
  163. core/modules/user-groups/entries/settings/components/edit-group-fields/style.scss +9 -0
  164. core/modules/user-groups/entries/settings/components/group-header/index.js +17 -0
  165. core/modules/user-groups/entries/settings/components/group-header/index.php +1 -0
  166. core/modules/user-groups/entries/settings/components/group-header/multi.js +22 -0
  167. core/modules/user-groups/entries/settings/components/group-header/new.js +29 -0
  168. core/modules/user-groups/entries/settings/components/group-header/single.js +58 -0
  169. core/modules/user-groups/entries/settings/components/group-header/style.scss +20 -0
  170. core/modules/user-groups/entries/settings/components/group-label/index.js +34 -0
  171. core/modules/user-groups/entries/settings/components/group-label/index.php +1 -0
  172. core/modules/user-groups/entries/settings/components/group-label/style.scss +3 -0
  173. core/modules/user-groups/entries/settings/components/group-nav/index.js +111 -0
  174. core/modules/user-groups/entries/settings/components/group-nav/index.php +1 -0
  175. core/modules/user-groups/entries/settings/components/group-nav/style.scss +46 -0
  176. core/modules/user-groups/entries/settings/components/index.js +16 -0
  177. core/modules/user-groups/entries/settings/components/index.php +1 -0
  178. core/modules/user-groups/entries/settings/components/layout/index.js +9 -0
  179. core/modules/user-groups/entries/settings/components/layout/index.php +1 -0
  180. core/modules/user-groups/entries/settings/components/manage-group/index.js +70 -0
  181. core/modules/user-groups/entries/settings/components/manage-group/index.php +1 -0
  182. core/modules/user-groups/entries/settings/components/manage-group/style.scss +51 -0
  183. core/modules/user-groups/entries/settings/components/manage-multiple-groups/index.js +39 -0
  184. core/modules/user-groups/entries/settings/components/manage-multiple-groups/index.php +1 -0
  185. core/modules/user-groups/entries/settings/components/panel-minimum-role/index.js +36 -0
  186. core/modules/user-groups/entries/settings/components/panel-minimum-role/index.php +1 -0
  187. core/modules/user-groups/entries/settings/components/panel-roles/index.js +114 -0
  188. core/modules/user-groups/entries/settings/components/panel-roles/index.php +1 -0
  189. core/modules/user-groups/entries/settings/components/panel-users/index.js +90 -0
  190. core/modules/user-groups/entries/settings/components/panel-users/index.php +1 -0
  191. core/modules/user-groups/entries/settings/components/panel-users/style.scss +11 -0
  192. core/modules/user-groups/entries/settings/components/settings-form/index.js +25 -0
  193. core/modules/user-groups/entries/settings/components/settings-form/index.php +1 -0
  194. core/modules/user-groups/entries/settings/components/tab-body/index.js +30 -0
  195. core/modules/user-groups/entries/settings/components/tab-body/index.php +1 -0
  196. core/modules/user-groups/entries/settings/components/tab-body/style.scss +15 -0
  197. core/modules/user-groups/entries/settings/components/tab-create-group/index.js +37 -0
  198. core/modules/user-groups/entries/settings/components/tab-create-group/index.php +1 -0
  199. core/modules/user-groups/entries/settings/components/tab-edit-group/index.js +39 -0
  200. core/modules/user-groups/entries/settings/components/tab-edit-group/index.php +1 -0
  201. core/modules/user-groups/entries/settings/components/tab-settings-bulk/field.js +33 -0
  202. core/modules/user-groups/entries/settings/components/tab-settings-bulk/index.js +47 -0
  203. core/modules/user-groups/entries/settings/components/tab-settings-bulk/index.php +1 -0
  204. core/modules/user-groups/entries/settings/components/tab-settings/field.js +29 -0
  205. core/modules/user-groups/entries/settings/components/tab-settings/index.js +48 -0
  206. core/modules/user-groups/entries/settings/components/tab-settings/index.php +1 -0
  207. core/modules/user-groups/entries/settings/components/tab-settings/style.scss +15 -0
  208. core/modules/user-groups/entries/settings/index.php +1 -0
  209. core/modules/user-groups/entries/settings/store/actions.js +226 -0
  210. core/modules/user-groups/entries/settings/store/controls.js +135 -0
  211. core/modules/user-groups/entries/settings/store/index.js +23 -0
  212. core/modules/user-groups/entries/settings/store/index.php +1 -0
  213. core/modules/user-groups/entries/settings/store/reducers.js +116 -0
  214. core/modules/user-groups/entries/settings/store/resolvers.js +14 -0
  215. core/modules/user-groups/entries/settings/store/selectors.js +188 -0
  216. core/modules/user-groups/entries/settings/style.scss +0 -0
  217. core/modules/user-groups/entries/store/actions.js +404 -0
  218. core/modules/user-groups/entries/store/controls.js +83 -0
  219. core/modules/user-groups/entries/store/index.js +23 -0
  220. core/modules/user-groups/entries/store/index.php +1 -0
  221. core/modules/user-groups/entries/store/reducers.js +199 -0
  222. core/modules/user-groups/entries/store/resolvers.js +54 -0
  223. core/modules/user-groups/entries/store/selectors.js +204 -0
  224. core/modules/user-groups/index.php +1 -0
  225. core/modules/user-groups/labels.php +5 -0
  226. core/modules/user-groups/settings-page.php +43 -0
  227. core/modules/wordpress-tweaks/labels.php +5 -0
  228. core/package.json +17 -11
  229. core/packages/components/src/checkbox-control/index.js +44 -0
  230. core/packages/components/src/checkbox-control/index.php +1 -0
  231. core/packages/components/src/checkbox-control/style.scss +111 -0
  232. core/packages/components/src/checkbox-group-control/index.js +35 -0
  233. core/packages/components/src/checkbox-group-control/index.php +1 -0
  234. core/packages/components/src/hierarchical-checkbox-control/index.js +267 -0
  235. core/packages/components/src/hierarchical-checkbox-control/index.php +1 -0
  236. core/packages/components/src/hierarchical-checkbox-control/style.scss +20 -0
  237. core/packages/components/src/index.js +7 -0
  238. core/packages/components/src/module-settings-notice-list/index.js +95 -0
  239. core/packages/components/src/module-settings-notice-list/index.php +1 -0
  240. core/packages/components/src/module-settings-notice-list/notice.js +54 -0
  241. core/packages/components/src/module-settings-notice-list/style.scss +25 -0
  242. core/packages/components/src/notice-list/index.js +48 -0
  243. core/packages/components/src/notice-list/index.php +1 -0
  244. core/packages/components/src/tab-panel/controlled.js +98 -0
  245. core/packages/components/src/tab-panel/index.js +30 -0
  246. core/packages/components/src/tab-panel/index.php +1 -0
  247. core/packages/components/src/tab-panel/multi.js +277 -0
  248. core/packages/components/src/tab-panel/tab-button.js +16 -0
  249. core/packages/components/src/toggle-control/index.js +68 -0
  250. core/packages/components/src/toggle-control/index.php +1 -0
  251. core/packages/components/src/toggle-control/style.scss +3 -0
  252. core/packages/data/index.php +1 -0
  253. core/packages/data/src/actions.js +40 -0
  254. core/packages/data/src/controls.js +172 -0
  255. core/packages/data/src/index.js +21 -0
  256. core/packages/data/src/index.php +1 -0
  257. core/packages/data/src/reducers.js +34 -0
  258. core/packages/data/src/resolvers.js +24 -0
  259. core/packages/data/src/selectors.js +56 -0
  260. core/packages/hocs/src/index.js +1 -0
  261. core/packages/hocs/src/with-pressed-modifier-keys.js +78 -0
  262. core/packages/i18n/index.php +1 -0
  263. core/packages/i18n/src/index.js +26 -0
  264. core/packages/i18n/src/index.php +1 -0
  265. core/packages/preload/index.php +1 -0
  266. core/packages/preload/src/index.js +78 -0
  267. core/packages/preload/src/index.php +1 -0
  268. core/packages/style-guide/src/breakpoints.scss +42 -0
  269. core/packages/style-guide/src/mixins.scss +53 -0
  270. core/packages/utils/src/index.js +59 -2
  271. core/packages/utils/src/wp-error.js +16 -0
  272. core/packages/webpack/src/babel.js +1 -0
  273. core/packages/webpack/src/config/index.js +3 -0
  274. core/response.php +11 -0
  275. core/rest.php +103 -0
  276. core/setup.php +133 -6
  277. dist/8.min.js +1 -0
  278. dist/core/admin-notices-api.min.js +1 -1
  279. dist/core/admin-notices-dashboard-admin-bar.min.css +2 -2
  280. dist/core/admin-notices-dashboard-admin-bar.min.js +1 -1
  281. dist/core/admin-notices.min.css +1 -1
  282. dist/core/admin-notices.min.js +1 -1
  283. dist/manifest.php +110 -8
  284. dist/packages/index.php +1 -0
  285. dist/packages/preload.min.js +1 -0
  286. dist/user-groups/api.min.js +1 -0
  287. dist/user-groups/index.php +1 -0
  288. dist/user-groups/settings.min.css +37 -0
  289. dist/user-groups/settings.min.js +7 -0
  290. dist/vendors/index.php +1 -0
  291. dist/vendors/user-groups/api.min.js +1 -0
  292. dist/vendors/user-groups/index.php +1 -0
  293. history.txt +20 -2
  294. package.json +17 -11
  295. readme.txt +27 -6
  296. vendor-prod/autoload.php +7 -0
  297. vendor-prod/composer/ClassLoader.php +445 -0
  298. vendor-prod/composer/LICENSE +21 -0
  299. vendor-prod/composer/autoload_classmap.php +327 -0
  300. vendor-prod/composer/autoload_namespaces.php +10 -0
  301. vendor-prod/composer/autoload_psr4.php +11 -0
  302. vendor-prod/composer/autoload_real.php +52 -0
  303. vendor-prod/composer/autoload_static.php +372 -0
  304. vendor-prod/composer/index.php +1 -0
  305. vendor-prod/composer/installed.json +156 -0
  306. vendor-prod/index.php +1 -0
  307. vendor-prod/pimple/index.php +1 -0
  308. vendor-prod/pimple/pimple/CHANGELOG +59 -0
  309. vendor-prod/pimple/pimple/LICENSE +19 -0
  310. vendor-prod/pimple/pimple/README.rst +315 -0
better-wp-security.php CHANGED
@@ -6,14 +6,15 @@
6
  * Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
7
  * Author: iThemes
8
  * Author URI: https://ithemes.com
9
- * Version: 7.6.1
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
13
- * Requires PHP: 5.5
 
14
  */
15
 
16
- if ( version_compare( phpversion(), '5.5.0', '<' ) ) {
17
  function itsec_free_minimum_php_version_notice() {
18
  echo '<div class="notice notice-error"><p>' . esc_html__( 'iThemes Security requires PHP 5.5 or higher.', 'better-wp-security' ) . '</p></div>';
19
  }
@@ -23,6 +24,16 @@ if ( version_compare( phpversion(), '5.5.0', '<' ) ) {
23
  return;
24
  }
25
 
 
 
 
 
 
 
 
 
 
 
26
  function itsec_load_textdomain() {
27
 
28
  if ( function_exists( 'determine_locale' ) ) {
@@ -46,6 +57,9 @@ if ( isset( $itsec_dir ) || class_exists( 'ITSEC_Core' ) ) {
46
  return;
47
  }
48
 
 
 
 
49
 
50
  $itsec_dir = dirname( __FILE__ );
51
 
6
  * Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
7
  * Author: iThemes
8
  * Author URI: https://ithemes.com
9
+ * Version: 7.7.0
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
13
+ * Requires PHP: 5.6
14
+ * Requires at least: 5.2
15
  */
16
 
17
+ if ( version_compare( phpversion(), '5.6.0', '<' ) ) {
18
  function itsec_free_minimum_php_version_notice() {
19
  echo '<div class="notice notice-error"><p>' . esc_html__( 'iThemes Security requires PHP 5.5 or higher.', 'better-wp-security' ) . '</p></div>';
20
  }
24
  return;
25
  }
26
 
27
+ if ( version_compare( $GLOBALS['wp_version'], '5.2.0', '<' ) ) {
28
+ function itsec_minimum_wp_version_notice() {
29
+ echo '<div class="notice notice-error"><p>' . esc_html__( 'iThemes Security Pro requires WordPress 5.2 or later.', 'better-wp-security' ) . '</p></div>';
30
+ }
31
+
32
+ add_action( 'admin_notices', 'itsec_minimum_wp_version_notice' );
33
+
34
+ return;
35
+ }
36
+
37
  function itsec_load_textdomain() {
38
 
39
  if ( function_exists( 'determine_locale' ) ) {
57
  return;
58
  }
59
 
60
+ if ( file_exists( __DIR__ . '/vendor-prod/autoload.php' ) ) {
61
+ require_once( __DIR__ . '/vendor-prod/autoload.php' );
62
+ }
63
 
64
  $itsec_dir = dirname( __FILE__ );
65
 
composer.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ithemes/ithemes-security",
3
+ "description": "Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.",
4
+ "type": "wordpress-plugin",
5
+ "license": "GPL-2.0-or-later",
6
+ "authors": [
7
+ {
8
+ "name": "Timothy Jacobs",
9
+ "email": "timothy@ithemes.com"
10
+ },
11
+ {
12
+ "name": "Chris Jean",
13
+ "email": "chris@ithemes.com"
14
+ }
15
+ ],
16
+ "minimum-stability": "stable",
17
+ "require": {
18
+ "php": "^5.4 | ^7.0",
19
+ "wikimedia/composer-merge-plugin": "^1.4"
20
+ },
21
+ "config": {
22
+ "platform": {
23
+ "php": "5.6.0"
24
+ }
25
+ },
26
+ "extra": {
27
+ "merge-plugin": {
28
+ "require": [
29
+ "core/composer.json"
30
+ ],
31
+ "merge-dev": false
32
+ }
33
+ }
34
+ }
core/Contracts/Runnable.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\Contracts;
4
+
5
+ interface Runnable {
6
+ /**
7
+ * Run the class.
8
+ *
9
+ * @return void
10
+ */
11
+ public function run();
12
+ }
core/Contracts/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/Exception/Exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\Exception;
4
+
5
+ interface Exception {
6
+
7
+ }
core/Exception/Invalid_Argument_Exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\Exception;
4
+
5
+ class Invalid_Argument_Exception extends \InvalidArgumentException implements Exception {
6
+
7
+ }
core/Exception/Unsatisfied_Module_Dependencies_Exception.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\Exception;
4
+
5
+ class Unsatisfied_Module_Dependencies_Exception extends \RuntimeException implements Exception {
6
+
7
+ }
core/Exception/WP_Error.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\Exception;
4
+
5
+ class WP_Error extends \Exception implements Exception {
6
+
7
+ /** @var \WP_Error */
8
+ private $error;
9
+
10
+ public function __construct( \WP_Error $error, \Exception $previous = null ) {
11
+ $this->error = $error;
12
+ parent::__construct( wp_sprintf( '%l', $error->get_error_messages() ), 0, $previous );
13
+ }
14
+
15
+ /**
16
+ * Create a WP Error instance from an error code and message.
17
+ *
18
+ * @param string $code
19
+ * @param string $message
20
+ * @param array $data
21
+ *
22
+ * @return static
23
+ */
24
+ public static function from_code( $code, $message, array $data = [] ) {
25
+ return new static( new \WP_Error( $code, $message, $data ) );
26
+ }
27
+
28
+ /**
29
+ * Get the WP Error instance.
30
+ *
31
+ * @return \WP_Error
32
+ */
33
+ public function get_error() {
34
+ return $this->error;
35
+ }
36
+ }
core/Exception/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/admin-pages/css/style.css CHANGED
@@ -124,8 +124,7 @@
124
  box-shadow: 0 0 20px rgba(0,0,0,0.2);
125
  z-index: 510;
126
  }
127
- .grid .itsec-modal-navigation,
128
- .grid .itsec-modal-content-footer {
129
  display: block;
130
  }
131
  .grid .itsec-list-content-footer,
@@ -261,7 +260,10 @@ body.itsec-modal-open {
261
  .itsec-modal-navigation button.itsec-close-modal::before {
262
  content: '\f335';
263
  }
264
- .itsec-modal-content-footer {
 
 
 
265
  position: absolute;
266
  bottom: 0;
267
  left: 0;
@@ -344,18 +346,17 @@ body.itsec-modal-open {
344
  box-sizing: border-box;
345
  }
346
  .itsec-module-cards {
347
- text-align: justify;
348
- font-size: 0.1px;
 
349
  }
350
  .itsec-module-cards:after {
351
  content: '';
352
- display: inline-block;
353
  width: 100%;
354
  }
355
  .itsec-module-card,
356
  .itsec-module-card-filler {
357
  font-size: 16px;
358
- display: inline-block;
359
  width: 100%;
360
  max-width: 32%;
361
  text-align: left;
@@ -449,6 +450,12 @@ body.itsec-modal-open {
449
  min-height: 14em;
450
  }
451
 
 
 
 
 
 
 
452
  @media ( max-width: 782px ) {
453
  #poststuff .list .itsec-module-card h2 {
454
  line-height: 2.6;
@@ -464,6 +471,10 @@ body.itsec-modal-open {
464
  }
465
  }
466
 
 
 
 
 
467
  /*
468
  * Success Error Messages
469
  */
@@ -880,6 +891,36 @@ body.security_page_itsec-logs .itsec-module-settings-content-main .form-table .w
880
  padding: 15px 10px;
881
  }
882
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
 
884
  /***************************************
885
  Tooltip
124
  box-shadow: 0 0 20px rgba(0,0,0,0.2);
125
  z-index: 510;
126
  }
127
+ .grid .itsec-modal-navigation {
 
128
  display: block;
129
  }
130
  .grid .itsec-list-content-footer,
260
  .itsec-modal-navigation button.itsec-close-modal::before {
261
  content: '\f335';
262
  }
263
+ .grid .itsec-modal-content-footer {
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: space-between;
267
  position: absolute;
268
  bottom: 0;
269
  left: 0;
346
  box-sizing: border-box;
347
  }
348
  .itsec-module-cards {
349
+ display: flex;
350
+ flex-wrap: wrap;
351
+ justify-content: space-between;
352
  }
353
  .itsec-module-cards:after {
354
  content: '';
 
355
  width: 100%;
356
  }
357
  .itsec-module-card,
358
  .itsec-module-card-filler {
359
  font-size: 16px;
 
360
  width: 100%;
361
  max-width: 32%;
362
  text-align: left;
450
  min-height: 14em;
451
  }
452
 
453
+ .list .itsec-list-content-footer {
454
+ display: flex;
455
+ align-items: center;
456
+ justify-content: space-between;
457
+ }
458
+
459
  @media ( max-width: 782px ) {
460
  #poststuff .list .itsec-module-card h2 {
461
  line-height: 2.6;
471
  }
472
  }
473
 
474
+ .itsec-module-documentation {
475
+ font-size: 13px;
476
+ }
477
+
478
  /*
479
  * Success Error Messages
480
  */
891
  padding: 15px 10px;
892
  }
893
 
894
+ .itsec-settings-module-settings .form-table td {
895
+ position: relative;
896
+ }
897
+
898
+ .itsec-settings-module-settings .ms-options-wrap {
899
+ max-width: 455px;
900
+ }
901
+
902
+ .itsec-settings-module-settings .ms-options-wrap > .ms-options {
903
+ margin-left: 10px;
904
+ margin-right: 10px;
905
+ width: auto;
906
+ }
907
+
908
+ .itsec-settings-module-settings .ms-options-wrap > .ms-options > ul label {
909
+ padding: 4px;
910
+ }
911
+
912
+ .itsec-settings-module-settings .ms-options-wrap > .ms-options > ul input[type="checkbox"] {
913
+ position: initial;
914
+ }
915
+
916
+ .itsec-settings-module-settings .ms-options-wrap > button {
917
+ text-overflow: ellipsis;
918
+ overflow: hidden;
919
+ }
920
+
921
+ .itsec-settings-module-settings .ms-options-wrap > button > span {
922
+ display: inline;
923
+ }
924
 
925
  /***************************************
926
  Tooltip
core/admin-pages/init.php CHANGED
@@ -24,10 +24,12 @@ final class ITSEC_Admin_Page_Loader {
24
  }
25
 
26
  public function add_scripts() {
27
-
 
28
  }
29
 
30
  public function add_styles() {
 
31
  wp_enqueue_style( 'itsec-settings-page-style', plugins_url( 'css/style.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
32
  }
33
 
@@ -63,7 +65,7 @@ final class ITSEC_Admin_Page_Loader {
63
  return $this->page_id;
64
  }
65
 
66
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
67
  if ( isset( $_REQUEST['action'] ) && preg_match( '/^itsec_(.+)_page$/', $_REQUEST['action'], $match ) ) {
68
  $this->page_id = $match[1];
69
  }
24
  }
25
 
26
  public function add_scripts() {
27
+ wp_register_script( 'itsec-jquery-multi-select', plugin_dir_url( __FILE__ ) . '/js/lib/jquery.multiselect.js', array( 'jquery' ), '2.4.17' );
28
+ wp_register_script( 'itsec-form-user-groups', plugin_dir_url( __FILE__ ) . '/js/form-user-groups.js', array( 'itsec-jquery-multi-select', 'lodash' ), 1 );
29
  }
30
 
31
  public function add_styles() {
32
+ wp_register_style( 'itsec-jquery-multi-select', plugin_dir_url( __FILE__ ) . '/js/lib/jquery.multiselect.css', array(), '2.4.17' );
33
  wp_enqueue_style( 'itsec-settings-page-style', plugins_url( 'css/style.css', __FILE__ ), array(), ITSEC_Core::get_plugin_build() );
34
  }
35
 
65
  return $this->page_id;
66
  }
67
 
68
+ if ( wp_doing_ajax() ) {
69
  if ( isset( $_REQUEST['action'] ) && preg_match( '/^itsec_(.+)_page$/', $_REQUEST['action'], $match ) ) {
70
  $this->page_id = $match[1];
71
  }
core/admin-pages/js/form-user-groups.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $, lodash, itsec, data ) {
2
+ function getValue( obj, path ) {
3
+ return path ? lodash.get( obj, path ) : obj;
4
+ }
5
+
6
+ function defaultCompare( a, b ) {
7
+ return a === b;
8
+ }
9
+
10
+ // Based on redux-watch licensed MIT: https://github.com/jprichardson/redux-watch
11
+ function watch( getState, objectPath, compare ) {
12
+ compare = compare || defaultCompare;
13
+ var currentValue = getValue( getState(), objectPath );
14
+ return function w( fn ) {
15
+ return function() {
16
+ var newValue = getValue( getState(), objectPath );
17
+ if ( !compare( currentValue, newValue ) ) {
18
+ var oldValue = currentValue;
19
+ currentValue = newValue;
20
+ fn( newValue, oldValue, objectPath );
21
+ }
22
+ };
23
+ };
24
+ }
25
+
26
+ $( function() {
27
+ $( '.itsec-form-input--type-user-groups' ).multiselect( {
28
+ selectAll: true,
29
+ } );
30
+
31
+ if ( itsec[ 'user-groups' ] && itsec[ 'user-groups' ][ 'api' ] && itsec[ 'user-groups' ][ 'api' ][ 'store' ] ) {
32
+ var store = itsec[ 'user-groups' ][ 'api' ][ 'store' ];
33
+
34
+ var watchMatchables = watch( data.select( 'ithemes-security/user-groups' ).getMatchables );
35
+ var watchSettings = watch( function() {
36
+ return store.getState().settings;
37
+ } );
38
+
39
+ store.subscribe( watchMatchables( function( currentValue, previousValue ) {
40
+ if ( !data.select( 'core/data' ).hasFinishedResolution( 'ithemes-security/user-groups', 'getMatchables' ) ) {
41
+ return;
42
+ }
43
+
44
+ $( '.itsec-form-input--type-user-groups' ).each( function() {
45
+ var $el = $( this ),
46
+ checked = $el.val() || [];
47
+
48
+ var options = ( currentValue || [] )
49
+ .sort( function( groupA, groupB ) {
50
+ if ( groupA.type === groupB.type ) {
51
+ return 0;
52
+ }
53
+
54
+ if ( groupA.type === 'user-group' ) {
55
+ return -1;
56
+ }
57
+
58
+ if ( groupB.type === 'user-group' ) {
59
+ return 1;
60
+ }
61
+
62
+ return 0;
63
+ } )
64
+ .map( function( userGroup ) {
65
+ return {
66
+ name : userGroup.label,
67
+ value : userGroup.id,
68
+ checked: checked.includes( userGroup.id ),
69
+ };
70
+ } );
71
+
72
+ $el.multiselect( 'loadOptions', options );
73
+ } );
74
+ } ) );
75
+
76
+ store.subscribe( watchSettings( lodash.debounce( function( currentValue, previousValue ) {
77
+ const settings = data.select( 'ithemes-security/user-groups' ).getGroupsBySetting();
78
+
79
+ for ( var module in settings ) {
80
+ if ( !settings.hasOwnProperty( module ) ) {
81
+ continue;
82
+ }
83
+
84
+ for ( var setting in settings[ module ] ) {
85
+ if ( !settings[ module ].hasOwnProperty( setting ) ) {
86
+ continue;
87
+ }
88
+
89
+ var groupIds = settings[ module ][ setting ];
90
+
91
+ var $option = $( '.itsec-form-input--type-user-groups[data-module="' + module + '"][data-setting="' + setting + '"]' );
92
+ var current = $option.val();
93
+
94
+ if ( !lodash.isEqual( current, groupIds ) ) {
95
+ $option.val( groupIds );
96
+ $option.multiselect( 'reload' );
97
+ }
98
+ }
99
+ }
100
+ }, 100 ) ) );
101
+ }
102
+ } );
103
+ } )( jQuery, lodash, window[ 'itsec' ], window[ 'wp' ][ 'data' ] );
core/admin-pages/js/lib/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/admin-pages/js/lib/jquery.multiselect.css ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .ms-options-wrap,
2
+ .ms-options-wrap * {
3
+ box-sizing: border-box;
4
+ }
5
+
6
+ .ms-options-wrap > button:focus,
7
+ .ms-options-wrap > button {
8
+ position: relative;
9
+ width: 100%;
10
+ text-align: left;
11
+ border: 1px solid #aaa;
12
+ background-color: #fff;
13
+ padding: 5px 20px 5px 5px;
14
+ margin-top: 1px;
15
+ font-size: 13px;
16
+ color: #aaa;
17
+ outline-offset: -2px;
18
+ white-space: nowrap;
19
+ }
20
+
21
+ .ms-options-wrap > button > span {
22
+ display: inline-block;
23
+ }
24
+
25
+ .ms-options-wrap > button[disabled] {
26
+ background-color: #e5e9ed;
27
+ color: #808080;
28
+ opacity: 0.6;
29
+ }
30
+
31
+ .ms-options-wrap > button:after {
32
+ content: ' ';
33
+ height: 0;
34
+ position: absolute;
35
+ top: 50%;
36
+ right: 5px;
37
+ width: 0;
38
+ border: 6px solid rgba(0, 0, 0, 0);
39
+ border-top-color: #999;
40
+ margin-top: -3px;
41
+ }
42
+
43
+ .ms-options-wrap.ms-has-selections > button {
44
+ color: #333;
45
+ }
46
+
47
+ .ms-options-wrap > .ms-options {
48
+ position: absolute;
49
+ left: 0;
50
+ width: 100%;
51
+ margin-top: 1px;
52
+ margin-bottom: 20px;
53
+ background: white;
54
+ z-index: 2000;
55
+ border: 1px solid #aaa;
56
+ overflow: auto;
57
+ visibility: hidden;
58
+ }
59
+
60
+ .ms-options-wrap.ms-active > .ms-options {
61
+ visibility: visible
62
+ }
63
+
64
+ .ms-options-wrap > .ms-options > .ms-search input {
65
+ width: 100%;
66
+ padding: 4px 5px;
67
+ border: none;
68
+ border-bottom: 1px groove;
69
+ outline: none;
70
+ }
71
+
72
+ .ms-options-wrap > .ms-options .ms-selectall {
73
+ display: inline-block;
74
+ font-size: .9em;
75
+ text-transform: lowercase;
76
+ text-decoration: none;
77
+ }
78
+ .ms-options-wrap > .ms-options .ms-selectall:hover {
79
+ text-decoration: underline;
80
+ }
81
+
82
+ .ms-options-wrap > .ms-options > .ms-selectall.global {
83
+ margin: 4px 5px;
84
+ }
85
+
86
+ .ms-options-wrap > .ms-options > ul,
87
+ .ms-options-wrap > .ms-options > ul > li.optgroup ul {
88
+ list-style-type: none;
89
+ padding: 0;
90
+ margin: 0;
91
+ }
92
+
93
+ .ms-options-wrap > .ms-options > ul li.ms-hidden {
94
+ display: none;
95
+ }
96
+
97
+ .ms-options-wrap > .ms-options > ul > li.optgroup {
98
+ padding: 5px;
99
+ }
100
+ .ms-options-wrap > .ms-options > ul > li.optgroup + li.optgroup {
101
+ border-top: 1px solid #aaa;
102
+ }
103
+
104
+ .ms-options-wrap > .ms-options > ul > li.optgroup .label {
105
+ display: block;
106
+ padding: 5px 0 0 0;
107
+ font-weight: bold;
108
+ }
109
+
110
+ .ms-options-wrap > .ms-options > ul label {
111
+ position: relative;
112
+ display: inline-block;
113
+ width: 100%;
114
+ padding: 4px 4px 4px 20px;
115
+ margin: 1px 0;
116
+ border: 1px dotted transparent;
117
+ }
118
+ .ms-options-wrap > .ms-options.checkbox-autofit > ul label,
119
+ .ms-options-wrap > .ms-options.hide-checkbox > ul label {
120
+ padding: 4px;
121
+ }
122
+
123
+ .ms-options-wrap > .ms-options > ul label.focused,
124
+ .ms-options-wrap > .ms-options > ul label:hover {
125
+ background-color: #efefef;
126
+ border-color: #999;
127
+ }
128
+
129
+ .ms-options-wrap > .ms-options > ul li.selected label {
130
+ background-color: #efefef;
131
+ border-color: transparent;
132
+ }
133
+
134
+ .ms-options-wrap > .ms-options > ul input[type="checkbox"] {
135
+ margin: 0 5px 0 0;
136
+ position: absolute;
137
+ left: 4px;
138
+ top: 7px;
139
+ }
140
+
141
+ .ms-options-wrap > .ms-options.hide-checkbox > ul input[type="checkbox"] {
142
+ position: absolute !important;
143
+ height: 1px;
144
+ width: 1px;
145
+ overflow: hidden;
146
+ clip: rect(1px 1px 1px 1px);
147
+ clip: rect(1px, 1px, 1px, 1px);
148
+ }
core/admin-pages/js/lib/jquery.multiselect.js ADDED
@@ -0,0 +1,936 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Display a nice easy to use multiselect list
3
+ * @Version: 2.4.17
4
+ * @Author: Patrick Springstubbe
5
+ * @Contact: @JediNobleclem
6
+ * @Website: springstubbe.us
7
+ * @Source: https://github.com/nobleclem/jQuery-MultiSelect
8
+ *
9
+ * Usage:
10
+ * $('select[multiple]').multiselect();
11
+ * $('select[multiple]').multiselect({ texts: { placeholder: 'Select options' } });
12
+ * $('select[multiple]').multiselect('reload');
13
+ * $('select[multiple]').multiselect( 'loadOptions', [{
14
+ * name : 'Option Name 1',
15
+ * value : 'option-value-1',
16
+ * checked: false,
17
+ * attributes : {
18
+ * custom1: 'value1',
19
+ * custom2: 'value2'
20
+ * }
21
+ * },{
22
+ * name : 'Option Name 2',
23
+ * value : 'option-value-2',
24
+ * checked: false,
25
+ * attributes : {
26
+ * custom1: 'value1',
27
+ * custom2: 'value2'
28
+ * }
29
+ * }]);
30
+ *
31
+ **/
32
+ (function($){
33
+ var defaults = {
34
+ columns: 1, // how many columns should be use to show options
35
+ search : false, // include option search box
36
+
37
+ // search filter options
38
+ searchOptions : {
39
+ delay : 250, // time (in ms) between keystrokes until search happens
40
+ showOptGroups: false, // show option group titles if no options remaining
41
+ searchText : true, // search within the text
42
+ searchValue : false, // search within the value
43
+ onSearch : function( element ){} // fires on keyup before search on options happens
44
+ },
45
+
46
+ // plugin texts
47
+ texts: {
48
+ placeholder : 'Select options', // text to use in dummy input
49
+ search : 'Search', // search input placeholder text
50
+ selectedOptions: ' selected', // selected suffix text
51
+ selectAll : 'Select all', // select all text
52
+ unselectAll : 'Unselect all', // unselect all text
53
+ noneSelected : 'None Selected' // None selected text
54
+ },
55
+
56
+ // general options
57
+ selectAll : false, // add select all option
58
+ selectGroup : false, // select entire optgroup
59
+ minHeight : 200, // minimum height of option overlay
60
+ maxHeight : null, // maximum height of option overlay
61
+ maxWidth : null, // maximum width of option overlay (or selector)
62
+ maxPlaceholderWidth: null, // maximum width of placeholder button
63
+ maxPlaceholderOpts : 10, // maximum number of placeholder options to show until "# selected" shown instead
64
+ showCheckbox : true, // display the checkbox to the user
65
+ checkboxAutoFit : false, // auto calc checkbox padding
66
+ optionAttributes : [], // attributes to copy to the checkbox from the option element
67
+
68
+ // Callbacks
69
+ onLoad : function( element ){}, // fires at end of list initialization
70
+ onOptionClick : function( element, option ){}, // fires when an option is clicked
71
+ onControlClose: function( element ){}, // fires when the options list is closed
72
+ onSelectAll : function( element, selected ){}, // fires when (un)select all is clicked
73
+ onPlaceholder : function( element, placeholder, selectedOpts ){}, // fires when the placeholder txt is updated
74
+ };
75
+
76
+ var msCounter = 1; // counter for each select list
77
+ var msOptCounter = 1; // counter for each option on page
78
+
79
+ // FOR LEGACY BROWSERS (talking to you IE8)
80
+ if( typeof Array.prototype.map !== 'function' ) {
81
+ Array.prototype.map = function( callback, thisArg ) {
82
+ if( typeof thisArg === 'undefined' ) {
83
+ thisArg = this;
84
+ }
85
+
86
+ return $.isArray( thisArg ) ? $.map( thisArg, callback ) : [];
87
+ };
88
+ }
89
+ if( typeof String.prototype.trim !== 'function' ) {
90
+ String.prototype.trim = function() {
91
+ return this.replace(/^\s+|\s+$/g, '');
92
+ };
93
+ }
94
+
95
+ function MultiSelect( element, options )
96
+ {
97
+ this.element = element;
98
+ this.options = $.extend( true, {}, defaults, options );
99
+ this.updateSelectAll = true;
100
+ this.updatePlaceholder = true;
101
+ this.listNumber = msCounter;
102
+
103
+ msCounter = msCounter + 1; // increment counter
104
+
105
+ /* Make sure its a multiselect list */
106
+ if( !$(this.element).attr('multiple') ) {
107
+ throw new Error( '[jQuery-MultiSelect] Select list must be a multiselect list in order to use this plugin' );
108
+ }
109
+
110
+ /* Options validation checks */
111
+ if( this.options.search ){
112
+ if( !this.options.searchOptions.searchText && !this.options.searchOptions.searchValue ){
113
+ throw new Error( '[jQuery-MultiSelect] Either searchText or searchValue should be true.' );
114
+ }
115
+ }
116
+
117
+ /** BACKWARDS COMPATIBILITY **/
118
+ if( 'placeholder' in this.options ) {
119
+ this.options.texts.placeholder = this.options.placeholder;
120
+ delete this.options.placeholder;
121
+ }
122
+ if( 'default' in this.options.searchOptions ) {
123
+ this.options.texts.search = this.options.searchOptions['default'];
124
+ delete this.options.searchOptions['default'];
125
+ }
126
+ /** END BACKWARDS COMPATIBILITY **/
127
+
128
+ // load this instance
129
+ this.load();
130
+ }
131
+
132
+ MultiSelect.prototype = {
133
+ /* LOAD CUSTOM MULTISELECT DOM/ACTIONS */
134
+ load: function() {
135
+ var instance = this;
136
+
137
+ // make sure this is a select list and not loaded
138
+ if( (instance.element.nodeName != 'SELECT') || $(instance.element).hasClass('jqmsLoaded') ) {
139
+ return true;
140
+ }
141
+
142
+ // sanity check so we don't double load on a select element
143
+ $(instance.element).addClass('jqmsLoaded ms-list-'+ instance.listNumber ).data( 'plugin_multiselect-instance', instance );
144
+
145
+ // add option container
146
+ $(instance.element).after('<div id="ms-list-'+ instance.listNumber +'" class="ms-options-wrap"><button type="button"><span>None Selected</span></button><div class="ms-options"><ul></ul></div></div>');
147
+
148
+ var placeholder = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> button:first-child');
149
+ var optionsWrap = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options');
150
+ var optionsList = optionsWrap.find('> ul');
151
+
152
+ // don't show checkbox (add class for css to hide checkboxes)
153
+ if( !instance.options.showCheckbox ) {
154
+ optionsWrap.addClass('hide-checkbox');
155
+ }
156
+ else if( instance.options.checkboxAutoFit ) {
157
+ optionsWrap.addClass('checkbox-autofit');
158
+ }
159
+
160
+ // check if list is disabled
161
+ if( $(instance.element).prop( 'disabled' ) ) {
162
+ placeholder.prop( 'disabled', true );
163
+ }
164
+
165
+ // set placeholder maxWidth
166
+ if( instance.options.maxPlaceholderWidth ) {
167
+ placeholder.css( 'maxWidth', instance.options.maxPlaceholderWidth );
168
+ }
169
+
170
+ // override with user defined maxHeight
171
+ if( instance.options.maxHeight ) {
172
+ var maxHeight = instance.options.maxHeight;
173
+ }
174
+ else {
175
+ // cacl default maxHeight
176
+ var maxHeight = ($(window).height() - optionsWrap.offset().top + $(window).scrollTop() - 20);
177
+ }
178
+
179
+ // maxHeight cannot be less than options.minHeight
180
+ maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight;
181
+
182
+ optionsWrap.css({
183
+ maxWidth : instance.options.maxWidth,
184
+ minHeight: instance.options.minHeight,
185
+ maxHeight: maxHeight,
186
+ });
187
+
188
+ // isolate options scroll
189
+ // @source: https://github.com/nobleclem/jQuery-IsolatedScroll
190
+ optionsWrap.on( 'touchmove mousewheel DOMMouseScroll', function ( e ) {
191
+ if( ($(this).outerHeight() < $(this)[0].scrollHeight) ) {
192
+ var e0 = e.originalEvent,
193
+ delta = e0.wheelDelta || -e0.detail;
194
+
195
+ if( ($(this).outerHeight() + $(this)[0].scrollTop) > $(this)[0].scrollHeight ) {
196
+ e.preventDefault();
197
+ this.scrollTop += ( delta < 0 ? 1 : -1 );
198
+ }
199
+ }
200
+ });
201
+
202
+ // hide options menus if click happens off of the list placeholder button
203
+ $(document).off('click.ms-hideopts').on('click.ms-hideopts', function( event ){
204
+ if( !$(event.target).closest('.ms-options-wrap').length ) {
205
+ $('.ms-options-wrap.ms-active > .ms-options').each(function(){
206
+ $(this).closest('.ms-options-wrap').removeClass('ms-active');
207
+
208
+ var listID = $(this).closest('.ms-options-wrap').attr('id');
209
+
210
+ var thisInst = $(this).parent().siblings('.'+ listID +'.jqmsLoaded').data('plugin_multiselect-instance');
211
+
212
+ // USER CALLBACK
213
+ if( typeof thisInst.options.onControlClose == 'function' ) {
214
+ thisInst.options.onControlClose( thisInst.element );
215
+ }
216
+ });
217
+ }
218
+ // hide open option lists if escape key pressed
219
+ }).on('keydown', function( event ){
220
+ if( (event.keyCode || event.which) == 27 ) { // esc key
221
+ $(this).trigger('click.ms-hideopts');
222
+ }
223
+ });
224
+
225
+ // handle pressing enter|space while tabbing through
226
+ placeholder.on('keydown', function( event ){
227
+ var code = (event.keyCode || event.which);
228
+ if( (code == 13) || (code == 32) ) { // enter OR space
229
+ placeholder.trigger( 'mousedown' );
230
+ }
231
+ });
232
+
233
+ // disable button action
234
+ placeholder.on( 'mousedown', function( event ){
235
+ // ignore if its not a left click
236
+ if( event.which && (event.which != 1) ) {
237
+ return true;
238
+ }
239
+
240
+ // hide other menus before showing this one
241
+ $('.ms-options-wrap.ms-active').each(function(){
242
+ if( $(this).siblings( '.'+ $(this).attr('id') +'.jqmsLoaded')[0] != optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded')[0] ) {
243
+ $(this).removeClass('ms-active');
244
+
245
+ var thisInst = $(this).siblings( '.'+ $(this).attr('id') +'.jqmsLoaded').data('plugin_multiselect-instance');
246
+
247
+ // USER CALLBACK
248
+ if( typeof thisInst.options.onControlClose == 'function' ) {
249
+ thisInst.options.onControlClose( thisInst.element );
250
+ }
251
+ }
252
+ });
253
+
254
+ // show/hide options
255
+ optionsWrap.closest('.ms-options-wrap').toggleClass('ms-active');
256
+
257
+ // recalculate height
258
+ if( optionsWrap.closest('.ms-options-wrap').hasClass('ms-active') ) {
259
+ optionsWrap.css( 'maxHeight', '' );
260
+
261
+ // override with user defined maxHeight
262
+ if( instance.options.maxHeight ) {
263
+ var maxHeight = instance.options.maxHeight;
264
+ }
265
+ else {
266
+ // cacl default maxHeight
267
+ var maxHeight = ($(window).height() - optionsWrap.offset().top + $(window).scrollTop() - 20);
268
+ }
269
+
270
+ if( maxHeight ) {
271
+ // maxHeight cannot be less than options.minHeight
272
+ maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight;
273
+
274
+ optionsWrap.css( 'maxHeight', maxHeight );
275
+ }
276
+ }
277
+ else if( typeof instance.options.onControlClose == 'function' ) {
278
+ instance.options.onControlClose( instance.element );
279
+ }
280
+ }).click(function( event ){ event.preventDefault(); });
281
+
282
+ // add placeholder copy
283
+ if( instance.options.texts.placeholder ) {
284
+ placeholder.find('span').text( instance.options.texts.placeholder );
285
+ }
286
+
287
+ // add search box
288
+ if( instance.options.search ) {
289
+ optionsList.before('<div class="ms-search"><input type="text" value="" placeholder="'+ instance.options.texts.search +'" /></div>');
290
+
291
+ var search = optionsWrap.find('.ms-search input');
292
+ search.on('keyup', function(){
293
+ // ignore keystrokes that don't make a difference
294
+ if( $(this).data('lastsearch') == $(this).val() ) {
295
+ return true;
296
+ }
297
+
298
+ // pause timeout
299
+ if( $(this).data('searchTimeout') ) {
300
+ clearTimeout( $(this).data('searchTimeout') );
301
+ }
302
+
303
+ var thisSearchElem = $(this);
304
+
305
+ $(this).data('searchTimeout', setTimeout(function(){
306
+ thisSearchElem.data('lastsearch', thisSearchElem.val() );
307
+
308
+ // USER CALLBACK
309
+ if( typeof instance.options.searchOptions.onSearch == 'function' ) {
310
+ instance.options.searchOptions.onSearch( instance.element );
311
+ }
312
+
313
+ // search non optgroup li's
314
+ var searchString = $.trim( search.val().toLowerCase() );
315
+ if( searchString ) {
316
+ optionsList.find('li[data-search-term*="'+ searchString +'"]:not(.optgroup)').removeClass('ms-hidden');
317
+ optionsList.find('li:not([data-search-term*="'+ searchString +'"], .optgroup)').addClass('ms-hidden');
318
+ }
319
+ else {
320
+ optionsList.find('.ms-hidden').removeClass('ms-hidden');
321
+ }
322
+
323
+ // show/hide optgroups based on if there are items visible within
324
+ if( !instance.options.searchOptions.showOptGroups ) {
325
+ optionsList.find('.optgroup').each(function(){
326
+ if( $(this).find('li:not(.ms-hidden)').length ) {
327
+ $(this).show();
328
+ }
329
+ else {
330
+ $(this).hide();
331
+ }
332
+ });
333
+ }
334
+
335
+ instance._updateSelectAllText();
336
+ }, instance.options.searchOptions.delay ));
337
+ });
338
+ }
339
+
340
+ // add global select all options
341
+ if( instance.options.selectAll ) {
342
+ optionsList.before('<a href="#" class="ms-selectall global">' + instance.options.texts.selectAll + '</a>');
343
+ }
344
+
345
+ // handle select all option
346
+ optionsWrap.on('click', '.ms-selectall', function( event ){
347
+ event.preventDefault();
348
+
349
+ instance.updateSelectAll = false;
350
+ instance.updatePlaceholder = false;
351
+
352
+ var select = optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded');
353
+
354
+ if( $(this).hasClass('global') ) {
355
+ // check if any options are not selected if so then select them
356
+ if( optionsList.find('li:not(.optgroup, .selected, .ms-hidden)').length ) {
357
+ // get unselected vals, mark as selected, return val list
358
+ optionsList.find('li:not(.optgroup, .selected, .ms-hidden)').addClass('selected');
359
+ optionsList.find('li.selected input[type="checkbox"]:not(:disabled)').prop( 'checked', true );
360
+ }
361
+ // deselect everything
362
+ else {
363
+ optionsList.find('li:not(.optgroup, .ms-hidden).selected').removeClass('selected');
364
+ optionsList.find('li:not(.optgroup, .ms-hidden, .selected) input[type="checkbox"]:not(:disabled)').prop( 'checked', false );
365
+ }
366
+ }
367
+ else if( $(this).closest('li').hasClass('optgroup') ) {
368
+ var optgroup = $(this).closest('li.optgroup');
369
+
370
+ // check if any selected if so then select them
371
+ if( optgroup.find('li:not(.selected, .ms-hidden)').length ) {
372
+ optgroup.find('li:not(.selected, .ms-hidden)').addClass('selected');
373
+ optgroup.find('li.selected input[type="checkbox"]:not(:disabled)').prop( 'checked', true );
374
+ }
375
+ // deselect everything
376
+ else {
377
+ optgroup.find('li:not(.ms-hidden).selected').removeClass('selected');
378
+ optgroup.find('li:not(.ms-hidden, .selected) input[type="checkbox"]:not(:disabled)').prop( 'checked', false );
379
+ }
380
+ }
381
+
382
+ var vals = [];
383
+ optionsList.find('li.selected input[type="checkbox"]').each(function(){
384
+ vals.push( $(this).val() );
385
+ });
386
+ select.val( vals ).trigger('change');
387
+
388
+ instance.updateSelectAll = true;
389
+ instance.updatePlaceholder = true;
390
+
391
+ // USER CALLBACK
392
+ if( typeof instance.options.onSelectAll == 'function' ) {
393
+ instance.options.onSelectAll( instance.element, vals.length );
394
+ }
395
+
396
+ instance._updateSelectAllText();
397
+ instance._updatePlaceholderText();
398
+ });
399
+
400
+ // add options to wrapper
401
+ var options = [];
402
+ $(instance.element).children().each(function(){
403
+ if( this.nodeName == 'OPTGROUP' ) {
404
+ var groupOptions = [];
405
+
406
+ $(this).children('option').each(function(){
407
+ var thisOptionAtts = {};
408
+ for( var i = 0; i < instance.options.optionAttributes.length; i++ ) {
409
+ var thisOptAttr = instance.options.optionAttributes[ i ];
410
+
411
+ if( $(this).attr( thisOptAttr ) !== undefined ) {
412
+ thisOptionAtts[ thisOptAttr ] = $(this).attr( thisOptAttr );
413
+ }
414
+ }
415
+
416
+ groupOptions.push({
417
+ name : $(this).text(),
418
+ value : $(this).val(),
419
+ checked: $(this).prop( 'selected' ),
420
+ attributes: thisOptionAtts
421
+ });
422
+ });
423
+
424
+ options.push({
425
+ label : $(this).attr('label'),
426
+ options: groupOptions
427
+ });
428
+ }
429
+ else if( this.nodeName == 'OPTION' ) {
430
+ var thisOptionAtts = {};
431
+ for( var i = 0; i < instance.options.optionAttributes.length; i++ ) {
432
+ var thisOptAttr = instance.options.optionAttributes[ i ];
433
+
434
+ if( $(this).attr( thisOptAttr ) !== undefined ) {
435
+ thisOptionAtts[ thisOptAttr ] = $(this).attr( thisOptAttr );
436
+ }
437
+ }
438
+
439
+ options.push({
440
+ name : $(this).text(),
441
+ value : $(this).val(),
442
+ checked : $(this).prop( 'selected' ),
443
+ attributes: thisOptionAtts
444
+ });
445
+ }
446
+ else {
447
+ // bad option
448
+ return true;
449
+ }
450
+ });
451
+ instance.loadOptions( options, true, false );
452
+
453
+ // BIND SELECT ACTION
454
+ optionsWrap.on( 'click', 'input[type="checkbox"]', function(){
455
+ $(this).closest( 'li' ).toggleClass( 'selected' );
456
+
457
+ var select = optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded');
458
+
459
+ // toggle clicked option
460
+ select.find('option[value="'+ instance._escapeSelector( $(this).val() ) +'"]').prop(
461
+ 'selected', $(this).is(':checked')
462
+ ).closest('select').trigger('change');
463
+
464
+ // USER CALLBACK
465
+ if( typeof instance.options.onOptionClick == 'function' ) {
466
+ instance.options.onOptionClick(instance.element, this);
467
+ }
468
+
469
+ instance._updateSelectAllText();
470
+ instance._updatePlaceholderText();
471
+ });
472
+
473
+ // BIND FOCUS EVENT
474
+ optionsWrap.on('focusin', 'input[type="checkbox"]', function(){
475
+ $(this).closest('label').addClass('focused');
476
+ }).on('focusout', 'input[type="checkbox"]', function(){
477
+ $(this).closest('label').removeClass('focused');
478
+ });
479
+
480
+ // USER CALLBACK
481
+ if( typeof instance.options.onLoad === 'function' ) {
482
+ instance.options.onLoad( instance.element );
483
+ }
484
+
485
+ // hide native select list
486
+ $(instance.element).hide();
487
+ },
488
+
489
+ /* LOAD SELECT OPTIONS */
490
+ loadOptions: function( options, overwrite, updateSelect ) {
491
+ overwrite = (typeof overwrite == 'boolean') ? overwrite : true;
492
+ updateSelect = (typeof updateSelect == 'boolean') ? updateSelect : true;
493
+
494
+ var instance = this;
495
+ var select = $(instance.element);
496
+ var optionsList = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options > ul');
497
+ var optionsWrap = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options');
498
+
499
+ if( overwrite ) {
500
+ optionsList.find('> li').remove();
501
+
502
+ if( updateSelect ) {
503
+ select.find('> *').remove();
504
+ }
505
+ }
506
+
507
+ var containers = [];
508
+ for( var key in options ) {
509
+ // Prevent prototype methods injected into options from being iterated over.
510
+ if( !options.hasOwnProperty( key ) ) {
511
+ continue;
512
+ }
513
+
514
+ var thisOption = options[ key ];
515
+ var container = $('<li/>');
516
+ var appendContainer = true;
517
+
518
+ // OPTION
519
+ if( thisOption.hasOwnProperty('value') ) {
520
+ if( instance.options.showCheckbox && instance.options.checkboxAutoFit ) {
521
+ container.addClass('ms-reflow');
522
+ }
523
+
524
+ // add option to ms dropdown
525
+ instance._addOption( container, thisOption );
526
+
527
+ if( updateSelect ) {
528
+ var selOption = $('<option value="'+ thisOption.value +'">'+ thisOption.name +'</option>');
529
+
530
+ // add custom user attributes
531
+ if( thisOption.hasOwnProperty('attributes') && Object.keys( thisOption.attributes ).length ) {
532
+ selOption.attr( thisOption.attributes );
533
+ }
534
+
535
+ // mark option as selected
536
+ if( thisOption.checked ) {
537
+ selOption.prop( 'selected', true );
538
+ }
539
+
540
+ select.append( selOption );
541
+ }
542
+ }
543
+ // OPTGROUP
544
+ else if( thisOption.hasOwnProperty('options') ) {
545
+ var optGroup = $('<optgroup label="'+ thisOption.label +'"></optgroup>');
546
+
547
+ optionsList.find('> li.optgroup > span.label').each(function(){
548
+ if( $(this).text() == thisOption.label ) {
549
+ container = $(this).closest('.optgroup');
550
+ appendContainer = false;
551
+ }
552
+ });
553
+
554
+ // prepare to append optgroup to select element
555
+ if( updateSelect ) {
556
+ if( select.find('optgroup[label="'+ thisOption.label +'"]').length ) {
557
+ optGroup = select.find('optgroup[label="'+ thisOption.label +'"]');
558
+ }
559
+ else {
560
+ select.append( optGroup );
561
+ }
562
+ }
563
+
564
+ // setup container
565
+ if( appendContainer ) {
566
+ container.addClass('optgroup');
567
+ container.append('<span class="label">'+ thisOption.label +'</span>');
568
+ container.find('> .label').css({
569
+ clear: 'both'
570
+ });
571
+
572
+ // add select all link
573
+ if( instance.options.selectGroup ) {
574
+ container.append('<a href="#" class="ms-selectall">' + instance.options.texts.selectAll + '</a>');
575
+ }
576
+
577
+ container.append('<ul/>');
578
+ }
579
+
580
+ for( var gKey in thisOption.options ) {
581
+ // Prevent prototype methods injected into options from
582
+ // being iterated over.
583
+ if( !thisOption.options.hasOwnProperty( gKey ) ) {
584
+ continue;
585
+ }
586
+
587
+ var thisGOption = thisOption.options[ gKey ];
588
+ var gContainer = $('<li/>');
589
+ if( instance.options.showCheckbox && instance.options.checkboxAutoFit ) {
590
+ gContainer.addClass('ms-reflow');
591
+ }
592
+
593
+ // no clue what this is we hit (skip)
594
+ if( !thisGOption.hasOwnProperty('value') ) {
595
+ continue;
596
+ }
597
+
598
+ instance._addOption( gContainer, thisGOption );
599
+
600
+ container.find('> ul').append( gContainer );
601
+
602
+ // add option to optgroup in select element
603
+ if( updateSelect ) {
604
+ var selOption = $('<option value="'+ thisGOption.value +'">'+ thisGOption.name +'</option>');
605
+
606
+ // add custom user attributes
607
+ if( thisGOption.hasOwnProperty('attributes') && Object.keys( thisGOption.attributes ).length ) {
608
+ selOption.attr( thisGOption.attributes );
609
+ }
610
+
611
+ // mark option as selected
612
+ if( thisGOption.checked ) {
613
+ selOption.prop( 'selected', true );
614
+ }
615
+
616
+ optGroup.append( selOption );
617
+ }
618
+ }
619
+ }
620
+ else {
621
+ // no clue what this is we hit (skip)
622
+ continue;
623
+ }
624
+
625
+ if( appendContainer ) {
626
+ containers.push( container );
627
+ }
628
+ }
629
+ optionsList.append( containers );
630
+
631
+ // pad out label for room for the checkbox
632
+ if( instance.options.checkboxAutoFit && instance.options.showCheckbox && !optionsWrap.hasClass('hide-checkbox') ) {
633
+ var chkbx = optionsList.find('.ms-reflow:eq(0) input[type="checkbox"]');
634
+ if( chkbx.length ) {
635
+ var checkboxWidth = chkbx.outerWidth();
636
+ checkboxWidth = checkboxWidth ? checkboxWidth : 15;
637
+
638
+ optionsList.find('.ms-reflow label').css(
639
+ 'padding-left',
640
+ (parseInt( chkbx.closest('label').css('padding-left') ) * 2) + checkboxWidth
641
+ );
642
+
643
+ optionsList.find('.ms-reflow').removeClass('ms-reflow');
644
+ }
645
+ }
646
+
647
+ // update placeholder text
648
+ instance._updatePlaceholderText();
649
+
650
+ // RESET COLUMN STYLES
651
+ optionsWrap.find('ul').css({
652
+ 'column-count' : '',
653
+ 'column-gap' : '',
654
+ '-webkit-column-count': '',
655
+ '-webkit-column-gap' : '',
656
+ '-moz-column-count' : '',
657
+ '-moz-column-gap' : ''
658
+ });
659
+
660
+ // COLUMNIZE
661
+ if( select.find('optgroup').length ) {
662
+ // float non grouped options
663
+ optionsList.find('> li:not(.optgroup)').css({
664
+ 'float': 'left',
665
+ width: (100 / instance.options.columns) +'%'
666
+ });
667
+
668
+ // add CSS3 column styles
669
+ optionsList.find('li.optgroup').css({
670
+ clear: 'both'
671
+ }).find('> ul').css({
672
+ 'column-count' : instance.options.columns,
673
+ 'column-gap' : 0,
674
+ '-webkit-column-count': instance.options.columns,
675
+ '-webkit-column-gap' : 0,
676
+ '-moz-column-count' : instance.options.columns,
677
+ '-moz-column-gap' : 0
678
+ });
679
+
680
+ // for crappy IE versions float grouped options
681
+ if( this._ieVersion() && (this._ieVersion() < 10) ) {
682
+ optionsList.find('li.optgroup > ul > li').css({
683
+ 'float': 'left',
684
+ width: (100 / instance.options.columns) +'%'
685
+ });
686
+ }
687
+ }
688
+ else {
689
+ // add CSS3 column styles
690
+ optionsList.css({
691
+ 'column-count' : instance.options.columns,
692
+ 'column-gap' : 0,
693
+ '-webkit-column-count': instance.options.columns,
694
+ '-webkit-column-gap' : 0,
695
+ '-moz-column-count' : instance.options.columns,
696
+ '-moz-column-gap' : 0
697
+ });
698
+
699
+ // for crappy IE versions float grouped options
700
+ if( this._ieVersion() && (this._ieVersion() < 10) ) {
701
+ optionsList.find('> li').css({
702
+ 'float': 'left',
703
+ width: (100 / instance.options.columns) +'%'
704
+ });
705
+ }
706
+ }
707
+
708
+ // update un/select all logic
709
+ instance._updateSelectAllText();
710
+ },
711
+
712
+ /* UPDATE MULTISELECT CONFIG OPTIONS */
713
+ settings: function( options ) {
714
+ this.options = $.extend( true, {}, this.options, options );
715
+ this.reload();
716
+ },
717
+
718
+ /* RESET THE DOM */
719
+ unload: function() {
720
+ $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').remove();
721
+ $(this.element).show(function(){
722
+ $(this).css('display','').removeClass('jqmsLoaded');
723
+ });
724
+ },
725
+
726
+ /* RELOAD JQ MULTISELECT LIST */
727
+ reload: function() {
728
+ // remove existing options
729
+ $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').remove();
730
+ $(this.element).removeClass('jqmsLoaded');
731
+
732
+ // load element
733
+ this.load();
734
+ },
735
+
736
+ // RESET BACK TO DEFAULT VALUES & RELOAD
737
+ reset: function() {
738
+ var defaultVals = [];
739
+ $(this.element).find('option').each(function(){
740
+ if( $(this).prop('defaultSelected') ) {
741
+ defaultVals.push( $(this).val() );
742
+ }
743
+ });
744
+
745
+ $(this.element).val( defaultVals );
746
+
747
+ this.reload();
748
+ },
749
+
750
+ disable: function( status ) {
751
+ status = (typeof status === 'boolean') ? status : true;
752
+ $(this.element).prop( 'disabled', status );
753
+ $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').find('button:first-child')
754
+ .prop( 'disabled', status );
755
+ },
756
+
757
+ /** PRIVATE FUNCTIONS **/
758
+ // update the un/select all texts based on selected options and visibility
759
+ _updateSelectAllText: function(){
760
+ if( !this.updateSelectAll ) {
761
+ return;
762
+ }
763
+
764
+ var instance = this;
765
+
766
+ // select all not used at all so just do nothing
767
+ if( !instance.options.selectAll && !instance.options.selectGroup ) {
768
+ return;
769
+ }
770
+
771
+ var optionsWrap = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options');
772
+
773
+ // update un/select all text
774
+ optionsWrap.find('.ms-selectall').each(function(){
775
+ var unselected = $(this).parent().find('li:not(.optgroup,.selected,.ms-hidden)');
776
+
777
+ $(this).text(
778
+ unselected.length ? instance.options.texts.selectAll : instance.options.texts.unselectAll
779
+ );
780
+ });
781
+ },
782
+
783
+ // update selected placeholder text
784
+ _updatePlaceholderText: function(){
785
+ if( !this.updatePlaceholder ) {
786
+ return;
787
+ }
788
+
789
+ var instance = this;
790
+ var select = $(instance.element);
791
+ var selectVals = select.val() ? select.val() : [];
792
+ var placeholder = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> button:first-child');
793
+ var placeholderTxt = placeholder.find('span');
794
+ var optionsWrap = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options');
795
+
796
+ // if there are disabled options get those values as well
797
+ if( select.find('option:selected:disabled').length ) {
798
+ selectVals = [];
799
+ select.find('option:selected').each(function(){
800
+ selectVals.push( $(this).val() );
801
+ });
802
+ }
803
+
804
+ // get selected options
805
+ var selOpts = [];
806
+ for( var key in selectVals ) {
807
+ // Prevent prototype methods injected into options from being iterated over.
808
+ if( !selectVals.hasOwnProperty( key ) ) {
809
+ continue;
810
+ }
811
+
812
+ selOpts.push(
813
+ $.trim( select.find('option[value="'+ instance._escapeSelector( selectVals[ key ] ) +'"]').text() )
814
+ );
815
+
816
+ if( selOpts.length >= instance.options.maxPlaceholderOpts ) {
817
+ break;
818
+ }
819
+ }
820
+
821
+ // UPDATE PLACEHOLDER TEXT WITH OPTIONS SELECTED
822
+ placeholderTxt.text( selOpts.join( ', ' ) );
823
+
824
+ if( selOpts.length ) {
825
+ optionsWrap.closest('.ms-options-wrap').addClass('ms-has-selections');
826
+
827
+ // USER CALLBACK
828
+ if( typeof instance.options.onPlaceholder == 'function' ) {
829
+ instance.options.onPlaceholder( instance.element, placeholderTxt, selOpts );
830
+ }
831
+ }
832
+ else {
833
+ optionsWrap.closest('.ms-options-wrap').removeClass('ms-has-selections');
834
+ }
835
+
836
+ // replace placeholder text
837
+ if( !selOpts.length ) {
838
+ placeholderTxt.text( instance.options.texts.placeholder );
839
+ }
840
+ // if copy is larger than button width use "# selected"
841
+ else if( (placeholderTxt.width() > placeholder.width()) || (selOpts.length != selectVals.length) ) {
842
+ placeholderTxt.text( selectVals.length + instance.options.texts.selectedOptions );
843
+ }
844
+ },
845
+
846
+ // Add option to the custom dom list
847
+ _addOption: function( container, option ) {
848
+ var instance = this;
849
+ var thisOption = $('<label/>', {
850
+ for : 'ms-opt-'+ msOptCounter,
851
+ text: option.name
852
+ });
853
+
854
+ var thisCheckbox = $('<input>', {
855
+ type : 'checkbox',
856
+ title: option.name,
857
+ id : 'ms-opt-'+ msOptCounter,
858
+ value: option.value
859
+ });
860
+
861
+ // add user defined attributes
862
+ if( option.hasOwnProperty('attributes') && Object.keys( option.attributes ).length ) {
863
+ thisCheckbox.attr( option.attributes );
864
+ }
865
+
866
+ if( option.checked ) {
867
+ container.addClass('default selected');
868
+ thisCheckbox.prop( 'checked', true );
869
+ }
870
+
871
+ thisOption.prepend( thisCheckbox );
872
+
873
+ var searchTerm = '';
874
+ if( instance.options.searchOptions.searchText ) {
875
+ searchTerm += ' ' + option.name.toLowerCase();
876
+ }
877
+ if( instance.options.searchOptions.searchValue ) {
878
+ searchTerm += ' ' + option.value.toLowerCase();
879
+ }
880
+
881
+ container.attr( 'data-search-term', $.trim( searchTerm ) ).prepend( thisOption );
882
+
883
+ msOptCounter = msOptCounter + 1;
884
+ },
885
+
886
+ // check ie version
887
+ _ieVersion: function() {
888
+ var myNav = navigator.userAgent.toLowerCase();
889
+ return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
890
+ },
891
+
892
+ // escape selector
893
+ _escapeSelector: function( string ) {
894
+ if( typeof $.escapeSelector == 'function' ) {
895
+ return $.escapeSelector( string );
896
+ }
897
+ else {
898
+ return string.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
899
+ }
900
+ }
901
+ };
902
+
903
+ // ENABLE JQUERY PLUGIN FUNCTION
904
+ $.fn.multiselect = function( options ){
905
+ if( !this.length ) {
906
+ return;
907
+ }
908
+
909
+ var args = arguments;
910
+ var ret;
911
+
912
+ // menuize each list
913
+ if( (options === undefined) || (typeof options === 'object') ) {
914
+ return this.each(function(){
915
+ if( !$.data( this, 'plugin_multiselect' ) ) {
916
+ $.data( this, 'plugin_multiselect', new MultiSelect( this, options ) );
917
+ }
918
+ });
919
+ } else if( (typeof options === 'string') && (options[0] !== '_') && (options !== 'init') ) {
920
+ this.each(function(){
921
+ var instance = $.data( this, 'plugin_multiselect' );
922
+
923
+ if( instance instanceof MultiSelect && typeof instance[ options ] === 'function' ) {
924
+ ret = instance[ options ].apply( instance, Array.prototype.slice.call( args, 1 ) );
925
+ }
926
+
927
+ // special destruct handler
928
+ if( options === 'unload' ) {
929
+ $.data( this, 'plugin_multiselect', null );
930
+ }
931
+ });
932
+
933
+ return ret;
934
+ }
935
+ };
936
+ }(jQuery));
core/admin-pages/js/script.js DELETED
@@ -1,1135 +0,0 @@
1
- "use strict";
2
-
3
- var itsecSettingsPage = {
4
-
5
- events: jQuery( {} ),
6
-
7
- init: function() {
8
- jQuery( '.itsec-module-settings-container' ).hide();
9
-
10
- this.bindEvents();
11
-
12
- jQuery( '.itsec-settings-view-toggle .itsec-selected' ).removeClass( 'itsec-selected' ).trigger( 'click' );
13
- jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
14
-
15
- this.initFilters();
16
- this.initCurrentModule();
17
- this.makeNoticesDismissible();
18
- },
19
-
20
- initFilters: function() {
21
- var module_type = this.getUrlParameter( 'module_type' );
22
- if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
23
- module_type = 'recommended';
24
- }
25
- jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'click' );
26
- },
27
-
28
- initCurrentModule: function() {
29
-
30
- var module = this.getUrlParameter( 'module' );
31
- if ( 'string' === typeof module ) {
32
- jQuery( '#itsec-module-card-' + module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'click' );
33
- }
34
- },
35
-
36
- bindEvents: function() {
37
-
38
- if ( itsecSettingsPage.bindEvents.bound ) {
39
- return;
40
- }
41
-
42
- jQuery(window).on("popstate", function(e, data) {
43
- if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module && '' !== e.originalEvent.state.module.replace( /[^\w-]/g, '' ) ) {
44
- jQuery( '#itsec-module-card-' + e.originalEvent.state.module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'itsec-popstate' );
45
- } else {
46
- itsecSettingsPage.closeGridSettingsModal( e );
47
- }
48
-
49
- if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module_type && '' !== e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) ) {
50
- jQuery( '#itsec-module-filter-' + e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'itsec-popstate' );
51
- }
52
- });
53
-
54
- var $container = jQuery( '#wpcontent' );
55
-
56
- $container.on( 'click', '.itsec-module-filter a', this.filterView );
57
- $container.on( 'itsec-popstate', '.itsec-module-filter a', this.filterView );
58
- $container.on( 'click', '.itsec-settings-view-toggle a', this.toggleView );
59
- // $container.on( 'click', '.itsec-toggle-settings, .itsec-module-card-content h2', this.toggleSettings );
60
- $container.on( 'click', 'a[data-module-link]', this.openModuleFromLink );
61
- $container.on( 'click', '.list .itsec-module-card:not(.itsec-module-pro-upsell) .itsec-module-card-content, .itsec-toggle-settings, .itsec-module-settings-cancel', this.toggleSettings );
62
- $container.on( 'itsec-popstate', '.list .itsec-module-card-content, .itsec-toggle-settings', this.toggleSettings );
63
- $container.on( 'click', '.itsec-close-modal, .itsec-modal-background', this.closeGridSettingsModal );
64
- $container.on( 'keyup', this.closeGridSettingsModal );
65
- $container.on( 'click', '.itsec-toggle-activation', this.toggleModuleActivation );
66
- $container.on( 'click', '.itsec-module-settings-save', this.saveSettings );
67
- $container.on( 'click', '.itsec-reload-module', this.reloadModule );
68
- $container.on( 'click', '.itsec-details-toggle-container a[href="#"]', this.toggleDetails );
69
-
70
- $container.on( 'change', '#itsec-filter', this.logPageChangeFilter );
71
-
72
- // For use by module content to show/hide settings sections based upon an input.
73
- $container.on( 'change', '.itsec-settings-toggle', this.toggleModuleContent );
74
- $container.on( 'click', '.itsec-copy-trigger', this.handleCopy );
75
-
76
- itsecSettingsPage.bindEvents.bound = true;
77
- },
78
-
79
- toggleDetails: function( e ) {
80
- e.preventDefault();
81
-
82
- var $details = jQuery(this).parent().find( '.itsec-details-toggle-details' ).toggleClass( 'hide-if-js' );
83
-
84
- if ( $details.hasClass( 'hide-if-js' ) ) {
85
- jQuery(this).html( itsec_page.translations.show_information );
86
- } else {
87
- jQuery(this).html( itsec_page.translations.hide_description );
88
- }
89
- },
90
-
91
- logPageChangeFilter: function( e ) {
92
- var filter = jQuery( this ).val();
93
- var url = itsec_page.logs_page_url + '&filter=' + filter;
94
- window.location.href = url;
95
- },
96
-
97
- toggleModuleContent: function( e ) {
98
- if ( 'checkbox' === jQuery(this).attr( 'type' ) ) {
99
- var show = jQuery(this).prop( 'checked' );
100
- } else {
101
- var show = ( jQuery(this).val() ) ? true : false;
102
- }
103
-
104
- var $content = jQuery( '.' + jQuery(this).attr( 'id' ) + '-content' );
105
-
106
- if ( show ) {
107
- $content.show();
108
-
109
-
110
- var $container = jQuery( '.itsec-module-cards-container' );
111
-
112
- if ( $container.hasClass( 'grid' ) ) {
113
- var $modal = jQuery(this).parents( '.itsec-module-settings-content-container' );
114
- var scrollOffset = $modal.scrollTop() + jQuery(this).parent().position().top;
115
-
116
- $modal.animate( {'scrollTop': scrollOffset}, 'slow' );
117
- }
118
- } else {
119
- $content.hide();
120
- }
121
- },
122
-
123
- handleCopy: function( e ) {
124
-
125
- e.preventDefault();
126
-
127
- var $trigger = jQuery( e.currentTarget );
128
- var fromId = $trigger.data( 'copy-from' );
129
-
130
- if ( ! fromId.length ) {
131
- return;
132
- }
133
-
134
- var el = document.getElementById( fromId );
135
-
136
- var removeSelect = itsecSettingsPage.selectText( el );
137
-
138
- try {
139
-
140
- document.execCommand( 'copy' );
141
- removeSelect();
142
- $trigger.text( itsec_page.translations.copied );
143
-
144
- } catch ( e ) {
145
- var $p = jQuery( '<p></p>' ).text( itsec_page.translations.copy_instruction ),
146
- $notice = jQuery( '<div class="notice notice-alt notice-info"></div>' ).append( $p ),
147
- $el = jQuery( el );
148
-
149
- $trigger.after( $notice );
150
-
151
- var removeNotice = function () {
152
- $notice.fadeOut( function () {
153
- $notice.remove();
154
- } );
155
- };
156
- var copy = function () {
157
-
158
- setTimeout( function () {
159
- removeNotice();
160
- removeSelect();
161
- }, 100 );
162
-
163
- $el.off( 'copy', copy );
164
-
165
- return true;
166
- };
167
-
168
- $el.on( 'copy', copy );
169
-
170
- setTimeout( removeNotice, 5000 );
171
- }
172
- },
173
-
174
- // https://stackoverflow.com/a/987376
175
- selectText: function( element ) {
176
- var doc = document, text = element, range, selection;
177
-
178
- if ( doc.body.createTextRange ) { // ie
179
- range = document.body.createTextRange();
180
- range.moveToElementText( text );
181
- range.select();
182
- } else if ( window.getSelection ) {
183
- selection = window.getSelection();
184
- range = document.createRange();
185
- range.selectNodeContents( text );
186
- selection.removeAllRanges();
187
- selection.addRange( range );
188
- }
189
-
190
- return function() {
191
- if ( selection ) {
192
- selection.removeAllRanges();
193
- } else {
194
- range.collapse();
195
- }
196
- };
197
- },
198
-
199
- saveSettings: function( e ) {
200
- e.preventDefault();
201
-
202
- var $button = jQuery(this);
203
-
204
- if ( $button.hasClass( 'itsec-module-settings-save' ) ) {
205
- var module = $button.parents( '.itsec-module-card' ).attr( 'id' ).replace( 'itsec-module-card-', '' );
206
- } else {
207
- var module = '';
208
- }
209
-
210
- $button.prop( 'disabled', true );
211
-
212
- var data = {
213
- '--itsec-form-serialized-data': jQuery( '#itsec-module-settings-form' ).serialize()
214
- };
215
-
216
- itsecSettingsPage.sendAJAXRequest( module, 'save', data, itsecSettingsPage.saveSettingsCallback );
217
- },
218
-
219
- saveSettingsCallback: function( results ) {
220
- if ( '' === results.module ) {
221
- jQuery( '#itsec-save' ).prop( 'disabled', false );
222
- } else {
223
- jQuery( '#itsec-module-card-' + results.module + ' button.itsec-module-settings-save' ).prop( 'disabled', false );
224
- }
225
-
226
- var $container = jQuery( '.itsec-module-cards-container' );
227
-
228
- if ( $container.hasClass( 'grid' ) ) {
229
- var view = 'grid';
230
- } else {
231
- var view = 'list';
232
- }
233
-
234
- itsecSettingsPage.clearMessages();
235
-
236
- if ( results.errors.length > 0 || results.warnings.length > 0 || ! results.closeModal ) {
237
- itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
238
- itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
239
- itsecSettingsPage.showMessages( results.messages, results.module, 'open' );
240
- itsecSettingsPage.showMessages( results.infos, results.module, 'open', 'info' );
241
-
242
- if ( 'grid' === view ) {
243
- $container.find( '.itsec-module-settings-content-container:visible' ).animate( {'scrollTop': 0}, 'fast' );
244
- }
245
-
246
- if ( 'list' === view ) {
247
- jQuery(document).scrollTop( 0 );
248
- }
249
- } else {
250
- itsecSettingsPage.showMessages( results.messages, results.module, 'closed' );
251
- itsecSettingsPage.showMessages( results.infos, results.module, 'closed', 'info' );
252
-
253
- if ( 'grid' === view ) {
254
- $container.find( '.itsec-module-settings-content-container:visible' ).scrollTop( 0 );
255
- itsecSettingsPage.closeGridSettingsModal();
256
- }
257
- }
258
- },
259
-
260
- clearMessages: function() {
261
- jQuery( '#itsec-settings-messages-container, .itsec-module-messages-container' ).empty();
262
- },
263
-
264
- showErrors: function( errors, module, containerStatus, type ) {
265
- jQuery.each( errors, function( index, error ) {
266
- itsecSettingsPage.showError( error, module, containerStatus, type );
267
- } );
268
- },
269
-
270
- showError: function( error, module, containerStatus, type ) {
271
-
272
- type = type || 'error';
273
-
274
- if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
275
- var view = 'grid';
276
- } else {
277
- var view = 'list';
278
- }
279
-
280
- if ( 'closed' !== containerStatus && 'open' !== containerStatus ) {
281
- containerStatus = 'closed';
282
- }
283
-
284
- if ( 'string' !== typeof module ) {
285
- module = '';
286
- }
287
-
288
-
289
- if ( 'closed' === containerStatus || '' === module ) {
290
- var container = jQuery( '#itsec-settings-messages-container' );
291
-
292
- if ( '' === module ) {
293
- container.addClass( 'no-module' );
294
- }
295
- } else {
296
- var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
297
- }
298
-
299
- var $notice = jQuery( '<div class="notice"><p><strong>' + error + '</strong></p></div>' );
300
- $notice.addClass( 'notice-' + type );
301
-
302
- if ( containerStatus === 'open' || module.length ) {
303
- $notice.addClass( 'notice-alt' );
304
- }
305
-
306
- container.append( $notice ).addClass( 'visible' );
307
- },
308
-
309
- showMessages: function( messages, module, containerStatus, type ) {
310
- jQuery.each( messages, function( index, message ) {
311
- itsecSettingsPage.showMessage( message, module, containerStatus, type );
312
- } );
313
- },
314
-
315
- showMessage: function( message, module, containerStatus, type ) {
316
-
317
- type = type || 'success';
318
-
319
- if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
320
- var view = 'grid';
321
- } else {
322
- var view = 'list';
323
- }
324
-
325
- if ( 'closed' !== containerStatus && 'open' !== containerStatus ) {
326
- containerStatus = 'closed';
327
- }
328
-
329
- if ( 'string' !== typeof module ) {
330
- module = '';
331
- }
332
-
333
-
334
- if ( 'closed' === containerStatus || '' === module ) {
335
- var container = jQuery( '#itsec-settings-messages-container' );
336
-
337
- var dismiss = function () {
338
-
339
- if ( container.is( ':hover' ) ) {
340
- return setTimeout( dismiss, 2000 );
341
- }
342
-
343
- container.removeClass( 'visible' );
344
- setTimeout( function () {
345
- container.find( 'div' ).remove();
346
- }, 500 );
347
- };
348
-
349
- setTimeout( dismiss, 4000 );
350
- } else {
351
- var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
352
- }
353
-
354
- var $notice = jQuery( '<div class="notice fade"><p><strong>' + message + '</strong></p></div>' );
355
- $notice.addClass( 'notice-' + type );
356
-
357
- if ( containerStatus === 'open' || module.length ) {
358
- $notice.addClass( 'notice-alt' );
359
- }
360
-
361
- container.append( $notice ).addClass( 'visible' );
362
- },
363
-
364
- filterView: function( e ) {
365
- e.preventDefault();
366
-
367
- var $activeLink = jQuery(this),
368
- $oldLink = $activeLink.parents( '.itsec-feature-tabs' ).find( '.current' ),
369
- type = $activeLink.parent().attr( 'id' ).substr( 20 );
370
-
371
- $oldLink.removeClass( 'current' );
372
- $activeLink.addClass( 'current' );
373
-
374
- if ( 'all' === type ) {
375
- jQuery( '.itsec-module-card' ).show();
376
- } else {
377
- jQuery( '.itsec-module-type-' + type ).show();
378
- jQuery( '.itsec-module-card' ).not( '.itsec-module-type-' + type ).hide();
379
- }
380
-
381
- // We use this to avoid pushing a new state when we're trying to handle a popstate
382
- if ( 'itsec-popstate' !== e.type ) {
383
- var url = '?page=itsec&module_type=' + type;
384
- var module = itsecSettingsPage.getUrlParameter( 'module' );
385
- if ( 'string' === typeof module ) {
386
- url += '&module=' + module;
387
- }
388
-
389
- window.history.pushState( {'module_type':type}, type, url );
390
- }
391
- },
392
-
393
- toggleView: function( e ) {
394
- e.preventDefault();
395
-
396
- var $self = jQuery(this);
397
-
398
- if ( $self.hasClass( 'itsec-selected' ) ) {
399
- // Do nothing if already selected.
400
- return;
401
- }
402
-
403
- var $view = $self.attr( 'class' ).replace( 'itsec-', '' );
404
-
405
- $self.addClass( 'itsec-selected' ).siblings().removeClass( 'itsec-selected' );
406
- jQuery( '.itsec-module-settings-container' ).hide();
407
-
408
- jQuery( '.itsec-toggle-settings' ).each(function( index ) {
409
- var $button = jQuery( this );
410
-
411
- if ( $button.parents( '.itsec-module-card' ).hasClass( 'itsec-module-type-enabled' ) && ! $button.hasClass( 'information-only' ) ) {
412
- $button.html( itsec_page.translations.show_settings );
413
- } else if ( $button.hasClass( 'information-only' ) ) {
414
- $button.html( itsec_page.translations.information_only );
415
- } else {
416
- $button.html( itsec_page.translations.show_description );
417
- }
418
- });
419
-
420
- var $cardContainer = jQuery( '.itsec-module-cards-container' );
421
- jQuery.post( ajaxurl, {
422
- 'action': 'itsec-set-user-setting',
423
- 'itsec-user-setting-nonce': $self.parent().data( 'nonce' ),
424
- 'setting': 'itsec-settings-view',
425
- 'value': $view
426
- } );
427
-
428
- $cardContainer.fadeOut( 100, function() {
429
- $cardContainer.removeClass( 'grid list' ).addClass( $view );
430
- } );
431
- $cardContainer.fadeIn( 100 );
432
- },
433
-
434
- openModuleFromLink: function( e ) {
435
-
436
- var $link = jQuery( this ), module = $link.data( 'module-link' ),
437
- $module = jQuery( '.itsec-module-card[data-module-id="' + module + '"]' ),
438
- highlight = $link.data( 'highlight-setting-id' );
439
-
440
- if ( ! $module.length ) {
441
- return; // safety check
442
- }
443
-
444
- e.preventDefault();
445
-
446
- jQuery( '.itsec-module-settings-container:visible' ).hide();
447
-
448
- var $listClassElement = $module.parents( '.itsec-module-cards-container' ),
449
- $toggleButton = $module.find( '.itsec-toggle-settings' );
450
-
451
- if ( highlight && highlight.length ) {
452
- jQuery( 'label[for="' + highlight + '"]', $module ).parents( 'tr' ).addClass( 'itsec-highlighted-setting' );
453
- }
454
-
455
- if ( $listClassElement.hasClass( 'list' ) ) {
456
- itsecSettingsPage.toggleListSettingsCard.call( $toggleButton, e );
457
- } else if ( $listClassElement.hasClass( 'grid' ) ) {
458
- itsecSettingsPage.showGridSettingsModal.call( $toggleButton, e );
459
- }
460
-
461
- var type = $module.hasClass( 'itsec-module-type-advanced' ) ? 'advanced' : 'recommended';
462
-
463
- window.history.pushState( {module: module}, module, '?page=itsec&module=' + module + '&module_type=' + type );
464
-
465
- var href = $link.attr( 'href' );
466
-
467
- if ( href && href.length > 1 && href.charAt( 0 ) === '#' ) {
468
- setTimeout( function () {
469
- jQuery( '.itsec-module-settings-content-container', '#itsec-module-card-notification-center' ).scrollTo( jQuery( href ), 'swing', { offset: -30 } );
470
- }, 350 );
471
- }
472
- },
473
-
474
- toggleSettings: function( e ) {
475
- e.stopPropagation();
476
-
477
- var $listClassElement = jQuery(e.currentTarget).parents( '.itsec-module-cards-container' );
478
-
479
- if ( $listClassElement.hasClass( 'list') ) {
480
- itsecSettingsPage.toggleListSettingsCard.call( this, e );
481
- } else if ( $listClassElement.hasClass( 'grid' ) ) {
482
- itsecSettingsPage.showGridSettingsModal.call( this, e );
483
- }
484
-
485
- // We use this to avoid pushing a new state when we're trying to handle a popstate
486
- if ( 'itsec-popstate' !== e.type ) {
487
- var module_id = jQuery(this).closest('.itsec-module-card').data( 'module-id' );
488
-
489
- var module_type = itsecSettingsPage.getUrlParameter( 'module_type' );
490
- if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
491
- module_type = 'recommended';
492
- }
493
- window.history.pushState( {'module':module_id}, module_id, '?page=itsec&module=' + module_id + '&module_type=' + module_type );
494
- }
495
- },
496
-
497
- toggleListSettingsCard: function( e ) {
498
- e.preventDefault();
499
-
500
- var $container = jQuery(this);
501
-
502
- if ( ! $container.hasClass( 'itsec-module-card-content' ) ) {
503
- $container = $container.parents( '.itsec-module-card' ).find( '.itsec-module-card-content' );
504
- }
505
-
506
- var $settings = $container.siblings( '.itsec-module-settings-container' ),
507
- isVisible = $settings.is( ':visible' );
508
- $settings.stop().slideToggle( 300 );
509
-
510
- if ( ! isVisible ) {
511
- var $highlighted = jQuery( '.itsec-highlighted-setting', $settings );
512
-
513
- if ( $highlighted.length ) {
514
- setTimeout( function () {
515
- jQuery.scrollTo( $highlighted.first(), 'swing', {
516
- offset: { top: -30 },
517
- onAfter: function() {
518
- var $el = jQuery( 'input[type!="button"], textarea, select', $highlighted ).not( ':hidden' ).first();
519
- itsecSettingsPage.focus( $el, $highlighted );
520
- }
521
- } );
522
- }, 50 );
523
- } else {
524
- var $el = jQuery( 'input[type!="button"], textarea, select', $settings ).not( ':hidden' ).first();
525
- itsecSettingsPage.focus( $el, $settings );
526
- }
527
- }
528
-
529
- var $button = $container.find( '.itsec-toggle-settings' );
530
-
531
- if ( $container.parent().hasClass( 'itsec-module-type-enabled' ) ) {
532
- if ( $button.html() == itsec_page.translations.show_settings ) {
533
- $button.html( itsec_page.translations.hide_settings );
534
- } else {
535
- $button.html( itsec_page.translations.show_settings );
536
- }
537
- } else {
538
- if ( $button.hasClass( 'information-only' ) ) {
539
- if ( $button.html() == itsec_page.translations.show_information ) {
540
- $button.html( itsec_page.translations.hide_description );
541
- } else {
542
- $button.html( itsec_page.translations.show_information );
543
- }
544
- } else {
545
- if ( $button.html() == itsec_page.translations.show_description ) {
546
- $button.html( itsec_page.translations.hide_description );
547
- } else {
548
- $button.html( itsec_page.translations.show_description );
549
- }
550
- }
551
- }
552
- },
553
-
554
- showGridSettingsModal: function( e ) {
555
- e.preventDefault();
556
-
557
- var $module = jQuery(this).parents( '.itsec-module-card' ),
558
- $settingsContainer = $module.find( '.itsec-module-settings-container' ),
559
- $modalBackground = jQuery( '.itsec-modal-background' );
560
-
561
- $module.show();
562
-
563
- $modalBackground.fadeIn();
564
- $settingsContainer.fadeIn( 200 );
565
-
566
- jQuery( 'body' ).addClass( 'itsec-modal-open' );
567
-
568
- var $highlighted = jQuery( '.itsec-highlighted-setting', $module ).first();
569
-
570
- if ( $highlighted.length ) {
571
- jQuery( '.itsec-module-settings-content-container', $module ).scrollTo( $highlighted, 'swing', {
572
- offset: { top: -20 },
573
- onAfter: function() {
574
- var $el = jQuery( 'input[type!="button"], textarea, select', $highlighted ).not( ':hidden' ).first();
575
- itsecSettingsPage.focus( $el, $highlighted );
576
- }
577
- } );
578
- } else {
579
- var $el = jQuery( 'input[type!="button"], textarea, select', $settingsContainer ).not( ':hidden' ).first();
580
- itsecSettingsPage.focus( $el, $settingsContainer );
581
- }
582
- },
583
-
584
- focus: function( $el, $fallback ) {
585
- if ( itsecSettingsPage.isElementVisible( $el ) && jQuery( window ).height() > 800 ) {
586
- $el.focus();
587
- } else {
588
- $fallback.prop( 'tabindex', -1 ).focus();
589
- }
590
- },
591
-
592
- isElementVisible: function( $el ) {
593
-
594
- var $window = jQuery( window ), height = $window.height(), width = $window.width(), offset = $el.offset();
595
-
596
- if ( ! $el || ! offset ) {
597
- return false;
598
- }
599
-
600
- return offset.top < height && offset.left < width;
601
- },
602
-
603
- closeGridSettingsModal: function( e ) {
604
- if ( 'undefined' !== typeof e ) {
605
- e.preventDefault();
606
-
607
- // For keyup events, only process esc
608
- if ( 'keyup' === e.type && 27 !== e.which ) {
609
- return;
610
- }
611
- }
612
-
613
- jQuery( '.itsec-modal-background' ).fadeOut();
614
- jQuery( '.itsec-module-settings-container' ).fadeOut( 200 );
615
- jQuery( 'body' ).removeClass( 'itsec-modal-open' );
616
-
617
- if ( 'undefined' === typeof e || 'popstate' !== e.type ) {
618
- var module_type = itsecSettingsPage.getUrlParameter( 'module_type' );
619
- if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
620
- module_type = 'recommended';
621
- }
622
- window.history.pushState( {'module':'', 'module_type':module_type}, module_type, '?page=itsec&module_type=' + module_type );
623
- }
624
-
625
- if ( jQuery( '#search' ).val().length ) {
626
- jQuery( '#search' ).focus();
627
- }
628
- },
629
-
630
- toggleModuleActivation: function( e ) {
631
- e.preventDefault();
632
- e.stopPropagation();
633
-
634
- var $button = jQuery(this),
635
- $card = $button.parents( '.itsec-module-card' ),
636
- $buttons = $card.find( '.itsec-toggle-activation' ),
637
- module = $card.attr( 'id' ).replace( 'itsec-module-card-', '' );
638
-
639
- $buttons.prop( 'disabled', true );
640
-
641
- if ( $button.html() == itsec_page.translations.activate ) {
642
- var method = 'activate';
643
- } else {
644
- var method = 'deactivate';
645
- }
646
-
647
- itsecSettingsPage.sendAJAXRequest( module, method, {}, itsecSettingsPage.toggleModuleActivationCallback );
648
- },
649
-
650
- setModuleToActive: function( module ) {
651
- var args = {
652
- 'module': module,
653
- 'method': 'activate',
654
- 'errors': []
655
- };
656
-
657
- itsecSettingsPage.toggleModuleActivationCallback( args );
658
- },
659
-
660
- setModuleToInactive: function( module ) {
661
- var args = {
662
- 'module': module,
663
- 'method': 'deactivate',
664
- 'errors': []
665
- };
666
-
667
- itsecSettingsPage.toggleModuleActivationCallback( args );
668
- },
669
-
670
- toggleModuleActivationCallback: function( results ) {
671
- var module = results.module;
672
- var method = results.method;
673
-
674
- var $card = jQuery( '#itsec-module-card-' + module ),
675
- $buttons = $card.find( '.itsec-toggle-activation' )
676
-
677
- if ( results.errors.length > 0 ) {
678
- $buttons
679
- .html( itsec_page.translations.error )
680
- .addClass( 'button-secondary' )
681
- .removeClass( 'button-primary' );
682
-
683
- setTimeout( function() {
684
- itsecSettingsPage.isModuleActive( module );
685
- }, 1000 );
686
-
687
- return;
688
- }
689
-
690
- if ( 'activate' === method ) {
691
- $buttons
692
- .html( itsec_page.translations.deactivate )
693
- .addClass( 'button-secondary' )
694
- .removeClass( 'button-primary' )
695
- .prop( 'disabled', false );
696
-
697
- $card
698
- .addClass( 'itsec-module-type-enabled' )
699
- .removeClass( 'itsec-module-type-disabled' );
700
-
701
- var newToggleSettingsLabel = itsec_page.translations.show_settings;
702
- } else {
703
- $buttons
704
- .html( itsec_page.translations.activate )
705
- .addClass( 'button-primary' )
706
- .removeClass( 'button-secondary' )
707
- .prop( 'disabled', false );
708
-
709
- $card
710
- .addClass( 'itsec-module-type-disabled' )
711
- .removeClass( 'itsec-module-type-enabled' );
712
-
713
- var newToggleSettingsLabel = itsec_page.translations.show_description;
714
- }
715
-
716
- $card.find( '.itsec-toggle-settings' ).html( newToggleSettingsLabel );
717
-
718
- var enabledCount = jQuery( '.itsec-module-type-enabled' ).length,
719
- disabledCount = jQuery( '.itsec-module-type-disabled' ).length;
720
-
721
- jQuery( '#itsec-module-filter-enabled .count' ).html( '(' + enabledCount + ')' );
722
- jQuery( '#itsec-module-filter-disabled .count' ).html( '(' + disabledCount + ')' );
723
-
724
-
725
- itsecSettingsPage.showErrors( results.warnings, results.module, 'closed', 'warning' );
726
- itsecSettingsPage.showMessages( results.messages, results.module, 'closed' );
727
- itsecSettingsPage.showMessages( results.infos, results.module, 'closed', 'info' );
728
- },
729
-
730
- isModuleActive: function( module ) {
731
- var data = {
732
- 'module': module,
733
- 'method': 'is_active'
734
- };
735
-
736
- itsecSettingsPage.sendAJAXRequest( module, 'is_active', {}, itsecSettingsPage.isModuleActiveCallback );
737
- },
738
-
739
- isModuleActiveCallback: function( results ) {
740
- if ( true === results.response ) {
741
- results.method = 'activate';
742
- } else if ( false === results.response ) {
743
- results.method = 'deactivate';
744
- } else {
745
- return;
746
- }
747
-
748
- itsecSettingsPage.toggleModuleActivationCallback( results );
749
- },
750
-
751
- reloadModule: function( module ) {
752
- if ( module.preventDefault ) {
753
- module.preventDefault();
754
-
755
- module = jQuery(this).parents( '.itsec-module-card' ).attr( 'id' ).replace( 'itsec-module-card-', '' );
756
- }
757
-
758
- var method = 'get_refreshed_module_settings';
759
- var data = {};
760
-
761
- itsecSettingsPage.sendAJAXRequest( module, method, data, function( results ) {
762
- if ( results.success && results.response ) {
763
- var $card = jQuery( '#itsec-module-card-' + module );
764
- var isHidden = $card.is( ':hidden' );
765
-
766
- jQuery( '.itsec-module-settings-content-main', $card ).html( results.response );
767
-
768
- if ( isHidden ) {
769
- $card.hide();
770
- } else {
771
- jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
772
- }
773
- } else if ( results.errors && results.errors.length > 0 ) {
774
- itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
775
- } else if ( results.warnings && results.warnings.length > 0 ) {
776
- itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
777
- }
778
-
779
- itsecSettingsPage.events.trigger( 'moduleReloaded', module );
780
-
781
- itsecSettingsPage.makeNoticesDismissible();
782
- } );
783
- },
784
-
785
- reloadAllModules: function( _, initialResponse) {
786
- itsecSettingsPage.sendAJAXRequest( '#', 'get_refreshed_module_form', null, function ( response ) {
787
-
788
- if ( ! response.success || response.errors.length ) {
789
- return;
790
- }
791
-
792
- var $open;
793
-
794
- if ( jQuery( 'body' ).hasClass( 'itsec-modal-open' ) ) {
795
- var $newModules = jQuery( response.response ), $cardsList = jQuery( '.itsec-module-cards' );
796
- $open = jQuery( '.itsec-module-settings-container:visible' ).parents( '.itsec-module-card' );
797
-
798
- jQuery( '.itsec-module-card', $newModules ).each( function () {
799
- var $new = jQuery( this ), $current = jQuery( '#' + $new.attr( 'id' ), $cardsList );
800
-
801
- if ( $new.attr( 'id' ).length && $new.attr( 'id' ) === $open.attr( 'id' ) ) {
802
- jQuery( '.itsec-module-settings-content-main', $current ).html( jQuery( '.itsec-module-settings-content-main', $new ).html() );
803
- } else {
804
- jQuery( '.itsec-module-settings-container', $new ).hide();
805
- $current.replaceWith( $new );
806
- }
807
- } );
808
-
809
- } else {
810
- jQuery( '.itsec-module-cards-container' ).html( response.response );
811
- }
812
-
813
- itsecSettingsPage.initFilters();
814
-
815
- if ( ! $open ) {
816
- jQuery( '.itsec-module-settings-container' ).hide();
817
- }
818
-
819
- if ( initialResponse ) {
820
- itsecSettingsPage.showMessages( initialResponse.messages, initialResponse.module, $open ? 'open' : 'closed' );
821
- itsecSettingsPage.showMessages( initialResponse.infos, initialResponse.module, $open ? 'open' : 'closed', 'info' );
822
- itsecSettingsPage.showErrors( initialResponse.errors, initialResponse.module, $open ? 'open' : 'closed' );
823
- itsecSettingsPage.showErrors( initialResponse.warnings, initialResponse.module, $open ? 'open' : 'closed', 'warning' );
824
- }
825
-
826
- itsecSettingsPage.makeNoticesDismissible();
827
- itsecSettingsPage.events.trigger( 'modulesReloaded', initialResponse );
828
- } );
829
- },
830
-
831
- reloadWidget: function( widget ) {
832
- var method = 'get_refreshed_widget_settings';
833
- var data = {};
834
-
835
- itsecSettingsPage.sendAJAXRequest( module, method, data, function( results ) {
836
- if ( results.success && results.response ) {
837
- jQuery( '#itsec-sidebar-widget-' + module + ' .inside' ).html( results.response );
838
- } else {
839
- itsecSettingsPage.showErrors( results.errors, results.module, 'closed' );
840
- itsecSettingsPage.showErrors( results.warnings, results.module, 'closed', 'warning' );
841
- }
842
- } );
843
- },
844
-
845
- sendAJAXRequest: function( module, method, data, callback ) {
846
- var postData = {
847
- 'action': itsec_page.ajax_action,
848
- 'nonce': itsec_page.ajax_nonce,
849
- 'module': module,
850
- 'method': method,
851
- 'data': data,
852
- };
853
-
854
- jQuery.post( ajaxurl, postData )
855
- .always(function( a, status, b ) {
856
- itsecSettingsPage.processAjaxResponse( a, status, b, module, method, data, callback );
857
- });
858
- },
859
-
860
- processAjaxResponse: function( a, status, b, module, method, data, callback ) {
861
- var results = {
862
- 'module': module,
863
- 'method': method,
864
- 'data': data,
865
- 'status': status,
866
- 'jqxhr': null,
867
- 'success': false,
868
- 'response': null,
869
- 'errors': [],
870
- 'warnings': [],
871
- 'messages': [],
872
- 'infos': [],
873
- 'functionCalls': [],
874
- 'redirect': false,
875
- 'closeModal': true
876
- };
877
-
878
-
879
- if ( 'ITSEC_Response' === a.source && 'undefined' !== a.response ) {
880
- // Successful response with a valid format.
881
- results.jqxhr = b;
882
- results.success = a.success;
883
- results.response = a.response;
884
- results.errors = a.errors;
885
- results.warnings = a.warnings;
886
- results.messages = a.messages;
887
- results.infos = a.infos;
888
- results.functionCalls = a.functionCalls;
889
- results.redirect = a.redirect;
890
- results.closeModal = a.closeModal;
891
- } else if ( a.responseText ) {
892
- // Failed response.
893
- results.jqxhr = a;
894
- var errorThrown = b;
895
-
896
- if ( 'undefined' === typeof results.jqxhr.status ) {
897
- results.jqxhr.status = -1;
898
- }
899
-
900
- if ( 'timeout' === status ) {
901
- var error = itsec_page.translations.ajax_timeout;
902
- } else if ( 'parsererror' === status ) {
903
- var error = itsec_page.translations.ajax_parsererror;
904
- } else if ( 403 == results.jqxhr.status ) {
905
- var error = itsec_page.translations.ajax_forbidden;
906
- } else if ( 404 == results.jqxhr.status ) {
907
- var error = itsec_page.translations.ajax_not_found;
908
- } else if ( 500 == results.jqxhr.status ) {
909
- var error = itsec_page.translations.ajax_server_error;
910
- } else {
911
- var error = itsec_page.translations.ajax_unknown;
912
- }
913
-
914
- error = error.replace( '%1$s', status );
915
- error = error.replace( '%2$s', errorThrown );
916
-
917
- results.errors = [ error ];
918
- } else {
919
- // Successful response with an invalid format.
920
- results.jqxhr = b;
921
-
922
- results.response = a;
923
- results.errors = [ itsec_page.translations.ajax_invalid ];
924
- }
925
-
926
-
927
- if ( results.redirect ) {
928
- window.location = results.redirect;
929
- }
930
-
931
-
932
- if ( 'function' === typeof callback ) {
933
- callback( results );
934
- } else if ( 'function' === typeof console.log ) {
935
- console.log( 'ERROR: Unable to handle settings AJAX request due to an invalid callback:', callback, {'data': postData, 'results': results} );
936
- }
937
-
938
-
939
- if ( results.functionCalls ) {
940
- for ( var i = 0; i < results.functionCalls.length; i++ ) {
941
- if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof itsecSettingsPage[results.functionCalls[i][0]] ) {
942
- itsecSettingsPage[results.functionCalls[i][0]]( results.functionCalls[i][1], results );
943
- } else if ( 'string' === typeof results.functionCalls[i] && 'function' === typeof window[results.functionCalls[i]] ) {
944
- window[results.functionCalls[i]]();
945
- } else if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof window[results.functionCalls[i][0]] ) {
946
- window[results.functionCalls[i][0]]( results.functionCalls[i][1] );
947
- } else if ( 'function' === typeof console.log ) {
948
- console.log( 'ERROR: Unable to call missing function:', results.functionCalls[i] );
949
- }
950
- }
951
- }
952
- },
953
-
954
- sendModuleAJAXRequest: function( module, data, callback ) {
955
- itsecSettingsPage.sendAJAXRequest( module, 'handle_module_request', data, callback );
956
- },
957
-
958
- sendWidgetAJAXRequest: function( widget, data, callback ) {
959
- itsecSettingsPage.sendAJAXRequest( widget, 'handle_widget_request', data, callback );
960
- },
961
-
962
- getUrlParameter: function( name ) {
963
- var pageURL = decodeURIComponent( window.location.search.substring( 1 ) ),
964
- URLParameters = pageURL.split( '&' ),
965
- parameterName,
966
- i;
967
-
968
- // Loop through all parameters
969
- for ( i = 0; i < URLParameters.length; i++ ) {
970
- parameterName = URLParameters[i].split( '=' );
971
-
972
- // If this is the parameter we're looking for
973
- if ( parameterName[0] === name ) {
974
- // Return the value or true if there is no value
975
- return parameterName[1] === undefined ? true : parameterName[1];
976
- }
977
- }
978
- // If the requested parameter doesn't exist, return false
979
- return false;
980
- },
981
-
982
- // Make notices dismissible
983
- makeNoticesDismissible: function() {
984
- jQuery( '.notice.itsec-is-dismissible' ).each( function() {
985
- var $el = jQuery( this ),
986
- $button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
987
- btnText = itsec_page.translations.dismiss || '';
988
-
989
- // Don't rebind twice
990
- if ( jQuery( '.notice-dismiss', $el ).length ) {
991
- return;
992
- }
993
-
994
- // Ensure plain text
995
- $button.find( '.screen-reader-text' ).text( btnText );
996
- $button.on( 'click.wp-dismiss-notice', function( event ) {
997
- event.preventDefault();
998
-
999
- $el.trigger( 'itsec-dismiss-notice' );
1000
-
1001
- $el.fadeTo( 100, 0, function() {
1002
- $el.slideUp( 100, function() {
1003
- $el.remove();
1004
- });
1005
- });
1006
- });
1007
-
1008
- $el.append( $button );
1009
- });
1010
- },
1011
-
1012
- refreshPage: function() {
1013
- location.reload( true );
1014
- }
1015
- };
1016
-
1017
- jQuery(document).ready(function( $ ) {
1018
- itsecSettingsPage.init();
1019
-
1020
- if ( itsec_page.show_security_check ) {
1021
- jQuery( '.itsec-settings-view-toggle a.itsec-grid' ).trigger( 'click' );
1022
- jQuery( '#itsec-module-card-security-check .itsec-toggle-settings' ).trigger( 'click' );
1023
- }
1024
-
1025
-
1026
- jQuery( '.dialog' ).click( function ( event ) {
1027
- event.preventDefault();
1028
-
1029
- var target = jQuery( this ).attr( 'href' );
1030
- var title = jQuery( this ).parents( '.inside' ).siblings( 'h3.hndle' ).children( 'span' ).text();
1031
-
1032
- jQuery( '#' + target ).dialog( {
1033
- dialogClass : 'wp-dialog itsec-dialog itsec-dialog-logs',
1034
- modal : true,
1035
- closeOnEscape: true,
1036
- title : title,
1037
- height : ( jQuery( window ).height() * 0.8 ),
1038
- width : ( jQuery( window ).width() * 0.8 ),
1039
- open : function ( event, ui ) {
1040
- jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
1041
- jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
1042
- } );
1043
- }
1044
- } );
1045
-
1046
- jQuery( '.ui-dialog :button' ).blur();
1047
- } );
1048
-
1049
- var regex = /[^\w]/ig;
1050
-
1051
- var $search = $( '#search' ), $cardsContainer = $( '.itsec-module-cards' ),
1052
- $cards = $( '.itsec-module-card', $cardsContainer ),
1053
- $searchFilter = $( '#itsec-module-filter-search' ),
1054
- $currentFilter = $( '.itsec-feature-tabs .current' ).parent();
1055
-
1056
- itsecSettingsPage.events.on( 'modulesReloaded', function() {
1057
- $cardsContainer = $( '.itsec-module-cards' );
1058
- $cards = $( '.itsec-module-card', $cardsContainer );
1059
- } );
1060
-
1061
- $search.on( 'input', _.debounce( function () {
1062
- var query = $search.val().trim().replace( regex, ' ' );
1063
-
1064
- var $maybeCurrent = $( '.itsec-feature-tabs .current' ).parent();
1065
-
1066
- if ( $maybeCurrent && $maybeCurrent.prop( 'id' ) !== 'itsec-module-filter-search' ) {
1067
- $currentFilter = $maybeCurrent;
1068
- }
1069
-
1070
- $( '.itsec-highlighted-setting', $cards ).removeClass( 'itsec-highlighted-setting' );
1071
-
1072
- if ( !query.length ) {
1073
- $searchFilter.addClass( 'hide-if-js' );
1074
- $( 'a', $searchFilter ).removeClass( 'current' );
1075
- $( 'a', $currentFilter ).addClass( 'current' );
1076
-
1077
- var type = $currentFilter.prop( 'id' ).substr( 20 );
1078
-
1079
- if ( 'all' === type ) {
1080
- $cards.show();
1081
- } else {
1082
- $( '.itsec-module-type-' + type ).show();
1083
- $( '.itsec-module-card' ).not( '.itsec-module-type-' + type ).hide();
1084
- }
1085
-
1086
- return;
1087
- }
1088
-
1089
- var $titleMatches = $( ".itsec-module-card-content > h2:itsecContains('" + query + "')", $cards ),
1090
- $titleMatchesCards = $titleMatches.parents( '.itsec-module-card' );
1091
-
1092
- var $descriptionMatches = $( ".itsec-module-card-content > p:itsecContains('" + query + "')", $cards ),
1093
- $descriptionMatchesCards = $descriptionMatches.parents( '.itsec-module-card' );
1094
-
1095
- var $settingMatches = $( ".itsec-module-settings-container .form-table tr > th > label:itsecContains('" + query + "')", $cards ),
1096
- $settingMatchesCards = $settingMatches.parents( '.itsec-module-card' );
1097
-
1098
-
1099
- var $matches = $titleMatchesCards.add( $descriptionMatchesCards ).add( $settingMatchesCards );
1100
-
1101
- $searchFilter.removeClass( 'hide-if-js' );
1102
- $( 'a', $currentFilter ).removeClass( 'current' );
1103
- $( 'a', $searchFilter ).addClass( 'current' );
1104
- $( '.count', $searchFilter ).text( '(' + $matches.length + ')' );
1105
-
1106
- $cards.hide();
1107
- $matches.show();
1108
-
1109
- $settingMatches.parents( 'tr' ).addClass( 'itsec-highlighted-setting' );
1110
-
1111
- if ( $matches.length === 1 ) {
1112
- $( '.itsec-toggle-settings', $matches.first() ).click();
1113
- }
1114
- }, 250 ) );
1115
-
1116
- $.expr[":"].itsecContains = $.expr.createPseudo( function ( arg ) {
1117
- return function ( elem ) {
1118
- var candidate = $( elem ).text().toUpperCase().replace( regex, ' ' ), term = arg.toUpperCase();
1119
- var index = candidate.indexOf( term );
1120
-
1121
- if ( index === -1 ) {
1122
- return false;
1123
- }
1124
-
1125
- if ( index === 0 ) {
1126
- return true;
1127
- }
1128
-
1129
- var prior = candidate.charAt( index - 1 ), next = candidate.charAt( term.length + index );
1130
-
1131
- // full word
1132
- return prior === ' ' && ( next === ' ' || next === '' );
1133
- };
1134
- } );
1135
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/admin-pages/js/util.js CHANGED
@@ -54,20 +54,21 @@ var itsecUtil = {
54
 
55
  processAjaxResponse: function( a, status, b, module, method, data, callback ) {
56
  var results = {
57
- 'module': module,
58
- 'method': method,
59
- 'data': data,
60
- 'status': status,
61
- 'jqxhr': null,
62
- 'success': false,
63
- 'response': null,
64
- 'errors': [],
65
- 'warnings': [],
66
- 'messages': [],
67
- 'infos': [],
68
- 'functionCalls': [],
69
- 'redirect': false,
70
- 'closeModal': true
 
71
  };
72
 
73
 
@@ -81,6 +82,7 @@ var itsecUtil = {
81
  results.messages = a.messages;
82
  results.infos = a.infos;
83
  results.functionCalls = a.functionCalls;
 
84
  results.redirect = a.redirect;
85
  results.closeModal = a.closeModal;
86
  } else if ( a.responseText ) {
@@ -146,6 +148,30 @@ var itsecUtil = {
146
  }
147
  }
148
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  },
150
 
151
  getUrlParameter: function( name ) {
54
 
55
  processAjaxResponse: function( a, status, b, module, method, data, callback ) {
56
  var results = {
57
+ 'module': module,
58
+ 'method': method,
59
+ 'data': data,
60
+ 'status': status,
61
+ 'jqxhr': null,
62
+ 'success': false,
63
+ 'response': null,
64
+ 'errors': [],
65
+ 'warnings': [],
66
+ 'messages': [],
67
+ 'infos': [],
68
+ 'functionCalls': [],
69
+ 'storeDispatches': [],
70
+ 'redirect': false,
71
+ 'closeModal': true
72
  };
73
 
74
 
82
  results.messages = a.messages;
83
  results.infos = a.infos;
84
  results.functionCalls = a.functionCalls;
85
+ results.storeDispatches = a.storeDispatches;
86
  results.redirect = a.redirect;
87
  results.closeModal = a.closeModal;
88
  } else if ( a.responseText ) {
148
  }
149
  }
150
  }
151
+
152
+ itsecUtil.handleStoreDispatches( results.storeDispatches );
153
+ },
154
+
155
+ handleStoreDispatches: function( dispatches ) {
156
+ if ( !wp.data ) {
157
+ return;
158
+ }
159
+
160
+ for ( var i = 0; i < dispatches.length; i++ ) {
161
+ var dispatch = dispatches[ i ];
162
+ var key = dispatch.store,
163
+ action = dispatch.action,
164
+ args = dispatch.args;
165
+
166
+ var store = wp.data.dispatch( key );
167
+
168
+ if ( ! store ) {
169
+ console.warn( 'Unable to call store dispatch. The store does not exist.', dispatch );
170
+ continue;
171
+ }
172
+
173
+ store[ action ].apply( store, args );
174
+ }
175
  },
176
 
177
  getUrlParameter: function( name ) {
core/admin-pages/logs-list-table.php CHANGED
@@ -223,7 +223,7 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
223
  $filters = $this->get_raw_filters();
224
 
225
  if ( 'process' === $filters['type'] ) {
226
- $filters['type'] = 'process-start';
227
  }
228
 
229
  if ( 'all' === $filters['type'] ) {
@@ -234,11 +234,13 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
234
  }
235
  if ( ! $options['show_process'] ) {
236
  $type_not[] = 'process-start';
 
 
237
  }
238
 
239
  unset( $filters['type'] );
240
  } else if ( 'important' === $filters['type'] ) {
241
- $type_not = array( 'action', 'notice', 'debug', 'process-start' );
242
 
243
  unset( $filters['type'] );
244
  }
@@ -424,7 +426,7 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
424
  <label for="itsec-module-filter" class="screen-reader-text"><?php esc_html_e( 'Filter by Module', 'better-wp-security' ) ?></label>
425
  <select name="filters[]" id="itsec-module-filter">
426
  <option value=""><?php esc_html_e( 'All Modules', 'better-wp-security' ); ?></option>
427
- <?php foreach ( $this->get_modules() as $module => $label ): ?>
428
  <option value="module|<?php echo esc_attr( $module ) ?>" <?php selected( $module, $current ); ?>>
429
  <?php echo $label; // Expected to be escaped by modules. ?>
430
  </option>
@@ -444,49 +446,4 @@ final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
444
  public function no_items() {
445
  esc_html_e( 'No events.', 'better-wp-security' );
446
  }
447
-
448
- private function get_modules() {
449
- $columns = implode(', ', array(
450
- 'id',
451
- 'parent_id',
452
- 'module',
453
- 'type',
454
- 'code',
455
- 'timestamp',
456
- 'init_timestamp',
457
- 'remote_ip',
458
- 'user_id',
459
- 'url',
460
- 'memory_current',
461
- 'memory_peak',
462
- ) );
463
-
464
- global $wpdb;
465
-
466
- $items = $wpdb->get_results( "SELECT {$columns} FROM {$wpdb->prefix}itsec_logs GROUP BY `module`", ARRAY_A );
467
-
468
- if ( ! is_array( $items ) ) {
469
- return array();
470
- }
471
-
472
- $modules = array();
473
-
474
- foreach ( $items as $item ) {
475
- if ( false === strpos( $item['code'], '::' ) ) {
476
- $code = $item['code'];
477
- $data = array();
478
- } else {
479
- list( $code, $data ) = explode( '::', $item['code'], 2 );
480
- $data = explode( ',', $data );
481
- }
482
-
483
- $item['description'] = $item['code'];
484
- $item['module_display'] = $item['module'];
485
- $item = apply_filters( "itsec_logs_prepare_{$item['module']}_entry_for_list_display", $item, $code, $data );
486
-
487
- $modules[ $item['module'] ] = $item['module_display'];
488
- }
489
-
490
- return $modules;
491
- }
492
  }
223
  $filters = $this->get_raw_filters();
224
 
225
  if ( 'process' === $filters['type'] ) {
226
+ $filters['type'] = [ 'process-start', 'process-update', 'process-stop' ];
227
  }
228
 
229
  if ( 'all' === $filters['type'] ) {
234
  }
235
  if ( ! $options['show_process'] ) {
236
  $type_not[] = 'process-start';
237
+ $type_not[] = 'process-update';
238
+ $type_not[] = 'process-stop';
239
  }
240
 
241
  unset( $filters['type'] );
242
  } else if ( 'important' === $filters['type'] ) {
243
+ $type_not = array( 'action', 'notice', 'debug', 'process-start', 'process-update', 'process-stop' );
244
 
245
  unset( $filters['type'] );
246
  }
426
  <label for="itsec-module-filter" class="screen-reader-text"><?php esc_html_e( 'Filter by Module', 'better-wp-security' ) ?></label>
427
  <select name="filters[]" id="itsec-module-filter">
428
  <option value=""><?php esc_html_e( 'All Modules', 'better-wp-security' ); ?></option>
429
+ <?php foreach ( ITSEC_Log_Util::get_modules() as $module => $label ): ?>
430
  <option value="module|<?php echo esc_attr( $module ) ?>" <?php selected( $module, $current ); ?>>
431
  <?php echo $label; // Expected to be escaped by modules. ?>
432
  </option>
446
  public function no_items() {
447
  esc_html_e( 'No events.', 'better-wp-security' );
448
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  }
core/admin-pages/module-settings.php CHANGED
@@ -14,6 +14,7 @@
14
  * @property-read string $upsell_url
15
  * @property-read bool $information_only
16
  * @property-read string $status
 
17
  */
18
  class ITSEC_Module_Settings_Page {
19
  /**
@@ -113,6 +114,12 @@ class ITSEC_Module_Settings_Page {
113
  */
114
  protected $status = '';
115
 
 
 
 
 
 
 
116
 
117
  /**
118
  * Constructor.
@@ -139,7 +146,7 @@ class ITSEC_Module_Settings_Page {
139
  * @return mixed Property.
140
  */
141
  public function __get( $name ) {
142
- if ( in_array( $name, array( 'id', 'title', 'description', 'type', 'pro', 'can_save', 'redraw_on_save', 'upsell', 'upsell_url', 'information_only', 'status' ) ) ) {
143
  return $this->$name;
144
  }
145
 
14
  * @property-read string $upsell_url
15
  * @property-read bool $information_only
16
  * @property-read string $status
17
+ * @property-read string $documentation
18
  */
19
  class ITSEC_Module_Settings_Page {
20
  /**
114
  */
115
  protected $status = '';
116
 
117
+ /**
118
+ * Link to documentation for this module.
119
+ *
120
+ * @var string
121
+ */
122
+ protected $documentation = '';
123
 
124
  /**
125
  * Constructor.
146
  * @return mixed Property.
147
  */
148
  public function __get( $name ) {
149
+ if ( in_array( $name, array( 'id', 'title', 'description', 'type', 'pro', 'can_save', 'redraw_on_save', 'upsell', 'upsell_url', 'information_only', 'status', 'documentation' ) ) ) {
150
  return $this->$name;
151
  }
152
 
core/admin-pages/page-debug.php CHANGED
@@ -80,8 +80,7 @@ final class ITSEC_Debug_Page {
80
  ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-module-request-missing-data', __( 'The server did not receive a valid request. The required "data" argument for the module is missing. Please try again.', 'better-wp-security' ) ) );
81
  }
82
  } elseif ( 'reset_scheduler' === $method ) {
83
- ITSEC_Core::get_scheduler()->uninstall();
84
- ITSEC_Core::get_scheduler()->register_events();
85
  ITSEC_Response::set_response( $this->get_events_table() );
86
  ITSEC_Response::set_success( true );
87
  ITSEC_Response::add_message( __( 'Scheduler reset.', 'better-wp-security' ) );
@@ -272,7 +271,7 @@ final class ITSEC_Debug_Page {
272
  $wp_config = array(
273
  'Version' => get_bloginfo( 'version' ),
274
  'Language' => defined( 'WPLANG' ) && WPLANG ? WPLANG : 'en_US',
275
- 'Permalink' => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default',
276
  'Theme' => wp_get_theme()->Name . ' ' . wp_get_theme()->Version,
277
  'Show on Front' => get_option( 'show_on_front' )
278
  );
@@ -287,9 +286,9 @@ final class ITSEC_Debug_Page {
287
 
288
  $wp_config['ABSPATH'] = ABSPATH;
289
  $wp_config['Table Prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'Too long' : 'Acceptable' );
290
- $wp_config['WP_DEBUG'] = defined( 'WP_DEBUG' ) ? WP_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
291
- $wp_config['WP_DEBUG_LOG'] = defined( 'WP_DEBUG_LOG' ) ? WP_DEBUG_LOG ? 'Enabled' : 'Disabled' : 'Not set';
292
- $wp_config['SCRIPT_DEBUG'] = defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG ? 'Enabled' : 'Disabled' : 'Not set';
293
  $wp_config['Object Cache'] = wp_using_ext_object_cache() ? 'Yes' : 'No';
294
  $wp_config['Memory Limit'] = WP_MEMORY_LIMIT;
295
  $info['WordPress Configuration'] = $wp_config;
@@ -312,6 +311,7 @@ final class ITSEC_Debug_Page {
312
  'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION',
313
  'ITSEC_DISABLE_PASSWORD_STRENGTH',
314
  'ITSEC_DISABLE_INACTIVE_USER_CHECK',
 
315
  );
316
 
317
  ITSEC_Lib::load( 'feature-flags' );
@@ -328,8 +328,15 @@ final class ITSEC_Debug_Page {
328
 
329
  foreach ( $defines as $define ) {
330
  if ( defined( $define ) ) {
331
- $value = constant( $define );
332
- $info['iThemes Security'][ $define ] = $value === true ? 'Enabled' : $value === false ? 'Disabled' : $value;
 
 
 
 
 
 
 
333
  }
334
  }
335
 
80
  ITSEC_Response::add_error( new WP_Error( 'itsec-debug-page-module-request-missing-data', __( 'The server did not receive a valid request. The required "data" argument for the module is missing. Please try again.', 'better-wp-security' ) ) );
81
  }
82
  } elseif ( 'reset_scheduler' === $method ) {
83
+ ITSEC_Core::get_scheduler()->reset();
 
84
  ITSEC_Response::set_response( $this->get_events_table() );
85
  ITSEC_Response::set_success( true );
86
  ITSEC_Response::add_message( __( 'Scheduler reset.', 'better-wp-security' ) );
271
  $wp_config = array(
272
  'Version' => get_bloginfo( 'version' ),
273
  'Language' => defined( 'WPLANG' ) && WPLANG ? WPLANG : 'en_US',
274
+ 'Permalink' => get_option( 'permalink_structure' ) ?: 'Default',
275
  'Theme' => wp_get_theme()->Name . ' ' . wp_get_theme()->Version,
276
  'Show on Front' => get_option( 'show_on_front' )
277
  );
286
 
287
  $wp_config['ABSPATH'] = ABSPATH;
288
  $wp_config['Table Prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' Status: ' . ( strlen( $wpdb->prefix ) > 16 ? 'Too long' : 'Acceptable' );
289
+ $wp_config['WP_DEBUG'] = defined( 'WP_DEBUG' ) ? ( WP_DEBUG ? 'Enabled' : 'Disabled' ) : 'Not set';
290
+ $wp_config['WP_DEBUG_LOG'] = defined( 'WP_DEBUG_LOG' ) ? ( WP_DEBUG_LOG ? 'Enabled' : 'Disabled' ) : 'Not set';
291
+ $wp_config['SCRIPT_DEBUG'] = defined( 'SCRIPT_DEBUG' ) ? ( SCRIPT_DEBUG ? 'Enabled' : 'Disabled' ) : 'Not set';
292
  $wp_config['Object Cache'] = wp_using_ext_object_cache() ? 'Yes' : 'No';
293
  $wp_config['Memory Limit'] = WP_MEMORY_LIMIT;
294
  $info['WordPress Configuration'] = $wp_config;
311
  'ITSEC_DISABLE_AUTOMATIC_REMOTE_IP_DETECTION',
312
  'ITSEC_DISABLE_PASSWORD_STRENGTH',
313
  'ITSEC_DISABLE_INACTIVE_USER_CHECK',
314
+ 'ITSEC_SHOW_FEATURE_FLAGS',
315
  );
316
 
317
  ITSEC_Lib::load( 'feature-flags' );
328
 
329
  foreach ( $defines as $define ) {
330
  if ( defined( $define ) ) {
331
+ $value = constant( $define );
332
+
333
+ if ( true === $value ) {
334
+ $value = 'Enabled';
335
+ } elseif ( false === $value ) {
336
+ $value = 'Disabled';
337
+ }
338
+
339
+ $info['iThemes Security'][ $define ] = $value;
340
  }
341
  }
342
 
core/admin-pages/page-settings.php CHANGED
@@ -169,9 +169,13 @@ final class ITSEC_Settings_Page {
169
  ITSEC_Modules::load_module_file( 'active.php', $module );
170
  }
171
 
 
 
172
  ITSEC_Response::maybe_flag_new_notifications_available();
173
  } else if ( 'deactivate' === $method ) {
174
  ITSEC_Response::set_response( ITSEC_Modules::deactivate( $module ) );
 
 
175
  ITSEC_Response::maybe_flag_new_notifications_available();
176
  } else if ( 'is_active' === $method ) {
177
  ITSEC_Response::set_response( ITSEC_Modules::is_active( $module ) );
@@ -478,7 +482,7 @@ final class ITSEC_Settings_Page {
478
  <input type="search" placeholder="<?php esc_attr_e( 'Search Modules', 'better-wp-security' ); ?>" id="search" spellcheck="false" autocomplete="off" autofill="off" x-autocomplete="false">
479
  </div>
480
  <ul class="subsubsub itsec-feature-tabs hide-if-no-js">
481
- <?php echo implode( $feature_tabs, " |</li>\n" ) . "</li>\n"; ?>
482
  <li class="itsec-module-filter hide-if-js" id="itsec-module-filter-search">| <a><?php esc_html_e( 'Search', 'better-wp-security' ); ?></a> <span class="count"></span></li>
483
  </ul>
484
  </div>
@@ -598,32 +602,48 @@ final class ITSEC_Settings_Page {
598
  <?php echo esc_html( $module->title ); ?>
599
  <?php do_action( 'itsec_module_settings_after_title', $id ); ?>
600
  </h3>
601
- <div class="itsec-module-messages-container"></div>
602
  <div class="itsec-module-settings-content-main">
603
  <?php $this->get_module_settings( $id, $form, true ); ?>
604
  </div>
605
  </div>
606
  </div>
607
  <div class="itsec-list-content-footer hide-if-no-js">
608
- <?php if ( $module->can_save ) : ?>
609
- <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
610
- <?php endif; ?>
611
- <button class="button button-secondary align-left itsec-module-settings-cancel"><?php _e( 'Cancel', 'better-wp-security' ); ?></button>
 
 
 
 
 
 
 
 
612
  </div>
613
  <div class="itsec-modal-content-footer">
614
- <?php if ( $module->enabled || $module->always_active || $module->information_only ) : ?>
615
- <?php if ( ! $module->always_active && ! $module->information_only ) : ?>
616
- <button class="button button-secondary align-right itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
 
 
617
  <?php endif; ?>
618
- <?php else : ?>
619
- <button class="button button-primary align-right itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
620
- <?php endif; ?>
621
 
622
- <?php if ( $module->can_save ) : ?>
623
- <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
624
- <?php else : ?>
625
- <button class="button button-primary align-left itsec-close-modal"><?php echo $this->translations['close_settings']; ?></button>
626
- <?php endif; ?>
 
 
 
 
 
 
 
 
627
  </div>
628
  </div>
629
  <?php endif; ?>
169
  ITSEC_Modules::load_module_file( 'active.php', $module );
170
  }
171
 
172
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/user-groups', 'fetchGroupsSettings' );
173
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/core', 'fetchIndex', [ true ] );
174
  ITSEC_Response::maybe_flag_new_notifications_available();
175
  } else if ( 'deactivate' === $method ) {
176
  ITSEC_Response::set_response( ITSEC_Modules::deactivate( $module ) );
177
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/user-groups', 'fetchGroupsSettings' );
178
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/core', 'fetchIndex', [ true ] );
179
  ITSEC_Response::maybe_flag_new_notifications_available();
180
  } else if ( 'is_active' === $method ) {
181
  ITSEC_Response::set_response( ITSEC_Modules::is_active( $module ) );
482
  <input type="search" placeholder="<?php esc_attr_e( 'Search Modules', 'better-wp-security' ); ?>" id="search" spellcheck="false" autocomplete="off" autofill="off" x-autocomplete="false">
483
  </div>
484
  <ul class="subsubsub itsec-feature-tabs hide-if-no-js">
485
+ <?php echo implode( " |</li>\n", $feature_tabs ) . "</li>\n"; ?>
486
  <li class="itsec-module-filter hide-if-js" id="itsec-module-filter-search">| <a><?php esc_html_e( 'Search', 'better-wp-security' ); ?></a> <span class="count"></span></li>
487
  </ul>
488
  </div>
602
  <?php echo esc_html( $module->title ); ?>
603
  <?php do_action( 'itsec_module_settings_after_title', $id ); ?>
604
  </h3>
605
+ <div class="itsec-module-messages-container" id="itsec-module-messages-container-<?php echo esc_attr( $id ); ?>"></div>
606
  <div class="itsec-module-settings-content-main">
607
  <?php $this->get_module_settings( $id, $form, true ); ?>
608
  </div>
609
  </div>
610
  </div>
611
  <div class="itsec-list-content-footer hide-if-no-js">
612
+ <div class="itsec-module-footer-actions itsec-module-footer-actions--left">
613
+ <?php if ( $module->can_save ) : ?>
614
+ <button class="button button-primary itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
615
+ <?php endif; ?>
616
+ <button class="button button-secondary itsec-module-settings-cancel"><?php _e( 'Cancel', 'better-wp-security' ); ?></button>
617
+ </div>
618
+
619
+ <div class="itsec-module-footer-actions itsec-module-footer-actions--right">
620
+ <?php if ( $module->documentation ): ?>
621
+ <a href="<?php echo esc_url( $module->documentation ); ?>" class="itsec-module-documentation"><?php esc_html_e( 'Documentation', 'better-wp-security' ); ?></a>
622
+ <?php endif; ?>
623
+ </div>
624
  </div>
625
  <div class="itsec-modal-content-footer">
626
+ <div class="itsec-module-footer-actions itsec-module-footer-actions--left">
627
+ <?php if ( $module->can_save ) : ?>
628
+ <button class="button button-primary itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
629
+ <?php else : ?>
630
+ <button class="button button-primary itsec-close-modal"><?php echo $this->translations['close_settings']; ?></button>
631
  <?php endif; ?>
632
+ </div>
 
 
633
 
634
+ <div class="itsec-module-footer-actions itsec-module-footer-actions--right">
635
+ <?php if ( $module->documentation ): ?>
636
+ <a href="<?php echo esc_url( $module->documentation ); ?>" class="itsec-module-documentation"><?php esc_html_e( 'Documentation', 'better-wp-security' ); ?></a>
637
+ <?php endif; ?>
638
+
639
+ <?php if ( $module->enabled || $module->always_active || $module->information_only ) : ?>
640
+ <?php if ( ! $module->always_active && ! $module->information_only ) : ?>
641
+ <button class="button button-secondary itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
642
+ <?php endif; ?>
643
+ <?php else : ?>
644
+ <button class="button button-primary itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
645
+ <?php endif; ?>
646
+ </div>
647
  </div>
648
  </div>
649
  <?php endif; ?>
core/composer.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "ithemes/ithemes-security-core",
3
+ "description": "iThemes Security Core",
4
+ "type": "project",
5
+ "require": {
6
+ "pimple/pimple": "^3.2"
7
+ },
8
+ "license": "GPL-2.0-or-later",
9
+ "authors": [
10
+ {
11
+ "name": "Timothy Jacobs",
12
+ "email": "timothy@ithemes.com"
13
+ },
14
+ {
15
+ "name": "Chris Jean",
16
+ "email": "chris@ithemes.com"
17
+ }
18
+ ],
19
+ "minimum-stability": "stable",
20
+ "autoload": {
21
+ "classmap": [
22
+ "Exception/",
23
+ "Contracts/",
24
+ "modules/",
25
+ "lib"
26
+ ],
27
+ "exclude-from-classmap": [
28
+ "lib/itsec-zxcvbn-php"
29
+ ]
30
+ }
31
+ }
core/core.php CHANGED
@@ -1,14 +1,16 @@
1
  <?php
2
 
 
 
3
  /**
4
  * iThemes Security Core.
5
  *
6
  * Core class for iThemes Security sets up globals and other items and dispatches modules.
7
  *
8
- * @package iThemes_Security
9
- *
10
  * @since 4.0
11
  *
 
 
12
  * @global array $itsec_globals Global variables for use throughout iThemes Security.
13
  * @global object $itsec_lockout Class for handling lockouts.
14
  *
@@ -24,7 +26,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
24
  *
25
  * @access private
26
  */
27
- private $plugin_build = 4116;
28
 
29
  /**
30
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
@@ -37,6 +39,9 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
37
  private $notices_loaded = false;
38
  private $doing_data_upgrade = false;
39
 
 
 
 
40
  private
41
  $itsec_files,
42
  $itsec_notify,
@@ -51,7 +56,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
51
  $is_iwp_call,
52
  $wp_upload_dir,
53
  $login_interstitial,
54
- $version;
 
55
 
56
 
57
  /**
@@ -59,7 +65,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
59
  *
60
  * @access private
61
  */
62
- private function __construct() {}
63
 
64
  /**
65
  * Function to instantiate our class and make it a singleton
@@ -78,7 +84,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
78
  * Creates all plugin globals, registers activation and related hooks,
79
  * loads the text domain and loads all plugin modules
80
  *
81
- * @since 4.0
82
  *
83
  * @access private
84
  *
@@ -89,10 +95,10 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
89
  public function init( $plugin_file, $plugin_name ) {
90
  global $itsec_globals, $itsec_lockout;
91
 
92
- $this->plugin_file = $plugin_file;
93
- $this->plugin_dir = dirname( $plugin_file ) . '/';
94
- $this->plugin_name = $plugin_name;
95
- $this->current_time = current_time( 'timestamp' );
96
  $this->current_time_gmt = current_time( 'timestamp', true );
97
 
98
  $itsec_globals = array(
@@ -110,6 +116,14 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
110
  require( $this->plugin_dir . 'core/modules.php' );
111
  require( $this->plugin_dir . 'core/lib.php' );
112
  require( $this->plugin_dir . 'core/lib/log.php' );
 
 
 
 
 
 
 
 
113
 
114
  add_action( 'itsec-register-modules', array( $this, 'register_modules' ) );
115
  ITSEC_Modules::init_modules();
@@ -127,35 +141,125 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
127
  require( $this->plugin_dir . 'core/lib/class-itsec-scheduler.php' );
128
  require( $this->plugin_dir . 'core/lib/class-itsec-job.php' );
129
 
130
- $this->itsec_files = ITSEC_Files::get_instance();
131
  $this->itsec_notify = new ITSEC_Notify();
132
- $itsec_lockout = new ITSEC_Lockout();
133
- $itsec_lockout->run();
134
 
135
  // Handle upgrade if needed.
136
- add_action( 'plugins_loaded', array( $this, 'handle_upgrade' ), -100, 0 );
 
137
 
138
- if ( is_admin() ) {
139
- require( $this->plugin_dir . 'core/admin-pages/init.php' );
 
 
 
 
 
 
 
 
140
 
141
- add_filter( 'plugin_action_links', array( $this, 'add_action_link' ), 10, 2 );
142
- add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 4 );
 
 
 
 
 
 
 
 
 
 
 
143
  }
144
 
 
 
 
145
 
146
- add_action( 'plugins_loaded', array( $this, 'continue_init' ), -90 );
147
- add_action( 'wp_login_failed', array( 'ITSEC_Lib', 'handle_wp_login_failed' ) );
 
 
 
 
 
148
 
149
- add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
 
150
 
151
- add_action( 'itsec_scheduler_register_events', array( $this, 'register_events' ) );
152
- add_action( 'itsec_scheduled_clear-locks', array( 'ITSEC_Lib', 'delete_expired_locks' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  }
154
 
155
  /**
156
  * Perform initialization that requires the plugins_loaded hook to be fired.
157
  */
158
  public function continue_init() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  $this->setup_scheduler();
160
  ITSEC_Modules::run_active_modules();
161
 
@@ -171,6 +275,23 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
171
  ITSEC_Lib_Remote_Messages::init();
172
  }
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  private function setup_scheduler() {
175
 
176
  if ( $this->scheduler ) {
@@ -194,6 +315,24 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
194
  self::get_scheduler()->run();
195
  }
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  /**
198
  * Retrieve the global instance of the files utility.
199
  *
@@ -201,6 +340,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
201
  */
202
  public static function get_itsec_files() {
203
  $self = self::get_instance();
 
204
  return $self->itsec_files;
205
  }
206
 
@@ -211,6 +351,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
211
  */
212
  public static function get_itsec_notify() {
213
  $self = self::get_instance();
 
214
  return $self->itsec_notify;
215
  }
216
 
@@ -275,9 +416,19 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
275
  */
276
  public static function get_sync_api() {
277
  $self = self::get_instance();
 
278
  return $self->sync_api;
279
  }
280
 
 
 
 
 
 
 
 
 
 
281
  /**
282
  * Register ITSEC verbs with sync.
283
  *
@@ -298,6 +449,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
298
  public function register_events( $scheduler ) {
299
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'clear-locks' );
300
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'health-check' );
 
301
  }
302
 
303
  /**
@@ -306,9 +458,11 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
306
  public function register_modules() {
307
  $path = dirname( __FILE__ );
308
 
 
309
  ITSEC_Modules::register_module( 'security-check', "$path/modules/security-check", 'always-active' );
310
  ITSEC_Modules::register_module( 'global', "$path/modules/global", 'always-active' );
311
  ITSEC_Modules::register_module( 'notification-center', "$path/modules/notification-center", 'always-active' );
 
312
  ITSEC_Modules::register_module( 'privacy', "$path/modules/privacy", 'always-active' );
313
  ITSEC_Modules::register_module( '404-detection', "$path/modules/404-detection" );
314
  ITSEC_Modules::register_module( 'admin-user', "$path/modules/admin-user", 'always-active' );
@@ -331,7 +485,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
331
 
332
  ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
333
 
334
- if ( ! defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS') || ! ITSEC_DISABLE_PASSWORD_REQUIREMENTS ) {
335
  ITSEC_Modules::register_module( 'password-requirements', "$path/modules/password-requirements/", 'always-active' );
336
  }
337
 
@@ -342,8 +496,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
342
  ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
343
  ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
344
  ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
345
- ITSEC_Modules::register_module( 'feature-flags', "$path/modules/feature-flags", 'always-active' );
346
  ITSEC_Modules::register_module( 'security-check-pro', "$path/modules/security-check-pro", self::is_pro() ? 'always-active' : 'default-inactive' );
 
347
 
348
  if ( ! ITSEC_Core::is_pro() ) {
349
  ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
@@ -405,22 +559,26 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
405
  * Dispatch a request to upgrade the data schema to another version.
406
  *
407
  * @param int|bool $build The version of the data storage format. Pass false to default to the current version.
 
 
408
  */
409
  public function handle_upgrade( $build = false ) {
410
 
411
- if ( func_num_args() === 0 && ITSEC_Modules::get_setting( 'global', 'build' ) >= $this->plugin_build ) {
412
- return;
413
  }
414
 
415
  $this->doing_data_upgrade = true;
416
 
417
  require_once( self::get_core_dir() . '/setup.php' );
418
- ITSEC_Setup::handle_upgrade( $build );
 
 
419
  }
420
 
421
  public static function handle_activation() {
422
  require_once( self::get_core_dir() . '/setup.php' );
423
- ITSEC_Setup::handle_activation();
424
  }
425
 
426
  public static function handle_deactivation() {
@@ -436,8 +594,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
436
  /**
437
  * Register a notice to be displayed in the WordPress admin.
438
  *
439
- * @param callable $callback Function that will render a notice.
440
- * @param bool $all_pages Display the notice on all pages or only on ITSEC, plugins, and upgrade page.
441
  */
442
  public static function add_notice( $callback, $all_pages = false ) {
443
  _deprecated_function( __METHOD__, '6.0.0', 'ITSEC_Lib_Admin_Notices::register' );
@@ -464,42 +622,57 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
464
  }
465
  }
466
 
467
- /**
468
- * Get the required capability to manage ITSEC.
469
- *
470
- * @return string
471
- */
472
- public static function get_required_cap() {
473
- return apply_filters( 'itsec_cap_required', is_multisite() ? 'manage_network_options' : 'manage_options' );
474
- }
475
-
476
- /**
477
- * Does the current user have permission to manage ITSEC.
478
- *
479
- * @return bool
480
- */
481
- public static function current_user_can_manage() {
482
- return current_user_can( self::get_required_cap() );
483
- }
484
-
485
  public static function get_plugin_file() {
486
  $self = self::get_instance();
 
487
  return $self->plugin_file;
488
  }
489
 
490
  public static function set_plugin_file( $plugin_file ) {
491
- $self = self::get_instance();
492
  $self->plugin_file = $plugin_file;
493
- $self->plugin_dir = dirname( $plugin_file ) . '/';
494
  }
495
 
 
 
 
 
 
496
  public static function get_plugin_build() {
497
  $self = self::get_instance();
 
498
  return $self->plugin_build;
499
  }
500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  public static function get_plugin_dir() {
502
  $self = self::get_instance();
 
503
  return $self->plugin_dir;
504
  }
505
 
@@ -509,6 +682,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
509
 
510
  public static function get_plugin_name() {
511
  $self = self::get_instance();
 
512
  return $self->plugin_name;
513
  }
514
 
@@ -562,6 +736,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
562
  */
563
  public static function get_current_time() {
564
  $self = self::get_instance();
 
565
  return $self->current_time;
566
  }
567
 
@@ -574,6 +749,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
574
  */
575
  public static function get_current_time_gmt() {
576
  $self = self::get_instance();
 
577
  return $self->current_time_gmt;
578
  }
579
 
@@ -584,6 +760,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
584
  */
585
  public static function get_time_offset() {
586
  $self = self::get_instance();
 
587
  return $self->current_time - $self->current_time_gmt;
588
  }
589
 
@@ -599,7 +776,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
599
  $filters = array();
600
 
601
  if ( $module ) {
602
- $filters[] = rawurlencode("module|{$module}");
603
  }
604
 
605
  if ( $type ) {
@@ -634,8 +811,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
634
  *
635
  * @param bool $interactive
636
  */
637
- public static function set_interactive( $interactive ) {
638
- $self = self::get_instance();
639
  $self->interactive = (bool) $interactive;
640
  }
641
 
@@ -646,6 +823,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
646
  */
647
  public static function is_interactive() {
648
  $self = self::get_instance();
 
649
  return $self->interactive;
650
  }
651
 
@@ -688,9 +866,9 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
688
  * This value is cached for both the lifetime of the request and possibly indefinitely when WordPress is
689
  * using an object cache.
690
  *
 
691
  * @see wp_upload_dir
692
  *
693
- * @return array
694
  */
695
  public static function get_wp_upload_dir() {
696
  $self = self::get_instance();
@@ -739,7 +917,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
739
  /**
740
  * Retrieve and/or create a directory for ITSEC to store data.
741
  *
742
- * @param string $dir Optionally specify an additional sub-directory.
743
  * @param bool $public Whether to get the public version of the directory.
744
  *
745
  * @return string
@@ -824,11 +1002,13 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
824
 
825
  if ( ! function_exists( 'rest_get_url_prefix' ) ) {
826
  $GLOBALS['__itsec_core_is_rest_api_request'] = false;
 
827
  return false;
828
  }
829
 
830
  if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
831
  $GLOBALS['__itsec_core_is_rest_api_request'] = true;
 
832
  return true;
833
  }
834
 
@@ -924,5 +1104,13 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
924
 
925
  return $self->version;
926
  }
 
 
 
 
 
 
 
 
927
  }
928
  }
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups;
4
+
5
  /**
6
  * iThemes Security Core.
7
  *
8
  * Core class for iThemes Security sets up globals and other items and dispatches modules.
9
  *
 
 
10
  * @since 4.0
11
  *
12
+ * @package iThemes_Security
13
+ *
14
  * @global array $itsec_globals Global variables for use throughout iThemes Security.
15
  * @global object $itsec_lockout Class for handling lockouts.
16
  *
26
  *
27
  * @access private
28
  */
29
+ private $plugin_build = 4118;
30
 
31
  /**
32
  * Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
39
  private $notices_loaded = false;
40
  private $doing_data_upgrade = false;
41
 
42
+ /** @var true|WP_Error|null */
43
+ private $setup_error;
44
+
45
  private
46
  $itsec_files,
47
  $itsec_notify,
56
  $is_iwp_call,
57
  $wp_upload_dir,
58
  $login_interstitial,
59
+ $version,
60
+ $importing = false;
61
 
62
 
63
  /**
65
  *
66
  * @access private
67
  */
68
+ private function __construct() { }
69
 
70
  /**
71
  * Function to instantiate our class and make it a singleton
84
  * Creates all plugin globals, registers activation and related hooks,
85
  * loads the text domain and loads all plugin modules
86
  *
87
+ * @since 4.0
88
  *
89
  * @access private
90
  *
95
  public function init( $plugin_file, $plugin_name ) {
96
  global $itsec_globals, $itsec_lockout;
97
 
98
+ $this->plugin_file = $plugin_file;
99
+ $this->plugin_dir = dirname( $plugin_file ) . '/';
100
+ $this->plugin_name = $plugin_name;
101
+ $this->current_time = current_time( 'timestamp' );
102
  $this->current_time_gmt = current_time( 'timestamp', true );
103
 
104
  $itsec_globals = array(
116
  require( $this->plugin_dir . 'core/modules.php' );
117
  require( $this->plugin_dir . 'core/lib.php' );
118
  require( $this->plugin_dir . 'core/lib/log.php' );
119
+ require( $this->plugin_dir . 'core/lib/class-itsec-mutex.php' );
120
+ require( $this->plugin_dir . 'core/rest.php' );
121
+
122
+ $this->setup_tables();
123
+
124
+ add_filter( 'user_has_cap', [ $this, 'user_has_cap' ], 10, 4 );
125
+
126
+ ( new ITSEC_REST() )->run();
127
 
128
  add_action( 'itsec-register-modules', array( $this, 'register_modules' ) );
129
  ITSEC_Modules::init_modules();
141
  require( $this->plugin_dir . 'core/lib/class-itsec-scheduler.php' );
142
  require( $this->plugin_dir . 'core/lib/class-itsec-job.php' );
143
 
144
+ $this->itsec_files = ITSEC_Files::get_instance();
145
  $this->itsec_notify = new ITSEC_Notify();
146
+ $itsec_lockout = new ITSEC_Lockout();
 
147
 
148
  // Handle upgrade if needed.
149
+ add_action( 'plugins_loaded', array( $this, 'handle_upgrade' ), - 100, 0 );
150
+ add_action( 'plugins_loaded', array( $this, 'continue_init' ), - 90 );
151
 
152
+ add_action( 'itsec_scheduler_register_events', array( $this, 'register_events' ) );
153
+ add_action( 'itsec_scheduled_clear-locks', array( 'ITSEC_Lib', 'delete_expired_locks' ) );
154
+ add_action( 'itsec_scheduled_clear-tokens', array( ITSEC_Lib_Opaque_Tokens::class, 'delete_expired_tokens' ) );
155
+ add_action( 'itsec_before_import', function () {
156
+ $this->importing = true;
157
+ } );
158
+ add_action( 'itsec_after_import', function () {
159
+ $this->importing = false;
160
+ } );
161
+ }
162
 
163
+ /**
164
+ * Handle dynamically granting the 'itsec_manage' cap to users in the necessary group.
165
+ *
166
+ * @param array $has_caps
167
+ * @param array $requested_caps
168
+ * @param array $args
169
+ * @param WP_User $user
170
+ *
171
+ * @return array
172
+ */
173
+ public function user_has_cap( $has_caps, $requested_caps, $args, $user ) {
174
+ if ( ! in_array( 'itsec_manage', $requested_caps, true ) ) {
175
+ return $has_caps;
176
  }
177
 
178
+ if ( isset( $has_caps['itsec_manage'] ) ) {
179
+ return $has_caps;
180
+ }
181
 
182
+ if (
183
+ is_wp_error( $this->setup_error ) ||
184
+ self::is_temp_disable_modules_set() ||
185
+ ! ITSEC_Modules::get_container()->has( User_Groups\Matcher::class ) ||
186
+ ! $this->has_valid_manage_groups()
187
+ ) {
188
+ $has_caps['itsec_manage'] = $user->has_cap( is_multisite() ? 'manage_network_options' : 'manage_options' );
189
 
190
+ return $has_caps;
191
+ }
192
 
193
+ $group = ITSEC_Modules::get_setting( 'global', 'manage_group' );
194
+ $matcher = ITSEC_Modules::get_container()->get( User_Groups\Matcher::class );
195
+
196
+ if ( ! $matcher->matches( User_Groups\Match_Target::for_user( $user ), $group ) ) {
197
+ return $has_caps;
198
+ }
199
+
200
+ $has_caps['itsec_manage'] = true;
201
+
202
+ return $has_caps;
203
+ }
204
+
205
+ /**
206
+ * Check if there are valid "Manage groups" selected.
207
+ *
208
+ * @return bool
209
+ */
210
+ private function has_valid_manage_groups() {
211
+ $source = ITSEC_Modules::get_container()->get( User_Groups\Matchables_Source::class );
212
+ $groups = ITSEC_Modules::get_setting( 'global', 'manage_group' );
213
+
214
+ foreach ( $groups as $group ) {
215
+ if ( $source->has( $group ) ) {
216
+ return true;
217
+ }
218
+ }
219
+
220
+ return false;
221
  }
222
 
223
  /**
224
  * Perform initialization that requires the plugins_loaded hook to be fired.
225
  */
226
  public function continue_init() {
227
+ global $itsec_lockout;
228
+
229
+ if ( is_wp_error( $this->setup_error ) ) {
230
+ if ( ! current_user_can( is_multisite() ? 'manage_network_options' : 'manage_options' ) ) {
231
+ return;
232
+ }
233
+
234
+ add_action( 'admin_notices', function () {
235
+ echo '<div class="notice notice-error">';
236
+ echo '<p>';
237
+ esc_html_e( 'Cannot run iThemes Security. Error encountered during setup. Please try deactivating and reactivating iThemes Security. Contact support if the error persists.', 'better-wp-security' );
238
+ echo '</p>';
239
+
240
+ echo '<ol>';
241
+ foreach ( ITSEC_Lib::get_error_strings( $this->setup_error ) as $string ) {
242
+ echo '<li>' . $string . '</li>';
243
+ }
244
+ echo '</ol>';
245
+ echo '</div>';
246
+ } );
247
+
248
+ return;
249
+ }
250
+
251
+ $itsec_lockout->run();
252
+
253
+ if ( is_admin() ) {
254
+ require( $this->plugin_dir . 'core/admin-pages/init.php' );
255
+
256
+ add_filter( 'plugin_action_links', array( $this, 'add_action_link' ), 10, 2 );
257
+ add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 4 );
258
+ }
259
+
260
+ add_action( 'wp_login_failed', array( 'ITSEC_Lib', 'handle_wp_login_failed' ) );
261
+ add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
262
+
263
  $this->setup_scheduler();
264
  ITSEC_Modules::run_active_modules();
265
 
275
  ITSEC_Lib_Remote_Messages::init();
276
  }
277
 
278
+ /**
279
+ * Register our tables with {@see wpdb}.
280
+ */
281
+ private function setup_tables() {
282
+ global $wpdb;
283
+
284
+ $wpdb->global_tables[] = 'itsec_logs';
285
+ $wpdb->global_tables[] = 'itsec_log';
286
+ $wpdb->global_tables[] = 'itsec_lockouts';
287
+ $wpdb->global_tables[] = 'itsec_temp';
288
+ $wpdb->global_tables[] = 'itsec_distributed_storage';
289
+ $wpdb->global_tables[] = 'itsec_geolocation_cache';
290
+ $wpdb->global_tables[] = 'itsec_fingerprints';
291
+ $wpdb->global_tables[] = 'itsec_user_groups';
292
+ $wpdb->global_tables[] = 'itsec_mutexes';
293
+ }
294
+
295
  private function setup_scheduler() {
296
 
297
  if ( $this->scheduler ) {
315
  self::get_scheduler()->run();
316
  }
317
 
318
+ /**
319
+ * Get the required capability to manage ITSEC.
320
+ *
321
+ * @return string
322
+ */
323
+ public static function get_required_cap() {
324
+ return apply_filters( 'itsec_cap_required', 'itsec_manage' );
325
+ }
326
+
327
+ /**
328
+ * Does the current user have permission to manage ITSEC.
329
+ *
330
+ * @return bool
331
+ */
332
+ public static function current_user_can_manage() {
333
+ return current_user_can( self::get_required_cap() );
334
+ }
335
+
336
  /**
337
  * Retrieve the global instance of the files utility.
338
  *
340
  */
341
  public static function get_itsec_files() {
342
  $self = self::get_instance();
343
+
344
  return $self->itsec_files;
345
  }
346
 
351
  */
352
  public static function get_itsec_notify() {
353
  $self = self::get_instance();
354
+
355
  return $self->itsec_notify;
356
  }
357
 
416
  */
417
  public static function get_sync_api() {
418
  $self = self::get_instance();
419
+
420
  return $self->sync_api;
421
  }
422
 
423
+ /**
424
+ * Check if an import is in progress.
425
+ *
426
+ * @return bool
427
+ */
428
+ public static function is_importing() {
429
+ return self::get_instance()->importing;
430
+ }
431
+
432
  /**
433
  * Register ITSEC verbs with sync.
434
  *
449
  public function register_events( $scheduler ) {
450
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'clear-locks' );
451
  $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'health-check' );
452
+ $scheduler->schedule( ITSEC_Scheduler::S_DAILY, 'clear-tokens' );
453
  }
454
 
455
  /**
458
  public function register_modules() {
459
  $path = dirname( __FILE__ );
460
 
461
+ ITSEC_Modules::register_module( 'feature-flags', "$path/modules/feature-flags", 'always-active' );
462
  ITSEC_Modules::register_module( 'security-check', "$path/modules/security-check", 'always-active' );
463
  ITSEC_Modules::register_module( 'global', "$path/modules/global", 'always-active' );
464
  ITSEC_Modules::register_module( 'notification-center', "$path/modules/notification-center", 'always-active' );
465
+ ITSEC_Modules::register_module( 'user-groups', "$path/modules/user-groups", 'always-active' );
466
  ITSEC_Modules::register_module( 'privacy', "$path/modules/privacy", 'always-active' );
467
  ITSEC_Modules::register_module( '404-detection', "$path/modules/404-detection" );
468
  ITSEC_Modules::register_module( 'admin-user', "$path/modules/admin-user", 'always-active' );
485
 
486
  ITSEC_Modules::register_module( 'network-brute-force', "$path/modules/ipcheck", 'default-active' );
487
 
488
+ if ( ! defined( 'ITSEC_DISABLE_PASSWORD_REQUIREMENTS' ) || ! ITSEC_DISABLE_PASSWORD_REQUIREMENTS ) {
489
  ITSEC_Modules::register_module( 'password-requirements', "$path/modules/password-requirements/", 'always-active' );
490
  }
491
 
496
  ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
497
  ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
498
  ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
 
499
  ITSEC_Modules::register_module( 'security-check-pro', "$path/modules/security-check-pro", self::is_pro() ? 'always-active' : 'default-inactive' );
500
+ ITSEC_Modules::register_module( 'sync-connect', "$path/modules/sync-connect", 'always-active' );
501
 
502
  if ( ! ITSEC_Core::is_pro() ) {
503
  ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
559
  * Dispatch a request to upgrade the data schema to another version.
560
  *
561
  * @param int|bool $build The version of the data storage format. Pass false to default to the current version.
562
+ *
563
+ * @return true|WP_Error|null
564
  */
565
  public function handle_upgrade( $build = false ) {
566
 
567
+ if ( func_num_args() === 0 && self::get_saved_plugin_build() >= $this->plugin_build ) {
568
+ return null;
569
  }
570
 
571
  $this->doing_data_upgrade = true;
572
 
573
  require_once( self::get_core_dir() . '/setup.php' );
574
+ self::get_instance()->setup_error = ITSEC_Setup::handle_upgrade( $build );
575
+
576
+ return self::get_instance()->setup_error;
577
  }
578
 
579
  public static function handle_activation() {
580
  require_once( self::get_core_dir() . '/setup.php' );
581
+ self::get_instance()->setup_error = ITSEC_Setup::handle_activation();
582
  }
583
 
584
  public static function handle_deactivation() {
594
  /**
595
  * Register a notice to be displayed in the WordPress admin.
596
  *
597
+ * @param callable $callback Function that will render a notice.
598
+ * @param bool $all_pages Display the notice on all pages or only on ITSEC, plugins, and upgrade page.
599
  */
600
  public static function add_notice( $callback, $all_pages = false ) {
601
  _deprecated_function( __METHOD__, '6.0.0', 'ITSEC_Lib_Admin_Notices::register' );
622
  }
623
  }
624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  public static function get_plugin_file() {
626
  $self = self::get_instance();
627
+
628
  return $self->plugin_file;
629
  }
630
 
631
  public static function set_plugin_file( $plugin_file ) {
632
+ $self = self::get_instance();
633
  $self->plugin_file = $plugin_file;
634
+ $self->plugin_dir = dirname( $plugin_file ) . '/';
635
  }
636
 
637
+ /**
638
+ * Get the build the plugin is running on.
639
+ *
640
+ * @return int
641
+ */
642
  public static function get_plugin_build() {
643
  $self = self::get_instance();
644
+
645
  return $self->plugin_build;
646
  }
647
 
648
+ /**
649
+ * Get the build the database is running.
650
+ *
651
+ * @param bool $use_cache
652
+ *
653
+ * @return int
654
+ */
655
+ public static function get_saved_plugin_build( $use_cache = true ) {
656
+ if ( $use_cache ) {
657
+ return ITSEC_Modules::get_setting( 'global', 'build' );
658
+ }
659
+
660
+ $storage = get_site_option( 'itsec-storage' );
661
+
662
+ if ( ! $storage ) {
663
+ return 0;
664
+ }
665
+
666
+ if ( ! isset( $storage['global']['build'] ) ) {
667
+ return 0;
668
+ }
669
+
670
+ return (int) $storage['global']['build'];
671
+ }
672
+
673
  public static function get_plugin_dir() {
674
  $self = self::get_instance();
675
+
676
  return $self->plugin_dir;
677
  }
678
 
682
 
683
  public static function get_plugin_name() {
684
  $self = self::get_instance();
685
+
686
  return $self->plugin_name;
687
  }
688
 
736
  */
737
  public static function get_current_time() {
738
  $self = self::get_instance();
739
+
740
  return $self->current_time;
741
  }
742
 
749
  */
750
  public static function get_current_time_gmt() {
751
  $self = self::get_instance();
752
+
753
  return $self->current_time_gmt;
754
  }
755
 
760
  */
761
  public static function get_time_offset() {
762
  $self = self::get_instance();
763
+
764
  return $self->current_time - $self->current_time_gmt;
765
  }
766
 
776
  $filters = array();
777
 
778
  if ( $module ) {
779
+ $filters[] = rawurlencode( "module|{$module}" );
780
  }
781
 
782
  if ( $type ) {
811
  *
812
  * @param bool $interactive
813
  */
814
+ public static function set_interactive( $interactive = true ) {
815
+ $self = self::get_instance();
816
  $self->interactive = (bool) $interactive;
817
  }
818
 
823
  */
824
  public static function is_interactive() {
825
  $self = self::get_instance();
826
+
827
  return $self->interactive;
828
  }
829
 
866
  * This value is cached for both the lifetime of the request and possibly indefinitely when WordPress is
867
  * using an object cache.
868
  *
869
+ * @return array
870
  * @see wp_upload_dir
871
  *
 
872
  */
873
  public static function get_wp_upload_dir() {
874
  $self = self::get_instance();
917
  /**
918
  * Retrieve and/or create a directory for ITSEC to store data.
919
  *
920
+ * @param string $dir Optionally specify an additional sub-directory.
921
  * @param bool $public Whether to get the public version of the directory.
922
  *
923
  * @return string
1002
 
1003
  if ( ! function_exists( 'rest_get_url_prefix' ) ) {
1004
  $GLOBALS['__itsec_core_is_rest_api_request'] = false;
1005
+
1006
  return false;
1007
  }
1008
 
1009
  if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
1010
  $GLOBALS['__itsec_core_is_rest_api_request'] = true;
1011
+
1012
  return true;
1013
  }
1014
 
1104
 
1105
  return $self->version;
1106
  }
1107
+
1108
+ public static function is_test_suite( $suite = '' ) {
1109
+ if ( ! defined( 'ITSEC_TEST_SUITE' ) ) {
1110
+ return false;
1111
+ }
1112
+
1113
+ return $suite ? ITSEC_TEST_SUITE === $suite : true;
1114
+ }
1115
  }
1116
  }
core/history.txt CHANGED
@@ -839,3 +839,41 @@
839
  New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
840
  Enhancement: Run Security Check Pro IP Detection automatically once a day.
841
  Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
  New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
840
  Enhancement: Run Security Check Pro IP Detection automatically once a day.
841
  Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
842
+ 5.5.1 - 2019-12-18 - Timothy Jacobs
843
+ Bug Fix: The "Mulisite Tweaks -> Hide Updates" setting prevented auto-updates from running with WP Cron.
844
+ Bug Fix: Remove "get_magic_quotes()" call that existed for backwards compatibility with PHP versions 5.3 and earlier. This function call was causing a warning on PHP 7.4.
845
+ 5.5.2 - 2020-01-07 - Timothy Jacobs
846
+ Bug Fix: Backup event was not added when the WP Cron Scheduler was reset manually.
847
+ Bug Fix: Admin Notices Popover was not being hidden when clicking outside the Popover on WP 5.3.
848
+ 5.6.0 - 2020-02-12 - Timothy Jacobs, Josh Oakes
849
+ Important: iThemes Security requires PHP 5.6 or greater and WordPress 5.2 or greater.
850
+ New Feature: Save Time Securing WordPress With User Groups!
851
+ New Feature: Simplified connection flow when setting up iThemes Sync.
852
+ Bug Fix: Warning when loading the settings page on PHP 7.4.
853
+ Bug Fix: Warning when loading the debug page on PHP 7.4.
854
+ 5.6.1 - 2020-02-13 - Timothy Jacobs
855
+ Bug Fix: A fatal error could occur when upgrading to User Groups if a custom role had been selected for Two-Factor or Passwordless Login that has since been deleted but the module's settings had not been updated.
856
+ 5.6.2 - 2020-02-17 - Timothy Jacobs, Josh Oakes
857
+ Tweak: Harden iThemes Sync connection flow by adding a second verification check.
858
+ Bug Fix: Prevent UnknownIdentifierException errors when modules are loaded before expected.
859
+ Bug Fix: Add additional type checks.
860
+ 5.7.0 - 2020-03-10 - Timothy Jacobs
861
+ Enhancement: Allow selecting users across all sites in a network for User Groups.
862
+ Enhancement: Add super admins as a selectable role for User Groups.
863
+ Enhancement: Add a warning if a WordPress Salt is set to an invalid value.
864
+ Enhancement: Include child log items in the logs list table. These are helpful for debugging issues.
865
+ Enhancement: Improve performance of the logs page on sites with large number of log items.
866
+ Tweak: When logging $_SERVER, only log a snapshot of available properties.
867
+ Bug Fix: New Password Requirements for already created accounts were not enforced until the second login.
868
+ Bug Fix: Prevent fatal error if invalid user IDs are encountered by User Groups.
869
+ 5.7.1 - 2020-03-11 - Timothy Jacobs
870
+ Enhancement: Further improve logs performance.
871
+ 5.7.2 - 2020-03-31 - Timothy Jacobs
872
+ Bug Fix: Update admin notices styling to be compatible with WordPress 5.4.
873
+ Bug Fix: Periodically clear expired opaque tokens.
874
+ Bug Fix: Exclude "Process Update" and "Process Stop" logs when other process logs are hidden.
875
+ 5.7.3 - 2020-04-08 - Timothy Jacobs
876
+ Bug Fix: Don't block registration page when "wp-signup.php" is the Hide Backend register slug.
877
+ 5.7.4 - 2020-04-15 - Timothy Jacobs
878
+ Bug Fix: Users with weak passwords would not be forced to change their password if the strong password requirement had been enabled after their password strength was checked.
879
+ Bug Fix: PHP warning if downgrading from Pro to Free.
core/img/bb-ad.jpg DELETED
Binary file
core/lib.php CHANGED
@@ -54,12 +54,12 @@ final class ITSEC_Lib {
54
  *
55
  * @since 4.0.0
56
  *
57
- * @return void
58
  */
59
  public static function create_database_tables() {
60
  require_once( ITSEC_Core::get_core_dir() . '/lib/schema.php' );
61
 
62
- ITSEC_Schema::create_database_tables();
63
  }
64
 
65
  /**
@@ -514,6 +514,25 @@ final class ITSEC_Lib {
514
  return $html;
515
  }
516
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  /**
518
  * Get a WordPress user object.
519
  *
@@ -976,16 +995,20 @@ final class ITSEC_Lib {
976
  return $storage;
977
  }
978
 
 
 
 
 
 
 
 
 
 
979
  public static function array_get( $array, $key, $default = null ) {
980
-
981
  if ( ! is_array( $array ) ) {
982
  return $default;
983
  }
984
 
985
- if ( null === $key ) {
986
- return $array;
987
- }
988
-
989
  if ( isset( $array[ $key ] ) ) {
990
  return $array[ $key ];
991
  }
@@ -1005,6 +1028,36 @@ final class ITSEC_Lib {
1005
  return $array;
1006
  }
1007
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1008
  public static function print_r( $data, $args = array() ) {
1009
  require_once( ITSEC_Core::get_core_dir() . '/lib/debug.php' );
1010
 
@@ -1759,10 +1812,10 @@ final class ITSEC_Lib {
1759
  *
1760
  * @return WP_Error
1761
  */
1762
- public static function combine_wp_error( $errors ) {
1763
  $combined = new WP_Error();
1764
 
1765
- self::add_to_wp_error( $combined, $errors );
1766
 
1767
  return $combined;
1768
  }
@@ -1770,19 +1823,22 @@ final class ITSEC_Lib {
1770
  /**
1771
  * Add the subsequent WP Error data to the first WP Error instance.
1772
  *
1773
- * @param WP_Error $add_to
1774
- * @param WP_Error[]|null[] ...$errors
1775
  */
1776
- public static function add_to_wp_error( WP_Error $add_to, $errors ) {
1777
- if ( ! is_array( $errors ) ) {
1778
- $errors = func_get_args();
1779
- array_shift( $errors );
1780
- }
1781
-
1782
  foreach ( $errors as $error ) {
1783
  if ( $error ) {
1784
  foreach ( $error->get_error_codes() as $code ) {
1785
- $add_to->add( $code, $error->get_error_message( $code ) );
 
 
 
 
 
 
 
 
1786
  }
1787
  }
1788
  }
@@ -1829,9 +1885,9 @@ final class ITSEC_Lib {
1829
  /**
1830
  * Get the WordPress branch version.
1831
  *
 
1832
  * @example 5.2.4 => 5.2
1833
  *
1834
- * @return string
1835
  */
1836
  public static function get_wp_branch() {
1837
  $version = get_bloginfo( 'version' );
@@ -1840,4 +1896,163 @@ final class ITSEC_Lib {
1840
 
1841
  return $major . '.' . $minor;
1842
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1843
  }
54
  *
55
  * @since 4.0.0
56
  *
57
+ * @return true|WP_Error
58
  */
59
  public static function create_database_tables() {
60
  require_once( ITSEC_Core::get_core_dir() . '/lib/schema.php' );
61
 
62
+ return ITSEC_Schema::create_database_tables();
63
  }
64
 
65
  /**
514
  return $html;
515
  }
516
 
517
+ /**
518
+ * Get an error string for all errors in a WP_Error isntance.
519
+ *
520
+ * @param WP_Error $error
521
+ *
522
+ * @return string[]
523
+ */
524
+ public static function get_error_strings( WP_Error $error ) {
525
+ $messages = array();
526
+
527
+ foreach ( $error->get_error_codes() as $code ) {
528
+ foreach ( $error->get_error_messages( $code ) as $str ) {
529
+ $messages[] = $str;
530
+ }
531
+ }
532
+
533
+ return $messages;
534
+ }
535
+
536
  /**
537
  * Get a WordPress user object.
538
  *
995
  return $storage;
996
  }
997
 
998
+ /**
999
+ * Get a dot nested value from an array.
1000
+ *
1001
+ * @param array $array
1002
+ * @param string $key
1003
+ * @param mixed $default
1004
+ *
1005
+ * @return mixed
1006
+ */
1007
  public static function array_get( $array, $key, $default = null ) {
 
1008
  if ( ! is_array( $array ) ) {
1009
  return $default;
1010
  }
1011
 
 
 
 
 
1012
  if ( isset( $array[ $key ] ) ) {
1013
  return $array[ $key ];
1014
  }
1028
  return $array;
1029
  }
1030
 
1031
+ /**
1032
+ * Set an array item to a given value using "dot" notation.
1033
+ *
1034
+ * @param array $array
1035
+ * @param string $key
1036
+ * @param mixed $value
1037
+ *
1038
+ * @return array
1039
+ */
1040
+ public static function array_set( $array, $key, $value ) {
1041
+ $keys = explode( '.', $key );
1042
+ $modify = &$array;
1043
+
1044
+ while ( count( $keys ) > 1 ) {
1045
+ $key = array_shift( $keys );
1046
+ // If the key doesn't exist at this depth, we will just create an empty array
1047
+ // to hold the next value, allowing us to create the arrays to hold final
1048
+ // values at the correct depth. Then we'll keep digging into the array.
1049
+ if ( ! isset( $modify[ $key ] ) || ! is_array( $modify[ $key ] ) ) {
1050
+ $modify[ $key ] = [];
1051
+ }
1052
+
1053
+ $modify = &$modify[ $key ];
1054
+ }
1055
+
1056
+ $modify[ array_shift( $keys ) ] = $value;
1057
+
1058
+ return $array;
1059
+ }
1060
+
1061
  public static function print_r( $data, $args = array() ) {
1062
  require_once( ITSEC_Core::get_core_dir() . '/lib/debug.php' );
1063
 
1812
  *
1813
  * @return WP_Error
1814
  */
1815
+ public static function combine_wp_error( ...$errors ) {
1816
  $combined = new WP_Error();
1817
 
1818
+ self::add_to_wp_error( $combined, ...$errors );
1819
 
1820
  return $combined;
1821
  }
1823
  /**
1824
  * Add the subsequent WP Error data to the first WP Error instance.
1825
  *
1826
+ * @param WP_Error $add_to
1827
+ * @param WP_Error|null ...$errors
1828
  */
1829
+ public static function add_to_wp_error( WP_Error $add_to, ...$errors ) {
 
 
 
 
 
1830
  foreach ( $errors as $error ) {
1831
  if ( $error ) {
1832
  foreach ( $error->get_error_codes() as $code ) {
1833
+ foreach ( $error->get_error_messages( $code ) as $message ) {
1834
+ $add_to->add( $code, $message );
1835
+ }
1836
+
1837
+ $data = $error->get_error_data( $code );
1838
+
1839
+ if ( null !== $data ) {
1840
+ $add_to->add_data( $data, $code );
1841
+ }
1842
  }
1843
  }
1844
  }
1885
  /**
1886
  * Get the WordPress branch version.
1887
  *
1888
+ * @return string
1889
  * @example 5.2.4 => 5.2
1890
  *
 
1891
  */
1892
  public static function get_wp_branch() {
1893
  $version = get_bloginfo( 'version' );
1896
 
1897
  return $major . '.' . $minor;
1898
  }
1899
+
1900
+ /**
1901
+ * Are two lists equal ignoring order.
1902
+ *
1903
+ * @param array $a
1904
+ * @param array $b
1905
+ * @param callable|null $cmp
1906
+ *
1907
+ * @return bool
1908
+ */
1909
+ public static function equal_sets( array $a, array $b, callable $cmp = null ) {
1910
+ if ( $cmp ) {
1911
+ usort( $a, $cmp );
1912
+ usort( $b, $cmp );
1913
+ } else {
1914
+ sort( $a );
1915
+ sort( $b );
1916
+ }
1917
+
1918
+ return $a === $b;
1919
+ }
1920
+
1921
+ /**
1922
+ * Convert the return val from {@see ITSEC_Modules::set_settings()} to a WP_Error object.
1923
+ *
1924
+ * @param array $updated
1925
+ *
1926
+ * @return WP_Error|null
1927
+ */
1928
+ public static function updated_settings_to_wp_error( $updated ) {
1929
+ if ( is_wp_error( $updated ) ) {
1930
+ return $updated;
1931
+ }
1932
+
1933
+ if ( $updated['saved'] ) {
1934
+ return null;
1935
+ }
1936
+
1937
+ if ( $updated['errors'] ) {
1938
+ $error = self::combine_wp_error( ...$updated['errors'] );
1939
+ } else {
1940
+ $error = new \WP_Error( 'itsec.settings.set-failed', __( 'Failed to update settings.', 'better-wp-security' ), [ 'status' => \WP_Http::BAD_REQUEST ] );
1941
+ }
1942
+
1943
+ return $error;
1944
+ }
1945
+
1946
+ /**
1947
+ * Sanitize the list of roles.
1948
+ *
1949
+ * @param string[] $roles
1950
+ *
1951
+ * @return array
1952
+ */
1953
+ public static function sanitize_roles( $roles ) {
1954
+ return array_filter( $roles, static function ( $role ) {
1955
+ return (bool) get_role( $role );
1956
+ } );
1957
+ }
1958
+
1959
+ /**
1960
+ * Get a snapshot of $_SERVER properties.
1961
+ *
1962
+ * @return array
1963
+ */
1964
+ public static function get_server_snapshot() {
1965
+ $whitelist = [
1966
+ 'REQUEST_TIME',
1967
+ 'REQUEST_TIME_FLOAT',
1968
+ 'REQUEST_METHOD',
1969
+ 'HTTPS',
1970
+ 'REQUEST_SCHEME',
1971
+ 'SERVER_PROTOCOL',
1972
+ 'SCRIPT_FILENAME',
1973
+ ];
1974
+
1975
+ return array_filter( $_SERVER, static function ( $key ) use ( $whitelist ) {
1976
+ if ( $key === 'HTTP_COOKIE' ) {
1977
+ return false;
1978
+ }
1979
+
1980
+ if ( self::str_starts_with( $key, 'HTTP_' ) ) {
1981
+ return true;
1982
+ }
1983
+
1984
+ if ( self::str_starts_with( $key, 'CONTENT_' ) ) {
1985
+ return true;
1986
+ }
1987
+
1988
+ return in_array( $key, $whitelist, true );
1989
+ }, ARRAY_FILTER_USE_KEY );
1990
+ }
1991
+
1992
+ /**
1993
+ * Version of {@see is_super_admin()} that operates on a `WP_User` instance.
1994
+ *
1995
+ * This bypasses an issue where {@see is_super_admin()} cannot be used during the `determine_current_user` filter since
1996
+ * `is_super_admin` has a side effect of querying for the current user, causing an infinite loop.
1997
+ *
1998
+ * @param WP_User $user
1999
+ *
2000
+ * @return bool
2001
+ */
2002
+ public static function is_super_admin( WP_User $user ) {
2003
+ if ( ! $user->exists() ) {
2004
+ return false;
2005
+ }
2006
+
2007
+ if ( is_multisite() ) {
2008
+ $super_admins = get_super_admins();
2009
+ if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) ) {
2010
+ return true;
2011
+ }
2012
+ } else {
2013
+ if ( $user->has_cap( 'delete_users' ) ) {
2014
+ return true;
2015
+ }
2016
+ }
2017
+
2018
+ return false;
2019
+ }
2020
+
2021
+ /**
2022
+ * Performs a {@see dbDelta()} but reports any errors encountered.
2023
+ *
2024
+ * @param string $delta
2025
+ *
2026
+ * @return WP_Error
2027
+ */
2028
+ public static function db_delta_with_error_handling( $delta ) {
2029
+ global $wpdb, $EZSQL_ERROR;
2030
+
2031
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
2032
+
2033
+ $err_count = is_array( $EZSQL_ERROR ) ? count( $EZSQL_ERROR ) : 0;
2034
+ $showed_errors = $wpdb->show_errors( false );
2035
+
2036
+ dbDelta( $delta );
2037
+
2038
+ if ( $showed_errors ) {
2039
+ $wpdb->show_errors();
2040
+ }
2041
+
2042
+ $wp_error = new WP_Error();
2043
+
2044
+ if ( is_array( $EZSQL_ERROR ) ) {
2045
+ for ( $i = $err_count, $i_max = count( $EZSQL_ERROR ); $i < $i_max; $i ++ ) {
2046
+ $error = $EZSQL_ERROR[ $i ];
2047
+
2048
+ if ( empty( $error['error_str'] ) || empty( $error['query'] ) || 0 === strpos( $error['query'], 'DESCRIBE ' ) ) {
2049
+ continue;
2050
+ }
2051
+
2052
+ $wp_error->add( 'db_delta_error', $error['error_str'] );
2053
+ }
2054
+ }
2055
+
2056
+ return $wp_error;
2057
+ }
2058
  }
core/lib/class-itsec-lib-canonical-roles.php CHANGED
@@ -2,6 +2,33 @@
2
 
3
  final class ITSEC_Lib_Canonical_Roles {
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  /**
6
  * Check if a given role is at least as or equally as powerful as a given role.
7
  *
@@ -21,11 +48,11 @@ final class ITSEC_Lib_Canonical_Roles {
21
  '' => 0,
22
  );
23
 
24
- if ( ! isset( $roles[$role] ) || ! isset( $roles[$min_role] ) ) {
25
  return false;
26
  }
27
 
28
- if ( $roles[$role] >= $roles[$min_role] ) {
29
  return true;
30
  }
31
 
@@ -51,13 +78,13 @@ final class ITSEC_Lib_Canonical_Roles {
51
  '' => 0,
52
  );
53
 
54
- if ( ! isset( $roles[$role] ) ) {
55
  return false;
56
  }
57
 
58
  $user_role = self::get_user_role( $user );
59
 
60
- if ( $roles[$user_role] >= $roles[$role] ) {
61
  return true;
62
  }
63
 
@@ -105,7 +132,7 @@ final class ITSEC_Lib_Canonical_Roles {
105
  return '';
106
  }
107
 
108
- if ( is_multisite() && is_super_admin( $user->ID ) ) {
109
  return 'super-admin';
110
  }
111
 
@@ -149,6 +176,10 @@ final class ITSEC_Lib_Canonical_Roles {
149
  public static function get_canonical_role_from_role_and_user( $role, $user ) {
150
  $user = ITSEC_Lib::get_user( $user );
151
 
 
 
 
 
152
  if ( empty( $role ) ) {
153
  $role_caps = array();
154
  } else {
@@ -162,7 +193,7 @@ final class ITSEC_Lib_Canonical_Roles {
162
 
163
  foreach ( $user->caps as $cap => $has ) {
164
  if ( $has && ! $wp_roles->is_role( $cap ) ) {
165
- $user_caps[] = $has;
166
  }
167
  }
168
  }
@@ -170,6 +201,29 @@ final class ITSEC_Lib_Canonical_Roles {
170
  return self::get_role_from_caps( array_merge( $role_caps, $user_caps ) );
171
  }
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  /**
174
  * Get all users that have the given canonical role.
175
  *
@@ -233,7 +287,7 @@ final class ITSEC_Lib_Canonical_Roles {
233
  'update_plugins',
234
  'update_themes',
235
  ),
236
- 'editor' => array(
237
  'delete_others_pages',
238
  'delete_others_posts',
239
  'delete_pages',
@@ -259,19 +313,19 @@ final class ITSEC_Lib_Canonical_Roles {
259
  'read_private_posts',
260
  'unfiltered_html',
261
  ),
262
- 'author' => array(
263
  'delete_published_posts',
264
  'edit_published_posts',
265
  'level_2',
266
  'publish_posts',
267
  'upload_files',
268
  ),
269
- 'contributor' => array(
270
  'delete_posts',
271
  'edit_posts',
272
  'level_1',
273
  ),
274
- 'subscriber' => array(
275
  'level_0',
276
  'read',
277
  ),
@@ -348,7 +402,7 @@ final class ITSEC_Lib_Canonical_Roles {
348
  'update_themes',
349
  'upload_files',
350
  ),
351
- 'editor' => array(
352
  'delete_others_pages',
353
  'delete_others_posts',
354
  'delete_pages',
@@ -384,7 +438,7 @@ final class ITSEC_Lib_Canonical_Roles {
384
  'unfiltered_html',
385
  'upload_files',
386
  ),
387
- 'author' => array(
388
  'delete_posts',
389
  'delete_published_posts',
390
  'edit_posts',
@@ -396,14 +450,14 @@ final class ITSEC_Lib_Canonical_Roles {
396
  'read',
397
  'upload_files',
398
  ),
399
- 'contributor' => array(
400
  'delete_posts',
401
  'edit_posts',
402
  'level_0',
403
  'level_1',
404
  'read',
405
  ),
406
- 'subscriber' => array(
407
  'level_0',
408
  'read',
409
  ),
2
 
3
  final class ITSEC_Lib_Canonical_Roles {
4
 
5
+ private static $canonical = [
6
+ 'super-admin',
7
+ 'administrator',
8
+ 'editor',
9
+ 'author',
10
+ 'contributor',
11
+ 'subscriber',
12
+ ];
13
+
14
+ /**
15
+ * Get a list of all the canonical roles.
16
+ *
17
+ * @param bool $include_super_admin Include the super admin "role".
18
+ *
19
+ * @return string[]
20
+ */
21
+ public static function get_canonical_roles( $include_super_admin = true ) {
22
+ $canonical = self::$canonical;
23
+
24
+ if ( ! $include_super_admin ) {
25
+ unset( $canonical[0] );
26
+ $canonical = array_values( $canonical );
27
+ }
28
+
29
+ return $canonical;
30
+ }
31
+
32
  /**
33
  * Check if a given role is at least as or equally as powerful as a given role.
34
  *
48
  '' => 0,
49
  );
50
 
51
+ if ( ! isset( $roles[ $role ] ) || ! isset( $roles[ $min_role ] ) ) {
52
  return false;
53
  }
54
 
55
+ if ( $roles[ $role ] >= $roles[ $min_role ] ) {
56
  return true;
57
  }
58
 
78
  '' => 0,
79
  );
80
 
81
+ if ( ! isset( $roles[ $role ] ) ) {
82
  return false;
83
  }
84
 
85
  $user_role = self::get_user_role( $user );
86
 
87
+ if ( $roles[ $user_role ] >= $roles[ $role ] ) {
88
  return true;
89
  }
90
 
132
  return '';
133
  }
134
 
135
+ if ( is_multisite() && ITSEC_Lib::is_super_admin( $user ) ) {
136
  return 'super-admin';
137
  }
138
 
176
  public static function get_canonical_role_from_role_and_user( $role, $user ) {
177
  $user = ITSEC_Lib::get_user( $user );
178
 
179
+ if ( is_multisite() && ITSEC_Lib::is_super_admin( $user ) ) {
180
+ return 'super-admin';
181
+ }
182
+
183
  if ( empty( $role ) ) {
184
  $role_caps = array();
185
  } else {
193
 
194
  foreach ( $user->caps as $cap => $has ) {
195
  if ( $has && ! $wp_roles->is_role( $cap ) ) {
196
+ $user_caps[] = $cap;
197
  }
198
  }
199
  }
201
  return self::get_role_from_caps( array_merge( $role_caps, $user_caps ) );
202
  }
203
 
204
+ /**
205
+ * Gets all canonical roles of at least the given canonical role capability.
206
+ *
207
+ * @example ::get_canonical_roles_of_at_least( 'editor' ) // [ 'administrator', 'editor' ]
208
+ *
209
+ * @param string $minimum
210
+ *
211
+ * @return string[]
212
+ */
213
+ public static function get_canonical_roles_of_at_least( $minimum ) {
214
+ $at_least = [];
215
+
216
+ foreach ( self::$canonical as $role ) {
217
+ $at_least[] = $role;
218
+
219
+ if ( $role === $minimum ) {
220
+ break;
221
+ }
222
+ }
223
+
224
+ return $at_least;
225
+ }
226
+
227
  /**
228
  * Get all users that have the given canonical role.
229
  *
287
  'update_plugins',
288
  'update_themes',
289
  ),
290
+ 'editor' => array(
291
  'delete_others_pages',
292
  'delete_others_posts',
293
  'delete_pages',
313
  'read_private_posts',
314
  'unfiltered_html',
315
  ),
316
+ 'author' => array(
317
  'delete_published_posts',
318
  'edit_published_posts',
319
  'level_2',
320
  'publish_posts',
321
  'upload_files',
322
  ),
323
+ 'contributor' => array(
324
  'delete_posts',
325
  'edit_posts',
326
  'level_1',
327
  ),
328
+ 'subscriber' => array(
329
  'level_0',
330
  'read',
331
  ),
402
  'update_themes',
403
  'upload_files',
404
  ),
405
+ 'editor' => array(
406
  'delete_others_pages',
407
  'delete_others_posts',
408
  'delete_pages',
438
  'unfiltered_html',
439
  'upload_files',
440
  ),
441
+ 'author' => array(
442
  'delete_posts',
443
  'delete_published_posts',
444
  'edit_posts',
450
  'read',
451
  'upload_files',
452
  ),
453
+ 'contributor' => array(
454
  'delete_posts',
455
  'edit_posts',
456
  'level_0',
457
  'level_1',
458
  'read',
459
  ),
460
+ 'subscriber' => array(
461
  'level_0',
462
  'read',
463
  ),
core/lib/class-itsec-lib-directory.php CHANGED
@@ -171,19 +171,44 @@ if ( ! class_exists( 'ITSEC_Lib_Directory' ) ) {
171
  *
172
  * @since 1.15.0
173
  *
 
 
174
  * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
175
  */
176
  public static function remove( $dir ) {
177
- if ( ! ITSEC_Lib_File::exists( $dir ) ) {
 
 
 
 
 
 
 
 
 
 
178
  return true;
179
  }
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  if ( ! ITSEC_Lib_Utility::is_callable_function( 'rmdir' ) ) {
183
  return new WP_Error( 'itsec-lib-directory-remove-rmdir-is-disabled', sprintf( __( 'The directory %s could not be removed as the rmdir() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $dir ) );
184
  }
185
 
186
-
187
  $files = self::read( $dir );
188
 
189
  if ( is_wp_error( $files ) ) {
@@ -198,15 +223,10 @@ if ( ! class_exists( 'ITSEC_Lib_Directory' ) ) {
198
  }
199
  }
200
 
201
- $result = rmdir( $dir );
202
  // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
203
  @clearstatcache( true, $dir );
204
 
205
- if ( $result ) {
206
- return true;
207
- }
208
-
209
- return new WP_Error( 'itsec-lib-directory-remove-unknown-error', sprintf( __( 'Unable to remove %s due to an unknown error.', 'better-wp-security' ), $dir ) );
210
  }
211
 
212
  /**
171
  *
172
  * @since 1.15.0
173
  *
174
+ * @param string $dir
175
+ *
176
  * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
177
  */
178
  public static function remove( $dir ) {
179
+ $emptied = self::empty_directory( $dir );
180
+
181
+ if ( is_wp_error( $emptied ) ) {
182
+ return $emptied;
183
+ }
184
+
185
+ $result = rmdir( $dir );
186
+ // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
187
+ @clearstatcache( true, $dir );
188
+
189
+ if ( $result ) {
190
  return true;
191
  }
192
 
193
+ return new WP_Error( 'itsec-lib-directory-remove-unknown-error', sprintf( __( 'Unable to remove %s due to an unknown error.', 'better-wp-security' ), $dir ) );
194
+ }
195
+
196
+ /**
197
+ * Empty a directory of all it's contents, but don't delete the directory.
198
+ *
199
+ * @param string $dir
200
+ *
201
+ * @return bool|WP_Error
202
+ */
203
+ public static function empty_directory( $dir ) {
204
+ if ( ! ITSEC_Lib_File::exists( $dir ) ) {
205
+ return true;
206
+ }
207
 
208
  if ( ! ITSEC_Lib_Utility::is_callable_function( 'rmdir' ) ) {
209
  return new WP_Error( 'itsec-lib-directory-remove-rmdir-is-disabled', sprintf( __( 'The directory %s could not be removed as the rmdir() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $dir ) );
210
  }
211
 
 
212
  $files = self::read( $dir );
213
 
214
  if ( is_wp_error( $files ) ) {
223
  }
224
  }
225
 
 
226
  // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
227
  @clearstatcache( true, $dir );
228
 
229
+ return true;
 
 
 
 
230
  }
231
 
232
  /**
core/lib/class-itsec-lib-feature-flags.php CHANGED
@@ -8,19 +8,43 @@ class ITSEC_Lib_Feature_Flags {
8
  /** @var array */
9
  private static $flags = array();
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  /**
12
  * Register a feature flag.
13
  *
14
  * @param string $name
15
  * @param array $args
 
 
16
  */
17
  public static function register_flag( $name, $args = array() ) {
 
 
 
 
18
  self::$flags[ $name ] = wp_parse_args( $args, array(
19
- 'rate' => false,
20
- 'remote' => false,
21
- 'title' => '',
22
- 'description' => '',
 
23
  ) );
 
 
24
  }
25
 
26
  /**
@@ -74,6 +98,10 @@ class ITSEC_Lib_Feature_Flags {
74
  return (bool) constant( 'ITSEC_FF_' . $flag );
75
  }
76
 
 
 
 
 
77
  $flags = ITSEC_Modules::get_setting( 'global', 'feature_flags' );
78
 
79
  if ( ! empty( $flags[ $flag ]['enabled'] ) ) {
@@ -88,8 +116,8 @@ class ITSEC_Lib_Feature_Flags {
88
  return false;
89
  }
90
 
91
- // If the rice haven't been rolled, or the rate has changed since the last run, roll the dice.
92
- if ( ! isset( $flags[ $flag ]['rate'] ) || $flags[ $flag ]['rate'] !== $rate ) {
93
  $enabled = mt_rand( 1, 100 ) <= $rate;
94
 
95
  $flags[ $flag ] = array(
@@ -109,6 +137,35 @@ class ITSEC_Lib_Feature_Flags {
109
  return false;
110
  }
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  /**
113
  * Manually enable a feature flag.
114
  *
@@ -151,7 +208,6 @@ class ITSEC_Lib_Feature_Flags {
151
  public static function get_flag_config( $flag ) {
152
  self::load();
153
 
154
-
155
  if ( ! isset( self::$flags[ $flag ] ) ) {
156
  return null;
157
  }
8
  /** @var array */
9
  private static $flags = array();
10
 
11
+ /**
12
+ * Whether the Feature Flag UI should be displayed.
13
+ *
14
+ * @return bool
15
+ */
16
+ public static function show_ui() {
17
+ $show = count( self::get_enabled() ) > 0;
18
+
19
+ if ( defined( 'ITSEC_SHOW_FEATURE_FLAGS' ) ) {
20
+ $show = ITSEC_SHOW_FEATURE_FLAGS;
21
+ }
22
+
23
+ return apply_filters( 'itsec_show_feature_flags_ui', $show );
24
+ }
25
+
26
  /**
27
  * Register a feature flag.
28
  *
29
  * @param string $name
30
  * @param array $args
31
+ *
32
+ * @return true|WP_Error
33
  */
34
  public static function register_flag( $name, $args = array() ) {
35
+ if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $name ) ) {
36
+ return new WP_Error( 'invalid_flag_name', __( 'Invalid flag name.', 'better-wp-security' ) );
37
+ }
38
+
39
  self::$flags[ $name ] = wp_parse_args( $args, array(
40
+ 'rate' => false,
41
+ 'remote' => false,
42
+ 'title' => '',
43
+ 'description' => '',
44
+ 'documentation' => '',
45
  ) );
46
+
47
+ return true;
48
  }
49
 
50
  /**
98
  return (bool) constant( 'ITSEC_FF_' . $flag );
99
  }
100
 
101
+ if ( ! empty( $config['disabled'] ) ) {
102
+ return false;
103
+ }
104
+
105
  $flags = ITSEC_Modules::get_setting( 'global', 'feature_flags' );
106
 
107
  if ( ! empty( $flags[ $flag ]['enabled'] ) ) {
116
  return false;
117
  }
118
 
119
+ // If the dice haven't been rolled, or the rate has changed since the last run, roll the dice.
120
+ if ( ! isset( $flags[ $flag ]['rate'] ) || $flags[ $flag ]['rate'] < $rate ) {
121
  $enabled = mt_rand( 1, 100 ) <= $rate;
122
 
123
  $flags[ $flag ] = array(
137
  return false;
138
  }
139
 
140
+ /**
141
+ * Get's the reason for the flag being enabled/disabled.
142
+ *
143
+ * @param string $flag
144
+ *
145
+ * @return array
146
+ */
147
+ public static function get_reason( $flag ) {
148
+ if ( ! $config = self::get_flag_config( $flag ) ) {
149
+ return [ 'unknown', __( 'Unknown flag' ) ];
150
+ }
151
+
152
+ if ( defined( 'ITSEC_FF_' . $flag ) ) {
153
+ return [ 'constant', __( 'Manually configured with a constant.' ) ];
154
+ }
155
+
156
+ if ( ! empty( $config['disabled'] ) ) {
157
+ return [ 'remote', __( 'Remotely disabled by iThemes.' ) ];
158
+ }
159
+
160
+ $flags = ITSEC_Modules::get_setting( 'global', 'feature_flags' );
161
+
162
+ if ( isset( $flags[ $flag ]['rate'] ) ) {
163
+ return [ 'rollout', __( 'Gradually rolling out.' ) ];
164
+ }
165
+
166
+ return [ 'setting', __( 'Configured in the Feature Flags settings page.' ) ];
167
+ }
168
+
169
  /**
170
  * Manually enable a feature flag.
171
  *
208
  public static function get_flag_config( $flag ) {
209
  self::load();
210
 
 
211
  if ( ! isset( self::$flags[ $flag ] ) ) {
212
  return null;
213
  }
core/lib/class-itsec-lib-fingerprinting.php CHANGED
@@ -1,5 +1,8 @@
1
  <?php
2
 
 
 
 
3
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint.php' );
4
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint-comparison.php' );
5
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint-value.php' );
@@ -155,15 +158,19 @@ class ITSEC_Lib_Fingerprinting {
155
  * @return bool
156
  */
157
  public static function applies_to_user( $user = false ) {
158
-
159
- if ( ! $role = ITSEC_Modules::get_setting( 'fingerprinting', 'role' ) ) {
160
  return false;
161
  }
162
 
163
- require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-canonical-roles.php' );
 
 
164
 
165
  $had_filter = remove_filter( 'user_has_cap', array( 'ITSEC_Fingerprinting', 'restrict_capabilities' ), 10 );
166
- $applies = ITSEC_Lib_Canonical_Roles::is_user_at_least( $role, $user );
 
 
 
167
 
168
  if ( $had_filter ) {
169
  add_filter( 'user_has_cap', array( 'ITSEC_Fingerprinting', 'restrict_capabilities' ), 10, 4 );
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups\Matcher;
4
+ use iThemesSecurity\User_Groups;
5
+
6
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint.php' );
7
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint-comparison.php' );
8
  require_once( dirname( __FILE__ ) . '/fingerprinting/class-itsec-fingerprint-value.php' );
158
  * @return bool
159
  */
160
  public static function applies_to_user( $user = false ) {
161
+ if ( ! $group = ITSEC_Modules::get_setting( 'fingerprinting', 'group' ) ) {
 
162
  return false;
163
  }
164
 
165
+ if ( ! $user = ITSEC_Lib::get_user( $user ) ) {
166
+ return false;
167
+ }
168
 
169
  $had_filter = remove_filter( 'user_has_cap', array( 'ITSEC_Fingerprinting', 'restrict_capabilities' ), 10 );
170
+
171
+ /** @var User_Groups\Matcher $matcher */
172
+ $matcher = ITSEC_Modules::get_container()->get( Matcher::class );
173
+ $applies = $matcher->matches( User_Groups\Match_Target::for_user( $user ), $group);
174
 
175
  if ( $had_filter ) {
176
  add_filter( 'user_has_cap', array( 'ITSEC_Fingerprinting', 'restrict_capabilities' ), 10, 4 );
core/lib/class-itsec-lib-login-interstitial.php CHANGED
@@ -608,7 +608,9 @@ class ITSEC_Lib_Login_Interstitial {
608
  wp_enqueue_script( 'itsec-login-interstitial-util' );
609
  ?>
610
 
611
- <?php if ( $this->error ) : ?>
 
 
612
  <div id="login-error" class="message" style="border-left-color: #dc3232;">
613
  <?php echo $this->error->get_error_message(); ?>
614
  </div>
608
  wp_enqueue_script( 'itsec-login-interstitial-util' );
609
  ?>
610
 
611
+ <?php if ( $this->error && $this->error->get_error_data() === 'message' ) : ?>
612
+ <p class="message"><?php echo $this->error->get_error_message(); ?></p>
613
+ <?php elseif ( $this->error ): ?>
614
  <div id="login-error" class="message" style="border-left-color: #dc3232;">
615
  <?php echo $this->error->get_error_message(); ?>
616
  </div>
core/lib/class-itsec-lib-password-requirements.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Class ITSEC_Lib_Password_Requirements
5
  */
@@ -144,19 +146,25 @@ class ITSEC_Lib_Password_Requirements {
144
  return $error;
145
  }
146
 
147
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
148
 
149
  if ( isset( $args['role'] ) && $user instanceof WP_User ) {
150
  $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $args['role'], $user );
 
151
  } elseif ( isset( $args['role'] ) ) {
 
152
  $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $args['role'] );
153
  } elseif ( empty( $user->ID ) || ! is_numeric( $user->ID ) ) {
154
- $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( get_option( 'default_role', 'subscriber' ) );
 
 
155
  } else {
 
156
  $canonical = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
157
  }
158
 
159
  $args['canonical'] = $canonical;
 
160
 
161
  /**
162
  * Fires when modules should validate a password according to their rules.
@@ -311,4 +319,4 @@ class ITSEC_Lib_Password_Requirements {
311
 
312
  return wp_parse_args( $settings, $requirements[ $requirement ]['defaults'] );
313
  }
314
- }
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups;
4
+
5
  /**
6
  * Class ITSEC_Lib_Password_Requirements
7
  */
146
  return $error;
147
  }
148
 
149
+ ITSEC_Lib::load( 'canonical-roles' );
150
 
151
  if ( isset( $args['role'] ) && $user instanceof WP_User ) {
152
  $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $args['role'], $user );
153
+ $target = User_Groups\Match_Target::for_user( $user, $args['role'] );
154
  } elseif ( isset( $args['role'] ) ) {
155
+ $target = User_Groups\Match_Target::for_role( $args['role'] );
156
  $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $args['role'] );
157
  } elseif ( empty( $user->ID ) || ! is_numeric( $user->ID ) ) {
158
+ $args['role'] = get_option( 'default_role', 'subscriber' );
159
+ $target = User_Groups\Match_Target::for_role( $args['role'] );
160
+ $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $args['role'] );
161
  } else {
162
+ $target = User_Groups\Match_Target::for_user( get_userdata( $user->ID ) );
163
  $canonical = ITSEC_Lib_Canonical_Roles::get_user_role( $user );
164
  }
165
 
166
  $args['canonical'] = $canonical;
167
+ $args['target'] = $target;
168
 
169
  /**
170
  * Fires when modules should validate a password according to their rules.
319
 
320
  return wp_parse_args( $settings, $requirements[ $requirement ]['defaults'] );
321
  }
322
+ }
core/lib/class-itsec-lib-remote-messages.php CHANGED
@@ -63,33 +63,58 @@ class ITSEC_Lib_Remote_Messages {
63
  * @param ITSEC_Job $job
64
  */
65
  public static function run_event( $job ) {
 
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  $response = wp_remote_get( self::URL, array(
68
  'user-agent' => 'WordPress',
69
  ) );
70
 
71
  if ( is_wp_error( $response ) ) {
72
- $job->reschedule_in( 5 * MINUTE_IN_SECONDS );
73
-
74
- return;
75
  }
76
 
77
  $data = wp_remote_retrieve_body( $response );
78
 
79
  if ( ! $data ) {
80
- $job->reschedule_in( 5 * MINUTE_IN_SECONDS );
81
-
82
- return;
83
  }
84
 
85
  $json = json_decode( $data, true );
86
 
87
  if ( ! $json ) {
88
- $job->reschedule_in( 5 * MINUTE_IN_SECONDS );
89
-
90
- return;
91
  }
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  $json = wp_parse_args( $json, array(
94
  'ttl' => HOUR_IN_SECONDS,
95
  'messages' => array(),
@@ -99,6 +124,7 @@ class ITSEC_Lib_Remote_Messages {
99
 
100
  $sanitized = array(
101
  'messages' => array(),
 
102
  'actions' => wp_parse_slug_list( $json['actions'] ),
103
  );
104
 
@@ -110,11 +136,14 @@ class ITSEC_Lib_Remote_Messages {
110
  );
111
  }
112
 
113
- update_site_option( self::OPTION, array(
114
- 'response' => $sanitized,
115
- 'ttl' => $json['ttl'],
116
- 'requested' => ITSEC_Core::get_current_time_gmt(),
117
- ) );
 
 
 
118
  }
119
 
120
  private static function sanitize_message( $message ) {
@@ -135,16 +164,25 @@ class ITSEC_Lib_Remote_Messages {
135
  return array();
136
  }
137
 
138
- if ( isset( self::$_response ) ) {
139
- return self::$_response;
140
  }
141
 
 
 
 
 
 
 
 
 
 
142
  $data = self::get_stored_response();
143
 
144
  if ( ! $data['response'] ) {
145
  self::schedule_check();
146
 
147
- return self::$_response = array();
148
  }
149
 
150
  if ( $data['requested'] + $data['ttl'] < ITSEC_Core::get_current_time_gmt() ) {
@@ -155,7 +193,7 @@ class ITSEC_Lib_Remote_Messages {
155
  // If we are less than an hour late for processing the refresh, return the stale data.
156
  if ( self::EVENT === $event['id'] ) {
157
  if ( $event['fire_at'] + HOUR_IN_SECONDS > ITSEC_Core::get_current_time_gmt() ) {
158
- return self::$_response = $data['response'];
159
  }
160
 
161
  // If its been more than a day, call the API right now.
@@ -164,16 +202,16 @@ class ITSEC_Lib_Remote_Messages {
164
  $data = self::get_stored_response();
165
 
166
  if ( $data['requested'] === ITSEC_Core::get_current_time_gmt() ) {
167
- return self::$_response = $data['response'];
168
  }
169
  }
170
  }
171
  }
172
 
173
- return self::$_response = array();
174
  }
175
 
176
- return self::$_response = $data['response'];
177
  }
178
 
179
  private static function get_stored_response() {
63
  * @param ITSEC_Job $job
64
  */
65
  public static function run_event( $job ) {
66
+ $fetched = self::fetch();
67
 
68
+ if ( is_wp_error( $fetched ) ) {
69
+ $job->reschedule_in( 5 * MINUTE_IN_SECONDS );
70
+
71
+ return;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Fetches and stores the remote messages response.
77
+ *
78
+ * @return null|WP_Error
79
+ */
80
+ public static function fetch() {
81
  $response = wp_remote_get( self::URL, array(
82
  'user-agent' => 'WordPress',
83
  ) );
84
 
85
  if ( is_wp_error( $response ) ) {
86
+ return $response;
 
 
87
  }
88
 
89
  $data = wp_remote_retrieve_body( $response );
90
 
91
  if ( ! $data ) {
92
+ return new WP_Error( 'empty_body', __( 'Empty response body.', 'better-wp-security' ) );
 
 
93
  }
94
 
95
  $json = json_decode( $data, true );
96
 
97
  if ( ! $json ) {
98
+ return new WP_Error( 'invalid_json', __( 'Invalid json response.', 'better-wp-security' ) );
 
 
99
  }
100
 
101
+ update_site_option( self::OPTION, array(
102
+ 'response' => self::sanitize_response( $json ),
103
+ 'ttl' => $json['ttl'],
104
+ 'requested' => ITSEC_Core::get_current_time_gmt(),
105
+ ) );
106
+
107
+ return null;
108
+ }
109
+
110
+ /**
111
+ * Sanitizes the JSON response.
112
+ *
113
+ * @param array $json
114
+ *
115
+ * @return array
116
+ */
117
+ public static function sanitize_response( $json ) {
118
  $json = wp_parse_args( $json, array(
119
  'ttl' => HOUR_IN_SECONDS,
120
  'messages' => array(),
124
 
125
  $sanitized = array(
126
  'messages' => array(),
127
+ 'features' => array(),
128
  'actions' => wp_parse_slug_list( $json['actions'] ),
129
  );
130
 
136
  );
137
  }
138
 
139
+ foreach ( $json['features'] as $feature => $f_config ) {
140
+ $sanitized['features'][ $feature ] = [
141
+ 'rate' => isset( $f_config['rate'] ) ? (int) $f_config['rate'] : false,
142
+ 'disabled' => ! empty( $f_config['disabled'] ),
143
+ ];
144
+ }
145
+
146
+ return $sanitized;
147
  }
148
 
149
  private static function sanitize_message( $message ) {
164
  return array();
165
  }
166
 
167
+ if ( ! isset( self::$_response ) ) {
168
+ self::$_response = self::load_response();
169
  }
170
 
171
+ return apply_filters( 'itsec_remote_messages', self::$_response );
172
+ }
173
+
174
+ /**
175
+ * Loads the response from the local cache or the server.
176
+ *
177
+ * @return array|mixed
178
+ */
179
+ private static function load_response() {
180
  $data = self::get_stored_response();
181
 
182
  if ( ! $data['response'] ) {
183
  self::schedule_check();
184
 
185
+ return array();
186
  }
187
 
188
  if ( $data['requested'] + $data['ttl'] < ITSEC_Core::get_current_time_gmt() ) {
193
  // If we are less than an hour late for processing the refresh, return the stale data.
194
  if ( self::EVENT === $event['id'] ) {
195
  if ( $event['fire_at'] + HOUR_IN_SECONDS > ITSEC_Core::get_current_time_gmt() ) {
196
+ return $data['response'];
197
  }
198
 
199
  // If its been more than a day, call the API right now.
202
  $data = self::get_stored_response();
203
 
204
  if ( $data['requested'] === ITSEC_Core::get_current_time_gmt() ) {
205
+ return $data['response'];
206
  }
207
  }
208
  }
209
  }
210
 
211
+ return array();
212
  }
213
 
214
+ return $data['response'];
215
  }
216
 
217
  private static function get_stored_response() {
core/lib/class-itsec-lib-rest.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Lib_REST {
4
+ const LINK_REL = 'https://s.api.ithemes.com/l/ithemes-security/';
5
+
6
+ /**
7
+ * Get the URI for an iThemes Security link relation.
8
+ *
9
+ * @param string $relation
10
+ *
11
+ * @return string
12
+ */
13
+ public static function get_link_relation( $relation ) {
14
+ return self::LINK_REL . $relation;
15
+ }
16
+
17
+ /**
18
+ * Converts an error to a response object.
19
+ *
20
+ * This iterates over all error codes and messages to change it into a flat
21
+ * array. This enables simpler client behaviour, as it is represented as a
22
+ * list in JSON rather than an object/map.
23
+ *
24
+ * {@see WP_REST_Server::error_to_response()}
25
+ *
26
+ * @param WP_Error $error WP_Error instance.
27
+ *
28
+ * @return WP_REST_Response List of associative arrays with code and message keys.
29
+ */
30
+ public static function error_to_response( WP_Error $error ) {
31
+ $error_data = $error->get_error_data();
32
+
33
+ if ( is_array( $error_data ) && isset( $error_data['status'] ) ) {
34
+ $status = $error_data['status'];
35
+ } else {
36
+ $status = 500;
37
+ }
38
+
39
+ $errors = array();
40
+
41
+ foreach ( (array) $error->errors as $code => $messages ) {
42
+ foreach ( (array) $messages as $message ) {
43
+ $errors[] = array(
44
+ 'code' => $code,
45
+ 'message' => $message,
46
+ 'data' => $error->get_error_data( $code ),
47
+ );
48
+ }
49
+ }
50
+
51
+ $data = $errors[0];
52
+ if ( count( $errors ) > 1 ) {
53
+ // Remove the primary error.
54
+ array_shift( $errors );
55
+ $data['additional_errors'] = $errors;
56
+ }
57
+
58
+ return new WP_REST_Response( $data, $status );
59
+ }
60
+
61
+ /**
62
+ * Get the status code to send from the list of statuses.
63
+ *
64
+ * @param int ...$statuses
65
+ *
66
+ * @return int
67
+ */
68
+ public static function get_status( ...$statuses ) {
69
+ if ( ! $statuses ) {
70
+ return 200;
71
+ }
72
+
73
+ $prev = $statuses[0];
74
+
75
+ foreach ( $statuses as $status ) {
76
+ if ( $prev !== $status ) {
77
+ return WP_Http::MULTI_STATUS;
78
+ }
79
+
80
+ $prev = $status;
81
+ }
82
+
83
+ return $prev;
84
+ }
85
+ }
core/lib/class-itsec-mutex.php ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class ITSEC_Mutex {
4
+ /** @var int */
5
+ private $id;
6
+
7
+ /** @var string */
8
+ private $name;
9
+
10
+ /** @var int */
11
+ private $expires;
12
+
13
+ /**
14
+ * ITSEC_Mutex constructor.
15
+ *
16
+ * @param int $id
17
+ * @param string $name
18
+ * @param int $expires
19
+ */
20
+ private function __construct( $id, $name, $expires ) {
21
+ $this->id = $id;
22
+ $this->name = $name;
23
+ $this->expires = $expires;
24
+ }
25
+
26
+ /**
27
+ * Get a mutex.
28
+ *
29
+ * @param string $name Name of the mutex.
30
+ * @param int $ttl How long to hold the mutex for.
31
+ *
32
+ * @return ITSEC_Mutex|null Returns null if the mutex is already being held.
33
+ */
34
+ public static function get( $name, $ttl = 30 ) {
35
+ global $wpdb;
36
+
37
+ $expires_at = time() + $ttl;
38
+
39
+ while ( ! $id = self::create( $name, $expires_at ) ) {
40
+ $existing = $wpdb->get_row( $wpdb->prepare(
41
+ "SELECT `mutex_id`, `mutex_expires` FROM `{$wpdb->base_prefix}itsec_mutexes` WHERE `mutex_name` = %s",
42
+ $name
43
+ ), ARRAY_A );
44
+
45
+ if ( ! $existing ) {
46
+ continue;
47
+ }
48
+
49
+ if ( $existing['mutex_expires'] < time() ) {
50
+ self::delete_by_id( $existing['mutex_id'] );
51
+ continue;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ return new self( $wpdb->insert_id, $name, $expires_at );
58
+ }
59
+
60
+ /**
61
+ * Release the mutex back.
62
+ */
63
+ public function release() {
64
+ self::delete_by_id( $this->get_id() );
65
+ }
66
+
67
+ /**
68
+ * Has the mutex expired.
69
+ *
70
+ * @return bool
71
+ */
72
+ public function is_expired() {
73
+ return $this->get_expires() < time();
74
+ }
75
+
76
+ /**
77
+ * Checks if this mutex still exists.
78
+ *
79
+ * @return bool
80
+ */
81
+ public function exists() {
82
+ global $wpdb;
83
+
84
+ return (bool) $wpdb->get_var( $wpdb->prepare(
85
+ "SELECT `mutex_id` FROM {$wpdb->base_prefix}itsec_mutexes WHERE `mutex_id` = %d",
86
+ $this->get_id()
87
+ ) );
88
+ }
89
+
90
+ /**
91
+ * Get the mutex's id.
92
+ *
93
+ * @return int
94
+ */
95
+ public function get_id() {
96
+ return $this->id;
97
+ }
98
+
99
+ /**
100
+ * Get the name of the mutex.
101
+ *
102
+ * @return string
103
+ */
104
+ public function get_name() {
105
+ return $this->name;
106
+ }
107
+
108
+ /**
109
+ * Get the epoch the mutex expires at.
110
+ *
111
+ * @return int
112
+ */
113
+ public function get_expires() {
114
+ return $this->expires;
115
+ }
116
+
117
+ /**
118
+ * Delete a mutex by it's id.
119
+ *
120
+ * @param string $id
121
+ */
122
+ private static function delete_by_id( $id ) {
123
+ global $wpdb;
124
+
125
+ $wpdb->delete( $wpdb->base_prefix . 'itsec_mutexes', [
126
+ 'mutex_id' => $id,
127
+ ] );
128
+ }
129
+
130
+ /**
131
+ * Create a mutex record.
132
+ *
133
+ * @param string $name
134
+ * @param int $expires_at
135
+ *
136
+ * @return int The mutex id, or 0 if it could not be acquired.
137
+ */
138
+ private static function create( $name, $expires_at ) {
139
+ global $wpdb;
140
+
141
+ $r = $wpdb->query( $wpdb->prepare(
142
+ "INSERT IGNORE INTO `{$wpdb->base_prefix}itsec_mutexes` (`mutex_name`, `mutex_expires`) VALUES (%s, %s) /* LOCK */",
143
+ $name,
144
+ $expires_at
145
+ ) );
146
+
147
+ if ( ! $r || ! $wpdb->insert_id ) {
148
+ return 0;
149
+ }
150
+
151
+ return (int) $wpdb->insert_id;
152
+ }
153
+ }
core/lib/class-itsec-scheduler.php CHANGED
@@ -372,4 +372,15 @@ abstract class ITSEC_Scheduler {
372
  public function uninstall() {
373
 
374
  }
 
 
 
 
 
 
 
 
 
 
 
375
  }
372
  public function uninstall() {
373
 
374
  }
375
+
376
+ /**
377
+ * Reset the scheduler.
378
+ *
379
+ * This unregisters all events, and re-registers them.
380
+ */
381
+ public function reset() {
382
+ $this->uninstall();
383
+ $this->run();
384
+ $this->register_events();
385
+ }
386
  }
core/lib/form.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  final class ITSEC_Form {
4
  private $options = array();
5
  private $tracked_booleans = array();
@@ -8,7 +10,6 @@ final class ITSEC_Form {
8
  private $input_group = '';
9
  private $input_group_stack = array();
10
 
11
-
12
  public function __construct( $options = array() ) {
13
  $this->options =& $options;
14
  }
@@ -27,10 +28,6 @@ final class ITSEC_Form {
27
  parse_str( $data['data']['--itsec-form-serialized-data'], $data );
28
  }
29
 
30
- if ( get_magic_quotes_gpc() ) {
31
- $data = stripslashes_deep( $data );
32
- }
33
-
34
  $defaults = array(
35
  'booleans' => false,
36
  'strings' => '',
@@ -493,6 +490,54 @@ final class ITSEC_Form {
493
  $this->add_select( $var, $options );
494
  }
495
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  public function get_clean_var( $var ) {
497
  $clean_var = trim( preg_replace( '/[^a-z0-9_]+/i', '-', $var ), '-' );
498
 
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups\Matchables_Source;
4
+
5
  final class ITSEC_Form {
6
  private $options = array();
7
  private $tracked_booleans = array();
10
  private $input_group = '';
11
  private $input_group_stack = array();
12
 
 
13
  public function __construct( $options = array() ) {
14
  $this->options =& $options;
15
  }
28
  parse_str( $data['data']['--itsec-form-serialized-data'], $data );
29
  }
30
 
 
 
 
 
31
  $defaults = array(
32
  'booleans' => false,
33
  'strings' => '',
490
  $this->add_select( $var, $options );
491
  }
492
 
493
+ public function add_user_group( $var, $options = array() ) {
494
+ $source = ITSEC_Modules::get_container()->get( Matchables_Source::class );
495
+
496
+ $user_groups = [];
497
+ foreach ( $source->all() as $matchable ) {
498
+ $user_groups[ $matchable->get_id() ] = $matchable->get_label();
499
+ }
500
+
501
+ if ( isset( $options['value'] ) ) {
502
+ $options['value'] = wp_parse_args( $options['value'], $user_groups );
503
+ } else {
504
+ $options['value'] = $user_groups;
505
+ }
506
+
507
+ $this->add_select( $var, $options );
508
+ }
509
+
510
+ public function add_user_groups( $var, $module, $setting = '', $options = array() ) {
511
+ $source = ITSEC_Modules::get_container()->get( Matchables_Source::class );
512
+
513
+ $user_groups = [];
514
+ foreach ( $source->all() as $matchable ) {
515
+ $user_groups[ $matchable->get_id() ] = $matchable->get_label();
516
+ }
517
+
518
+ if ( isset( $options['value'] ) ) {
519
+ $options['value'] = wp_parse_args( $options['value'], $user_groups );
520
+ } else {
521
+ $options['value'] = $user_groups;
522
+ }
523
+
524
+ $options['data-module'] = $module;
525
+ $options['data-setting'] = $setting ?: $var;
526
+
527
+ $options['class'] = 'itsec-form-input--type-user-groups';
528
+
529
+ $this->add_multi_select( $var, $options );
530
+ wp_enqueue_script( 'itsec-form-user-groups' );
531
+ wp_enqueue_style( 'itsec-jquery-multi-select' );
532
+ }
533
+
534
+ public function get_dotted_var( $var ) {
535
+ $dot = $this->input_group_stack;
536
+ $dot[] = $var;
537
+
538
+ return implode( '.', $dot );
539
+ }
540
+
541
  public function get_clean_var( $var ) {
542
  $clean_var = trim( preg_replace( '/[^a-z0-9_]+/i', '-', $var ), '-' );
543
 
core/lib/lock.php DELETED
@@ -1,47 +0,0 @@
1
- <?php
2
-
3
- final class ITSEC_Lock {
4
- public static function get( $name, $expiration = HOUR_IN_SECONDS, $allow_api_request = false ) {
5
- global $wpdb;
6
-
7
- if ( ! $allow_api_request && ITSEC_Core::is_api_request() ) {
8
- return false;
9
- }
10
-
11
- $lock = "itsec-lock-$name";
12
- $now = time();
13
-
14
- if ( ! empty( $wpdb->sitemeta ) ) {
15
- $result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->sitemeta` (`site_id`, `meta_key`, `meta_value`) VALUES (%d, %s, %s) /* LOCK */", $wpdb->siteid, $lock, $now ) );
16
- } else {
17
- $result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no') /* LOCK */", $lock, $now ) );
18
- }
19
-
20
- if ( ! $result ) {
21
- // The lock exists. See if it has expired.
22
-
23
- $locked = get_site_option( $lock );
24
-
25
- if ( ! $locked ) {
26
- // Can't write or read the lock. Bail due to an unknown and hopefully temporary error.
27
- return false;
28
- }
29
-
30
- if ( $locked > $now - $expiration ) {
31
- // The lock still exists and has not expired.
32
- return false;
33
- }
34
- }
35
-
36
- // Ensure that the lock is set properly by triggering all the regular actions and filters.
37
- update_site_option( $lock, $now );
38
-
39
- return true;
40
- }
41
-
42
- public static function remove( $name ) {
43
- $lock = "itsec-lock-$name";
44
-
45
- delete_site_option( $lock );
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/lib/log-util.php CHANGED
@@ -4,19 +4,24 @@ final class ITSEC_Log_Util {
4
  public static function get_type_counts( $min_timestamp = 0 ) {
5
  global $wpdb;
6
 
7
-
8
- $where = 'parent_id=0';
9
- $prepare_args = array();
10
 
11
  if ( $min_timestamp > 0 ) {
12
- $where .= ' AND init_timestamp>%s';
13
- $prepare_args[] = date( 'Y-m-d H:i:s', $min_timestamp );
 
 
 
 
 
 
14
  }
15
 
16
- $query = "SELECT type, COUNT(*) AS count FROM `{$wpdb->base_prefix}itsec_logs` WHERE $where GROUP BY type";
17
 
18
- if ( ! empty( $prepare_args ) ) {
19
- $query = $wpdb->prepare( $query, $prepare_args );
20
  }
21
 
22
  $results = $wpdb->get_results( $query, ARRAY_A );
@@ -38,6 +43,34 @@ final class ITSEC_Log_Util {
38
  return $counts;
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  public static function get_entries( $filters = array(), $limit = 100, $page = 1, $sort_by_column = 'timestamp', $sort_direction = 'DESC', $columns = false ) {
42
  global $wpdb;
43
 
@@ -124,10 +157,6 @@ final class ITSEC_Log_Util {
124
 
125
  $where_entries = array();
126
 
127
- if ( ! isset( $filters['parent_id'] ) ) {
128
- $filters['parent_id'] = 0;
129
- }
130
-
131
  foreach ( (array) $filters as $column => $value ) {
132
  if ( preg_match( '/^(.+)_not$/', $column, $match ) ) {
133
  $not = true;
@@ -186,8 +215,9 @@ final class ITSEC_Log_Util {
186
  $prepare_args[] = date( 'Y-m-d H:i:s', $max_timestamp );
187
  }
188
 
189
- $query .= ' WHERE ' . implode( ' AND ', $where_entries );
190
-
 
191
 
192
  if ( ! $get_count ) {
193
  if ( ! is_array( $sort_by_column ) ) {
@@ -203,8 +233,9 @@ final class ITSEC_Log_Util {
203
  }
204
  }
205
 
206
- $query = $wpdb->prepare( $query, $prepare_args );
207
-
 
208
 
209
  if ( $get_count ) {
210
  return intval( $wpdb->get_var( $query ) );
4
  public static function get_type_counts( $min_timestamp = 0 ) {
5
  global $wpdb;
6
 
7
+ $where = [];
8
+ $prepare = [];
 
9
 
10
  if ( $min_timestamp > 0 ) {
11
+ $where[] = 'init_timestamp > %s';
12
+ $prepare[] = date( 'Y-m-d H:i:s', $min_timestamp );
13
+ }
14
+
15
+ if ( $where ) {
16
+ $where = 'WHERE ' . implode( ' AND ', $where );
17
+ } else {
18
+ $where = '';
19
  }
20
 
21
+ $query = "SELECT type, COUNT(*) AS count FROM `{$wpdb->base_prefix}itsec_logs` {$where} GROUP BY type";
22
 
23
+ if ( ! empty( $prepare ) ) {
24
+ $query = $wpdb->prepare( $query, $prepare );
25
  }
26
 
27
  $results = $wpdb->get_results( $query, ARRAY_A );
43
  return $counts;
44
  }
45
 
46
+ public static function get_modules() {
47
+ global $wpdb;
48
+
49
+ $items = $wpdb->get_col( "SELECT DISTINCT module FROM {$wpdb->base_prefix}itsec_logs" );
50
+
51
+ if ( ! is_array( $items ) ) {
52
+ return array();
53
+ }
54
+
55
+ $modules = array();
56
+
57
+ foreach ( $items as $module_slug ) {
58
+ $labels = ITSEC_Modules::get_labels( $module_slug );
59
+
60
+ if ( ! $labels ) {
61
+ $labels = ITSEC_Modules::get_labels( str_replace( '_', '-', $module_slug ) );
62
+ }
63
+
64
+ if ( $labels ) {
65
+ $modules[ $module_slug ] = $labels['title'];
66
+ } else {
67
+ $modules[ $module_slug ] = $module_slug;
68
+ }
69
+ }
70
+
71
+ return $modules;
72
+ }
73
+
74
  public static function get_entries( $filters = array(), $limit = 100, $page = 1, $sort_by_column = 'timestamp', $sort_direction = 'DESC', $columns = false ) {
75
  global $wpdb;
76
 
157
 
158
  $where_entries = array();
159
 
 
 
 
 
160
  foreach ( (array) $filters as $column => $value ) {
161
  if ( preg_match( '/^(.+)_not$/', $column, $match ) ) {
162
  $not = true;
215
  $prepare_args[] = date( 'Y-m-d H:i:s', $max_timestamp );
216
  }
217
 
218
+ if ( $where_entries ) {
219
+ $query .= ' WHERE ' . implode( ' AND ', $where_entries );
220
+ }
221
 
222
  if ( ! $get_count ) {
223
  if ( ! is_array( $sort_by_column ) ) {
233
  }
234
  }
235
 
236
+ if ( $prepare_args ) {
237
+ $query = $wpdb->prepare( $query, $prepare_args );
238
+ }
239
 
240
  if ( $get_count ) {
241
  return intval( $wpdb->get_var( $query ) );
core/lib/log.php CHANGED
@@ -245,6 +245,8 @@ final class ITSEC_Log {
245
  'notice' => esc_html__( 'Notice', 'better-wp-security' ),
246
  'debug' => esc_html__( 'Debug', 'better-wp-security' ),
247
  'process-start' => esc_html__( 'Process', 'better-wp-security' ),
 
 
248
  );
249
  }
250
 
245
  'notice' => esc_html__( 'Notice', 'better-wp-security' ),
246
  'debug' => esc_html__( 'Debug', 'better-wp-security' ),
247
  'process-start' => esc_html__( 'Process', 'better-wp-security' ),
248
+ 'process-update' => esc_html__( 'Process Update', 'better-wp-security' ),
249
+ 'process-stop' => esc_html__( 'Process Stop', 'better-wp-security' ),
250
  );
251
  }
252
 
core/lib/login-interstitial/class-itsec-login-interstitial-session.php CHANGED
@@ -183,6 +183,44 @@ class ITSEC_Login_Interstitial_Session {
183
  return $this;
184
  }
185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  /**
187
  * Verify the session.
188
  *
@@ -407,6 +445,8 @@ class ITSEC_Login_Interstitial_Session {
407
  if ( ! empty( $_REQUEST['rememberme'] ) ) {
408
  $this->set_remember_me();
409
  }
 
 
410
  }
411
 
412
  /**
@@ -471,7 +511,7 @@ class ITSEC_Login_Interstitial_Session {
471
  public static function create( WP_User $user, $current = '' ) {
472
  $log = ITSEC_Log::add_process_start( 'login-interstitial', 'create', [
473
  'current' => $current,
474
- '_server' => $_SERVER,
475
  ], [ 'user_id' => $user->ID ] );
476
 
477
  $data = array(
@@ -484,6 +524,7 @@ class ITSEC_Login_Interstitial_Session {
484
  'remember_me' => false,
485
  'interim_login' => false,
486
  'state' => array(),
 
487
  'log' => $log,
488
  );
489
 
183
  return $this;
184
  }
185
 
186
+ /**
187
+ * Get a meta value by key.
188
+ *
189
+ * @param string $key
190
+ *
191
+ * @return mixed|null The meta value, or null if the meta key does not exist.
192
+ */
193
+ public function get_meta( $key ) {
194
+ return isset( $this->data['meta'][ $key ] ) ? $this->data['meta'][ $key ] : null;
195
+ }
196
+
197
+ /**
198
+ * Set meta key-value pair.
199
+ *
200
+ * @param string $key The meta key to set the value for.
201
+ * @param mixed $value The value to set. Must be serializable.
202
+ *
203
+ * @return $this
204
+ */
205
+ public function set_meta( $key, $value ) {
206
+ $this->data['meta'][ $key ] = $value;
207
+
208
+ return $this;
209
+ }
210
+
211
+ /**
212
+ * Remove a meta value by key.
213
+ *
214
+ * @param string $key
215
+ *
216
+ * @return $this
217
+ */
218
+ public function remove_meta( $key ) {
219
+ unset( $this->data['meta'][ $key ] );
220
+
221
+ return $this;
222
+ }
223
+
224
  /**
225
  * Verify the session.
226
  *
445
  if ( ! empty( $_REQUEST['rememberme'] ) ) {
446
  $this->set_remember_me();
447
  }
448
+
449
+ do_action( 'itsec_initialize_login_interstitial_session_from_global_state', $this );
450
  }
451
 
452
  /**
511
  public static function create( WP_User $user, $current = '' ) {
512
  $log = ITSEC_Log::add_process_start( 'login-interstitial', 'create', [
513
  'current' => $current,
514
+ '_server' => ITSEC_Lib::get_server_snapshot(),
515
  ], [ 'user_id' => $user->ID ] );
516
 
517
  $data = array(
524
  'remember_me' => false,
525
  'interim_login' => false,
526
  'state' => array(),
527
+ 'meta' => array(),
528
  'log' => $log,
529
  );
530
 
core/lib/mail-templates/small-code.html CHANGED
@@ -8,7 +8,7 @@
8
  <td class="section-padding" align="center" valign="top" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;padding-top: 10px;padding-right: 20px;padding-left: 20px;">
9
  <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
10
  <tr>
11
- <td class="container-cell" valign="top" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #545454;font-family: monospace;font-size: 16px;font-weight: bolder;line-height: 150%;text-align: center;padding-bottom: 10px;">
12
  {{ $content }}
13
  </td>
14
  </tr>
8
  <td class="section-padding" align="center" valign="top" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;padding-top: 10px;padding-right: 20px;padding-left: 20px;">
9
  <table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
10
  <tr>
11
+ <td class="container-cell small-code" valign="top" style="border-collapse: collapse;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;color: #545454;font-family: monospace;font-size: 16px;font-weight: bolder;line-height: 150%;text-align: center;padding-bottom: 10px;">
12
  {{ $content }}
13
  </td>
14
  </tr>
core/lib/schema.php CHANGED
@@ -1,6 +1,18 @@
1
  <?php
2
 
3
  final class ITSEC_Schema {
 
 
 
 
 
 
 
 
 
 
 
 
4
  /**
5
  * Creates appropriate database tables.
6
  *
@@ -8,7 +20,7 @@ final class ITSEC_Schema {
8
  *
9
  * @since 3.9.0
10
  *
11
- * @return void
12
  */
13
  public static function create_database_tables() {
14
  global $wpdb;
@@ -36,6 +48,7 @@ CREATE TABLE {$wpdb->base_prefix}itsec_logs (
36
  KEY code (code),
37
  KEY type (type),
38
  KEY timestamp (timestamp),
 
39
  KEY user_id (user_id),
40
  KEY blog_id (blog_id)
41
  ) $charset_collate;
@@ -125,10 +138,51 @@ CREATE TABLE {$wpdb->base_prefix}itsec_opaque_tokens (
125
  PRIMARY KEY (token_id),
126
  UNIQUE KEY token_hashed (token_hashed)
127
  ) $charset_collate;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  ";
129
 
130
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
131
- dbDelta( $tables );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
 
134
  public static function remove_database_tables() {
@@ -141,5 +195,7 @@ CREATE TABLE {$wpdb->base_prefix}itsec_opaque_tokens (
141
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_distributed_storage;" );
142
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_geolocation_cache;" );
143
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_fingerprints;" );
 
 
144
  }
145
  }
1
  <?php
2
 
3
  final class ITSEC_Schema {
4
+ const TABLES = [
5
+ 'itsec_logs',
6
+ 'itsec_lockouts',
7
+ 'itsec_temp',
8
+ 'itsec_distributed_storage',
9
+ 'itsec_geolocation_cache',
10
+ 'itsec_fingerprints',
11
+ 'itsec_opaque_tokens',
12
+ 'itsec_user_groups',
13
+ 'itsec_mutexes',
14
+ ];
15
+
16
  /**
17
  * Creates appropriate database tables.
18
  *
20
  *
21
  * @since 3.9.0
22
  *
23
+ * @return true|WP_Error
24
  */
25
  public static function create_database_tables() {
26
  global $wpdb;
48
  KEY code (code),
49
  KEY type (type),
50
  KEY timestamp (timestamp),
51
+ KEY init_timestamp (init_timestamp),
52
  KEY user_id (user_id),
53
  KEY blog_id (blog_id)
54
  ) $charset_collate;
138
  PRIMARY KEY (token_id),
139
  UNIQUE KEY token_hashed (token_hashed)
140
  ) $charset_collate;
141
+
142
+ CREATE TABLE {$wpdb->base_prefix}itsec_user_groups (
143
+ group_id char(36) NOT NULL,
144
+ group_label varchar(255) NOT NULL default '',
145
+ group_roles TEXT,
146
+ group_canonical TEXT,
147
+ group_users TEXT,
148
+ group_min_role varchar(255),
149
+ group_created_at DATETIME,
150
+ PRIMARY KEY (group_id)
151
+ ) $charset_collate;
152
+
153
+ CREATE TABLE {$wpdb->base_prefix}itsec_mutexes (
154
+ mutex_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
155
+ mutex_name varchar(100) NOT NULL,
156
+ mutex_expires int(11) UNSIGNED NOT NULL,
157
+ PRIMARY KEY (mutex_id),
158
+ UNIQUE KEY mutex_name (mutex_name)
159
+ ) $charset_collate;
160
  ";
161
 
162
+ $wp_error = new WP_Error();
163
+ ITSEC_Lib::add_to_wp_error( $wp_error, ITSEC_Lib::db_delta_with_error_handling( $tables ) );
164
+
165
+ foreach ( self::TABLES as $table ) {
166
+ if ( ! count( $wpdb->get_results( "SHOW TABLES LIKE '{$wpdb->base_prefix}{$table}'" ) ) ) {
167
+ $wp_error->add(
168
+ 'missing_table',
169
+ sprintf( __( 'The %s table is not installed.', 'better-wp-security' ), $table )
170
+ );
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Fires when the DB schema is installed or updated.
176
+ *
177
+ * @param WP_Error $wp_error
178
+ */
179
+ do_action( 'itsec_install_schema', $wp_error );
180
+
181
+ if ( $wp_error->has_errors() ) {
182
+ return $wp_error;
183
+ }
184
+
185
+ return true;
186
  }
187
 
188
  public static function remove_database_tables() {
195
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_distributed_storage;" );
196
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_geolocation_cache;" );
197
  $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_fingerprints;" );
198
+ $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_user_groups;" );
199
+ $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_mutexes;" );
200
  }
201
  }
core/lib/settings.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  abstract class ITSEC_Settings {
4
  protected $settings;
5
 
@@ -12,7 +14,24 @@ abstract class ITSEC_Settings {
12
  abstract public function get_id();
13
  abstract public function get_defaults();
14
  protected function after_save() {}
15
- protected function handle_settings_changes( $old_settings ) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
  public function export() {
18
  return $this->settings;
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups;
4
+
5
  abstract class ITSEC_Settings {
6
  protected $settings;
7
 
14
  abstract public function get_id();
15
  abstract public function get_defaults();
16
  protected function after_save() {}
17
+
18
+ protected function handle_settings_changes( $old_settings ) {
19
+ $user_group_settings = ITSEC_Modules::get_container()->get( User_Groups\Settings_Registry::class );
20
+
21
+ foreach ( $user_group_settings->get_settings() as $user_group_setting ) {
22
+ if ( $user_group_setting->get_module() !== $this->get_id() ) {
23
+ continue;
24
+ }
25
+
26
+ $current = ITSEC_Lib::array_get( $this->settings, $user_group_setting->get_setting() );
27
+ $previous = ITSEC_Lib::array_get( $old_settings, $user_group_setting->get_setting() );
28
+
29
+ if ( $previous !== $current ) {
30
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/user-groups', 'fetchGroupsSettings' );
31
+ break;
32
+ }
33
+ }
34
+ }
35
 
36
  public function export() {
37
  return $this->settings;
core/lib/validator.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  abstract class ITSEC_Validator {
4
  protected $run_validate_matching_fields = true;
5
  protected $run_validate_matching_types = true;
@@ -28,11 +30,13 @@ abstract class ITSEC_Validator {
28
  }
29
 
30
  abstract public function get_id();
31
- protected function sanitize_settings() {}
32
- protected function validate_settings() {}
 
 
33
 
34
  public function validate( $settings ) {
35
- $this->settings = $settings;
36
  $this->previous_settings = ITSEC_Modules::get_settings( $this->get_id() );
37
 
38
  $this->sanitize_settings();
@@ -52,14 +56,14 @@ abstract class ITSEC_Validator {
52
  $id = $this->get_id();
53
 
54
  foreach ( array_keys( $this->defaults ) as $name ) {
55
- if ( ! isset( $this->settings[$name] ) && ! in_array( $name, $this->vars_to_skip_validate_matching_fields ) ) {
56
  $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_fields-missing-name-$name", sprintf( __( 'A validation function for %1$s received data that did not have the required entry for %2$s.', 'better-wp-security' ), $id, $name ) ) );
57
  $this->set_can_save( false );
58
  }
59
  }
60
 
61
  foreach ( array_keys( $this->settings ) as $name ) {
62
- if ( ! isset( $this->defaults[$name] ) && ! in_array( $name, $this->vars_to_skip_validate_matching_fields ) ) {
63
  $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_fields-unknown-name-$name", sprintf( __( 'A validation function for %1$s received data that has an entry for %2$s when no such entry exists.', 'better-wp-security' ), $id, $name ) ) );
64
  $this->set_can_save( false );
65
  }
@@ -75,14 +79,14 @@ abstract class ITSEC_Validator {
75
  continue;
76
  }
77
 
78
- if ( ! isset( $this->settings[$name] ) ) {
79
  // Skip missing entries to allow implementations that use validate_matching_types() but not
80
  // validate_matching_fields().
81
  continue;
82
  }
83
 
84
- if ( gettype( $value ) !== gettype( $this->settings[$name] ) ) {
85
- $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_types-inmatching-type-$name", sprintf( __( 'A validation function for %1$s received data that does not match the expected data type for the %2$s entry. A data type of %3$s was expected, but a data type of %4$s was received.', 'better-wp-security' ), $id, $name, gettype( $value ), gettype( $this->settings[$name] ) ) ) );
86
  $this->set_can_save( false );
87
  }
88
  }
@@ -90,23 +94,23 @@ abstract class ITSEC_Validator {
90
 
91
  final protected function set_default_if_empty( $vars ) {
92
  foreach ( (array) $vars as $var ) {
93
- if ( ! isset( $this->settings[$var] ) || '' === $this->settings[$var] ) {
94
- $this->settings[$var] = $this->defaults[$var];
95
  }
96
  }
97
  }
98
 
99
  final protected function set_previous_if_empty( $vars ) {
100
  foreach ( (array) $vars as $var ) {
101
- if ( ! isset( $this->settings[$var] ) || '' === $this->settings[$var] ) {
102
- $this->settings[$var] = $this->previous_settings[$var];
103
  }
104
  }
105
  }
106
 
107
  final protected function preserve_setting_if_exists( $vars ) {
108
  foreach ( (array) $vars as $var ) {
109
- if ( array_key_exists( $var, $this->previous_settings ) && ( ! isset( $this->settings['var'] ) || '' === $this->settings[ $var ] ) ) {
110
  $this->settings[ $var ] = $this->previous_settings[ $var ];
111
  }
112
  }
@@ -115,31 +119,32 @@ abstract class ITSEC_Validator {
115
  final protected function sanitize_setting( $type, $var, $name, $prevent_save_on_error = true, $trim_value = true, $custom_error = '' ) {
116
  $id = $this->get_id();
117
 
118
- if ( ! isset( $this->settings[$var] ) ) {
119
  $this->add_error( new WP_Error( "itsec-validator-missing-var-$id-$var", sprintf( __( 'A validation check for %1$s failed. The %2$s value is missing. This could be due to a problem with the iThemes Security installation or an invalid modification. Please reinstall iThemes Security and try again.', 'better-wp-security' ), $id, $name ) ) );
 
120
  return false;
121
  }
122
 
123
- if ( $trim_value && is_string( $this->settings[$var] ) ) {
124
- $this->settings[$var] = trim( $this->settings[$var] );
125
  }
126
 
127
  $error = false;
128
 
129
  if ( 'string' === $type ) {
130
- $this->settings[$var] = (string) $this->settings[$var];
131
- } else if ( 'non-empty-string' === $type ) {
132
- $this->settings[$var] = (string) $this->settings[$var];
133
 
134
- if ( empty( $this->settings[$var] ) ) {
135
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
136
  }
137
- } else if ( 'title' === $type ) {
138
- $this->settings[$var] = sanitize_title( $this->settings[$var] );
139
- } else if ( 'non-empty-title' === $type ) {
140
- $this->settings[$var] = sanitize_title( $this->settings[$var] );
141
 
142
- if ( empty( $this->settings[$var] ) ) {
143
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
144
  }
145
  } elseif ( 'text' === $type || 'non-empty-text' === $type ) {
@@ -155,63 +160,63 @@ abstract class ITSEC_Validator {
155
  if ( 'non-empty-text' === $type && empty( $this->settings[ $var ] ) ) {
156
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
157
  }
158
- } else if ( 'array' === $type ) {
159
- if ( ! is_array( $this->settings[$var] ) ) {
160
- if ( empty( $this->settings[$var] ) ) {
161
- $this->settings[$var] = array();
162
  } else {
163
- $this->settings[$var] = array( $this->settings[$var] );
164
  }
165
  }
166
- } else if ( 'bool' === $type ) {
167
- if ( 'false' === $this->settings[$var] ) {
168
- $this->settings[$var] = false;
169
- } else if ( 'true' === $this->settings[$var] ) {
170
- $this->settings[$var] = true;
171
  } else {
172
- $this->settings[$var] = (bool) $this->settings[$var];
173
  }
174
- } else if ( 'int' === $type ) {
175
- $test_val = intval( $this->settings[$var] );
176
- if ( (string) $test_val === (string) $this->settings[$var] ) {
177
- $this->settings[$var] = $test_val;
178
  } else {
179
  $error = sprintf( __( 'The %1$s value must be an integer.', 'better-wp-security' ), $name );
180
  }
181
- } else if ( 'positive-int' === $type ) {
182
- $test_val = intval( $this->settings[$var] );
183
- if ( (string) $test_val === (string) $this->settings[$var] && $test_val >= 0 ) {
184
- $this->settings[$var] = $test_val;
185
  } else {
186
  $error = sprintf( __( 'The %1$s value must be a positive integer.', 'better-wp-security' ), $name );
187
  }
188
- } else if ( 'number' === $type ) {
189
- if ( is_numeric($this->settings[ $var ] ) ) {
190
  $this->settings[ $var ] = (float) $this->settings[ $var ];
191
  } else {
192
  $error = sprintf( __( 'The %1$s value must be a number.', 'better-wp-security' ), $name );
193
  }
194
- } else if ( 'email' === $type ) {
195
- $this->settings[$var] = sanitize_text_field( $this->settings[$var] );
196
 
197
- if ( empty( $this->settings[$var] ) || ! is_email( $this->settings[$var] ) ) {
198
  $error = sprintf( __( 'The %1$s value must be a valid email address.', 'better-wp-security' ), $name );
199
  }
200
- } else if ( 'valid-username' === $type ) {
201
- $this->settings[$var] = sanitize_text_field( $this->settings[$var] );
202
 
203
- if ( ! empty( $this->settings[$var] ) && ! validate_username( $this->settings[$var] ) ) {
204
  $error = sprintf( __( 'The %1$s value is not a valid username.', 'better-wp-security' ), $name );
205
  }
206
- } else if ( 'date' === $type ) {
207
- $val = $this->settings[$var];
208
 
209
  $separator = '[\-/\. ]';
210
 
211
  if ( preg_match( "|^(\d\d\d\d)$separator(\d\d?)$separator(\d\d?)$|", $val, $match ) ) {
212
- $year = intval( $match[1] );
213
  $month = intval( $match[2] );
214
- $day = intval( $match[3] );
215
 
216
  if ( ! checkdate( $month, $day, $year ) ) {
217
  $error = sprintf( __( 'The %1$s value must be a valid date.', 'better-wp-security' ), $name );
@@ -219,215 +224,250 @@ abstract class ITSEC_Validator {
219
  } else {
220
  $error = sprintf( __( 'The %1$s value must be a valid date in the format of YYYY-MM-DD.', 'better-wp-security' ), $name );
221
  }
222
- } else if ( 'writable-directory' === $type ) {
223
- if ( ! is_string( $this->settings[$var] ) ) {
224
  $error = sprintf( __( 'The %1$s value must be a string.', 'better-wp-security' ), $name );
225
  } else {
226
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php' );
227
 
228
- $this->settings[$var] = rtrim( $this->settings[$var], DIRECTORY_SEPARATOR );
229
 
230
- if ( ! ITSEC_Lib_Directory::is_dir( $this->settings[$var] ) ) {
231
- $result = ITSEC_Lib_Directory::create( $this->settings[$var] );
232
 
233
  if ( is_wp_error( $result ) ) {
234
  $error = sprintf( _x( 'The directory supplied in %1$s cannot be used as a valid directory. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security' ), $name, $result->get_error_message() );
235
  }
236
  }
237
 
238
- if ( empty( $error ) && ! ITSEC_Lib_Directory::is_writable( $this->settings[$var] ) ) {
239
  $error = sprintf( __( 'The directory supplied in %1$s is not writable. Please select a directory that can be written to.', 'better-wp-security' ), $name );
240
  }
241
 
242
  if ( empty( $error ) ) {
243
- ITSEC_Lib_Directory::add_file_listing_protection( $this->settings[$var] );
244
  }
245
  }
246
- } else if ( 'writable-file' === $type ) {
247
- if ( ! is_string( $this->settings[$var] ) ) {
248
  $error = sprintf( __( 'The %1$s value must be a string.', 'better-wp-security' ), $name );
249
  } else {
250
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php' );
251
 
252
- if ( ! ITSEC_Lib_File::is_file( $this->settings[$var] ) && ITSEC_Lib_File::exists( $this->settings[$var] ) ) {
253
  $error = sprintf( __( 'The file path supplied in %1$s cannot be used as it already exists but is not a file. Please supply a valid file path.', 'better-wp-security' ), $name );
254
  } else {
255
- $result = ITSEC_Lib_Directory::create( dirname( $this->settings[$var] ) );
256
 
257
  if ( is_wp_error( $result ) ) {
258
  $error = sprintf( _x( 'The file path supplied in %1$s cannot be used as the parent directory cannot be created. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security' ), $name, $result->get_error_message() );
259
- } else if ( ! ITSEC_Lib_File::exists( $this->settings[$var] ) ) {
260
- $result = ITSEC_Lib_File::write( $this->settings[$var], '' );
261
 
262
  if ( is_wp_error( $result ) ) {
263
  $error = sprintf( __( 'The file path supplied in %1$s could not be created. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
264
- } else if ( ! is_writable( $this->settings[$var] ) ) {
265
  $error = sprintf( __( 'The file path supplied in %1$s was successfully created, but it cannot be updated. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
266
  }
267
- } else if ( ! is_writable( $this->settings[$var] ) ) {
268
  $error = sprintf( __( 'The file path supplied in %1$s is not writable. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
269
  }
270
  }
271
  }
272
- } else if ( is_array( $type ) && 2 === count( $type ) && $this === $type[0] ) {
273
- $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
274
 
275
- if ( ! is_array( $this->settings[$var] ) ) {
276
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
277
  } else {
278
  $invalid_entries = array();
279
 
280
- foreach ( $this->settings[$var] as $index => $entry ) {
281
- $entry = sanitize_text_field( trim( $entry ) );
282
- $this->settings[$var][$index] = $entry;
283
 
284
  if ( empty( $entry ) ) {
285
- unset( $this->settings[$var][$index] );
286
  } else {
287
  $result = call_user_func( $type, $entry );
288
 
289
  if ( false === $result ) {
290
  $invalid_entries[] = $entry;
291
  } else {
292
- $this->settings[$var][$index] = $result;
293
  }
294
  }
295
  }
296
 
297
- $this->settings[$var] = array_unique( $this->settings[$var] );
298
 
299
  if ( ! empty( $invalid_entries ) ) {
300
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
301
  }
302
  }
303
- } else if ( is_array( $type ) ) {
304
- if ( is_array( $this->settings[$var] ) ) {
305
  $invalid_entries = array();
306
 
307
- foreach ( $this->settings[$var] as $index => $entry ) {
308
- $entry = sanitize_text_field( trim( $entry ) );
309
- $this->settings[$var][$index] = $entry;
310
 
311
  if ( empty( $entry ) ) {
312
- unset( $this->settings[$var][$index] );
313
- } else if ( ! in_array( $entry, $type, true ) ) {
314
  $invalid_entries[] = $entry;
315
  }
316
  }
317
 
318
- $this->settings[$var] = array_unique( $this->settings[$var] );
319
 
320
  if ( ! empty( $invalid_entries ) ) {
321
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
322
  }
323
- } else if ( ! in_array( $this->settings[$var], $type, true ) ) {
324
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
325
- $type = 'array';
326
  }
327
  } elseif ( 'canonical-roles' === $type ) {
328
  $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
329
 
330
- if ( is_array( $this->settings[$var] ) ) {
331
  $invalid_entries = array();
332
 
333
- foreach ( $this->settings[$var] as $index => $entry ) {
334
- $entry = sanitize_text_field( trim( $entry ) );
335
- $this->settings[$var][$index] = $entry;
336
 
337
  if ( empty( $entry ) ) {
338
- unset( $this->settings[$var][$index] );
339
- } else if ( ! in_array( $entry, $roles, true ) ) {
340
  $invalid_entries[] = $entry;
341
  }
342
  }
343
 
344
- $this->settings[$var] = array_unique( $this->settings[$var] );
345
 
346
  if ( ! empty( $invalid_entries ) ) {
347
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
348
  }
349
- } else if ( ! in_array( $this->settings[$var], $roles, true ) ) {
350
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $roles ), 'better-wp-security' ), $name, $roles );
351
- $type = 'array';
352
  }
353
- } else if ( 'newline-separated-array' === $type ) {
354
- $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
355
 
356
- if ( ! is_array( $this->settings[$var] ) ) {
357
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
358
  }
359
- } else if ( 'newline-separated-emails' === $type ) {
360
- $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
361
 
362
- if ( ! is_array( $this->settings[$var] ) ) {
363
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
364
  } else {
365
  $invalid_emails = array();
366
 
367
- foreach ( $this->settings[$var] as $index => $email ) {
368
- $email = sanitize_text_field( trim( $email ) );
369
- $this->settings[$var][$index] = $email;
370
 
371
  if ( empty( $email ) ) {
372
- unset( $this->settings[$var][$index] );
373
- } else if ( ! is_email( $email ) ) {
374
  $invalid_emails[] = $email;
375
  }
376
  }
377
 
378
- $this->settings[$var] = array_unique( $this->settings[$var] );
379
 
380
  if ( ! empty( $invalid_emails ) ) {
381
  $error = wp_sprintf( _n( 'The following email in %1$s is invalid: %2$l', 'The following emails in %1$s are invalid: %2$l', count( $invalid_emails ), 'better-wp-security' ), $name, $invalid_emails );
382
  }
383
  }
384
- } else if ( 'newline-separated-ips' === $type ) {
385
- $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
386
 
387
- if ( ! is_array( $this->settings[$var] ) ) {
388
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
389
  } else {
390
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-ip-tools.php' );
391
 
392
  $invalid_ips = array();
393
 
394
- foreach ( $this->settings[$var] as $index => $ip ) {
395
  $ip = trim( $ip );
396
 
397
  if ( '' === $ip ) {
398
- unset( $this->settings[$var][$index] );
399
  } else {
400
  $validated_ip = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( $ip );
401
 
402
  if ( false === $validated_ip ) {
403
  $invalid_ips[] = $ip;
404
  } else {
405
- $this->settings[$var][$index] = $validated_ip;
406
  }
407
  }
408
  }
409
 
410
- $this->settings[$var] = array_unique( $this->settings[$var] );
411
 
412
  if ( ! empty( $invalid_ips ) ) {
413
  $error = wp_sprintf( _n( 'The following IP in %1$s is invalid: %2$l', 'The following IPs in %1$s are invalid: %2$l', count( $invalid_ips ), 'better-wp-security' ), $name, $invalid_ips );
414
  }
415
  }
416
- } else if ( 'newline-separated-extensions' === $type ) {
417
- $this->settings[$var] = $this->convert_string_to_array( $this->settings[$var] );
 
 
 
 
 
 
 
 
 
 
418
 
419
- if ( ! is_array( $this->settings[$var] ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
421
  } else {
422
  $invalid_extensions = array();
423
 
424
- foreach ( $this->settings[$var] as $index => $extension ) {
425
  if ( ! preg_match( '/^(\.[^.]+)+$/', $extension ) ) {
426
  $invalid_extensions[] = $extension;
427
  }
428
  }
429
 
430
- $this->settings[$var] = array_unique( $this->settings[$var] );
431
 
432
  if ( ! empty( $invalid_extensions ) ) {
433
  $error = wp_sprintf( _n( 'The following extension in %1$s is invalid: %2$l', 'The following extensions in %1$s are invalid: %2$l', count( $invalid_extensions ), 'better-wp-security' ), $name, $invalid_extensions );
@@ -452,7 +492,7 @@ abstract class ITSEC_Validator {
452
  if ( false === $result ) {
453
  $invalid_entries[] = is_string( $entry ) ? $entry : $index;
454
  } elseif ( is_wp_error( $result ) ) {
455
- $invalid_entries[] = "'{$index}': {$result->get_error_message()}";
456
  } else {
457
  $this->settings[ $var ][ $index ] = $result;
458
  }
@@ -494,7 +534,7 @@ abstract class ITSEC_Validator {
494
  final protected function convert_string_to_array( $string ) {
495
  if ( is_string( $string ) ) {
496
  $array = preg_split( "/[\r\n]+/", $string );
497
- } else if ( is_array( $string ) ) {
498
  $array = $string;
499
  } else {
500
  return $string;
@@ -504,9 +544,9 @@ abstract class ITSEC_Validator {
504
  $val = trim( $val );
505
 
506
  if ( empty( $val ) ) {
507
- unset( $array[$key] );
508
  } else {
509
- $array[$key] = $val;
510
  }
511
  }
512
 
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups\Matchables_Source;
4
+
5
  abstract class ITSEC_Validator {
6
  protected $run_validate_matching_fields = true;
7
  protected $run_validate_matching_types = true;
30
  }
31
 
32
  abstract public function get_id();
33
+
34
+ protected function sanitize_settings() { }
35
+
36
+ protected function validate_settings() { }
37
 
38
  public function validate( $settings ) {
39
+ $this->settings = $settings;
40
  $this->previous_settings = ITSEC_Modules::get_settings( $this->get_id() );
41
 
42
  $this->sanitize_settings();
56
  $id = $this->get_id();
57
 
58
  foreach ( array_keys( $this->defaults ) as $name ) {
59
+ if ( ! isset( $this->settings[ $name ] ) && ! in_array( $name, $this->vars_to_skip_validate_matching_fields ) ) {
60
  $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_fields-missing-name-$name", sprintf( __( 'A validation function for %1$s received data that did not have the required entry for %2$s.', 'better-wp-security' ), $id, $name ) ) );
61
  $this->set_can_save( false );
62
  }
63
  }
64
 
65
  foreach ( array_keys( $this->settings ) as $name ) {
66
+ if ( ! isset( $this->defaults[ $name ] ) && ! in_array( $name, $this->vars_to_skip_validate_matching_fields ) ) {
67
  $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_fields-unknown-name-$name", sprintf( __( 'A validation function for %1$s received data that has an entry for %2$s when no such entry exists.', 'better-wp-security' ), $id, $name ) ) );
68
  $this->set_can_save( false );
69
  }
79
  continue;
80
  }
81
 
82
+ if ( ! isset( $this->settings[ $name ] ) ) {
83
  // Skip missing entries to allow implementations that use validate_matching_types() but not
84
  // validate_matching_fields().
85
  continue;
86
  }
87
 
88
+ if ( gettype( $value ) !== gettype( $this->settings[ $name ] ) ) {
89
+ $this->add_error( new WP_Error( "itsec-validator-$id-validate_matching_types-inmatching-type-$name", sprintf( __( 'A validation function for %1$s received data that does not match the expected data type for the %2$s entry. A data type of %3$s was expected, but a data type of %4$s was received.', 'better-wp-security' ), $id, $name, gettype( $value ), gettype( $this->settings[ $name ] ) ) ) );
90
  $this->set_can_save( false );
91
  }
92
  }
94
 
95
  final protected function set_default_if_empty( $vars ) {
96
  foreach ( (array) $vars as $var ) {
97
+ if ( ! isset( $this->settings[ $var ] ) || '' === $this->settings[ $var ] ) {
98
+ $this->settings[ $var ] = $this->defaults[ $var ];
99
  }
100
  }
101
  }
102
 
103
  final protected function set_previous_if_empty( $vars ) {
104
  foreach ( (array) $vars as $var ) {
105
+ if ( ! isset( $this->settings[ $var ] ) || '' === $this->settings[ $var ] ) {
106
+ $this->settings[ $var ] = $this->previous_settings[ $var ];
107
  }
108
  }
109
  }
110
 
111
  final protected function preserve_setting_if_exists( $vars ) {
112
  foreach ( (array) $vars as $var ) {
113
+ if ( array_key_exists( $var, $this->previous_settings ) && ( ! isset( $this->settings[ $var ] ) || '' === $this->settings[ $var ] ) ) {
114
  $this->settings[ $var ] = $this->previous_settings[ $var ];
115
  }
116
  }
119
  final protected function sanitize_setting( $type, $var, $name, $prevent_save_on_error = true, $trim_value = true, $custom_error = '' ) {
120
  $id = $this->get_id();
121
 
122
+ if ( ! isset( $this->settings[ $var ] ) ) {
123
  $this->add_error( new WP_Error( "itsec-validator-missing-var-$id-$var", sprintf( __( 'A validation check for %1$s failed. The %2$s value is missing. This could be due to a problem with the iThemes Security installation or an invalid modification. Please reinstall iThemes Security and try again.', 'better-wp-security' ), $id, $name ) ) );
124
+
125
  return false;
126
  }
127
 
128
+ if ( $trim_value && is_string( $this->settings[ $var ] ) ) {
129
+ $this->settings[ $var ] = trim( $this->settings[ $var ] );
130
  }
131
 
132
  $error = false;
133
 
134
  if ( 'string' === $type ) {
135
+ $this->settings[ $var ] = (string) $this->settings[ $var ];
136
+ } elseif ( 'non-empty-string' === $type ) {
137
+ $this->settings[ $var ] = (string) $this->settings[ $var ];
138
 
139
+ if ( empty( $this->settings[ $var ] ) ) {
140
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
141
  }
142
+ } elseif ( 'title' === $type ) {
143
+ $this->settings[ $var ] = sanitize_title( $this->settings[ $var ] );
144
+ } elseif ( 'non-empty-title' === $type ) {
145
+ $this->settings[ $var ] = sanitize_title( $this->settings[ $var ] );
146
 
147
+ if ( empty( $this->settings[ $var ] ) ) {
148
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
149
  }
150
  } elseif ( 'text' === $type || 'non-empty-text' === $type ) {
160
  if ( 'non-empty-text' === $type && empty( $this->settings[ $var ] ) ) {
161
  $error = sprintf( __( 'The %1$s value cannot be empty.', 'better-wp-security' ), $name );
162
  }
163
+ } elseif ( 'array' === $type ) {
164
+ if ( ! is_array( $this->settings[ $var ] ) ) {
165
+ if ( empty( $this->settings[ $var ] ) ) {
166
+ $this->settings[ $var ] = array();
167
  } else {
168
+ $this->settings[ $var ] = array( $this->settings[ $var ] );
169
  }
170
  }
171
+ } elseif ( 'bool' === $type ) {
172
+ if ( 'false' === $this->settings[ $var ] ) {
173
+ $this->settings[ $var ] = false;
174
+ } elseif ( 'true' === $this->settings[ $var ] ) {
175
+ $this->settings[ $var ] = true;
176
  } else {
177
+ $this->settings[ $var ] = (bool) $this->settings[ $var ];
178
  }
179
+ } elseif ( 'int' === $type ) {
180
+ $test_val = intval( $this->settings[ $var ] );
181
+ if ( (string) $test_val === (string) $this->settings[ $var ] ) {
182
+ $this->settings[ $var ] = $test_val;
183
  } else {
184
  $error = sprintf( __( 'The %1$s value must be an integer.', 'better-wp-security' ), $name );
185
  }
186
+ } elseif ( 'positive-int' === $type ) {
187
+ $test_val = intval( $this->settings[ $var ] );
188
+ if ( (string) $test_val === (string) $this->settings[ $var ] && $test_val >= 0 ) {
189
+ $this->settings[ $var ] = $test_val;
190
  } else {
191
  $error = sprintf( __( 'The %1$s value must be a positive integer.', 'better-wp-security' ), $name );
192
  }
193
+ } elseif ( 'number' === $type ) {
194
+ if ( is_numeric( $this->settings[ $var ] ) ) {
195
  $this->settings[ $var ] = (float) $this->settings[ $var ];
196
  } else {
197
  $error = sprintf( __( 'The %1$s value must be a number.', 'better-wp-security' ), $name );
198
  }
199
+ } elseif ( 'email' === $type ) {
200
+ $this->settings[ $var ] = sanitize_text_field( $this->settings[ $var ] );
201
 
202
+ if ( empty( $this->settings[ $var ] ) || ! is_email( $this->settings[ $var ] ) ) {
203
  $error = sprintf( __( 'The %1$s value must be a valid email address.', 'better-wp-security' ), $name );
204
  }
205
+ } elseif ( 'valid-username' === $type ) {
206
+ $this->settings[ $var ] = sanitize_text_field( $this->settings[ $var ] );
207
 
208
+ if ( ! empty( $this->settings[ $var ] ) && ! validate_username( $this->settings[ $var ] ) ) {
209
  $error = sprintf( __( 'The %1$s value is not a valid username.', 'better-wp-security' ), $name );
210
  }
211
+ } elseif ( 'date' === $type ) {
212
+ $val = $this->settings[ $var ];
213
 
214
  $separator = '[\-/\. ]';
215
 
216
  if ( preg_match( "|^(\d\d\d\d)$separator(\d\d?)$separator(\d\d?)$|", $val, $match ) ) {
217
+ $year = intval( $match[1] );
218
  $month = intval( $match[2] );
219
+ $day = intval( $match[3] );
220
 
221
  if ( ! checkdate( $month, $day, $year ) ) {
222
  $error = sprintf( __( 'The %1$s value must be a valid date.', 'better-wp-security' ), $name );
224
  } else {
225
  $error = sprintf( __( 'The %1$s value must be a valid date in the format of YYYY-MM-DD.', 'better-wp-security' ), $name );
226
  }
227
+ } elseif ( 'writable-directory' === $type ) {
228
+ if ( ! is_string( $this->settings[ $var ] ) ) {
229
  $error = sprintf( __( 'The %1$s value must be a string.', 'better-wp-security' ), $name );
230
  } else {
231
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php' );
232
 
233
+ $this->settings[ $var ] = rtrim( $this->settings[ $var ], DIRECTORY_SEPARATOR );
234
 
235
+ if ( ! ITSEC_Lib_Directory::is_dir( $this->settings[ $var ] ) ) {
236
+ $result = ITSEC_Lib_Directory::create( $this->settings[ $var ] );
237
 
238
  if ( is_wp_error( $result ) ) {
239
  $error = sprintf( _x( 'The directory supplied in %1$s cannot be used as a valid directory. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security' ), $name, $result->get_error_message() );
240
  }
241
  }
242
 
243
+ if ( empty( $error ) && ! ITSEC_Lib_Directory::is_writable( $this->settings[ $var ] ) ) {
244
  $error = sprintf( __( 'The directory supplied in %1$s is not writable. Please select a directory that can be written to.', 'better-wp-security' ), $name );
245
  }
246
 
247
  if ( empty( $error ) ) {
248
+ ITSEC_Lib_Directory::add_file_listing_protection( $this->settings[ $var ] );
249
  }
250
  }
251
+ } elseif ( 'writable-file' === $type ) {
252
+ if ( ! is_string( $this->settings[ $var ] ) ) {
253
  $error = sprintf( __( 'The %1$s value must be a string.', 'better-wp-security' ), $name );
254
  } else {
255
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-directory.php' );
256
 
257
+ if ( ! ITSEC_Lib_File::is_file( $this->settings[ $var ] ) && ITSEC_Lib_File::exists( $this->settings[ $var ] ) ) {
258
  $error = sprintf( __( 'The file path supplied in %1$s cannot be used as it already exists but is not a file. Please supply a valid file path.', 'better-wp-security' ), $name );
259
  } else {
260
+ $result = ITSEC_Lib_Directory::create( dirname( $this->settings[ $var ] ) );
261
 
262
  if ( is_wp_error( $result ) ) {
263
  $error = sprintf( _x( 'The file path supplied in %1$s cannot be used as the parent directory cannot be created. %2$s', '%1$s is the input name. %2$s is the error message.', 'better-wp-security' ), $name, $result->get_error_message() );
264
+ } elseif ( ! ITSEC_Lib_File::exists( $this->settings[ $var ] ) ) {
265
+ $result = ITSEC_Lib_File::write( $this->settings[ $var ], '' );
266
 
267
  if ( is_wp_error( $result ) ) {
268
  $error = sprintf( __( 'The file path supplied in %1$s could not be created. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
269
+ } elseif ( ! is_writable( $this->settings[ $var ] ) ) {
270
  $error = sprintf( __( 'The file path supplied in %1$s was successfully created, but it cannot be updated. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
271
  }
272
+ } elseif ( ! is_writable( $this->settings[ $var ] ) ) {
273
  $error = sprintf( __( 'The file path supplied in %1$s is not writable. Please supply a file path that can be written to.', 'better-wp-security' ), $name );
274
  }
275
  }
276
  }
277
+ } elseif ( is_array( $type ) && 2 === count( $type ) && $this === $type[0] ) {
278
+ $this->settings[ $var ] = $this->convert_string_to_array( $this->settings[ $var ] );
279
 
280
+ if ( ! is_array( $this->settings[ $var ] ) ) {
281
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
282
  } else {
283
  $invalid_entries = array();
284
 
285
+ foreach ( $this->settings[ $var ] as $index => $entry ) {
286
+ $entry = sanitize_text_field( trim( $entry ) );
287
+ $this->settings[ $var ][ $index ] = $entry;
288
 
289
  if ( empty( $entry ) ) {
290
+ unset( $this->settings[ $var ][ $index ] );
291
  } else {
292
  $result = call_user_func( $type, $entry );
293
 
294
  if ( false === $result ) {
295
  $invalid_entries[] = $entry;
296
  } else {
297
+ $this->settings[ $var ][ $index ] = $result;
298
  }
299
  }
300
  }
301
 
302
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
303
 
304
  if ( ! empty( $invalid_entries ) ) {
305
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
306
  }
307
  }
308
+ } elseif ( is_array( $type ) ) {
309
+ if ( is_array( $this->settings[ $var ] ) ) {
310
  $invalid_entries = array();
311
 
312
+ foreach ( $this->settings[ $var ] as $index => $entry ) {
313
+ $entry = sanitize_text_field( trim( $entry ) );
314
+ $this->settings[ $var ][ $index ] = $entry;
315
 
316
  if ( empty( $entry ) ) {
317
+ unset( $this->settings[ $var ][ $index ] );
318
+ } elseif ( ! in_array( $entry, $type, true ) ) {
319
  $invalid_entries[] = $entry;
320
  }
321
  }
322
 
323
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
324
 
325
  if ( ! empty( $invalid_entries ) ) {
326
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
327
  }
328
+ } elseif ( ! in_array( $this->settings[ $var ], $type, true ) ) {
329
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $type ), 'better-wp-security' ), $name, $type );
330
+ $type = 'array';
331
  }
332
  } elseif ( 'canonical-roles' === $type ) {
333
  $roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
334
 
335
+ if ( is_array( $this->settings[ $var ] ) ) {
336
  $invalid_entries = array();
337
 
338
+ foreach ( $this->settings[ $var ] as $index => $entry ) {
339
+ $entry = sanitize_text_field( trim( $entry ) );
340
+ $this->settings[ $var ][ $index ] = $entry;
341
 
342
  if ( empty( $entry ) ) {
343
+ unset( $this->settings[ $var ][ $index ] );
344
+ } elseif ( ! in_array( $entry, $roles, true ) ) {
345
  $invalid_entries[] = $entry;
346
  }
347
  }
348
 
349
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
350
 
351
  if ( ! empty( $invalid_entries ) ) {
352
  $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_entries ), 'better-wp-security' ), $name, $invalid_entries );
353
  }
354
+ } elseif ( ! in_array( $this->settings[ $var ], $roles, true ) ) {
355
  $error = wp_sprintf( _n( 'The valid value for %1$s is: %2$l.', 'The valid values for %1$s are: %2$l.', count( $roles ), 'better-wp-security' ), $name, $roles );
356
+ $type = 'array';
357
  }
358
+ } elseif ( 'newline-separated-array' === $type ) {
359
+ $this->settings[ $var ] = $this->convert_string_to_array( $this->settings[ $var ] );
360
 
361
+ if ( ! is_array( $this->settings[ $var ] ) ) {
362
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
363
  }
364
+ } elseif ( 'newline-separated-emails' === $type ) {
365
+ $this->settings[ $var ] = $this->convert_string_to_array( $this->settings[ $var ] );
366
 
367
+ if ( ! is_array( $this->settings[ $var ] ) ) {
368
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
369
  } else {
370
  $invalid_emails = array();
371
 
372
+ foreach ( $this->settings[ $var ] as $index => $email ) {
373
+ $email = sanitize_text_field( trim( $email ) );
374
+ $this->settings[ $var ][ $index ] = $email;
375
 
376
  if ( empty( $email ) ) {
377
+ unset( $this->settings[ $var ][ $index ] );
378
+ } elseif ( ! is_email( $email ) ) {
379
  $invalid_emails[] = $email;
380
  }
381
  }
382
 
383
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
384
 
385
  if ( ! empty( $invalid_emails ) ) {
386
  $error = wp_sprintf( _n( 'The following email in %1$s is invalid: %2$l', 'The following emails in %1$s are invalid: %2$l', count( $invalid_emails ), 'better-wp-security' ), $name, $invalid_emails );
387
  }
388
  }
389
+ } elseif ( 'newline-separated-ips' === $type ) {
390
+ $this->settings[ $var ] = $this->convert_string_to_array( $this->settings[ $var ] );
391
 
392
+ if ( ! is_array( $this->settings[ $var ] ) ) {
393
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
394
  } else {
395
  require_once( ITSEC_Core::get_core_dir() . 'lib/class-itsec-lib-ip-tools.php' );
396
 
397
  $invalid_ips = array();
398
 
399
+ foreach ( $this->settings[ $var ] as $index => $ip ) {
400
  $ip = trim( $ip );
401
 
402
  if ( '' === $ip ) {
403
+ unset( $this->settings[ $var ][ $index ] );
404
  } else {
405
  $validated_ip = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( $ip );
406
 
407
  if ( false === $validated_ip ) {
408
  $invalid_ips[] = $ip;
409
  } else {
410
+ $this->settings[ $var ][ $index ] = $validated_ip;
411
  }
412
  }
413
  }
414
 
415
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
416
 
417
  if ( ! empty( $invalid_ips ) ) {
418
  $error = wp_sprintf( _n( 'The following IP in %1$s is invalid: %2$l', 'The following IPs in %1$s are invalid: %2$l', count( $invalid_ips ), 'better-wp-security' ), $name, $invalid_ips );
419
  }
420
  }
421
+ } elseif ( 'user-groups' === $type ) {
422
+ $source = ITSEC_Modules::get_container()->get( Matchables_Source::class );
423
+
424
+ $this->sanitize_setting( 'array', $var, $name, $prevent_save_on_error, $trim_value, $custom_error );
425
+ $invalid_user_groups = [];
426
+
427
+ foreach ( $this->settings[ $var ] as $i => $group ) {
428
+ if ( ! is_string( $group ) ) {
429
+ unset( $this->settings[ $var ][ $i ] );
430
+
431
+ continue;
432
+ }
433
 
434
+ if ( $source->has( $group ) ) {
435
+ continue;
436
+ }
437
+
438
+ if ( in_array( $group, $this->previous_settings[ $var ], true ) ) {
439
+ unset( $this->settings[ $var ][ $i ] );
440
+ } else {
441
+ $invalid_user_groups[] = $group;
442
+ }
443
+ }
444
+
445
+ $this->settings[ $var ] = wp_is_numeric_array( $this->settings[ $var ] ) ? array_values( $this->settings[ $var ] ) : $this->settings[ $var ];
446
+
447
+ if ( $invalid_user_groups ) {
448
+ $error = wp_sprintf( _n( 'The following entry in %1$s is invalid: %2$l', 'The following entries in %1$s are invalid: %2$l', count( $invalid_user_groups ), 'better-wp-security' ), $name, $invalid_user_groups );
449
+ }
450
+ } elseif ( 'user-group' === $type ) {
451
+ $source = ITSEC_Modules::get_container()->get( Matchables_Source::class );
452
+
453
+ if ( ! $source->has( $this->settings[ $var ] ) ) {
454
+ $error = sprintf( __( 'The user group selected for %1$s is invalid.', 'better-wp-security' ), $name );
455
+ }
456
+ } elseif ( 'newline-separated-extensions' === $type ) {
457
+ $this->settings[ $var ] = $this->convert_string_to_array( $this->settings[ $var ] );
458
+
459
+ if ( ! is_array( $this->settings[ $var ] ) ) {
460
  $error = sprintf( __( 'The %1$s value must be a string with each entry separated by a new line.', 'better-wp-security' ), $name );
461
  } else {
462
  $invalid_extensions = array();
463
 
464
+ foreach ( $this->settings[ $var ] as $index => $extension ) {
465
  if ( ! preg_match( '/^(\.[^.]+)+$/', $extension ) ) {
466
  $invalid_extensions[] = $extension;
467
  }
468
  }
469
 
470
+ $this->settings[ $var ] = array_unique( $this->settings[ $var ] );
471
 
472
  if ( ! empty( $invalid_extensions ) ) {
473
  $error = wp_sprintf( _n( 'The following extension in %1$s is invalid: %2$l', 'The following extensions in %1$s are invalid: %2$l', count( $invalid_extensions ), 'better-wp-security' ), $name, $invalid_extensions );
492
  if ( false === $result ) {
493
  $invalid_entries[] = is_string( $entry ) ? $entry : $index;
494
  } elseif ( is_wp_error( $result ) ) {
495
+ $invalid_entries[] = "'{$index}': {$result->get_error_message()}";
496
  } else {
497
  $this->settings[ $var ][ $index ] = $result;
498
  }
534
  final protected function convert_string_to_array( $string ) {
535
  if ( is_string( $string ) ) {
536
  $array = preg_split( "/[\r\n]+/", $string );
537
+ } elseif ( is_array( $string ) ) {
538
  $array = $string;
539
  } else {
540
  return $string;
544
  $val = trim( $val );
545
 
546
  if ( empty( $val ) ) {
547
+ unset( $array[ $key ] );
548
  } else {
549
+ $array[ $key ] = $val;
550
  }
551
  }
552
 
core/modules.php CHANGED
@@ -1,5 +1,9 @@
1
  <?php
2
 
 
 
 
 
3
  final class ITSEC_Modules {
4
  /**
5
  * @var ITSEC_Modules - Static property to hold our singleton instance
@@ -15,11 +19,25 @@ final class ITSEC_Modules {
15
  private $_module_settings = false;
16
  private $_module_validators = false;
17
  private $_settings_files_loaded = false;
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  protected function __construct() {
20
  // Action triggered from another part of Security which runs when the settings page is loaded.
21
  add_action( 'itsec-settings-page-init', array( $this, 'load_settings_page' ) );
22
  add_action( 'itsec-logs-page-init', array( $this, 'load_settings_page' ) );
 
 
 
23
  }
24
 
25
  /**
@@ -52,16 +70,17 @@ final class ITSEC_Modules {
52
 
53
  if ( ! is_dir( $path ) ) {
54
  trigger_error( sprintf( __( 'An attempt to register the %1$s module failed since the supplied path (%2$s) is invalid. This could indicate an invalid modification or incomplete installation of the iThemes Security plugin. Please reinstall the plugin and try again.', 'better-wp-security' ), $slug, $path ) );
 
55
  return false;
56
  }
57
 
58
  $self->_module_paths[ $slug ] = $path;
59
- $self->_available_modules = array_keys( $self->_module_paths );
60
 
61
  if ( 'always-active' === $type ) {
62
- $self->_always_active_modules[$slug] = true;
63
- } else if ( 'default-active' === $type ) {
64
- $self->_default_active_modules[$slug] = true;
65
  }
66
 
67
  return true;
@@ -84,8 +103,7 @@ final class ITSEC_Modules {
84
  unset( $self->_module_paths[ $slug ] );
85
  $self->_available_modules = array_keys( $self->_module_paths );
86
 
87
- unset( $self->_always_active_modules[$slug] );
88
- unset( $self->_default_active_modules[$slug] );
89
 
90
  return true;
91
  }
@@ -105,7 +123,7 @@ final class ITSEC_Modules {
105
  $self = self::get_instance();
106
 
107
  foreach ( $self->_module_paths as $slug => $path ) {
108
- $self->_module_paths[$slug] = str_replace( $old_dir, $new_dir, $path );
109
  }
110
  }
111
 
@@ -115,8 +133,7 @@ final class ITSEC_Modules {
115
  * @param ITSEC_Settings $settings
116
  */
117
  public static function register_settings( $settings ) {
118
- $self = self::get_instance();
119
- $self->_module_settings[ $settings->get_id() ] = $settings;
120
  }
121
 
122
  /**
@@ -150,8 +167,6 @@ final class ITSEC_Modules {
150
  * @return array
151
  */
152
  public static function get_defaults( $slug ) {
153
- $self = self::get_instance();
154
-
155
  $settings_obj = self::get_settings_obj( $slug );
156
 
157
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get_defaults' ) ) ) {
@@ -171,12 +186,10 @@ final class ITSEC_Modules {
171
  * @return mixed
172
  */
173
  public static function get_default( $slug, $name, $default = null ) {
174
- $self = self::get_instance();
175
-
176
  $defaults = self::get_defaults( $slug );
177
 
178
- if ( isset( $defaults[$name] ) ) {
179
- return $defaults[$name];
180
  }
181
 
182
  return $default;
@@ -190,8 +203,6 @@ final class ITSEC_Modules {
190
  * @return array
191
  */
192
  public static function get_settings( $slug ) {
193
- $self = self::get_instance();
194
-
195
  $settings_obj = self::get_settings_obj( $slug );
196
 
197
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get_all' ) ) ) {
@@ -212,8 +223,6 @@ final class ITSEC_Modules {
212
  * @return mixed
213
  */
214
  public static function get_setting( $slug, $name, $default = null ) {
215
- $self = self::get_instance();
216
-
217
  $settings_obj = self::get_settings_obj( $slug );
218
 
219
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get' ) ) ) {
@@ -234,8 +243,6 @@ final class ITSEC_Modules {
234
  * @return array|WP_Error
235
  */
236
  public static function set_settings( $slug, $settings ) {
237
- $self = self::get_instance();
238
-
239
  $settings_obj = self::get_settings_obj( $slug );
240
 
241
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'set_all' ) ) ) {
@@ -254,19 +261,18 @@ final class ITSEC_Modules {
254
  * The new value will be validated and updated in memory. The change isn't persisted until
255
  * the end of the request or a manual call to {@see ITSEC_Storage::save()}.
256
  *
257
- * @param string $slug The module slug.
258
- * @param string $name The setting name to updated.
259
- * @param mixed $value The settings' new value.
260
  *
261
  * @return array|false
262
  */
263
  public static function set_setting( $slug, $name, $value ) {
264
- $self = self::get_instance();
265
-
266
  $settings_obj = self::get_settings_obj( $slug );
267
 
268
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'set_all' ) ) ) {
269
  trigger_error( sprintf( __( 'Unable to find a valid settings object for %s. Setting was unable to be saved.', 'better-wp-security' ), $slug ) );
 
270
  return false;
271
  }
272
 
@@ -281,8 +287,7 @@ final class ITSEC_Modules {
281
  * @param ITSEC_Validator $validator
282
  */
283
  public static function register_validator( $validator ) {
284
- $self = self::get_instance();
285
- $self->_module_validators[ $validator->get_id() ] = $validator;
286
  }
287
 
288
  /**
@@ -350,14 +355,14 @@ final class ITSEC_Modules {
350
 
351
  if ( ! is_array( $self->_active_modules ) ) {
352
  $self->_active_modules = array();
353
- } else if ( isset( $self->_active_modules[0] ) ) {
354
  // Found data from an old format.
355
  foreach ( $self->_active_modules as $key => $value ) {
356
  if ( ! is_bool( $value ) ) {
357
- unset( $self->_active_modules[$key] );
358
 
359
- if ( ! isset( $self->_active_modules[$value] ) ) {
360
- $self->_active_modules[$value] = true;
361
  }
362
  }
363
  }
@@ -399,6 +404,7 @@ final class ITSEC_Modules {
399
  */
400
  public static function get_always_active_modules() {
401
  $self = self::get_instance();
 
402
  return array_keys( $self->_always_active_modules );
403
  }
404
 
@@ -412,7 +418,7 @@ final class ITSEC_Modules {
412
  public static function is_always_active( $module_id ) {
413
  $self = self::get_instance();
414
 
415
- if ( ! empty( $self->_always_active_modules[$module_id] ) ) {
416
  return true;
417
  }
418
 
@@ -433,7 +439,7 @@ final class ITSEC_Modules {
433
  self::get_active_modules();
434
  }
435
 
436
- if ( ! empty( $self->_always_active_modules[$module_id] ) ) {
437
  return true;
438
  }
439
 
@@ -469,8 +475,10 @@ final class ITSEC_Modules {
469
  $was_active = $self->_active_modules[ $module_id ];
470
  }
471
 
472
- if ( is_wp_error( $error = self::load_module_file( 'activate.php', $module_id ) ) ) {
473
- return $error;
 
 
474
  }
475
 
476
  $self->_active_modules[ $module_id ] = true;
@@ -554,39 +562,58 @@ final class ITSEC_Modules {
554
  *
555
  * The file will only be loaded once and will not error if does not exist.
556
  *
557
- * @param string $file The file name to load, including extension.
558
  * @param string|string[] $modules The modules to load the files from. Accepts either a module slug, an array of
559
  * module slugs, ':all' to load the files from all modules, or ':active' to load the
560
  * files from active modules.
 
561
  *
562
  * @return bool|WP_Error True if a module matching the $modules parameter is found, false otherwise.
563
  */
564
- public static function load_module_file( $file, $modules = ':all' ) {
565
  $self = self::get_instance();
566
 
567
  if ( ':all' === $modules ) {
568
  $modules = self::get_available_modules();
569
- } else if ( ':active' === $modules ) {
570
- if ( ITSEC_Core::is_temp_disable_modules_set() ) {
571
- $modules = array();
572
- } else {
573
- $modules = self::get_active_modules();
574
- }
575
-
576
- $modules = array_merge( $modules, array_keys( $self->_always_active_modules ) );
577
- $modules = array_unique( $modules );
578
- } else if ( is_string( $modules ) ) {
579
  $modules = array( $modules );
580
- } else if ( ! is_array( $modules ) ) {
581
  return false;
582
  }
583
 
584
  foreach ( $modules as $module ) {
585
- if ( ! empty( $self->_module_paths[$module] ) && file_exists( "{$self->_module_paths[$module]}/{$file}" ) ) {
586
- $returned = include_once( "{$self->_module_paths[$module]}/{$file}" );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
 
588
- if ( is_wp_error( $returned ) ) {
589
- return $returned;
 
 
 
590
  }
591
  }
592
  }
@@ -594,6 +621,24 @@ final class ITSEC_Modules {
594
  return true;
595
  }
596
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  /**
598
  * Fires an action to begin the registration of modules.
599
  */
@@ -601,10 +646,22 @@ final class ITSEC_Modules {
601
  do_action( 'itsec-register-modules' );
602
  }
603
 
 
 
 
 
 
 
 
 
 
 
 
604
  /**
605
  * Load and run all active modules.
606
  */
607
  public static function run_active_modules() {
 
608
  // The active.php file is for code that will only run when the module is active.
609
  self::load_module_file( 'active.php', ':active' );
610
  }
@@ -648,6 +705,19 @@ final class ITSEC_Modules {
648
  do_action( 'itsec_modules_do_plugin_upgrade', $old_version, $new_version );
649
  }
650
 
 
 
 
 
 
 
 
 
 
 
 
 
 
651
  /**
652
  * Load the settings controller for all registered modules.
653
  *
@@ -662,5 +732,54 @@ final class ITSEC_Modules {
662
 
663
  $this->_settings_files_loaded = true;
664
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
665
  }
 
666
  ITSEC_Modules::get_instance();
1
  <?php
2
 
3
+ use iThemesSecurity\Contracts\Runnable;
4
+ use iThemesSecurity\Exception\Unsatisfied_Module_Dependencies_Exception;
5
+ use Pimple\Container;
6
+
7
  final class ITSEC_Modules {
8
  /**
9
  * @var ITSEC_Modules - Static property to hold our singleton instance
19
  private $_module_settings = false;
20
  private $_module_validators = false;
21
  private $_settings_files_loaded = false;
22
+ private $loaded_containers = [];
23
+ private $labels = array();
24
+
25
+ /** @var Container */
26
+ private $pimple;
27
+
28
+ /** @var Psr\Container\ContainerInterface */
29
+ private $container;
30
+
31
+ /** @var bool */
32
+ private $initialized_container = false;
33
 
34
  protected function __construct() {
35
  // Action triggered from another part of Security which runs when the settings page is loaded.
36
  add_action( 'itsec-settings-page-init', array( $this, 'load_settings_page' ) );
37
  add_action( 'itsec-logs-page-init', array( $this, 'load_settings_page' ) );
38
+
39
+ $this->pimple = new Container();
40
+ $this->container = new Pimple\Psr11\Container( $this->pimple );
41
  }
42
 
43
  /**
70
 
71
  if ( ! is_dir( $path ) ) {
72
  trigger_error( sprintf( __( 'An attempt to register the %1$s module failed since the supplied path (%2$s) is invalid. This could indicate an invalid modification or incomplete installation of the iThemes Security plugin. Please reinstall the plugin and try again.', 'better-wp-security' ), $slug, $path ) );
73
+
74
  return false;
75
  }
76
 
77
  $self->_module_paths[ $slug ] = $path;
78
+ $self->_available_modules = array_keys( $self->_module_paths );
79
 
80
  if ( 'always-active' === $type ) {
81
+ $self->_always_active_modules[ $slug ] = true;
82
+ } elseif ( 'default-active' === $type ) {
83
+ $self->_default_active_modules[ $slug ] = true;
84
  }
85
 
86
  return true;
103
  unset( $self->_module_paths[ $slug ] );
104
  $self->_available_modules = array_keys( $self->_module_paths );
105
 
106
+ unset( $self->_always_active_modules[ $slug ], $self->_default_active_modules[ $slug ] );
 
107
 
108
  return true;
109
  }
123
  $self = self::get_instance();
124
 
125
  foreach ( $self->_module_paths as $slug => $path ) {
126
+ $self->_module_paths[ $slug ] = str_replace( $old_dir, $new_dir, $path );
127
  }
128
  }
129
 
133
  * @param ITSEC_Settings $settings
134
  */
135
  public static function register_settings( $settings ) {
136
+ self::get_instance()->_module_settings[ $settings->get_id() ] = $settings;
 
137
  }
138
 
139
  /**
167
  * @return array
168
  */
169
  public static function get_defaults( $slug ) {
 
 
170
  $settings_obj = self::get_settings_obj( $slug );
171
 
172
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get_defaults' ) ) ) {
186
  * @return mixed
187
  */
188
  public static function get_default( $slug, $name, $default = null ) {
 
 
189
  $defaults = self::get_defaults( $slug );
190
 
191
+ if ( isset( $defaults[ $name ] ) ) {
192
+ return $defaults[ $name ];
193
  }
194
 
195
  return $default;
203
  * @return array
204
  */
205
  public static function get_settings( $slug ) {
 
 
206
  $settings_obj = self::get_settings_obj( $slug );
207
 
208
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get_all' ) ) ) {
223
  * @return mixed
224
  */
225
  public static function get_setting( $slug, $name, $default = null ) {
 
 
226
  $settings_obj = self::get_settings_obj( $slug );
227
 
228
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'get' ) ) ) {
243
  * @return array|WP_Error
244
  */
245
  public static function set_settings( $slug, $settings ) {
 
 
246
  $settings_obj = self::get_settings_obj( $slug );
247
 
248
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'set_all' ) ) ) {
261
  * The new value will be validated and updated in memory. The change isn't persisted until
262
  * the end of the request or a manual call to {@see ITSEC_Storage::save()}.
263
  *
264
+ * @param string $slug The module slug.
265
+ * @param string $name The setting name to updated.
266
+ * @param mixed $value The settings' new value.
267
  *
268
  * @return array|false
269
  */
270
  public static function set_setting( $slug, $name, $value ) {
 
 
271
  $settings_obj = self::get_settings_obj( $slug );
272
 
273
  if ( is_null( $settings_obj ) || ! is_callable( array( $settings_obj, 'set_all' ) ) ) {
274
  trigger_error( sprintf( __( 'Unable to find a valid settings object for %s. Setting was unable to be saved.', 'better-wp-security' ), $slug ) );
275
+
276
  return false;
277
  }
278
 
287
  * @param ITSEC_Validator $validator
288
  */
289
  public static function register_validator( $validator ) {
290
+ self::get_instance()->_module_validators[ $validator->get_id() ] = $validator;
 
291
  }
292
 
293
  /**
355
 
356
  if ( ! is_array( $self->_active_modules ) ) {
357
  $self->_active_modules = array();
358
+ } elseif ( isset( $self->_active_modules[0] ) ) {
359
  // Found data from an old format.
360
  foreach ( $self->_active_modules as $key => $value ) {
361
  if ( ! is_bool( $value ) ) {
362
+ unset( $self->_active_modules[ $key ] );
363
 
364
+ if ( ! isset( $self->_active_modules[ $value ] ) ) {
365
+ $self->_active_modules[ $value ] = true;
366
  }
367
  }
368
  }
404
  */
405
  public static function get_always_active_modules() {
406
  $self = self::get_instance();
407
+
408
  return array_keys( $self->_always_active_modules );
409
  }
410
 
418
  public static function is_always_active( $module_id ) {
419
  $self = self::get_instance();
420
 
421
+ if ( ! empty( $self->_always_active_modules[ $module_id ] ) ) {
422
  return true;
423
  }
424
 
439
  self::get_active_modules();
440
  }
441
 
442
+ if ( ! empty( $self->_always_active_modules[ $module_id ] ) ) {
443
  return true;
444
  }
445
 
475
  $was_active = $self->_active_modules[ $module_id ];
476
  }
477
 
478
+ try {
479
+ self::load_module_file( 'activate.php', $module_id );
480
+ } catch ( Unsatisfied_Module_Dependencies_Exception $e ) {
481
+ return new WP_Error( 'itsec-modules-cannot-activate-module-unsatisfied-dependencies', $e->getMessage() );
482
  }
483
 
484
  $self->_active_modules[ $module_id ] = true;
562
  *
563
  * The file will only be loaded once and will not error if does not exist.
564
  *
565
+ * @param string $file The file name to load, including extension.
566
  * @param string|string[] $modules The modules to load the files from. Accepts either a module slug, an array of
567
  * module slugs, ':all' to load the files from all modules, or ':active' to load the
568
  * files from active modules.
569
+ * @param callable|null $process Callback to run returned values from module files through.
570
  *
571
  * @return bool|WP_Error True if a module matching the $modules parameter is found, false otherwise.
572
  */
573
+ public static function load_module_file( $file, $modules = ':all', callable $process = null ) {
574
  $self = self::get_instance();
575
 
576
  if ( ':all' === $modules ) {
577
  $modules = self::get_available_modules();
578
+ } elseif ( ':active' === $modules ) {
579
+ $modules = self::get_active_modules_to_run();
580
+ } elseif ( is_string( $modules ) ) {
 
 
 
 
 
 
 
581
  $modules = array( $modules );
582
+ } elseif ( ! is_array( $modules ) ) {
583
  return false;
584
  }
585
 
586
  foreach ( $modules as $module ) {
587
+ if ( empty( $self->_module_paths[ $module ] ) ) {
588
+ continue;
589
+ }
590
+
591
+ $self->load_container_definitions( $module );
592
+ $returned = null;
593
+
594
+ if ( self::get_container()->has( "module.{$module}.files" ) ) {
595
+ $files = self::get_container()->get( "module.{$module}.files" );
596
+
597
+ if ( isset( $files[ $file ] ) ) {
598
+ $returned = $files[ $file ];
599
+ }
600
+ }
601
+
602
+ if ( ! $returned ) {
603
+ $path = "{$self->_module_paths[$module]}/{$file}";
604
+
605
+ if ( ! file_exists( $path ) ) {
606
+ continue;
607
+ }
608
+
609
+ $returned = include_once( $path );
610
+ }
611
 
612
+ if ( $returned ) {
613
+ if ( $process ) {
614
+ $process( $returned, $module );
615
+ } else {
616
+ $self->run( $returned );
617
  }
618
  }
619
  }
621
  return true;
622
  }
623
 
624
+ /**
625
+ * Get a list of the active modules to run.
626
+ *
627
+ * @return string[]
628
+ */
629
+ protected static function get_active_modules_to_run() {
630
+ if ( ITSEC_Core::is_temp_disable_modules_set() ) {
631
+ $modules = array();
632
+ } else {
633
+ $modules = self::get_active_modules();
634
+ }
635
+
636
+ $modules = array_merge( $modules, array_keys( self::get_instance()->_always_active_modules ) );
637
+ $modules = array_unique( $modules );
638
+
639
+ return $modules;
640
+ }
641
+
642
  /**
643
  * Fires an action to begin the registration of modules.
644
  */
646
  do_action( 'itsec-register-modules' );
647
  }
648
 
649
+ /**
650
+ * Initialize the container.
651
+ */
652
+ public static function initialize_container() {
653
+ foreach ( self::get_active_modules_to_run() as $module ) {
654
+ self::get_instance()->load_container_definitions( $module );
655
+ }
656
+
657
+ self::get_instance()->initialized_container = true;
658
+ }
659
+
660
  /**
661
  * Load and run all active modules.
662
  */
663
  public static function run_active_modules() {
664
+ self::initialize_container();
665
  // The active.php file is for code that will only run when the module is active.
666
  self::load_module_file( 'active.php', ':active' );
667
  }
705
  do_action( 'itsec_modules_do_plugin_upgrade', $old_version, $new_version );
706
  }
707
 
708
+ /**
709
+ * Get the container.
710
+ *
711
+ * @return \Psr\Container\ContainerInterface
712
+ */
713
+ public static function get_container() {
714
+ if ( ! self::get_instance()->initialized_container ) {
715
+ self::initialize_container();
716
+ }
717
+
718
+ return self::get_instance()->container;
719
+ }
720
+
721
  /**
722
  * Load the settings controller for all registered modules.
723
  *
732
 
733
  $this->_settings_files_loaded = true;
734
  }
735
+
736
+ /**
737
+ * Get labels for a module.
738
+ *
739
+ * @param string $module
740
+ *
741
+ * @return array
742
+ */
743
+ public static function get_labels( $module ) {
744
+ if ( ! isset( self::get_instance()->labels[ $module ] ) ) {
745
+ self::get_instance()->labels[ $module ] = [];
746
+ self::load_module_file( 'labels.php', $module, function ( $labels, $module ) {
747
+ if ( is_array( $labels ) ) {
748
+ self::get_instance()->labels[ $module ] = $labels;
749
+ }
750
+ } );
751
+ }
752
+
753
+ return self::get_instance()->labels[ $module ];
754
+ }
755
+
756
+ private function run( $definition ) {
757
+ if ( $definition && is_string( $definition ) ) {
758
+ $object = $this->container->get( $definition );
759
+
760
+ if ( $object instanceof Runnable ) {
761
+ $object->run();
762
+ }
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Load the container definitions for a module.
768
+ *
769
+ * @param string $module
770
+ */
771
+ private function load_container_definitions( $module ) {
772
+ if ( ! isset( $this->loaded_containers[ $module ] ) && isset( $this->_module_paths[ $module ] ) ) {
773
+ $path = $this->_module_paths[ $module ] . '/container.php';
774
+
775
+ if ( file_exists( $this->_module_paths[ $module ] . '/container.php' ) && $register = include( $path ) ) {
776
+ $this->loaded_containers[ $module ] = true;
777
+ $register( $this->pimple );
778
+ } else {
779
+ $this->loaded_containers[ $module ] = false;
780
+ }
781
+ }
782
+ }
783
  }
784
+
785
  ITSEC_Modules::get_instance();
core/modules/404-detection/class-itsec-four-oh-four.php CHANGED
@@ -35,7 +35,7 @@ class ITSEC_Four_Oh_Four {
35
  ! in_array( '/' . ITSEC_Lib::get_request_path(), $this->settings['white_list'], true ) &&
36
  ! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'], true )
37
  ) {
38
- ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
39
  $itsec_lockout->do_lockout( new Host_Context( 'four_oh_four' ) );
40
  } else {
41
  do_action( 'itsec_four_oh_four_whitelisted', $uri );
35
  ! in_array( '/' . ITSEC_Lib::get_request_path(), $this->settings['white_list'], true ) &&
36
  ! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'], true )
37
  ) {
38
+ ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => ITSEC_Lib::get_server_snapshot() ) );
39
  $itsec_lockout->do_lockout( new Host_Context( 'four_oh_four' ) );
40
  } else {
41
  do_action( 'itsec_four_oh_four_whitelisted', $uri );
core/modules/404-detection/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( '404 Detection', 'better-wp-security' ),
5
+ ];
core/modules/admin-user/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Admin User', 'better-wp-security' ),
5
+ ];
core/modules/away-mode/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Away Mode', 'better-wp-security' ),
5
+ ];
core/modules/backup/activate.php CHANGED
@@ -3,5 +3,6 @@
3
  $settings = ITSEC_Modules::get_settings( 'backup' );
4
 
5
  if ( $settings['enabled'] && $settings['interval'] > 0 ) {
 
6
  ITSEC_Core::get_scheduler()->schedule( 'backup', 'backup' );
7
- }
3
  $settings = ITSEC_Modules::get_settings( 'backup' );
4
 
5
  if ( $settings['enabled'] && $settings['interval'] > 0 ) {
6
+ ITSEC_Core::get_scheduler()->register_custom_schedule( 'backup', DAY_IN_SECONDS * $settings['interval'] );
7
  ITSEC_Core::get_scheduler()->schedule( 'backup', 'backup' );
8
+ }
core/modules/backup/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Backup', 'better-wp-security' ),
5
+ ];
core/modules/backup/settings.php CHANGED
@@ -4,7 +4,7 @@ final class ITSEC_Backup_Settings extends ITSEC_Settings {
4
  public function get_id() {
5
  return 'backup';
6
  }
7
-
8
  public function get_defaults() {
9
  return array(
10
  'all_sites' => false,
@@ -24,6 +24,7 @@ final class ITSEC_Backup_Settings extends ITSEC_Settings {
24
  }
25
 
26
  protected function handle_settings_changes( $old_settings ) {
 
27
 
28
  if ( $old_settings['enabled'] !== $this->settings['enabled'] ) {
29
  if ( $this->settings['enabled'] ) {
4
  public function get_id() {
5
  return 'backup';
6
  }
7
+
8
  public function get_defaults() {
9
  return array(
10
  'all_sites' => false,
24
  }
25
 
26
  protected function handle_settings_changes( $old_settings ) {
27
+ parent::handle_settings_changes( $old_settings );
28
 
29
  if ( $old_settings['enabled'] !== $this->settings['enabled'] ) {
30
  if ( $this->settings['enabled'] ) {
core/modules/ban-users/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Ban Users', 'better-wp-security' ),
5
+ ];
core/modules/ban-users/lists/user-agents.php ADDED
@@ -0,0 +1,1543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ "1Noonbot",
5
+ "1on1searchBot",
6
+ "3D_SEARCH",
7
+ "3DE_SEARCH2",
8
+ "3GSE",
9
+ "50.nu",
10
+ "192.comAgent",
11
+ "360Spider",
12
+ "A6-Indexer",
13
+ "AASP",
14
+ "ABACHOBot",
15
+ "Abonti",
16
+ "abot",
17
+ "AbotEmailSearch",
18
+ "Aboundex",
19
+ "AboutUsBot",
20
+ "AccMonitor Compliance",
21
+ "accoona",
22
+ "AChulkov.NET page walker",
23
+ "Acme.Spider",
24
+ "AcoonBot",
25
+ "acquia-crawler",
26
+ "ActiveTouristBot",
27
+ "Acunetix",
28
+ "Ad Muncher",
29
+ "AdamM",
30
+ "adbeat_bot",
31
+ "adminshop.com",
32
+ "Advanced Email",
33
+ "AESOP_com_SpiderMan",
34
+ "AESpider",
35
+ "AF Knowledge Now Verity",
36
+ "aggregator:Vocus",
37
+ "ah-ha.com",
38
+ "AIBOT",
39
+ "aiHitBot",
40
+ "aipbot",
41
+ "AISIID",
42
+ "AITCSRobot",
43
+ "Akamai-SiteSnapshot",
44
+ "AlexaWebSearchPlatform",
45
+ "AlexaBot",
46
+ "AlexfDownload",
47
+ "Alexibot",
48
+ "AlkalineBOT",
49
+ "All Acronyms",
50
+ "Amfibibot",
51
+ "AmPmPPC.com",
52
+ "AMZNKAssocBot",
53
+ "Anemone",
54
+ "Anonymous",
55
+ "Anonymouse.org",
56
+ "AnotherBot",
57
+ "AnswerBot",
58
+ "AnswerBus",
59
+ "AnswerChase PROve",
60
+ "AntBot",
61
+ "antibot-",
62
+ "AntiSantyWorm",
63
+ "Antro.Net",
64
+ "AONDE-Spider",
65
+ "Aport",
66
+ "Aqua_Products",
67
+ "AraBot",
68
+ "Arachmo",
69
+ "Arachnophilia",
70
+ "archive.org_bot",
71
+ "aria eQualizer",
72
+ "aria2",
73
+ "arianna.libero.it",
74
+ "Arikus_Spider",
75
+ "Art-Online.com",
76
+ "ArtavisBot",
77
+ "Artera",
78
+ "ASpider",
79
+ "ASPSeek",
80
+ "asterias",
81
+ "AstroFind",
82
+ "athenusbot",
83
+ "AtlocalBot",
84
+ "Atomic_Email_Hunter",
85
+ "attach",
86
+ "attrakt",
87
+ "attributor",
88
+ "Attributor.comBot",
89
+ "augurfind",
90
+ "AURESYS",
91
+ "AutoBaron",
92
+ "autoemailspider",
93
+ "autowebdir",
94
+ "AVSearch-",
95
+ "axfeedsbot",
96
+ "Axonize-bot",
97
+ "Ayna",
98
+ "b2w",
99
+ "BackDoorBot",
100
+ "BackRub",
101
+ "BackStreet Browser",
102
+ "BackWeb",
103
+ "Baiduspider-video",
104
+ "Bandit",
105
+ "BatchFTP",
106
+ "baypup",
107
+ "BDFetch",
108
+ "BecomeBot",
109
+ "BecomeJPBot",
110
+ "BeetleBot",
111
+ "Bender",
112
+ "besserscheitern-crawl",
113
+ "betaBot",
114
+ "Big Brother",
115
+ "Big Data",
116
+ "Bigado.com",
117
+ "BigCliqueBot",
118
+ "Bigfoot",
119
+ "BIGLOTRON",
120
+ "Bilbo",
121
+ "BilgiBetaBot",
122
+ "BilgiBot",
123
+ "binlar",
124
+ "bintellibot",
125
+ "bitlybot",
126
+ "BitvoUserAgent",
127
+ "bixocrawler",
128
+ "Bizbot003",
129
+ "BizBot04",
130
+ "BizBot04 kirk.overleaf.com",
131
+ "Black.Hole",
132
+ "Black Hole",
133
+ "Blackbird",
134
+ "BlackWidow",
135
+ "bladder fusion",
136
+ "Blaiz-Bee",
137
+ "BLEXBot",
138
+ "Blinkx",
139
+ "BlitzBOT",
140
+ "Blog Conversation Project",
141
+ "BlogMyWay",
142
+ "BlogPulseLive",
143
+ "BlogRefsBot",
144
+ "BlogScope",
145
+ "Blogslive",
146
+ "BloobyBot",
147
+ "BlowFish",
148
+ "BLT",
149
+ "bnf.fr_bot",
150
+ "BoaConstrictor",
151
+ "BoardReader-Image-Fetcher",
152
+ "BOI_crawl_00",
153
+ "BOIA-Scan-Agent",
154
+ "BOIA.ORG-Scan-Agent",
155
+ "boitho.com-dc",
156
+ "Bookmark Buddy",
157
+ "bosug",
158
+ "Bot Apoena",
159
+ "BotALot",
160
+ "BotRightHere",
161
+ "Botswana",
162
+ "bottybot",
163
+ "BpBot",
164
+ "BRAINTIME_SEARCH",
165
+ "BrokenLinkCheck.com",
166
+ "BrowserEmulator",
167
+ "BrowserMob",
168
+ "BruinBot",
169
+ "BSearchR&D",
170
+ "BSpider",
171
+ "btbot",
172
+ "Btsearch",
173
+ "Buddy",
174
+ "Buibui",
175
+ "BuildCMS",
176
+ "BuiltBotTough",
177
+ "Bullseye",
178
+ "bumblebee",
179
+ "BunnySlippers",
180
+ "BuscadorClarin",
181
+ "Butterfly",
182
+ "BuyHawaiiBot",
183
+ "BuzzBot",
184
+ "byindia",
185
+ "BySpider",
186
+ "byteserver",
187
+ "bzBot",
188
+ "c r a w l 3 r",
189
+ "CacheBlaster",
190
+ "CACTVS Chemistry",
191
+ "Caddbot",
192
+ "Cafi",
193
+ "Camcrawler",
194
+ "CamelStampede",
195
+ "Canon-WebRecord",
196
+ "Canon-WebRecordPro",
197
+ "CareerBot",
198
+ "casper",
199
+ "cataguru",
200
+ "CatchBot",
201
+ "CazoodleBot",
202
+ "CCBot",
203
+ "CCGCrawl",
204
+ "ccubee",
205
+ "CD-Preload",
206
+ "CE-Preload",
207
+ "Cegbfeieh",
208
+ "Cerberian Drtrs",
209
+ "CERT FigleafBot",
210
+ "cfetch",
211
+ "CFNetwork",
212
+ "Chameleon",
213
+ "ChangeDetection",
214
+ "Charlotte",
215
+ "Check&Get",
216
+ "Checkbot",
217
+ "Checklinks",
218
+ "checkprivacy",
219
+ "CheeseBot",
220
+ "ChemieDE-NodeBot",
221
+ "CherryPicker",
222
+ "CherryPickerElite",
223
+ "CherryPickerSE",
224
+ "Chilkat",
225
+ "ChinaClaw",
226
+ "CipinetBot",
227
+ "cis455crawler",
228
+ "citeseerxbot",
229
+ "cizilla.com",
230
+ "ClariaBot",
231
+ "clshttp",
232
+ "Clushbot",
233
+ "cmsworldmap",
234
+ "coccoc",
235
+ "CollapsarWEB",
236
+ "Collector",
237
+ "combine",
238
+ "conceptbot",
239
+ "ConnectSearch",
240
+ "conpilot",
241
+ "ContentSmartz",
242
+ "ContextAd",
243
+ "contype",
244
+ "cookieNET",
245
+ "CoolBot†",
246
+ "CoolCheck",
247
+ "Copernic",
248
+ "Copier",
249
+ "CopyRightCheck",
250
+ "core-project",
251
+ "cosmos",
252
+ "Covario-IDS",
253
+ "Cowbot-",
254
+ "Cowdog",
255
+ "crabbyBot",
256
+ "crawl",
257
+ "Crawl_Application",
258
+ "crawl.UserAgent",
259
+ "CrawlConvera",
260
+ "crawler",
261
+ "crawler_for_infomine",
262
+ "CRAWLER-ALTSE.VUNET.ORG-Lynx",
263
+ "crawler-upgrade-config",
264
+ "crawler.kpricorn.org",
265
+ "crawler@",
266
+ "crawler4j",
267
+ "crawler43.ejupiter.com",
268
+ "Crawly",
269
+ "CreativeCommons",
270
+ "Crescent",
271
+ "Crescent Internet ToolPak HTTP OLE Control",
272
+ "cs-crawler",
273
+ "CSE HTML Validator",
274
+ "CSHttpClient",
275
+ "Cuasarbot",
276
+ "culsearch",
277
+ "Curl",
278
+ "Custo",
279
+ "Cutbot",
280
+ "cvaulev",
281
+ "Cyberdog",
282
+ "CyberNavi_WebGet",
283
+ "CyberSpyder",
284
+ "CydralSpider",
285
+ "D1GArabicEngine",
286
+ "DataCha0s",
287
+ "DataFountains",
288
+ "DataparkSearch",
289
+ "DataSpearSpiderBot",
290
+ "DataSpider",
291
+ "Dattatec.com",
292
+ "Dattatec.com-Sitios-Top",
293
+ "Daumoa",
294
+ "DAUMOA-video",
295
+ "DAUMOA-web",
296
+ "Declumbot",
297
+ "Deepindex",
298
+ "deepnet",
299
+ "DeepTrawl",
300
+ "dejan",
301
+ "del.icio.us-thumbnails",
302
+ "DelvuBot",
303
+ "Deweb",
304
+ "DiaGem",
305
+ "Diamond",
306
+ "DiamondBot",
307
+ "diavol",
308
+ "DiBot",
309
+ "didaxusbot",
310
+ "DigExt",
311
+ "Digger",
312
+ "DiGi-RSSBot",
313
+ "DigitalArchivesBot",
314
+ "DigOut4U",
315
+ "DIIbot",
316
+ "Dillo",
317
+ "Dir_Snatch.exe",
318
+ "DISCo",
319
+ "DISCo Pump",
320
+ "discobot",
321
+ "DISCoFinder",
322
+ "Distilled-Reputation-Monitor",
323
+ "Dit",
324
+ "DittoSpyder",
325
+ "DjangoTraineeBot",
326
+ "DKIMRepBot",
327
+ "DoCoMo",
328
+ "DOF-Verify",
329
+ "domaincrawler",
330
+ "DomainScan",
331
+ "DomainWatcher",
332
+ "dotbot",
333
+ "DotSpotsBot",
334
+ "Dow Jonesbot",
335
+ "Download",
336
+ "Download Demon",
337
+ "Downloader",
338
+ "DOY",
339
+ "dragonfly",
340
+ "Drip",
341
+ "drone",
342
+ "DTAAgent",
343
+ "dtSearchSpider",
344
+ "dumbot",
345
+ "Dwaar",
346
+ "Dwaarbot",
347
+ "DXSeeker",
348
+ "EAH",
349
+ "EasouSpider",
350
+ "EasyDL",
351
+ "ebingbong",
352
+ "EC2LinkFinder",
353
+ "eCairn-Grabber",
354
+ "eCatch",
355
+ "eChooseBot",
356
+ "ecxi",
357
+ "EdisterBot",
358
+ "EduGovSearch",
359
+ "egothor",
360
+ "eidetica.com",
361
+ "EirGrabber",
362
+ "ElisaBot",
363
+ "EllerdaleBot",
364
+ "EMail Exractor",
365
+ "EmailCollector",
366
+ "EmailLeach",
367
+ "EmailSiphon",
368
+ "EmailWolf",
369
+ "EMPAS_ROBOT",
370
+ "EnaBot",
371
+ "endeca",
372
+ "EnigmaBot",
373
+ "Enswer Neuro",
374
+ "EntityCubeBot",
375
+ "EroCrawler",
376
+ "eStyleSearch",
377
+ "eSyndiCat",
378
+ "Eurosoft-Bot",
379
+ "Evaal",
380
+ "Eventware",
381
+ "Everest-Vulcan",
382
+ "Exabot",
383
+ "Exabot-Images",
384
+ "Exabot-Test",
385
+ "Exabot-XXX",
386
+ "ExaBotTest",
387
+ "ExactSearch",
388
+ "exactseek.com",
389
+ "exooba",
390
+ "Exploder",
391
+ "explorersearch",
392
+ "extract",
393
+ "Extractor",
394
+ "ExtractorPro",
395
+ "EyeNetIE",
396
+ "ez-robot",
397
+ "Ezooms",
398
+ "factbot",
399
+ "FairAd Client",
400
+ "falcon",
401
+ "Falconsbot",
402
+ "fast-search-engine",
403
+ "FAST Data Document",
404
+ "FAST ESP",
405
+ "fastbot",
406
+ "fastbot.de",
407
+ "FatBot",
408
+ "Favcollector",
409
+ "Faviconizer",
410
+ "FDM",
411
+ "FedContractorBot",
412
+ "feedfinder",
413
+ "FelixIDE",
414
+ "fembot",
415
+ "fetch_ici",
416
+ "Fetch API Request",
417
+ "fgcrawler",
418
+ "FHscan",
419
+ "fido",
420
+ "Filangy",
421
+ "FileHound",
422
+ "FindAnISP.com_ISP_Finder",
423
+ "findlinks",
424
+ "FindWeb",
425
+ "Firebat",
426
+ "Fish-Search-Robot",
427
+ "Flaming AttackBot",
428
+ "Flamingo_SearchEngine",
429
+ "FlashCapture",
430
+ "FlashGet",
431
+ "flicky",
432
+ "FlickySearchBot",
433
+ "flunky",
434
+ "focused_crawler",
435
+ "FollowSite",
436
+ "Foobot",
437
+ "Fooooo_Web_Video_Crawl",
438
+ "Fopper",
439
+ "FormulaFinderBot",
440
+ "Forschungsportal",
441
+ "fr_crawler",
442
+ "Francis",
443
+ "Freecrawl",
444
+ "FreshDownload",
445
+ "freshlinks.exe",
446
+ "FriendFeedBot",
447
+ "frodo.at",
448
+ "froGgle",
449
+ "FrontPage",
450
+ "Froola",
451
+ "FU-NBI",
452
+ "full_breadth_crawler",
453
+ "FunnelBack",
454
+ "FunWebProducts",
455
+ "FurlBot",
456
+ "g00g1e",
457
+ "G10-Bot",
458
+ "Gaisbot",
459
+ "GalaxyBot",
460
+ "gazz",
461
+ "gcreep",
462
+ "generate_infomine_category_classifiers",
463
+ "genevabot",
464
+ "genieBot",
465
+ "GenieBotRD_SmallCrawl",
466
+ "Genieo",
467
+ "Geomaxenginebot",
468
+ "geometabot",
469
+ "GeonaBot",
470
+ "GeoVisu",
471
+ "GermCrawler",
472
+ "GetHTMLContents",
473
+ "Getleft",
474
+ "GetRight",
475
+ "GetSmart",
476
+ "GetURL.rexx",
477
+ "GetWeb!",
478
+ "Giant",
479
+ "GigablastOpenSource",
480
+ "Gigabot",
481
+ "Girafabot",
482
+ "GleameBot",
483
+ "gnome-vfs",
484
+ "Go-Ahead-Got-It",
485
+ "Go!Zilla",
486
+ "GoForIt.com",
487
+ "GOFORITBOT",
488
+ "gold",
489
+ "Golem",
490
+ "GoodJelly",
491
+ "Gordon-College-Google-Mini",
492
+ "goroam",
493
+ "GoSeebot",
494
+ "gotit",
495
+ "Govbot",
496
+ "GPU p2p",
497
+ "grab",
498
+ "Grabber",
499
+ "GrabNet",
500
+ "Grafula",
501
+ "grapeFX",
502
+ "grapeshot",
503
+ "GrapeshotCrawler",
504
+ "grbot",
505
+ "GreenYogi [ZSEBOT]",
506
+ "Gromit",
507
+ "GroupMe",
508
+ "GroupHigh",
509
+ "grub",
510
+ "grub-client",
511
+ "Grubclient-",
512
+ "GrubNG",
513
+ "GruBot",
514
+ "GSLFbot",
515
+ "GT::WWW",
516
+ "Gulliver",
517
+ "GulperBot",
518
+ "GurujiBot",
519
+ "GVC",
520
+ "GVC BUSINESS",
521
+ "gvcbot.com",
522
+ "HappyFunBot",
523
+ "harvest",
524
+ "HarvestMan",
525
+ "Hatena Antenna",
526
+ "Hawler",
527
+ "Hazel's Ferret hopper",
528
+ "hcat",
529
+ "hclsreport-crawler",
530
+ "HD nutch agent",
531
+ "Header_Test_Client",
532
+ "healia",
533
+ "Helix",
534
+ "hijbul-heritrix-crawler",
535
+ "HiScan",
536
+ "HiSoftware AccMonitor",
537
+ "HiSoftware AccVerify",
538
+ "hitcrawler_",
539
+ "hivaBot",
540
+ "hloader",
541
+ "HMSEbot",
542
+ "HMView",
543
+ "hoge",
544
+ "holmes",
545
+ "HomePageSearch",
546
+ "Hooblybot-Image",
547
+ "HooWWWer",
548
+ "Hostcrawler",
549
+ "HSFT - Link",
550
+ "HSFT - LVU",
551
+ "HSlide",
552
+ "ht:",
553
+ "htdig",
554
+ "Html Link Validator",
555
+ "HTMLParser",
556
+ "HTTP::Lite",
557
+ "httplib",
558
+ "HTTrack",
559
+ "Huaweisymantecspider",
560
+ "hul-wax",
561
+ "humanlinks",
562
+ "HyperEstraier",
563
+ "Hyperix",
564
+ "ia_archiver",
565
+ "IAArchiver-",
566
+ "ibuena",
567
+ "iCab",
568
+ "ICDS-Ingestion",
569
+ "ichiro",
570
+ "iCopyright Conductor",
571
+ "id-search",
572
+ "IDBot",
573
+ "IEAutoDiscovery",
574
+ "IECheck",
575
+ "iHWebChecker",
576
+ "IIITBOT",
577
+ "iim_405",
578
+ "IlseBot",
579
+ "IlTrovatore",
580
+ "Iltrovatore-Setaccio",
581
+ "ImageBot",
582
+ "imagefortress",
583
+ "ImagesHereImagesThereImagesEverywhere",
584
+ "ImageVisu",
585
+ "imds_monitor",
586
+ "imo-google-robot-intelink",
587
+ "IncyWincy",
588
+ "Industry Cortexcrawler",
589
+ "indylabs_marius",
590
+ "InelaBot",
591
+ "Inet32 Ctrl",
592
+ "inetbot",
593
+ "InfoLink",
594
+ "INFOMINE",
595
+ "infomine.ucr.edu",
596
+ "InfoNaviRobot",
597
+ "Informant",
598
+ "Infoseek",
599
+ "InfoTekies",
600
+ "InfoUSABot",
601
+ "INGRID",
602
+ "Inktomi",
603
+ "InsightsCollector",
604
+ "InsightsWorksBot",
605
+ "InspireBot",
606
+ "InsumaScout",
607
+ "Intelix",
608
+ "InterGET",
609
+ "Internet Ninja",
610
+ "InternetLinkAgent",
611
+ "Interseek",
612
+ "IOI",
613
+ "ip-web-crawler.com",
614
+ "IPAdd",
615
+ "Ipselonbot",
616
+ "Iria",
617
+ "IRLbot",
618
+ "Iron33",
619
+ "Isara",
620
+ "iSearch",
621
+ "iSiloX",
622
+ "IsraeliSearch",
623
+ "IstellaBot",
624
+ "its-learning",
625
+ "IU_CSCI_B659_class_crawler",
626
+ "iVia",
627
+ "iVia Page Fetcher",
628
+ "JadynAve",
629
+ "JadynAveBot",
630
+ "jakarta",
631
+ "Java",
632
+ "Jbot",
633
+ "JemmaTheTourist",
634
+ "JennyBot",
635
+ "Jetbot",
636
+ "JetBrains Omea Pro",
637
+ "JetCar",
638
+ "Jim",
639
+ "JoBo",
640
+ "JobSpider_BA",
641
+ "JOC",
642
+ "JoeDog",
643
+ "JoyScapeBot",
644
+ "JSpyda",
645
+ "JubiiRobot",
646
+ "jumpstation",
647
+ "Junut",
648
+ "JustView",
649
+ "Jyxobot",
650
+ "K.S.Bot",
651
+ "KakcleBot",
652
+ "kalooga",
653
+ "KaloogaBot",
654
+ "kanagawa",
655
+ "KATATUDO-Spider",
656
+ "Katipo",
657
+ "kbeta1",
658
+ "Kenjin.Spider",
659
+ "KeywenBot",
660
+ "Keyword.Density",
661
+ "Keyword Density",
662
+ "kinjabot",
663
+ "KIT-Fireball",
664
+ "Kitenga-crawler-bot",
665
+ "KiwiStatus",
666
+ "kmbot-",
667
+ "kmccrew",
668
+ "Knight",
669
+ "KnowItAll",
670
+ "Knowledge.com",
671
+ "Knowledge Engine",
672
+ "KoepaBot",
673
+ "Koninklijke",
674
+ "KrOWLer",
675
+ "KSbot",
676
+ "kuloko-bot",
677
+ "kulturarw3",
678
+ "KummHttp",
679
+ "Kurzor",
680
+ "Kyluka",
681
+ "L.webis",
682
+ "LabelGrab",
683
+ "Labhoo",
684
+ "labourunions411",
685
+ "lachesis",
686
+ "Lament",
687
+ "LamerExterminator",
688
+ "LapozzBot",
689
+ "larbin",
690
+ "LARBIN-EXPERIMENTAL",
691
+ "LBot",
692
+ "LBBROWSER",
693
+ "LeapTag",
694
+ "LeechFTP",
695
+ "LeechGet",
696
+ "LetsCrawl.com",
697
+ "LexiBot",
698
+ "LexxeBot",
699
+ "lftp",
700
+ "libcrawl",
701
+ "libiViaCore",
702
+ "libWeb",
703
+ "libwww",
704
+ "libwww-perl",
705
+ "likse",
706
+ "Linguee",
707
+ "Link",
708
+ "link_checker",
709
+ "LinkAlarm",
710
+ "linkbot",
711
+ "LinkCheck by Siteimprove.com",
712
+ "LinkChecker",
713
+ "linkdexbot",
714
+ "linkdex.com",
715
+ "LinkextractorPro",
716
+ "LinkLint",
717
+ "linklooker",
718
+ "Linkman",
719
+ "LinkScan",
720
+ "LinksCrawler",
721
+ "LinksManager.com_bot",
722
+ "LinkSweeper",
723
+ "linkwalker",
724
+ "LiteFinder",
725
+ "LitlrBot",
726
+ "Little Grabber at Skanktale.com",
727
+ "Livelapbot",
728
+ "LM Harvester",
729
+ "LMQueueBot",
730
+ "LNSpiderguy",
731
+ "LoadTimeBot",
732
+ "LocalcomBot",
733
+ "locust",
734
+ "LolongBot",
735
+ "LookBot",
736
+ "Lsearch",
737
+ "lssbot",
738
+ "LWP",
739
+ "lwp-request",
740
+ "lwp-trivial",
741
+ "LWP::Simple",
742
+ "Lycos_Spider",
743
+ "Lydia Entity",
744
+ "LynnBot",
745
+ "Lytranslate",
746
+ "Mag-Net",
747
+ "Magnet",
748
+ "magpie-crawler",
749
+ "Magus",
750
+ "Mail.Ru",
751
+ "Mail.Ru_Bot",
752
+ "MAINSEEK_BOT",
753
+ "Mammoth",
754
+ "MarkWatch",
755
+ "MaSagool",
756
+ "masidani_bot_",
757
+ "Mass",
758
+ "Mata.Hari",
759
+ "Mata Hari",
760
+ "matentzn at cs dot man dot ac dot uk",
761
+ "maxamine.com--robot",
762
+ "maxamine.com-robot",
763
+ "maxomobot",
764
+ "Maxthon$",
765
+ "McBot",
766
+ "MediaFox",
767
+ "medrabbit",
768
+ "Megite",
769
+ "MemacBot",
770
+ "Memo",
771
+ "MendeleyBot",
772
+ "Mercator-",
773
+ "mercuryboard_user_agent_sql_injection.nasl",
774
+ "MerzScope",
775
+ "metacarta",
776
+ "Metager2",
777
+ "metager2-verification-bot",
778
+ "MetaGloss",
779
+ "METAGOPHER",
780
+ "metal",
781
+ "metaquerier.cs.uiuc.edu",
782
+ "METASpider",
783
+ "Metaspinner",
784
+ "MetaURI",
785
+ "MetaURI API",
786
+ "MFC_Tear_Sample",
787
+ "MFcrawler",
788
+ "MFHttpScan",
789
+ "Microsoft.URL",
790
+ "MIIxpc",
791
+ "miner",
792
+ "mini-robot",
793
+ "minibot",
794
+ "miniRank",
795
+ "Mirror",
796
+ "Missigua Locator",
797
+ "Mister.PiX",
798
+ "Mister PiX",
799
+ "Miva",
800
+ "MJ12bot",
801
+ "mnoGoSearch",
802
+ "mod_accessibility",
803
+ "moduna.com",
804
+ "moget",
805
+ "MojeekBot",
806
+ "MOMspider",
807
+ "MonkeyCrawl",
808
+ "MOSES",
809
+ "Motor",
810
+ "mowserbot",
811
+ "MQbot",
812
+ "MSE360",
813
+ "MSFrontPage",
814
+ "MSIECrawler",
815
+ "MSIndianWebcrawl",
816
+ "MSMOBOT",
817
+ "Msnbot",
818
+ "msnbot-products",
819
+ "MSNPTC",
820
+ "MSRBOT",
821
+ "MT-Soft",
822
+ "MultiText",
823
+ "My_Little_SearchEngine_Project",
824
+ "my-heritrix-crawler",
825
+ "MyApp",
826
+ "MYCOMPANYBOT",
827
+ "mycrawler",
828
+ "MyEngines-US-Bot",
829
+ "MyFamilyBot",
830
+ "Myra",
831
+ "nabot",
832
+ "nabot_",
833
+ "Najdi.si",
834
+ "Nambu",
835
+ "NAMEPROTECT",
836
+ "NatchCVS",
837
+ "naver",
838
+ "naverbookmarkcrawler",
839
+ "NaverBot",
840
+ "Navroad",
841
+ "NearSite",
842
+ "NEC-MeshExplorer",
843
+ "NeoScioCrawler",
844
+ "NerdByNature.Bot",
845
+ "NerdyBot",
846
+ "Nerima-crawl-",
847
+ "Nessus",
848
+ "NESSUS::SOAP",
849
+ "nestReader",
850
+ "Net::Trackback",
851
+ "NetAnts",
852
+ "NetCarta CyberPilot Pro",
853
+ "Netcraft",
854
+ "NetID.com",
855
+ "NetMechanic",
856
+ "Netprospector",
857
+ "NetResearchServer",
858
+ "NetScoop",
859
+ "NetSeer",
860
+ "NetShift=",
861
+ "NetSongBot",
862
+ "Netsparker",
863
+ "NetSpider",
864
+ "NetSrcherP",
865
+ "NetZip",
866
+ "NetZip-Downloader",
867
+ "NewMedhunt",
868
+ "news",
869
+ "News_Search_App",
870
+ "NewsGatherer",
871
+ "Newsgroupreporter",
872
+ "NewsTroveBot",
873
+ "NextGenSearchBot",
874
+ "nextthing.org",
875
+ "NHSEWalker",
876
+ "nicebot",
877
+ "NICErsPRO",
878
+ "niki-bot",
879
+ "NimbleCrawler",
880
+ "nimbus-1",
881
+ "ninetowns",
882
+ "Ninja",
883
+ "NjuiceBot",
884
+ "NLese",
885
+ "Nogate",
886
+ "Nomad-V2.x",
887
+ "NoteworthyBot",
888
+ "NPbot",
889
+ "NPBot-",
890
+ "NRCan intranet",
891
+ "NSDL_Search_Bot",
892
+ "nu_tch-princeton",
893
+ "nuggetize.com",
894
+ "nutch",
895
+ "nutch1",
896
+ "NutchCVS",
897
+ "NutchOrg",
898
+ "NWSpider",
899
+ "Nymesis",
900
+ "nys-crawler",
901
+ "ObjectsSearch",
902
+ "oBot",
903
+ "Obvius external linkcheck",
904
+ "Occam",
905
+ "Ocelli",
906
+ "Octopus",
907
+ "ODP entries",
908
+ "Offline.Explorer",
909
+ "Offline Explorer",
910
+ "Offline Navigator",
911
+ "OGspider",
912
+ "OmiExplorer_Bot",
913
+ "OmniExplorer_Bot",
914
+ "omnifind",
915
+ "OmniWeb",
916
+ "OnetSzukaj",
917
+ "online link validator",
918
+ "OOZBOT",
919
+ "Openbot",
920
+ "Openfind",
921
+ "Openfind data",
922
+ "OpenHoseBot",
923
+ "OpenIntelligenceData",
924
+ "OpenISearch",
925
+ "OpenSearchServer_Bot",
926
+ "OpiDig",
927
+ "optidiscover",
928
+ "OrangeBot",
929
+ "ORISBot",
930
+ "ornl_crawler_1",
931
+ "ORNL_Mercury",
932
+ "osis-project.jp",
933
+ "OutfoxBot",
934
+ "OutfoxMelonBot",
935
+ "OWLER-BOT",
936
+ "Owlin",
937
+ "owsBot",
938
+ "ozelot",
939
+ "P3P Client",
940
+ "page_verifier",
941
+ "PageBitesHyperBot",
942
+ "Pagebull",
943
+ "PageDown",
944
+ "PageFetcher",
945
+ "PageGrabber",
946
+ "PagePeeker",
947
+ "PageRank Monitor",
948
+ "pamsnbot.htm",
949
+ "Panopy",
950
+ "panscient.com",
951
+ "Pansophica",
952
+ "Papa Foto",
953
+ "PaperLiBot",
954
+ "parasite",
955
+ "parsijoo",
956
+ "Pathtraq",
957
+ "Pattern",
958
+ "Patwebbot",
959
+ "pavuk",
960
+ "PaxleFramework",
961
+ "PBBOT",
962
+ "pcBrowser",
963
+ "pd-crawler",
964
+ "PECL::HTTP",
965
+ "Pcore-HTTP",
966
+ "penthesila",
967
+ "PeoplePal",
968
+ "perform_crawl",
969
+ "PerMan",
970
+ "PGP-KA",
971
+ "PHPCrawl",
972
+ "PhpDig",
973
+ "PicoSearch",
974
+ "pipBot",
975
+ "pipeLiner",
976
+ "Pita",
977
+ "pixfinder",
978
+ "PiyushBot",
979
+ "planetwork",
980
+ "PleaseCrawl",
981
+ "Plucker",
982
+ "Plukkie",
983
+ "Plumtree",
984
+ "Pockey",
985
+ "Pockey-GetHTML",
986
+ "PoCoHTTP",
987
+ "pogodak.ba",
988
+ "Pogodak.co.yu",
989
+ "Poirot",
990
+ "polybot",
991
+ "Pompos",
992
+ "Poodle predictor",
993
+ "PopScreenBot",
994
+ "PostPost",
995
+ "PrivacyFinder",
996
+ "ProjectWF-java-test-crawler",
997
+ "ProPowerBot",
998
+ "ProWebWalker",
999
+ "psbot",
1000
+ "psbot-page",
1001
+ "PSS-Bot",
1002
+ "psycheclone",
1003
+ "pub-crawler",
1004
+ "pucl",
1005
+ "pulseBot (pulse",
1006
+ "Pump",
1007
+ "purebot",
1008
+ "PWeBot",
1009
+ "pycurl",
1010
+ "Python-urllib",
1011
+ "pythonic-crawler",
1012
+ "PythonWikipediaBot",
1013
+ "q1",
1014
+ "QEAVis agent",
1015
+ "QFKBot",
1016
+ "qualidade",
1017
+ "Qualidator.com",
1018
+ "QuepasaCreep",
1019
+ "QueryN.Metasearch",
1020
+ "QueryN Metasearch",
1021
+ "quest.durato",
1022
+ "Quintura-Crw",
1023
+ "QunarBot",
1024
+ "Qweery_robot.txt_CheckBot",
1025
+ "QweeryBot",
1026
+ "r2iBot",
1027
+ "R6_CommentReader",
1028
+ "R6_FeedFetcher",
1029
+ "R6_VoteReader",
1030
+ "RaBot",
1031
+ "Radian6",
1032
+ "radian6_linkcheck",
1033
+ "RAMPyBot",
1034
+ "RankurBot",
1035
+ "RcStartBot",
1036
+ "RealDownload",
1037
+ "Reaper",
1038
+ "REBI-shoveler",
1039
+ "Recorder",
1040
+ "RedBot",
1041
+ "RedCarpet",
1042
+ "ReGet",
1043
+ "RepoMonkey",
1044
+ "RepoMonkey Bait",
1045
+ "Riddler",
1046
+ "RIIGHTBOT",
1047
+ "RiseNetBot",
1048
+ "RiverglassScanner",
1049
+ "RoboPal",
1050
+ "Robosourcer",
1051
+ "robot",
1052
+ "robotek",
1053
+ "robots",
1054
+ "Robozilla",
1055
+ "rogerBot",
1056
+ "Rome Client",
1057
+ "Rondello",
1058
+ "Rotondo",
1059
+ "Roverbot",
1060
+ "RPT-HTTPClient",
1061
+ "rtgibot",
1062
+ "RufusBot",
1063
+ "Runnk online rss reader",
1064
+ "SafetyNet",
1065
+ "s~stremor-crawler",
1066
+ "S2Bot",
1067
+ "SafariBookmarkChecker",
1068
+ "SaladSpoon",
1069
+ "Sapienti",
1070
+ "SBIder",
1071
+ "SBL-BOT",
1072
+ "SCFCrawler",
1073
+ "Scich",
1074
+ "ScientificCommons.org",
1075
+ "ScollSpider",
1076
+ "ScooperBot",
1077
+ "Scooter",
1078
+ "ScoutJet",
1079
+ "ScrapeBox",
1080
+ "Scrapy",
1081
+ "SCrawlTest",
1082
+ "Scrubby",
1083
+ "scSpider",
1084
+ "Scumbot",
1085
+ "SeaMonkey$",
1086
+ "Search-Channel",
1087
+ "Search-Engine-Studio",
1088
+ "search.KumKie.com",
1089
+ "search.msn.com",
1090
+ "search.updated.com",
1091
+ "search.usgs.gov",
1092
+ "Search Publisher",
1093
+ "Searcharoo.NET",
1094
+ "SearchBlox",
1095
+ "searchbot",
1096
+ "searchengine",
1097
+ "searchhippo.com",
1098
+ "SearchIt-Bot",
1099
+ "searchmarking",
1100
+ "searchmarks",
1101
+ "searchmee_v",
1102
+ "SearchmetricsBot",
1103
+ "searchmining",
1104
+ "SearchnowBot_v1",
1105
+ "searchpreview",
1106
+ "SearchSpider.com",
1107
+ "SearQuBot",
1108
+ "Seekbot",
1109
+ "Seeker.lookseek.com",
1110
+ "SeeqBot",
1111
+ "seeqpod-vertical-crawler",
1112
+ "Selflinkchecker",
1113
+ "Semager",
1114
+ "semanticdiscovery",
1115
+ "Semantifire1",
1116
+ "semisearch",
1117
+ "SemrushBot",
1118
+ "Senrigan",
1119
+ "SEOENGWorldBot",
1120
+ "ShablastBot",
1121
+ "ShadowWebAnalyzer",
1122
+ "Shareaza",
1123
+ "Shelob",
1124
+ "sherlock",
1125
+ "ShopWiki",
1126
+ "ShowLinks",
1127
+ "ShowyouBot",
1128
+ "siclab",
1129
+ "silk",
1130
+ "Siphon",
1131
+ "SiteArchive",
1132
+ "SiteCheck-sitecrawl",
1133
+ "sitecheck.internetseer.com",
1134
+ "SiteFinder",
1135
+ "SiteGuardBot",
1136
+ "SiteOrbiter",
1137
+ "SiteSnagger",
1138
+ "SiteSucker",
1139
+ "SiteSweeper",
1140
+ "SiteXpert",
1141
+ "SkimBot",
1142
+ "SkimWordsBot",
1143
+ "SkreemRBot",
1144
+ "skygrid",
1145
+ "Skywalker",
1146
+ "Sleipnir",
1147
+ "slow-crawler",
1148
+ "SlySearch",
1149
+ "smart-crawler",
1150
+ "SmartDownload",
1151
+ "Smarte",
1152
+ "smartwit.com",
1153
+ "Snake",
1154
+ "Snapbot",
1155
+ "SnapPreviewBot",
1156
+ "Snappy",
1157
+ "snookit",
1158
+ "Snooper",
1159
+ "Snoopy",
1160
+ "SocialSearcher",
1161
+ "SocSciBot",
1162
+ "SOFT411 Directory",
1163
+ "sogou",
1164
+ "sohu-search",
1165
+ "sohu agent",
1166
+ "Sokitomi",
1167
+ "Solbot",
1168
+ "sootle",
1169
+ "Sosospider",
1170
+ "Space Bison",
1171
+ "Space Fung",
1172
+ "SpaceBison",
1173
+ "SpankBot",
1174
+ "spanner",
1175
+ "Spatineo Monitor Controller",
1176
+ "special_archiver",
1177
+ "SpeedySpider",
1178
+ "Sphider",
1179
+ "Sphider2",
1180
+ "spider",
1181
+ "Spider.TerraNautic.net",
1182
+ "SpiderEngine",
1183
+ "SpiderKU",
1184
+ "SpiderMan",
1185
+ "Spinn3r",
1186
+ "Spinne",
1187
+ "sportcrew-Bot",
1188
+ "spyder3.microsys.com",
1189
+ "sqlmap",
1190
+ "Squid-Prefetch",
1191
+ "SquidClamAV_Redirector",
1192
+ "Sqworm",
1193
+ "SrevBot",
1194
+ "sslbot",
1195
+ "SSM Agent",
1196
+ "StackRambler",
1197
+ "StarDownloader",
1198
+ "statbot",
1199
+ "statcrawler",
1200
+ "statedept-crawler",
1201
+ "Steeler",
1202
+ "STEGMANN-Bot",
1203
+ "stero",
1204
+ "Stripper",
1205
+ "Stumbler",
1206
+ "suchclip",
1207
+ "sucker",
1208
+ "SumeetBot",
1209
+ "SumitBot",
1210
+ "SummizeBot",
1211
+ "SummizeFeedReader",
1212
+ "SuperBot",
1213
+ "superbot.com",
1214
+ "SuperHTTP",
1215
+ "SuperLumin",
1216
+ "SuperPagesBot",
1217
+ "Supybot",
1218
+ "SURF",
1219
+ "Surfbot",
1220
+ "SurfControl",
1221
+ "SurveyBot",
1222
+ "suzuran",
1223
+ "SWEBot",
1224
+ "swish-e",
1225
+ "SygolBot",
1226
+ "SynapticWalker",
1227
+ "Syntryx ANT Scout Chassis Pheromone",
1228
+ "SystemSearch-robot",
1229
+ "Szukacz",
1230
+ "T-H-U-N-D-E-R-S-T-O-N-E",
1231
+ "Tailrank",
1232
+ "tAkeOut",
1233
+ "TAMU_CRAWLER",
1234
+ "TapuzBot",
1235
+ "Tarantula",
1236
+ "targetblaster.com",
1237
+ "TargetYourNews.com",
1238
+ "TAUSDataBot",
1239
+ "taxinomiabot",
1240
+ "Tecomi",
1241
+ "TeezirBot",
1242
+ "Teleport",
1243
+ "Teleport Pro",
1244
+ "TeleportPro",
1245
+ "Telesoft",
1246
+ "Teradex Mapper",
1247
+ "TERAGRAM_CRAWLER",
1248
+ "TerrawizBot",
1249
+ "testbot",
1250
+ "testing of",
1251
+ "TextBot",
1252
+ "thatrobotsite.com",
1253
+ "The.Intraformant",
1254
+ "The Dyslexalizer",
1255
+ "The Intraformant",
1256
+ "TheNomad",
1257
+ "Theophrastus",
1258
+ "theusefulbot",
1259
+ "TheUsefulbot_",
1260
+ "ThumbBot",
1261
+ "thumbshots-de-bot",
1262
+ "tigerbot",
1263
+ "TightTwatBot",
1264
+ "TinEye",
1265
+ "Titan",
1266
+ "to-dress_ru_bot_",
1267
+ "to-night-Bot",
1268
+ "toCrawl",
1269
+ "Topicalizer",
1270
+ "topicblogs",
1271
+ "Toplistbot",
1272
+ "TopServer PHP",
1273
+ "topyx-crawler",
1274
+ "Touche",
1275
+ "TourlentaScanner",
1276
+ "TPSystem",
1277
+ "TRAAZI",
1278
+ "TranSGeniKBot",
1279
+ "travel-search",
1280
+ "TravelBot",
1281
+ "TravelLazerBot",
1282
+ "Treezy",
1283
+ "TREX",
1284
+ "TridentSpider",
1285
+ "Trovator",
1286
+ "True_Robot",
1287
+ "tScholarsBot",
1288
+ "TsWebBot",
1289
+ "TulipChain",
1290
+ "turingos",
1291
+ "turnit",
1292
+ "TurnitinBot",
1293
+ "TutorGigBot",
1294
+ "TweetedTimes",
1295
+ "TweetmemeBot",
1296
+ "TwengaBot",
1297
+ "TwengaBot-Discover",
1298
+ "Twiceler",
1299
+ "Twikle",
1300
+ "twinuffbot",
1301
+ "Twisted PageGetter",
1302
+ "Twitturls",
1303
+ "Twitturly",
1304
+ "TygoBot",
1305
+ "TygoProwler",
1306
+ "Typhoeus",
1307
+ "U.S. Government Printing Office",
1308
+ "uberbot",
1309
+ "ucb-nutch",
1310
+ "UCSD-Crawler",
1311
+ "UdmSearch",
1312
+ "UFAM-crawler-",
1313
+ "Ultraseek",
1314
+ "UnChaos",
1315
+ "unchaos_crawler_",
1316
+ "UnisterBot",
1317
+ "UniversalSearch",
1318
+ "UnwindFetchor",
1319
+ "UofTDB_experiment",
1320
+ "updated",
1321
+ "URI::Fetch",
1322
+ "url_gather",
1323
+ "URL-Checker",
1324
+ "URL Control",
1325
+ "URLAppendBot",
1326
+ "URLBlaze",
1327
+ "urlchecker",
1328
+ "urlck",
1329
+ "UrlDispatcher",
1330
+ "urllib",
1331
+ "URLSpiderPro",
1332
+ "URLy.Warning",
1333
+ "USAF AFKN",
1334
+ "usasearch",
1335
+ "USS-Cosmix",
1336
+ "USyd-NLP-Spider",
1337
+ "Vacobot",
1338
+ "Vacuum",
1339
+ "VadixBot",
1340
+ "Vagabondo",
1341
+ "Validator",
1342
+ "Valkyrie",
1343
+ "vBSEO",
1344
+ "VCI",
1345
+ "Vegi bot",
1346
+ "VerbstarBot",
1347
+ "VeriCiteCrawler",
1348
+ "Verifactrola",
1349
+ "Verity-URL-Gateway",
1350
+ "vermut",
1351
+ "versus",
1352
+ "versus.integis.ch",
1353
+ "viasarchivinginformation.html",
1354
+ "vikspider",
1355
+ "VIP",
1356
+ "VIPr",
1357
+ "virus-detector",
1358
+ "VisBot",
1359
+ "Vishal For CLIA",
1360
+ "VisWeb",
1361
+ "vlad",
1362
+ "vlsearch",
1363
+ "VMBot",
1364
+ "VocusBot",
1365
+ "VoidEYE",
1366
+ "VoilaBot",
1367
+ "Vortex",
1368
+ "voyager",
1369
+ "voyager-hc",
1370
+ "voyager-partner-deep",
1371
+ "VSE",
1372
+ "vspider",
1373
+ "W3C_Unicorn",
1374
+ "W3C-WebCon",
1375
+ "w3m",
1376
+ "w3search",
1377
+ "wacbot",
1378
+ "wastrix",
1379
+ "WatzBot",
1380
+ "wauuu engine",
1381
+ "Wavefire",
1382
+ "Waypath",
1383
+ "Wazzup",
1384
+ "Wazzup1.0.4800",
1385
+ "wbdbot",
1386
+ "web-agent",
1387
+ "Web-Sniffer",
1388
+ "Web.Image.Collector",
1389
+ "Web Link Validator",
1390
+ "Web Magnet",
1391
+ "webalta",
1392
+ "WebaltBot",
1393
+ "WebAuto",
1394
+ "webbandit",
1395
+ "webbot",
1396
+ "webbul-bot",
1397
+ "WebCapture",
1398
+ "webcheck",
1399
+ "Webclipping.com",
1400
+ "webcollage",
1401
+ "WebCopier",
1402
+ "WebCopy",
1403
+ "WebCorp",
1404
+ "webcrawl.net",
1405
+ "webcrawler",
1406
+ "WebDownloader for",
1407
+ "Webdup",
1408
+ "WebEMailExtrac",
1409
+ "WebEMailExtrac.*",
1410
+ "WebEnhancer",
1411
+ "WebFerret",
1412
+ "webfetch",
1413
+ "WebFetcher",
1414
+ "WebGather",
1415
+ "WebGo IS",
1416
+ "webGobbler",
1417
+ "WebImages",
1418
+ "Webinator-search2.fasthealth.com",
1419
+ "Webinator-WBI",
1420
+ "WebIndex",
1421
+ "WebIndexer",
1422
+ "weblayers",
1423
+ "WebLeacher",
1424
+ "WeblexBot",
1425
+ "WebLinker",
1426
+ "webLyzard",
1427
+ "WebmasterCoffee",
1428
+ "WebmasterWorld",
1429
+ "WebmasterWorldForumBot",
1430
+ "WebMiner",
1431
+ "WebMoose",
1432
+ "WeBot",
1433
+ "WebPix",
1434
+ "WebReaper",
1435
+ "WebRipper",
1436
+ "WebSauger",
1437
+ "Webscan",
1438
+ "websearchbench",
1439
+ "WebSite",
1440
+ "websitemirror",
1441
+ "WebSpear",
1442
+ "websphinx.test",
1443
+ "WebSpider",
1444
+ "Webster",
1445
+ "Webster.Pro",
1446
+ "Webster Pro",
1447
+ "WebStripper",
1448
+ "WebTrafficExpress",
1449
+ "WebTrends Link Analyzer",
1450
+ "webvac",
1451
+ "webwalk",
1452
+ "WebWalker",
1453
+ "Webwasher",
1454
+ "WebWatch",
1455
+ "WebWhacker",
1456
+ "WebXM",
1457
+ "WebZip",
1458
+ "Weddings.info",
1459
+ "wenbin",
1460
+ "WEPA",
1461
+ "WeRelateBot",
1462
+ "Whacker",
1463
+ "whisper",
1464
+ "Widow",
1465
+ "WikiaBot",
1466
+ "Wikio",
1467
+ "wikiwix-bot-",
1468
+ "WinHttp.WinHttpRequest",
1469
+ "WinHTTP Example",
1470
+ "WIRE",
1471
+ "wired-digital-newsbot",
1472
+ "WISEbot",
1473
+ "WISENutbot",
1474
+ "wish-la",
1475
+ "wish-project",
1476
+ "wisponbot",
1477
+ "WMCAI-robot",
1478
+ "wminer",
1479
+ "WMSBot",
1480
+ "woriobot",
1481
+ "worldshop",
1482
+ "WorQmada",
1483
+ "Wotbox",
1484
+ "WPScan",
1485
+ "wume_crawler",
1486
+ "WWW-Mechanize",
1487
+ "www.freeloader.com.",
1488
+ "WWW Collector",
1489
+ "WWWOFFLE",
1490
+ "wwwrobot",
1491
+ "wwwster",
1492
+ "WWWWanderer",
1493
+ "wwwxref",
1494
+ "Wysigot",
1495
+ "X-clawler",
1496
+ "Xaldon",
1497
+ "Xenu",
1498
+ "Xenu's",
1499
+ "Xerka MetaBot",
1500
+ "XGET",
1501
+ "xirq",
1502
+ "XmarksFetch",
1503
+ "XoviBot",
1504
+ "xqrobot",
1505
+ "Y!J",
1506
+ "Y!TunnelPro",
1507
+ "yacy.net",
1508
+ "yacybot",
1509
+ "yarienavoir.net",
1510
+ "Yasaklibot",
1511
+ "yBot",
1512
+ "YebolBot",
1513
+ "yellowJacket",
1514
+ "yes",
1515
+ "YesupBot",
1516
+ "Yeti",
1517
+ "YioopBot",
1518
+ "YisouSpider",
1519
+ "yolinkBot",
1520
+ "yoogliFetchAgent",
1521
+ "yoono",
1522
+ "Yoriwa",
1523
+ "YottaCars_Bot",
1524
+ "you-dir",
1525
+ "Z-Add Link",
1526
+ "zagrebin",
1527
+ "Zao",
1528
+ "zedzo.digest",
1529
+ "zedzo.validate",
1530
+ "zermelo",
1531
+ "Zeus",
1532
+ "Zeus Link Scout",
1533
+ "zibber-v",
1534
+ "zimeno",
1535
+ "Zing-BottaBot",
1536
+ "ZipppBot",
1537
+ "zmeu",
1538
+ "ZoomSpider",
1539
+ "ZuiBot",
1540
+ "ZumBot",
1541
+ "Zyborg",
1542
+ "Zyte",
1543
+ ];
core/modules/brute-force/class-itsec-brute-force.php CHANGED
@@ -39,7 +39,7 @@ class ITSEC_Brute_Force {
39
  // Failed authentication.
40
 
41
  $details = ITSEC_Lib::get_login_details();
42
- $SERVER = $_SERVER;
43
 
44
  if ( 'admin' === $username && $this->settings['auto_ban_admin'] ) {
45
  ITSEC_Log::add_notice( 'brute_force', 'auto-ban-admin-username', compact( 'details', 'user', 'username', 'SERVER' ) );
39
  // Failed authentication.
40
 
41
  $details = ITSEC_Lib::get_login_details();
42
+ $SERVER = ITSEC_Lib::get_server_snapshot();
43
 
44
  if ( 'admin' === $username && $this->settings['auto_ban_admin'] ) {
45
  ITSEC_Log::add_notice( 'brute_force', 'auto-ban-admin-username', compact( 'details', 'user', 'username', 'SERVER' ) );
core/modules/brute-force/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Brute Force', 'better-wp-security' ),
5
+ ];
core/modules/content-directory/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Change Content Directory', 'better-wp-security' ),
5
+ ];
core/modules/core/entries/admin-notices/components/admin-bar/index.js CHANGED
@@ -24,7 +24,7 @@ function AdminBar( { notices, noticesLoaded, isToggled, setState } ) {
24
  <AdminBarFill>
25
  <div className="itsec-admin-bar__admin-notices">
26
  <div className={ classnames( 'itsec-admin-bar-admin-notices__trigger', { 'itsec-admin-bar-admin-notices__trigger--has-notices': notices.length > 0 } ) }>
27
- <Button aria-expanded={ isToggled } onClick={ () => setState( { isToggled: ! isToggled } ) }>
28
  <Dashicon icon="megaphone" size={ 15 } />
29
  { __( 'Notifications', 'better-wp-security' ) }
30
  </Button>
@@ -38,9 +38,22 @@ function AdminBar( { notices, noticesLoaded, isToggled, setState } ) {
38
  onClose={ () => setState( { isToggled: false } ) }
39
  onClickOutside={ ( e ) => {
40
  if (
41
- e.target.id !== 'itsec-admin-notices-toolbar-trigger' &&
42
- e.target.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' &&
43
- ! doesElementBelongToPanel( e.target )
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  ) {
45
  setState( { isToggled: false } );
46
  }
24
  <AdminBarFill>
25
  <div className="itsec-admin-bar__admin-notices">
26
  <div className={ classnames( 'itsec-admin-bar-admin-notices__trigger', { 'itsec-admin-bar-admin-notices__trigger--has-notices': notices.length > 0 } ) }>
27
+ <Button aria-expanded={ isToggled } onClick={ () => setState( { isToggled: ! isToggled } ) } isSecondary>
28
  <Dashicon icon="megaphone" size={ 15 } />
29
  { __( 'Notifications', 'better-wp-security' ) }
30
  </Button>
38
  onClose={ () => setState( { isToggled: false } ) }
39
  onClickOutside={ ( e ) => {
40
  if (
41
+ ! e.target || (
42
+ e.target.id !== 'itsec-admin-notices-toolbar-trigger' &&
43
+ e.target.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' &&
44
+ ! doesElementBelongToPanel( e.target )
45
+ )
46
+ ) {
47
+ setState( { isToggled: false } );
48
+ }
49
+ } }
50
+ onFocusOutside={ () => {
51
+ const activeElement = document.activeElement;
52
+
53
+ if (
54
+ activeElement.id !== 'itsec-admin-notices-toolbar-trigger' &&
55
+ ( ! activeElement.parentNode || activeElement.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' ) &&
56
+ ! doesElementBelongToPanel( activeElement )
57
  ) {
58
  setState( { isToggled: false } );
59
  }
core/modules/core/entries/admin-notices/components/admin-bar/style.scss CHANGED
@@ -6,6 +6,8 @@
6
 
7
  & .itsec-admin-bar-admin-notices__trigger > .components-button {
8
  color: $main-blue;
 
 
9
 
10
  &:hover {
11
  color: lighten($main-blue, 20%);
6
 
7
  & .itsec-admin-bar-admin-notices__trigger > .components-button {
8
  color: $main-blue;
9
+ border: none;
10
+ background: transparent;
11
 
12
  &:hover {
13
  color: lighten($main-blue, 20%);
core/modules/core/entries/admin-notices/components/panel/style.scss CHANGED
@@ -42,7 +42,7 @@
42
  }
43
  }
44
 
45
- & .itsec-admin-notice-panel__configure-trigger.components-icon-button {
46
  padding: 5px;
47
  height: 30px;
48
  position: absolute;
@@ -56,7 +56,7 @@
56
  }
57
  }
58
 
59
- &.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-icon-button {
60
  color: $main-blue;
61
 
62
  &:hover {
42
  }
43
  }
44
 
45
+ & .itsec-admin-notice-panel__configure-trigger.components-button {
46
  padding: 5px;
47
  height: 30px;
48
  position: absolute;
56
  }
57
  }
58
 
59
+ &.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-button {
60
  color: $main-blue;
61
 
62
  &:hover {
core/modules/core/entries/admin-notices/components/toolbar/index.js CHANGED
@@ -50,9 +50,22 @@ function Toolbar( { notices, noticesLoaded, isToggled, setState } ) {
50
  onClose={ () => setState( { isToggled: false } ) }
51
  onClickOutside={ ( e ) => {
52
  if (
53
- e.target.id !== 'itsec-admin-notices-toolbar-trigger' &&
54
- e.target.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' &&
55
- ! doesElementBelongToPanel( e.target )
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  ) {
57
  setState( { isToggled: false } );
58
  }
50
  onClose={ () => setState( { isToggled: false } ) }
51
  onClickOutside={ ( e ) => {
52
  if (
53
+ ! e.target || (
54
+ e.target.id !== 'itsec-admin-notices-toolbar-trigger' &&
55
+ e.target.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' &&
56
+ ! doesElementBelongToPanel( e.target )
57
+ )
58
+ ) {
59
+ setState( { isToggled: false } );
60
+ }
61
+ } }
62
+ onFocusOutside={ () => {
63
+ const activeElement = document.activeElement;
64
+
65
+ if (
66
+ activeElement.id !== 'itsec-admin-notices-toolbar-trigger' &&
67
+ ( ! activeElement.parentNode || activeElement.parentNode.id !== 'itsec-admin-notices-toolbar-trigger' ) &&
68
+ ! doesElementBelongToPanel( activeElement )
69
  ) {
70
  setState( { isToggled: false } );
71
  }
core/modules/core/entries/admin-notices/store/controls.js CHANGED
@@ -12,7 +12,7 @@ import { default as triggerApiFetch } from '@wordpress/api-fetch';
12
  /**
13
  * Internal dependencies
14
  */
15
- import responseToError from '@ithemes/security-utils';
16
 
17
  /**
18
  * Trigger an API Fetch request.
12
  /**
13
  * Internal dependencies
14
  */
15
+ import { responseToError } from '@ithemes/security-utils';
16
 
17
  /**
18
  * Trigger an API Fetch request.
core/modules/core/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Core', 'better-wp-security' ),
5
+ ];
core/modules/core/notices.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Admin_Notice_New_Feature_Core implements ITSEC_Admin_Notice {
4
+
5
+ public function get_id() {
6
+ return 'release-user-groups';
7
+ }
8
+
9
+ public function get_title() {
10
+ return '';
11
+ }
12
+
13
+ public function get_message() {
14
+ return sprintf(
15
+ esc_html__( 'New in iThemes Security version %1$s, %2$sSave Time Securing WordPress With User Groups%3$s!', 'better-wp-security' ),
16
+ ITSEC_Core::is_pro() ? '6.4.0' : '7.7.0',
17
+ '<a href="{{ $blog }}">',
18
+ '</a>'
19
+ );
20
+ }
21
+
22
+ public function get_meta() {
23
+ return array();
24
+ }
25
+
26
+ public function get_severity() {
27
+ return self::S_INFO;
28
+ }
29
+
30
+ public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
31
+ return true;
32
+ }
33
+
34
+ public function get_actions() {
35
+ return array(
36
+ 'blog' => new ITSEC_Admin_Notice_Action_Link(
37
+ add_query_arg( 'itsec_view_release_post', 'user-groups', admin_url( 'index.php' ) ),
38
+ esc_html__( 'See How It Works', 'better-wp-security' ),
39
+ ITSEC_Admin_Notice_Action::S_PRIMARY,
40
+ function () {
41
+ $this->handle_dismiss();
42
+
43
+ wp_redirect( 'https://ithemes.com/new-save-time-securing-wordpress-with-user-groups' );
44
+ die;
45
+ }
46
+ )
47
+ );
48
+ }
49
+
50
+ private function handle_dismiss() {
51
+ $dismissed = $this->get_storage();
52
+ $dismissed[] = $this->get_id();
53
+ $this->save_storage( $dismissed );
54
+
55
+ return null;
56
+ }
57
+
58
+ private function get_storage() {
59
+ $dismissed = get_site_option( 'itsec_dismissed_notices', array() );
60
+
61
+ if ( ! is_array( $dismissed ) ) {
62
+ $dismissed = array();
63
+ }
64
+
65
+ return $dismissed;
66
+ }
67
+
68
+ private function save_storage( $storage ) {
69
+ update_site_option( 'itsec_dismissed_notices', $storage );
70
+ }
71
+ }
72
+
73
+ ITSEC_Lib_Admin_Notices::register( new ITSEC_Admin_Notice_Globally_Dismissible( new ITSEC_Admin_Notice_Managers_Only( new ITSEC_Admin_Notice_New_Feature_Core() ) ) );
core/modules/database-prefix/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Change Database Prefix', 'better-wp-security' ),
5
+ ];
core/modules/feature-flags/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Feature Flags', 'better-wp-security' ),
5
+ ];
core/modules/feature-flags/settings-page.php CHANGED
@@ -7,21 +7,21 @@ class ITSEC_Feature_Flags_Settings_Page extends ITSEC_Module_Settings_Page {
7
 
8
  $this->id = 'feature-flags';
9
  $this->title = esc_html__( 'Feature Flags', 'better-wp-security' );
10
- $this->description = esc_html__( 'Toggle feature flags.', 'better-wp-security' );
11
  $this->type = 'advanced';
12
  }
13
 
14
  public function register() {
15
  ITSEC_Lib::load( 'feature-flags' );
16
 
17
- if ( ITSEC_Lib_Feature_Flags::get_available_flags() ) {
18
  parent::register();
19
  }
20
  }
21
 
22
  protected function render_description( $form ) {
23
  echo '<p>';
24
- echo esc_html__( 'Toggle feature flags.', 'better-wp-security' );
25
  echo '</p>';
26
  }
27
 
@@ -43,11 +43,13 @@ class ITSEC_Feature_Flags_Settings_Page extends ITSEC_Module_Settings_Page {
43
  $description = $config['description'];
44
  }
45
 
46
- $form->set_option( $flag, ITSEC_Lib_Feature_Flags::is_enabled( $flag ) );
 
 
47
 
48
  $cb_opts = array();
49
 
50
- if ( defined( 'ITSEC_FF_' . $flag ) ) {
51
  $cb_opts['disabled'] = true;
52
  }
53
  ?>
@@ -60,8 +62,19 @@ class ITSEC_Feature_Flags_Settings_Page extends ITSEC_Module_Settings_Page {
60
  <td>
61
  <?php $form->add_checkbox( $flag, $cb_opts ); ?>
62
  <?php if ( $description ): ?>
63
- <p class="description"><?php echo $description; ?></p>
 
 
 
64
  <?php endif; ?>
 
 
 
 
 
 
 
 
65
  </td>
66
  </tr>
67
  </tbody>
7
 
8
  $this->id = 'feature-flags';
9
  $this->title = esc_html__( 'Feature Flags', 'better-wp-security' );
10
+ $this->description = esc_html__( 'Feature Flags in iThemes Security allow you to try experimental features before they are released.', 'better-wp-security' );
11
  $this->type = 'advanced';
12
  }
13
 
14
  public function register() {
15
  ITSEC_Lib::load( 'feature-flags' );
16
 
17
+ if ( ITSEC_Lib_Feature_Flags::get_available_flags() && ITSEC_Lib_Feature_Flags::show_ui() ) {
18
  parent::register();
19
  }
20
  }
21
 
22
  protected function render_description( $form ) {
23
  echo '<p>';
24
+ echo $this->description;
25
  echo '</p>';
26
  }
27
 
43
  $description = $config['description'];
44
  }
45
 
46
+ $enabled = ITSEC_Lib_Feature_Flags::is_enabled( $flag );
47
+ $form->set_option( $flag, $enabled );
48
+ list( $reason_code, $reason ) = ITSEC_Lib_Feature_Flags::get_reason( $flag );
49
 
50
  $cb_opts = array();
51
 
52
+ if ( 'constant' === $reason_code ) {
53
  $cb_opts['disabled'] = true;
54
  }
55
  ?>
62
  <td>
63
  <?php $form->add_checkbox( $flag, $cb_opts ); ?>
64
  <?php if ( $description ): ?>
65
+ <p><?php echo $description; ?></p>
66
+ <?php endif; ?>
67
+ <?php if ( $config['documentation'] ): ?>
68
+ <p><a href="<?php echo esc_url( $config['documentation'] ) ?>"><?php esc_html_e( 'Read the documentation', 'better-wp-security' ); ?></a></p>
69
  <?php endif; ?>
70
+
71
+ <p class="description">
72
+ <?php if ( $enabled ): ?>
73
+ <?php echo esc_html( sprintf( __( 'Enabled reason: %s', 'better-wp-security' ), $reason ) ); ?>
74
+ <?php else: ?>
75
+ <?php echo esc_html( sprintf( __( 'Disabled reason: %s', 'better-wp-security' ), $reason ) ); ?>
76
+ <?php endif; ?>
77
+ </p>
78
  </td>
79
  </tr>
80
  </tbody>
core/modules/file-change/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'File Change', 'better-wp-security' ),
5
+ ];
core/modules/file-permissions/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'File Permissions', 'better-wp-security' ),
5
+ ];
core/modules/global/active.php CHANGED
@@ -1,8 +1,11 @@
1
  <?php
2
 
 
 
3
  function itsec_global_filter_whitelisted_ips( $whitelisted_ips ) {
4
  return array_merge( $whitelisted_ips, ITSEC_Modules::get_setting( 'global', 'lockout_white_list', array() ) );
5
  }
 
6
  add_filter( 'itsec_white_ips', 'itsec_global_filter_whitelisted_ips', 0 );
7
 
8
  /**
@@ -115,3 +118,14 @@ function itsec_basename_attachment_thumbs( $data ) {
115
  }
116
 
117
  add_filter( 'wp_update_attachment_metadata', 'itsec_basename_attachment_thumbs' );
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
+ use \iThemesSecurity\User_Groups;
4
+
5
  function itsec_global_filter_whitelisted_ips( $whitelisted_ips ) {
6
  return array_merge( $whitelisted_ips, ITSEC_Modules::get_setting( 'global', 'lockout_white_list', array() ) );
7
  }
8
+
9
  add_filter( 'itsec_white_ips', 'itsec_global_filter_whitelisted_ips', 0 );
10
 
11
  /**
118
  }
119
 
120
  add_filter( 'wp_update_attachment_metadata', 'itsec_basename_attachment_thumbs' );
121
+
122
+ function itsec_register_global_user_group_settings( User_Groups\Settings_Registry $registry ) {
123
+ $registry->register( new User_Groups\Settings_Registration( 'global', 'manage_group', User_Groups\Settings_Registration::T_MULTIPLE, static function () {
124
+ return [
125
+ 'title' => __( 'Manage iThemes Security', 'better-wp-security' ),
126
+ 'description' => __( 'Allow users in the group to manage iThemes Security.', 'better-wp-security' ),
127
+ ];
128
+ } ) );
129
+ }
130
+
131
+ add_action( 'itsec_register_user_group_settings', 'itsec_register_global_user_group_settings', 0 );
core/modules/global/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Global', 'better-wp-security' ),
5
+ ];
core/modules/global/settings-page.php CHANGED
@@ -223,6 +223,19 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
223
  </p>
224
  </td>
225
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  <tr>
227
  <th scope="row"><label for="itsec-global-log_type"><?php _e( 'Log Type', 'better-wp-security' ); ?></label></th>
228
  <td>
223
  </p>
224
  </td>
225
  </tr>
226
+ <tr>
227
+ <th scope="row">
228
+ <label for="itsec-global-manage_group">
229
+ <?php esc_html_e( 'Manage iThemes Security', 'better-wp-security' ) ?>
230
+ </label>
231
+ </th>
232
+ <td>
233
+ <p class="description">
234
+ <?php esc_html_e( 'Select the group of users that can manage iThemes Security. If no groups are selected, all administrator users will have access.', 'better-wp-security' ); ?>
235
+ </p>
236
+ <?php $form->add_user_groups( 'manage_group', $this->id ); ?>
237
+ </td>
238
+ </tr>
239
  <tr>
240
  <th scope="row"><label for="itsec-global-log_type"><?php _e( 'Log Type', 'better-wp-security' ); ?></label></th>
241
  <td>
core/modules/global/settings.php CHANGED
@@ -40,10 +40,13 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
40
  'enable_grade_report' => false,
41
  'server_ips' => array(),
42
  'feature_flags' => array(),
 
43
  );
44
  }
45
 
46
  protected function handle_settings_changes( $old_settings ) {
 
 
47
  if ( $this->settings['write_files'] && ! $old_settings['write_files'] ) {
48
  ITSEC_Response::regenerate_server_config();
49
  ITSEC_Response::regenerate_wp_config();
@@ -58,7 +61,7 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
58
  ITSEC_Modules::load_module_file( 'activate.php', 'grade-report' );
59
  ITSEC_Response::flag_new_notifications_available();
60
  ITSEC_Response::refresh_page();
61
- } else if ( ! $this->settings['enable_grade_report'] && $old_settings['enable_grade_report'] ) {
62
  update_site_option( 'itsec-enable-grade-report', false );
63
  ITSEC_Modules::load_module_file( 'deactivate.php', 'grade-report' );
64
  ITSEC_Response::refresh_page();
40
  'enable_grade_report' => false,
41
  'server_ips' => array(),
42
  'feature_flags' => array(),
43
+ 'manage_group' => array(),
44
  );
45
  }
46
 
47
  protected function handle_settings_changes( $old_settings ) {
48
+ parent::handle_settings_changes( $old_settings );
49
+
50
  if ( $this->settings['write_files'] && ! $old_settings['write_files'] ) {
51
  ITSEC_Response::regenerate_server_config();
52
  ITSEC_Response::regenerate_wp_config();
61
  ITSEC_Modules::load_module_file( 'activate.php', 'grade-report' );
62
  ITSEC_Response::flag_new_notifications_available();
63
  ITSEC_Response::refresh_page();
64
+ } elseif ( ! $this->settings['enable_grade_report'] && $old_settings['enable_grade_report'] ) {
65
  update_site_option( 'itsec-enable-grade-report', false );
66
  ITSEC_Modules::load_module_file( 'deactivate.php', 'grade-report' );
67
  ITSEC_Response::refresh_page();
core/modules/global/validator.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  class ITSEC_Global_Validator extends ITSEC_Validator {
4
  public function get_id() {
5
  return 'global';
@@ -60,6 +62,18 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
60
 
61
  $this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
62
  $this->sanitize_setting( 'array', 'feature_flags', __( 'Feature Flags', 'better-wp-security' ) );
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  public function get_proxy_types() {
1
  <?php
2
 
3
+ use \iThemesSecurity\User_Groups;
4
+
5
  class ITSEC_Global_Validator extends ITSEC_Validator {
6
  public function get_id() {
7
  return 'global';
62
 
63
  $this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
64
  $this->sanitize_setting( 'array', 'feature_flags', __( 'Feature Flags', 'better-wp-security' ) );
65
+ $this->sanitize_setting( 'user-groups', 'manage_group', __( 'Manage Group', 'better-wp-security' ) );
66
+ }
67
+
68
+ protected function validate_settings() {
69
+ if ( ITSEC_Core::is_interactive() && $this->settings['manage_group'] && $this->settings['manage_group'] !== $this->previous_settings['manage_group'] ) {
70
+ $matcher = ITSEC_Modules::get_container()->get( User_Groups\Matcher::class );
71
+
72
+ if ( ! $matcher->matches( User_Groups\Match_Target::for_user( wp_get_current_user() ), $this->settings['manage_group'] ) ) {
73
+ $this->add_error( new WP_Error( 'itsec-validator-global-cannot-exclude-self', __( 'The configuration you have chosen removes your capability to manage iThemes Security.', 'better-wp-security' ), [ 'status' => 400 ] ) );
74
+ $this->set_can_save( false );
75
+ }
76
+ }
77
  }
78
 
79
  public function get_proxy_types() {
core/modules/hide-backend/class-itsec-hide-backend.php CHANGED
@@ -55,7 +55,7 @@ class ITSEC_Hide_Backend {
55
  // The email is plain text and the links are at the end of lines, so a lazy match can be used.
56
  if ( preg_match_all( '|(https?:\/\/((.*)wp-admin(.*)))|', $text, $urls ) ) {
57
  foreach ( $urls[0] as $url ) {
58
- $url = trim( $url );
59
  $text = str_replace( $url, wp_login_url( $url ), $text );
60
  }
61
  }
@@ -139,17 +139,27 @@ class ITSEC_Hide_Backend {
139
 
140
  if ( 'postpass' === $action ) {
141
  return;
142
- } else if ( 'register' === $action ) {
143
- $this->block_access( 'register' );
 
 
 
 
 
144
  return;
145
- } else if ( 'jetpack_json_api_authorization' === $action && has_filter( 'login_form_jetpack_json_api_authorization' ) ) {
 
 
146
  // Jetpack handles authentication for this action. Processing is left to it.
147
  return;
148
- } else if ( 'jetpack-sso' === $action && has_filter( 'login_form_jetpack-sso' ) ) {
 
 
149
  // Jetpack's SSO redirects from wordpress.com to wp-login.php on the site. Only allow this process to
150
  // continue if they successfully log in, which should happen by login_init in Jetpack which happens just
151
  // before this action fires.
152
  add_action( 'login_form_jetpack-sso', array( $this, 'block_access' ) );
 
153
  return;
154
  }
155
 
@@ -239,9 +249,9 @@ class ITSEC_Hide_Backend {
239
  $this->set_cookie( $type );
240
 
241
  // Preserve existing query vars and add access token query arg.
242
- $query_vars = $_GET;
243
- $query_vars[$this->token_var] = $this->get_access_token( $type );
244
- $query = http_build_query( $query_vars, null, '&' );
245
 
246
  // Disable the Hide Backend URL filters to prevent infinite loops when calling site_url().
247
  $this->disable_filters = true;
@@ -285,7 +295,7 @@ class ITSEC_Hide_Backend {
285
  } elseif ( 'wp-login.php' !== $request_path || empty( $_GET['action'] ) || 'register' !== $_GET['action'] ) {
286
  $url = $this->add_token_to_url( $url, 'login' );
287
  }
288
- } else if ( 'wp-signup.php' === $clean_path && 'wp-signup.php' !== $this->settings['register'] ) {
289
  $url = $this->add_token_to_url( $url, 'register' );
290
  }
291
 
@@ -295,7 +305,7 @@ class ITSEC_Hide_Backend {
295
  /**
296
  * Filter the admin URL to include hide backend tokens when necessary.
297
  *
298
- * @param string $url Complete admin URL.
299
  * @param string $path Path passed to the admin_url function.
300
  *
301
  * @return string
@@ -443,10 +453,11 @@ class ITSEC_Hide_Backend {
443
  private function is_validated( $type ) {
444
  $token = $this->get_access_token( $type );
445
 
446
- if ( isset( $_REQUEST[$this->token_var] ) && $_REQUEST[$this->token_var] === $token ) {
447
  $this->set_cookie( $type );
 
448
  return true;
449
- } else if ( isset( $_COOKIE["itsec-hb-$type-" . COOKIEHASH] ) && $_COOKIE["itsec-hb-$type-" . COOKIEHASH] === $token ) {
450
  return true;
451
  }
452
 
@@ -461,8 +472,8 @@ class ITSEC_Hide_Backend {
461
  * @return string The access token.
462
  */
463
  private function get_access_token( $type ) {
464
- if ( isset( $this->settings[$type] ) ) {
465
- return $this->settings[$type];
466
  }
467
 
468
  return $this->settings['slug'];
55
  // The email is plain text and the links are at the end of lines, so a lazy match can be used.
56
  if ( preg_match_all( '|(https?:\/\/((.*)wp-admin(.*)))|', $text, $urls ) ) {
57
  foreach ( $urls[0] as $url ) {
58
+ $url = trim( $url );
59
  $text = str_replace( $url, wp_login_url( $url ), $text );
60
  }
61
  }
139
 
140
  if ( 'postpass' === $action ) {
141
  return;
142
+ }
143
+
144
+ if ( 'register' === $action ) {
145
+ if ( 'wp-signup.php' !== $this->settings['register'] ) {
146
+ $this->block_access( 'register' );
147
+ }
148
+
149
  return;
150
+ }
151
+
152
+ if ( 'jetpack_json_api_authorization' === $action && has_filter( 'login_form_jetpack_json_api_authorization' ) ) {
153
  // Jetpack handles authentication for this action. Processing is left to it.
154
  return;
155
+ }
156
+
157
+ if ( 'jetpack-sso' === $action && has_filter( 'login_form_jetpack-sso' ) ) {
158
  // Jetpack's SSO redirects from wordpress.com to wp-login.php on the site. Only allow this process to
159
  // continue if they successfully log in, which should happen by login_init in Jetpack which happens just
160
  // before this action fires.
161
  add_action( 'login_form_jetpack-sso', array( $this, 'block_access' ) );
162
+
163
  return;
164
  }
165
 
249
  $this->set_cookie( $type );
250
 
251
  // Preserve existing query vars and add access token query arg.
252
+ $query_vars = $_GET;
253
+ $query_vars[ $this->token_var ] = $this->get_access_token( $type );
254
+ $query = http_build_query( $query_vars, null, '&' );
255
 
256
  // Disable the Hide Backend URL filters to prevent infinite loops when calling site_url().
257
  $this->disable_filters = true;
295
  } elseif ( 'wp-login.php' !== $request_path || empty( $_GET['action'] ) || 'register' !== $_GET['action'] ) {
296
  $url = $this->add_token_to_url( $url, 'login' );
297
  }
298
+ } elseif ( 'wp-signup.php' === $clean_path && 'wp-signup.php' !== $this->settings['register'] ) {
299
  $url = $this->add_token_to_url( $url, 'register' );
300
  }
301
 
305
  /**
306
  * Filter the admin URL to include hide backend tokens when necessary.
307
  *
308
+ * @param string $url Complete admin URL.
309
  * @param string $path Path passed to the admin_url function.
310
  *
311
  * @return string
453
  private function is_validated( $type ) {
454
  $token = $this->get_access_token( $type );
455
 
456
+ if ( isset( $_REQUEST[ $this->token_var ] ) && $_REQUEST[ $this->token_var ] === $token ) {
457
  $this->set_cookie( $type );
458
+
459
  return true;
460
+ } elseif ( isset( $_COOKIE[ "itsec-hb-$type-" . COOKIEHASH ] ) && $_COOKIE[ "itsec-hb-$type-" . COOKIEHASH ] === $token ) {
461
  return true;
462
  }
463
 
472
  * @return string The access token.
473
  */
474
  private function get_access_token( $type ) {
475
+ if ( isset( $this->settings[ $type ] ) ) {
476
+ return $this->settings[ $type ];
477
  }
478
 
479
  return $this->settings['slug'];
core/modules/hide-backend/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Hide Backend', 'better-wp-security' ),
5
+ ];
core/modules/hide-backend/settings.php CHANGED
@@ -17,6 +17,7 @@ final class ITSEC_Hide_Backend_Settings extends ITSEC_Settings {
17
  }
18
 
19
  protected function handle_settings_changes( $old_settings ) {
 
20
 
21
  if ( $this->settings['enabled'] && ! $old_settings['enabled'] ) {
22
  $url = get_site_url() . '/' . $this->settings['slug'];
17
  }
18
 
19
  protected function handle_settings_changes( $old_settings ) {
20
+ parent::handle_settings_changes( $old_settings );
21
 
22
  if ( $this->settings['enabled'] && ! $old_settings['enabled'] ) {
23
  $url = get_site_url() . '/' . $this->settings['slug'];
core/modules/ipcheck/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Network Brute Force', 'better-wp-security' ),
5
+ ];
core/modules/malware/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Malware', 'better-wp-security' ),
5
+ ];
core/modules/malware/settings-page.php CHANGED
@@ -5,8 +5,10 @@ class ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan extends ITSEC_Settings_Pag
5
 
6
 
7
  public function __construct() {
8
- $this->id = 'malware-scan';
9
- $this->title = __( 'Malware Scan', 'better-wp-security' );
 
 
10
  $this->priority = 8;
11
 
12
  parent::__construct();
@@ -23,6 +25,7 @@ class ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan extends ITSEC_Settings_Pag
23
  wp_localize_script( 'itsec-settings-malware-scan-settings-script', 'itsecMalwareScanData', $vars );
24
 
25
  wp_enqueue_style( 'itsec-settings-malware-scan-style', plugins_url( 'css/settings.css', __FILE__ ), array(), $this->script_version );
 
26
  }
27
 
28
  public function handle_ajax_request( $data ) {
@@ -46,21 +49,32 @@ class ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan extends ITSEC_Settings_Pag
46
  }
47
 
48
  public function render( $form ) {
 
 
 
 
 
 
 
 
 
 
49
 
50
- ?>
51
- <div class="hide-if-no-js">
52
- <p><?php printf( __( 'This malware scan is powered by <a href="%s">Sucuri SiteCheck</a>. It checks for known malware, blacklisting status, website errors and out-of-date software. Although the Sucuri team does its best to provide thorough results, 100%% accuracy is not realistic and is not guaranteed.', 'better-wp-security' ), esc_url( 'https://ithemes.com/sitecheck' ) ); ?></p>
53
- <p><?php printf( __( 'Results of previous malware scans can be found on the <a href="%s">logs page</a>.', 'better-wp-security' ), ITSEC_Core::get_logs_page_url('malware' ) ); ?></p>
54
- <div class='itsec-malware-scan-results-wrapper'></div>
55
- <?php $form->add_button( 'start', array( 'value' => __( 'Scan Homepage for Malware', 'better-wp-security' ), 'class' => 'button-primary' ) ); ?>
56
- </div>
57
- <div class="hide-if-js">
58
- <p><?php _e( 'The malware scanner requires Javascript in order to function. If Javascript is disabled in your browser, please enable it. If Javascript is not disabled, a script from another plugin, the theme, or a broken WordPress file is preventing the malware scanner\'s script from executing properly. Please try disabling other plugins to see if that resolves the issue.', 'better-wp-security' ); ?></p>
59
- </div>
60
- <?php
61
  }
62
 
63
  protected function save( $data ) {
64
  }
65
  }
 
66
  new ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan();
5
 
6
 
7
  public function __construct() {
8
+ ITSEC_Lib::load( 'feature-flags' );
9
+
10
+ $this->id = 'malware-scan';
11
+ $this->title = ITSEC_Lib_Feature_Flags::is_enabled( 'site_scanner' ) ? __( 'Site Scan', 'better-wp-security' ) : __( 'Malware Scan', 'better-wp-security' );
12
  $this->priority = 8;
13
 
14
  parent::__construct();
25
  wp_localize_script( 'itsec-settings-malware-scan-settings-script', 'itsecMalwareScanData', $vars );
26
 
27
  wp_enqueue_style( 'itsec-settings-malware-scan-style', plugins_url( 'css/settings.css', __FILE__ ), array(), $this->script_version );
28
+ wp_enqueue_style( 'itsec-core-packages-components-site-scan-results-style' );
29
  }
30
 
31
  public function handle_ajax_request( $data ) {
49
  }
50
 
51
  public function render( $form ) {
52
+ if ( ITSEC_Lib_Feature_Flags::is_enabled( 'site_scanner' ) ) {
53
+ $button = __( 'Scan Now', 'better-wp-security' );
54
+ $module = 'site-scanner';
55
+ $description = __( 'This %1$ssite scan is powered by iThemes%2$s. We use several datapoints to check for known malware, blacklisting status, website errors and out-of-date software. These datapoints are not 100% accurate, but we try our best to provide thorough results.', 'better-wp-security' );
56
+ $description = sprintf( $description, '<a href="https://help.ithemes.com/hc/en-us/articles/360046334433" target="_blank">', '</a>' );
57
+ } else {
58
+ $button = __( 'Scan Homepage for Malware', 'better-wp-security' );
59
+ $module = 'malware';
60
+ $description = sprintf( __( 'This malware scan is powered by <a href="%s">Sucuri SiteCheck</a>. It checks for known malware, blacklisting status, website errors and out-of-date software. Although the Sucuri team does its best to provide thorough results, 100%% accuracy is not realistic and is not guaranteed.', 'better-wp-security' ), esc_url( 'https://ithemes.com/sitecheck' ) );
61
+ }
62
 
63
+ ?>
64
+ <div class="hide-if-no-js">
65
+ <p><?php echo $description; ?></p>
66
+ <p><?php printf( __( 'Results of previous scans can be found on the <a href="%s">logs page</a>.', 'better-wp-security' ), ITSEC_Core::get_logs_page_url( $module ) ); ?></p>
67
+ <div class='itsec-malware-scan-results-wrapper'></div>
68
+ <?php $form->add_button( 'start', array( 'value' => $button, 'class' => 'button-primary' ) ); ?>
69
+ </div>
70
+ <div class="hide-if-js">
71
+ <p><?php _e( 'The malware scanner requires Javascript in order to function. If Javascript is disabled in your browser, please enable it. If Javascript is not disabled, a script from another plugin, the theme, or a broken WordPress file is preventing the malware scanner\'s script from executing properly. Please try disabling other plugins to see if that resolves the issue.', 'better-wp-security' ); ?></p>
72
+ </div>
73
+ <?php
74
  }
75
 
76
  protected function save( $data ) {
77
  }
78
  }
79
+
80
  new ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan();
core/modules/multisite-tweaks/class-itsec-multisite-tweaks.php CHANGED
@@ -6,34 +6,34 @@ final class ITSEC_Multisite_Tweaks {
6
  // Don't risk blocking anything with WP_CLI.
7
  return;
8
  }
9
-
10
  add_action( 'init', array( $this, 'init' ) );
11
  }
12
-
13
  public function init() {
14
  if ( ITSEC_Core::is_iwp_call() ) {
15
  return;
16
  }
17
-
18
- if ( current_user_can( 'manage_options' ) ) {
19
  return;
20
  }
21
-
22
-
23
  $settings = ITSEC_Modules::get_settings( 'multisite-tweaks' );
24
-
25
  if ( $settings['theme_updates'] ) {
26
  remove_action( 'load-update-core.php', 'wp_update_themes' );
27
  add_filter( 'pre_site_transient_update_themes', '__return_null' );
28
  wp_clear_scheduled_hook( 'wp_update_themes' );
29
  }
30
-
31
  if ( $settings['plugin_updates'] ) {
32
  remove_action( 'load-update-core.php', 'wp_update_plugins' );
33
  add_filter( 'pre_site_transient_update_plugins', '__return_null' );
34
  wp_clear_scheduled_hook( 'wp_update_plugins' );
35
  }
36
-
37
  if ( $settings['core_updates'] ) {
38
  remove_action( 'admin_notices', 'update_nag', 3 );
39
  add_filter( 'pre_site_transient_update_core', '__return_null' );
6
  // Don't risk blocking anything with WP_CLI.
7
  return;
8
  }
9
+
10
  add_action( 'init', array( $this, 'init' ) );
11
  }
12
+
13
  public function init() {
14
  if ( ITSEC_Core::is_iwp_call() ) {
15
  return;
16
  }
17
+
18
+ if ( current_user_can( 'manage_options' ) || wp_doing_cron() ) {
19
  return;
20
  }
21
+
22
+
23
  $settings = ITSEC_Modules::get_settings( 'multisite-tweaks' );
24
+
25
  if ( $settings['theme_updates'] ) {
26
  remove_action( 'load-update-core.php', 'wp_update_themes' );
27
  add_filter( 'pre_site_transient_update_themes', '__return_null' );
28
  wp_clear_scheduled_hook( 'wp_update_themes' );
29
  }
30
+
31
  if ( $settings['plugin_updates'] ) {
32
  remove_action( 'load-update-core.php', 'wp_update_plugins' );
33
  add_filter( 'pre_site_transient_update_plugins', '__return_null' );
34
  wp_clear_scheduled_hook( 'wp_update_plugins' );
35
  }
36
+
37
  if ( $settings['core_updates'] ) {
38
  remove_action( 'admin_notices', 'update_nag', 3 );
39
  add_filter( 'pre_site_transient_update_core', '__return_null' );
core/modules/multisite-tweaks/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Multisite Tweaks', 'better-wp-security' ),
5
+ ];
core/modules/multisite-tweaks/settings-page.php CHANGED
@@ -6,22 +6,21 @@ final class ITSEC_Multisite_Tweaks_Settings_Page extends ITSEC_Module_Settings_P
6
  $this->title = __( 'Multisite Tweaks', 'better-wp-security' );
7
  $this->description = __( 'Advanced settings that improve security by changing default WordPress Multisite behavior.', 'better-wp-security' );
8
  $this->type = 'recommended';
9
-
10
  parent::__construct();
11
  }
12
-
13
  protected function render_description( $form ) {
14
-
15
  ?>
16
  <p><?php _e( 'These are advanced settings that may be utilized to further strengthen the security of your WordPress site.', 'better-wp-security' ); ?></p>
17
  <?php
18
-
19
  }
20
-
21
  protected function render_settings( $form ) {
22
-
23
  ?>
24
- <p><?php _e( 'Note: These settings are listed as advanced because they block common forms of attacks but they can also block legitimate plugins and themes that rely on the same techniques. When activating the settings below, we recommend enabling them one by one to test that everything on your site is still working as expected.', 'better-wp-security' ); ?></p>
25
  <p><?php _e( 'Remember, some of these settings might conflict with other plugins or themes, so test your site after enabling each setting.', 'better-wp-security' ); ?></p>
26
  <table class="form-table">
27
  <tr>
@@ -29,7 +28,7 @@ final class ITSEC_Multisite_Tweaks_Settings_Page extends ITSEC_Module_Settings_P
29
  <td>
30
  <?php $form->add_checkbox( 'theme_updates' ); ?>
31
  <label for="itsec-multisite-tweaks-theme_updates"><?php _e( 'Hide Theme Update Notifications', 'better-wp-security' ); ?></label>
32
- <p class="description"><?php _e( 'Hides theme update notifications from users who cannot update themes. Please note that this only makes a difference in multi-site installations.', 'better-wp-security' ); ?></p>
33
  </td>
34
  </tr>
35
  <tr>
@@ -37,7 +36,7 @@ final class ITSEC_Multisite_Tweaks_Settings_Page extends ITSEC_Module_Settings_P
37
  <td>
38
  <?php $form->add_checkbox( 'plugin_updates' ); ?>
39
  <label for="itsec-multisite-tweaks-plugin_updates"><?php _e( 'Hide Plugin Update Notifications', 'better-wp-security' ); ?></label>
40
- <p class="description"><?php _e( 'Hides plugin update notifications from users who cannot update plugins. Please note that this only makes a difference in multi-site installations.', 'better-wp-security' ); ?></p>
41
  </td>
42
  </tr>
43
  <tr>
@@ -45,12 +44,12 @@ final class ITSEC_Multisite_Tweaks_Settings_Page extends ITSEC_Module_Settings_P
45
  <td>
46
  <?php $form->add_checkbox( 'core_updates' ); ?>
47
  <label for="itsec-multisite-tweaks-core_updates"><?php _e( 'Hide Core Update Notifications', 'better-wp-security' ); ?></label>
48
- <p class="description"><?php _e( 'Hides core update notifications from users who cannot update core. Please note that this only makes a difference in multi-site installations.', 'better-wp-security' ); ?></p>
49
  </td>
50
  </tr>
51
  </table>
52
  <?php
53
-
54
  }
55
  }
56
 
6
  $this->title = __( 'Multisite Tweaks', 'better-wp-security' );
7
  $this->description = __( 'Advanced settings that improve security by changing default WordPress Multisite behavior.', 'better-wp-security' );
8
  $this->type = 'recommended';
9
+
10
  parent::__construct();
11
  }
12
+
13
  protected function render_description( $form ) {
14
+
15
  ?>
16
  <p><?php _e( 'These are advanced settings that may be utilized to further strengthen the security of your WordPress site.', 'better-wp-security' ); ?></p>
17
  <?php
18
+
19
  }
20
+
21
  protected function render_settings( $form ) {
22
+
23
  ?>
 
24
  <p><?php _e( 'Remember, some of these settings might conflict with other plugins or themes, so test your site after enabling each setting.', 'better-wp-security' ); ?></p>
25
  <table class="form-table">
26
  <tr>
28
  <td>
29
  <?php $form->add_checkbox( 'theme_updates' ); ?>
30
  <label for="itsec-multisite-tweaks-theme_updates"><?php _e( 'Hide Theme Update Notifications', 'better-wp-security' ); ?></label>
31
+ <p class="description"><?php _e( 'Hides theme update notifications from users who cannot update themes.', 'better-wp-security' ); ?></p>
32
  </td>
33
  </tr>
34
  <tr>
36
  <td>
37
  <?php $form->add_checkbox( 'plugin_updates' ); ?>
38
  <label for="itsec-multisite-tweaks-plugin_updates"><?php _e( 'Hide Plugin Update Notifications', 'better-wp-security' ); ?></label>
39
+ <p class="description"><?php _e( 'Hides plugin update notifications from users who cannot update plugins.', 'better-wp-security' ); ?></p>
40
  </td>
41
  </tr>
42
  <tr>
44
  <td>
45
  <?php $form->add_checkbox( 'core_updates' ); ?>
46
  <label for="itsec-multisite-tweaks-core_updates"><?php _e( 'Hide Core Update Notifications', 'better-wp-security' ); ?></label>
47
+ <p class="description"><?php _e( 'Hides core update notifications from users who cannot update core.', 'better-wp-security' ); ?></p>
48
  </td>
49
  </tr>
50
  </table>
51
  <?php
52
+
53
  }
54
  }
55
 
core/modules/notification-center/class-notification-center.php CHANGED
@@ -728,6 +728,14 @@ final class ITSEC_Notification_Center {
728
  }
729
  }
730
 
 
 
 
 
 
 
 
 
731
  $ret = array();
732
 
733
  if ( $to_send ) {
728
  }
729
  }
730
 
731
+ /**
732
+ * Filters the list of scheduled notifications to send on this request.
733
+ *
734
+ * @param string[] $to_send Notification slugs to send.
735
+ * @param array $notifications List of available notifications that could be sent.
736
+ */
737
+ $to_send = apply_filters( 'itsec_notification_center_send_scheduled_notifications', $to_send, $notifications );
738
+
739
  $ret = array();
740
 
741
  if ( $to_send ) {
core/modules/notification-center/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Notification Center', 'better-wp-security' ),
5
+ ];
core/modules/notification-center/settings.php CHANGED
@@ -61,6 +61,7 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
61
  }
62
 
63
  protected function handle_settings_changes( $old_settings ) {
 
64
 
65
  $nc = ITSEC_Core::get_notification_center();
66
 
@@ -220,4 +221,4 @@ class ITSEC_Notification_Center_Settings extends ITSEC_Settings {
220
  }
221
  }
222
 
223
- ITSEC_Modules::register_settings( new ITSEC_Notification_Center_Settings() );
61
  }
62
 
63
  protected function handle_settings_changes( $old_settings ) {
64
+ parent::handle_settings_changes( $old_settings );
65
 
66
  $nc = ITSEC_Core::get_notification_center();
67
 
221
  }
222
  }
223
 
224
+ ITSEC_Modules::register_settings( new ITSEC_Notification_Center_Settings() );
core/modules/password-requirements/class-itsec-password-requirements.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Class ITSEC_Password_Requirements
5
  */
@@ -22,7 +24,8 @@ class ITSEC_Password_Requirements {
22
 
23
  add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
24
 
25
- add_action( 'wp_login', array( $this, 'flag_check' ), 9, 2 );
 
26
 
27
  add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
28
  }
@@ -102,6 +105,7 @@ class ITSEC_Password_Requirements {
102
  $args = array(
103
  'role' => $user->role,
104
  'canonical' => ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $user->role, $user ),
 
105
  );
106
 
107
  $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
@@ -243,7 +247,9 @@ class ITSEC_Password_Requirements {
243
  continue;
244
  }
245
 
246
- $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], array() );
 
 
247
 
248
  if ( true === $validated ) {
249
  continue;
@@ -333,7 +339,23 @@ class ITSEC_Password_Requirements {
333
  if ( $requirement['flag_check'] && call_user_func( $requirement['flag_check'], $user, $settings ) ) {
334
  ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
335
 
336
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  }
338
  }
339
  }
@@ -422,7 +444,7 @@ class ITSEC_Password_Requirements {
422
  <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
423
  <div class="pw-weak">
424
  <label>
425
- <input type="checkbox" name="pw_weak" class="pw-checkbox" />
426
  <?php _e( 'Confirm use of weak password' ); ?>
427
  </label>
428
  </div>
1
  <?php
2
 
3
+ use \iThemesSecurity\User_Groups;
4
+
5
  /**
6
  * Class ITSEC_Password_Requirements
7
  */
24
 
25
  add_action( 'itsec_validate_password', array( $this, 'validate_password' ), 10, 4 );
26
 
27
+ // This needs to run before the interstitial runs.
28
+ add_action( 'wp_login', array( $this, 'flag_check' ), - 2000, 2 );
29
 
30
  add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
31
  }
105
  $args = array(
106
  'role' => $user->role,
107
  'canonical' => ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $user->role, $user ),
108
+ 'target' => User_Groups\Match_Target::for_user( get_userdata( $user->ID ), $user->role )
109
  );
110
 
111
  $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], $args );
247
  continue;
248
  }
249
 
250
+ $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], array(
251
+ 'target' => User_Groups\Match_Target::for_user( $user ),
252
+ ) );
253
 
254
  if ( true === $validated ) {
255
  continue;
339
  if ( $requirement['flag_check'] && call_user_func( $requirement['flag_check'], $user, $settings ) ) {
340
  ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
341
 
342
+ continue;
343
+ }
344
+
345
+ if ( $requirement['validate'] ) {
346
+ $evaluation = get_user_meta( $user->ID, $requirement['meta'], true );
347
+
348
+ if ( '' !== $evaluation ) {
349
+ $validated = call_user_func( $requirement['validate'], $evaluation, $user, $settings[ $code ], array(
350
+ 'target' => User_Groups\Match_Target::for_user( $user ),
351
+ ) );
352
+
353
+ if ( true !== $validated ) {
354
+ ITSEC_Lib_Password_Requirements::flag_password_change_required( $user, $code );
355
+
356
+ continue;
357
+ }
358
+ }
359
  }
360
  }
361
  }
444
  <div id="pass-strength-result" class="hide-if-no-js" aria-live="polite"><?php _e( 'Strength indicator', 'better-wp-security' ); ?></div>
445
  <div class="pw-weak">
446
  <label>
447
+ <input type="checkbox" name="pw_weak" class="pw-checkbox"/>
448
  <?php _e( 'Confirm use of weak password' ); ?>
449
  </label>
450
  </div>
core/modules/password-requirements/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Password Requirements', 'better-wp-security' ),
5
+ ];
core/modules/password-requirements/settings.php CHANGED
@@ -46,6 +46,15 @@ class ITSEC_Password_Requirements_Settings extends ITSEC_Settings {
46
  $this->settings['requirement_settings'][ $code ] = wp_parse_args( $current, $requirement['defaults'] );
47
  }
48
  }
 
 
 
 
 
 
 
 
 
49
  }
50
 
51
- ITSEC_Modules::register_settings( new ITSEC_Password_Requirements_Settings() );
46
  $this->settings['requirement_settings'][ $code ] = wp_parse_args( $current, $requirement['defaults'] );
47
  }
48
  }
49
+
50
+ protected function handle_settings_changes( $old_settings ) {
51
+ parent::handle_settings_changes( $old_settings );
52
+
53
+ if ( $this->settings['enabled_requirements'] !== $old_settings['enabled_requirements'] ) {
54
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/user-groups', 'fetchGroupsSettings' );
55
+ ITSEC_Response::add_store_dispatch( 'ithemes-security/core', 'fetchIndex', [ true ] );
56
+ }
57
+ }
58
  }
59
 
60
+ ITSEC_Modules::register_settings( new ITSEC_Password_Requirements_Settings() );
core/modules/password-requirements/validator.php CHANGED
@@ -13,6 +13,7 @@ class ITSEC_Password_Requirements_Validator extends ITSEC_Validator {
13
  }
14
 
15
  protected function sanitize_settings() {
 
16
  $this->sanitize_setting( 'array', 'enabled_requirements', __( 'Enabled Requirements', 'better-wp-security' ) );
17
  $this->sanitize_setting( 'array', 'requirement_settings', __( 'Requirement Settings', 'better-wp-security' ) );
18
 
@@ -59,4 +60,4 @@ class ITSEC_Password_Requirements_Validator extends ITSEC_Validator {
59
  }
60
  }
61
 
62
- ITSEC_Modules::register_validator( new ITSEC_Password_Requirements_Validator() );
13
  }
14
 
15
  protected function sanitize_settings() {
16
+ $this->set_previous_if_empty( [ 'enabled_requirements', 'requirement_settings' ] );
17
  $this->sanitize_setting( 'array', 'enabled_requirements', __( 'Enabled Requirements', 'better-wp-security' ) );
18
  $this->sanitize_setting( 'array', 'requirement_settings', __( 'Requirement Settings', 'better-wp-security' ) );
19
 
60
  }
61
  }
62
 
63
+ ITSEC_Modules::register_validator( new ITSEC_Password_Requirements_Validator() );
core/modules/privacy/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Privacy', 'better-wp-security' ),
5
+ ];
core/modules/salts/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'WordPress Salts', 'better-wp-security' ),
5
+ ];
core/modules/salts/settings-page.php CHANGED
@@ -6,24 +6,43 @@ final class ITSEC_WordPress_Salts_Settings_Page extends ITSEC_Module_Settings_Pa
6
  $this->title = __( 'WordPress Salts', 'better-wp-security' );
7
  $this->description = __( 'Update the secret keys WordPress uses to increase the security of your site.', 'better-wp-security' );
8
  $this->type = 'recommended';
9
-
 
 
 
 
 
 
10
  parent::__construct();
11
  }
12
-
13
  protected function render_description( $form ) {
14
-
15
  ?>
16
- <p>A secret key makes your site harder to hack and access by adding random elements to the password.</p>
17
- <p>In simple terms, a secret key is a password with elements that make it harder to generate enough options to break through your security barriers. A password like "password" or "test" is simple and easily broken. A random, unpredictable password such as "88a7da62429ba6ad3cb3c76a09641fc" takes years to come up with the right combination. A salt is used to further enhance the security of the generated result.</p>
18
  <?php
19
-
20
  }
21
-
22
  protected function render_settings( $form ) {
23
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  ?>
25
  <div class="itsec-write-files-enabled">
26
- <p><strong>Note that changing the salts will log you out of your WordPress site.</strong></p>
27
  <table class="form-table itsec-settings-section">
28
  <tr>
29
  <th scope="row"><label for="itsec-wordpress-salts-regenerate"><?php _e( 'Change WordPress Salts', 'better-wp-security' ); ?></label></th>
@@ -39,7 +58,7 @@ final class ITSEC_WordPress_Salts_Settings_Page extends ITSEC_Module_Settings_Pa
39
  <div class="itsec-warning-message"><?php _e( 'The "Write to Files" setting is disabled in Global Settings. In order to use this feature, you must enable the "Write to Files" setting.', 'better-wp-security' ); ?></div>
40
  </div>
41
  <?php
42
-
43
  }
44
  }
45
 
6
  $this->title = __( 'WordPress Salts', 'better-wp-security' );
7
  $this->description = __( 'Update the secret keys WordPress uses to increase the security of your site.', 'better-wp-security' );
8
  $this->type = 'recommended';
9
+
10
+ require_once( __DIR__ . '/utilities.php' );
11
+
12
+ if ( ! ITSEC_WordPress_Salts_Utilities::check_valid_salts() ) {
13
+ $this->status = 'warning';
14
+ }
15
+
16
  parent::__construct();
17
  }
18
+
19
  protected function render_description( $form ) {
20
+
21
  ?>
22
+ <p><?php esc_html_e( 'A secret key makes your site harder to hack and access by adding random elements to the password.', 'better-wp-security' ) ?></p>
23
+ <p><?php esc_html_e( 'In simple terms, a secret key is a password with elements that make it harder to generate enough options to break through your security barriers. A password like "password" or "test" is simple and easily broken. A random, unpredictable password such as "88a7da62429ba6ad3cb3c76a09641fc" takes years to come up with the right combination. A salt is used to further enhance the security of the generated result.', 'better-wp-security' ); ?></p>
24
  <?php
 
25
  }
26
+
27
  protected function render_settings( $form ) {
28
+
29
+ if ( $this->status === 'warning' ) {
30
+ ?>
31
+ <div class="notice notice-warning notice-alt below-h2">
32
+ <p>
33
+ <?php printf(
34
+ esc_html__( 'Currently one or more of your salts is invalid. Please either use the tool below, or %1$smanually update your wp-config.php file%2$s.', 'better-wp-security' ),
35
+ '<a href="https://ithemes.com/easily-change-wordpress-security-keys-salts-ithemes-security-plugin/">',
36
+ '</a>'
37
+ ); ?>
38
+ </p>
39
+ </div>
40
+ <?php
41
+ }
42
+
43
  ?>
44
  <div class="itsec-write-files-enabled">
45
+ <p><strong><?php esc_html_e( 'Note that changing the salts will log you out of your WordPress site.', 'better-wp-security' ) ?></strong></p>
46
  <table class="form-table itsec-settings-section">
47
  <tr>
48
  <th scope="row"><label for="itsec-wordpress-salts-regenerate"><?php _e( 'Change WordPress Salts', 'better-wp-security' ); ?></label></th>
58
  <div class="itsec-warning-message"><?php _e( 'The "Write to Files" setting is disabled in Global Settings. In order to use this feature, you must enable the "Write to Files" setting.', 'better-wp-security' ); ?></div>
59
  </div>
60
  <?php
61
+
62
  }
63
  }
64
 
core/modules/salts/utilities.php CHANGED
@@ -1,40 +1,60 @@
1
  <?php
2
 
3
  final class ITSEC_WordPress_Salts_Utilities {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  public static function generate_new_salts() {
5
  if ( ! ITSEC_Modules::get_setting( 'global', 'write_files' ) ) {
6
  return new WP_Error( 'itsec-wordpress-salts-utilities-write-files-disabled', __( 'The "Write to Files" setting is disabled in Global Settings. In order to use this feature, you must enable the "Write to Files" setting.', 'better-wp-security' ) );
7
  }
8
-
9
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-config-file.php' );
10
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-file.php' );
11
-
12
  $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path();
13
  $config = ITSEC_Lib_File::read( $config_file_path );
14
-
15
  if ( is_wp_error( $config ) ) {
16
  return new WP_Error( 'itsec-wordpress-salts-utilities-cannot-read-wp-config.php', sprintf( __( 'Unable to read the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() ) );
17
  }
18
-
19
-
20
- $defines = array(
21
- 'AUTH_KEY',
22
- 'SECURE_AUTH_KEY',
23
- 'LOGGED_IN_KEY',
24
- 'NONCE_KEY',
25
- 'AUTH_SALT',
26
- 'SECURE_AUTH_SALT',
27
- 'LOGGED_IN_SALT',
28
- 'NONCE_SALT',
29
- );
30
-
31
- foreach ( $defines as $define ) {
32
  if ( empty( $salts ) ) {
33
  $salts = self::get_new_salts();
34
  }
35
-
36
  $salt = array_pop( $salts );
37
-
38
  if ( empty( $salt ) ) {
39
  $salt = wp_generate_password( 64, true, true );
40
  }
@@ -45,17 +65,17 @@ final class ITSEC_WordPress_Salts_Utilities {
45
  }
46
 
47
  $write_result = ITSEC_Lib_File::write( $config_file_path, $config );
48
-
49
  if ( is_wp_error( $write_result ) ) {
50
  return new WP_Error( 'itsec-wordpress-salts-utilities-cannot-save-wp-config.php', sprintf( __( 'Unable to update the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() ) );
51
  }
52
-
53
  return true;
54
  }
55
-
56
  public static function get_new_salts() {
57
  // From wp-admin/setup-config.php in WordPress 4.5.
58
-
59
  // Generate keys and salts using secure CSPRNG; fallback to API if enabled; further fallback to original wp_generate_password().
60
  try {
61
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|';
@@ -69,7 +89,7 @@ final class ITSEC_WordPress_Salts_Utilities {
69
  }
70
  } catch ( Exception $ex ) {
71
  $secret_keys = wp_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' );
72
-
73
  if ( is_wp_error( $secret_keys ) ) {
74
  $secret_keys = array();
75
  for ( $i = 0; $i < 8; $i++ ) {
@@ -82,7 +102,7 @@ final class ITSEC_WordPress_Salts_Utilities {
82
  }
83
  }
84
  }
85
-
86
  return $secret_keys;
87
  }
88
  }
1
  <?php
2
 
3
  final class ITSEC_WordPress_Salts_Utilities {
4
+ private static $defines = [
5
+ 'AUTH_KEY',
6
+ 'SECURE_AUTH_KEY',
7
+ 'LOGGED_IN_KEY',
8
+ 'NONCE_KEY',
9
+ 'AUTH_SALT',
10
+ 'SECURE_AUTH_SALT',
11
+ 'LOGGED_IN_SALT',
12
+ 'NONCE_SALT',
13
+ ];
14
+
15
+ /**
16
+ * Check that all the salts have valid values.
17
+ *
18
+ * @return bool
19
+ */
20
+ public static function check_valid_salts() {
21
+ foreach ( self::$defines as $define ) {
22
+ if ( ! defined( $define ) ) {
23
+ return false;
24
+ }
25
+
26
+ $value = constant( $define );
27
+
28
+ if ( ! $value || 'put your unique phrase here' === $value ) {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
  public static function generate_new_salts() {
37
  if ( ! ITSEC_Modules::get_setting( 'global', 'write_files' ) ) {
38
  return new WP_Error( 'itsec-wordpress-salts-utilities-write-files-disabled', __( 'The "Write to Files" setting is disabled in Global Settings. In order to use this feature, you must enable the "Write to Files" setting.', 'better-wp-security' ) );
39
  }
40
+
41
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-config-file.php' );
42
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-file.php' );
43
+
44
  $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path();
45
  $config = ITSEC_Lib_File::read( $config_file_path );
46
+
47
  if ( is_wp_error( $config ) ) {
48
  return new WP_Error( 'itsec-wordpress-salts-utilities-cannot-read-wp-config.php', sprintf( __( 'Unable to read the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() ) );
49
  }
50
+
51
+ foreach ( self::$defines as $define ) {
 
 
 
 
 
 
 
 
 
 
 
 
52
  if ( empty( $salts ) ) {
53
  $salts = self::get_new_salts();
54
  }
55
+
56
  $salt = array_pop( $salts );
57
+
58
  if ( empty( $salt ) ) {
59
  $salt = wp_generate_password( 64, true, true );
60
  }
65
  }
66
 
67
  $write_result = ITSEC_Lib_File::write( $config_file_path, $config );
68
+
69
  if ( is_wp_error( $write_result ) ) {
70
  return new WP_Error( 'itsec-wordpress-salts-utilities-cannot-save-wp-config.php', sprintf( __( 'Unable to update the <code>wp-config.php</code> file in order to update the salts. You will need to manually update the file. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() ) );
71
  }
72
+
73
  return true;
74
  }
75
+
76
  public static function get_new_salts() {
77
  // From wp-admin/setup-config.php in WordPress 4.5.
78
+
79
  // Generate keys and salts using secure CSPRNG; fallback to API if enabled; further fallback to original wp_generate_password().
80
  try {
81
  $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_ []{}<>~`+=,.;:/?|';
89
  }
90
  } catch ( Exception $ex ) {
91
  $secret_keys = wp_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' );
92
+
93
  if ( is_wp_error( $secret_keys ) ) {
94
  $secret_keys = array();
95
  for ( $i = 0; $i < 8; $i++ ) {
102
  }
103
  }
104
  }
105
+
106
  return $secret_keys;
107
  }
108
  }
core/modules/security-check-pro/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Security Check Pro', 'better-wp-security' ),
5
+ ];
core/modules/security-check-pro/utility.php CHANGED
@@ -129,6 +129,9 @@ final class ITSEC_Security_Check_Pro_Utility {
129
  public static function get_remote_ip_index() {
130
  $remote_ips = self::get_remote_ips();
131
 
 
 
 
132
 
133
  $standard_indexes = array(
134
  'REMOTE_ADDR',
@@ -288,6 +291,11 @@ final class ITSEC_Security_Check_Pro_Utility {
288
  return $key;
289
  }
290
 
 
 
 
 
 
291
  public static function get_remote_ips() {
292
  $remote_ips = apply_filters( 'itsec-security-check-pro-remote-ips', array() );
293
 
@@ -295,28 +303,28 @@ final class ITSEC_Security_Check_Pro_Utility {
295
  return $remote_ips;
296
  }
297
 
298
-
299
  $settings = ITSEC_Modules::get_settings( 'security-check-pro' );
300
 
301
  if ( $settings['remote_ips_timestamp'] + ( 5 * MINUTE_IN_SECONDS ) > time() && ! empty( $settings['remote_ips'] ) ) {
302
  return $settings['remote_ips'];
303
  }
304
 
305
-
306
  $response = wp_remote_get( self::$config_url );
307
 
308
  if ( is_wp_error( $response ) ) {
309
- return array();
310
  }
311
 
312
-
313
  $body = $response['body'];
314
  $data = json_decode( $body, true );
315
 
316
- if ( ! is_array( $data ) || ! isset( $data['ips'] ) || ! is_array( $data['ips'] ) ) {
317
- return array();
318
  }
319
 
 
 
 
320
 
321
  $settings['remote_ips_timestamp'] = time();
322
  $settings['remote_ips'] = $data['ips'];
129
  public static function get_remote_ip_index() {
130
  $remote_ips = self::get_remote_ips();
131
 
132
+ if ( is_wp_error( $remote_ips ) ) {
133
+ return false;
134
+ }
135
 
136
  $standard_indexes = array(
137
  'REMOTE_ADDR',
291
  return $key;
292
  }
293
 
294
+ /**
295
+ * Get the list of remote IPs that the SSL Proxy Detect server may be issuing requests from.
296
+ *
297
+ * @return string[]|WP_Error
298
+ */
299
  public static function get_remote_ips() {
300
  $remote_ips = apply_filters( 'itsec-security-check-pro-remote-ips', array() );
301
 
303
  return $remote_ips;
304
  }
305
 
 
306
  $settings = ITSEC_Modules::get_settings( 'security-check-pro' );
307
 
308
  if ( $settings['remote_ips_timestamp'] + ( 5 * MINUTE_IN_SECONDS ) > time() && ! empty( $settings['remote_ips'] ) ) {
309
  return $settings['remote_ips'];
310
  }
311
 
 
312
  $response = wp_remote_get( self::$config_url );
313
 
314
  if ( is_wp_error( $response ) ) {
315
+ return $response;
316
  }
317
 
 
318
  $body = $response['body'];
319
  $data = json_decode( $body, true );
320
 
321
+ if ( json_last_error() !== JSON_ERROR_NONE ) {
322
+ return new WP_Error( 'itsec_security_check_pro_invalid_json_response', json_last_error_msg() );
323
  }
324
 
325
+ if ( ! is_array( $data ) || ! isset( $data['ips'] ) || ! is_array( $data['ips'] ) ) {
326
+ return new WP_Error( 'itsec_security_check_pro_malformed_response', __( 'The response body is missing the "ips" entry.', 'better-wp-security' ) );
327
+ }
328
 
329
  $settings['remote_ips_timestamp'] = time();
330
  $settings['remote_ips'] = $data['ips'];
core/modules/security-check/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Security Check', 'better-wp-security' ),
5
+ ];
core/modules/security-check/scanner.php CHANGED
@@ -41,6 +41,8 @@ final class ITSEC_Security_Check_Scanner {
41
  }
42
 
43
  public static function run_scan() {
 
 
44
  require_once( dirname( __FILE__ ) . '/feedback.php' );
45
 
46
  self::$feedback = new ITSEC_Security_Check_Feedback();
@@ -84,7 +86,7 @@ final class ITSEC_Security_Check_Scanner {
84
  self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
85
  self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
86
  self::enforce_setting( 'two-factor', 'exclude_type', 'disabled', esc_html__( 'Changed the Disabled Force Two-Factor for Certain Users to "None".', 'better-wp-security' ) );
87
- self::enforce_setting( 'two-factor', 'protect_user_type', 'privileged_users', esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
88
  self::enforce_setting( 'two-factor', 'protect_vulnerable_users', true, esc_html__( 'Enabled the Vulnerable User Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
89
  self::enforce_setting( 'two-factor', 'protect_vulnerable_site', true, esc_html__( 'Enabled the Vulnerable Site Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
90
 
41
  }
42
 
43
  public static function run_scan() {
44
+ $admin_group = ( $group_id = ITSEC_Modules::get_settings_obj( 'user-groups' )->get_default_group_id( 'administrator' ) ) ? [ $group_id ] : [];
45
+
46
  require_once( dirname( __FILE__ ) . '/feedback.php' );
47
 
48
  self::$feedback = new ITSEC_Security_Check_Feedback();
86
  self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
87
  self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
88
  self::enforce_setting( 'two-factor', 'exclude_type', 'disabled', esc_html__( 'Changed the Disabled Force Two-Factor for Certain Users to "None".', 'better-wp-security' ) );
89
+ self::enforce_setting( 'two-factor', 'protect_user_group', $admin_group, esc_html__( 'Changed the User Type Protection setting in Two-Factor Authentication to "Privileged Users".', 'better-wp-security' ) );
90
  self::enforce_setting( 'two-factor', 'protect_vulnerable_users', true, esc_html__( 'Enabled the Vulnerable User Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
91
  self::enforce_setting( 'two-factor', 'protect_vulnerable_site', true, esc_html__( 'Enabled the Vulnerable Site Protection setting in Two-Factor Authentication.', 'better-wp-security' ) );
92
 
core/modules/ssl/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'SSL', 'better-wp-security' ),
5
+ ];
core/modules/strong-passwords/active.php CHANGED
@@ -1,3 +1,5 @@
1
  <?php
2
 
3
- require_once( 'class-itsec-strong-passwords.php' );
 
 
1
  <?php
2
 
3
+ require_once( __DIR__ . '/class-itsec-strong-passwords.php' );
4
+
5
+ return 'ITSEC_Strong_Passwords';
core/modules/strong-passwords/class-itsec-strong-passwords.php CHANGED
@@ -1,13 +1,27 @@
1
  <?php
2
 
3
- final class ITSEC_Strong_Passwords {
 
 
 
4
 
5
  const STRENGTH_KEY = 'itsec-password-strength';
6
 
7
- public function __construct() {
 
8
 
9
- add_action( 'itsec_register_password_requirements', array( $this, 'register_requirements' ) );
 
 
 
 
 
 
 
10
 
 
 
 
11
  add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
12
  add_action( 'resetpass_form', array( $this, 'add_scripts_to_wp_login' ) );
13
  add_action( 'itsec_password_requirements_change_form', array( $this, 'add_scripts_to_wp_login' ) );
@@ -23,18 +37,30 @@ final class ITSEC_Strong_Passwords {
23
  'reason' => array( $this, 'reason' ),
24
  'meta' => self::STRENGTH_KEY,
25
  'evaluate_if_not_enabled' => true,
26
- 'defaults' => array( 'role' => 'administrator' ),
 
 
27
  'settings_config' => array( $this, 'get_settings_config' ),
28
  ) );
29
  }
30
 
 
 
 
 
 
 
 
 
 
 
 
31
  /**
32
  * Enqueue script to hide the acknowledge weak password checkbox.
33
  *
34
  * @return void
35
  */
36
  public function add_scripts() {
37
-
38
  global $pagenow;
39
 
40
  if ( 'profile.php' !== $pagenow ) {
@@ -46,11 +72,8 @@ final class ITSEC_Strong_Passwords {
46
  }
47
 
48
  $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
49
- $role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
50
-
51
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
52
 
53
- if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role ) ) {
54
  wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
55
  }
56
  }
@@ -69,11 +92,8 @@ final class ITSEC_Strong_Passwords {
69
  }
70
 
71
  $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
72
- $role = isset( $settings['role'] ) ? $settings['role'] : 'administrator';
73
 
74
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
75
-
76
- if ( ITSEC_Lib_Canonical_Roles::is_user_at_least( $role, $user ) ) {
77
  wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
78
  }
79
  }
@@ -112,16 +132,17 @@ final class ITSEC_Strong_Passwords {
112
  * @return bool
113
  */
114
  public function validate( $strength, $user, $settings, $args ) {
115
-
116
  if ( (int) $strength === 4 ) {
117
  return true;
118
  }
119
 
120
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
 
 
121
 
122
- $role = isset( $args['canonical'] ) ? $args['canonical'] : ITSEC_Lib_Canonical_Roles::get_user_role( $user );
123
 
124
- if ( ! ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $settings['role'], $role ) ) {
125
  return true;
126
  }
127
 
@@ -143,22 +164,17 @@ final class ITSEC_Strong_Passwords {
143
  * @param ITSEC_Form $form
144
  */
145
  public function render_settings( $form ) {
146
-
147
- $href = 'http://codex.wordpress.org/Roles_and_Capabilities';
148
- $link = '<a href="' . $href . '" target="_blank" rel="noopener noreferrer">' . $href . '</a>';
149
  ?>
150
  <tr>
151
  <th scope="row">
152
- <label for="itsec-password-requirements-requirement_settings-strength-role">
153
- <?php esc_html_e( 'Minimum Role', 'better-wp-security' ); ?>
154
  </label>
155
  </th>
156
  <td>
157
- <?php $form->add_canonical_roles( 'role' ); ?>
158
  <br/>
159
- <label for="itsec-password-requirements-requirement_settings-strength-role"><?php _e( 'Minimum role at which a user must choose a strong password.', 'better-wp-security' ); ?></label>
160
- <p class="description"><?php printf( __( 'For more information on WordPress roles and capabilities please see %s.', 'better-wp-security' ), $link ); ?></p>
161
- <p class="warningtext description"><?php _e( 'Warning: If your site invites public registrations setting the role too low may annoy your members.', 'better-wp-security' ); ?></p>
162
  </td>
163
  </tr>
164
  <?php
@@ -173,8 +189,7 @@ final class ITSEC_Strong_Passwords {
173
  */
174
  public function sanitize_settings( $settings ) {
175
  return array(
176
- array( 'string', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
177
- array( 'canonical-roles', 'role', esc_html__( 'Minimum Role for Strong Passwords', 'better-wp-security' ) ),
178
  );
179
  }
180
 
@@ -215,5 +230,3 @@ final class ITSEC_Strong_Passwords {
215
  return $results->score;
216
  }
217
  }
218
-
219
- new ITSEC_Strong_Passwords();
1
  <?php
2
 
3
+ use iThemesSecurity\Contracts\Runnable;
4
+ use iThemesSecurity\User_Groups;
5
+
6
+ final class ITSEC_Strong_Passwords implements Runnable {
7
 
8
  const STRENGTH_KEY = 'itsec-password-strength';
9
 
10
+ /** @var User_Groups\Matcher */
11
+ private $matcher;
12
 
13
+ /**
14
+ * ITSEC_Strong_Passwords constructor.
15
+ *
16
+ * @param User_Groups\Matcher $matcher
17
+ */
18
+ public function __construct( User_Groups\Matcher $matcher ) {
19
+ $this->matcher = $matcher;
20
+ }
21
 
22
+ public function run() {
23
+ add_action( 'itsec_register_password_requirements', array( $this, 'register_requirements' ) );
24
+ add_action( 'itsec_register_user_group_settings', [ $this, 'register_group_setting' ] );
25
  add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts' ) );
26
  add_action( 'resetpass_form', array( $this, 'add_scripts_to_wp_login' ) );
27
  add_action( 'itsec_password_requirements_change_form', array( $this, 'add_scripts_to_wp_login' ) );
37
  'reason' => array( $this, 'reason' ),
38
  'meta' => self::STRENGTH_KEY,
39
  'evaluate_if_not_enabled' => true,
40
+ 'defaults' => array(
41
+ 'group' => ITSEC_Modules::get_settings_obj( 'user-groups' )->get_groups_for_all_users(),
42
+ ),
43
  'settings_config' => array( $this, 'get_settings_config' ),
44
  ) );
45
  }
46
 
47
+ public function register_group_setting( User_Groups\Settings_Registry $registry ) {
48
+ if ( ITSEC_Lib_Password_Requirements::is_requirement_enabled( 'strength' ) ) {
49
+ $registry->register( new User_Groups\Settings_Registration( 'password-requirements', 'requirement_settings.strength.group', User_Groups\Settings_Registration::T_MULTIPLE, static function () {
50
+ return [
51
+ 'title' => __( 'Require Strong Passwords', 'better-wp-security' ),
52
+ 'description' => __( 'Force users in the group to use strong passwords.', 'better-wp-security' ),
53
+ ];
54
+ } ) );
55
+ }
56
+ }
57
+
58
  /**
59
  * Enqueue script to hide the acknowledge weak password checkbox.
60
  *
61
  * @return void
62
  */
63
  public function add_scripts() {
 
64
  global $pagenow;
65
 
66
  if ( 'profile.php' !== $pagenow ) {
72
  }
73
 
74
  $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
 
 
 
75
 
76
+ if ( $this->matcher->matches( User_Groups\Match_Target::for_user( wp_get_current_user() ), $settings['group'] ) ) {
77
  wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
78
  }
79
  }
92
  }
93
 
94
  $settings = ITSEC_Lib_Password_Requirements::get_requirement_settings( 'strength' );
 
95
 
96
+ if ( $this->matcher->matches( User_Groups\Match_Target::for_user( $user ), $settings['group'] ) ) {
 
 
97
  wp_enqueue_script( 'itsec_strong_passwords', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery' ), ITSEC_Core::get_plugin_build() );
98
  }
99
  }
132
  * @return bool
133
  */
134
  public function validate( $strength, $user, $settings, $args ) {
 
135
  if ( (int) $strength === 4 ) {
136
  return true;
137
  }
138
 
139
+ if ( ! $user = get_userdata( $user->ID ) ) {
140
+ return true;
141
+ }
142
 
143
+ $target = isset( $args['target'] ) ? $args['target'] : User_Groups\Match_Target::for_user( $user );
144
 
145
+ if ( ! $this->matcher->matches( $target, $settings['group'] ) ) {
146
  return true;
147
  }
148
 
164
  * @param ITSEC_Form $form
165
  */
166
  public function render_settings( $form ) {
 
 
 
167
  ?>
168
  <tr>
169
  <th scope="row">
170
+ <label for="itsec-password-requirements-requirement_settings-strength-group">
171
+ <?php esc_html_e( 'User Group', 'better-wp-security' ); ?>
172
  </label>
173
  </th>
174
  <td>
175
+ <?php $form->add_user_groups( 'group', 'password-requirements', 'requirement_settings.strength.group' ); ?>
176
  <br/>
177
+ <label for="itsec-password-requirements-requirement_settings-strength-group"><?php _e( 'Force users in the selected groups to use strong passwords.', 'better-wp-security' ); ?></label>
 
 
178
  </td>
179
  </tr>
180
  <?php
189
  */
190
  public function sanitize_settings( $settings ) {
191
  return array(
192
+ array( 'user-groups', 'group', esc_html__( 'User Groups for Strong Passwords', 'better-wp-security' ) ),
 
193
  );
194
  }
195
 
230
  return $results->score;
231
  }
232
  }
 
 
core/modules/strong-passwords/container.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use iThemesSecurity\User_Groups\Matcher;
4
+
5
+ return static function ( \Pimple\Container $c ) {
6
+ $c['ITSEC_Strong_Passwords'] = static function ( \Pimple\Container $c ) {
7
+ return new ITSEC_Strong_Passwords( $c[ Matcher::class ] );
8
+ };
9
+ };
core/modules/strong-passwords/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Strong Passwords', 'better-wp-security' ),
5
+ ];
core/modules/strong-passwords/setup.php CHANGED
@@ -1,117 +1,94 @@
1
  <?php
2
 
3
- if ( ! class_exists( 'ITSEC_Strong_Passwords_Setup' ) ) {
4
 
5
- class ITSEC_Strong_Passwords_Setup {
6
 
7
- public function __construct() {
 
 
8
 
9
- add_action( 'itsec_modules_do_plugin_activation', array( $this, 'execute_activate' ) );
10
- add_action( 'itsec_modules_do_plugin_deactivation', array( $this, 'execute_deactivate' ) );
11
- add_action( 'itsec_modules_do_plugin_uninstall', array( $this, 'execute_uninstall' ) );
12
- add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), null, 2 );
 
 
 
 
13
 
14
- }
15
 
16
- /**
17
- * Execute module activation.
18
- *
19
- * @since 4.0
20
- *
21
- * @return void
22
- */
23
- public function execute_activate() {
24
- }
25
 
26
- /**
27
- * Execute module deactivation
28
- *
29
- * @return void
30
- */
31
- public function execute_deactivate() {
32
- }
33
 
34
- /**
35
- * Execute module uninstall
36
- *
37
- * @return void
38
- */
39
- public function execute_uninstall() {
40
 
41
- $this->execute_deactivate();
 
42
 
43
- delete_site_option( 'itsec_strong_passwords' );
 
44
 
45
  }
46
 
47
- /**
48
- * Execute module upgrade
49
- *
50
- * @return void
51
- */
52
- public function execute_upgrade( $itsec_old_version ) {
53
-
54
- if ( $itsec_old_version < 4000 ) {
55
 
56
- global $itsec_bwps_options;
57
-
58
- $current_options = get_site_option( 'itsec_strong_passwords' );
59
-
60
- // Don't do anything if settings haven't already been set, defaults exist in the module system and we prefer to use those
61
- if ( false !== $current_options ) {
62
-
63
- $current_options['enabled'] = isset( $itsec_bwps_options['st_enablepassword'] ) && $itsec_bwps_options['st_enablepassword'] == 1 ? true : false;
64
- $current_options['roll'] = isset( $itsec_bwps_options['st_passrole'] ) ? $itsec_bwps_options['st_passrole'] : 'administrator';
65
-
66
- update_site_option( 'itsec_strong_passwords', $current_options );
67
  }
68
 
69
- }
70
-
71
- if ( $itsec_old_version < 4041 ) {
72
- $current_options = get_site_option( 'itsec_strong_passwords' );
73
 
74
- // If there are no current options, go with the new defaults by not saving anything
75
- if ( is_array( $current_options ) ) {
76
- // Make sure the new module is properly activated or deactivated
77
- if ( $current_options['enabled'] ) {
78
- ITSEC_Modules::activate( 'strong-passwords' );
79
- } else {
80
- ITSEC_Modules::deactivate( 'strong-passwords' );
81
- }
82
 
83
- $settings = array( 'role' => $current_options['roll'] );
 
84
 
85
- ITSEC_Modules::set_settings( 'strong-passwords', $settings );
86
- }
 
 
87
  }
88
 
89
- if ( $itsec_old_version < 4096 ) {
90
- $active = get_site_option( 'itsec_active_modules', array() );
 
91
 
92
- if ( ! empty( $active['strong-passwords'] ) ) {
93
- $active_requirements = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
94
- $active_requirements['strength'] = true;
95
- ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active_requirements );
96
- }
97
 
98
- $requirement_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
99
- $requirement_settings['strength']['role'] = ITSEC_Modules::get_setting( 'strong-passwords', 'role', 'administrator' );
100
- ITSEC_Modules::set_setting( 'password-requirements', 'requirement_settings', $requirement_settings );
 
 
 
 
101
 
102
- unset( $active['strong-passwords'] );
 
 
103
 
104
- // Need to do this directly to be able to remove a module from the list entirely.
105
- if ( is_multisite() ) {
106
- update_site_option( 'itsec_active_modules', $active );
107
- } else {
108
- update_option( 'itsec_active_modules', $active );
109
- }
110
  }
111
- }
112
 
 
 
113
  }
114
-
115
  }
116
 
117
  new ITSEC_Strong_Passwords_Setup();
1
  <?php
2
 
3
+ use iThemesSecurity\User_Groups\Upgrader;
4
 
5
+ class ITSEC_Strong_Passwords_Setup {
6
 
7
+ public function __construct() {
8
+ add_action( 'itsec_modules_do_plugin_upgrade', array( $this, 'execute_upgrade' ), 0 );
9
+ }
10
 
11
+ /**
12
+ * Execute module upgrade
13
+ *
14
+ * @param int $itsec_old_version
15
+ *
16
+ * @return void
17
+ */
18
+ public function execute_upgrade( $itsec_old_version ) {
19
 
20
+ if ( $itsec_old_version < 4000 ) {
21
 
22
+ global $itsec_bwps_options;
 
 
 
 
 
 
 
 
23
 
24
+ $current_options = get_site_option( 'itsec_strong_passwords' );
 
 
 
 
 
 
25
 
26
+ // Don't do anything if settings haven't already been set, defaults exist in the module system and we prefer to use those
27
+ if ( false !== $current_options ) {
 
 
 
 
28
 
29
+ $current_options['enabled'] = isset( $itsec_bwps_options['st_enablepassword'] ) && $itsec_bwps_options['st_enablepassword'] == 1 ? true : false;
30
+ $current_options['roll'] = isset( $itsec_bwps_options['st_passrole'] ) ? $itsec_bwps_options['st_passrole'] : 'administrator';
31
 
32
+ update_site_option( 'itsec_strong_passwords', $current_options );
33
+ }
34
 
35
  }
36
 
37
+ if ( $itsec_old_version < 4041 ) {
38
+ $current_options = get_site_option( 'itsec_strong_passwords' );
 
 
 
 
 
 
39
 
40
+ // If there are no current options, go with the new defaults by not saving anything
41
+ if ( is_array( $current_options ) ) {
42
+ // Make sure the new module is properly activated or deactivated
43
+ if ( $current_options['enabled'] ) {
44
+ ITSEC_Modules::activate( 'strong-passwords' );
45
+ } else {
46
+ ITSEC_Modules::deactivate( 'strong-passwords' );
 
 
 
 
47
  }
48
 
49
+ $settings = array( 'role' => $current_options['roll'] );
 
 
 
50
 
51
+ ITSEC_Modules::set_settings( 'strong-passwords', $settings );
52
+ }
53
+ }
 
 
 
 
 
54
 
55
+ if ( $itsec_old_version < 4096 ) {
56
+ $active = get_site_option( 'itsec_active_modules', array() );
57
 
58
+ if ( ! empty( $active['strong-passwords'] ) ) {
59
+ $active_requirements = ITSEC_Modules::get_setting( 'password-requirements', 'enabled_requirements' );
60
+ $active_requirements['strength'] = true;
61
+ ITSEC_Modules::set_setting( 'password-requirements', 'enabled_requirements', $active_requirements );
62
  }
63
 
64
+ $requirement_settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
65
+ $requirement_settings['strength']['role'] = ITSEC_Modules::get_setting( 'strong-passwords', 'role', 'administrator' );
66
+ ITSEC_Modules::set_setting( 'password-requirements', 'requirement_settings', $requirement_settings );
67
 
68
+ unset( $active['strong-passwords'] );
 
 
 
 
69
 
70
+ // Need to do this directly to be able to remove a module from the list entirely.
71
+ if ( is_multisite() ) {
72
+ update_site_option( 'itsec_active_modules', $active );
73
+ } else {
74
+ update_option( 'itsec_active_modules', $active );
75
+ }
76
+ }
77
 
78
+ if ( $itsec_old_version < 4117 ) {
79
+ delete_site_option( 'itsec_strong_passwords' );
80
+ $settings = ITSEC_Modules::get_setting( 'password-requirements', 'requirement_settings' );
81
 
82
+ if ( isset( $settings['strength']['role'] ) ) {
83
+ $settings['strength']['group'] = ITSEC_Modules::get_container()
84
+ ->get( Upgrader::class )
85
+ ->upgrade_from_min_role( $settings['strength']['role'] );
86
+ unset( $settings['strength']['role'] );
 
87
  }
 
88
 
89
+ ITSEC_Modules::set_setting( 'password-requirements', 'requirement_settings', $settings );
90
+ }
91
  }
 
92
  }
93
 
94
  new ITSEC_Strong_Passwords_Setup();
core/modules/sync-connect/active.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once( __DIR__ . '/class-itsec-sync-connect.php' );
4
+
5
+ $sync_connect = new ITSEC_Sync_Connect();
6
+ $sync_connect->run();
core/modules/sync-connect/class-itsec-sync-connect-interstitial.php ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Sync_Connect_Interstitial extends ITSEC_Login_Interstitial {
4
+
5
+ /** @var string */
6
+ private $sync_dashboard = 'https://sync.ithemes.com/manage/site/';
7
+
8
+ /** @var ITSEC_Sync_Connect */
9
+ private $sync_connect;
10
+
11
+ /**
12
+ * ITSEC_Sync_Connect_Interstitial constructor.
13
+ *
14
+ * @param ITSEC_Sync_Connect $sync_connect
15
+ */
16
+ public function __construct( ITSEC_Sync_Connect $sync_connect ) {
17
+ $this->sync_connect = $sync_connect;
18
+ }
19
+
20
+ public function render( ITSEC_Login_Interstitial_Session $session, array $args ) {
21
+
22
+ $user = $session->get_user();
23
+
24
+ if ( $user && $this->sync_connect->user_can_install_and_activate( $user ) ) {
25
+ require_once( __DIR__ . '/templates/connect-prompt.php' );
26
+ } else {
27
+ require_once( __DIR__ . '/templates/connect-unauthorized.php' );
28
+ }
29
+
30
+ }
31
+
32
+ public function pre_render( ITSEC_Login_Interstitial_Session $session ) {
33
+ add_action( 'login_enqueue_scripts', static function () {
34
+ wp_enqueue_style( 'itsec-sync-connect', plugin_dir_url( __FILE__ ) . 'css/connect.css' );
35
+ } );
36
+ }
37
+
38
+ public function is_completion_forced( ITSEC_Login_Interstitial_Session $session ) {
39
+ return false;
40
+ }
41
+
42
+ public function has_submit() {
43
+ return true;
44
+ }
45
+
46
+ public function submit( ITSEC_Login_Interstitial_Session $session, array $post_data ) {
47
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
48
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
49
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
50
+ require_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
51
+
52
+ $user = $session->get_user();
53
+
54
+ if ( ! $user || ! $this->sync_connect->user_can_install_and_activate( $user ) ) {
55
+ return null;
56
+ }
57
+
58
+ // If the plugin is not installed, install it
59
+ if ( ! $this->sync_connect->is_plugin_installed() ) {
60
+ $install = $this->sync_connect->install_plugin();
61
+
62
+ // Could not install plugin, return error to manually install
63
+ if ( is_wp_error( $install ) ) {
64
+ return new WP_Error(
65
+ 'itsec-sync-connect-plugin-install-failure',
66
+ sprintf(
67
+ __( 'The plugin could not be installed. Please manually install the plugin. Error: %s', 'better-wp-security' ),
68
+ implode( ' ', $install->get_error_messages() )
69
+ )
70
+ );
71
+ }
72
+ }
73
+
74
+ // Activate the plugin
75
+ if ( ! is_plugin_active( ITSEC_Sync_Connect::PLUGIN_SLUG ) ) {
76
+ $activate = activate_plugin( ITSEC_Sync_Connect::PLUGIN_SLUG );
77
+
78
+ // Could not activate plugin, return error to activate or try reinstalling
79
+ if ( is_wp_error( $activate ) ) {
80
+ return new WP_Error(
81
+ 'itsec-sync-connect-plugin-activate-failure',
82
+ sprintf(
83
+ __( 'The plugin could not be activated. Please manually activate the plugin. Error: %s', 'better-wp-security' ),
84
+ implode( ' ', $activate->get_error_messages() )
85
+ )
86
+ );
87
+ }
88
+ }
89
+
90
+ $token = $session->get_meta( 'itsec_sync_connect_token' );
91
+
92
+ if ( ! $token && isset( $post_data['itsec_sync_connect_token'] ) ) {
93
+ $token = $post_data['itsec_sync_connect_token'];
94
+ }
95
+
96
+ // Link to iThemes account
97
+ if ( ! $token ) {
98
+ return new WP_Error( 'itsec-sync-connect-plugin-token-missing', __( 'Missing sync connect token.', 'better-wp-security' ) );
99
+ }
100
+
101
+ require_once( WP_PLUGIN_DIR . '/ithemes-sync/functions.php' );
102
+ $sync_nonce = Ithemes_Sync_Functions::generate_sync_nonce( 'itsec-connect' );
103
+
104
+ $response = $this->sync_connect->send_activation_request( $user->user_login, $token, $sync_nonce );
105
+
106
+ if ( is_wp_error( $response ) ) {
107
+ return $response;
108
+ }
109
+
110
+ require_once( WP_PLUGIN_DIR . '/ithemes-sync/settings.php' );
111
+ $GLOBALS['ithemes-sync-settings']->add_authentication( $response['site_id'], $response['username'], $response['key'], $user->user_login );
112
+
113
+ ITSEC_Log::add_notice( 'sync_connect', 'successful_authentication::' . $user->ID, compact( 'post_data' ), compact( 'site_id' ) );
114
+
115
+ wp_redirect( $this->sync_dashboard . $response['site_id'] );
116
+ exit;
117
+ }
118
+
119
+ /**
120
+ * Whether the sync interstitial should be shown to the given user.
121
+ *
122
+ * We only show this interstitial if it was requested by the user.
123
+ *
124
+ * @param WP_User $user
125
+ * @param bool $is_requested
126
+ *
127
+ * @return bool
128
+ */
129
+ public function show_to_user( WP_User $user, $is_requested ) {
130
+ return $is_requested;
131
+ }
132
+
133
+ public function get_priority() {
134
+ return PHP_INT_MAX;
135
+ }
136
+ }
core/modules/sync-connect/class-itsec-sync-connect.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Sync-Connection Execution
5
+ *
6
+ * Handles the Sync Connection Interstitial
7
+ *
8
+ */
9
+ class ITSEC_Sync_Connect {
10
+
11
+ /** @var string */
12
+ private $sync_api = 'https://sync.ithemes.com/plugin-api/authenticate-token';
13
+
14
+ const R_SYNC_TOKEN = 'itsec_sync_connect_token';
15
+ const PLUGIN_SLUG = 'ithemes-sync/init.php';
16
+
17
+ public function run() {
18
+ add_action( 'itsec_login_interstitial_init', array( $this, 'register_interstitial' ) );
19
+ add_action( 'login_form', array( $this, 'ferry_sync_connect_token' ) );
20
+ add_action( 'itsec_initialize_login_interstitial_session_from_global_state', array( $this, 'set_sync_token_meta' ) );
21
+ add_filter( 'itsec_rest_supports', array( $this, 'add_sync_connect_to_rest_supports' ) );
22
+ }
23
+
24
+ /**
25
+ * Register the sync connect interstitial.
26
+ *
27
+ * @param ITSEC_Lib_Login_Interstitial $lib
28
+ */
29
+ public function register_interstitial( $lib ) {
30
+ require_once( __DIR__ . '/class-itsec-sync-connect-interstitial.php' );
31
+
32
+ $lib->register( 'sync-connect', new ITSEC_Sync_Connect_Interstitial( $this ) );
33
+ }
34
+
35
+ /**
36
+ * Is the given plugin installed.
37
+ *
38
+ * @return bool
39
+ */
40
+ public function is_plugin_installed() {
41
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
42
+
43
+ $plugins = get_plugins();
44
+
45
+ return ! empty( $plugins[ self::PLUGIN_SLUG ] );
46
+ }
47
+
48
+ /**
49
+ * Install the plugin contained in a zip file.
50
+ *
51
+ * @return true|WP_Error
52
+ */
53
+ public function install_plugin() {
54
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
55
+ require_once( __DIR__ . '/includes/upgrader-skin.php' );
56
+
57
+ $skin = new ITSEC_Ithemes_Sync_Upgrader_Skin();
58
+ $upgrader = new Plugin_Upgrader( $skin );
59
+ $installed = $upgrader->install( 'https://downloads.wordpress.org/plugin/ithemes-sync.zip' );
60
+
61
+ if ( true !== $installed && ! is_wp_error( $installed ) ) {
62
+ $error = new WP_Error();
63
+
64
+ foreach ( $skin->errors as $additional ) {
65
+ if ( is_wp_error( $additional ) ) {
66
+ ITSEC_Lib::add_to_wp_error( $error, $additional );
67
+ } elseif ( is_string( $additional ) ) {
68
+ $error->add( '', $additional );
69
+ }
70
+ }
71
+
72
+ if ( ! $error->has_errors() ) {
73
+ $error->add( 'installation_failed_unknown_reason', __( 'Installation failed for an unknown reason.', 'better-wp-security' ) );
74
+ }
75
+
76
+ if ( $messages = $skin->get_upgrade_messages() ) {
77
+ $error->add( 'feedback', implode( ' ', $messages ) );
78
+ }
79
+
80
+ return $error;
81
+ }
82
+
83
+ return $installed;
84
+ }
85
+
86
+ /**
87
+ * Check if the user has permission to install and activate plugins.
88
+ *
89
+ * @param WP_User $user
90
+ *
91
+ * @return bool
92
+ */
93
+ public function user_can_install_and_activate( WP_User $user ) {
94
+ return $user->has_cap( 'install_plugins' ) && $user->has_cap( 'activate_plugins' ) && $user->has_cap( 'activate_plugin', self::PLUGIN_SLUG );
95
+ }
96
+
97
+ /**
98
+ * Send the activation request to iThemes Sync.
99
+ *
100
+ * @param string $username Username of the WordPress user installing Sync.
101
+ * @param string $token The secure token to pass back to iThemes Sync.
102
+ * @param string $sync_nonce Generated by sync plugin to verify connection from Sync dashboard.
103
+ *
104
+ * @return array|WP_Error
105
+ */
106
+ public function send_activation_request( $username, $token, $sync_nonce ) {
107
+
108
+ $data = array(
109
+ 'site' => get_home_url(),
110
+ 'u' => $username,
111
+ 'token' => $token,
112
+ 'wp' => get_bloginfo( 'version' ),
113
+ 'nonce' => $sync_nonce
114
+ );
115
+
116
+ $remote_post_args = array(
117
+ 'method' => 'POST',
118
+ 'timeout' => 30,
119
+ 'body' => $data,
120
+ 'data_format' => 'body'
121
+ );
122
+
123
+ $request = wp_remote_post( $this->sync_api, $remote_post_args );
124
+
125
+ if ( is_wp_error( $request ) ) {
126
+ if ( 'connect() timed out!' === $request->get_error_message() ) {
127
+ return new WP_Error( 'http_request_failed', __( 'The server was unable to be contacted.', 'better-wp-security' ) );
128
+ }
129
+
130
+ return $request;
131
+ }
132
+
133
+ if ( $request['response']['code'] !== 200 ) {
134
+ return new WP_Error( 'itsec-sync-connect-invalid-response', sprintf( __( 'Invalid response from the server (Code: %d). Please manually activate the plugin.', 'better-wp-security' ), $request['response']['code'] ) );
135
+ }
136
+
137
+ $response_body = json_decode( wp_remote_retrieve_body( $request ), true );
138
+
139
+ if ( ! $response_body ) {
140
+ return new WP_Error( 'itsec-sync-connect-invalid-json', __( 'Invalid JSON response from Sync API. Please manually activate the plugin.', 'better-wp-security' ) );
141
+ }
142
+
143
+ if ( ! $response_body['success'] ) {
144
+ return new WP_Error( 'itsec-sync-connect-invalid-token', 'Sync user or connection token could not be validated.' );
145
+ }
146
+
147
+ return $response_body;
148
+ }
149
+
150
+ /**
151
+ * Ferry the sync connection token into the form
152
+ *
153
+ * @internal
154
+ */
155
+ public function ferry_sync_connect_token() {
156
+ if ( ! empty( $_REQUEST[ self::R_SYNC_TOKEN ] ) ) {
157
+ echo '<input type="hidden" name="' . esc_attr( self::R_SYNC_TOKEN ) . '" value="' . esc_attr( $_REQUEST[ self::R_SYNC_TOKEN ] ) . '">';
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Saves the sync token as meta for the interstitial.
163
+ *
164
+ * When Sync redirects the user to the login page, capture the secure sync token provided in the URL.
165
+ *
166
+ * @param ITSEC_Login_Interstitial_Session $session
167
+ */
168
+ public function set_sync_token_meta( ITSEC_Login_Interstitial_Session $session ) {
169
+ if ( ! empty( $_REQUEST[ self::R_SYNC_TOKEN ] ) ) {
170
+ $session->set_meta( self::R_SYNC_TOKEN, $_REQUEST[ self::R_SYNC_TOKEN ] );
171
+
172
+ if ( ! in_array( 'sync-connect', $session->get_show_after() ) ) {
173
+ $session->add_show_after( 'sync-connect' );
174
+ }
175
+ }
176
+ }
177
+
178
+ public function add_sync_connect_to_rest_supports( $supported ) {
179
+ $supported[] = 'sync-connect';
180
+
181
+ return $supported;
182
+ }
183
+ }
core/modules/sync-connect/css/connect.css ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-sync-connect__logo {
2
+ margin: 0 auto 20px auto;
3
+ display: block;
4
+ width: 100%;
5
+ }
6
+
7
+ .itsec-sync-connect__title,
8
+ .itsec-sync-connect__description,
9
+ .itsec-sync-connect__link-wrap,
10
+ .itsec-sync-connect-fallback__link-wrap {
11
+ text-align: center;
12
+ }
13
+
14
+ .itsec-sync-connect__title {
15
+ font-weight: bold;
16
+ margin-bottom: 10px !important;
17
+ }
18
+
19
+ .itsec-sync-connect__description {
20
+ margin-bottom: 10px !important;
21
+ }
22
+
23
+ .itsec-sync-connect__link {
24
+ margin: 20px 0 !important;
25
+ }
26
+
27
+ .itsec-sync-connect__link {
28
+ vertical-align: top;
29
+ display: block;
30
+ text-decoration: none;
31
+ height: 28px;
32
+ margin: 0;
33
+ cursor: pointer;
34
+ -webkit-appearance: none;
35
+ border-radius: 3px;
36
+ white-space: nowrap;
37
+ box-sizing: border-box;
38
+ background: #0084CB;
39
+ color: #fff;
40
+ text-shadow: none;
41
+ padding: 20px 30px;
42
+ line-height: 0;
43
+ box-shadow: none;
44
+ font-weight: 300;
45
+ font-size: 1.2em;
46
+ border: none;
47
+ width: 100%;
48
+ }
49
+
50
+ .itsec-sync-connect__link::-moz-focus-inner {
51
+ border-width: 0;
52
+ border-style: none;
53
+ padding: 0;
54
+ }
55
+
56
+ .itsec-sync-connect__link:hover {
57
+ background: #006799;
58
+ color: #fff;
59
+ }
60
+
61
+ .itsec-sync-connect-fallback__or {
62
+ width: 100%;
63
+ margin-bottom: 10px;
64
+ position: relative;
65
+ text-align: center;
66
+ }
67
+
68
+ .itsec-sync-connect-fallback__or:before {
69
+ background: #E5E5E5;
70
+ content: '';
71
+ height: 1px;
72
+ position: absolute;
73
+ left: 0;
74
+ top: 50%;
75
+ width: 100%;
76
+ }
77
+
78
+ .itsec-sync-connect-fallback__or span {
79
+ background: #fff;
80
+ color: #777;
81
+ position: relative;
82
+ padding: 0 8px;
83
+ text-transform: uppercase
84
+ }
core/modules/sync-connect/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/sync-connect/img/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/sync-connect/img/sync-logo.svg ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 15.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
5
+ y="0px"
6
+ width="282.653px" height="102.378px" viewBox="0 0 282.653 102.378" enable-background="new 0 0 282.653 102.378"
7
+ xml:space="preserve">
8
+ <g>
9
+ <g>
10
+ <path fill="#0084CB" d="M159.311,68.345c0,4.953-1.641,8.755-4.92,11.403c-3.281,2.648-7.847,3.974-13.697,3.974
11
+ c-2.819,0-5.276-0.2-7.373-0.593c-2.093-0.397-4.051-1.053-5.87-1.979v-9.803c4.138,2.373,8.341,3.559,12.611,3.559
12
+ c2.106,0,3.833-0.429,5.177-1.285c1.343-0.855,2.015-2.021,2.015-3.498c0-1.133-0.488-2.146-1.462-3.044
13
+ c-0.975-0.894-2.701-2.027-5.179-3.399c-3.186-1.79-5.415-3.576-6.678-5.355c-1.266-1.777-1.897-3.88-1.897-6.305
14
+ c0-4.481,1.457-7.952,4.367-10.416c2.911-2.461,7.07-3.694,12.47-3.694c5.299,0,10.079,1.251,14.35,3.755l-3.914,8.498
15
+ c-3.69-2.213-7.088-3.32-10.198-3.32c-1.502,0-2.714,0.336-3.636,1.008c-0.924,0.672-1.383,1.575-1.383,2.708
16
+ c0,1.026,0.42,1.929,1.265,2.707c0.843,0.777,2.425,1.758,4.742,2.943c3.241,1.661,5.587,3.466,7.036,5.415
17
+ C158.585,63.575,159.311,65.813,159.311,68.345z"/>
18
+ <path fill="#0084CB" d="M165.971,38.74h11.661l2.212,20.475c0.371,3.215,0.554,7.074,0.554,11.58h0.237
19
+ c0.529-1.345,1.108-2.919,1.739-4.722c0.635-1.807,1.488-3.83,2.569-6.068l10.277-21.265h12.925l-26.877,50.515
20
+ c-4.663,8.748-11.027,13.123-19.093,13.123c-2.371,0-4.307-0.253-5.81-0.752V92.14c1.791,0.342,3.321,0.512,4.585,0.512
21
+ c2.213,0,4.155-0.631,5.829-1.896c1.675-1.264,3.223-3.227,4.646-5.889l1.027-1.938L165.971,38.74z"/>
22
+ <path fill="#0084CB" d="M238.525,82.929h-11.896l5.414-25.81c0.421-1.791,0.632-3.36,0.632-4.703c0-3.242-1.423-4.863-4.268-4.863
23
+ c-2.425,0-4.625,1.502-6.601,4.506c-1.978,3.005-3.533,7.193-4.664,12.569l-3.876,18.3h-11.896l9.367-44.189h9.091L219,46.922
24
+ h0.236c3.849-6.009,8.523-9.013,14.032-9.013c3.636,0,6.449,1.101,8.438,3.301c1.99,2.201,2.984,5.341,2.984,9.425
25
+ c0,1.927-0.304,4.298-0.91,7.116L238.525,82.929z"/>
26
+ <path fill="#0084CB" d="M262.73,83.722c-5.295,0-9.362-1.42-12.193-4.251c-2.832-2.833-4.248-6.831-4.248-11.997
27
+ c0-5.584,0.982-10.663,2.942-15.236c1.964-4.572,4.726-8.104,8.282-10.593c3.559-2.49,7.614-3.736,12.174-3.736
28
+ c4.797,0,9.116,0.95,12.966,2.846l-3.638,9.051c-1.423-0.604-2.82-1.131-4.189-1.58c-1.369-0.447-2.925-0.672-4.665-0.672
29
+ c-2.238,0-4.261,0.844-6.066,2.531c-1.805,1.686-3.216,3.999-4.229,6.936c-1.014,2.938-1.521,6.093-1.521,9.466
30
+ c0,2.531,0.599,4.436,1.799,5.711c1.199,1.279,2.865,1.919,5,1.919c2,0,3.86-0.311,5.573-0.931
31
+ c1.715-0.617,3.479-1.389,5.295-2.312v9.725C272.007,82.679,267.579,83.722,262.73,83.722z"/>
32
+ </g>
33
+ <g>
34
+ <path fill="#8CC63F" d="M140.895,31.638h-3.789l2.984-14.076h3.79L140.895,31.638z M140.694,14.264
35
+ c0-0.729,0.199-1.281,0.597-1.656c0.398-0.373,0.963-0.559,1.695-0.559c0.611,0,1.077,0.129,1.397,0.39
36
+ c0.318,0.259,0.477,0.633,0.477,1.12c0,0.673-0.185,1.215-0.554,1.63c-0.37,0.416-0.939,0.623-1.712,0.623
37
+ C141.328,15.812,140.694,15.296,140.694,14.264z"/>
38
+ <path fill="#8CC63F" d="M150.766,28.831c0.546,0,1.225-0.146,2.039-0.442v2.833c-0.933,0.444-2.048,0.667-3.349,0.667
39
+ c-1.259,0-2.185-0.264-2.776-0.792c-0.591-0.53-0.888-1.348-0.888-2.457c0-0.419,0.051-0.89,0.15-1.41l1.449-6.787h-1.914
40
+ l0.365-1.85l2.468-1.057l1.662-2.973h2.442l-0.617,2.997h3.563l-0.63,2.882h-3.551l-1.449,6.787
41
+ c-0.049,0.251-0.075,0.474-0.075,0.667C149.656,28.519,150.026,28.831,150.766,28.831z"/>
42
+ <path fill="#8CC63F" d="M166.108,31.638h-3.791l1.726-8.222c0.134-0.569,0.202-1.07,0.202-1.497c0-1.034-0.453-1.549-1.359-1.549
43
+ c-0.773,0-1.475,0.479-2.103,1.436c-0.63,0.956-1.125,2.291-1.486,4.003l-1.233,5.829h-3.79l4.154-19.589h3.789
44
+ c-0.327,1.519-0.577,2.686-0.754,3.499c-0.177,0.815-0.537,2.111-1.084,3.892h0.102c0.521-0.647,1.1-1.165,1.736-1.555
45
+ c0.639-0.392,1.378-0.586,2.217-0.586c1.158,0,2.054,0.35,2.688,1.05c0.634,0.704,0.95,1.704,0.95,3.003
46
+ c0,0.612-0.097,1.37-0.289,2.267L166.108,31.638z"/>
47
+ <path fill="#8CC63F" d="M174.874,31.89c-1.763,0-3.131-0.474-4.104-1.421c-0.975-0.95-1.46-2.289-1.46-4.018
48
+ c0-1.737,0.346-3.321,1.038-4.752c0.693-1.432,1.63-2.521,2.812-3.273c1.185-0.751,2.523-1.127,4.017-1.127
49
+ c1.486,0,2.645,0.341,3.475,1.026c0.833,0.684,1.248,1.623,1.248,2.815c0,1.568-0.701,2.779-2.103,3.63
50
+ c-1.402,0.854-3.404,1.279-6.005,1.279h-0.643l-0.026,0.264v0.252c0,0.766,0.217,1.366,0.649,1.807
51
+ c0.432,0.442,1.051,0.66,1.855,0.66c0.731,0,1.394-0.08,1.989-0.238c0.598-0.16,1.318-0.44,2.166-0.842v2.856
52
+ C178.341,31.528,176.703,31.89,174.874,31.89z M176.938,20.03c-0.737,0-1.436,0.336-2.09,1.007
53
+ c-0.655,0.671-1.083,1.49-1.284,2.456h0.566c1.302,0,2.313-0.205,3.039-0.611c0.727-0.407,1.09-0.959,1.09-1.656
54
+ C178.26,20.429,177.819,20.03,176.938,20.03z"/>
55
+ <path fill="#8CC63F" d="M191.116,17.298c1.837,0,2.938,0.958,3.298,2.87h0.076c0.571-0.922,1.243-1.632,2.021-2.127
56
+ c0.775-0.496,1.605-0.743,2.486-0.743c1.142,0,2.012,0.357,2.613,1.071c0.598,0.713,0.9,1.708,0.9,2.982
57
+ c0,0.639-0.097,1.395-0.29,2.267l-1.675,8.02h-3.79l1.737-8.222c0.135-0.569,0.202-1.07,0.202-1.497
58
+ c0-1.034-0.411-1.549-1.234-1.549c-0.772,0-1.472,0.47-2.095,1.41c-0.627,0.941-1.124,2.274-1.494,4.004l-1.207,5.854h-3.791
59
+ l1.726-8.222c0.135-0.569,0.201-1.07,0.201-1.497c0-1.034-0.41-1.549-1.233-1.549c-0.771,0-1.473,0.479-2.103,1.436
60
+ c-0.629,0.956-1.126,2.291-1.485,4.003l-1.233,5.829h-3.79l2.984-14.076h2.896l-0.265,2.606h0.075
61
+ C187.872,18.256,189.361,17.298,191.116,17.298z"/>
62
+ <path fill="#8CC63F" d="M208.584,31.89c-1.764,0-3.131-0.474-4.104-1.421c-0.975-0.95-1.461-2.289-1.461-4.018
63
+ c0-1.737,0.346-3.321,1.039-4.752c0.691-1.432,1.629-2.521,2.814-3.273c1.182-0.751,2.521-1.127,4.015-1.127
64
+ c1.486,0,2.645,0.341,3.475,1.026c0.831,0.684,1.247,1.623,1.247,2.815c0,1.568-0.7,2.779-2.104,3.63
65
+ c-1.401,0.854-3.402,1.279-6.005,1.279h-0.642l-0.025,0.264v0.252c0,0.766,0.216,1.366,0.648,1.807
66
+ c0.432,0.442,1.052,0.66,1.856,0.66c0.731,0,1.395-0.08,1.99-0.238c0.597-0.16,1.316-0.44,2.164-0.842v2.856
67
+ C212.05,31.528,210.412,31.89,208.584,31.89z M210.648,20.03c-0.738,0-1.436,0.336-2.092,1.007
68
+ c-0.653,0.671-1.081,1.49-1.283,2.456h0.566c1.301,0,2.316-0.205,3.042-0.611c0.725-0.407,1.088-0.959,1.088-1.656
69
+ C211.97,20.429,211.529,20.03,210.648,20.03z"/>
70
+ <path fill="#8CC63F" d="M225.398,26.993c0,1.577-0.523,2.787-1.568,3.631c-1.044,0.845-2.498,1.265-4.362,1.265
71
+ c-0.897,0-1.681-0.063-2.348-0.189c-0.667-0.125-1.29-0.335-1.87-0.63v-3.12c1.318,0.755,2.657,1.132,4.018,1.132
72
+ c0.671,0,1.221-0.136,1.647-0.409c0.429-0.273,0.644-0.645,0.644-1.115c0-0.361-0.156-0.684-0.467-0.97
73
+ c-0.31-0.285-0.86-0.646-1.648-1.081c-1.016-0.571-1.725-1.141-2.129-1.706c-0.402-0.567-0.604-1.237-0.604-2.009
74
+ c0-1.427,0.466-2.533,1.393-3.318c0.928-0.784,2.252-1.176,3.972-1.176c1.688,0,3.211,0.399,4.571,1.197l-1.247,2.706
75
+ c-1.176-0.706-2.259-1.058-3.248-1.058c-0.479,0-0.865,0.107-1.159,0.322c-0.294,0.215-0.44,0.501-0.44,0.862
76
+ c0,0.327,0.135,0.616,0.403,0.862c0.269,0.248,0.772,0.561,1.511,0.938c1.032,0.528,1.779,1.104,2.24,1.726
77
+ C225.167,25.473,225.398,26.187,225.398,26.993z"/>
78
+ </g>
79
+ </g>
80
+ <path fill="#8CC63F" d="M115.257,20.541L97.52,24.091C89.779,9.219,74.633,0,57.876,0c-2.739,0-5.503,0.256-8.214,0.759
81
+ c-2.579,0.479-5.146,1.196-7.63,2.128C21.849,10.474,9.955,31.32,13.66,52.533L0,69.843l18.391-3.68
82
+ c7.827,14.521,22.855,23.523,39.338,23.523l0,0c2.739,0,5.503-0.255,8.215-0.76c2.58-0.479,5.147-1.195,7.631-2.127
83
+ c20.081-7.549,31.989-28.306,28.404-49.433L115.257,20.541z M4.73,66.942L14.99,53.94l0.721-0.912l-0.207-1.109
84
+ c-3.381-20.175,8.059-40.042,27.202-47.237c2.373-0.892,4.831-1.578,7.306-2.038c2.597-0.482,5.243-0.727,7.866-0.727
85
+ c16.15,0,31.213,9.381,38.374,23.898l0.217,0.44l14.058-2.813L99.938,36.859l0.127,0.688c3.505,20.264-7.919,40.223-27.167,47.458
86
+ c-2.379,0.895-4.837,1.58-7.307,2.038c-2.596,0.482-5.241,0.728-7.863,0.728c-15.864,0-30.347-8.732-37.776-22.744l-0.248-0.567
87
+ l-0.258-0.463L4.73,66.942z"/>
88
+ <path fill="#0084CB" d="M57.859,78.396c-11.458,0-21.991-5.525-28.174-14.781l-0.107-0.215l-0.58-1.648l25.604-5.125
89
+ c-6.569-3.504-21.57-11.568-22.904-12.852c-2.959-2.846-3.445-4.716-3.959-6.696l-0.036-0.138c-0.476-1.827,0.097-5.077,0.831-7.324
90
+ c1.826-5.278,5.44-11.891,17.326-16.359c3.824-1.438,7.816-2.167,11.866-2.167c11.407,0,22.302,5.963,28.434,15.562l0.077,0.147
91
+ l0.699,1.679l-26.482,5.301c5.007,2.769,16.539,9.151,18.273,10.173c4.735,2.793,7.432,5.83,8.242,9.282
92
+ c0.91,3.874,0.118,6.825-0.673,8.963c-0.871,2.643-7.35,11.087-16.718,14.252C65.761,77.741,61.817,78.396,57.859,78.396z
93
+ M31.701,63.164c5.876,8.352,15.598,13.315,26.158,13.315c3.75,0,7.486-0.62,11.107-1.844c8.823-2.98,14.823-10.946,15.522-13.069
94
+ c0.709-1.918,1.409-4.516,0.616-7.892c-0.675-2.876-3.148-5.592-7.35-8.07C75.6,44.334,57.422,34.294,57.239,34.193l-2.291-1.265
95
+ l29.203-5.845c-5.848-8.701-15.905-14.076-26.424-14.076c-3.818,0-7.584,0.688-11.192,2.044
96
+ C35.46,19.214,32.139,25.059,30.35,30.228c-0.75,2.3-1.101,5.044-0.792,6.23l0.036,0.139c0.482,1.858,0.863,3.325,3.433,5.796
97
+ c1.06,1.01,15.8,9,24.842,13.8l2.372,1.259L31.701,63.164z"/>
98
+ </svg>
core/modules/sync-connect/includes/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/sync-connect/includes/upgrader-skin.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Ithemes_Sync_Upgrader_Skin extends Automatic_Upgrader_Skin {
4
+ public $errors = [];
5
+
6
+ public function error( $errors ) {
7
+ $this->errors[] = $errors;
8
+ }
9
+
10
+ public function request_filesystem_credentials( $error = false, $context = '', $allow_relaxed_file_ownership = false ) {
11
+ if ( ! function_exists( 'submit_button' ) ) {
12
+ require_once( ABSPATH . 'wp-admin/includes/template.php' );
13
+ }
14
+
15
+ $r = parent::request_filesystem_credentials( $error, $context, $allow_relaxed_file_ownership );
16
+
17
+ if ( false === $r ) {
18
+ $this->error( __( 'Could not request filesystem credentials.', 'better-wp-security' ) );
19
+ }
20
+
21
+ return $r;
22
+ }
23
+ }
core/modules/sync-connect/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/sync-connect/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'Sync Connect', 'better-wp-security' ),
5
+ ];
core/modules/sync-connect/templates/connect-prompt.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <div class="itsec-sync-connect-wrap">
2
+ <img class="itsec-sync-connect__logo" height="116" src="<?php echo esc_url( plugin_dir_url( __DIR__ ) . 'img/sync-logo.svg' ); ?>" alt="">
3
+ <p class="itsec-sync-connect__title"><?php esc_html_e( 'Connecting iThemes Sync', 'better-wp-security' ); ?></p>
4
+ <p class="itsec-sync-connect__description"><?php esc_html_e( 'This will install the iThemes Sync plugin and connect the plugin to your Sync account.', 'better-wp-security' ); ?></p>
5
+
6
+ <?php require( __DIR__ . '/prompt-link.php' ); ?>
7
+
8
+ <?php require( __DIR__ . '/fallback.php' ); ?>
9
+ </div>
core/modules/sync-connect/templates/connect-unauthorized.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="itsec-sync-connect-wrap">
2
+ <img class="itsec-sync-connect__logo" height="116" src="<?php echo esc_url( plugin_dir_url( __DIR__ ) . 'img/sync-logo.svg' ); ?>" alt="">
3
+ <p class="itsec-sync-connect__title"><?php esc_html_e( 'Account is not authorized', 'better-wp-security' ); ?></p>
4
+ <p class="itsec-sync-connect__description"><?php esc_html_e( 'Your user account does not have the ability to install or activate plugins. Please ask your site administrator to install Sync.', 'better-wp-security' ); ?></p>
5
+
6
+ <p class="itsec-sync-connect__link-wrap">
7
+ <a class="itsec-sync-connect__link" href="https://sync.ithemes.com">
8
+ <?php esc_html_e( 'Take me back to Sync', 'better-wp-security' ); ?>
9
+ </a>
10
+ </p>
11
+ </div>
core/modules/sync-connect/templates/fallback.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="itsec-sync-connect-fallback">
2
+ <?php require( __DIR__ . '/or.php' ); ?>
3
+
4
+ <p class="itsec-sync-connect-fallback__link-wrap itsec-sync-connect-fallback__link-wrap--type-wp">
5
+ <a class="itsec-sync-connect-fallback__link"
6
+ href="https://sync.ithemes.com">
7
+ <?php esc_html_e( 'Take me back to Sync', 'better-wp-security' ); ?>
8
+ </a>
9
+ </p>
10
+
11
+ </div>
core/modules/sync-connect/templates/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/sync-connect/templates/or.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <div class="itsec-sync-connect-fallback__or">
2
+ <span><?php esc_html_e( 'Or', 'better-wp-security' ) ?></span>
3
+ </div>
core/modules/sync-connect/templates/prompt-link.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <p class="itsec-sync-connect__link-wrap">
2
+ <input type="submit" class="itsec-sync-connect__link" value="<?php esc_html_e( 'Connect Sync', 'better-wp-security' ) ?>">
3
+ </p>
4
+
5
+ <?php
6
+ if ( isset( $_REQUEST['itsec_sync_connect_token'] ) ) {
7
+ echo '<input type="hidden" name="itsec_sync_connect_token" value="' . esc_attr( $_REQUEST['itsec_sync_connect_token'] ) . '">';
8
+ }
9
+ ?>
core/modules/system-tweaks/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'System Tweaks', 'better-wp-security' ),
5
+ ];
core/modules/user-groups/All_Users.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ final class All_Users implements Matchable {
6
+ public function get_id() {
7
+ return ':all';
8
+ }
9
+
10
+ public function get_label() {
11
+ return __( 'All Users', 'better-wp-security' );
12
+ }
13
+
14
+ public function matches( Match_Target $target ) {
15
+ return true;
16
+ }
17
+ }
core/modules/user-groups/Everybody_Else.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use iThemesSecurity\User_Groups\Repository\Repository;
6
+
7
+ final class Everybody_Else implements Matchable {
8
+
9
+ const ID = 'everybody-else';
10
+
11
+ /** @var Repository */
12
+ private $repository;
13
+
14
+ /**
15
+ * Everybody_Else constructor.
16
+ *
17
+ * @param Repository $repository
18
+ */
19
+ public function __construct( Repository $repository ) { $this->repository = $repository; }
20
+
21
+ public function matches( Match_Target $target ) {
22
+ foreach ( $this->repository->all() as $user_group ) {
23
+ if ( $user_group->matches( $target ) ) {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ return true;
29
+ }
30
+
31
+ public function get_id() {
32
+ return self::ID;
33
+ }
34
+
35
+ public function get_label() {
36
+ return __( 'Everybody Else', 'better-wp-security' );
37
+ }
38
+ }
core/modules/user-groups/Match/Default_Matcher.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ final class Default_Matcher implements Matcher {
6
+
7
+ /** @var Matchables_Source */
8
+ private $source;
9
+
10
+ /**
11
+ * Default_Matcher constructor.
12
+ *
13
+ * @param Matchables_Source $source
14
+ */
15
+ public function __construct( Matchables_Source $source ) { $this->source = $source; }
16
+
17
+ public function matches( Match_Target $target, $groups ) {
18
+ foreach ( $groups as $group ) {
19
+ try {
20
+ $matchable = $this->source->find( $group );
21
+ } catch ( Matchable_Not_Found $e ) {
22
+ continue;
23
+ }
24
+
25
+ if ( $matchable->matches( $target ) ) {
26
+ return true;
27
+ }
28
+ }
29
+
30
+ return false;
31
+ }
32
+ }
33
+
core/modules/user-groups/Match/Match_Target.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use WP_User;
6
+
7
+ final class Match_Target {
8
+
9
+ /** @var WP_User|null */
10
+ private $user;
11
+
12
+ /** @var string */
13
+ private $role;
14
+
15
+ /**
16
+ * Match_Target constructor.
17
+ *
18
+ * @param WP_User|null $user
19
+ * @param string $role
20
+ */
21
+ private function __construct( WP_User $user = null, $role = '' ) {
22
+ $this->user = $user;
23
+ $this->role = $role;
24
+ }
25
+
26
+ /**
27
+ * Creates a new Match Target with a user.
28
+ *
29
+ * @param WP_User $user User to match against.
30
+ * @param string $role Optionally, specify the role to check against instead of the user's current role.
31
+ *
32
+ * @return Match_Target
33
+ */
34
+ public static function for_user( WP_User $user, $role = '' ) {
35
+ return new self( $user, $role );
36
+ }
37
+
38
+ /**
39
+ * Creates a new Match Target for a role.
40
+ *
41
+ * @param string $role
42
+ *
43
+ * @return Match_Target
44
+ */
45
+ public static function for_role( $role ) {
46
+ return new self( null, $role );
47
+ }
48
+
49
+ /**
50
+ * Gets the user to match against.
51
+ *
52
+ * @return WP_User|null The user may not be defined.
53
+ */
54
+ public function get_user() {
55
+ return $this->user;
56
+ }
57
+
58
+ /**
59
+ * Gets the role to check against.
60
+ *
61
+ * @return string
62
+ */
63
+ public function get_role() {
64
+ return $this->role;
65
+ }
66
+ }
core/modules/user-groups/Match/Matchable_Not_Found.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use iThemesSecurity\Exception\Exception;
6
+
7
+ final class Matchable_Not_Found extends \OutOfBoundsException implements Exception {
8
+
9
+ }
core/modules/user-groups/Match/Matcher.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ interface Matcher {
6
+
7
+ /**
8
+ * Checks if their is a match for one of the group identifiers.
9
+ *
10
+ * @param Match_Target $target
11
+ * @param string[] $groups
12
+ *
13
+ * @return bool
14
+ */
15
+ public function matches( Match_Target $target, $groups );
16
+ }
core/modules/user-groups/Match/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/Matchable.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ interface Matchable {
6
+
7
+ /**
8
+ * Checks if the given user matches the rules for this user group
9
+ *
10
+ * @param Match_Target $target
11
+ *
12
+ * @return bool
13
+ */
14
+ public function matches( Match_Target $target );
15
+
16
+ /**
17
+ * Get a globally unique identifier for this matchable object.
18
+ *
19
+ * @return string
20
+ */
21
+ public function get_id();
22
+
23
+ /**
24
+ * Gets the label for this group.
25
+ *
26
+ * @return string
27
+ */
28
+ public function get_label();
29
+ }
core/modules/user-groups/Matchables_Source.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+ use iThemesSecurity\User_Groups\Repository\Repository;
7
+ use iThemesSecurity\User_Groups\Repository\User_Group_Not_Found;
8
+
9
+ final class Matchables_Source {
10
+
11
+ /** @var Repository */
12
+ private $repository;
13
+
14
+ /** @var Matchable[] */
15
+ private $additional = [];
16
+
17
+ /**
18
+ * Matchables_Source constructor.
19
+ *
20
+ * @param Repository $repository
21
+ */
22
+ public function __construct( Repository $repository ) { $this->repository = $repository; }
23
+
24
+ /**
25
+ * Add a matchable.
26
+ *
27
+ * @param Matchable $matchable
28
+ *
29
+ * @return $this
30
+ */
31
+ public function add( Matchable $matchable ) {
32
+ $this->additional[ $matchable->get_id() ] = $matchable;
33
+
34
+ return $this;
35
+ }
36
+
37
+ /**
38
+ * Find a matchable by its string identifier.
39
+ *
40
+ * @param string $id
41
+ *
42
+ * @return Matchable
43
+ *
44
+ * @throws Matchable_Not_Found
45
+ */
46
+ public function find( $id ) {
47
+ if ( ! is_string( $id ) ) {
48
+ throw new Invalid_Argument_Exception( 'Id must be a string.' );
49
+ }
50
+
51
+ try {
52
+ return isset( $this->additional[ $id ] ) ? $this->additional[ $id ] : $this->repository->get( $id );
53
+ } catch ( User_Group_Not_Found $e ) {
54
+ throw new Matchable_Not_Found( $e->getMessage(), $e->getCode(), $e );
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Check if the matchables source has a matchable identified by the given id.
60
+ *
61
+ * @param string $id
62
+ *
63
+ * @return bool
64
+ */
65
+ public function has( $id ) {
66
+ return isset( $this->additional[ $id ] ) || $this->repository->has( $id );
67
+ }
68
+
69
+ /**
70
+ * Get all matchables.
71
+ *
72
+ * @return Matchable[]
73
+ */
74
+ public function all() {
75
+ return array_merge( $this->repository->all(), array_values( $this->additional ) );
76
+ }
77
+ }
core/modules/user-groups/Module/Module.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Module;
4
+
5
+ use iThemesSecurity\Contracts\Runnable;
6
+ use iThemesSecurity\User_Groups\Everybody_Else;
7
+ use iThemesSecurity\User_Groups\Repository\Repository;
8
+ use iThemesSecurity\User_Groups\Settings_Proxy;
9
+ use iThemesSecurity\User_Groups\Settings_Registry;
10
+ use iThemesSecurity\User_Groups\User_Group;
11
+
12
+ class Module implements Runnable {
13
+
14
+ /** @var Repository */
15
+ private $repository;
16
+
17
+ /** @var Settings_Registry */
18
+ private $settings_registry;
19
+
20
+ /** @var Settings_Proxy */
21
+ private $settings_proxy;
22
+
23
+ /**
24
+ * Active constructor.
25
+ *
26
+ * @param Repository $repository
27
+ * @param Settings_Registry $settings_registry
28
+ * @param Settings_Proxy $settings_proxy
29
+ */
30
+ public function __construct( Repository $repository, Settings_Registry $settings_registry, Settings_Proxy $settings_proxy ) {
31
+ $this->repository = $repository;
32
+ $this->settings_registry = $settings_registry;
33
+ $this->settings_proxy = $settings_proxy;
34
+ }
35
+
36
+ public function run() {
37
+ add_action( 'itsec_initialized', [ $this, 'trigger_setting_registration' ] );
38
+ add_filter( 'map_meta_cap', [ $this, 'map_meta_cap' ], 10, 4 );
39
+ add_action( 'itsec_create_user_group', [ $this, 'initialize_settings' ] );
40
+ }
41
+
42
+ public function trigger_setting_registration() {
43
+ do_action( 'itsec_register_user_group_settings', $this->settings_registry );
44
+ }
45
+
46
+ /**
47
+ * Map meta capabilities.
48
+ *
49
+ * @param string[] $caps
50
+ * @param string $cap
51
+ * @param int $user_id
52
+ * @param array $args
53
+ *
54
+ * @return string[]
55
+ */
56
+ public function map_meta_cap( $caps, $cap, $user_id, $args ) {
57
+ switch ( $cap ) {
58
+ case 'itsec_list_user_groups':
59
+ case 'itsec_create_user_groups':
60
+ case 'itsec_edit_user_groups':
61
+ return [ \ITSEC_Core::get_required_cap() ];
62
+ case 'itsec_read_user_group':
63
+ case 'itsec_edit_user_group':
64
+ case 'itsec_delete_user_group':
65
+ if ( empty( $args[0] ) || ! is_string( $args[0] ) || ! $this->repository->has( $args[0] ) ) {
66
+ return [ 'do_not_allow' ];
67
+ }
68
+
69
+ return [ \ITSEC_Core::get_required_cap() ];
70
+ default:
71
+ return $caps;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Initialize a new user group's settings to use the configuration for the Everybody Else group.
77
+ *
78
+ * @param User_Group $user_group
79
+ */
80
+ public function initialize_settings( User_Group $user_group ) {
81
+ if ( \ITSEC_Core::is_importing() ) {
82
+ // During an import, there is no need to initialize these groups as "new" since we'll
83
+ // be importing their settings shortly.
84
+ return;
85
+ }
86
+
87
+ foreach ( $this->settings_registry->get_settings() as $registration ) {
88
+ $value = $this->settings_proxy->is_enabled( new Everybody_Else( $this->repository ), $registration );
89
+
90
+ if ( $value ) {
91
+ $this->settings_proxy->set_enabled( $user_group, $registration );
92
+ }
93
+ }
94
+ }
95
+ }
core/modules/user-groups/Module/Settings.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Module;
4
+
5
+ use iThemesSecurity\Contracts\Runnable;
6
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
7
+ use iThemesSecurity\User_Groups\Everybody_Else;
8
+ use iThemesSecurity\User_Groups\Repository\Repository;
9
+ use iThemesSecurity\User_Groups\User_Group;
10
+ use ITSEC_Settings;
11
+
12
+ class Settings extends ITSEC_Settings implements Runnable {
13
+ /** @var Repository */
14
+ private $repository;
15
+
16
+ /**
17
+ * Settings constructor.
18
+ *
19
+ * @param Repository $repository
20
+ */
21
+ public function __construct( Repository $repository ) {
22
+ $this->repository = $repository;
23
+ parent::__construct();
24
+ }
25
+
26
+ public function run() {
27
+ \ITSEC_Modules::register_settings( $this );
28
+ }
29
+
30
+ public function get_id() {
31
+ return 'user-groups';
32
+ }
33
+
34
+ public function get_defaults() {
35
+ return [
36
+ 'default_groups' => [],
37
+ ];
38
+ }
39
+
40
+ /**
41
+ * Get the default user group.
42
+ *
43
+ * @param string $name The name of the gorup.
44
+ * @param bool $recreate Whether to recreate the group if it was deleted.
45
+ *
46
+ * @return string
47
+ *
48
+ * @throws Invalid_Argument_Exception
49
+ * @throws \iThemesSecurity\Exception\WP_Error
50
+ */
51
+ public function get_default_group_id( $name, $recreate = false ) {
52
+ if ( ! in_array( $name, \ITSEC_Lib_Canonical_Roles::get_canonical_roles( false ), true ) ) {
53
+ throw new Invalid_Argument_Exception( sprintf( __( 'No default group called %s', 'better-wp-security' ), $name ) );
54
+ }
55
+
56
+ $defaults = $this->get( 'default_groups' );
57
+
58
+ if ( ! isset( $defaults[ $name ] ) ) {
59
+ $this->create_default_groups();
60
+ $defaults = $this->get( 'default_groups' );
61
+ }
62
+
63
+ if ( $this->repository->has( $defaults[ $name ] ) ) {
64
+ return $defaults[ $name ];
65
+ }
66
+
67
+ $prototype = new User_Group( $this->repository->next_id() );
68
+ $this->configure_group( $prototype, $name );
69
+
70
+ foreach ( $this->repository->all() as $group ) {
71
+ if ( $group->equals( $prototype ) ) {
72
+ $defaults[ $name ] = $group->get_id();
73
+ $this->set( 'default_groups', $defaults );
74
+
75
+ return $group->get_id();
76
+ }
77
+ }
78
+
79
+ if ( $recreate ) {
80
+ $this->repository->persist( $prototype );
81
+ $defaults[ $name ] = $prototype->get_id();
82
+
83
+ $this->set( 'default_groups', $defaults );
84
+
85
+ return $prototype->get_id();
86
+ }
87
+
88
+ return '';
89
+ }
90
+
91
+ /**
92
+ * Get the default canonical groups and the everybody-else group.
93
+ *
94
+ * @return string[]
95
+ */
96
+ public function get_groups_for_all_users() {
97
+ $groups = [];
98
+
99
+ foreach ( \ITSEC_Lib_Canonical_Roles::get_canonical_roles( false ) as $canonical ) {
100
+ if ( $group = $this->get_default_group_id( $canonical ) ) {
101
+ $groups[] = $group;
102
+ }
103
+ }
104
+
105
+ $groups[] = Everybody_Else::ID;
106
+
107
+ return $groups;
108
+ }
109
+
110
+ /**
111
+ * Create the default user groups.
112
+ */
113
+ private function create_default_groups() {
114
+ $defaults = $this->get( 'default_groups' );
115
+ $save = false;
116
+
117
+ foreach ( \ITSEC_Lib_Canonical_Roles::get_canonical_roles( false ) as $role ) {
118
+ if ( ! isset( $defaults[ $role ] ) ) {
119
+ $group = new User_Group( $this->repository->next_id() );
120
+ $this->configure_group( $group, $role );
121
+ $this->repository->persist( $group );
122
+ $defaults[ $role ] = $group->get_id();
123
+
124
+ $save = true;
125
+ }
126
+ }
127
+
128
+ if ( $save ) {
129
+ $this->set( 'default_groups', $defaults );
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Configure a group
135
+ *
136
+ * @param User_Group $group
137
+ * @param string $type
138
+ *
139
+ * @return void
140
+ */
141
+ private function configure_group( User_Group $group, $type ) {
142
+ $group->add_canonical_role( $type );
143
+ $group->set_label( sprintf( __( '%s Users', 'better-wp-security' ), translate_user_role( ucfirst( $type ) ) ) );
144
+
145
+ if ( $type === 'administrator' && is_multisite() ) {
146
+ $group->add_canonical_role( 'super-admin' );
147
+ }
148
+ }
149
+ }
core/modules/user-groups/Module/Validator.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Module;
4
+
5
+ use iThemesSecurity\Contracts\Runnable;
6
+
7
+ class Validator extends \ITSEC_Validator implements Runnable {
8
+ public function get_id() {
9
+ return 'user-groups';
10
+ }
11
+
12
+ public function run() {
13
+ \ITSEC_Modules::register_validator( $this );
14
+ }
15
+
16
+ protected function sanitize_settings() {
17
+ $this->sanitize_setting( 'user-groups', 'default_groups', __( 'Default Groups', 'better-wp-security' ) );
18
+ }
19
+ }
core/modules/user-groups/Module/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/REST/Matchables.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\REST;
4
+
5
+ use iThemesSecurity\User_Groups\Matchable;
6
+ use iThemesSecurity\User_Groups\Matchables_Source;
7
+ use iThemesSecurity\User_Groups\User_Group;
8
+
9
+ class Matchables extends \WP_REST_Controller {
10
+
11
+ /** @var Matchables_Source */
12
+ private $source;
13
+
14
+ /**
15
+ * Matchables constructor.
16
+ *
17
+ * @param Matchables_Source $source
18
+ */
19
+ public function __construct( Matchables_Source $source ) {
20
+ $this->source = $source;
21
+ $this->namespace = 'ithemes-security/v1';
22
+ $this->rest_base = 'user-matchables';
23
+ }
24
+
25
+ public function register_routes() {
26
+ register_rest_route( $this->namespace, $this->rest_base, [
27
+ 'methods' => \WP_REST_Server::READABLE,
28
+ 'callback' => [ $this, 'get_items' ],
29
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
30
+ 'schema' => [ $this, 'get_public_item_schema' ],
31
+ ] );
32
+ }
33
+
34
+ public function get_items_permissions_check( $request ) {
35
+ if ( ! current_user_can( 'itsec_list_user_groups' ) ) {
36
+ return new \WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to list user matchables.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ public function get_items( $request ) {
43
+ $data = [];
44
+
45
+ foreach ( $this->source->all() as $matchable ) {
46
+ $data[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $matchable, $request ) );
47
+ }
48
+
49
+ return new \WP_REST_Response( $data );
50
+ }
51
+
52
+ public function prepare_item_for_response( $item, $request ) {
53
+ if ( ! $item instanceof Matchable ) {
54
+ return new \WP_REST_Response();
55
+ }
56
+
57
+ $data = [
58
+ 'id' => $item->get_id(),
59
+ 'label' => $item->get_label(),
60
+ 'type' => $item instanceof User_Group ? 'user-group' : 'meta',
61
+ ];
62
+
63
+ $response = new \WP_REST_Response( $data );
64
+ $response->add_links( $this->prepare_links( $item ) );
65
+
66
+ return $response;
67
+ }
68
+
69
+ /**
70
+ * Prepare the links for each user group.
71
+ *
72
+ * @param Matchable $matchable
73
+ *
74
+ * @return array
75
+ */
76
+ public function prepare_links( Matchable $matchable ) {
77
+ $links = [];
78
+
79
+ if ( $matchable instanceof User_Group ) {
80
+ $links['self'] = [
81
+ 'href' => add_query_arg( 'context', 'view', rest_url( "{$this->namespace}/user-groups/{$matchable->get_id()}" ) ),
82
+ 'embeddable' => true,
83
+ ];
84
+ }
85
+
86
+ $links[ \ITSEC_Lib_REST::get_link_relation( 'user-matchable-settings' ) ] = [
87
+ 'href' => rest_url( "{$this->namespace}/user-matchable-settings/{$matchable->get_id()}" ),
88
+ 'embeddable' => true,
89
+ ];
90
+
91
+ return $links;
92
+ }
93
+
94
+ public function get_item_schema() {
95
+ if ( ! empty( $this->schema ) && ! \ITSEC_Core::is_test_suite( 'wpunit' ) ) {
96
+ return $this->schema;
97
+ }
98
+
99
+ $schema = [
100
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
101
+ 'title' => 'ithemes-security-user-matchable',
102
+ 'type' => 'object',
103
+ 'properties' => [
104
+ 'id' => [
105
+ 'type' => 'string',
106
+ ],
107
+ 'label' => [
108
+ 'type' => 'string',
109
+ ],
110
+ ],
111
+ 'links' => [
112
+ [
113
+ 'rel' => 'self',
114
+ 'href' => rest_url( sprintf( '%s/user-groups/{id}', $this->rest_base ) ),
115
+ 'hrefSchema' => [
116
+ 'type' => 'object',
117
+ 'properties' => [
118
+ 'id' => [
119
+ 'type' => 'string',
120
+ ],
121
+ ],
122
+ ]
123
+ ]
124
+ ]
125
+ ];
126
+
127
+ if ( isset( $this->schema ) ) {
128
+ $this->schema = $schema;
129
+ }
130
+
131
+ return $schema;
132
+ }
133
+ }
core/modules/user-groups/REST/REST.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\REST;
4
+
5
+ use iThemesSecurity\Contracts\Runnable;
6
+
7
+ class REST implements Runnable {
8
+
9
+ /** @var \WP_REST_Controller[] */
10
+ private $controllers;
11
+
12
+ /**
13
+ * REST constructor.
14
+ *
15
+ * @param \WP_REST_Controller[] $controllers
16
+ */
17
+ public function __construct( array $controllers ) { $this->controllers = $controllers; }
18
+
19
+ public function run() {
20
+ add_action( 'rest_api_init', [ $this, 'register_routes' ] );
21
+ }
22
+
23
+ public function register_routes() {
24
+ foreach ( $this->controllers as $controller ) {
25
+ $controller->register_routes();
26
+ }
27
+ }
28
+ }
core/modules/user-groups/REST/Settings.php ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\REST;
4
+
5
+ use iThemesSecurity\User_Groups\Matchable;
6
+ use iThemesSecurity\User_Groups\Matchables_Source;
7
+ use iThemesSecurity\User_Groups\Settings_Proxy;
8
+ use iThemesSecurity\User_Groups\Settings_Registration;
9
+ use iThemesSecurity\User_Groups\Settings_Registry;
10
+ use iThemesSecurity\User_Groups\Repository\User_Group_Not_Found;
11
+
12
+ class Settings extends \WP_REST_Controller {
13
+
14
+ /** @var Matchables_Source */
15
+ private $source;
16
+
17
+ /** @var Settings_Registry */
18
+ private $settings_registry;
19
+
20
+ /** @var Settings_Proxy */
21
+ private $proxy;
22
+
23
+ /**
24
+ * REST constructor.
25
+ *
26
+ * @param Matchables_Source $source
27
+ * @param Settings_Registry $settings_registry
28
+ * @param Settings_Proxy $proxy
29
+ */
30
+ public function __construct( Matchables_Source $source, Settings_Registry $settings_registry, Settings_Proxy $proxy ) {
31
+ $this->source = $source;
32
+ $this->settings_registry = $settings_registry;
33
+ $this->proxy = $proxy;
34
+ $this->namespace = 'ithemes-security/v1';
35
+ $this->rest_base = 'user-matchable-settings';
36
+ }
37
+
38
+ public function register_routes() {
39
+ register_rest_route( $this->namespace, $this->rest_base, [
40
+ [
41
+ 'methods' => \WP_REST_Server::READABLE,
42
+ 'callback' => [ $this, 'get_items' ],
43
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
44
+ 'args' => $this->get_collection_params(),
45
+ ],
46
+ [
47
+ 'methods' => 'PATCH',
48
+ 'callback' => [ $this, 'patch_items' ],
49
+ 'permission_callback' => [ $this, 'patch_items_permissions_check' ],
50
+ 'args' => array_merge(
51
+ $this->get_endpoint_args_for_item_schema( 'PATCH' ),
52
+ $this->get_collection_params()
53
+ ),
54
+ ],
55
+ 'schema' => [ $this, 'get_public_item_schema' ],
56
+ ] );
57
+
58
+ register_rest_route( $this->namespace, $this->rest_base . '/(?P<id>.*)', [
59
+ [
60
+ 'methods' => \WP_REST_Server::READABLE,
61
+ 'callback' => [ $this, 'get_item' ],
62
+ 'permission_callback' => [ $this, 'get_item_permissions_check' ],
63
+ 'args' => [
64
+ 'context' => $this->get_context_param( [ 'default' => 'view' ] ),
65
+ ],
66
+ ],
67
+ [
68
+ 'methods' => 'PUT',
69
+ 'callback' => [ $this, 'update_item' ],
70
+ 'permission_callback' => [ $this, 'update_item_permissions_check' ],
71
+ 'args' => $this->get_endpoint_args_for_item_schema( 'PUT' ),
72
+ ],
73
+ 'schema' => [ $this, 'get_public_item_schema' ],
74
+ 'args' => [
75
+ 'id' => [
76
+ 'type' => 'string',
77
+ ],
78
+ ],
79
+ 'show_in_index' => false,
80
+ ] );
81
+ }
82
+
83
+ public function get_items_permissions_check( $request ) {
84
+ return \ITSEC_Core::current_user_can_manage();
85
+ }
86
+
87
+ public function get_items( $request ) {
88
+ $matchables = $this->source->all();
89
+ $return = [];
90
+
91
+ foreach ( $matchables as $matchable ) {
92
+ $return[ $matchable->get_id() ] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $matchable, $request ) );
93
+ }
94
+
95
+ return new \WP_REST_Response( $return );
96
+ }
97
+
98
+ public function get_item_permissions_check( $request ) {
99
+ return $this->update_item_permissions_check( $request );
100
+ }
101
+
102
+ public function get_item( $request ) {
103
+ try {
104
+ return $this->prepare_item_for_response( $this->source->find( $request['id'] ), $request );
105
+ } catch ( User_Group_Not_Found $e ) {
106
+ return new \WP_Error( 'rest_user_group_not_found', $e->getMessage(), [ 'status' => \WP_Http::NOT_FOUND ] );
107
+ }
108
+ }
109
+
110
+ public function update_item_permissions_check( $request ) {
111
+ if ( ! \ITSEC_Core::current_user_can_manage() ) {
112
+ return false;
113
+ }
114
+
115
+ if ( ! $this->source->has( $request['id'] ) ) {
116
+ return new \WP_Error( 'rest_user_group_not_found', __( 'No user group found.', 'better-wp-security' ), [ 'status' => \WP_Http::NOT_FOUND ] );
117
+ }
118
+
119
+ return true;
120
+ }
121
+
122
+ public function update_item( $request ) {
123
+ \ITSEC_Core::set_interactive();
124
+
125
+ try {
126
+ $matchable = $this->source->find( $request['id'] );
127
+ $maybe_error = $this->update_matchable_for_request( $matchable, $request );
128
+
129
+ if ( is_wp_error( $maybe_error ) ) {
130
+ return $maybe_error;
131
+ }
132
+
133
+ $request['context'] = 'edit';
134
+
135
+ return $this->prepare_item_for_response( $matchable, $request );
136
+ } catch ( User_Group_Not_Found $e ) {
137
+ return new \WP_Error( 'rest_user_group_not_found', $e->getMessage(), [ 'status' => \WP_Http::NOT_FOUND ] );
138
+ } catch ( \Exception $e ) {
139
+ return new \WP_Error( 'internal_server_error', __( 'An unexpected error occurred.', 'better-wp-security' ), [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Patch items.
145
+ *
146
+ * @param \WP_REST_Request $request
147
+ *
148
+ * @return \WP_REST_Response
149
+ */
150
+ public function patch_items( \WP_REST_Request $request ) {
151
+ \ITSEC_Core::set_interactive();
152
+
153
+ $request['context'] = 'edit';
154
+
155
+ $return = [];
156
+
157
+ if ( isset( $request['include'] ) ) {
158
+ $matchables = $request['include'];
159
+ } else {
160
+ $matchables = $this->source->all();
161
+ }
162
+
163
+ foreach ( $matchables as $matchable ) {
164
+ if ( $matchable instanceof Matchable ) {
165
+ $id = $matchable->get_id();
166
+ } else {
167
+ $id = $matchable;
168
+ }
169
+
170
+ $href = rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $id ) );
171
+
172
+ try {
173
+ if ( ! $matchable instanceof Matchable ) {
174
+ $matchable = $this->source->find( $id );
175
+ }
176
+
177
+ $maybe_error = $this->update_matchable_for_request( $matchable, $request );
178
+
179
+ if ( is_wp_error( $maybe_error ) ) {
180
+ $return[] = [
181
+ 'href' => $href,
182
+ 'status' => \WP_Http::BAD_REQUEST,
183
+ 'error' => rest_get_server()->response_to_data( \ITSEC_Lib_REST::error_to_response( $maybe_error ), false ),
184
+ ];
185
+
186
+ continue;
187
+ }
188
+
189
+ $return[] = [
190
+ 'href' => $href,
191
+ 'status' => \WP_Http::OK,
192
+ 'response' => $this->prepare_response_for_collection( $this->prepare_item_for_response( $matchable, $request ) ),
193
+ ];
194
+ } catch ( User_Group_Not_Found $e ) {
195
+ $return[] = [
196
+ 'href' => $href,
197
+ 'status' => \WP_Http::NOT_FOUND,
198
+ 'error' => rest_get_server()->response_to_data( \ITSEC_Lib_REST::error_to_response(
199
+ new \WP_Error( 'rest_user_group_not_found', $e->getMessage(), [ 'status' => \WP_Http::NOT_FOUND ] )
200
+ ), false ),
201
+ ];
202
+ } catch ( \Exception $e ) {
203
+ $return[] = [
204
+ 'href' => $href,
205
+ 'status' => \WP_Http::NOT_FOUND,
206
+ 'error' => rest_get_server()->response_to_data( \ITSEC_Lib_REST::error_to_response(
207
+ new \WP_Error( 'internal_server_error', __( 'An unexpected error occurred.', 'better-wp-security' ), [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] )
208
+ ), false ),
209
+ ];
210
+ }
211
+ }
212
+
213
+ return new \WP_REST_Response( $return, 207 );
214
+ }
215
+
216
+ /**
217
+ * Check if the user has permissions to perform a patch.
218
+ *
219
+ * @param \WP_REST_Request $request
220
+ *
221
+ * @return bool
222
+ */
223
+ public function patch_items_permissions_check( $request ) {
224
+ return \ITSEC_Core::current_user_can_manage();
225
+ }
226
+
227
+ public function prepare_item_for_response( $item, $request ) {
228
+ $schema = $this->get_item_schema();
229
+ $data = [];
230
+
231
+ if ( ! $item instanceof Matchable ) {
232
+ return new \WP_REST_Response( $data );
233
+ }
234
+
235
+ foreach ( $this->settings_registry->get_settings() as $registration ) {
236
+ if ( ! isset( $schema['properties'][ $registration->get_module() ]['properties'][ $registration->get_setting() ] ) ) {
237
+ continue;
238
+ }
239
+
240
+ $data[ $registration->get_module() ][ $registration->get_setting() ] = $this->proxy->is_enabled( $item, $registration );
241
+ }
242
+
243
+ $data = $this->filter_response_by_context( $data, $request['context'] );
244
+
245
+ return new \WP_REST_Response( $data );
246
+ }
247
+
248
+ /**
249
+ * Update a matchable based on the request data.
250
+ *
251
+ * @param Matchable $matchable
252
+ * @param \WP_REST_Request $request
253
+ *
254
+ * @return \WP_Error|null
255
+ */
256
+ protected function update_matchable_for_request( Matchable $matchable, \WP_REST_Request $request ) {
257
+ $schema = $this->get_item_schema();
258
+
259
+ foreach ( $this->settings_registry->get_settings() as $registration ) {
260
+ if ( ! isset( $request[ $registration->get_module() ][ $registration->get_setting() ] ) ) {
261
+ continue;
262
+ }
263
+
264
+ if ( ! isset( $schema['properties'][ $registration->get_module() ]['properties'][ $registration->get_setting() ] ) ) {
265
+ continue;
266
+ }
267
+
268
+ $enabled = $request[ $registration->get_module() ][ $registration->get_setting() ];
269
+ $updated = $this->proxy->set_enabled( $matchable, $registration, $enabled );
270
+
271
+ if ( is_wp_error( $updated ) ) {
272
+ return $updated;
273
+ }
274
+ }
275
+
276
+ return null;
277
+ }
278
+
279
+ public function get_item_schema() {
280
+ if ( ! empty( $this->schema ) && ! \ITSEC_Core::is_test_suite( 'wpunit' ) ) {
281
+ return $this->schema;
282
+ }
283
+
284
+ $schema = [
285
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
286
+ 'title' => 'ithemes-security-user-group-settings',
287
+ 'type' => 'object',
288
+ 'properties' => [],
289
+ 'additionalProperties' => false,
290
+ 'links' => [
291
+ [
292
+ 'rel' => 'self',
293
+ 'href' => rest_url( sprintf( '%s/%s/{id}', $this->namespace, $this->rest_base ) ),
294
+ 'hrefSchema' => [
295
+ 'type' => 'object',
296
+ 'properties' => [
297
+ 'id' => [
298
+ 'type' => 'string',
299
+ ],
300
+ ],
301
+ ]
302
+ ]
303
+ ],
304
+ ];
305
+
306
+ foreach ( $this->settings_registry->get_settings() as $registration ) {
307
+ if ( Settings_Registration::T_MULTIPLE !== $registration->get_type() ) {
308
+ continue;
309
+ }
310
+
311
+ $title = $registration->get_module();
312
+
313
+ if ( $labels = \ITSEC_Modules::get_labels( $registration->get_module() ) ) {
314
+ $title = $labels['title'];
315
+ }
316
+
317
+ if ( ! isset( $schema['properties'][ $registration->get_module() ] ) ) {
318
+ $schema['properties'][ $registration->get_module() ] = [
319
+ 'title' => $title,
320
+ 'type' => 'object',
321
+ 'properties' => [],
322
+ 'context' => [ 'view', 'edit', 'embed' ],
323
+ 'additionalProperties' => false,
324
+ ];
325
+ }
326
+
327
+ $labels = $registration->get_labels();
328
+
329
+ $schema['properties'][ $registration->get_module() ]['properties'][ $registration->get_setting() ] = [
330
+ 'title' => isset( $labels['title'] ) ? $labels['title'] : '',
331
+ 'description' => isset( $labels['description'] ) ? $labels['description'] : '',
332
+ 'type' => 'boolean',
333
+ 'context' => [ 'view', 'edit', 'embed' ],
334
+ ];
335
+ }
336
+
337
+ if ( isset( $this->schema ) ) {
338
+ $this->schema = $schema;
339
+ }
340
+
341
+ return $schema;
342
+ }
343
+
344
+ public function get_collection_params() {
345
+ return [
346
+ 'context' => $this->get_context_param( [ 'default' => 'view' ] ),
347
+ 'include' => [
348
+ 'type' => 'array',
349
+ 'items' => [
350
+ 'type' => 'string',
351
+ ],
352
+ ],
353
+ ];
354
+ }
355
+ }
core/modules/user-groups/REST/User_Groups.php ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\REST;
4
+
5
+ use iThemesSecurity\User_Groups\Repository\Repository;
6
+ use iThemesSecurity\User_Groups\User_Group;
7
+ use iThemesSecurity\User_Groups\Repository\User_Group_Not_Found;
8
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
9
+ use iThemesSecurity\Exception\WP_Error;
10
+
11
+ class User_Groups extends \WP_REST_Controller {
12
+
13
+ const ID_PATTERN = '(?P<id>[\\w_:-]+)';
14
+
15
+ /** @var \iThemesSecurity\User_Groups\Repository\Repository */
16
+ private $repository;
17
+
18
+ /**
19
+ * REST constructor.
20
+ *
21
+ * @param \iThemesSecurity\User_Groups\Repository\Repository $repository
22
+ */
23
+ public function __construct( Repository $repository ) {
24
+ $this->repository = $repository;
25
+ $this->namespace = 'ithemes-security/v1';
26
+ $this->rest_base = 'user-groups';
27
+ }
28
+
29
+ public function register_routes() {
30
+ register_rest_route( $this->namespace, $this->rest_base, [
31
+ [
32
+ 'methods' => \WP_REST_Server::READABLE,
33
+ 'callback' => [ $this, 'get_items' ],
34
+ 'permission_callback' => [ $this, 'get_items_permissions_check' ],
35
+ 'args' => $this->get_collection_params(),
36
+ ],
37
+ [
38
+ 'methods' => \WP_REST_Server::CREATABLE,
39
+ 'callback' => [ $this, 'create_item' ],
40
+ 'permission_callback' => [ $this, 'create_item_permissions_check' ],
41
+ 'args' => array_merge( $this->get_endpoint_args_for_item_schema(), [
42
+ 'ignore_duplicate' => [
43
+ 'type' => 'boolean',
44
+ ],
45
+ ] ),
46
+ ],
47
+ 'schema' => [ $this, 'get_public_item_schema' ],
48
+ ] );
49
+
50
+ register_rest_route( $this->namespace, $this->rest_base . '/' . self::ID_PATTERN, [
51
+ [
52
+ 'methods' => \WP_REST_Server::READABLE,
53
+ 'callback' => [ $this, 'get_item' ],
54
+ 'permission_callback' => [ $this, 'get_item_permissions_check' ],
55
+ 'args' => [
56
+ 'context' => $this->get_context_param( [ 'default' => 'view' ] ),
57
+ ],
58
+ ],
59
+ [
60
+ 'methods' => 'PUT',
61
+ 'callback' => [ $this, 'update_item' ],
62
+ 'permission_callback' => [ $this, 'update_item_permissions_check' ],
63
+ 'args' => $this->get_endpoint_args_for_item_schema( 'PUT' ),
64
+ ],
65
+ [
66
+ 'methods' => 'DELETE',
67
+ 'callback' => [ $this, 'delete_item' ],
68
+ 'permission_callback' => [ $this, 'delete_item_permissions_check' ],
69
+ ],
70
+ 'schema' => [ $this, 'get_public_item_schema' ],
71
+ 'args' => [
72
+ 'id' => [
73
+ 'type' => 'string',
74
+ ],
75
+ ],
76
+ ] );
77
+ }
78
+
79
+ public function get_items_permissions_check( $request ) {
80
+ if ( 'edit' === $request['context'] && ! current_user_can( 'itsec_edit_user_groups' ) ) {
81
+ return new \WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit user groups.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
82
+ }
83
+
84
+ if ( ! current_user_can( 'itsec_list_user_groups' ) ) {
85
+ return new \WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to list user groups.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
86
+ }
87
+
88
+ return true;
89
+ }
90
+
91
+ public function get_items( $request ) {
92
+ $data = [];
93
+
94
+ foreach ( $this->repository->all() as $user_group ) {
95
+ $data[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $user_group, $request ) );
96
+ }
97
+
98
+ return new \WP_REST_Response( $data );
99
+ }
100
+
101
+ public function get_item_permissions_check( $request ) {
102
+ if ( true !== ( $error = $this->check_group_exists( $request ) ) ) {
103
+ return $error;
104
+ }
105
+
106
+ if ( 'edit' === $request['context'] && ! current_user_can( 'itsec_edit_user_group', $request['id'] ) ) {
107
+ return new \WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this user group.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
108
+ }
109
+
110
+ if ( ! current_user_can( 'itsec_read_user_group', $request['id'] ) ) {
111
+ return new \WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to view this user group.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
112
+ }
113
+
114
+ return true;
115
+ }
116
+
117
+ public function get_item( $request ) {
118
+ try {
119
+ return $this->prepare_item_for_response( $this->repository->get( $request['id'] ), $request );
120
+ } catch ( User_Group_Not_Found $e ) {
121
+ return new \WP_Error( 'rest_user_group_not_found', $e->getMessage(), [ 'status' => \WP_Http::NOT_FOUND ] );
122
+ }
123
+ }
124
+
125
+ public function create_item_permissions_check( $request ) {
126
+ if ( ! current_user_can( 'itsec_create_user_groups' ) ) {
127
+ return new \WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create user groups.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
128
+ }
129
+
130
+ return true;
131
+ }
132
+
133
+ public function create_item( $request ) {
134
+ try {
135
+ $user_group = $this->prepare_item_for_database( $request );
136
+
137
+ if ( is_wp_error( $user_group ) ) {
138
+ return $user_group;
139
+ }
140
+
141
+ if ( ! $request['ignore_duplicate'] && $response = $this->handle_duplicate_check( $user_group ) ) {
142
+ return $response;
143
+ }
144
+
145
+ $this->repository->persist( $user_group );
146
+ $request['context'] = 'edit';
147
+
148
+ $response = $this->prepare_item_for_response( $user_group, $request );
149
+ $response->set_status( \WP_Http::CREATED );
150
+ $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $user_group->get_id() ) ) );
151
+
152
+ return $response;
153
+ } catch ( \Exception $e ) {
154
+ return new \WP_Error( 'internal_server_error', __( 'An unexpected error occurred.', 'better-wp-security' ), [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
155
+ }
156
+ }
157
+
158
+ public function update_item_permissions_check( $request ) {
159
+ if ( true !== ( $error = $this->check_group_exists( $request ) ) ) {
160
+ return $error;
161
+ }
162
+
163
+ if ( ! current_user_can( 'itsec_edit_user_group', $request['id'] ) ) {
164
+ return new \WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this user group.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
165
+ }
166
+
167
+ return true;
168
+ }
169
+
170
+ /**
171
+ * Handle checking for duplicate user groups.
172
+ *
173
+ * @param User_Group $user_group
174
+ *
175
+ * @return \WP_REST_Response|null
176
+ */
177
+ private function handle_duplicate_check( User_Group $user_group ) {
178
+ /** @var User_Group[] $duplicates */
179
+ $duplicates = array_filter( $this->repository->all(), static function ( User_Group $maybe_group ) use ( $user_group ) {
180
+ return $user_group->equals( $maybe_group );
181
+ } );
182
+
183
+ if ( ! $duplicates ) {
184
+ return null;
185
+ }
186
+
187
+ \ITSEC_Lib::load( 'rest' );
188
+ $error = new \WP_Error( 'rest_duplicate_user_group', __( 'Another user group with this configuration already exists.', 'better-wp-security' ), [ 'status' => \WP_Http::BAD_REQUEST ] );
189
+ $response = \ITSEC_Lib_REST::error_to_response( $error );
190
+
191
+ foreach ( $duplicates as $duplicate ) {
192
+ $response->add_link( 'duplicate', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $duplicate->get_id() ) ) );
193
+ }
194
+
195
+ return $response;
196
+ }
197
+
198
+ public function update_item( $request ) {
199
+ try {
200
+ $user_group = $this->prepare_item_for_database( $request );
201
+
202
+ if ( is_wp_error( $user_group ) ) {
203
+ return $user_group;
204
+ }
205
+
206
+ $this->repository->persist( $user_group );
207
+ $request['context'] = 'edit';
208
+
209
+ return $this->prepare_item_for_response( $user_group, $request );
210
+ } catch ( WP_Error $e ) {
211
+ return $e->get_error();
212
+ } catch ( \Exception $e ) {
213
+ return new \WP_Error( 'internal_server_error', __( 'An unexpected error occurred.', 'better-wp-security' ), [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
214
+ }
215
+ }
216
+
217
+ public function delete_item_permissions_check( $request ) {
218
+ if ( true !== ( $error = $this->check_group_exists( $request ) ) ) {
219
+ return $error;
220
+ }
221
+
222
+ if ( ! current_user_can( 'itsec_delete_user_group', $request['id'] ) ) {
223
+ return new \WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this user group.', 'better-wp-security' ), [ 'status' => rest_authorization_required_code() ] );
224
+ }
225
+
226
+ return true;
227
+ }
228
+
229
+ public function delete_item( $request ) {
230
+ try {
231
+ $user_group = $this->repository->get( $request['id'] );
232
+ $this->repository->delete( $user_group );
233
+
234
+ return new \WP_REST_Response( null, \WP_Http::NO_CONTENT );
235
+ } catch ( User_Group_Not_Found $e ) {
236
+ return new \WP_Error( 'rest_user_group_not_found', $e->getMessage(), [ 'status' => \WP_Http::NOT_FOUND ] );
237
+ }
238
+ }
239
+
240
+ protected function prepare_item_for_database( $request ) {
241
+ try {
242
+ if ( isset( $request->get_url_params()['id'] ) ) {
243
+ $user_group = $this->repository->get( $request->get_url_params()['id'] );
244
+ } elseif ( isset( $request['id'] ) ) {
245
+ if ( $this->repository->has( $request['id'] ) ) {
246
+ throw WP_Error::from_code(
247
+ 'rest_duplicate_user_group_id',
248
+ sprintf( __( 'A user group already exists with the id \'%s\'.', 'better-wp-security' ), $request['id'] ),
249
+ [ 'status' => \WP_Http::BAD_REQUEST ]
250
+ );
251
+ }
252
+
253
+ $user_group = new User_Group( $request['id'] );
254
+ } else {
255
+ $user_group = new User_Group( $this->repository->next_id() );
256
+ }
257
+
258
+ if ( isset( $request['users'] ) ) {
259
+ $user_group->set_users( array_map( static function ( $id ) {
260
+ if ( ! $user = get_userdata( $id ) ) {
261
+ throw WP_Error::from_code( 'rest_user_not_found', sprintf( __( 'No user found for %d.', 'better-wp-security' ), $id ), [ 'status' => \WP_Http::BAD_REQUEST ] );
262
+ }
263
+
264
+ return $user;
265
+ }, $request['users'] ) );
266
+ }
267
+
268
+ if ( isset( $request['roles'] ) ) {
269
+ $user_group->set_roles( $request['roles'] );
270
+ }
271
+
272
+ if ( isset( $request['canonical'] ) ) {
273
+ $user_group->set_canonical_roles( $request['canonical'] );
274
+ }
275
+
276
+ if ( isset( $request['min_role'] ) ) {
277
+ $user_group->set_min_role( $request['min_role'] );
278
+ }
279
+
280
+ if ( isset( $request['label'] ) ) {
281
+ $user_group->set_label( $request['label'] );
282
+ }
283
+ } catch ( WP_Error $e ) {
284
+ return $e->get_error();
285
+ } catch ( Invalid_Argument_Exception $e ) {
286
+ return new \WP_Error( 'rest_invalid_param', $e->getMessage(), [ 'status' => \WP_Http::BAD_REQUEST ] );
287
+ } catch ( \Exception $e ) {
288
+ return new \WP_Error( 'internal_server_error', __( 'An unexpected error occurred.', 'better-wp-security' ), [ 'status' => \WP_Http::INTERNAL_SERVER_ERROR ] );
289
+ }
290
+
291
+ if ( ! $user_group->is_configured() ) {
292
+ return new \WP_Error(
293
+ 'rest_user_group_not_configured',
294
+ __( 'A user group must have a minimum role, list of roles, or list of users to be created.' ),
295
+ [ 'status' => \WP_Http::BAD_REQUEST ]
296
+ );
297
+ }
298
+
299
+ return $user_group;
300
+ }
301
+
302
+ public function prepare_item_for_response( $item, $request ) {
303
+ if ( ! $item instanceof User_Group ) {
304
+ return new \WP_REST_Response();
305
+ }
306
+
307
+ $fields = $this->get_fields_for_response( $request );
308
+ $data = [
309
+ 'id' => $item->get_id(),
310
+ ];
311
+
312
+ if ( in_array( 'label', $fields, true ) ) {
313
+ $data['label'] = $item->get_label();
314
+ }
315
+
316
+ if ( in_array( 'description', $fields, true ) ) {
317
+ $data['description'] = $item->get_description();
318
+ }
319
+
320
+ if ( in_array( 'users', $fields, true ) ) {
321
+ $data['users'] = wp_list_pluck( $item->get_users(), 'ID' );
322
+ }
323
+
324
+ if ( in_array( 'roles', $fields, true ) ) {
325
+ $data['roles'] = $item->get_roles();
326
+ }
327
+
328
+ if ( in_array( 'canonical', $fields, true ) ) {
329
+ $data['canonical'] = $item->get_canonical_roles();
330
+ }
331
+
332
+ if ( in_array( 'min_role', $fields, true ) ) {
333
+ $data['min_role'] = $item->get_min_role();
334
+ }
335
+
336
+ $response = new \WP_REST_Response( $data );
337
+ $response->add_links( $this->prepare_links( $item ) );
338
+
339
+ return $response;
340
+ }
341
+
342
+ /**
343
+ * Check that a user group exists.
344
+ *
345
+ * @param \WP_REST_Request $request
346
+ *
347
+ * @return bool|\WP_Error
348
+ */
349
+ protected function check_group_exists( \WP_REST_Request $request ) {
350
+ if ( $this->repository->has( $request['id'] ) ) {
351
+ return true;
352
+ }
353
+
354
+ return new \WP_Error( 'rest_not_found', __( 'Sorry, no user group exists with that id.', 'better-wp-security' ), [ 'status' => \WP_Http::NOT_FOUND ] );
355
+ }
356
+
357
+ /**
358
+ * Prepare the links for each user group.
359
+ *
360
+ * @param User_Group $user_group
361
+ *
362
+ * @return array
363
+ */
364
+ public function prepare_links( User_Group $user_group ) {
365
+ $links = [
366
+ 'self' => [
367
+ 'href' => rest_url( "{$this->namespace}/{$this->rest_base}/{$user_group->get_id()}" ),
368
+ ],
369
+
370
+ \ITSEC_Lib_REST::get_link_relation( 'user-matchable-settings' ) => [
371
+ 'href' => rest_url( "{$this->namespace}/user-matchable-settings/{$user_group->get_id()}" ),
372
+ 'embeddable' => true,
373
+ ],
374
+ ];
375
+
376
+ foreach ( $user_group->get_users() as $user ) {
377
+ $links[ \ITSEC_Lib_REST::get_link_relation( 'user-group-member' ) ][] = [
378
+ 'href' => rest_url( "wp/v2/users/{$user->ID}" ),
379
+ 'embeddable' => true
380
+ ];
381
+ }
382
+
383
+ return $links;
384
+ }
385
+
386
+ public function get_item_schema() {
387
+ if ( ! empty( $this->schema ) && ! \ITSEC_Core::is_test_suite( 'wpunit' ) ) {
388
+ return $this->schema;
389
+ }
390
+
391
+ $schema = [
392
+ '$schema' => 'http://json-schema.org/draft-04/schema#',
393
+ 'title' => 'ithemes-security-user-group',
394
+ 'type' => 'object',
395
+ 'properties' => [
396
+ 'id' => [
397
+ 'type' => 'string',
398
+ 'readonly' => true,
399
+ 'context' => [ 'view', 'edit', 'embed' ],
400
+ ],
401
+ 'label' => [
402
+ 'type' => 'string',
403
+ 'minLength' => 1,
404
+ 'required' => true,
405
+ 'context' => [ 'view', 'edit', 'embed' ],
406
+ ],
407
+ 'description' => [
408
+ 'type' => 'string',
409
+ 'readonly' => true,
410
+ 'context' => [ 'view', 'edit', 'embed' ],
411
+ ],
412
+ 'users' => [
413
+ 'type' => 'array',
414
+ 'items' => [
415
+ 'type' => 'integer',
416
+ 'minimum' => 0,
417
+ ],
418
+ 'uniqueItems' => true,
419
+ 'context' => [ 'view', 'edit' ],
420
+ ],
421
+ 'roles' => [
422
+ 'type' => 'array',
423
+ 'items' => [
424
+ 'type' => 'string',
425
+ 'enum' => array_keys( wp_roles()->get_names() ),
426
+ ],
427
+ 'context' => [ 'view', 'edit' ],
428
+ ],
429
+ 'canonical' => [
430
+ 'type' => 'array',
431
+ 'items' => [
432
+ 'type' => 'string',
433
+ 'enum' => \ITSEC_Lib_Canonical_Roles::get_canonical_roles( is_multisite() ),
434
+ ],
435
+ 'arg_options' => [
436
+ 'validate_callback' => static function ( $value ) {
437
+ return rest_validate_value_from_schema( $value, [
438
+ 'type' => 'array',
439
+ 'items' => [
440
+ 'type' => 'string',
441
+ 'enum' => \ITSEC_Lib_Canonical_Roles::get_canonical_roles(),
442
+ ],
443
+ ] );
444
+ },
445
+ ],
446
+ ],
447
+ 'min_role' => [
448
+ 'type' => 'string',
449
+ 'enum' => array_merge( [ '' ], array_keys( wp_roles()->get_names() ) ),
450
+ 'context' => [ 'view', 'edit' ],
451
+ ],
452
+ ]
453
+ ];
454
+
455
+ if ( isset( $this->schema ) ) {
456
+ $this->schema = $schema;
457
+ }
458
+
459
+ return $schema;
460
+ }
461
+
462
+ public function get_collection_params() {
463
+ return [
464
+ 'context' => $this->get_context_param( [ 'default' => 'view' ] ),
465
+ ];
466
+ }
467
+ }
core/modules/user-groups/REST/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/Repository/DB_Repository.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+ use iThemesSecurity\Exception\WP_Error;
7
+ use iThemesSecurity\User_Groups\User_Group;
8
+
9
+ final class DB_Repository implements Repository {
10
+
11
+ /** @var \wpdb */
12
+ private $wpdb;
13
+
14
+ /**
15
+ * DB_Repository constructor.
16
+ *
17
+ * @param \wpdb $wpdb
18
+ */
19
+ public function __construct( \wpdb $wpdb ) { $this->wpdb = $wpdb; }
20
+
21
+ public function next_id() {
22
+ return wp_generate_uuid4();
23
+ }
24
+
25
+ public function get( $id ) {
26
+ if ( ! is_string( $id ) ) {
27
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
28
+ }
29
+
30
+ $record = $this->wpdb->get_row( $this->wpdb->prepare(
31
+ "SELECT * FROM {$this->tn()} WHERE group_id = %s",
32
+ $id
33
+ ), ARRAY_A );
34
+
35
+ if ( ! $record ) {
36
+ throw new User_Group_Not_Found( sprintf( __( "No user group found for '%s'", 'better-wp-security' ), $id ) );
37
+ }
38
+
39
+ return $this->hydrate( $record );
40
+ }
41
+
42
+ public function has( $id ) {
43
+ if ( ! is_string( $id ) ) {
44
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
45
+ }
46
+
47
+ return (bool) $this->wpdb->get_var( $this->wpdb->prepare(
48
+ "SELECT count(1) FROM {$this->tn()} WHERE group_id = %s",
49
+ $id
50
+ ) );
51
+ }
52
+
53
+ public function persist( User_Group $user_group ) {
54
+ $this->wpdb->query( $this->wpdb->prepare(
55
+ "INSERT INTO {$this->tn()} (group_id, group_label, group_roles, group_canonical, group_users, group_min_role, group_created_at) VALUES (%s, %s, %s, %s, %s, %s, %s)" .
56
+ 'ON DUPLICATE KEY UPDATE group_label = VALUES(group_label), group_roles = VALUES(group_roles), group_canonical = VALUES(group_canonical), group_users = VALUES(group_users), group_min_role = VALUES(group_min_role)',
57
+ $user_group->get_id(),
58
+ $user_group->get_label(),
59
+ implode( ',', $user_group->get_roles() ),
60
+ implode( ',', $user_group->get_canonical_roles() ),
61
+ implode( ',', wp_list_pluck( $user_group->get_users(), 'ID' ) ),
62
+ $user_group->get_min_role(),
63
+ date( 'Y-m-d H:i:s' )
64
+ ) );
65
+
66
+ // When a on duplicate key update updates a record, the number of affected rows is 2 instead of 1 for creating.
67
+ $affected = $this->wpdb->rows_affected;
68
+
69
+ if ( $affected === 1 ) {
70
+ /**
71
+ * Fires when a user group is created.
72
+ *
73
+ * @since 6.4.0
74
+ *
75
+ * @param User_Group $user_group
76
+ */
77
+ do_action( 'itsec_create_user_group', $user_group );
78
+ } else {
79
+ /**
80
+ * Fires when a user group is updated.
81
+ *
82
+ * @since 6.4.0
83
+ *
84
+ * @param User_Group $user_group
85
+ */
86
+ do_action( 'itsec_update_user_group', $user_group );
87
+ }
88
+ }
89
+
90
+ public function delete( User_Group $user_group ) {
91
+ $deleted = $this->wpdb->delete( $this->tn(), [ 'group_id' => $user_group->get_id() ] );
92
+
93
+ if ( false === $deleted ) {
94
+ throw WP_Error::from_code( 'itsec-delete-user-group-failed', __( 'Failed to delete group.', 'better-wp-security' ) );
95
+ }
96
+ }
97
+
98
+ public function all() {
99
+ $records = $this->wpdb->get_results( "SELECT * FROM {$this->tn()} ORDER BY group_created_at ASC", ARRAY_A );
100
+
101
+ return array_map( [ $this, 'hydrate' ], $records );
102
+ }
103
+
104
+ /**
105
+ * Hydrate a user group record.
106
+ *
107
+ * @param array $record
108
+ *
109
+ * @return User_Group
110
+ */
111
+ private function hydrate( array $record ) {
112
+ $group = new User_Group( $record['group_id'] );
113
+ $group->set_label( $record['group_label'] );
114
+
115
+ foreach ( wp_parse_list( $record['group_roles'] ) as $role ) {
116
+ if ( get_role( $role ) ) {
117
+ $group->add_role( $role );
118
+ }
119
+ }
120
+
121
+ if ( get_role( $record['group_min_role'] ) ) {
122
+ $group->set_min_role( $record['group_min_role'] );
123
+ }
124
+
125
+ if ( ! empty( $record['group_canonical'] ) ) {
126
+ $group->set_canonical_roles( wp_parse_list( $record['group_canonical'] ) );
127
+ }
128
+
129
+ $group->set_users( array_filter( array_map( static function ( $id ) {
130
+ return get_userdata( $id ) ?: null;
131
+ }, wp_parse_id_list( $record['group_users'] ) ) ) );
132
+
133
+ return $group;
134
+ }
135
+
136
+ private function tn() {
137
+ return $this->wpdb->base_prefix . 'itsec_user_groups';
138
+ }
139
+ }
core/modules/user-groups/Repository/Decorator.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\User_Groups\User_Group;
6
+
7
+ trait Decorator {
8
+
9
+ /** @var Repository */
10
+ private $decorates;
11
+
12
+ public function next_id() {
13
+ return $this->decorates->next_id();
14
+ }
15
+
16
+ public function get( $id ) {
17
+ $this->decorates->get( $id );
18
+ }
19
+
20
+ public function has( $id ) {
21
+ return $this->decorates->has( $id );
22
+ }
23
+
24
+ public function persist( User_Group $user_group ) {
25
+ $this->decorates->persist( $user_group );
26
+ }
27
+
28
+ public function delete( User_Group $user_group ) {
29
+ $this->decorates->delete( $user_group );
30
+ }
31
+
32
+ public function all() {
33
+ return $this->decorates->all();
34
+ }
35
+ }
core/modules/user-groups/Repository/Eager_Loading_Decorator.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+ use iThemesSecurity\User_Groups\User_Group;
7
+
8
+ final class Eager_Loading_Decorator implements Repository {
9
+ use Decorator;
10
+
11
+ /** @var User_Group[] */
12
+ private $all;
13
+
14
+ public function __construct( Repository $decorates ) {
15
+ $this->decorates = $decorates;
16
+ }
17
+
18
+ public function get( $id ) {
19
+ if ( ! is_string( $id ) ) {
20
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
21
+ }
22
+
23
+ $this->all();
24
+
25
+ if ( ! isset( $this->all[ $id ] ) ) {
26
+ throw new User_Group_Not_Found( sprintf( __( "No user group found for '%s'", 'better-wp-security' ), $id ) );
27
+ }
28
+
29
+ return clone $this->all[ $id ];
30
+ }
31
+
32
+ public function has( $id ) {
33
+ if ( ! is_string( $id ) ) {
34
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
35
+ }
36
+
37
+ $this->all();
38
+
39
+ return isset( $this->all[ $id ] );
40
+ }
41
+
42
+ public function persist( User_Group $user_group ) {
43
+ $this->decorates->persist( $user_group );
44
+
45
+ if ( null !== $this->all ) {
46
+ $this->all[ $user_group->get_id() ] = $user_group;
47
+ }
48
+ }
49
+
50
+ public function delete( User_Group $user_group ) {
51
+ $this->decorates->delete( $user_group );
52
+ if ( null !== $this->all ) {
53
+ unset( $this->all[ $user_group->get_id() ] );
54
+ }
55
+ }
56
+
57
+ public function all() {
58
+ if ( null === $this->all ) {
59
+ $this->all = [];
60
+
61
+ foreach ( $this->decorates->all() as $user_group ) {
62
+ $this->all[ $user_group->get_id() ] = $user_group;
63
+ }
64
+ }
65
+
66
+ return array_map( 'wp_clone', array_values( $this->all ) );
67
+ }
68
+ }
core/modules/user-groups/Repository/In_Memory_Repository.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+ use iThemesSecurity\User_Groups\User_Group;
7
+
8
+ final class In_Memory_Repository implements Repository {
9
+ /** @var array */
10
+ private $memory;
11
+
12
+ /**
13
+ * In_Memory_Repository constructor.
14
+ *
15
+ * @param array $memory
16
+ */
17
+ public function __construct( array $memory = [] ) { $this->memory = $memory; }
18
+
19
+ public function next_id() {
20
+ return wp_generate_uuid4();
21
+ }
22
+
23
+ public function get( $id ) {
24
+ if ( ! is_string( $id ) ) {
25
+ throw new Invalid_Argument_Exception( __( 'uuid must be a string.', 'better-wp-security' ) );
26
+ }
27
+
28
+ if ( ! $this->has( $id ) ) {
29
+ throw new User_Group_Not_Found( sprintf( __( "No user group found for '%s'", 'better-wp-security' ), $id ) );
30
+ }
31
+
32
+ return clone $this->memory[ $id ];
33
+ }
34
+
35
+ public function has( $id ) {
36
+ return isset( $this->memory[ $id ] );
37
+ }
38
+
39
+ public function persist( User_Group $user_group ) {
40
+ $created = empty( $this->memory[ $user_group->get_id() ] );
41
+
42
+ $this->memory[ $user_group->get_id() ] = $user_group;
43
+
44
+ if ( $created ) {
45
+ /**
46
+ * Fires when a user group is created.
47
+ *
48
+ * @since 6.4.0
49
+ *
50
+ * @param User_Group $user_group
51
+ */
52
+ do_action( 'itsec_create_user_group', $user_group );
53
+ } else {
54
+ /**
55
+ * Fires when a user group is updated.
56
+ *
57
+ * @since 6.4.0
58
+ *
59
+ * @param User_Group $user_group
60
+ */
61
+ do_action( 'itsec_update_user_group', $user_group );
62
+ }
63
+ }
64
+
65
+ public function delete( User_Group $user_group ) {
66
+ unset( $this->memory[ $user_group->get_id() ] );
67
+ }
68
+
69
+ public function all() {
70
+ return array_values( $this->memory );
71
+ }
72
+ }
core/modules/user-groups/Repository/Object_Caching_Decorator.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+ use iThemesSecurity\User_Groups\User_Group;
7
+
8
+ final class Object_Caching_Decorator implements Repository {
9
+
10
+ use Decorator;
11
+
12
+ const GROUP = 'itsec-user-group';
13
+
14
+ /**
15
+ * Caching_Decorator constructor.
16
+ *
17
+ * @param Repository $decorates
18
+ */
19
+ public function __construct( Repository $decorates ) { $this->decorates = $decorates; }
20
+
21
+ public function get( $id ) {
22
+ if ( ! is_string( $id ) ) {
23
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
24
+ }
25
+
26
+ $serialized = wp_cache_get( $id, self::GROUP );
27
+
28
+ if ( $serialized ) {
29
+ return $this->hydrate( $id, $serialized );
30
+ }
31
+
32
+ $user_group = $this->decorates->get( $id );
33
+ $this->cache( $user_group );
34
+
35
+ return $user_group;
36
+ }
37
+
38
+ public function has( $id ) {
39
+ if ( ! is_string( $id ) ) {
40
+ throw new Invalid_Argument_Exception( __( 'Id must be a string.', 'better-wp-security' ) );
41
+ }
42
+
43
+ if ( wp_cache_get( $id, self::GROUP ) ) {
44
+ return true;
45
+ }
46
+
47
+ return $this->decorates->has( $id );
48
+ }
49
+
50
+ public function persist( User_Group $user_group ) {
51
+ $this->decorates->persist( $user_group );
52
+ $this->cache( $user_group );
53
+ }
54
+
55
+ public function delete( User_Group $user_group ) {
56
+ $this->decorates->delete( $user_group );
57
+ wp_cache_delete( $user_group->get_id(), self::GROUP );
58
+ }
59
+
60
+ /**
61
+ * Cache the user group's data.
62
+ *
63
+ * @param User_Group $user_group
64
+ */
65
+ private function cache( User_Group $user_group ) {
66
+ wp_cache_set( $user_group->get_id(), $this->serialize( $user_group ), self::GROUP );
67
+ }
68
+
69
+ /**
70
+ * Serialize a user group to an array.
71
+ *
72
+ * @param User_Group $group
73
+ *
74
+ * @return array
75
+ */
76
+ private function serialize( User_Group $group ) {
77
+ return [
78
+ 'label' => $group->get_label(),
79
+ 'roles' => $group->get_roles(),
80
+ 'canonical' => $group->get_canonical_roles(),
81
+ 'min_role' => $group->get_min_role(),
82
+ 'users' => wp_list_pluck( $group->get_users(), 'ID' ),
83
+ ];
84
+ }
85
+
86
+ /**
87
+ * Hydrate a user group record.
88
+ *
89
+ * @param string $uuid
90
+ * @param array $record
91
+ *
92
+ * @return User_Group
93
+ */
94
+ private function hydrate( $uuid, $record ) {
95
+ $group = new User_Group( $uuid );
96
+ $group->set_label( $record['label'] );
97
+
98
+ foreach ( $record['roles'] as $role ) {
99
+ if ( get_role( $role ) ) {
100
+ $group->add_role( $role );
101
+ }
102
+ }
103
+
104
+ if ( get_role( $record['min_role'] ) ) {
105
+ $group->set_min_role( $record['min_role'] );
106
+ }
107
+
108
+ if ( ! empty( $record['canonical'] ) ) {
109
+ $group->set_canonical_roles( $record['canonical'] );
110
+ }
111
+
112
+ $group->set_users( array_filter( array_map( static function ( $id ) {
113
+ return get_userdata( $id ) ?: null;
114
+ }, $record['users'] ) ) );
115
+
116
+ return $group;
117
+ }
118
+ }
core/modules/user-groups/Repository/Repository.php ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\WP_Error;
6
+ use iThemesSecurity\User_Groups\User_Group;
7
+
8
+ interface Repository {
9
+
10
+ /**
11
+ * Get the next id for a user group.
12
+ *
13
+ * @return string
14
+ */
15
+ public function next_id();
16
+
17
+ /**
18
+ * Get a user group by it's id.
19
+ *
20
+ * @param string $id
21
+ *
22
+ * @return User_Group
23
+ * @throws User_Group_Not_Found
24
+ */
25
+ public function get( $id );
26
+
27
+ /**
28
+ * Does the repository have a user group identified by the uuid.
29
+ *
30
+ * @param string $id
31
+ *
32
+ * @return bool
33
+ */
34
+ public function has( $id );
35
+
36
+ /**
37
+ * Persist a user group.
38
+ *
39
+ * @param User_Group $user_group
40
+ *
41
+ * @return void
42
+ *
43
+ * @throws WP_Error
44
+ */
45
+ public function persist( User_Group $user_group );
46
+
47
+ /**
48
+ * Delete a user group.
49
+ *
50
+ * @param User_Group $user_group
51
+ *
52
+ * @return void
53
+ */
54
+ public function delete( User_Group $user_group );
55
+
56
+ /**
57
+ * Get a list of all the user groups.
58
+ *
59
+ * @return User_Group[]
60
+ */
61
+ public function all();
62
+ }
core/modules/user-groups/Repository/User_Group_Not_Found.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups\Repository;
4
+
5
+ use iThemesSecurity\Exception\Exception;
6
+
7
+ final class User_Group_Not_Found extends \OutOfBoundsException implements Exception {
8
+
9
+ }
core/modules/user-groups/Repository/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/Settings/Settings_Proxy.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ final class Settings_Proxy {
6
+
7
+ /**
8
+ * Check if a matchable has the given setting enabled.
9
+ *
10
+ * @param Matchable $matchable The matchable to check for.
11
+ * @param Settings_Registration $registration The setting to check.
12
+ *
13
+ * @return bool
14
+ */
15
+ public function is_enabled( Matchable $matchable, Settings_Registration $registration ) {
16
+ $value = \ITSEC_Lib::array_get( \ITSEC_Modules::get_settings( $registration->get_module() ), $registration->get_setting() );
17
+
18
+ return in_array( $matchable->get_id(), $value, true );
19
+ }
20
+
21
+ /**
22
+ * Set whether a setting is enabled.
23
+ *
24
+ * @param Matchable $matchable The matchable to enable/disable the setting for.
25
+ * @param Settings_Registration $registration The setting.
26
+ * @param bool $enabled Whether the setting should be enabled.
27
+ *
28
+ * @return \WP_Error|null Null on success, WP_Error on error.
29
+ */
30
+ public function set_enabled( Matchable $matchable, Settings_Registration $registration, $enabled = true ) {
31
+ $settings = \ITSEC_Modules::get_settings( $registration->get_module() );
32
+ $current = \ITSEC_Lib::array_get( $settings, $registration->get_setting() );
33
+
34
+ if ( $enabled && ! in_array( $matchable->get_id(), $current, true ) ) {
35
+ $current[] = $matchable->get_id();
36
+ } elseif ( ! $enabled && ( $i = array_search( $matchable->get_id(), $current, true ) ) !== false ) {
37
+ unset( $current[ $i ] );
38
+ }
39
+
40
+ $settings = \ITSEC_Lib::array_set( $settings, $registration->get_setting(), $current );
41
+
42
+ $updated = \ITSEC_Modules::set_settings( $registration->get_module(), $settings );
43
+
44
+ return \ITSEC_Lib::updated_settings_to_wp_error( $updated );
45
+ }
46
+ }
core/modules/user-groups/Settings/Settings_Registration.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use iThemesSecurity\Exception\Invalid_Argument_Exception;
6
+
7
+ final class Settings_Registration {
8
+
9
+ const T_SINGLE = 'single';
10
+ const T_MULTIPLE = 'multiple';
11
+
12
+ /** @var string */
13
+ private $module;
14
+
15
+ /** @var string */
16
+ private $setting;
17
+
18
+ /** @var string */
19
+ private $type;
20
+
21
+ /** @var callable */
22
+ private $labels;
23
+
24
+ /** @var string */
25
+ private $generated;
26
+
27
+ /**
28
+ * Settings_Registration constructor.
29
+ *
30
+ * @param string $module
31
+ * @param string $setting
32
+ * @param string $type
33
+ * @param callable $labels
34
+ */
35
+ public function __construct( $module, $setting, $type, callable $labels ) {
36
+ if ( ! in_array( $type, [ self::T_SINGLE, self::T_MULTIPLE ], true ) ) {
37
+ throw new Invalid_Argument_Exception( __( 'Type must be either single or multiple.', 'better-wp-security' ) );
38
+ }
39
+
40
+ $this->module = $module;
41
+ $this->setting = $setting;
42
+ $this->type = $type;
43
+ $this->labels = $labels;
44
+ }
45
+
46
+ /**
47
+ * Get the slug of the module.
48
+ *
49
+ * @return string
50
+ */
51
+ public function get_module() {
52
+ return $this->module;
53
+ }
54
+
55
+ /**
56
+ * Get the slug of the setting.
57
+ *
58
+ * @return string
59
+ */
60
+ public function get_setting() {
61
+ return $this->setting;
62
+ }
63
+
64
+ /**
65
+ * Get the setting type.
66
+ *
67
+ * @return string
68
+ */
69
+ public function get_type() {
70
+ return $this->type;
71
+ }
72
+
73
+ /**
74
+ * Get the labels for this setting.
75
+ *
76
+ * Has 'title' and 'description' keys.
77
+ *
78
+ * @return string[]
79
+ */
80
+ public function get_labels() {
81
+ if ( ! $this->generated ) {
82
+ $this->generated = call_user_func( $this->labels );
83
+ }
84
+
85
+ return $this->generated;
86
+ }
87
+ }
core/modules/user-groups/Settings/Settings_Registry.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ final class Settings_Registry {
6
+
7
+ /** @var Settings_Registration[] */
8
+ private $settings = [];
9
+
10
+ /**
11
+ * Register the setting.
12
+ *
13
+ * @param Settings_Registration $registration
14
+ */
15
+ public function register( Settings_Registration $registration ) {
16
+ $this->settings[] = $registration;
17
+ }
18
+
19
+ /**
20
+ * Unregister a setting.
21
+ *
22
+ * @param string $module
23
+ * @param string $setting
24
+ *
25
+ * @return bool True if the setting was unregistered.
26
+ */
27
+ public function unregister( $module, $setting ) {
28
+ foreach ( $this->settings as $i => $registration ) {
29
+ if ( $registration->get_module() === $module && $registration->get_setting() === $setting ) {
30
+ unset( $this->settings[ $i ] );
31
+ $this->settings = array_values( $this->settings );
32
+
33
+ return true;
34
+ }
35
+ }
36
+
37
+ return false;
38
+ }
39
+
40
+ /**
41
+ * Get a list of all the settings.
42
+ *
43
+ * @return Settings_Registration[]
44
+ */
45
+ public function get_settings() {
46
+ return $this->settings;
47
+ }
48
+
49
+ /**
50
+ * Find a setting registration by module and setting.
51
+ *
52
+ * @param string $module
53
+ * @param string $setting
54
+ *
55
+ * @return Settings_Registration|null
56
+ */
57
+ public function find( $module, $setting ) {
58
+ foreach ( $this->get_settings() as $registration ) {
59
+ if ( $registration->get_module() === $module && $registration->get_setting() === $setting ) {
60
+ return $registration;
61
+ }
62
+ }
63
+
64
+ return null;
65
+ }
66
+
67
+ /**
68
+ * Clear all registered settings.
69
+ */
70
+ public function clear_settings() {
71
+ $this->settings = [];
72
+ }
73
+ }
core/modules/user-groups/Settings/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/Upgrader.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use iThemesSecurity\User_Groups\Module\Settings;
6
+ use iThemesSecurity\User_Groups\Repository\Repository;
7
+ use ITSEC_Lib_Canonical_Roles;
8
+
9
+ final class Upgrader {
10
+
11
+ /** @var Repository */
12
+ private $user_groups;
13
+
14
+ /** @var Settings */
15
+ private $settings;
16
+
17
+ /**
18
+ * Upgrader constructor.
19
+ *
20
+ * @param Repository $user_groups
21
+ * @param Settings $settings
22
+ */
23
+ public function __construct( Repository $user_groups, Settings $settings ) {
24
+ $this->user_groups = $user_groups;
25
+ $this->settings = $settings;
26
+ }
27
+
28
+ /**
29
+ * Upgrade an existing setting to a user group.
30
+ *
31
+ * @param string $label Label to give the user group if a new one has to be created.
32
+ * @param callable $configure Callable to configure the user group.
33
+ *
34
+ * @return User_Group Either the newly created user group, or an existing user group that was an exact match.
35
+ */
36
+ public function find_or_create( $label, callable $configure ) {
37
+ $user_group = new User_Group( $this->user_groups->next_id() );
38
+ $configure( $user_group );
39
+
40
+ foreach ( $this->user_groups->all() as $maybe_group ) {
41
+ if ( $maybe_group->equals( $user_group ) ) {
42
+ return $maybe_group;
43
+ }
44
+ }
45
+
46
+ $user_group->set_label( $label );
47
+ $this->user_groups->persist( $user_group );
48
+
49
+ return $user_group;
50
+ }
51
+
52
+ /**
53
+ * Upgrade a min role based setting to a group based setting.
54
+ *
55
+ * @param string $min_role The minimum canonical role.
56
+ *
57
+ * @return string[] The list of user group IDs to store.
58
+ */
59
+ public function upgrade_from_min_role( $min_role ) {
60
+ $groups = [];
61
+
62
+ foreach ( ITSEC_Lib_Canonical_Roles::get_canonical_roles_of_at_least( $min_role ) as $role ) {
63
+ if ( 'super-admin' === $role ) {
64
+ continue;
65
+ }
66
+
67
+ $groups[] = $this->settings->get_default_group_id( $role, true );
68
+ }
69
+
70
+ if ( 'subscriber' === $min_role ) {
71
+ $groups[] = 'everybody-else';
72
+ }
73
+
74
+ return $groups;
75
+ }
76
+ }
core/modules/user-groups/User_Group.php ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ use InvalidArgumentException;
6
+ use WP_User;
7
+ use ITSEC_Lib;
8
+ use ITSEC_Lib_Canonical_Roles;
9
+
10
+ final class User_Group implements Matchable {
11
+
12
+ /** @var string */
13
+ private $id;
14
+
15
+ /** @var string */
16
+ private $label = '';
17
+
18
+ /** @var int[] */
19
+ private $users = [];
20
+
21
+ /** @var string[] */
22
+ private $roles = [];
23
+
24
+ /** @var array */
25
+ private $canonical_roles = [];
26
+
27
+ /** @var string */
28
+ private $min_role = '';
29
+
30
+ /**
31
+ * $this constructor.
32
+ *
33
+ * @param string $id
34
+ */
35
+ public function __construct( $id ) {
36
+ if ( ! wp_is_uuid( $id, 4 ) ) {
37
+ throw new InvalidArgumentException( 'id must be a UUIDv4' );
38
+ }
39
+
40
+ $this->id = $id;
41
+ }
42
+
43
+ /**
44
+ * Get the id.
45
+ *
46
+ * @return string
47
+ */
48
+ public function get_id() {
49
+ return $this->id;
50
+ }
51
+
52
+ /**
53
+ * Add a user to this group.
54
+ *
55
+ * @param WP_User $user
56
+ *
57
+ * @return $this
58
+ */
59
+ public function add_user( WP_User $user ) {
60
+ if ( ! in_array( $user->ID, $this->users, true ) ) {
61
+ $this->users[] = $user->ID;
62
+ }
63
+
64
+ return $this;
65
+ }
66
+
67
+ /**
68
+ * Remove a user from this group.
69
+ *
70
+ * @param WP_User $user
71
+ *
72
+ * @return $this
73
+ */
74
+ public function remove_user( WP_User $user ) {
75
+ if ( ( $i = array_search( $user->ID, $this->users, true ) ) !== false ) {
76
+ unset( $this->users[ $i ] );
77
+ }
78
+
79
+ return $this;
80
+ }
81
+
82
+ /**
83
+ * Set the list of users to contain in this group.
84
+ *
85
+ * @param WP_User[] $users
86
+ *
87
+ * @return $this
88
+ */
89
+ public function set_users( array $users ) {
90
+ $this->users = [];
91
+ array_walk( $users, [ $this, 'add_user' ] );
92
+
93
+ return $this;
94
+ }
95
+
96
+ /**
97
+ * Get the list of users contained in this group.
98
+ *
99
+ * @return WP_User[]
100
+ */
101
+ public function get_users() {
102
+ return array_filter( array_map( 'get_userdata', $this->users ), static function ( $user ) {
103
+ return $user instanceof WP_User;
104
+ } );
105
+ }
106
+
107
+ /**
108
+ * Add a role to the user group.
109
+ *
110
+ * @param string $role
111
+ *
112
+ * @return $this
113
+ */
114
+ public function add_role( $role ) {
115
+ if ( ! get_role( $role ) ) {
116
+ throw new InvalidArgumentException( sprintf( 'Role %s not found.', $role ) );
117
+ }
118
+
119
+ $this->roles[] = $role;
120
+
121
+ return $this;
122
+ }
123
+
124
+ /**
125
+ * Remove a role from the user group.
126
+ *
127
+ * @param string $role
128
+ *
129
+ * @return $this
130
+ */
131
+ public function remove_role( $role ) {
132
+ if ( ( $i = array_search( $role, $this->roles, true ) ) !== false ) {
133
+ unset( $this->roles[ $i ] );
134
+ }
135
+
136
+ return $this;
137
+ }
138
+
139
+ /**
140
+ * Set the list of roles for the user group.
141
+ *
142
+ * @param string[] $roles
143
+ *
144
+ * @return $this
145
+ */
146
+ public function set_roles( array $roles ) {
147
+ $this->roles = [];
148
+ array_walk( $roles, [ $this, 'add_role' ] );
149
+
150
+ return $this;
151
+ }
152
+
153
+ /**
154
+ * Get the list of roles for the user group.
155
+ *
156
+ * @return string[]
157
+ */
158
+ public function get_roles() {
159
+ return $this->roles;
160
+ }
161
+
162
+ /**
163
+ * Add a canonical role to the user group.
164
+ *
165
+ * @param string $role
166
+ *
167
+ * @return $this
168
+ */
169
+ public function add_canonical_role( $role ) {
170
+ if ( ! in_array( $role, ITSEC_Lib_Canonical_Roles::get_canonical_roles(), true ) ) {
171
+ throw new InvalidArgumentException( sprintf( 'Role %s is not a valid canonical role.', $role ) );
172
+ }
173
+
174
+ $this->canonical_roles[] = $role;
175
+
176
+ return $this;
177
+ }
178
+
179
+ /**
180
+ * Remove a canonical role from the user group.
181
+ *
182
+ * @param string $role
183
+ *
184
+ * @return $this
185
+ */
186
+ public function remove_canonical_role( $role ) {
187
+ if ( ( $i = array_search( $role, $this->canonical_roles, true ) ) !== false ) {
188
+ unset( $this->canonical_roles[ $i ] );
189
+ }
190
+
191
+ return $this;
192
+ }
193
+
194
+ /**
195
+ * Set the list of roles for the user group.
196
+ *
197
+ * @param string[] $roles
198
+ *
199
+ * @return $this
200
+ */
201
+ public function set_canonical_roles( array $roles ) {
202
+ $this->canonical_roles = [];
203
+ array_walk( $roles, [ $this, 'add_canonical_role' ] );
204
+
205
+ return $this;
206
+ }
207
+
208
+ /**
209
+ * Get the list of canonical roles for the user group.
210
+ *
211
+ * @return string[]
212
+ */
213
+ public function get_canonical_roles() {
214
+ return $this->canonical_roles;
215
+ }
216
+
217
+ /**
218
+ * Get the role that users must be greater than for the group to apply.
219
+ *
220
+ * @return string
221
+ */
222
+ public function get_min_role() {
223
+ return $this->min_role;
224
+ }
225
+
226
+ /**
227
+ * Set the role that users must be greater than for the group to apply.
228
+ *
229
+ * @param string $role
230
+ *
231
+ * @return $this
232
+ */
233
+ public function set_min_role( $role ) {
234
+ if ( '' !== $role && ! get_role( $role ) ) {
235
+ throw new InvalidArgumentException( sprintf( 'Role %s not found.', $role ) );
236
+ }
237
+
238
+ $this->min_role = $role;
239
+
240
+ return $this;
241
+ }
242
+
243
+ /**
244
+ * Get the user provided label for this user group.
245
+ *
246
+ * @return string
247
+ */
248
+ public function get_label() {
249
+ return $this->label;
250
+ }
251
+
252
+ /**
253
+ * Set the label for this user group.
254
+ *
255
+ * @param string $label
256
+ *
257
+ * @return $this
258
+ */
259
+ public function set_label( $label ) {
260
+ if ( ! is_string( $label ) ) {
261
+ throw new InvalidArgumentException( '$label must be a string.' );
262
+ }
263
+
264
+ $this->label = $label;
265
+
266
+ return $this;
267
+ }
268
+
269
+ /**
270
+ * Get the generated description for this user group.
271
+ *
272
+ * @return string
273
+ */
274
+ public function get_description() {
275
+ $parts = [];
276
+
277
+ if ( $roles = $this->get_roles() ) {
278
+ $parts[] = wp_sprintf( __( 'the following roles: %l', 'better-wp-security' ), array_map( 'translate_user_role', $roles ) );
279
+ }
280
+
281
+ if ( $canonical = $this->get_canonical_roles() ) {
282
+ $parts[] = wp_sprintf( __( 'the following canonical roles: %l', 'better-wp-security' ), array_map( 'translate_user_role', $canonical ) );
283
+ }
284
+
285
+ if ( $min_role = $this->get_min_role() ) {
286
+ $parts[] = sprintf( __( 'roles greater than %s', 'better-wp-security' ), translate_user_role( $min_role ) );
287
+ }
288
+
289
+ if ( $users = $this->get_users() ) {
290
+ $parts[] = wp_sprintf( __( 'the following users: %l', 'better-wp-security' ), wp_list_pluck( $users, 'user_login' ) );
291
+ }
292
+
293
+ if ( ! $parts ) {
294
+ return wp_sprintf( __( 'This user group contains no users.', 'better-wp-security' ) );
295
+ }
296
+
297
+ return wp_sprintf( __( 'This user group contains %l.', 'better-wp-security' ), $parts );
298
+ }
299
+
300
+ /**
301
+ * Get the computed list of roles this user group applies to.
302
+ *
303
+ * This is the manually selected list of roles, as well as all roles greater
304
+ * than the minimum role.
305
+ *
306
+ * @return string[]
307
+ */
308
+ public function get_computed_role_list() {
309
+ ITSEC_Lib::load( 'canonical-roles' );
310
+ $roles = $this->get_roles();
311
+ $min_role = $this->get_min_role();
312
+ $canonical_roles = $this->get_canonical_roles();
313
+
314
+ foreach ( wp_roles()->get_names() as $role => $name ) {
315
+ if ( in_array( $role, $roles, true ) ) {
316
+ continue;
317
+ }
318
+
319
+ $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $role );
320
+
321
+ if ( in_array( $canonical, $canonical_roles, true ) ) {
322
+ $roles[] = $role;
323
+ continue;
324
+ }
325
+
326
+ if ( $min_role && ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $min_role, $canonical ) ) {
327
+ $roles[] = $role;
328
+ continue;
329
+ }
330
+ }
331
+
332
+ return $roles;
333
+ }
334
+
335
+ /**
336
+ * Is the user group properly configured.
337
+ *
338
+ * @return bool
339
+ */
340
+ public function is_configured() {
341
+ return $this->min_role || $this->roles || $this->users || $this->canonical_roles;
342
+ }
343
+
344
+ /**
345
+ * Does the given user match this user group.
346
+ *
347
+ * @param Match_Target $target
348
+ *
349
+ * @return bool
350
+ */
351
+ public function matches( Match_Target $target ) {
352
+ if ( ( $user = $target->get_user() ) && in_array( $user->ID, $this->users, true ) ) {
353
+ return true;
354
+ }
355
+
356
+ if ( array_intersect( $this->get_roles(), $target->get_role() ? [ $target->get_role() ] : $user->roles ) ) {
357
+ return true;
358
+ }
359
+
360
+ $min_role = $this->get_min_role();
361
+ $canonical_roles = $this->get_canonical_roles();
362
+
363
+ if ( $min_role || $canonical_roles ) {
364
+ ITSEC_Lib::load( 'canonical-roles' );
365
+
366
+ if ( $target->get_role() && $target->get_user() ) {
367
+ $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role_and_user( $target->get_role(), $target->get_user() );
368
+ } elseif ( $target->get_role() ) {
369
+ $canonical = ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $target->get_role() );
370
+ } elseif ( $target->get_user() ) {
371
+ $canonical = ITSEC_Lib_Canonical_Roles::get_user_role( $target->get_user() );
372
+ } else {
373
+ $canonical = '';
374
+ }
375
+
376
+ if ( $canonical ) {
377
+ if ( $min_role && ITSEC_Lib_Canonical_Roles::is_canonical_role_at_least( $min_role, $canonical ) ) {
378
+ return true;
379
+ }
380
+
381
+ if ( $canonical_roles && in_array( $canonical, $canonical_roles, true ) ) {
382
+ return true;
383
+ }
384
+ }
385
+ }
386
+
387
+ return false;
388
+ }
389
+
390
+ /**
391
+ * Is this user group the same record as the given user group.
392
+ *
393
+ * @param User_Group $user_group
394
+ *
395
+ * @return bool
396
+ */
397
+ public function identical( User_Group $user_group ) {
398
+ return $this->get_id() === $user_group->get_id();
399
+ }
400
+
401
+ /**
402
+ * Is this user group made up of the same restrictions as the given user group.
403
+ *
404
+ * @param User_Group $user_group
405
+ *
406
+ * @return bool
407
+ */
408
+ public function equals( User_Group $user_group ) {
409
+ if ( $this->get_min_role() !== $user_group->get_min_role() ) {
410
+ return false;
411
+ }
412
+
413
+ if ( ! ITSEC_Lib::equal_sets( $this->get_roles(), $user_group->get_roles() ) ) {
414
+ return false;
415
+ }
416
+
417
+ if ( ! ITSEC_Lib::equal_sets( $this->get_canonical_roles(), $user_group->get_canonical_roles() ) ) {
418
+ return false;
419
+ }
420
+
421
+ if ( ! ITSEC_Lib::equal_sets( $this->users, $user_group->users ) ) {
422
+ return false;
423
+ }
424
+
425
+ return true;
426
+ }
427
+
428
+ /**
429
+ * Converts the user group to a string.
430
+ *
431
+ * @return string
432
+ */
433
+ public function __toString() {
434
+ return $this->get_label();
435
+ }
436
+ }
core/modules/user-groups/container.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use iThemesSecurity\User_Groups;
4
+ use iThemesSecurity\User_Groups\REST;
5
+ use Pimple\Container;
6
+
7
+ return static function ( Container $c ) {
8
+ $c['module.user-groups.files'] = [
9
+ 'active.php' => User_Groups\Module\Module::class,
10
+ 'settings.php' => User_Groups\Module\Settings::class,
11
+ 'validator.php' => User_Groups\Module\Validator::class,
12
+ 'rest.php' => REST\REST::class,
13
+ ];
14
+
15
+ $c[ User_Groups\Module\Settings::class ] = static function ( Container $c ) {
16
+ return new User_Groups\Module\Settings( $c[ User_Groups\Repository\Repository::class ] );
17
+ };
18
+
19
+ $c[ User_Groups\Module\Validator::class ] = static function ( Container $c ) {
20
+ return new User_Groups\Module\Validator();
21
+ };
22
+
23
+ $c[ User_Groups\Module\Module::class ] = static function ( Container $c ) {
24
+ return new User_Groups\Module\Module(
25
+ $c[ User_Groups\Repository\Repository::class ],
26
+ $c[ User_Groups\Settings_Registry::class ],
27
+ $c[ User_Groups\Settings_Proxy::class ]
28
+ );
29
+ };
30
+
31
+ $c[ User_Groups\Repository\Repository::class ] = static function ( Container $c ) {
32
+ return new User_Groups\Repository\Eager_Loading_Decorator( $c[ User_Groups\Repository\DB_Repository::class ] );
33
+ };
34
+
35
+ $c[ User_Groups\Repository\DB_Repository::class ] = static function ( Container $c ) {
36
+ return new User_Groups\Repository\DB_Repository( $GLOBALS['wpdb'] );
37
+ };
38
+
39
+ $c[ User_Groups\Upgrader::class ] = static function ( Container $c ) {
40
+ return new User_Groups\Upgrader(
41
+ $c[ User_Groups\Repository\Repository::class ],
42
+ $c[ User_Groups\Module\Settings::class ]
43
+ );
44
+ };
45
+
46
+ $c[ User_Groups\Matcher::class ] = static function ( Container $c ) {
47
+ return $c[ User_Groups\Default_Matcher::class ];
48
+ };
49
+
50
+ $c[ User_Groups\Default_Matcher::class ] = static function ( Container $c ) {
51
+ return new User_Groups\Default_Matcher( $c[ User_Groups\Matchables_Source::class ] );
52
+ };
53
+
54
+ $c[ User_Groups\Matchables_Source::class ] = static function ( Container $c ) {
55
+ return ( new User_Groups\Matchables_Source( $c[ User_Groups\Repository\Repository::class ] ) )
56
+ ->add( new User_Groups\Everybody_Else( $c[ User_Groups\Repository\Repository::class ] ) );
57
+ };
58
+
59
+ $c[ User_Groups\Settings_Registry::class ] = static function ( Container $c ) {
60
+ return new User_Groups\Settings_Registry();
61
+ };
62
+
63
+ $c[ User_Groups\Settings_Proxy::class ] = static function () {
64
+ return new User_Groups\Settings_Proxy();
65
+ };
66
+
67
+ $c[ User_Groups\REST\REST::class ] = static function ( Container $c ) {
68
+ return new User_Groups\REST\REST( array(
69
+ $c[ REST\User_Groups::class ],
70
+ $c[ REST\Matchables::class ],
71
+ $c[ REST\Settings::class ],
72
+ ) );
73
+ };
74
+
75
+ $c[ REST\User_Groups::class ] = static function ( Container $c ) {
76
+ return new REST\User_Groups( $c[ User_Groups\Repository\Repository::class ] );
77
+ };
78
+
79
+ $c[ REST\Matchables::class ] = static function ( Container $c ) {
80
+ return new REST\Matchables( $c[ User_Groups\Matchables_Source::class ] );
81
+ };
82
+
83
+ $c[ REST\Settings::class ] = static function ( Container $c ) {
84
+ return new REST\Settings(
85
+ $c[ User_Groups\Matchables_Source::class ],
86
+ $c[ User_Groups\Settings_Registry::class ],
87
+ $c[ User_Groups\Settings_Proxy::class ]
88
+ );
89
+ };
90
+ };
core/modules/user-groups/entries/api.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ import store from './store';
2
+
3
+ export { store };
core/modules/user-groups/entries/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { setLocaleData } from '@wordpress/i18n';
5
+ import { render } from '@wordpress/element';
6
+ import domReady from '@wordpress/dom-ready';
7
+
8
+ // Silence warnings until JS i18n is stable.
9
+ setLocaleData( { '': {} }, 'ithemes-security-pro' );
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import App from './settings/app.js';
15
+
16
+ domReady( () => {
17
+ init();
18
+
19
+ if ( window.itsecSettingsPage ) {
20
+ window.itsecSettingsPage.events.on( 'modulesReloaded', init );
21
+ window.itsecSettingsPage.events.on( 'moduleReloaded', function( _, module ) {
22
+ if ( 'user-groups' === module ) {
23
+ init();
24
+ }
25
+ } );
26
+ }
27
+ } );
28
+
29
+ function init() {
30
+ const containerEl = document.getElementById( 'itsec-user-groups-settings-root' );
31
+ const noticeEl = document.getElementById( 'itsec-module-messages-container-user-groups' );
32
+
33
+ return render(
34
+ <App noticeEl={ noticeEl } />,
35
+ containerEl,
36
+ );
37
+ }
core/modules/user-groups/entries/settings/app.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import '@wordpress/notices';
5
+ import { createPortal } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import '@ithemes/security-data';
11
+ import '@ithemes/security.user-groups.api';
12
+ import { ModuleSettingsNoticeList } from '@ithemes/security-components';
13
+ import './store';
14
+ import { Layout } from './components';
15
+ import './style.scss';
16
+
17
+ function App( { noticeEl } ) {
18
+ return (
19
+ <div className="itsec-user-groups-app">
20
+ { createPortal( <ModuleSettingsNoticeList />, noticeEl ) }
21
+ <Layout />
22
+ </div>
23
+ );
24
+ }
25
+
26
+ export default App;
core/modules/user-groups/entries/settings/components/edit-group-fields/index.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { GroupLabel, PanelRoles, PanelUsers, TabBody } from '../';
5
+ import './style.scss';
6
+
7
+ function EditGroupFields( { groupId, disabled } ) {
8
+ return (
9
+ <TabBody.Row name="edit-fields">
10
+ <GroupLabel groupId={ groupId } disabled={ disabled } />
11
+ <PanelRoles groupId={ groupId } disabled={ disabled } />
12
+ <PanelUsers groupId={ groupId } disabled={ disabled } />
13
+ </TabBody.Row>
14
+ );
15
+ }
16
+
17
+ export default EditGroupFields;
core/modules/user-groups/entries/settings/components/edit-group-fields/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/edit-group-fields/style.scss ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ .itsec-user-groups-group-tab__row--edit-fields > .components-base-control:first-child {
2
+ margin-bottom: 16px;
3
+ }
4
+
5
+ .itsec-user-groups-group-tab__row--edit-fields .components-base-control__label {
6
+ font-weight: 600;
7
+ margin-bottom: 1em;
8
+ font-size: 1.2em;
9
+ }
core/modules/user-groups/entries/settings/components/group-header/index.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import './style.scss';
5
+
6
+ export function GroupHeader( { label, children } ) {
7
+ return (
8
+ <div className="itsec-user-group-header">
9
+ <h4 className="itsec-user-group-header__label">{ label }</h4>
10
+ { children }
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export SingleGroupHeader from './single';
16
+ export NewGroupHeader from './new';
17
+ export MultiGroupHeader from './multi';
core/modules/user-groups/entries/settings/components/group-header/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/group-header/multi.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { GroupHeader } from './';
11
+
12
+ function MultiGroupHeader( { label } ) {
13
+ return (
14
+ <GroupHeader label={ label } />
15
+ );
16
+ }
17
+
18
+ export default compose( [
19
+ withSelect( ( select, { groupIds } ) => ( {
20
+ label: groupIds.map( select( 'ithemes-security/user-groups' ).getMatchableLabel ).join( ', ' ),
21
+ } ) ),
22
+ ] )( MultiGroupHeader );
core/modules/user-groups/entries/settings/components/group-header/new.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { compose } from '@wordpress/compose';
6
+ import { withSelect } from '@wordpress/data';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { GroupHeader } from './';
12
+
13
+ function NewGroupHeader( { label } ) {
14
+ if ( ! label || ! label.length ) {
15
+ label = __( 'New Group', 'better-wp-security' );
16
+ }
17
+
18
+ return (
19
+ <GroupHeader label={ label } />
20
+ );
21
+ }
22
+
23
+ export default compose( [
24
+ withSelect( ( select ) => {
25
+ return ( {
26
+ label: select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( 'new', 'label' ),
27
+ } );
28
+ } ),
29
+ ] )( NewGroupHeader );
core/modules/user-groups/entries/settings/components/group-header/single.js ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { GroupHeader } from './';
13
+
14
+ function SingleGroupHeader( { type, label, isDeleting, deleteGroup } ) {
15
+ const canDelete = type === 'user-group';
16
+
17
+ if ( ! label || ! label.length ) {
18
+ label = __( 'Untitled', 'better-wp-security' );
19
+ }
20
+
21
+ return (
22
+ <GroupHeader label={ label }>
23
+ { canDelete && (
24
+ <Button onClick={ deleteGroup } isBusy={ isDeleting } isLink isDestructive>
25
+ { __( 'Delete Group', 'better-wp-security' ) }
26
+ </Button>
27
+ ) }
28
+ </GroupHeader>
29
+ );
30
+ }
31
+
32
+ export default compose( [
33
+ withSelect( ( select, { groupId } ) => {
34
+ const type = select( 'ithemes-security/user-groups' ).getMatchableType( groupId );
35
+ const isDeleting = type === 'user-group' ? select( 'ithemes-security/user-groups' ).isDeleting( groupId ) : false;
36
+
37
+ let label;
38
+
39
+ if ( type === 'user-group' ) {
40
+ label = select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'label' );
41
+ }
42
+
43
+ if ( label === undefined ) {
44
+ label = select( 'ithemes-security/user-groups' ).getMatchableLabel( groupId );
45
+ }
46
+
47
+ return ( {
48
+ type,
49
+ label,
50
+ isDeleting,
51
+ } );
52
+ } ),
53
+ withDispatch( ( dispatch, { groupId } ) => ( {
54
+ deleteGroup() {
55
+ return dispatch( 'ithemes-security/user-groups' ).deleteGroup( groupId );
56
+ },
57
+ } ) ),
58
+ ] )( SingleGroupHeader );
core/modules/user-groups/entries/settings/components/group-header/style.scss ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-user-group-header {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: space-between;
5
+ margin-bottom: 1em;
6
+ height: 1.2em;
7
+ }
8
+
9
+ .itsec-user-group-header__label {
10
+ margin: 0 .5em 0 0;
11
+ font-size: 1.2em;
12
+ line-height: 1.2em;
13
+ text-overflow: ellipsis;
14
+ overflow: hidden;
15
+ white-space: nowrap;
16
+ }
17
+
18
+ .itsec-user-group-header .components-button {
19
+ flex-shrink: 0;
20
+ }
core/modules/user-groups/entries/settings/components/group-label/index.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress Dependencies
3
+ */
4
+ import { TextControl } from '@wordpress/components';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { compose } from '@wordpress/compose';
7
+ import { withSelect, withDispatch } from '@wordpress/data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import './style.scss';
13
+
14
+ function GroupLabel( { label, edit, disabled = false } ) {
15
+ return (
16
+ <TextControl
17
+ label={ __( 'Group Name', 'better-wp-security' ) }
18
+ value={ label }
19
+ maxLength={ 50 }
20
+ disabled={ disabled }
21
+ onChange={ ( newLabel ) => edit( { label: newLabel } ) } />
22
+ );
23
+ }
24
+
25
+ export default compose( [
26
+ withSelect( ( select, { groupId } ) => ( {
27
+ label: select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'label' ) || '',
28
+ } ) ),
29
+ withDispatch( ( dispatch, { groupId } ) => ( {
30
+ edit( edit ) {
31
+ return dispatch( 'ithemes-security/user-groups-editor' ).editGroup( groupId, edit );
32
+ },
33
+ } ) ),
34
+ ] )( GroupLabel );
core/modules/user-groups/entries/settings/components/group-label/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/group-label/style.scss ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ .itsec-user-group-label h4 {
2
+ margin-top: 0;
3
+ }
core/modules/user-groups/entries/settings/components/group-nav/index.js ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import memize from 'memize';
5
+ import { filter } from 'lodash';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { compose } from '@wordpress/compose';
11
+ import { withSelect, withDispatch } from '@wordpress/data';
12
+ import { Icon } from '@wordpress/components';
13
+ import { __ } from '@wordpress/i18n';
14
+ import { Fragment, useEffect } from '@wordpress/element';
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ import { ControlledMultiTabPanel } from '@ithemes/security-components';
20
+ import { ManageGroup, ManageMultipleGroups } from '../';
21
+ import './style.scss';
22
+
23
+ const convertToTabs = memize( ( groups ) => groups
24
+ .sort( ( groupA, groupB ) => {
25
+ if ( groupA.type === groupB.type ) {
26
+ return 0;
27
+ }
28
+
29
+ if ( groupA.type === 'user-group' ) {
30
+ return -1;
31
+ }
32
+
33
+ if ( groupB.type === 'user-group' ) {
34
+ return 1;
35
+ }
36
+
37
+ return 0;
38
+ } )
39
+ .map( ( group ) => ( {
40
+ name: group.id,
41
+ title: group.label,
42
+ className: 'itsec-user-groups-list__item',
43
+ group,
44
+ } ) )
45
+ .concat( {
46
+ name: 'new',
47
+ title: (
48
+ <Fragment>
49
+ <Icon icon="plus" />
50
+ { __( 'New Group', 'better-wp-security' ) }
51
+ </Fragment>
52
+ ),
53
+ className: 'itsec-user-groups-list__item itsec-user-groups-list__item--new',
54
+ allowMultiple: false,
55
+ } )
56
+ );
57
+
58
+ /**
59
+ * Group Navigation component.
60
+ *
61
+ * @return {Element|null}
62
+ */
63
+ function GroupNav( { matchables, resolvingMatchables, selectedGroup, selectGroup } ) {
64
+ useEffect( () => {
65
+ if ( ! resolvingMatchables && matchables.length && selectedGroup.length === 0 ) {
66
+ selectGroup( [ matchables[ 0 ].id ] );
67
+ }
68
+ }, [ resolvingMatchables ] );
69
+
70
+ if ( resolvingMatchables && ! matchables.length ) {
71
+ return null;
72
+ }
73
+
74
+ const tabs = convertToTabs( matchables );
75
+
76
+ return (
77
+ <ControlledMultiTabPanel
78
+ tabs={ tabs }
79
+ selected={ selectedGroup }
80
+ onSelect={ selectGroup }
81
+ allowMultiple
82
+ orientation="vertical"
83
+ className="itsec-user-groups-list"
84
+ >
85
+ { ( selectedTabs ) => {
86
+ if ( selectedTabs.length > 1 ) {
87
+ const groupIds = filter( selectedTabs.map( ( { group } ) => group && group.id ) );
88
+
89
+ return <ManageMultipleGroups groupIds={ groupIds } />;
90
+ }
91
+
92
+ if ( selectedTabs[ 0 ] ) {
93
+ return <ManageGroup groupId={ selectedTabs[ 0 ].name } isNew={ selectedTabs[ 0 ].name === 'new' } />;
94
+ }
95
+
96
+ return null;
97
+ } }
98
+ </ControlledMultiTabPanel>
99
+ );
100
+ }
101
+
102
+ export default compose( [
103
+ withSelect( ( select ) => ( {
104
+ matchables: select( 'ithemes-security/user-groups' ).getMatchables(),
105
+ resolvingMatchables: select( 'core/data' ).isResolving( 'ithemes-security/user-groups', 'getMatchables' ),
106
+ selectedGroup: select( 'ithemes-security/user-groups-editor' ).getSelectedGroup(),
107
+ } ) ),
108
+ withDispatch( ( dispatch ) => ( {
109
+ selectGroup: dispatch( 'ithemes-security/user-groups-editor' ).selectGroup,
110
+ } ) ),
111
+ ] )( GroupNav );
core/modules/user-groups/entries/settings/components/group-nav/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/group-nav/style.scss ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "colors.scss";
2
+
3
+ .itsec-user-groups-list {
4
+ display: grid;
5
+ grid-template-columns: 200px minmax(0, auto);
6
+ grid-gap: 1.25em;
7
+ padding-bottom: 2em;
8
+
9
+ & > .components-tab-panel__tabs {
10
+ display: flex;
11
+ flex-direction: column;
12
+ margin-top: 2.2em; // Matches .itsec-manage-user-group__label
13
+ }
14
+ }
15
+
16
+ .itsec-user-groups-list__item {
17
+ padding: 1em .5em;
18
+ margin: 1.25em 0 0;
19
+ border: 1px solid $dark-gray-200;
20
+ border-radius: 4px;
21
+ text-align: left;
22
+ height: auto;
23
+
24
+ &.is-active {
25
+ border-color: $blue-medium-focus;
26
+ box-shadow: 0 0 0 1px $blue-medium-focus;
27
+ outline: 2px solid transparent;
28
+ }
29
+
30
+ &:first-child {
31
+ margin-top: 0;
32
+ }
33
+
34
+ &:last-child {
35
+ margin-bottom: 0;
36
+ }
37
+ }
38
+
39
+ .itsec-user-groups-list__item--new {
40
+ justify-content: center;
41
+ align-items: center;
42
+
43
+ & .dashicon {
44
+ margin-right: 10px;
45
+ }
46
+ }
core/modules/user-groups/entries/settings/components/index.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export { default as Layout } from './layout';
2
+ export { default as GroupNav } from './group-nav';
3
+ export { default as ManageGroup } from './manage-group';
4
+ export { default as ManageMultipleGroups } from './manage-multiple-groups';
5
+ export { default as GroupLabel } from './group-label';
6
+ export { default as TabBody } from './tab-body';
7
+ export { default as TabCreateGroup } from './tab-create-group';
8
+ export { default as TabEditGroup } from './tab-edit-group';
9
+ export { default as TabSettings } from './tab-settings';
10
+ export { default as TabSettingsBulk } from './tab-settings-bulk';
11
+ export { default as PanelMinimumRole } from './panel-minimum-role';
12
+ export { default as PanelRoles } from './panel-roles';
13
+ export { default as PanelUsers } from './panel-users';
14
+ export { NewGroupHeader, SingleGroupHeader, MultiGroupHeader } from './group-header';
15
+ export { default as EditGroupFields } from './edit-group-fields';
16
+ export { default as SettingsForm } from './settings-form';
core/modules/user-groups/entries/settings/components/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/layout/index.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ import { GroupNav } from '../';
2
+
3
+ export default function Layout() {
4
+ return (
5
+ <div>
6
+ <GroupNav />
7
+ </div>
8
+ );
9
+ }
core/modules/user-groups/entries/settings/components/layout/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/manage-group/index.js ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import memize from 'memize';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __ } from '@wordpress/i18n';
10
+ import { compose } from '@wordpress/compose';
11
+ import { withSelect } from '@wordpress/data';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { TabPanel } from '@ithemes/security-components';
17
+ import { TabCreateGroup, TabEditGroup, TabSettings, NewGroupHeader, SingleGroupHeader } from '../';
18
+ import './style.scss';
19
+
20
+ const getTabs = memize( ( groupId, type ) => {
21
+ if ( groupId === 'new' ) {
22
+ return [
23
+ {
24
+ name: 'create',
25
+ title: __( 'Edit Group', 'better-wp-security' ),
26
+ className: 'itsec-manage-user-group-tabs__tab',
27
+ Component: TabCreateGroup,
28
+ },
29
+ ];
30
+ }
31
+
32
+ const tabs = [
33
+ {
34
+ name: 'settings',
35
+ title: __( 'Features', 'better-wp-security' ),
36
+ className: 'itsec-manage-user-group-tabs__tab',
37
+ Component: TabSettings,
38
+ },
39
+ ];
40
+
41
+ if ( type === 'user-group' ) {
42
+ tabs.push( {
43
+ name: 'edit',
44
+ title: __( 'Edit Group', 'better-wp-security' ),
45
+ className: 'itsec-manage-user-group-tabs__tab',
46
+ Component: TabEditGroup,
47
+ } );
48
+ }
49
+
50
+ return tabs;
51
+ } );
52
+
53
+ function ManageGroup( { groupId, type, isNew } ) {
54
+ return (
55
+ <div className="itsec-manage-user-group">
56
+ { isNew ? <NewGroupHeader /> : <SingleGroupHeader groupId={ groupId } /> }
57
+ <TabPanel tabs={ getTabs( groupId, type ) } className="itsec-manage-user-group-tabs">
58
+ { ( { Component } ) => (
59
+ <Component groupId={ groupId } />
60
+ ) }
61
+ </TabPanel>
62
+ </div>
63
+ );
64
+ }
65
+
66
+ export default compose( [
67
+ withSelect( ( select, { groupId } ) => ( {
68
+ type: select( 'ithemes-security/user-groups' ).getMatchableType( groupId ),
69
+ } ) ),
70
+ ] )( ManageGroup );
core/modules/user-groups/entries/settings/components/manage-group/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/manage-group/style.scss ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "colors.scss";
2
+
3
+ .itsec-manage-user-group-tabs {
4
+ border: 1px solid $dark-gray-200;
5
+ border-radius: 4px;
6
+ }
7
+
8
+ .itsec-manage-user-group-tabs .components-tab-panel__tabs {
9
+ background: $light-gray-200;
10
+ border-radius: 4px 4px 0 0;
11
+ display: flex;
12
+ flex-basis: 0;
13
+ }
14
+
15
+ .itsec-manage-user-group-tabs__tab.components-button {
16
+ flex-grow: 1;
17
+ flex-basis: 0;
18
+ height: 40px;
19
+ padding: 3px 15px 0;
20
+ border-bottom: 1px solid $dark-gray-200;
21
+ border-right: 1px solid $dark-gray-200;
22
+ cursor: pointer;
23
+ font-weight: 400;
24
+ justify-content: center;
25
+ align-items: center;
26
+ text-transform: uppercase;
27
+ outline-offset: -1px;
28
+ border-radius: 0;
29
+
30
+ &:hover {
31
+ box-shadow: none !important;
32
+ }
33
+
34
+ &:first-child {
35
+ border-top-left-radius: 3px;
36
+ }
37
+
38
+ &:last-child {
39
+ border-top-right-radius: 3px;
40
+ border-right: none;
41
+ }
42
+
43
+ &.is-active {
44
+ padding-top: 0;
45
+ background: white;
46
+ border-top: 3px solid $main-blue;
47
+ border-bottom-color: transparent;
48
+ font-weight: 600;
49
+ outline: none;
50
+ }
51
+ }
core/modules/user-groups/entries/settings/components/manage-multiple-groups/index.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import memize from 'memize';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __ } from '@wordpress/i18n';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { TabPanel } from '@ithemes/security-components';
15
+ import { MultiGroupHeader, TabSettingsBulk } from '../';
16
+
17
+ const getTabs = memize( () => ( [
18
+ {
19
+ name: 'settings',
20
+ title: __( 'Features', 'better-wp-security' ),
21
+ className: 'itsec-manage-user-group-tabs__tab',
22
+ Component: TabSettingsBulk,
23
+ },
24
+ ] ) );
25
+
26
+ function ManageMultipleGroups( { groupIds } ) {
27
+ return (
28
+ <div className="itsec-manage-multiple-user-groups">
29
+ <MultiGroupHeader groupIds={ groupIds } />
30
+ <TabPanel tabs={ getTabs() } className="itsec-manage-user-group-tabs">
31
+ { ( { Component } ) => (
32
+ <Component groupIds={ groupIds } />
33
+ ) }
34
+ </TabPanel>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ export default ManageMultipleGroups;
core/modules/user-groups/entries/settings/components/manage-multiple-groups/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/panel-minimum-role/index.js ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { SelectControl } from '@wordpress/components';
5
+ import { __ } from '@wordpress/i18n';
6
+ import { compose } from '@wordpress/compose';
7
+ import { withSelect, withDispatch } from '@wordpress/data';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { getCanonicalRoles } from '@ithemes/security-i18n';
13
+
14
+ function PanelMinimumRole( { minRole, onChange } ) {
15
+ return (
16
+ <div>
17
+ <SelectControl
18
+ options={ getCanonicalRoles() }
19
+ label={ __( 'Minimum Role', 'better-wp-security' ) }
20
+ value={ minRole }
21
+ onChange={ ( newMinRole ) => onChange( { min_role: newMinRole } ) }
22
+ help={ __( 'Add users with the selected minimum role to this group. To edit roles, go to Users in your WordPress Dashboard.', 'better-wp-security' ) } />
23
+ </div>
24
+ );
25
+ }
26
+
27
+ export default compose( [
28
+ withSelect( ( select, { groupId } ) => ( {
29
+ minRole: select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'min_role' ),
30
+ } ) ),
31
+ withDispatch( ( dispatch, { groupId } ) => ( {
32
+ onChange( edit ) {
33
+ return dispatch( 'ithemes-security/user-groups-editor' ).editGroup( groupId, edit );
34
+ },
35
+ } ) ),
36
+ ] )( PanelMinimumRole );
core/modules/user-groups/entries/settings/components/panel-minimum-role/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/panel-roles/index.js ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import memize from 'memize';
5
+ import { without, some, get } from 'lodash';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { compose } from '@wordpress/compose';
11
+ import { withDispatch, withSelect } from '@wordpress/data';
12
+ import { __ } from '@wordpress/i18n';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import { HierarchicalCheckboxControl } from '@ithemes/security-components';
18
+ import { bifurcate } from '@ithemes/security-utils';
19
+
20
+ const toCanonicalGroup = memize( ( availableRoles, includeSuperAdmin ) => {
21
+ const group = [
22
+ {
23
+ value: '$administrator$',
24
+ label: __( 'Administrator Capabilities', 'better-wp-security' ),
25
+ },
26
+ {
27
+ value: '$editor$',
28
+ label: __( 'Editor Capabilities', 'better-wp-security' ),
29
+ },
30
+ {
31
+ value: '$author$',
32
+ label: __( 'Author Capabilities', 'better-wp-security' ),
33
+ },
34
+ {
35
+ value: '$contributor$',
36
+ label: __( 'Contributor Capabilities', 'better-wp-security' ),
37
+ },
38
+ {
39
+ value: '$subscriber$',
40
+ label: __( 'Subscriber Capabilities', 'better-wp-security' ),
41
+ },
42
+ ];
43
+
44
+ if ( includeSuperAdmin ) {
45
+ group.unshift( {
46
+ value: '$super-admin$',
47
+ label: __( 'Super Admin', 'better-wp-security' ),
48
+ } );
49
+ }
50
+
51
+ if ( some( availableRoles, ( role ) => role.canonical === '' ) ) {
52
+ group.push( {
53
+ value: '$other$',
54
+ label: __( 'Other', 'better-wp-security' ),
55
+ selectable: false,
56
+ } );
57
+ }
58
+
59
+ for ( const role in availableRoles ) {
60
+ if ( ! availableRoles.hasOwnProperty( role ) ) {
61
+ continue;
62
+ }
63
+
64
+ const { canonical, label } = availableRoles[ role ];
65
+
66
+ group.push( {
67
+ value: role,
68
+ parent: canonical.length > 0 ? `$${ canonical }$` : '$other$',
69
+ label,
70
+ } );
71
+ }
72
+
73
+ return Object.values( group );
74
+ } );
75
+
76
+ function PanelRoles( { canonical, roles, onChange, available, schema, disabled = false } ) {
77
+ const includeSuperAdmin = get( schema, [ 'properties', 'canonical', 'items', 'enum' ], [] ).includes( 'super-admin' );
78
+ const value = [
79
+ ...roles,
80
+ ...canonical.map( ( role ) => `$${ role }$` ),
81
+ ];
82
+
83
+ return (
84
+ <HierarchicalCheckboxControl
85
+ label={ __( 'Select Roles', 'better-wp-security' ) }
86
+ help={ __( 'Add users with the selected roles to this group.', 'better-wp-security' ) }
87
+ value={ value }
88
+ disabled={ disabled }
89
+ options={ toCanonicalGroup( available, includeSuperAdmin ) }
90
+ onChange={ ( change ) => {
91
+ const [ newCanonical, newRoles ] = bifurcate( change, ( role ) => role.startsWith( '$' ) && role.endsWith( '$' ) );
92
+
93
+ onChange( {
94
+ roles: newRoles,
95
+ canonical: without( newCanonical.map( ( role ) => role.slice( 1, -1 ) ), 'other' ),
96
+ } );
97
+ } }
98
+ />
99
+ );
100
+ }
101
+
102
+ export default compose( [
103
+ withSelect( ( select, { groupId } ) => ( {
104
+ roles: select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'roles' ) || [],
105
+ canonical: select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'canonical' ) || [],
106
+ available: select( 'ithemes-security/core' ).getRoles(),
107
+ schema: select( 'ithemes-security/core' ).getSchema( 'ithemes-security-user-group' ),
108
+ } ) ),
109
+ withDispatch( ( dispatch, { groupId } ) => ( {
110
+ onChange( edit ) {
111
+ return dispatch( 'ithemes-security/user-groups-editor' ).editGroup( groupId, edit );
112
+ },
113
+ } ) ),
114
+ ] )( PanelRoles );
core/modules/user-groups/entries/settings/components/panel-roles/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/panel-users/index.js ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { map } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import apiFetch from '@wordpress/api-fetch';
10
+ import { addQueryArgs } from '@wordpress/url';
11
+ import { compose, withState, withInstanceId } from '@wordpress/compose';
12
+ import { withDispatch, withSelect, dispatch as dataDispatch } from '@wordpress/data';
13
+ import { __ } from '@wordpress/i18n';
14
+ import { BaseControl } from '@wordpress/components';
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ import { AsyncSelect } from '@ithemes/security-components';
20
+ import './style.scss';
21
+
22
+ function formatUser( user ) {
23
+ return { value: user.id, label: user.name, user };
24
+ }
25
+
26
+ const loadUsers = ( search ) => new Promise( ( resolve, reject ) => {
27
+ apiFetch( { path: addQueryArgs( '/wp/v2/users', { search, per_page: 100, context: 'embed', itsec_global: true } ) } )
28
+ .then( ( response ) => {
29
+ response.forEach( dataDispatch( 'ithemes-security/core' ).receiveUser );
30
+
31
+ return response;
32
+ } )
33
+ .then( ( response ) => resolve( response.map( formatUser ) ) )
34
+ .catch( reject );
35
+ } );
36
+
37
+ function PanelUsers( { instanceId, users, loading, onChange, disabled = false, selectSearch, setState } ) {
38
+ const selectId = `itsec-user-group-panel-users__select-${ instanceId }`;
39
+ const values = loading ? [] : users.map( formatUser );
40
+
41
+ return (
42
+ <BaseControl className="itsec-user-group-panel-users__select-control"
43
+ label={ __( 'Select Users', 'better-wp-security' ) }
44
+ help={ __( 'Select specific users to add to this group.', 'better-wp-security' ) }
45
+ id={ selectId }>
46
+ <AsyncSelect
47
+ classNamePrefix="components-itsec-async-select-control"
48
+ inputId={ selectId }
49
+ isDisabled={ disabled || loading } isLoading={ loading }
50
+ isMulti
51
+ cacheOptions defaultOptions loadOptions={ loadUsers }
52
+ value={ values } onChange={ ( newUsers ) => onChange( { users: map( newUsers, 'value' ) } ) }
53
+ inputValue={ selectSearch } onInputChange={ ( newSelect ) => setState( { selectSearch: newSelect } ) }
54
+ />
55
+ </BaseControl>
56
+ );
57
+ }
58
+
59
+ export default compose( [
60
+ withState( { selectSearch: '' } ),
61
+ withSelect( ( select, { groupId } ) => {
62
+ const userIds = select( 'ithemes-security/user-groups-editor' ).getEditedGroupAttribute( groupId, 'users' ) || [];
63
+ const users = [];
64
+ let loading = false;
65
+
66
+ userIds.forEach( ( userId ) => {
67
+ const user = select( 'ithemes-security/core' ).getUser( userId );
68
+
69
+ if ( user ) {
70
+ users.push( user );
71
+ } else if ( select( 'core/data' ).isResolving( 'ithemes-security/core', 'getUser', [ userId ] ) ) {
72
+ loading = true;
73
+ }
74
+ } );
75
+
76
+ return ( {
77
+ users,
78
+ userIds,
79
+ loading,
80
+ } );
81
+ } ),
82
+ withDispatch( ( dispatch, { groupId } ) => ( {
83
+ receiveUser: dispatch( 'ithemes-security/core' ).receiveUser,
84
+ onChange( edit ) {
85
+ return dispatch( 'ithemes-security/user-groups-editor' ).editGroup( groupId, edit );
86
+ },
87
+ } ) ),
88
+ withInstanceId,
89
+ ] )( PanelUsers );
90
+
core/modules/user-groups/entries/settings/components/panel-users/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/panel-users/style.scss ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-user-group-panel-users__selected span {
2
+ padding-right: .5em;
3
+ }
4
+
5
+ .components-itsec-async-select-control__input input {
6
+ min-height: 0;
7
+
8
+ &:focus {
9
+ box-shadow: none;
10
+ }
11
+ }
core/modules/user-groups/entries/settings/components/settings-form/index.js ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { map } from 'lodash';
5
+
6
+ export default function SettingsForm( { schema, settingComponent: SettingComponent, ...props } ) {
7
+ return (
8
+ <ul className="itsec-user-groups-group-tab__modules-list">
9
+ { map( schema.properties, ( moduleSchema, module ) => (
10
+ <li key={ module }>
11
+ <fieldset>
12
+ <legend>{ moduleSchema.title }</legend>
13
+ <ul>
14
+ { map( moduleSchema.properties, ( settingSchema, setting ) => (
15
+ <li key={ setting }>
16
+ <SettingComponent schema={ settingSchema } module={ module } setting={ setting } { ...props } />
17
+ </li>
18
+ ) ) }
19
+ </ul>
20
+ </fieldset>
21
+ </li>
22
+ ) ) }
23
+ </ul>
24
+ );
25
+ }
core/modules/user-groups/entries/settings/components/settings-form/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-body/index.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import './style.scss';
10
+
11
+ TabBody.Row = function( { name, children } ) {
12
+ return (
13
+ <div className={ `itsec-user-groups-group-tab__row itsec-user-groups-group-tab__row--${ name }` }>
14
+ { children }
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default function TabBody( { name, isLoading, children } ) {
20
+ const className = classnames( 'itsec-user-groups-group-tab', {
21
+ [ `itsec-user-groups-group-tab--${ name }` ]: name,
22
+ 'itsec-user-groups-group-tab--is-loading': isLoading,
23
+ } );
24
+
25
+ return (
26
+ <div className={ className }>
27
+ { children }
28
+ </div>
29
+ );
30
+ }
core/modules/user-groups/entries/settings/components/tab-body/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-body/style.scss ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "colors.scss";
2
+
3
+ .itsec-user-groups-group-tab__row {
4
+ padding: 1em;
5
+ }
6
+
7
+ .itsec-user-groups-group-tab__row--save {
8
+ border-top: 1px solid $dark-gray-200;
9
+ text-align: right;
10
+ }
11
+
12
+ .itsec-user-groups-group-tab--is-loading {
13
+ opacity: .5;
14
+ pointer-events: none;
15
+ }
core/modules/user-groups/entries/settings/components/tab-create-group/index.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { EditGroupFields, TabBody } from '../';
13
+
14
+ function TabCreateGroup( { hasEdits, save, isSaving } ) {
15
+ return (
16
+ <TabBody name="create-group">
17
+ <EditGroupFields groupId="new" />
18
+ <TabBody.Row name="save">
19
+ <Button disabled={ ! hasEdits } isPrimary onClick={ save } isBusy={ isSaving }>
20
+ { __( 'Create', 'better-wp-security' ) }
21
+ </Button>
22
+ </TabBody.Row>
23
+ </TabBody>
24
+ );
25
+ }
26
+
27
+ export default compose( [
28
+ withSelect( ( select ) => ( {
29
+ hasEdits: select( 'ithemes-security/user-groups-editor' ).hasEdits( 'new' ),
30
+ isSaving: select( 'ithemes-security/user-groups-editor' ).isCreating( 'new' ),
31
+ } ) ),
32
+ withDispatch( ( dispatch ) => ( {
33
+ save() {
34
+ dispatch( 'ithemes-security/user-groups-editor' ).createGroup();
35
+ },
36
+ } ) ),
37
+ ] )( TabCreateGroup );
core/modules/user-groups/entries/settings/components/tab-create-group/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-edit-group/index.js ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { EditGroupFields, TabBody } from '../';
13
+
14
+ function TabEditGroup( { groupId, hasEdits, save, isSaving, isLoading } ) {
15
+ return (
16
+ <TabBody name="edit-group" isLoading={ isLoading }>
17
+ <EditGroupFields groupId={ groupId } disabled={ isLoading } />
18
+ <TabBody.Row name="save">
19
+ <Button disabled={ ! hasEdits } isPrimary onClick={ save } isBusy={ isSaving }>
20
+ { __( 'Save', 'better-wp-security' ) }
21
+ </Button>
22
+ </TabBody.Row>
23
+ </TabBody>
24
+ );
25
+ }
26
+
27
+ export default compose( [
28
+ withSelect( ( select, { groupId } ) => ( {
29
+ isLoading: select( 'core/data' ).isResolving( 'ithemes-security/user-groups', 'getGroup', [ groupId ] ) ||
30
+ select( 'core/data' ).isResolving( 'ithemes-security/core', 'getIndex' ),
31
+ hasEdits: select( 'ithemes-security/user-groups-editor' ).hasEdits( groupId ),
32
+ isSaving: select( 'ithemes-security/user-groups' ).isUpdating( groupId ),
33
+ } ) ),
34
+ withDispatch( ( dispatch, { groupId } ) => ( {
35
+ save() {
36
+ return dispatch( 'ithemes-security/user-groups-editor' ).saveGroup( groupId );
37
+ },
38
+ } ) ),
39
+ ] )( TabEditGroup );
core/modules/user-groups/entries/settings/components/tab-edit-group/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-settings-bulk/field.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { CheckboxControl } from '@ithemes/security-components';
11
+
12
+ function Field( { schema, value, edit, disabled = false } ) {
13
+ return (
14
+ <CheckboxControl
15
+ checked={ value === true }
16
+ indeterminate={ value === null || value === undefined }
17
+ label={ schema.title }
18
+ help={ schema.description }
19
+ disabled={ disabled }
20
+ onChange={ ( checked ) => edit( checked ) } />
21
+ );
22
+ }
23
+
24
+ export default compose( [
25
+ withSelect( ( select, { module, setting, groupIds } ) => ( {
26
+ value: select( 'ithemes-security/user-groups-editor' ).getBulkSettingValue( groupIds, module, setting ),
27
+ } ) ),
28
+ withDispatch( ( dispatch, { module, setting } ) => ( {
29
+ edit( value ) {
30
+ return dispatch( 'ithemes-security/user-groups-editor' ).bulkEditGroupSetting( module, setting, value );
31
+ },
32
+ } ) ),
33
+ ] )( Field );
core/modules/user-groups/entries/settings/components/tab-settings-bulk/index.js ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { TabBody, SettingsForm } from '../';
13
+ import Field from './field';
14
+
15
+ function TabSettingsBulk( { schema, hasEdits, save, isSaving, groupIds } ) {
16
+ if ( ! schema ) {
17
+ return null;
18
+ }
19
+
20
+ return (
21
+ <TabBody name="settings">
22
+ <TabBody.Row>
23
+ <SettingsForm schema={ schema } settingComponent={ Field } groupIds={ groupIds } />
24
+ </TabBody.Row>
25
+ <TabBody.Row name="save">
26
+ <Button disabled={ ! hasEdits } isPrimary onClick={ save } isBusy={ isSaving }>
27
+ { __( 'Save', 'better-wp-security' ) }
28
+ </Button>
29
+ </TabBody.Row>
30
+ </TabBody>
31
+ );
32
+ }
33
+
34
+ export default compose( [
35
+ withSelect( ( select, { groupIds } ) => {
36
+ return ( {
37
+ schema: select( 'ithemes-security/core' ).getSchema( 'ithemes-security-user-group-settings' ),
38
+ hasEdits: select( 'ithemes-security/user-groups-editor' ).hasBulkSettingEdits(),
39
+ isSaving: select( 'ithemes-security/user-groups-editor' ).isSavingBulkEdits( groupIds ),
40
+ } );
41
+ } ),
42
+ withDispatch( ( dispatch, { groupIds } ) => ( {
43
+ save() {
44
+ return dispatch( 'ithemes-security/user-groups-editor' ).saveBulkEdits( groupIds );
45
+ },
46
+ } ) ),
47
+ ] )( TabSettingsBulk );
core/modules/user-groups/entries/settings/components/tab-settings-bulk/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-settings/field.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { ToggleControl } from '@wordpress/components';
7
+
8
+ function Field( { schema, value, edit, disabled = false } ) {
9
+ return (
10
+ <ToggleControl
11
+ checked={ value === true }
12
+ label={ schema.title }
13
+ help={ schema.description }
14
+ disabled={ disabled }
15
+ onChange={ ( checked ) => edit( checked ) } />
16
+ );
17
+ }
18
+
19
+ export default compose( [
20
+ withSelect( ( select, { groupId, module, setting } ) => ( {
21
+ value: select( 'ithemes-security/user-groups-editor' ).getEditedGroupSetting( groupId, module, setting ),
22
+ } ) ),
23
+ withDispatch( ( dispatch, { groupId, module, setting } ) => ( {
24
+ edit( value ) {
25
+ return dispatch( 'ithemes-security/user-groups-editor' ).editGroupSetting( groupId, module, setting, value );
26
+ },
27
+ } ) ),
28
+ ] )( Field );
29
+
core/modules/user-groups/entries/settings/components/tab-settings/index.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { compose } from '@wordpress/compose';
5
+ import { withSelect, withDispatch } from '@wordpress/data';
6
+ import { Button } from '@wordpress/components';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ /**
10
+ * Internal dependencies
11
+ */
12
+ import { TabBody, SettingsForm } from '../';
13
+ import Field from './field';
14
+ import './style.scss';
15
+
16
+ function TabSettings( { schema, groupId, hasEdits, save, isSaving, isLoading } ) {
17
+ if ( ! schema ) {
18
+ return null;
19
+ }
20
+
21
+ return (
22
+ <TabBody name="settings" isLoading={ isLoading }>
23
+ <TabBody.Row>
24
+ <SettingsForm schema={ schema } settingComponent={ Field } groupId={ groupId } disabled={ isLoading } />
25
+ </TabBody.Row>
26
+ <TabBody.Row name="save">
27
+ <Button disabled={ ! hasEdits } isPrimary onClick={ save } isBusy={ isSaving }>
28
+ { __( 'Save', 'better-wp-security' ) }
29
+ </Button>
30
+ </TabBody.Row>
31
+ </TabBody>
32
+ );
33
+ }
34
+
35
+ export default compose( [
36
+ withSelect( ( select, { groupId } ) => ( {
37
+ groupSettings: select( 'ithemes-security/user-groups' ).getGroupSettings( groupId ), // Hack to make sure isResolving is triggered in this component
38
+ isLoading: select( 'core/data' ).isResolving( 'ithemes-security/user-groups', 'getGroupSettings', [ groupId ] ),
39
+ schema: select( 'ithemes-security/core' ).getSchema( 'ithemes-security-user-group-settings' ),
40
+ hasEdits: select( 'ithemes-security/user-groups-editor' ).settingHasEdits( groupId ),
41
+ isSaving: select( 'ithemes-security/user-groups' ).isUpdatingSettings( groupId ),
42
+ } ) ),
43
+ withDispatch( ( dispatch, { groupId } ) => ( {
44
+ save() {
45
+ return dispatch( 'ithemes-security/user-groups-editor' ).saveGroupSettings( groupId );
46
+ },
47
+ } ) ),
48
+ ] )( TabSettings );
core/modules/user-groups/entries/settings/components/tab-settings/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/components/tab-settings/style.scss ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-user-groups-group-tab--settings {
2
+ & .itsec-user-groups-group-tab__modules-list {
3
+ padding-left: 0;
4
+ }
5
+
6
+ & ul {
7
+ list-style-type: none;
8
+ }
9
+
10
+ & legend {
11
+ font-weight: 600;
12
+ margin-bottom: 1em;
13
+ font-size: 1.2em;
14
+ }
15
+ }
core/modules/user-groups/entries/settings/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/store/actions.js ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { get, map } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __, _n, sprintf } from '@wordpress/i18n';
10
+ import { dispatch as dataDispatch } from '@wordpress/data';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { castWPError } from '@ithemes/security-utils';
16
+ import { select, dispatch, createNotice } from './controls';
17
+
18
+ export function selectGroup( ids ) {
19
+ return {
20
+ type: SELECT_GROUP,
21
+ ids,
22
+ };
23
+ }
24
+
25
+ export function editGroup( id, edit ) {
26
+ return {
27
+ type: EDIT_GROUP,
28
+ id,
29
+ edit,
30
+ };
31
+ }
32
+
33
+ export function* saveGroup( id ) {
34
+ const group = yield select( 'ithemes-security/user-groups-editor', 'getEditedGroup', id );
35
+
36
+ if ( ! group ) {
37
+ return;
38
+ }
39
+
40
+ yield { type: START_SAVE_GROUP, id };
41
+
42
+ const updated = yield dispatch( 'ithemes-security/user-groups', 'updateGroup', id, group );
43
+
44
+ if ( updated instanceof Error ) {
45
+ yield createNotice( 'error', updated.message );
46
+ yield { type: FAILED_SAVE_GROUP, id, error: updated };
47
+ } else {
48
+ yield { type: FINISH_SAVE_GROUP, id, updated };
49
+ yield createNotice( 'success', __( 'Updated group.', 'better-wp-security' ), { type: 'snackbar' } );
50
+ }
51
+
52
+ return updated;
53
+ }
54
+
55
+ export function resetEdits( id ) {
56
+ return {
57
+ type: RESET_EDITS,
58
+ id,
59
+ };
60
+ }
61
+
62
+ export function* createGroup( args = {} ) {
63
+ const group = yield select( 'ithemes-security/user-groups-editor', 'getEditedGroup', 'new' );
64
+
65
+ if ( ! group ) {
66
+ return;
67
+ }
68
+
69
+ yield { type: START_CREATE_GROUP };
70
+ const created = yield dispatch( 'ithemes-security/user-groups', 'createGroup', {
71
+ ...group,
72
+ ...args,
73
+ } );
74
+
75
+ if ( created instanceof Error ) {
76
+ if ( created.code === 'rest_duplicate_user_group' ) {
77
+ yield createNotice( 'error', created.message, {
78
+ actions: [
79
+ {
80
+ label: __( 'View Duplicate', 'better-wp-security' ),
81
+ isLink: true,
82
+ onClick() {
83
+ const duplicateLink = get( created, [ '_links', 'duplicate', 0, 'href' ] );
84
+ const duplicateId = duplicateLink.split( '/' ).pop();
85
+
86
+ dataDispatch( 'ithemes-security/user-groups-editor' ).selectGroup( [ duplicateId ] );
87
+ },
88
+ },
89
+ {
90
+ label: __( 'Create Anyway', 'better-wp-security' ),
91
+ onClick() {
92
+ dataDispatch( 'ithemes-security/user-groups-editor' ).createGroup( {
93
+ ignore_duplicate: true,
94
+ } );
95
+ },
96
+ },
97
+ ],
98
+ } );
99
+ } else {
100
+ yield createNotice( 'error', created.message );
101
+ }
102
+
103
+ yield { type: FAILED_CREATE_GROUP, error: created };
104
+ } else {
105
+ yield resetEdits( 'new' );
106
+ yield { type: FINISH_CREATE_GROUP, created };
107
+ yield dispatch( 'ithemes-security/user-groups-editor', 'selectGroup', created.id );
108
+ yield createNotice( 'success', __( 'Created group.', 'better-wp-security' ), { type: 'snackbar' } );
109
+ }
110
+
111
+ return created;
112
+ }
113
+
114
+ export function editGroupSetting( id, module, setting, value ) {
115
+ return {
116
+ type: EDIT_GROUP_SETTING,
117
+ id,
118
+ module,
119
+ setting,
120
+ value,
121
+ };
122
+ }
123
+
124
+ export function* saveGroupSettings( id ) {
125
+ const settings = yield select( 'ithemes-security/user-groups-editor', 'getEditedGroupSettings', id );
126
+
127
+ if ( ! settings ) {
128
+ return;
129
+ }
130
+
131
+ yield { type: START_SAVE_GROUP_SETTINGS, id };
132
+
133
+ const updated = yield dispatch( 'ithemes-security/user-groups', 'updateGroupSettings', id, settings );
134
+
135
+ if ( updated instanceof Error ) {
136
+ yield createNotice( 'error', updated.message );
137
+ yield { type: FAILED_SAVE_GROUP_SETTINGS, id, error: updated };
138
+ } else {
139
+ yield { type: FINISH_SAVE_GROUP_SETTINGS, id, updated };
140
+ yield createNotice( 'success', __( 'Updated group settings.', 'better-wp-security' ), { type: 'snackbar' } );
141
+ }
142
+
143
+ return updated;
144
+ }
145
+
146
+ export function bulkEditGroupSetting( module, setting, value ) {
147
+ return {
148
+ type: BULK_EDIT_GROUP_SETTING,
149
+ module,
150
+ setting,
151
+ value,
152
+ };
153
+ }
154
+
155
+ export function resetBulkGroupSettingEdit( module, setting ) {
156
+ return {
157
+ type: RESET_BULK_GROUP_SETTING_EDIT,
158
+ module,
159
+ setting,
160
+ };
161
+ }
162
+
163
+ export function resetBulkGroupSettingEdits() {
164
+ return {
165
+ type: RESET_BULK_GROUP_SETTING_EDITS,
166
+ };
167
+ }
168
+
169
+ export function* saveBulkEdits( groupIds ) {
170
+ const edits = yield select( 'ithemes-security/user-groups-editor', 'getBulkSettingEdits' );
171
+ const response = yield dispatch(
172
+ 'ithemes-security/user-groups',
173
+ 'patchBulkGroupSettings',
174
+ groupIds,
175
+ edits
176
+ );
177
+
178
+ if ( response instanceof Error ) {
179
+ yield createNotice( 'error', response.message );
180
+ } else if ( map( response, 'status' ).every( ( status ) => status === 200 ) ) {
181
+ yield createNotice( 'success', __( 'Updated group settings.', 'better-wp-security' ), { type: 'snackbar' } );
182
+ } else {
183
+ const errors = response.filter( ( { status } ) => status !== 200 ).map( ( { error } ) => castWPError( error ) );
184
+ const combinedMessage = errors.map( ( error ) => error.getAllErrorMessages().join( ' ' ) ).join( ' ' );
185
+
186
+ if ( errors.length === response.length ) {
187
+ yield createNotice( 'error', combinedMessage );
188
+ } else {
189
+ yield createNotice(
190
+ 'warning',
191
+ sprintf(
192
+ _n( '%1$d group was not updated: %2$s', '%1$d groups were not updated: %2$s', errors.length, 'better-wp-security' ),
193
+ errors.length,
194
+ combinedMessage
195
+ )
196
+ );
197
+ }
198
+ }
199
+
200
+ yield resetBulkGroupSettingEdits();
201
+
202
+ return response;
203
+ }
204
+
205
+ export const SELECT_GROUP = 'SELECT_GROUP';
206
+
207
+ export const EDIT_GROUP = 'EDIT_GROUP';
208
+ export const RESET_EDITS = 'RESET_EDITS';
209
+
210
+ export const START_SAVE_GROUP = 'START_SAVE_GROUP';
211
+ export const FINISH_SAVE_GROUP = 'FINISH_SAVE_GROUP';
212
+ export const FAILED_SAVE_GROUP = 'FAILED_SAVE_GROUP';
213
+
214
+ export const START_CREATE_GROUP = 'START_CREATE_GROUP';
215
+ export const FINISH_CREATE_GROUP = 'FINISH_CREATE_GROUP';
216
+ export const FAILED_CREATE_GROUP = 'FAILED_CREATE_GROUP';
217
+
218
+ export const EDIT_GROUP_SETTING = 'EDIT_GROUP_SETTING';
219
+
220
+ export const START_SAVE_GROUP_SETTINGS = 'START_SAVE_GROUP_SETTINGS';
221
+ export const FINISH_SAVE_GROUP_SETTINGS = 'FINISH_SAVE_GROUP_SETTINGS';
222
+ export const FAILED_SAVE_GROUP_SETTINGS = 'FAILED_SAVE_GROUP_SETTINGS';
223
+
224
+ export const BULK_EDIT_GROUP_SETTING = 'BULK_EDIT_GROUP_SETTING';
225
+ export const RESET_BULK_GROUP_SETTING_EDIT = 'RESET_BULK_GROUP_SETTING_EDIT';
226
+ export const RESET_BULK_GROUP_SETTING_EDITS = 'RESET_BULK_GROUP_SETTING_EDITS';
core/modules/user-groups/entries/settings/store/controls.js ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { uniqueId } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { select as selectData, dispatch as dispatchData } from '@wordpress/data';
10
+ import { default as triggerApiFetch } from '@wordpress/api-fetch';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { responseToError } from '@ithemes/security-utils';
16
+
17
+ /**
18
+ * Trigger an API Fetch request.
19
+ *
20
+ * @param {Object} request API Fetch Request Object.
21
+ * @return {Object} control descriptor.
22
+ */
23
+ export function apiFetch( request ) {
24
+ return {
25
+ type: 'API_FETCH',
26
+ request,
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Calls a selector using the current state.
32
+ * @param {string} storeKey Store key.
33
+ * @param {string} selectorName Selector name.
34
+ * @param {Array} args Selector arguments.
35
+ *
36
+ * @return {Object} control descriptor.
37
+ */
38
+ export function select( storeKey, selectorName, ...args ) {
39
+ return {
40
+ type: 'SELECT',
41
+ storeKey,
42
+ selectorName,
43
+ args,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Dispatches a control action for triggering a registry dispatch.
49
+ *
50
+ * @param {string} storeKey The key for the store the action belongs to
51
+ * @param {string} actionName The name of the action to dispatch
52
+ * @param {Array} args Arguments for the dispatch action.
53
+ *
54
+ * @example
55
+ * ```js
56
+ * import { dispatch } from '@wordpress/data-controls';
57
+ *
58
+ * // Action generator using dispatch
59
+ * export function* myAction() {
60
+ * yield dispatch( 'core/edit-post', 'togglePublishSidebar' );
61
+ * // do some other things.
62
+ * }
63
+ * ```
64
+ *
65
+ * @return {Object} The control descriptor.
66
+ */
67
+ export function dispatch( storeKey, actionName, ...args ) {
68
+ return {
69
+ type: 'DISPATCH',
70
+ storeKey,
71
+ actionName,
72
+ args,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Yields action objects used in signalling that a notice is to be created.
78
+ *
79
+ * @see @wordpress/notices#createNotice()
80
+ *
81
+ * @param {?string} status Notice status.
82
+ * Defaults to `info`.
83
+ * @param {string} content Notice message.
84
+ * @param {?Object} options Notice options.
85
+ * @param {?string} options.context Context under which to
86
+ * group notice.
87
+ * @param {?string} options.id Identifier for notice.
88
+ * Automatically assigned
89
+ * if not specified.
90
+ * @param {?boolean} options.isDismissible Whether the notice can
91
+ * be dismissed by user.
92
+ * Defaults to `true`.
93
+ * @param {?number} options.autoDismiss Whether the notice should
94
+ * by automatically dismissed
95
+ * after x milliseconds.
96
+ * Defaults to `false`.
97
+ * @param {?string} options.type Notice type. Either 'default' or 'snackbar'.
98
+ * @param {?Array<WPNoticeAction>} options.actions User actions to be
99
+ * presented with notice.
100
+ *
101
+ * @return {Object} control descriptor.
102
+ */
103
+ export function createNotice( status = 'info', content, options = {} ) {
104
+ return {
105
+ type: 'CREATE_NOTICE',
106
+ status,
107
+ content,
108
+ options: {
109
+ context: 'ithemes-security',
110
+ ...options,
111
+ },
112
+ };
113
+ }
114
+
115
+ const controls = {
116
+ API_FETCH( { request } ) {
117
+ return triggerApiFetch( request ).catch( responseToError );
118
+ },
119
+ SELECT( { storeKey, selectorName, args } ) {
120
+ return selectData( storeKey )[ selectorName ]( ...args );
121
+ },
122
+ DISPATCH( { storeKey, actionName, args } ) {
123
+ return dispatchData( storeKey )[ actionName ]( ...args );
124
+ },
125
+ CREATE_NOTICE( { status, content, options } ) {
126
+ if ( options.autoDismiss ) {
127
+ options.id = options.id || uniqueId( 'itsec-auto-dismiss-' );
128
+ setTimeout( () => dispatchData( 'core/notices' ).removeNotice( options.id, options.context ), options.autoDismiss );
129
+ }
130
+
131
+ dispatchData( 'core/notices' ).createNotice( status, content, options );
132
+ },
133
+ };
134
+
135
+ export default controls;
core/modules/user-groups/entries/settings/store/index.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { registerStore } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import controls from './controls';
10
+ import * as actions from './actions';
11
+ import * as selectors from './selectors';
12
+ import * as resolvers from './resolvers';
13
+ import userGroupsEditor from './reducers';
14
+
15
+ const store = registerStore( 'ithemes-security/user-groups-editor', {
16
+ controls,
17
+ actions,
18
+ selectors,
19
+ resolvers,
20
+ reducer: userGroupsEditor,
21
+ } );
22
+
23
+ export default store;
core/modules/user-groups/entries/settings/store/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/settings/store/reducers.js ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { get, omit } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import {
10
+ EDIT_GROUP,
11
+ FINISH_SAVE_GROUP,
12
+ EDIT_GROUP_SETTING,
13
+ FINISH_SAVE_GROUP_SETTINGS,
14
+ START_CREATE_GROUP,
15
+ FINISH_CREATE_GROUP,
16
+ FAILED_CREATE_GROUP,
17
+ RESET_EDITS,
18
+ SELECT_GROUP,
19
+ BULK_EDIT_GROUP_SETTING, RESET_BULK_GROUP_SETTING_EDIT, RESET_BULK_GROUP_SETTING_EDITS,
20
+ } from './actions';
21
+
22
+ const DEFAULT_STATE = {
23
+ edits: {},
24
+ settingEdits: {},
25
+ bulkSettingEdits: {},
26
+ creating: false,
27
+ selectedGroup: [],
28
+ };
29
+
30
+ export default function userGroupsEditor( state = DEFAULT_STATE, action ) {
31
+ switch ( action.type ) {
32
+ case SELECT_GROUP:
33
+ return {
34
+ ...state,
35
+ selectedGroup: action.ids,
36
+ };
37
+ case EDIT_GROUP:
38
+ return {
39
+ ...state,
40
+ edits: {
41
+ ...state.edits,
42
+ [ action.id ]: {
43
+ ...( state.edits[ action.id ] || {} ),
44
+ ...action.edit,
45
+ },
46
+ },
47
+ };
48
+ case FINISH_SAVE_GROUP:
49
+ return {
50
+ ...state,
51
+ edits: omit( state.edits, [ action.id ] ),
52
+ };
53
+ case EDIT_GROUP_SETTING:
54
+ return {
55
+ ...state,
56
+ settingEdits: {
57
+ ...state.settingEdits,
58
+ [ action.id ]: {
59
+ ...get( state, [ 'settingEdits', action.id ], {} ),
60
+ [ action.module ]: {
61
+ ...get( state, [ 'settingEdits', action.id, action.module ], {} ),
62
+ [ action.setting ]: action.value,
63
+ },
64
+ },
65
+ },
66
+ };
67
+ case FINISH_SAVE_GROUP_SETTINGS:
68
+ return {
69
+ ...state,
70
+ settingEdits: omit( state.settingEdits, [ action.id ] ),
71
+ };
72
+ case START_CREATE_GROUP:
73
+ return {
74
+ ...state,
75
+ creating: true,
76
+ };
77
+ case FAILED_CREATE_GROUP:
78
+ return {
79
+ ...state,
80
+ creating: false,
81
+ };
82
+ case FINISH_CREATE_GROUP:
83
+ return {
84
+ ...state,
85
+ creating: false,
86
+ };
87
+ case RESET_EDITS:
88
+ return {
89
+ ...state,
90
+ edits: omit( state.edits, [ action.id ] ),
91
+ };
92
+ case BULK_EDIT_GROUP_SETTING:
93
+ return {
94
+ ...state,
95
+ bulkSettingEdits: {
96
+ ...state.bulkSettingEdits,
97
+ [ action.module ]: {
98
+ ...( state.bulkSettingEdits[ action.module ] || {} ),
99
+ [ action.setting ]: action.value,
100
+ },
101
+ },
102
+ };
103
+ case RESET_BULK_GROUP_SETTING_EDIT:
104
+ return {
105
+ ...state,
106
+ bulkSettingEdits: omit( state.bulkSettingEdits, `${ action.module }.${ action.setting }` ),
107
+ };
108
+ case RESET_BULK_GROUP_SETTING_EDITS:
109
+ return {
110
+ ...state,
111
+ bulkSettingEdits: {},
112
+ };
113
+ default:
114
+ return state;
115
+ }
116
+ }
core/modules/user-groups/entries/settings/store/resolvers.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { dispatch } from './controls';
5
+
6
+ export function* getAvailableGroups() {
7
+ const groups = yield dispatch( 'ithemes-security/user-groups', 'query', 'available', { _embed: 1 } );
8
+
9
+ if ( groups.length > 0 ) {
10
+ yield dispatch( 'ithemes-security/user-groups-editor', 'selectGroup', [ groups[ 0 ].id ] );
11
+ }
12
+
13
+ return groups;
14
+ }
core/modules/user-groups/entries/settings/store/selectors.js ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { get, isEmpty } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { select } from '@wordpress/data';
10
+
11
+ /**
12
+ * Get the group being edited.
13
+ * @param {Object} state
14
+ * @return {Array<string>}
15
+ */
16
+ export function getSelectedGroup( state ) {
17
+ return state.selectedGroup;
18
+ }
19
+
20
+ /**
21
+ * Is a new group being created.
22
+ * @param {Object} state
23
+ * @return {boolean}
24
+ */
25
+ export function isCreating( state ) {
26
+ return state.creating;
27
+ }
28
+
29
+ /**
30
+ * Get the full edited group object.
31
+ * @param {Object} state
32
+ * @param {string} id
33
+ * @return {Object|undefined}
34
+ */
35
+ export function getEditedGroup( state, id ) {
36
+ return state.edits[ id ];
37
+ }
38
+
39
+ /**
40
+ * Get the edited attribute for a group.
41
+ * @param {Object} state
42
+ * @param {string} id
43
+ * @param {string} attribute
44
+ * @return {*}
45
+ */
46
+ export function getEditedGroupAttribute( state, id, attribute ) {
47
+ const edited = get( state, [ 'edits', id, attribute ] );
48
+
49
+ if ( typeof edited !== 'undefined' ) {
50
+ return edited;
51
+ }
52
+
53
+ if ( id === 'new' ) {
54
+ return undefined;
55
+ }
56
+
57
+ return select( 'ithemes-security/user-groups' ).getGroupAttribute( id, attribute );
58
+ }
59
+
60
+ /**
61
+ * Checks if there are changes to be saved for a group.
62
+ * @param {Object} state
63
+ * @param {string} id
64
+ * @return {boolean}
65
+ */
66
+ export function hasEdits( state, id ) {
67
+ return !! state.edits[ id ];
68
+ }
69
+
70
+ /**
71
+ * Are there any unsaved changes for a group's settings.
72
+ * @param {Object} state
73
+ * @param {string} id
74
+ * @return {boolean}
75
+ */
76
+ export function settingHasEdits( state, id ) {
77
+ return !! state.settingEdits[ id ];
78
+ }
79
+
80
+ /**
81
+ * Get the full edited group settings.
82
+ *
83
+ * @param {Object} state
84
+ * @param {string} id
85
+ * @return {Object}
86
+ */
87
+ export function getEditedGroupSettings( state, id ) {
88
+ return state.settingEdits[ id ];
89
+ }
90
+
91
+ /**
92
+ * Get a group's edited value for a setting.
93
+ *
94
+ * @param {Object} state
95
+ * @param {string} id
96
+ * @param {string} module
97
+ * @param {string} setting
98
+ * @return {boolean}
99
+ */
100
+ export function getEditedGroupSetting( state, id, module, setting ) {
101
+ const value = get( state, [ 'settingEdits', id, module, setting ] );
102
+
103
+ if ( value !== undefined ) {
104
+ return value;
105
+ }
106
+
107
+ return select( 'ithemes-security/user-groups' ).getGroupSetting( id, module, setting );
108
+ }
109
+
110
+ /**
111
+ * Check if there have been bulk setting edits.
112
+ * @param {Object} state
113
+ * @return {boolean}
114
+ */
115
+ export function hasBulkSettingEdits( state ) {
116
+ return ! isEmpty( state.bulkSettingEdits );
117
+ }
118
+
119
+ /**
120
+ * Get all the bulk setting edits.
121
+ * @param {Object} state
122
+ * @return {{}}
123
+ */
124
+ export function getBulkSettingEdits( state ) {
125
+ return state.bulkSettingEdits;
126
+ }
127
+
128
+ /**
129
+ * Get the bulk edited setting value.
130
+ *
131
+ * @param {Object} state
132
+ * @param {string} module
133
+ * @param {string} setting
134
+ * @return {boolean|undefined}
135
+ */
136
+ export function getBulkSettingEdit( state, module, setting ) {
137
+ return get( state, [ 'bulkSettingEdits', module, setting ] );
138
+ }
139
+
140
+ /**
141
+ * Get the value for a bulk edited setting.
142
+ *
143
+ * @param {Object} state
144
+ * @param {Array<Object>} groupIds
145
+ * @param {string} module
146
+ * @param {string} setting
147
+ * @return {null|boolean}
148
+ */
149
+ export function getBulkSettingValue( state, groupIds, module, setting ) {
150
+ const edit = getBulkSettingEdit( state, module, setting );
151
+
152
+ if ( edit !== undefined ) {
153
+ return edit;
154
+ }
155
+
156
+ const getValue = ( groupId ) => select( 'ithemes-security/user-groups' ).getGroupSetting( groupId, module, setting );
157
+
158
+ const firstVal = getValue( groupIds[ 0 ] );
159
+ const allSame = groupIds.every( ( groupId ) => getValue( groupId ) === firstVal );
160
+
161
+ if ( allSame ) {
162
+ return firstVal;
163
+ }
164
+
165
+ return null;
166
+ }
167
+
168
+ /**
169
+ * Are bulk edits being saved.
170
+ *
171
+ * @param {Object} state The state object.
172
+ * @param {Array<string>} groupIds The list of group ids.
173
+ * @return {boolean}
174
+ */
175
+ export function isSavingBulkEdits( state, groupIds ) {
176
+ const edits = getBulkSettingEdits( state );
177
+
178
+ return select( 'ithemes-security/user-groups' ).isBulkPatchingSettings( groupIds, edits );
179
+ }
180
+
181
+ /**
182
+ * Get the list of available groups.
183
+ *
184
+ * @return {Array<Object>}
185
+ */
186
+ export function getAvailableGroups() {
187
+ return select( 'ithemes-security/user-groups' ).getGroups( 'available' );
188
+ }
core/modules/user-groups/entries/settings/style.scss ADDED
File without changes
core/modules/user-groups/entries/store/actions.js ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { get } from 'lodash';
5
+ import UriTemplate from 'uri-templates';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { addQueryArgs } from '@wordpress/url';
11
+ import { select } from '@wordpress/data';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { getSchemaLink } from '@ithemes/security-utils';
17
+ import { apiFetch, dispatch } from './controls';
18
+
19
+ export const path = '/ithemes-security/v1/user-groups';
20
+
21
+ export function* query( queryId, queryParams ) {
22
+ const items = yield apiFetch( {
23
+ path: addQueryArgs( path, queryParams ),
24
+ } );
25
+ yield receiveQuery( queryId, items );
26
+
27
+ for ( const item of items ) {
28
+ yield* processItem( item );
29
+ }
30
+
31
+ return items;
32
+ }
33
+
34
+ export function* appendToQuery( queryId, item ) {
35
+ yield {
36
+ type: APPEND_TO_QUERY,
37
+ queryId,
38
+ item,
39
+ };
40
+ yield* processItem( item );
41
+ }
42
+
43
+ export function receiveQuery( queryId, items ) {
44
+ return {
45
+ type: RECEIVE_QUERY,
46
+ queryId,
47
+ items,
48
+ };
49
+ }
50
+
51
+ export function* processItem( item ) {
52
+ const users = get( item, [ '_embedded', 'ithemes-security:user-group-member' ], [] );
53
+ const settings = get( item, [ '_embedded', 'ithemes-security:user-matchable-settings', 0 ] );
54
+
55
+ for ( const user of users ) {
56
+ yield dispatch( 'ithemes-security/core', 'receiveUser', user );
57
+ }
58
+
59
+ if ( settings ) {
60
+ yield receiveGroupSettings( item.id, settings );
61
+ }
62
+ }
63
+
64
+ export function receiveGroup( group ) {
65
+ return {
66
+ type: RECEIVE_GROUP,
67
+ group,
68
+ };
69
+ }
70
+
71
+ export function receiveMatchables( matchables ) {
72
+ return {
73
+ type: RECEIVE_MATCHABLES,
74
+ matchables,
75
+ };
76
+ }
77
+
78
+ export function startCreateGroup( group ) {
79
+ return { type: START_CREATE_GROUP, group };
80
+ }
81
+
82
+ export function failedCreateGroup( group, error ) {
83
+ return { type: FAILED_CREATE_GROUP, group, error };
84
+ }
85
+
86
+ export function finishCreateGroup( group, response ) {
87
+ return { type: FINISH_CREATE_GROUP, group, response };
88
+ }
89
+
90
+ export function startUpdateGroup( id, group ) {
91
+ return { type: START_UPDATE_GROUP, id, group };
92
+ }
93
+
94
+ export function failedUpdateGroup( id, error ) {
95
+ return { type: FAILED_UPDATE_GROUP, id, error };
96
+ }
97
+
98
+ export function finishUpdateGroup( id, response ) {
99
+ return { type: FINISH_UPDATE_GROUP, id, response };
100
+ }
101
+
102
+ export function startDeleteGroup( id ) {
103
+ return { type: START_DELETE_GROUP, id };
104
+ }
105
+
106
+ export function failedDeleteGroup( id, error ) {
107
+ return { type: FAILED_DELETE_GROUP, id, error };
108
+ }
109
+
110
+ export function finishDeleteGroup( id ) {
111
+ return { type: FINISH_DELETE_GROUP, id };
112
+ }
113
+
114
+ export function receiveGroupSettings( id, settings ) {
115
+ return {
116
+ type: RECEIVE_GROUP_SETTINGS,
117
+ id,
118
+ settings,
119
+ };
120
+ }
121
+
122
+ export function startUpdateGroupSettings( id, settings ) {
123
+ return { type: START_UPDATE_GROUP_SETTINGS, id, settings };
124
+ }
125
+
126
+ export function failedUpdateGroupSettings( id, error ) {
127
+ return { type: FAILED_UPDATE_GROUP_SETTINGS, id, error };
128
+ }
129
+
130
+ export function finishUpdateGroupSettings( id, response ) {
131
+ return { type: FINISH_UPDATE_GROUP_SETTINGS, id, response };
132
+ }
133
+
134
+ /**
135
+ * Creates a new user group.
136
+ *
137
+ * @param {Object} group Group data.
138
+ * @return {IterableIterator<*>} Iterator
139
+ */
140
+ export function* createGroup( group ) {
141
+ yield startCreateGroup( group );
142
+
143
+ let response;
144
+
145
+ try {
146
+ response = yield apiFetch( {
147
+ path: addQueryArgs( path, { _embed: 1 } ),
148
+ method: 'POST',
149
+ data: group,
150
+ } );
151
+ } catch ( e ) {
152
+ yield failedCreateGroup( group, e );
153
+ return e;
154
+ }
155
+
156
+ yield finishCreateGroup( group, response );
157
+ yield receiveGroup( response );
158
+ yield* processItem( response );
159
+
160
+ return response;
161
+ }
162
+
163
+ /**
164
+ * Updates a user group.
165
+ *
166
+ * @param {string} id Group id id to update.
167
+ * @param {Object} group Group data.
168
+ * @return {IterableIterator<*>} Iterator
169
+ */
170
+ export function* updateGroup( id, group ) {
171
+ yield startUpdateGroup( id, group );
172
+
173
+ let response;
174
+
175
+ try {
176
+ response = yield apiFetch( {
177
+ path: path + '/' + id,
178
+ method: 'PUT',
179
+ data: group,
180
+ } );
181
+ } catch ( e ) {
182
+ yield failedUpdateGroup( id, e );
183
+ return e;
184
+ }
185
+
186
+ yield finishUpdateGroup( id, response );
187
+ yield receiveGroup( response );
188
+
189
+ return response;
190
+ }
191
+
192
+ /**
193
+ * Deletes a user group.
194
+ *
195
+ * @param {string} id Group id to delete.
196
+ * @return {IterableIterator<*>} Iterator
197
+ */
198
+ export function* deleteGroup( id ) {
199
+ yield startDeleteGroup( id );
200
+
201
+ try {
202
+ yield apiFetch( {
203
+ path: `${ path }/${ id }`,
204
+ method: 'DELETE',
205
+ } );
206
+ } catch ( e ) {
207
+ yield failedDeleteGroup( id, e );
208
+ return e;
209
+ }
210
+
211
+ yield finishDeleteGroup( id );
212
+
213
+ return null;
214
+ }
215
+
216
+ /**
217
+ * Updates a user group.
218
+ *
219
+ * @param {Object} id Id of group.
220
+ * @param {Object} settings New settings.
221
+ * @return {IterableIterator<*>} Iterator
222
+ */
223
+ export function* updateGroupSettings( id, settings ) {
224
+ yield startUpdateGroupSettings( id, settings );
225
+
226
+ let response;
227
+
228
+ try {
229
+ response = yield apiFetch( {
230
+ path: `ithemes-security/v1/user-matchable-settings/${ id }`,
231
+ method: 'PUT',
232
+ data: settings,
233
+ } );
234
+ } catch ( e ) {
235
+ yield failedUpdateGroupSettings( id, e );
236
+ return e;
237
+ }
238
+
239
+ yield finishUpdateGroupSettings( id, response );
240
+ yield receiveGroupSettings( id, response );
241
+
242
+ return response;
243
+ }
244
+
245
+ export function* fetchGroupsSettings( groupIds = [] ) {
246
+ yield startFetchGroupsSettings( groupIds );
247
+
248
+ let response;
249
+
250
+ try {
251
+ let fetchPath = 'ithemes-security/v1/user-matchable-settings';
252
+
253
+ if ( groupIds.length > 0 ) {
254
+ fetchPath = addQueryArgs( fetchPath, { include: groupIds } );
255
+ }
256
+
257
+ response = yield apiFetch( {
258
+ path: fetchPath,
259
+ } );
260
+ } catch ( e ) {
261
+ yield failedFetchGroupsSettings( groupIds, e );
262
+
263
+ return e;
264
+ }
265
+
266
+ yield finishFetchGroupsSettings( groupIds, response );
267
+
268
+ for ( const groupId in response ) {
269
+ if ( ! response.hasOwnProperty( groupId ) ) {
270
+ continue;
271
+ }
272
+
273
+ yield receiveGroupSettings( groupId, response[ groupId ] );
274
+ }
275
+
276
+ return response;
277
+ }
278
+
279
+ export function startFetchGroupsSettings( groupIds ) {
280
+ return {
281
+ type: START_FETCH_GROUPS_SETTINGS,
282
+ groupIds,
283
+ };
284
+ }
285
+
286
+ export function finishFetchGroupsSettings( groupIds, response ) {
287
+ return {
288
+ type: FINISH_FETCH_GROUPS_SETTINGS,
289
+ groupIds,
290
+ response,
291
+ };
292
+ }
293
+
294
+ export function failedFetchGroupsSettings( groupIds, error ) {
295
+ return {
296
+ type: FAILED_FETCH_GROUPS_SETTINGS,
297
+ groupIds,
298
+ error,
299
+ };
300
+ }
301
+
302
+ export function* patchBulkGroupSettings( groupIds, patch ) {
303
+ yield startPatchBulkGroupSettings( groupIds, patch );
304
+
305
+ let response;
306
+
307
+ try {
308
+ response = yield apiFetch( {
309
+ path: addQueryArgs( `ithemes-security/v1/user-matchable-settings`, { include: groupIds } ),
310
+ method: 'PATCH',
311
+ data: patch,
312
+ } );
313
+ } catch ( e ) {
314
+ yield failedPatchBulkGroupSettings( groupIds, patch, e );
315
+
316
+ return e;
317
+ }
318
+
319
+ yield finishPatchBulkGroupSettings( groupIds, patch, response );
320
+
321
+ const schema = select( 'ithemes-security/core' ).getSchema( 'ithemes-security-user-group-settings' );
322
+ const selfLink = getSchemaLink( schema, 'self' );
323
+
324
+ if ( ! selfLink ) {
325
+ return response;
326
+ }
327
+
328
+ const template = new UriTemplate( selfLink.href );
329
+
330
+ for ( const result of response ) {
331
+ if ( result.status !== 200 ) {
332
+ continue;
333
+ }
334
+
335
+ const props = template.fromUri( result.href );
336
+
337
+ if ( ! props.id ) {
338
+ continue;
339
+ }
340
+
341
+ yield receiveGroupSettings( props.id, result.response );
342
+ }
343
+
344
+ return response;
345
+ }
346
+
347
+ export function startPatchBulkGroupSettings( groupIds, patch ) {
348
+ return {
349
+ type: START_PATCH_BULK_GROUP_SETTINGS,
350
+ groupIds,
351
+ patch,
352
+ };
353
+ }
354
+
355
+ export function finishPatchBulkGroupSettings( groupIds, patch, response ) {
356
+ return {
357
+ type: FINISH_PATCH_BULK_GROUP_SETTINGS,
358
+ groupIds,
359
+ patch,
360
+ response,
361
+ };
362
+ }
363
+
364
+ export function failedPatchBulkGroupSettings( groupIds, patch, error ) {
365
+ return {
366
+ type: FAILED_PATCH_BULK_GROUP_SETTINGS,
367
+ groupIds,
368
+ patch,
369
+ error,
370
+ };
371
+ }
372
+
373
+ export const RECEIVE_QUERY = 'RECEIVE_QUERY';
374
+ export const APPEND_TO_QUERY = 'APPEND_TO_QUERY';
375
+
376
+ export const RECEIVE_MATCHABLES = 'RECEIVE_MATCHABLES';
377
+
378
+ export const START_CREATE_GROUP = 'START_CREATE_GROUP';
379
+ export const FINISH_CREATE_GROUP = 'FINISH_CREATE_GROUP';
380
+ export const FAILED_CREATE_GROUP = 'FAILED_CREATE_GROUP';
381
+
382
+ export const RECEIVE_GROUP = 'RECEIVE_GROUP';
383
+
384
+ export const START_UPDATE_GROUP = 'START_UPDATE_GROUP';
385
+ export const FINISH_UPDATE_GROUP = 'FINISH_UPDATE_GROUP';
386
+ export const FAILED_UPDATE_GROUP = 'FAILED_UPDATE_GROUP';
387
+
388
+ export const START_DELETE_GROUP = 'START_DELETE_GROUP';
389
+ export const FINISH_DELETE_GROUP = 'FINISH_DELETE_GROUP';
390
+ export const FAILED_DELETE_GROUP = 'FAILED_DELETE_GROUP';
391
+
392
+ export const RECEIVE_GROUP_SETTINGS = 'RECEIVE_GROUP_SETTINGS';
393
+
394
+ export const START_UPDATE_GROUP_SETTINGS = 'START_UPDATE_GROUP_SETTINGS';
395
+ export const FINISH_UPDATE_GROUP_SETTINGS = 'FINISH_UPDATE_GROUP_SETTINGS';
396
+ export const FAILED_UPDATE_GROUP_SETTINGS = 'FAILED_UPDATE_GROUP_SETTINGS';
397
+
398
+ export const START_FETCH_GROUPS_SETTINGS = 'START_FETCH_GROUPS_SETTINGS';
399
+ export const FINISH_FETCH_GROUPS_SETTINGS = 'FINISH_FETCH_GROUPS_SETTINGS';
400
+ export const FAILED_FETCH_GROUPS_SETTINGS = 'FAILED_FETCH_GROUPS_SETTINGS';
401
+
402
+ export const START_PATCH_BULK_GROUP_SETTINGS = 'START_PATCH_BULK_GROUP_SETTINGS';
403
+ export const FINISH_PATCH_BULK_GROUP_SETTINGS = 'FINISH_PATCH_BULK_GROUP_SETTINGS';
404
+ export const FAILED_PATCH_BULK_GROUP_SETTINGS = 'FAILED_PATCH_BULK_GROUP_SETTINGS';
core/modules/user-groups/entries/store/controls.js ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { dispatch as dispatchData, select as selectData } from '@wordpress/data';
5
+ import { default as triggerApiFetch } from '@wordpress/api-fetch';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { responseToError } from '@ithemes/security-utils';
11
+
12
+ /**
13
+ * Trigger an API Fetch request.
14
+ *
15
+ * @param {Object} request API Fetch Request Object.
16
+ * @return {Object} control descriptor.
17
+ */
18
+ export function apiFetch( request ) {
19
+ return {
20
+ type: 'API_FETCH',
21
+ request,
22
+ };
23
+ }
24
+
25
+ /**
26
+ * Calls a selector using the current state.
27
+ * @param {string} storeKey Store key.
28
+ * @param {string} selectorName Selector name.
29
+ * @param {Array} args Selector arguments.
30
+ *
31
+ * @return {Object} control descriptor.
32
+ */
33
+ export function select( storeKey, selectorName, ...args ) {
34
+ return {
35
+ type: 'SELECT',
36
+ storeKey,
37
+ selectorName,
38
+ args,
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Dispatches a control action for triggering a registry dispatch.
44
+ *
45
+ * @param {string} storeKey The key for the store the action belongs to
46
+ * @param {string} actionName The name of the action to dispatch
47
+ * @param {Array} args Arguments for the dispatch action.
48
+ *
49
+ * @example
50
+ * ```js
51
+ * import { dispatch } from '@wordpress/data-controls';
52
+ *
53
+ * // Action generator using dispatch
54
+ * export function* myAction() {
55
+ * yield dispatch( 'core/edit-post', 'togglePublishSidebar' );
56
+ * // do some other things.
57
+ * }
58
+ * ```
59
+ *
60
+ * @return {Object} The control descriptor.
61
+ */
62
+ export function dispatch( storeKey, actionName, ...args ) {
63
+ return {
64
+ type: 'DISPATCH',
65
+ storeKey,
66
+ actionName,
67
+ args,
68
+ };
69
+ }
70
+
71
+ const controls = {
72
+ API_FETCH( { request } ) {
73
+ return triggerApiFetch( request ).catch( responseToError );
74
+ },
75
+ SELECT( { storeKey, selectorName, args } ) {
76
+ return selectData( storeKey )[ selectorName ]( ...args );
77
+ },
78
+ DISPATCH( { storeKey, actionName, args } ) {
79
+ return dispatchData( storeKey )[ actionName ]( ...args );
80
+ },
81
+ };
82
+
83
+ export default controls;
core/modules/user-groups/entries/store/index.js ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { registerStore } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import controls from './controls';
10
+ import * as actions from './actions';
11
+ import * as selectors from './selectors';
12
+ import userGroups from './reducers';
13
+ import * as resolvers from './resolvers';
14
+
15
+ const store = registerStore( 'ithemes-security/user-groups', {
16
+ controls,
17
+ actions,
18
+ selectors,
19
+ resolvers,
20
+ reducer: userGroups,
21
+ } );
22
+
23
+ export default store;
core/modules/user-groups/entries/store/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/entries/store/reducers.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { omit, keyBy, map } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import {
10
+ FAILED_CREATE_GROUP,
11
+ FAILED_DELETE_GROUP,
12
+ FAILED_UPDATE_GROUP,
13
+ FINISH_CREATE_GROUP,
14
+ FINISH_DELETE_GROUP,
15
+ FINISH_UPDATE_GROUP,
16
+ RECEIVE_GROUP,
17
+ RECEIVE_GROUP_SETTINGS,
18
+ RECEIVE_QUERY,
19
+ START_CREATE_GROUP,
20
+ START_DELETE_GROUP,
21
+ START_UPDATE_GROUP,
22
+ START_UPDATE_GROUP_SETTINGS,
23
+ FINISH_UPDATE_GROUP_SETTINGS,
24
+ FAILED_UPDATE_GROUP_SETTINGS,
25
+ APPEND_TO_QUERY,
26
+ START_PATCH_BULK_GROUP_SETTINGS,
27
+ FINISH_PATCH_BULK_GROUP_SETTINGS,
28
+ FAILED_PATCH_BULK_GROUP_SETTINGS, RECEIVE_MATCHABLES,
29
+ } from './actions';
30
+
31
+ const DEFAULT_STATE = {
32
+ matchablesById: {},
33
+ matchableIds: [],
34
+ byId: {},
35
+ queries: {},
36
+ creating: [],
37
+ updating: [],
38
+ deleting: [],
39
+ settings: {},
40
+ updatingSettings: [],
41
+ bulkPatchingSettings: {},
42
+ };
43
+
44
+ export default function userGroups( state = DEFAULT_STATE, action ) {
45
+ switch ( action.type ) {
46
+ case RECEIVE_MATCHABLES:
47
+ return {
48
+ ...state,
49
+ matchableIds: map( action.matchables, 'id' ),
50
+ matchablesById: keyBy( action.matchables, 'id' ),
51
+ };
52
+ case RECEIVE_QUERY:
53
+ return {
54
+ ...state,
55
+ byId: {
56
+ ...state.byId,
57
+ ...keyBy( action.items, 'id' ),
58
+ },
59
+ queries: {
60
+ ...state.queries,
61
+ [ action.queryId ]: map( action.items, 'id' ),
62
+ },
63
+ };
64
+ case APPEND_TO_QUERY:
65
+ return {
66
+ ...state,
67
+ byId: {
68
+ ...state.byId,
69
+ [ action.item.id ]: action.item,
70
+ },
71
+ queries: {
72
+ ...state.queries,
73
+ [ action.queryId ]: [
74
+ ...( state.queries[ action.queryId ] || [] ),
75
+ action.item.id,
76
+ ],
77
+ },
78
+ };
79
+ case RECEIVE_GROUP:
80
+ return {
81
+ ...state,
82
+ byId: {
83
+ ...state.byId,
84
+ [ action.group.id ]: action.group,
85
+ },
86
+ matchablesById: state.matchablesById[ action.group.id ] ? {
87
+ ...state.matchablesById,
88
+ [ action.group.id ]: {
89
+ ...state.matchablesById[ action.group.id ],
90
+ label: action.group.label,
91
+ },
92
+ } : state.matchablesById,
93
+ };
94
+ case START_CREATE_GROUP:
95
+ return {
96
+ ...state,
97
+ creating: [
98
+ ...state.creating,
99
+ action.group,
100
+ ],
101
+ };
102
+ case FINISH_CREATE_GROUP:
103
+ return {
104
+ ...state,
105
+ creating: state.creating.filter( ( group ) => group !== action.group ),
106
+ matchablesById: {
107
+ ...state.matchablesById,
108
+ [ action.response.id ]: {
109
+ id: action.response.id,
110
+ label: action.response.label,
111
+ type: 'user-group',
112
+ },
113
+ },
114
+ matchableIds: [
115
+ ...state.matchableIds,
116
+ [ action.response.id ],
117
+ ],
118
+ };
119
+ case FAILED_CREATE_GROUP:
120
+ return {
121
+ ...state,
122
+ creating: state.creating.filter( ( group ) => group !== action.group ),
123
+ };
124
+ case START_UPDATE_GROUP:
125
+ return {
126
+ ...state,
127
+ updating: [
128
+ ...state.updating,
129
+ action.id,
130
+ ],
131
+ };
132
+ case FINISH_UPDATE_GROUP:
133
+ case FAILED_UPDATE_GROUP:
134
+ return {
135
+ ...state,
136
+ updating: state.updating.filter( ( id ) => id !== action.id ),
137
+ };
138
+ case START_DELETE_GROUP:
139
+ return {
140
+ ...state,
141
+ deleting: [
142
+ ...state.deleting,
143
+ action.id,
144
+ ],
145
+ };
146
+ case FINISH_DELETE_GROUP:
147
+ return {
148
+ ...state,
149
+ deleting: state.deleting.filter( ( id ) => id !== action.id ),
150
+ byId: omit( state.byId, [ action.id ] ),
151
+ matchablesById: omit( state.matchablesById, [ action.id ] ),
152
+ matchableIds: state.matchableIds.filter( ( id ) => id !== action.id ),
153
+ settings: omit( state.settings, [ action.id ] ),
154
+ };
155
+ case FAILED_DELETE_GROUP:
156
+ return {
157
+ ...state,
158
+ deleting: state.deleting.filter( ( id ) => id !== action.id ),
159
+ };
160
+ case RECEIVE_GROUP_SETTINGS:
161
+ return {
162
+ ...state,
163
+ settings: {
164
+ ...state.settings,
165
+ [ action.id ]: action.settings,
166
+ },
167
+ };
168
+ case START_UPDATE_GROUP_SETTINGS:
169
+ return {
170
+ ...state,
171
+ updatingSettings: [
172
+ ...state.updatingSettings,
173
+ action.id,
174
+ ],
175
+ };
176
+ case FINISH_UPDATE_GROUP_SETTINGS:
177
+ case FAILED_UPDATE_GROUP_SETTINGS:
178
+ return {
179
+ ...state,
180
+ updatingSettings: state.updatingSettings.filter( ( id ) => id !== action.id ),
181
+ };
182
+ case START_PATCH_BULK_GROUP_SETTINGS:
183
+ return {
184
+ ...state,
185
+ bulkPatchingSettings: {
186
+ ...state.bulkPatchingSettings,
187
+ [ action.groupIds.join( '_' ) ]: action.patch,
188
+ },
189
+ };
190
+ case FINISH_PATCH_BULK_GROUP_SETTINGS:
191
+ case FAILED_PATCH_BULK_GROUP_SETTINGS:
192
+ return {
193
+ ...state,
194
+ bulkPatchingSettings: omit( state.bulkPatchingSettings, [ action.groupIds.join( '_' ) ] ),
195
+ };
196
+ default:
197
+ return state;
198
+ }
199
+ }
core/modules/user-groups/entries/store/resolvers.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isEmpty, get } from 'lodash';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { apiFetch } from './controls';
10
+ import { path, receiveGroup, receiveGroupSettings, receiveMatchables, processItem } from './actions';
11
+
12
+ export const getGroup = {
13
+ *fulfill( id ) {
14
+ const group = yield apiFetch( { path: `${ path }/${ id }?_embed=1` } );
15
+ yield receiveGroup( group );
16
+ yield* processItem( group );
17
+ },
18
+ isFulfilled( state, id ) {
19
+ return state.byId.hasOwnProperty( id );
20
+ },
21
+ };
22
+
23
+ export const getMatchables = {
24
+ *fulfill() {
25
+ const matchables = yield apiFetch( { path: '/ithemes-security/v1/user-matchables?_embed=1' } );
26
+ yield receiveMatchables( matchables );
27
+
28
+ for ( const matchable of matchables ) {
29
+ const group = get( matchable, [ '_embedded', 'self', 0 ] );
30
+ const settings = get( matchable, [ '_embedded', 'ithemes-security:user-matchable-settings', 0 ] );
31
+
32
+ if ( group ) {
33
+ yield receiveGroup( group );
34
+ }
35
+
36
+ if ( settings ) {
37
+ yield receiveGroupSettings( matchable.id, settings );
38
+ }
39
+ }
40
+ },
41
+ isFulfilled( state ) {
42
+ return ! isEmpty( state.matchablesById );
43
+ },
44
+ };
45
+
46
+ export const getGroupSettings = {
47
+ *fulfill( id ) {
48
+ const settings = yield apiFetch( { path: `ithemes-security/v1/user-matchable-settings/${ id }` } );
49
+ yield receiveGroupSettings( id, settings );
50
+ },
51
+ isFulfilled( state, id ) {
52
+ return state.settings.hasOwnProperty( id );
53
+ },
54
+ };
core/modules/user-groups/entries/store/selectors.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import createSelector from 'rememo';
5
+ import { map, filter, isObject, get } from 'lodash';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { select } from '@wordpress/data';
11
+
12
+ /**
13
+ * Get the list of matchables.
14
+ *
15
+ * @param {Object} state
16
+ * @return {Array<Object>}
17
+ */
18
+ export const getMatchables = createSelector(
19
+ ( state ) => filter( map( state.matchableIds, ( id ) => state.matchablesById[ id ] ), isObject ),
20
+ ( state ) => [ state.matchablesById, state.matchableIds ],
21
+ );
22
+
23
+ /**
24
+ * Gets the type of a matchable.
25
+ *
26
+ * @param {Object} state Store state.
27
+ * @param {string} id Matchable id.
28
+ *
29
+ * @return {string} Either 'user-group' or 'meta'.
30
+ */
31
+ export function getMatchableType( state, id ) {
32
+ return ( state.matchablesById[ id ] || {} ).type;
33
+ }
34
+
35
+ /**
36
+ * Gets the label for a matchable.
37
+ *
38
+ * @param {Object} state Store state.
39
+ * @param {string} id Matchable id.
40
+ *
41
+ * @return {string} The matchable's label.
42
+ */
43
+ export function getMatchableLabel( state, id ) {
44
+ return ( state.matchablesById[ id ] || {} ).label;
45
+ }
46
+
47
+ /**
48
+ * Returns all the groups returned by a query ID.
49
+ *
50
+ * @param {Object} state Data state.
51
+ * @param {string} queryId Query ID.
52
+ *
53
+ * @return {Array} Groups list.
54
+ */
55
+ export const getGroups = createSelector(
56
+ ( state, queryId ) => filter( map( state.queries[ queryId ], ( id ) => state.byId[ id ] ), isObject ),
57
+ ( state, queryId ) => [ state.queries[ queryId ], state.byId ],
58
+ );
59
+
60
+ const UNKNOWN_QUERIED_OBJECTS = [];
61
+
62
+ /**
63
+ * Get the object ids returned by a query.
64
+ * @param {Object} state
65
+ * @param {string} queryId
66
+ * @return {Array<string|number>}
67
+ */
68
+ export function getQueriedObjectIds( state, queryId ) {
69
+ return state.queries[ queryId ] || UNKNOWN_QUERIED_OBJECTS;
70
+ }
71
+
72
+ /**
73
+ * Gets the data for the group.
74
+ *
75
+ * @param {Object} state
76
+ * @param {string} id
77
+ * @return {Object|undefined}
78
+ */
79
+ export function getGroup( state, id ) {
80
+ return state.byId[ id ];
81
+ }
82
+
83
+ /**
84
+ * Get a group's attribute value.
85
+ * @param {Object} state
86
+ * @param {string} id
87
+ * @param {string} attribute
88
+ * @return {*}
89
+ */
90
+ export function getGroupAttribute( state, id, attribute ) {
91
+ const group = select( 'ithemes-security/user-groups' ).getGroup( id );
92
+
93
+ return group ? group[ attribute ] : undefined;
94
+ }
95
+
96
+ /**
97
+ * Checks if the given user group is being updated.
98
+ * @param {Object} state
99
+ * @param {string} id
100
+ * @return {boolean}
101
+ */
102
+ export function isUpdating( state, id ) {
103
+ return state.updating.includes( id );
104
+ }
105
+
106
+ /**
107
+ * Checks if the given user group is being deleted.
108
+ * @param {Object} state
109
+ * @param {string} id
110
+ * @return {boolean}
111
+ */
112
+ export function isDeleting( state, id ) {
113
+ return state.deleting.includes( id );
114
+ }
115
+
116
+ /**
117
+ * Get all the settings for a group.
118
+ *
119
+ * @param {Object} state
120
+ * @param {string} id
121
+ * @return {Object|undefined}
122
+ */
123
+ export function getGroupSettings( state, id ) {
124
+ return state.settings[ id ];
125
+ }
126
+
127
+ /**
128
+ * Get a group's value for a setting.
129
+ *
130
+ * @param {Object} state
131
+ * @param {string} id
132
+ * @param {string} module
133
+ * @param {string} setting
134
+ * @return {boolean}
135
+ */
136
+ export function getGroupSetting( state, id, module, setting ) {
137
+ const settings = select( 'ithemes-security/user-groups' ).getGroupSettings( id );
138
+
139
+ return get( settings, [ module, setting ] );
140
+ }
141
+
142
+ /**
143
+ * Is the application updating a group's settings.
144
+ * @param {Object} state
145
+ * @param {string} id
146
+ * @return {boolean}
147
+ */
148
+ export function isUpdatingSettings( state, id ) {
149
+ return state.updatingSettings.includes( id );
150
+ }
151
+
152
+ /**
153
+ * Is a bulk patch in progress.
154
+ * @param {Object} state
155
+ * @param {Array<string>} groupIds
156
+ * @param {Object} patch
157
+ * @return {boolean}
158
+ */
159
+ export function isBulkPatchingSettings( state, groupIds, patch ) {
160
+ const id = groupIds.join( '_' );
161
+
162
+ return state.bulkPatchingSettings[ id ] === patch;
163
+ }
164
+
165
+ /**
166
+ * Gets the groups for each setting.
167
+ * @param {Object} state State object.
168
+ * @return {{}} Object of modules -> setting -> array of group ids.
169
+ */
170
+ export function getGroupsBySetting( state ) {
171
+ const bySetting = {};
172
+
173
+ for ( const groupId in state.settings ) {
174
+ if ( ! state.settings.hasOwnProperty( groupId ) ) {
175
+ continue;
176
+ }
177
+
178
+ for ( const module in state.settings[ groupId ] ) {
179
+ if ( ! state.settings[ groupId ].hasOwnProperty( module ) ) {
180
+ continue;
181
+ }
182
+
183
+ for ( const setting in state.settings[ groupId ][ module ] ) {
184
+ if ( ! state.settings[ groupId ][ module ].hasOwnProperty( setting ) ) {
185
+ continue;
186
+ }
187
+
188
+ if ( ! bySetting[ module ] ) {
189
+ bySetting[ module ] = {};
190
+ }
191
+
192
+ if ( ! bySetting[ module ][ setting ] ) {
193
+ bySetting[ module ][ setting ] = [];
194
+ }
195
+
196
+ if ( state.settings[ groupId ][ module ][ setting ] ) {
197
+ bySetting[ module ][ setting ].push( groupId );
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ return bySetting;
204
+ }
core/modules/user-groups/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/modules/user-groups/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'User Groups', 'better-wp-security' ),
5
+ ];
core/modules/user-groups/settings-page.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace iThemesSecurity\User_Groups;
4
+
5
+ class Settings_Page extends \ITSEC_Module_Settings_Page {
6
+
7
+ public function __construct() {
8
+ $this->id = 'user-groups';
9
+ $this->title = __( 'User Groups', 'better-wp-security' );
10
+ $this->description = __( 'Manage user groups.', 'better-wp-security' );
11
+ $this->type = 'recommended';
12
+ $this->can_save = false;
13
+ $this->documentation = 'https://ithemeshelp.zendesk.com/hc/en-us/articles/360042653774';
14
+
15
+ parent::__construct();
16
+ }
17
+
18
+ public function enqueue_scripts_and_styles() {
19
+ $preload = \ITSEC_Lib::preload_rest_requests( [
20
+ '/ithemes-security/v1/user-matchables?_embed=1' => [
21
+ 'route' => '/ithemes-security/v1/user-matchables',
22
+ 'embed' => true,
23
+ ],
24
+ '/ithemes-security/v1?context=help' => [
25
+ 'route' => '/ithemes-security/v1',
26
+ 'query' => [ 'context' => 'help' ],
27
+ ]
28
+ ] );
29
+
30
+ wp_enqueue_script( 'itsec-user-groups-settings' );
31
+ wp_enqueue_style( 'itsec-user-groups-settings' );
32
+ wp_add_inline_script(
33
+ 'itsec-user-groups-settings',
34
+ sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( $preload ) )
35
+ );
36
+ }
37
+
38
+ public function render( $form ) {
39
+ echo '<div id="itsec-user-groups-settings-root"></div>';
40
+ }
41
+ }
42
+
43
+ new Settings_Page();
core/modules/wordpress-tweaks/labels.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ return [
4
+ 'title' => __( 'WordPress Tweaks', 'better-wp-security' ),
5
+ ];
core/package.json CHANGED
@@ -9,28 +9,28 @@
9
  "dependencies": {
10
  "@wordpress/a11y": "*",
11
  "@wordpress/api-fetch": "*",
12
- "@wordpress/autop": "^2.2.0",
13
- "@wordpress/components": "^7.0.8",
14
- "@wordpress/compose": "^3.0.1",
15
- "@wordpress/data": "^4.2.1",
16
- "@wordpress/date": "^3.0.1",
17
  "@wordpress/dom-ready": "*",
18
  "@wordpress/element": "*",
19
  "@wordpress/hooks": "*",
20
  "@wordpress/html-entities": "*",
21
  "@wordpress/i18n": "*",
22
  "@wordpress/is-shallow-equal": "*",
23
- "@wordpress/keycodes": "^2.0.6",
24
  "@wordpress/notices": "*",
25
- "@wordpress/plugins": "^2.2.0",
26
  "@wordpress/redux-routine": "*",
27
- "@wordpress/rich-text": "^3.0.7",
28
  "@wordpress/url": "*",
29
  "@wordpress/viewport": "*",
30
  "classnames": "^2.2.6",
31
  "contrast": "^1.0.1",
32
  "li": "^1.3.0",
33
- "lodash": "^4.17.11",
34
  "memize": "^1.0.5",
35
  "react": "^16.6.3",
36
  "react-error-boundary": "^1.2.3",
@@ -38,11 +38,14 @@
38
  "react-select": "^2.4.1",
39
  "react-transition-group": "^2.0.0",
40
  "recharts": "^1.5.0",
41
- "rememo": "^3.0.0"
 
 
42
  },
43
  "devDependencies": {
44
  "@babel/core": "^7.3.3",
45
  "@babel/plugin-proposal-class-properties": "^7.3.3",
 
46
  "@babel/plugin-syntax-dynamic-import": "^7.2.0",
47
  "@babel/plugin-transform-react-jsx": "^7.3.0",
48
  "@babel/runtime-corejs2": "^7.3.1",
@@ -110,8 +113,11 @@
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
 
113
  "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
114
  "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
115
- "test-acceptance:build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
 
 
116
  }
117
  }
9
  "dependencies": {
10
  "@wordpress/a11y": "*",
11
  "@wordpress/api-fetch": "*",
12
+ "@wordpress/autop": "*",
13
+ "@wordpress/components": "*",
14
+ "@wordpress/compose": "*",
15
+ "@wordpress/data": "*",
16
+ "@wordpress/date": "*",
17
  "@wordpress/dom-ready": "*",
18
  "@wordpress/element": "*",
19
  "@wordpress/hooks": "*",
20
  "@wordpress/html-entities": "*",
21
  "@wordpress/i18n": "*",
22
  "@wordpress/is-shallow-equal": "*",
23
+ "@wordpress/keycodes": "*",
24
  "@wordpress/notices": "*",
25
+ "@wordpress/plugins": "*",
26
  "@wordpress/redux-routine": "*",
27
+ "@wordpress/rich-text": "*",
28
  "@wordpress/url": "*",
29
  "@wordpress/viewport": "*",
30
  "classnames": "^2.2.6",
31
  "contrast": "^1.0.1",
32
  "li": "^1.3.0",
33
+ "lodash": "^4.17.15",
34
  "memize": "^1.0.5",
35
  "react": "^16.6.3",
36
  "react-error-boundary": "^1.2.3",
38
  "react-select": "^2.4.1",
39
  "react-transition-group": "^2.0.0",
40
  "recharts": "^1.5.0",
41
+ "rememo": "^3.0.0",
42
+ "uri-templates": "^0.2.0",
43
+ "uuid": "^3.3.3"
44
  },
45
  "devDependencies": {
46
  "@babel/core": "^7.3.3",
47
  "@babel/plugin-proposal-class-properties": "^7.3.3",
48
+ "@babel/plugin-proposal-export-default-from": "^7.8.3",
49
  "@babel/plugin-syntax-dynamic-import": "^7.2.0",
50
  "@babel/plugin-transform-react-jsx": "^7.3.0",
51
  "@babel/runtime-corejs2": "^7.3.1",
113
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
114
  "test-unit:watch": "npm run test-unit -- --watch",
115
  "watch": "./node_modules/.bin/webpack --watch",
116
+ "test-up": "./bin/test-up.sh",
117
  "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
118
  "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
119
+ "test-cli": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpcli",
120
+ "test-upgrade": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run upgrade",
121
+ "test-build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
122
  }
123
  }
core/packages/components/src/checkbox-control/index.js ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { withInstanceId } from '@wordpress/compose';
5
+ import { BaseControl, Dashicon } from '@wordpress/components';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import './style.scss';
11
+
12
+ function CheckboxControl( { label, className, heading, checked, help, instanceId, onChange, indeterminate, ...props } ) {
13
+ const id = `inspector-checkbox-control-${ instanceId }`;
14
+ const onChangeValue = ( event ) => onChange( event.target.checked );
15
+
16
+ return (
17
+ <BaseControl label={ heading } id={ id } help={ help } className={ className }>
18
+ <span className="components-checkbox-control__input-container">
19
+ <input
20
+ id={ id }
21
+ className="components-checkbox-control__input"
22
+ type="checkbox"
23
+ value="1"
24
+ onChange={ onChangeValue }
25
+ checked={ checked }
26
+ aria-describedby={ !! help ? id + '__help' : undefined }
27
+ ref={ ( ref ) => {
28
+ if ( ref ) {
29
+ ref.indeterminate = indeterminate;
30
+ }
31
+ } }
32
+ { ...props }
33
+ />
34
+ { checked ? <Dashicon icon="yes" className="components-checkbox-control__checked" role="presentation" /> : null }
35
+ { indeterminate ? <Dashicon icon="minus" className="components-checkbox-control__checked components-checkbox-control__checked--indeterminate" role="presentation" /> : null }
36
+ </span>
37
+ <label className="components-checkbox-control__label" htmlFor={ id }>
38
+ { label }
39
+ </label>
40
+ </BaseControl>
41
+ );
42
+ }
43
+
44
+ export default withInstanceId( CheckboxControl );
core/packages/components/src/checkbox-control/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/checkbox-control/style.scss ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "mixins.scss";
2
+
3
+ $checkbox-input-size: 16px;
4
+ $checkbox-input-size-sm: 25px; // width + height for small viewports
5
+
6
+ .components-checkbox-control__input[type="checkbox"] {
7
+ border: 1px solid #b4b9be;
8
+ background: #fff;
9
+ color: #555;
10
+ clear: none;
11
+ cursor: pointer;
12
+ display: inline-block;
13
+ line-height: 0;
14
+ margin: 0 4px 0 0;
15
+ outline: 0;
16
+ padding: 0 !important;
17
+ text-align: center;
18
+ vertical-align: top;
19
+ width: $checkbox-input-size-sm;
20
+ height: $checkbox-input-size-sm;
21
+ @include break-small() {
22
+ height: $checkbox-input-size;
23
+ width: $checkbox-input-size;
24
+ }
25
+ -webkit-appearance: none;
26
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
27
+ transition: 0.05s border-color ease-in-out;
28
+
29
+ &:focus {
30
+ border-color: #5b9dd9;
31
+ box-shadow: 0 0 2px rgba(30, 140, 190, 0.8);
32
+ // Only visible in Windows High Contrast mode.
33
+ outline: 2px solid transparent;
34
+ }
35
+
36
+ &:checked {
37
+ background: #11a0d2;
38
+ border-color: #11a0d2;
39
+
40
+ // Hide default checkbox styles in IE.
41
+ &::-ms-check {
42
+ opacity: 0;
43
+ }
44
+ }
45
+
46
+ &:focus:checked {
47
+ border: none;
48
+ }
49
+
50
+ &:checked::before {
51
+ content: none;
52
+ }
53
+ }
54
+
55
+ .components-checkbox-control__input-container {
56
+ position: relative;
57
+ display: inline-block;
58
+ margin-right: 12px;
59
+ vertical-align: middle;
60
+ width: $checkbox-input-size-sm;
61
+ height: $checkbox-input-size-sm;
62
+ @include break-small() {
63
+ width: $checkbox-input-size;
64
+ height: $checkbox-input-size;
65
+ }
66
+ }
67
+
68
+ svg.dashicon.components-checkbox-control__checked {
69
+ fill: #fff;
70
+ cursor: pointer;
71
+ position: absolute;
72
+ left: -4px;
73
+ top: -2px;
74
+ width: 31px;
75
+ height: 31px;
76
+ @include break-small() {
77
+ width: 21px;
78
+ height: 21px;
79
+ left: -3px;
80
+ }
81
+ user-select: none;
82
+ pointer-events: none;
83
+ }
84
+
85
+
86
+ .components-checkbox-control__input[type="checkbox"] {
87
+ &:indeterminate {
88
+ background: #11a0d2;
89
+ border-color: #11a0d2;
90
+
91
+ // Hide default checkbox styles in IE.
92
+ &::-ms-check {
93
+ opacity: 0;
94
+ }
95
+ }
96
+
97
+ &:focus:checked,
98
+ &:focus:indeterminate {
99
+ border: none;
100
+ }
101
+
102
+ &:checked::before,
103
+ &:indeterminate::before {
104
+ content: none;
105
+ }
106
+ }
107
+
108
+ svg.dashicon.components-checkbox-control__checked.components-checkbox-control__checked--indeterminate {
109
+ width: 16px;
110
+ left: 0;
111
+ }
core/packages/components/src/checkbox-group-control/index.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { omit, isArray } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { CheckboxControl } from '@wordpress/components';
10
+
11
+ export default function CheckboxGroupControl( { value, onChange, options, label, help } ) {
12
+ let isChecked, update;
13
+
14
+ if ( isArray( value ) ) {
15
+ isChecked = ( option ) => value.includes( option.value );
16
+ update = ( option ) => ( checked ) => onChange( checked ? [ ...value, option.value ] : value.filter( ( maybeValue ) => maybeValue !== option.value ) );
17
+ } else {
18
+ isChecked = ( option ) => value[ option.value ] || false;
19
+ update = ( option ) => ( checked ) => onChange( { ...value, [ option.value ]: checked } );
20
+ }
21
+
22
+ return (
23
+ <fieldset className="components-base-control">
24
+ <div className="components-base-control__field">
25
+ <legend className="components-base-control__label">{ label }</legend>
26
+ { help && <p className="components-base-control__help">{ help }</p> }
27
+ { options.map( ( option ) => (
28
+ <div key={ option.value }>
29
+ <CheckboxControl { ...omit( option, [ 'value' ] ) } checked={ isChecked( option ) } onChange={ update( option ) } />
30
+ </div>
31
+ ) ) }
32
+ </div>
33
+ </fieldset>
34
+ );
35
+ }
core/packages/components/src/checkbox-group-control/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/hierarchical-checkbox-control/index.js ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isArray } from 'lodash';
5
+ import memize from 'memize';
6
+ import classnames from 'classnames';
7
+
8
+ /**
9
+ * WordPress dependencies
10
+ */
11
+ import { Component } from '@wordpress/element';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { CheckboxControl } from '../';
17
+ import './style.scss';
18
+
19
+ class Node {
20
+ tree;
21
+ name;
22
+ data;
23
+ parent;
24
+ children;
25
+
26
+ constructor( tree, name, data, parent = null, children = [] ) {
27
+ this.tree = tree;
28
+ this.name = name;
29
+ this.data = data;
30
+ this.parent = parent;
31
+ this.children = children;
32
+ }
33
+
34
+ /**
35
+ * Get the parent node.
36
+ * @return {Node|null}
37
+ */
38
+ getParent() {
39
+ if ( ! this.parent ) {
40
+ return null;
41
+ }
42
+
43
+ return this.tree.nodes[ this.parent ];
44
+ }
45
+
46
+ /**
47
+ * Get all parents of an option.
48
+ *
49
+ * @return {[string]}
50
+ */
51
+ getAllParents() {
52
+ const all = [];
53
+ let parent = this.getParent();
54
+
55
+ while ( parent ) {
56
+ all.push( parent.name );
57
+ parent = parent.getParent();
58
+ }
59
+
60
+ return all;
61
+ }
62
+
63
+ hasChildren() {
64
+ return this.children.length > 0;
65
+ }
66
+
67
+ /**
68
+ * Get all children.
69
+ *
70
+ * @return {[string]}
71
+ */
72
+ getAllChildren() {
73
+ const all = [];
74
+
75
+ if ( ! this.hasChildren() ) {
76
+ return all;
77
+ }
78
+
79
+ for ( const child of this ) {
80
+ all.push( child.name, ...child.getAllChildren() );
81
+ }
82
+
83
+ return all;
84
+ }
85
+
86
+ /**
87
+ * Iterator over all children of the node.
88
+ * @yields {Node}
89
+ */
90
+ *[Symbol.iterator]() {
91
+ for ( let i = 0; i < this.children.length; i++ ) {
92
+ const name = this.children[ i ];
93
+ yield this.tree.nodes[ name ];
94
+ }
95
+ }
96
+ }
97
+
98
+ class Tree {
99
+ nodes = {};
100
+ ordered = [];
101
+
102
+ add( name, data, parent = null ) {
103
+ this.ordered.push( name );
104
+
105
+ if ( this.nodes[ name ] ) {
106
+ this.nodes[ name ].data = data;
107
+ this.nodes[ name ].parent = parent;
108
+ } else {
109
+ this.nodes[ name ] = new Node( this, name, data, parent );
110
+ }
111
+
112
+ if ( parent ) {
113
+ if ( this.nodes[ parent ] ) {
114
+ this.nodes[ parent ].children.push( name );
115
+ } else {
116
+ this.nodes[ parent ] = new Node( this, parent );
117
+ }
118
+ }
119
+ }
120
+
121
+ *[Symbol.iterator]() {
122
+ for ( let i = 0; i < this.ordered.length; i++ ) {
123
+ const name = this.ordered[ i ];
124
+
125
+ if ( ! this.nodes[ name ].parent ) {
126
+ yield this.nodes[ name ];
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ const toTree = memize( ( options ) => {
133
+ const tree = new Tree();
134
+
135
+ for ( const option of options ) {
136
+ tree.add( option.value, option, option.parent );
137
+ }
138
+
139
+ return tree;
140
+ } );
141
+
142
+ class HierarchicalCheckboxControl extends Component {
143
+ props;
144
+
145
+ constructor() {
146
+ super( ...arguments );
147
+
148
+ this.renderOption = this.renderOption.bind( this );
149
+ this.isChecked = this.isChecked.bind( this );
150
+ this.isIndeterminate = this.isIndeterminate.bind( this );
151
+ this.onChange = this.onChange.bind( this );
152
+ }
153
+
154
+ indeterminate( ref ) {
155
+ ref.indeterminate = true;
156
+ }
157
+
158
+ /**
159
+ * Is the given option checked.
160
+ * @param {Node|null} value
161
+ * @return {boolean}
162
+ */
163
+ isChecked( value ) {
164
+ if ( ! value ) {
165
+ return false;
166
+ }
167
+
168
+ if ( isArray( this.props.value ) ) {
169
+ return this.props.value.includes( value.name ) || this.isChecked( value.getParent() );
170
+ }
171
+
172
+ return this.props.value[ value.name ] || this.isChecked( value.getParent() );
173
+ }
174
+
175
+ /**
176
+ * Does this option have an indeterminate value.
177
+ * @param {Node} option
178
+ * @return {boolean}
179
+ */
180
+ isIndeterminate( option ) {
181
+ if ( ! option.hasChildren() ) {
182
+ return false;
183
+ }
184
+
185
+ for ( const child of option ) {
186
+ if ( this.isChecked( child ) ) {
187
+ return true;
188
+ }
189
+
190
+ if ( this.isIndeterminate( child ) ) {
191
+ return true;
192
+ }
193
+ }
194
+
195
+ return false;
196
+ }
197
+
198
+ onChange( option, checked ) {
199
+ const values = [ option.name, ...option.getAllChildren() ];
200
+ const parents = checked ? [] : option.getAllParents();
201
+
202
+ if ( isArray( this.props.value ) ) {
203
+ let changed;
204
+
205
+ if ( checked ) {
206
+ changed = [ ...this.props.value, ...values ];
207
+ } else {
208
+ changed = this.props.value.filter( ( maybeValue ) => ! values.includes( maybeValue ) && ! parents.includes( maybeValue ) );
209
+ }
210
+
211
+ this.props.onChange( changed );
212
+ } else {
213
+ this.props.onChange( {
214
+ ...this.props.value,
215
+ ...values.reduce( ( acc, key ) => acc[ key ] = checked, {} ),
216
+ ...parents.reduce( ( acc, key ) => acc[ key ] = false, {} ),
217
+ } );
218
+ }
219
+ }
220
+
221
+ render() {
222
+ const { label, help, options } = this.props;
223
+ const tree = toTree( options );
224
+
225
+ return (
226
+ <fieldset className="components-base-control">
227
+ <div className="components-base-control__field">
228
+ <legend className="components-base-control__label">{ label }</legend>
229
+ { help && <p className="components-base-control__help">{ help }</p> }
230
+ </div>
231
+ <ul className="components-hierarchical-checkbox-control__group">
232
+ { Array.from( tree, this.renderOption ) }
233
+ </ul>
234
+ </fieldset>
235
+ );
236
+ }
237
+
238
+ /**
239
+ * Render a single option and its children.
240
+ * @param {Node} option
241
+ * @return {*}
242
+ */
243
+ renderOption( option ) {
244
+ const { value, selectable = true, ...rest } = option.data;
245
+ const checked = this.isChecked( option );
246
+ const indeterminate = ! checked && this.isIndeterminate( option );
247
+
248
+ return (
249
+ <li key={ value } className={ classnames( 'components-hierarchical-checkbox-control__option', {
250
+ 'components-hierarchical-checkbox-control__option--has-children': option.hasChildren(),
251
+ } ) }>
252
+ <CheckboxControl { ...rest }
253
+ checked={ selectable ? checked : false }
254
+ disabled={ ! selectable || this.props.disabled }
255
+ indeterminate={ indeterminate }
256
+ onChange={ ( newChecked ) => this.onChange( option, newChecked ) } />
257
+ { option.hasChildren() && (
258
+ <ul className="components-hierarchical-checkbox-control__group">
259
+ { Array.from( option, this.renderOption ) }
260
+ </ul>
261
+ ) }
262
+ </li>
263
+ );
264
+ }
265
+ }
266
+
267
+ export default HierarchicalCheckboxControl;
core/packages/components/src/hierarchical-checkbox-control/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/hierarchical-checkbox-control/style.scss ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .components-base-control > ul.components-hierarchical-checkbox-control__group {
2
+ padding-left: 0;
3
+ }
4
+
5
+ ul.components-hierarchical-checkbox-control__group {
6
+ padding-left: 1em;
7
+ }
8
+
9
+ .components-hierarchical-checkbox-control__group .components-hierarchical-checkbox-control__option {
10
+ list-style-type: none;
11
+ }
12
+
13
+ .components-hierarchical-checkbox-control__option--has-children {
14
+ margin-bottom: 1em;
15
+ }
16
+
17
+ .components-hierarchical-checkbox-control__option--has-children > .components-base-control {
18
+ font-weight: bold;
19
+ }
20
+
core/packages/components/src/index.js CHANGED
@@ -6,3 +6,10 @@ export { default as AsyncSelect } from './async-select';
6
  export { default as HoverDetector } from './hover-detector';
7
  export { default as CloseButton } from './close-button';
8
  export { default as Loader } from './loader';
 
 
 
 
 
 
 
6
  export { default as HoverDetector } from './hover-detector';
7
  export { default as CloseButton } from './close-button';
8
  export { default as Loader } from './loader';
9
+ export { default as CheckboxGroupControl } from './checkbox-group-control';
10
+ export { default as HierarchicalCheckboxControl } from './hierarchical-checkbox-control';
11
+ export { default as NoticeList } from './notice-list';
12
+ export { default as ModuleSettingsNoticeList } from './module-settings-notice-list';
13
+ export { default as ToggleControl } from './toggle-control';
14
+ export { default as CheckboxControl } from './checkbox-control';
15
+ export { default as TabPanel, ControlledTabPanel, ControlledMultiTabPanel } from './tab-panel';
core/packages/components/src/module-settings-notice-list/index.js ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { filter, omit } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { SnackbarList } from '@wordpress/components';
10
+ import { withSelect, withDispatch } from '@wordpress/data';
11
+ import { compose } from '@wordpress/compose';
12
+ import { Fragment, useEffect, useRef } from '@wordpress/element';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import Notice from './notice';
18
+ import './style.scss';
19
+
20
+ function usePrevious( value ) {
21
+ const ref = useRef();
22
+ useEffect( () => {
23
+ ref.current = value;
24
+ } );
25
+ return ref.current;
26
+ }
27
+
28
+ function calculateLength( notices ) {
29
+ if ( ! SnackbarList ) {
30
+ return notices.length;
31
+ }
32
+
33
+ let length = 0;
34
+
35
+ for ( const notice of notices ) {
36
+ if ( notice.type !== 'snackbar' ) {
37
+ length++;
38
+ }
39
+ }
40
+
41
+ return length;
42
+ }
43
+
44
+ function ModuleSettingsNoticeList( { notices, onRemove } ) {
45
+ const length = calculateLength( notices );
46
+ const prevLength = usePrevious( length );
47
+ useEffect( () => {
48
+ if ( length > prevLength && window.itsecSettingsPage ) {
49
+ window.itsecSettingsPage.scrollTop();
50
+ }
51
+ }, [ length, prevLength ] );
52
+
53
+ const createRemoveNotice = ( id ) => () => onRemove( id );
54
+ const snackbarNotices = SnackbarList ? filter( notices, {
55
+ type: 'snackbar',
56
+ } ) : [];
57
+
58
+ return (
59
+ <Fragment>
60
+ <div className="itsec-module-settings-notice-list">
61
+ { notices.map( ( notice ) => {
62
+ if ( notice.type === 'snackbar' && SnackbarList ) {
63
+ return null;
64
+ }
65
+
66
+ return (
67
+ <Notice
68
+ { ...omit( notice, [ 'content' ] ) }
69
+ key={ notice.id }
70
+ onRemove={ createRemoveNotice( notice.id ) }
71
+ >
72
+ { notice.content }
73
+ </Notice>
74
+ );
75
+ } ) }
76
+ </div>
77
+ { SnackbarList && <SnackbarList
78
+ notices={ snackbarNotices }
79
+ className="components-editor-notices__snackbar"
80
+ onRemove={ onRemove } />
81
+ }
82
+ </Fragment>
83
+ );
84
+ }
85
+
86
+ export default compose( [
87
+ withSelect( ( select, { context = 'ithemes-security' } ) => ( {
88
+ notices: select( 'core/notices' ).getNotices( context ),
89
+ } ) ),
90
+ withDispatch( ( dispatch, { context = 'ithemes-security' } ) => ( {
91
+ onRemove( noticeId ) {
92
+ return dispatch( 'core/notices' ).removeNotice( noticeId, context );
93
+ },
94
+ } ) ),
95
+ ] )( ModuleSettingsNoticeList );
core/packages/components/src/module-settings-notice-list/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/module-settings-notice-list/notice.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { noop } from 'lodash';
5
+ import classnames from 'classnames';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { __ } from '@wordpress/i18n';
11
+ import { Button } from '@wordpress/components';
12
+
13
+ function Notice( {
14
+ className,
15
+ status,
16
+ children,
17
+ onRemove = noop,
18
+ isDismissible = true,
19
+ actions = [],
20
+ } ) {
21
+ const classes = classnames( className, 'notice', 'notice-alt', 'notice-' + status, {
22
+ 'is-dismissible': isDismissible,
23
+ } );
24
+
25
+ return (
26
+ <div className={ classes }>
27
+ <p>
28
+ { children }
29
+ { actions.map( ( { className: buttonCustomClasses, label, onClick, url, isLink = false }, index ) => (
30
+ <Button
31
+ key={ index }
32
+ href={ url }
33
+ isSmall={ ! isLink && ! url }
34
+ isLink={ isLink || url }
35
+ onClick={ url ? undefined : () => {
36
+ onRemove();
37
+ onClick();
38
+ } }
39
+ className={ classnames( 'notice__action', buttonCustomClasses ) }
40
+ >
41
+ { label }
42
+ </Button>
43
+ ) ) }
44
+ </p>
45
+ { isDismissible && (
46
+ <button type="button" className="notice-dismiss" onClick={ onRemove }>
47
+ <span className="screen-reader-text">{ __( 'Dismiss this notice', 'better-wp-security' ) }</span>
48
+ </button>
49
+ ) }
50
+ </div>
51
+ );
52
+ }
53
+
54
+ export default Notice;
core/packages/components/src/module-settings-notice-list/style.scss ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-module-settings-notice-list .notice .notice__action {
2
+ margin-left: .5em;
3
+ }
4
+
5
+ .itsec-module-settings-notice-list {
6
+ position: relative;
7
+ padding-bottom: 1em;
8
+ font-size: 13px;
9
+
10
+ &:empty {
11
+ display: none;
12
+ }
13
+ }
14
+
15
+ .itsec-module-settings-content .components-editor-notices__snackbar {
16
+ position: fixed;
17
+ bottom: 100px;
18
+ padding-left: 16px;
19
+ padding-right: 16px;
20
+ width: max-content;
21
+ }
22
+
23
+ .itsec-module-settings-content .components-snackbar {
24
+ width: max-content;
25
+ }
core/packages/components/src/notice-list/index.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { filter } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { Fragment } from '@wordpress/element';
10
+ import { NoticeList, SnackbarList } from '@wordpress/components';
11
+ import { withSelect, withDispatch } from '@wordpress/data';
12
+ import { compose } from '@wordpress/compose';
13
+
14
+ function CompositeNoticeList( { notices, onRemove } ) {
15
+ const dismissibleNotices = filter( notices, ( notice ) => notice.isDismissible && ( ! notice.type || notice.type === 'default' ) );
16
+ const nonDismissibleNotices = filter( notices, ( notice ) => ! notice.isDismissible && ( ! notice.type || notice.type === 'default' ) );
17
+ const snackbarNotices = SnackbarList ? filter( notices, {
18
+ type: 'snackbar',
19
+ } ) : [];
20
+
21
+ return (
22
+ <Fragment>
23
+ <NoticeList
24
+ notices={ nonDismissibleNotices }
25
+ className="components-editor-notices__pinned" />
26
+ <NoticeList
27
+ notices={ dismissibleNotices }
28
+ className="components-editor-notices__dismissible"
29
+ onRemove={ onRemove } />
30
+ { SnackbarList && <SnackbarList
31
+ notices={ snackbarNotices }
32
+ className="components-editor-notices__snackbar"
33
+ onRemove={ onRemove } />
34
+ }
35
+ </Fragment>
36
+ );
37
+ }
38
+
39
+ export default compose( [
40
+ withSelect( ( select, { context = 'ithemes-security' } ) => ( {
41
+ notices: select( 'core/notices' ).getNotices( context ),
42
+ } ) ),
43
+ withDispatch( ( dispatch, { context = 'ithemes-security' } ) => ( {
44
+ onRemove( noticeId ) {
45
+ return dispatch( 'core/notices' ).removeNotice( noticeId, context );
46
+ },
47
+ } ) ),
48
+ ] )( CompositeNoticeList );
core/packages/components/src/notice-list/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/tab-panel/controlled.js ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+ import { find, noop, partial } from 'lodash';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { Component } from '@wordpress/element';
11
+ import { NavigableMenu } from '@wordpress/components';
12
+ import { withInstanceId } from '@wordpress/compose';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import TabButton from './tab-button';
18
+
19
+ class ControlledTabPanel extends Component {
20
+ constructor() {
21
+ super( ...arguments );
22
+
23
+ this.handleClick = this.handleClick.bind( this );
24
+ this.onNavigate = this.onNavigate.bind( this );
25
+ this.onKeyDown = this.onKeyDown.bind( this );
26
+ }
27
+
28
+ handleClick( tabKey ) {
29
+ const { onSelect = noop } = this.props;
30
+ onSelect( tabKey );
31
+ }
32
+
33
+ onNavigate( childIndex, child ) {
34
+ const event = this.event;
35
+
36
+ if ( event && event.target.getAttribute( 'role' ) === 'tab' ) {
37
+ event.preventDefault();
38
+ }
39
+
40
+ child.click();
41
+ }
42
+
43
+ onKeyDown( event ) {
44
+ // Stores the event for use in onNavigate. We don't need to persist the event
45
+ // since onNavigate is called during the original onKeyDown event handler.
46
+ this.event = event;
47
+ }
48
+
49
+ render() {
50
+ const {
51
+ activeClass = 'is-active',
52
+ className,
53
+ instanceId,
54
+ orientation = 'horizontal',
55
+ tabs,
56
+ selected,
57
+ } = this.props;
58
+
59
+ const selectedTab = find( tabs, { name: selected } ) || tabs[ 0 ];
60
+ const selectedId = instanceId + '-' + selectedTab.name;
61
+
62
+ return (
63
+ <div className={ className }>
64
+ <NavigableMenu
65
+ role="tablist"
66
+ orientation={ orientation }
67
+ onNavigate={ this.onNavigate }
68
+ onKeyDown={ this.onKeyDown }
69
+ className="components-tab-panel__tabs"
70
+ >
71
+ { tabs.map( ( tab ) => (
72
+ <TabButton className={ classnames( tab.className, { [ activeClass ]: tab.name === selectedTab.name } ) }
73
+ tabId={ instanceId + '-' + tab.name }
74
+ aria-controls={ instanceId + '-' + tab.name + '-view' }
75
+ selected={ tab.name === selectedTab.name }
76
+ key={ tab.name }
77
+ onClick={ partial( this.handleClick, tab.name ) }
78
+ >
79
+ { tab.title }
80
+ </TabButton>
81
+ ) ) }
82
+ </NavigableMenu>
83
+ { selectedTab && (
84
+ <div aria-labelledby={ selectedId }
85
+ role="tabpanel"
86
+ id={ selectedId + '-view' }
87
+ className="components-tab-panel__tab-content"
88
+ tabIndex="0"
89
+ >
90
+ { this.props.children( selectedTab ) }
91
+ </div>
92
+ ) }
93
+ </div>
94
+ );
95
+ }
96
+ }
97
+
98
+ export default withInstanceId( ControlledTabPanel );
core/packages/components/src/tab-panel/index.js ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { Component } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import ControlledTabPanel from './controlled';
10
+ import ControlledMultiTabPanel from './multi';
11
+
12
+ export { ControlledTabPanel, ControlledMultiTabPanel };
13
+
14
+ export default class UncontrolledTabPanel extends Component {
15
+ constructor() {
16
+ super( ...arguments );
17
+
18
+ this.state = {
19
+ selected: this.props.initialTab || '',
20
+ };
21
+ }
22
+
23
+ onSelect = ( selected ) => {
24
+ this.setState( { selected } );
25
+ };
26
+
27
+ render() {
28
+ return ( <ControlledTabPanel { ...this.props } selected={ this.state.selected } onSelect={ this.onSelect } /> );
29
+ }
30
+ }
core/packages/components/src/tab-panel/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/tab-panel/multi.js ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+ import { partial, map, find, findIndex } from 'lodash';
6
+
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { Component } from '@wordpress/element';
11
+ import { NavigableMenu } from '@wordpress/components';
12
+ import { withInstanceId, compose } from '@wordpress/compose';
13
+ import isShallowEqual from '@wordpress/is-shallow-equal';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { withPressedModifierKeys } from '@ithemes/security-hocs';
19
+ import TabButton from './tab-button';
20
+
21
+ class ControlledMultiTabPanel extends Component {
22
+ constructor() {
23
+ super( ...arguments );
24
+
25
+ this.handleClick = this.handleClick.bind( this );
26
+ this.onNavigate = this.onNavigate.bind( this );
27
+ this.onKeyDown = this.onKeyDown.bind( this );
28
+ this.toggleTab = this.toggleTab.bind( this );
29
+ this.getSelectedTabs = this.getSelectedTabs.bind( this );
30
+ this.isSelected = this.isSelected.bind( this );
31
+ this.getSelectedId = this.getSelectedId.bind( this );
32
+ this.getLabelledBy = this.getLabelledBy.bind( this );
33
+ this.getTabId = this.getTabId.bind( this );
34
+ this.getTabPanelId = this.getTabPanelId.bind( this );
35
+ this.isTabDisabled = this.isTabDisabled.bind( this );
36
+ this.isNonMultiSelectableTabSelected = this.isNonMultiSelectableTabSelected.bind( this );
37
+ }
38
+
39
+ handleClick( tabKey, event ) {
40
+ if ( event.metaKey || event.ctrlKey ) {
41
+ this.toggleTab( tabKey );
42
+ } else {
43
+ this.props.onSelect( [ tabKey ] );
44
+ }
45
+ }
46
+
47
+ onNavigate( childIndex, child ) {
48
+ const event = this.event;
49
+
50
+ if ( event ) {
51
+ if ( event.target.getAttribute( 'role' ) === 'tab' ) {
52
+ event.preventDefault();
53
+ }
54
+
55
+ if ( event.ctrlKey ) {
56
+ return;
57
+ }
58
+
59
+ if ( event.shiftKey ) {
60
+ if ( this.isTabDisabled( this.props.tabs[ childIndex ] ) ) {
61
+ return;
62
+ }
63
+
64
+ const name = this.props.tabs[ childIndex ].name;
65
+ this.toggleTab( name );
66
+
67
+ return;
68
+ }
69
+ }
70
+
71
+ child.click();
72
+ }
73
+
74
+ onKeyDown( event ) {
75
+ // onKeyDown is not omitted from the NavigableContainer GB-19694
76
+ if ( event.nativeEvent ) {
77
+ return;
78
+ }
79
+
80
+ // Stores the event for use in onNavigate. We don't need to persist the event
81
+ // since onNavigate is called during the original onKeyDown event handler.
82
+ this.event = event;
83
+
84
+ if ( event.ctrlKey && ( event.code === 'Space' || event.keyCode === 32 ) ) {
85
+ event.preventDefault();
86
+ const tabName = event.target.dataset.tabname;
87
+
88
+ if ( tabName ) {
89
+ this.toggleTab( tabName );
90
+ }
91
+ }
92
+ }
93
+
94
+ toggleTab( name ) {
95
+ const tab = find( this.props.tabs, { name } );
96
+
97
+ if ( tab && tab.allowMultiple === false ) {
98
+ return;
99
+ }
100
+
101
+ if ( this.props.selected.includes( name ) ) {
102
+ this.props.onSelect( this.props.selected.filter( ( maybeName ) => maybeName !== name ) );
103
+ } else {
104
+ this.props.onSelect( [
105
+ ...this.props.selected,
106
+ name,
107
+ ] );
108
+ }
109
+ }
110
+
111
+ getSelectedTabs() {
112
+ const selectedNames = this.props.selected;
113
+
114
+ if ( ! selectedNames.length && this.props.initialTab ) {
115
+ selectedNames.push( this.props.initialTab );
116
+ }
117
+
118
+ const tabs = [];
119
+
120
+ this.props.tabs.forEach( ( tab ) => {
121
+ if ( this.props.selected.includes( tab.name ) ) {
122
+ tabs.push( tab );
123
+ }
124
+ } );
125
+
126
+ return tabs;
127
+ }
128
+
129
+ isSelected( selectedTabs, maybeTab ) {
130
+ return selectedTabs.some( ( tab ) => tab.name === maybeTab.name );
131
+ }
132
+
133
+ isTabDisabled( tab ) {
134
+ const { pressedModifierKeys } = this.props;
135
+
136
+ if ( this.props.selected.includes( tab.name ) ) {
137
+ return false;
138
+ }
139
+
140
+ if ( tab.allowMultiple !== false && ! this.isNonMultiSelectableTabSelected() ) {
141
+ return false;
142
+ }
143
+
144
+ if ( pressedModifierKeys.meta || pressedModifierKeys.ctrl ) {
145
+ return true;
146
+ }
147
+
148
+ if ( pressedModifierKeys.shift ) {
149
+ const { activeElement } = document;
150
+
151
+ if ( activeElement.parentElement && activeElement.parentElement.id === `components-tab-panel__tabs-${ this.props.instanceId }` ) {
152
+ return true;
153
+ }
154
+ }
155
+
156
+ return false;
157
+ }
158
+
159
+ isNonMultiSelectableTabSelected() {
160
+ if ( this.props.selected.length !== 1 ) {
161
+ return false;
162
+ }
163
+
164
+ const selectedTab = find( this.props.tabs, { name: this.props.selected[ 0 ] } );
165
+
166
+ return selectedTab && selectedTab.allowMultiple === false;
167
+ }
168
+
169
+ getSelectedId( selectedTabs ) {
170
+ if ( selectedTabs.length === 1 ) {
171
+ return this.getTabPanelId( selectedTabs[ 0 ].name );
172
+ }
173
+
174
+ return `components-tab-panel__panel-${ this.props.instanceId }-${ map( selectedTabs, 'name' ).join( '-' ) }`;
175
+ }
176
+
177
+ getLabelledBy( selectedTabs ) {
178
+ return selectedTabs.map( ( tab ) => this.getTabId( tab.name ) ).join( ',' );
179
+ }
180
+
181
+ getTabId( tabName ) {
182
+ return `components-tab-panel__tab-${ this.props.instanceId }-${ tabName }`;
183
+ }
184
+
185
+ getTabPanelId( tabName ) {
186
+ return `components-tab-panel__panel-${ this.props.instanceId }-${ tabName }`;
187
+ }
188
+
189
+ componentDidUpdate( prevProps ) {
190
+ if ( this.props.selected.length !== 1 ) {
191
+ return;
192
+ }
193
+
194
+ if ( ! isShallowEqual( this.props.selected, prevProps.selected ) ) {
195
+ return;
196
+ }
197
+
198
+ const selected = this.props.selected[ 0 ];
199
+
200
+ if ( find( this.props.tabs, { name: selected } ) ) {
201
+ return;
202
+ }
203
+
204
+ const removedIndex = findIndex( prevProps.tabs, { name: selected } );
205
+
206
+ if ( removedIndex === -1 ) {
207
+ return;
208
+ }
209
+
210
+ const prevIndex = Math.max( removedIndex - 1, 0 );
211
+ const tab = this.props.tabs[ prevIndex ];
212
+
213
+ if ( tab ) {
214
+ this.props.onSelect( [ tab.name ] );
215
+ }
216
+ }
217
+
218
+ render() {
219
+ const {
220
+ tabs,
221
+ className,
222
+ activeClass = 'is-active',
223
+ orientation = 'horizontal',
224
+ } = this.props;
225
+
226
+ const selectedTabs = this.getSelectedTabs();
227
+ const selectedId = this.getSelectedId( selectedTabs );
228
+
229
+ return (
230
+ <div className={ className }>
231
+ <NavigableMenu
232
+ role="tablist"
233
+ aria-multiselectable
234
+ orientation={ orientation }
235
+ onNavigate={ this.onNavigate }
236
+ onKeyDown={ this.onKeyDown }
237
+ className="components-tab-panel__tabs"
238
+ id={ `components-tab-panel__tabs-${ this.props.instanceId }` }
239
+ >
240
+ { tabs.map( ( tab ) => {
241
+ const isSelected = this.isSelected( selectedTabs, tab );
242
+ const controls = isSelected && selectedTabs.length > 1 ? selectedId : this.getTabPanelId( tab.name );
243
+
244
+ return (
245
+ <TabButton className={ classnames( tab.className, { [ activeClass ]: isSelected } ) }
246
+ tabId={ this.getTabId( tab.name ) }
247
+ aria-controls={ controls }
248
+ selected={ isSelected }
249
+ disabled={ this.isTabDisabled( tab ) }
250
+ key={ tab.name }
251
+ onClick={ partial( this.handleClick, tab.name ) }
252
+ data-tabname={ tab.name }
253
+ >
254
+ { tab.title }
255
+ </TabButton>
256
+ );
257
+ } ) }
258
+ </NavigableMenu>
259
+ { selectedTabs.length > 0 && (
260
+ <div aria-labelledby={ this.getLabelledBy( selectedTabs ) }
261
+ role="tabpanel"
262
+ id={ selectedId }
263
+ className="components-tab-panel__tab-content"
264
+ tabIndex="0"
265
+ >
266
+ { this.props.children( selectedTabs ) }
267
+ </div>
268
+ ) }
269
+ </div>
270
+ );
271
+ }
272
+ }
273
+
274
+ export default compose( [
275
+ withInstanceId,
276
+ withPressedModifierKeys,
277
+ ] )( ControlledMultiTabPanel );
core/packages/components/src/tab-panel/tab-button.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { Button } from '@wordpress/components';
5
+
6
+ export default function TabButton( { tabId, onClick, children, selected, ...rest } ) {
7
+ return <Button role="tab"
8
+ tabIndex={ selected ? null : -1 }
9
+ aria-selected={ selected }
10
+ id={ tabId }
11
+ onClick={ onClick }
12
+ { ...rest }
13
+ >
14
+ { children }
15
+ </Button>;
16
+ }
core/packages/components/src/toggle-control/index.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { isFunction } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { Component } from '@wordpress/element';
10
+ import { withInstanceId } from '@wordpress/compose';
11
+ import { FormToggle, BaseControl } from '@wordpress/components';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import './style.scss';
17
+
18
+ /**
19
+ * This is a copy of core's toggle control but passes thru additional props to the FormToggle.
20
+ */
21
+ class ToggleControl extends Component {
22
+ constructor() {
23
+ super( ...arguments );
24
+
25
+ this.onChange = this.onChange.bind( this );
26
+ }
27
+
28
+ onChange( event ) {
29
+ if ( this.props.onChange ) {
30
+ this.props.onChange( event.target.checked );
31
+ }
32
+ }
33
+
34
+ render() {
35
+ const { label, checked, help, instanceId, ...props } = this.props;
36
+ const id = `inspector-toggle-control-${ instanceId }`;
37
+
38
+ let describedBy, helpLabel;
39
+ if ( help ) {
40
+ describedBy = id + '__help';
41
+ helpLabel = isFunction( help ) ? help( checked ) : help;
42
+ }
43
+
44
+ return (
45
+ <BaseControl
46
+ id={ id }
47
+ help={ helpLabel }
48
+ className="components-toggle-control"
49
+ >
50
+ <FormToggle
51
+ { ...props }
52
+ id={ id }
53
+ checked={ checked }
54
+ onChange={ this.onChange }
55
+ aria-describedby={ describedBy }
56
+ />
57
+ <label
58
+ htmlFor={ id }
59
+ className="components-toggle-control__label"
60
+ >
61
+ { label }
62
+ </label>
63
+ </BaseControl>
64
+ );
65
+ }
66
+ }
67
+
68
+ export default withInstanceId( ToggleControl );
core/packages/components/src/toggle-control/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/components/src/toggle-control/style.scss ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ .components-form-toggle__input:disabled + .components-form-toggle__track {
2
+ opacity: .5;
3
+ }
core/packages/data/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/data/src/actions.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { apiFetch } from './controls';
5
+
6
+ /**
7
+ * Fetch the index.
8
+ *
9
+ * @param {boolean} breakCache Whether to break the cache or not.
10
+ * @return {Object} The index.
11
+ */
12
+ export function* fetchIndex( breakCache = false ) {
13
+ let path = '/ithemes-security/v1?context=help';
14
+
15
+ if ( breakCache ) {
16
+ path += '&_=' + Date.now();
17
+ }
18
+
19
+ const index = yield apiFetch( { path } );
20
+ yield receiveIndex( index );
21
+
22
+ return index;
23
+ }
24
+
25
+ export function receiveIndex( index ) {
26
+ return {
27
+ type: RECEIVE_INDEX,
28
+ index,
29
+ };
30
+ }
31
+
32
+ export function receiveUser( user ) {
33
+ return {
34
+ type: RECEIVE_USER,
35
+ user,
36
+ };
37
+ }
38
+
39
+ export const RECEIVE_INDEX = 'RECEIVE_INDEX';
40
+ export const RECEIVE_USER = 'RECEIVE_USER';
core/packages/data/src/controls.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { uniqueId } from 'lodash';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { select as selectData, dispatch as dispatchData, subscribe } from '@wordpress/data';
10
+ import { default as triggerApiFetch } from '@wordpress/api-fetch';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { responseToError } from '@ithemes/security-utils';
16
+
17
+ /**
18
+ * Utility for returning a promise that handles a selector with a resolver.
19
+ *
20
+ * @param {Object} options
21
+ * @param {string} options.storeKey The store the selector belongs to
22
+ * @param {string} options.selectorName The selector name
23
+ * @param {Array} options.args The arguments fed to the selector
24
+ *
25
+ * @return {Promise} A promise for resolving the given selector.
26
+ */
27
+ const resolveSelect = ( { storeKey, selectorName, args } ) => {
28
+ return new Promise( ( resolve ) => {
29
+ const hasFinished = () => selectData( 'core/data' ).hasFinishedResolution( storeKey, selectorName, args );
30
+ const getResult = () => selectData( storeKey )[ selectorName ].apply( null, args );
31
+
32
+ // trigger the selector (to trigger the resolver)
33
+ const result = getResult();
34
+
35
+ if ( hasFinished() ) {
36
+ return resolve( result );
37
+ }
38
+
39
+ const unsubscribe = subscribe( () => {
40
+ if ( hasFinished() ) {
41
+ unsubscribe();
42
+ resolve( getResult() );
43
+ }
44
+ } );
45
+ } );
46
+ };
47
+
48
+ /**
49
+ * Trigger an API Fetch request.
50
+ *
51
+ * @param {Object} request API Fetch Request Object.
52
+ * @return {Object} control descriptor.
53
+ */
54
+ export function apiFetch( request ) {
55
+ return {
56
+ type: 'API_FETCH',
57
+ request,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Calls a selector using the current state.
63
+ * @param {string} storeKey Store key.
64
+ * @param {string} selectorName Selector name.
65
+ * @param {Array} args Selector arguments.
66
+ *
67
+ * @return {Object} control descriptor.
68
+ */
69
+ export function select( storeKey, selectorName, ...args ) {
70
+ return {
71
+ type: 'SELECT',
72
+ storeKey,
73
+ selectorName,
74
+ args,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Dispatches a control action for triggering a registry dispatch.
80
+ *
81
+ * @param {string} storeKey The key for the store the action belongs to
82
+ * @param {string} actionName The name of the action to dispatch
83
+ * @param {Array} args Arguments for the dispatch action.
84
+ *
85
+ * @example
86
+ * ```js
87
+ * import { dispatch } from '@wordpress/data-controls';
88
+ *
89
+ * // Action generator using dispatch
90
+ * export function* myAction() {
91
+ * yield dispatch( 'core/edit-post', 'togglePublishSidebar' );
92
+ * // do some other things.
93
+ * }
94
+ * ```
95
+ *
96
+ * @return {Object} The control descriptor.
97
+ */
98
+ export function dispatch( storeKey, actionName, ...args ) {
99
+ return {
100
+ type: 'DISPATCH',
101
+ storeKey,
102
+ actionName,
103
+ args,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Yields action objects used in signalling that a notice is to be created.
109
+ *
110
+ * @see @wordpress/notices#createNotice()
111
+ *
112
+ * @param {?string} status Notice status.
113
+ * Defaults to `info`.
114
+ * @param {string} content Notice message.
115
+ * @param {?Object} options Notice options.
116
+ * @param {?string} options.context Context under which to
117
+ * group notice.
118
+ * @param {?string} options.id Identifier for notice.
119
+ * Automatically assigned
120
+ * if not specified.
121
+ * @param {?boolean} options.isDismissible Whether the notice can
122
+ * be dismissed by user.
123
+ * Defaults to `true`.
124
+ * @param {?number} options.autoDismiss Whether the notice should
125
+ * by automatically dismissed
126
+ * after x milliseconds.
127
+ * Defaults to `false`.
128
+ * @param {?string} options.type Notice type. Either 'default' or 'snackbar'.
129
+ * @param {?Array<WPNoticeAction>} options.actions User actions to be
130
+ * presented with notice.
131
+ *
132
+ * @return {Object} control descriptor.
133
+ */
134
+ export function createNotice( status = 'info', content, options = {} ) {
135
+ return {
136
+ type: 'CREATE_NOTICE',
137
+ status,
138
+ content,
139
+ options: {
140
+ context: 'ithemes-security',
141
+ ...options,
142
+ },
143
+ };
144
+ }
145
+
146
+ const controls = {
147
+ API_FETCH( { request } ) {
148
+ return triggerApiFetch( request ).catch( responseToError );
149
+ },
150
+ SELECT( { storeKey, selectorName, args } ) {
151
+ const selector = selectData( storeKey )[ selectorName ];
152
+
153
+ if ( selector.hasResolver ) {
154
+ return resolveSelect( { storeKey, selectorName, args } );
155
+ }
156
+
157
+ return selector( ...args );
158
+ },
159
+ DISPATCH( { storeKey, actionName, args } ) {
160
+ return dispatchData( storeKey )[ actionName ]( ...args );
161
+ },
162
+ CREATE_NOTICE( { status, content, options } ) {
163
+ if ( options.autoDismiss ) {
164
+ options.id = options.id || uniqueId( 'itsec-auto-dismiss-' );
165
+ setTimeout( () => dispatchData( 'core/notices' ).removeNotice( options.id, options.context ), options.autoDismiss );
166
+ }
167
+
168
+ dispatchData( 'core/notices' ).createNotice( status, content, options );
169
+ },
170
+ };
171
+
172
+ export default controls;
core/packages/data/src/index.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { registerStore } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import controls from './controls';
10
+ import * as selectors from './selectors';
11
+ import * as resolvers from './resolvers';
12
+ import * as actions from './actions';
13
+ import reducer from './reducers';
14
+
15
+ registerStore( 'ithemes-security/core', {
16
+ controls,
17
+ selectors,
18
+ resolvers,
19
+ actions,
20
+ reducer,
21
+ } );
core/packages/data/src/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/data/src/reducers.js ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { RECEIVE_INDEX, RECEIVE_USER } from './actions';
5
+
6
+ const DEFAULT_STATE = {
7
+ users: {
8
+ byId: {},
9
+ },
10
+ index: null,
11
+ };
12
+
13
+ export default function reducer( state = DEFAULT_STATE, action ) {
14
+ switch ( action.type ) {
15
+ case RECEIVE_INDEX:
16
+ return {
17
+ ...state,
18
+ index: action.index,
19
+ };
20
+ case RECEIVE_USER:
21
+ return {
22
+ ...state,
23
+ users: {
24
+ ...state.users,
25
+ byId: {
26
+ ...state.users.byId,
27
+ [ action.user.id ]: action.user,
28
+ },
29
+ },
30
+ };
31
+ default:
32
+ return state;
33
+ }
34
+ }
core/packages/data/src/resolvers.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import { apiFetch } from './controls';
5
+ import { receiveIndex, receiveUser } from './actions';
6
+
7
+ export function* getIndex() {
8
+ const index = yield apiFetch( { path: '/ithemes-security/v1?context=help' } );
9
+ yield receiveIndex( index );
10
+ }
11
+
12
+ export const getUser = {
13
+ *fulfill( userId ) {
14
+ const user = yield apiFetch( {
15
+ path: `/wp/v2/users/${ userId }`,
16
+ } );
17
+
18
+ yield receiveUser( user );
19
+ },
20
+ isFulfilled( state, userId ) {
21
+ return !! state.users.byId[ userId ];
22
+ },
23
+ };
24
+
core/packages/data/src/selectors.js ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { select } from '@wordpress/data';
5
+
6
+ /**
7
+ * Get a WP User by its ID.
8
+ * @param {Object} state
9
+ * @param {number} userId
10
+ * @return {Object}
11
+ */
12
+ export function getUser( state, userId ) {
13
+ return state.users.byId[ userId ];
14
+ }
15
+
16
+ export function getIndex( state ) {
17
+ return state.index;
18
+ }
19
+
20
+ /**
21
+ * Get a schema from the root index.
22
+ * @param {Object} state
23
+ * @param {string} schemaId The full schema ID like ithemes-security-user-group
24
+ * @return {Object|null}
25
+ */
26
+ export function getSchema( state, schemaId ) {
27
+ const index = select( 'ithemes-security/core' ).getIndex();
28
+
29
+ if ( ! index ) {
30
+ return null;
31
+ }
32
+
33
+ for ( const route in index.routes ) {
34
+ if ( ! index.routes.hasOwnProperty( route ) ) {
35
+ continue;
36
+ }
37
+
38
+ const schema = index.routes[ route ].schema;
39
+
40
+ if ( schema && schema.title === schemaId ) {
41
+ return schema;
42
+ }
43
+ }
44
+
45
+ return null;
46
+ }
47
+
48
+ export function getRoles() {
49
+ const index = select( 'ithemes-security/core' ).getIndex();
50
+
51
+ if ( ! index ) {
52
+ return null;
53
+ }
54
+
55
+ return index.roles;
56
+ }
core/packages/hocs/src/index.js CHANGED
@@ -3,3 +3,4 @@ export { default as withDebounceHandler } from './with-debounce-handler';
3
  export { default as withPropChangeCallback } from './with-prop-change-callback';
4
  export { default as withInterval } from './with-interval';
5
  export { default as withWidth } from './with-width';
 
3
  export { default as withPropChangeCallback } from './with-prop-change-callback';
4
  export { default as withInterval } from './with-interval';
5
  export { default as withWidth } from './with-width';
6
+ export { default as withPressedModifierKeys } from './with-pressed-modifier-keys';
core/packages/hocs/src/with-pressed-modifier-keys.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { createHigherOrderComponent } from '@wordpress/compose';
5
+ import { Component } from '@wordpress/element';
6
+
7
+ export default createHigherOrderComponent( ( WrappedComponent ) => {
8
+ return class WithPressedModifierKeys extends Component {
9
+ state = {
10
+ pressed: {
11
+ shift: false,
12
+ ctrl: false,
13
+ meta: false,
14
+ alt: false,
15
+ },
16
+ };
17
+
18
+ mounted = false;
19
+
20
+ constructor() {
21
+ super( ...arguments );
22
+
23
+ this.listener = this.listener.bind( this );
24
+ this.onBlur = this.onBlur.bind( this );
25
+ }
26
+
27
+ componentDidMount() {
28
+ this.mounted = true;
29
+ window.addEventListener( 'keydown', this.listener );
30
+ window.addEventListener( 'keyup', this.listener );
31
+ window.addEventListener( 'click', this.listener );
32
+ window.addEventListener( 'blur', this.onBlur );
33
+ }
34
+
35
+ componentWillUnmount() {
36
+ this.mounted = false;
37
+ window.removeEventListener( 'keydown', this.listener );
38
+ window.removeEventListener( 'keyup', this.listener );
39
+ window.removeEventListener( 'click', this.listener );
40
+ window.removeEventListener( 'blur', this.onBlur );
41
+ }
42
+
43
+ /**
44
+ * Fires whenever a key is pressed down.
45
+ * @param {KeyboardEvent} e
46
+ */
47
+ listener( e ) {
48
+ if ( this.mounted ) {
49
+ this.setState( {
50
+ pressed: {
51
+ shift: e.shiftKey,
52
+ ctrl: e.ctrlKey,
53
+ meta: e.metaKey,
54
+ alt: e.altKey,
55
+ },
56
+ } );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * When the window blurs, remove all pressed modifier keys.
62
+ */
63
+ onBlur() {
64
+ this.setState( {
65
+ pressed: {
66
+ shift: false,
67
+ ctrl: false,
68
+ meta: false,
69
+ alt: false,
70
+ },
71
+ } );
72
+ }
73
+
74
+ render() {
75
+ return <WrappedComponent pressedModifierKeys={ this.state.pressed } { ...this.props } />;
76
+ }
77
+ };
78
+ }, 'withPressedModifierKeys' );
core/packages/i18n/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/i18n/src/index.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { _x } from '@wordpress/i18n';
2
+
3
+ export function getCanonicalRoles() {
4
+ return [
5
+ {
6
+ value: 'administrator',
7
+ label: _x( 'Administrator', 'User role', 'default' ),
8
+ },
9
+ {
10
+ value: 'editor',
11
+ label: _x( 'Editor', 'User role', 'default' ),
12
+ },
13
+ {
14
+ value: 'author',
15
+ label: _x( 'Author', 'User role', 'default' ),
16
+ },
17
+ {
18
+ value: 'contributor',
19
+ label: _x( 'Contributor', 'User role', 'default' ),
20
+ },
21
+ {
22
+ value: 'subscriber',
23
+ label: _x( 'Subscriber', 'User role', 'default' ),
24
+ },
25
+ ];
26
+ }
core/packages/i18n/src/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/preload/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/preload/src/index.js ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const cache = {};
2
+
3
+ /**
4
+ * Given a path, returns a normalized path where equal query parameter values
5
+ * will be treated as identical, regardless of order they appear in the original
6
+ * text.
7
+ *
8
+ * @param {string} path Original path.
9
+ *
10
+ * @return {string} Normalized path.
11
+ */
12
+ export function getStablePath( path ) {
13
+ const splitted = path.split( '?' );
14
+ const query = splitted[ 1 ];
15
+ const base = splitted[ 0 ];
16
+ if ( ! query ) {
17
+ return base;
18
+ }
19
+
20
+ // 'b=1&c=2&a=5'
21
+ return base + '?' + query
22
+ // [ 'b=1', 'c=2', 'a=5' ]
23
+ .split( '&' )
24
+ // [ [ 'b, '1' ], [ 'c', '2' ], [ 'a', '5' ] ]
25
+ .map( function( entry ) {
26
+ return entry.split( '=' );
27
+ } )
28
+ // [ [ 'a', '5' ], [ 'b, '1' ], [ 'c', '2' ] ]
29
+ .sort( function( a, b ) {
30
+ return a[ 0 ].localeCompare( b[ 0 ] );
31
+ } )
32
+ // [ 'a=5', 'b=1', 'c=2' ]
33
+ .map( function( pair ) {
34
+ return pair.join( '=' );
35
+ } )
36
+ // 'a=5&b=1&c=2'
37
+ .join( '&' );
38
+ }
39
+
40
+ export function createMiddleware( preloadedData ) {
41
+ Object.keys( preloadedData ).reduce( ( result, path ) => {
42
+ result[ getStablePath( path ) ] = preloadedData[ path ];
43
+ return result;
44
+ }, cache );
45
+
46
+ return ( options, next ) => {
47
+ const { parse = true } = options;
48
+ if ( typeof options.path === 'string' ) {
49
+ const method = options.method || 'GET';
50
+ const path = getStablePath( options.path );
51
+
52
+ if ( parse && 'GET' === method && cache[ path ] ) {
53
+ return Promise.resolve( cache[ path ].body );
54
+ } else if (
55
+ 'OPTIONS' === method &&
56
+ cache[ method ] &&
57
+ cache[ method ][ path ]
58
+ ) {
59
+ return Promise.resolve( cache[ method ][ path ] );
60
+ }
61
+ }
62
+
63
+ return next( options );
64
+ };
65
+ }
66
+
67
+ export function invalidatePreload( { path } ) {
68
+ delete cache[ getStablePath( path ) ];
69
+ }
70
+
71
+ export const CONTROL = 'INVALIDATE_API_FETCH_PRELOAD';
72
+
73
+ export function invalidatePreloadControl( path ) {
74
+ return {
75
+ type: CONTROL,
76
+ path,
77
+ };
78
+ }
core/packages/preload/src/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
core/packages/style-guide/src/breakpoints.scss CHANGED
@@ -4,3 +4,45 @@ $large: 960px;
4
  $medium: 782px;
5
  $small: 600px;
6
  $mobile: 480px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  $medium: 782px;
5
  $small: 600px;
6
  $mobile: 480px;
7
+
8
+ /**
9
+ * Breakpoints & Media Queries
10
+ */
11
+
12
+ // Most used breakpoints
13
+ $break-huge: 1440px;
14
+ $break-wide: 1280px;
15
+ $break-xlarge: 1080px;
16
+ $break-large: 960px; // admin sidebar auto folds
17
+ $break-medium: 782px; // adminbar goes big
18
+ $break-small: 600px;
19
+ $break-mobile: 480px;
20
+ $break-zoomed-in: 280px;
21
+
22
+ // All media queries currently in WordPress:
23
+ //
24
+ // min-width: 2000px
25
+ // min-width: 1680px
26
+ // min-width: 1250px
27
+ // max-width: 1120px *
28
+ // max-width: 1000px
29
+ // min-width: 769px and max-width: 1000px
30
+ // max-width: 960px *
31
+ // max-width: 900px
32
+ // max-width: 850px
33
+ // min-width: 800px and max-width: 1499px
34
+ // max-width: 800px
35
+ // max-width: 799px
36
+ // max-width: 782px *
37
+ // max-width: 768px
38
+ // max-width: 640px *
39
+ // max-width: 600px *
40
+ // max-width: 520px
41
+ // max-width: 500px
42
+ // max-width: 480px *
43
+ // max-width: 400px *
44
+ // max-width: 380px
45
+ // max-width: 320px *
46
+ //
47
+ // Those marked * seem to be more commonly used than the others.
48
+ // Let's try and use as few of these as possible, and be mindful about adding new ones, so we don't make the situation worse
core/packages/style-guide/src/mixins.scss CHANGED
@@ -1,4 +1,5 @@
1
  @import "colors.scss";
 
2
 
3
  @mixin sticky-table($spacing: 1em) {
4
  & thead th {
@@ -74,3 +75,55 @@
74
  }
75
  }
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  @import "colors.scss";
2
+ @import "breakpoints.scss";
3
 
4
  @mixin sticky-table($spacing: 1em) {
5
  & thead th {
75
  }
76
  }
77
  }
78
+
79
+ /**
80
+ * Breakpoint mixins
81
+ */
82
+
83
+ @mixin break-huge() {
84
+ @media (min-width: #{ ($break-huge) }) {
85
+ @content;
86
+ }
87
+ }
88
+
89
+ @mixin break-wide() {
90
+ @media (min-width: #{ ($break-wide) }) {
91
+ @content;
92
+ }
93
+ }
94
+
95
+ @mixin break-xlarge() {
96
+ @media (min-width: #{ ($break-xlarge) }) {
97
+ @content;
98
+ }
99
+ }
100
+
101
+ @mixin break-large() {
102
+ @media (min-width: #{ ($break-large) }) {
103
+ @content;
104
+ }
105
+ }
106
+
107
+ @mixin break-medium() {
108
+ @media (min-width: #{ ($break-medium) }) {
109
+ @content;
110
+ }
111
+ }
112
+
113
+ @mixin break-small() {
114
+ @media (min-width: #{ ($break-small) }) {
115
+ @content;
116
+ }
117
+ }
118
+
119
+ @mixin break-mobile() {
120
+ @media (min-width: #{ ($break-mobile) }) {
121
+ @content;
122
+ }
123
+ }
124
+
125
+ @mixin break-zoomed-in() {
126
+ @media (min-width: #{ ($break-zoomed-in) }) {
127
+ @content;
128
+ }
129
+ }
core/packages/utils/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
  /**
2
  * External dependencies
3
  */
4
- import { isPlainObject } from 'lodash';
5
 
6
  /**
7
  * Internal dependencies
@@ -149,12 +149,28 @@ export function entriesToObject( entries ) {
149
  return obj;
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  /**
153
  * Convert a response object from @wordpress/apiFetch to an Error object.
154
  *
155
  * @param {Object} response
156
  */
157
- export default function responseToError( response ) {
158
  if ( response instanceof Error ) {
159
  throw response;
160
  }
@@ -163,3 +179,44 @@ export default function responseToError( response ) {
163
  }
164
 
165
  export const MYSTERY_MAN_AVATAR = 'https://secure.gravatar.com/avatar/d7a973c7dab26985da5f961be7b74480?s=96&d=mm&f=y&r=g';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
  * External dependencies
3
  */
4
+ import { get, isPlainObject } from 'lodash';
5
 
6
  /**
7
  * Internal dependencies
149
  return obj;
150
  }
151
 
152
+ /**
153
+ * Splits a list into two arrays, with items that pass the filter in the first array, and ones that fail in the second.
154
+ * @param {Array} array
155
+ * @param {Function} filter
156
+ * @return {[][]}
157
+ */
158
+ export function bifurcate( array, filter ) {
159
+ const bifurcated = [ [], [] ];
160
+
161
+ for ( const value of array ) {
162
+ bifurcated[ filter( value ) ? 0 : 1 ].push( value );
163
+ }
164
+
165
+ return bifurcated;
166
+ }
167
+
168
  /**
169
  * Convert a response object from @wordpress/apiFetch to an Error object.
170
  *
171
  * @param {Object} response
172
  */
173
+ export function responseToError( response ) {
174
  if ( response instanceof Error ) {
175
  throw response;
176
  }
179
  }
180
 
181
  export const MYSTERY_MAN_AVATAR = 'https://secure.gravatar.com/avatar/d7a973c7dab26985da5f961be7b74480?s=96&d=mm&f=y&r=g';
182
+
183
+ /**
184
+ * Get the "self" link for a REST API object.
185
+ *
186
+ * @param {Object} object
187
+ * @return {string|undefined}
188
+ */
189
+ export function getSelf( object ) {
190
+ return getLink( object, 'self' );
191
+ }
192
+
193
+ /**
194
+ * Get the href for a link with the given relation.
195
+ *
196
+ * @param {Object} object
197
+ * @param {string} rel
198
+ * @return {string|undefined}
199
+ */
200
+ export function getLink( object, rel ) {
201
+ return get( object, [ '_links', rel, 0, 'href' ] );
202
+ }
203
+
204
+ /**
205
+ * Get a link from a schema document.
206
+ *
207
+ * @param {Object} schema
208
+ * @param {string} rel
209
+ *
210
+ * @return {Object|undefined}
211
+ */
212
+ export function getSchemaLink( schema, rel ) {
213
+ if ( ! schema || ! schema.links ) {
214
+ return;
215
+ }
216
+
217
+ for ( const link of schema.links ) {
218
+ if ( link.rel === rel ) {
219
+ return link;
220
+ }
221
+ }
222
+ }
core/packages/utils/src/wp-error.js CHANGED
@@ -124,4 +124,20 @@ export default class WPError {
124
 
125
  return this.#errorData[ code ];
126
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
124
 
125
  return this.#errorData[ code ];
126
  };
127
+
128
+ /**
129
+ * Get all error messages combined into one string.
130
+ * @return {Array<string>} All error messages combined into a single array ignoring code.
131
+ */
132
+ getAllErrorMessages = () => {
133
+ const messages = [];
134
+
135
+ for ( const errorCode in this.#errors ) {
136
+ if ( this.#errors.hasOwnProperty( errorCode ) ) {
137
+ messages.push( ...this.#errors[ errorCode ] );
138
+ }
139
+ }
140
+
141
+ return messages
142
+ };
143
  }
core/packages/webpack/src/babel.js CHANGED
@@ -11,5 +11,6 @@ module.exports = {
11
  } ],
12
  '@babel/plugin-proposal-class-properties',
13
  '@babel/plugin-syntax-dynamic-import',
 
14
  ],
15
  };
11
  } ],
12
  '@babel/plugin-proposal-class-properties',
13
  '@babel/plugin-syntax-dynamic-import',
14
+ '@babel/plugin-proposal-export-default-from',
15
  ],
16
  };
core/packages/webpack/src/config/index.js CHANGED
@@ -54,6 +54,7 @@ module.exports = function makeConfig( directory, pro ) {
54
  entry: {
55
  ...entries,
56
  'core/packages/components/site-scan-results/style': './core/packages/components/src/site-scan-results/style.scss',
 
57
  },
58
  output: {
59
  path: path.resolve( directory, 'dist' ),
@@ -174,6 +175,8 @@ module.exports = function makeConfig( directory, pro ) {
174
  '@ithemes/security-style-guide': path.resolve( directory, './core/packages/style-guide/src/index.js' ),
175
  '@ithemes/security-hocs': path.resolve( directory, './core/packages/hocs/src/index.js' ),
176
  '@ithemes/security-components': path.resolve( directory, './core/packages/components/src/index.js' ),
 
 
177
  ...Object.keys( entries ).reduce( function( acc, entry ) {
178
  const parts = entry.split( '/' );
179
  const alias = `@ithemes/security.${ parts[ 0 ] }.${ parts[ 1 ] }`;
54
  entry: {
55
  ...entries,
56
  'core/packages/components/site-scan-results/style': './core/packages/components/src/site-scan-results/style.scss',
57
+ 'packages/preload': './core/packages/preload/src/index.js',
58
  },
59
  output: {
60
  path: path.resolve( directory, 'dist' ),
175
  '@ithemes/security-style-guide': path.resolve( directory, './core/packages/style-guide/src/index.js' ),
176
  '@ithemes/security-hocs': path.resolve( directory, './core/packages/hocs/src/index.js' ),
177
  '@ithemes/security-components': path.resolve( directory, './core/packages/components/src/index.js' ),
178
+ '@ithemes/security-i18n': path.resolve( directory, './core/packages/i18n/src/index.js' ),
179
+ '@ithemes/security-data': path.resolve( directory, './core/packages/data/src/index.js' ),
180
  ...Object.keys( entries ).reduce( function( acc, entry ) {
181
  const parts = entry.split( '/' );
182
  const alias = `@ithemes/security.${ parts[ 0 ] }.${ parts[ 1 ] }`;
core/response.php CHANGED
@@ -10,6 +10,7 @@ final class ITSEC_Response {
10
  private $infos;
11
  private $success;
12
  private $js_function_calls;
 
13
  private $show_default_success_message;
14
  private $show_default_error_message;
15
  private $force_logout;
@@ -162,6 +163,14 @@ final class ITSEC_Response {
162
  } ) );
163
  }
164
 
 
 
 
 
 
 
 
 
165
  public static function set_show_default_success_message( $show_default_success_message ) {
166
  $self = self::get_instance();
167
 
@@ -334,6 +343,7 @@ final class ITSEC_Response {
334
  'messages' => $self->messages,
335
  'infos' => $self->infos,
336
  'functionCalls' => self::parse_js_function_calls_for_module_reloads(),
 
337
  'redirect' => $self->redirect,
338
  'closeModal' => $self->close_modal,
339
  'newNotifications' => $self->has_new_notifications,
@@ -365,6 +375,7 @@ final class ITSEC_Response {
365
  $this->infos = array();
366
  $this->success = true;
367
  $this->js_function_calls = array();
 
368
  $this->show_default_success_message = true;
369
  $this->show_default_error_message = true;
370
  $this->force_logout = false;
10
  private $infos;
11
  private $success;
12
  private $js_function_calls;
13
+ private $store_dispatches;
14
  private $show_default_success_message;
15
  private $show_default_error_message;
16
  private $force_logout;
163
  } ) );
164
  }
165
 
166
+ public static function add_store_dispatch( $store, $action, $args = array() ) {
167
+ self::get_instance()->store_dispatches[] = compact( 'store', 'action', 'args' );
168
+ }
169
+
170
+ public static function get_store_dispatches() {
171
+ return self::get_instance()->store_dispatches;
172
+ }
173
+
174
  public static function set_show_default_success_message( $show_default_success_message ) {
175
  $self = self::get_instance();
176
 
343
  'messages' => $self->messages,
344
  'infos' => $self->infos,
345
  'functionCalls' => self::parse_js_function_calls_for_module_reloads(),
346
+ 'storeDispatches' => $self->store_dispatches,
347
  'redirect' => $self->redirect,
348
  'closeModal' => $self->close_modal,
349
  'newNotifications' => $self->has_new_notifications,
375
  $this->infos = array();
376
  $this->success = true;
377
  $this->js_function_calls = array();
378
+ $this->store_dispatches = array();
379
  $this->show_default_success_message = true;
380
  $this->show_default_error_message = true;
381
  $this->force_logout = false;
core/rest.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_REST {
4
+ public function run() {
5
+ add_action( 'rest_api_init', array( $this, 'rest_api_init' ), 0 );
6
+ add_filter( 'rest_response_link_curies', array( $this, 'register_curie' ) );
7
+ add_filter( 'rest_namespace_index', array( $this, 'modify_index' ) );
8
+ add_filter( 'rest_user_collection_params', [ $this, 'register_global_users_query' ] );
9
+ add_filter( 'rest_user_query', [ $this, 'apply_global_users_query' ], 10, 2 );
10
+ }
11
+
12
+ /**
13
+ * Runs when the REST API is initialized.
14
+ */
15
+ public function rest_api_init() {
16
+ ITSEC_Modules::load_module_file( 'rest.php', ':active' );
17
+ }
18
+
19
+ /**
20
+ * Register the CURIE to shorten link refs.
21
+ *
22
+ * @param array $curies
23
+ *
24
+ * @return array
25
+ */
26
+ public function register_curie( $curies ) {
27
+ ITSEC_Lib::load( 'rest' );
28
+
29
+ $curies[] = array(
30
+ 'name' => 'ithemes-security',
31
+ 'href' => ITSEC_Lib_REST::LINK_REL . '{rel}',
32
+ 'templated' => true,
33
+ );
34
+
35
+ return $curies;
36
+ }
37
+
38
+ /**
39
+ * Modify the ithemes-security/v1 index to include some additional global information we need.
40
+ *
41
+ * @param WP_REST_Response $response
42
+ *
43
+ * @return WP_REST_Response
44
+ */
45
+ public function modify_index( $response ) {
46
+ if (
47
+ ITSEC_Core::current_user_can_manage() ||
48
+ current_user_can( 'create_users' ) ||
49
+ current_user_can( 'edit_users' ) ||
50
+ current_user_can( 'promote_users' )
51
+ ) {
52
+ $roles = [];
53
+
54
+ foreach ( wp_roles()->get_names() as $role => $label ) {
55
+ $roles[ $role ] = [
56
+ 'label' => translate_user_role( $label ),
57
+ 'canonical' => ITSEC_Lib_Canonical_Roles::get_canonical_role_from_role( $role ),
58
+ ];
59
+ }
60
+
61
+ $response->data['roles'] = $roles;
62
+ }
63
+
64
+ $response->data['supports'] = apply_filters( 'itsec_rest_supports', [] );
65
+
66
+ return $response;
67
+ }
68
+
69
+ /**
70
+ * Registers the "itsec_global" query parameter for the users endpoint.
71
+ *
72
+ * @param array $params
73
+ *
74
+ * @return array
75
+ */
76
+ public function register_global_users_query( $params ) {
77
+ if ( is_multisite() ) {
78
+ $params['itsec_global'] = [
79
+ 'description' => __( 'Return results for users across the entire network, not just the current site.', 'better-wp-security' ),
80
+ 'type' => 'boolean',
81
+ 'default' => false,
82
+ ];
83
+ }
84
+
85
+ return $params;
86
+ }
87
+
88
+ /**
89
+ * Applies the "itsec_global" query parameter.
90
+ *
91
+ * @param array $prepared_args
92
+ * @param WP_REST_Request $request
93
+ *
94
+ * @return array
95
+ */
96
+ public function apply_global_users_query( $prepared_args, $request ) {
97
+ if ( is_multisite() && $request['itsec_global'] && current_user_can( 'manage_network_users' ) ) {
98
+ $prepared_args['blog_id'] = null;
99
+ }
100
+
101
+ return $prepared_args;
102
+ }
103
+ }
core/setup.php CHANGED
@@ -3,16 +3,31 @@
3
  /**
4
  * Plugin activation, upgrade, deactivation and uninstall
5
  *
6
- * @package iThemes-Security
7
  * @since 4.0
 
8
  */
9
  final class ITSEC_Setup {
 
 
 
 
 
 
 
 
 
10
  public static function handle_activation() {
11
- self::setup_plugin_data();
 
 
 
 
12
 
13
  if ( ! ITSEC_Modules::get_setting( 'global', 'initial_build' ) ) {
14
  ITSEC_Modules::set_setting( 'global', 'initial_build', ITSEC_Core::get_plugin_build() );
15
  }
 
 
16
  }
17
 
18
  public static function handle_deactivation() {
@@ -45,15 +60,31 @@ final class ITSEC_Setup {
45
  }
46
  }
47
 
 
 
 
 
 
 
 
48
  public static function handle_upgrade( $build = false ) {
49
  if ( ! ITSEC_Modules::get_setting( 'global', 'initial_build' ) ) {
50
  ITSEC_Modules::set_setting( 'global', 'initial_build', ITSEC_Core::get_plugin_build() - 1 );
51
  }
52
 
53
- self::setup_plugin_data( $build );
54
  }
55
 
56
  private static function setup_plugin_data( $build = false ) {
 
 
 
 
 
 
 
 
 
57
  // Determine build number of current data if it was not passed in.
58
 
59
  if ( empty( $build ) ) {
@@ -91,8 +122,17 @@ final class ITSEC_Setup {
91
  ITSEC_Modules::set_setting( 'global', 'activation_timestamp', ITSEC_Core::get_current_time_gmt() );
92
  }
93
 
94
- // Ensure that the database tables are present and updated to the current schema.
95
- ITSEC_Lib::create_database_tables();
 
 
 
 
 
 
 
 
 
96
 
97
  // Run activation routines for modules to ensure that they are properly set up.
98
  $itsec_modules = ITSEC_Modules::get_instance();
@@ -171,6 +211,93 @@ final class ITSEC_Setup {
171
 
172
  // Update stored build number.
173
  ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
 
176
  private static function deactivate() {
@@ -277,7 +404,7 @@ final class ITSEC_Setup {
277
 
278
  if ( is_multisite() ) {
279
  $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
280
- $active_plugins = array_merge( $active_plugins, array_keys( $network_plugins ) );
281
  }
282
 
283
  return $active_plugins;
3
  /**
4
  * Plugin activation, upgrade, deactivation and uninstall
5
  *
 
6
  * @since 4.0
7
+ * @package iThemes-Security
8
  */
9
  final class ITSEC_Setup {
10
+ private static $protected = [
11
+ 4117,
12
+ ];
13
+
14
+ /**
15
+ * Setups plugin data when activating the plugin.
16
+ *
17
+ * @return true|WP_Error
18
+ */
19
  public static function handle_activation() {
20
+ $setup = self::setup_plugin_data();
21
+
22
+ if ( is_wp_error( $setup ) ) {
23
+ return $setup;
24
+ }
25
 
26
  if ( ! ITSEC_Modules::get_setting( 'global', 'initial_build' ) ) {
27
  ITSEC_Modules::set_setting( 'global', 'initial_build', ITSEC_Core::get_plugin_build() );
28
  }
29
+
30
+ return true;
31
  }
32
 
33
  public static function handle_deactivation() {
60
  }
61
  }
62
 
63
+ /**
64
+ * Setups plugin data for the latest build.
65
+ *
66
+ * @param int|false $build The build number to upgrade from. Pass false to use the current build.
67
+ *
68
+ * @return true|WP_Error
69
+ */
70
  public static function handle_upgrade( $build = false ) {
71
  if ( ! ITSEC_Modules::get_setting( 'global', 'initial_build' ) ) {
72
  ITSEC_Modules::set_setting( 'global', 'initial_build', ITSEC_Core::get_plugin_build() - 1 );
73
  }
74
 
75
+ return self::setup_plugin_data( $build );
76
  }
77
 
78
  private static function setup_plugin_data( $build = false ) {
79
+ // Ensure that the database tables are present and updated to the current schema.
80
+ $created = ITSEC_Lib::create_database_tables();
81
+
82
+ if ( is_wp_error( $created ) ) {
83
+ return $created;
84
+ }
85
+
86
+ ITSEC_Modules::initialize_container();
87
+
88
  // Determine build number of current data if it was not passed in.
89
 
90
  if ( empty( $build ) ) {
122
  ITSEC_Modules::set_setting( 'global', 'activation_timestamp', ITSEC_Core::get_current_time_gmt() );
123
  }
124
 
125
+ $mutex = null;
126
+
127
+ if ( self::should_do_protected_upgrade( $build ) ) {
128
+ $mutex = self::init_protected_upgrade();
129
+
130
+ if ( ! $mutex ) {
131
+ ITSEC_Storage::reload();
132
+
133
+ return new WP_Error( 'no_mutex', __( 'Failed to acquire a mutex.', 'better-wp-security' ) );
134
+ }
135
+ }
136
 
137
  // Run activation routines for modules to ensure that they are properly set up.
138
  $itsec_modules = ITSEC_Modules::get_instance();
211
 
212
  // Update stored build number.
213
  ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
214
+ ITSEC_Storage::save();
215
+
216
+ if ( $mutex ) {
217
+ $mutex->release();
218
+ }
219
+
220
+ return true;
221
+ }
222
+
223
+ /**
224
+ * Should the upgrade routine be done in a protected manner with locks.
225
+ *
226
+ * @param int $current_build
227
+ *
228
+ * @return bool
229
+ */
230
+ private static function should_do_protected_upgrade( $current_build ) {
231
+ foreach ( self::$protected as $build ) {
232
+ if ( $current_build < $build ) {
233
+ return true;
234
+ }
235
+ }
236
+
237
+ return false;
238
+ }
239
+
240
+ /**
241
+ * Initialize the protected upgrade.
242
+ *
243
+ * @return ITSEC_Mutex|null
244
+ */
245
+ private static function init_protected_upgrade() {
246
+ usleep( 50000 );
247
+ $mutex = ITSEC_Mutex::get( 'protected-upgrade', 5 );
248
+
249
+ if ( ! $mutex ) {
250
+ if ( self::wait_for_completed_upgrade() ) {
251
+ return null;
252
+ }
253
+
254
+ self::handle_uncompleted_upgrade();
255
+ }
256
+
257
+ usleep( 50000 );
258
+
259
+ if ( ! $mutex->exists() ) {
260
+ if ( self::wait_for_completed_upgrade() ) {
261
+ return null;
262
+ }
263
+
264
+ self::handle_uncompleted_upgrade();
265
+ }
266
+
267
+ return $mutex;
268
+ }
269
+
270
+ /**
271
+ * Wait for the upgrade routine to be completed.
272
+ *
273
+ * @return bool
274
+ */
275
+ private static function wait_for_completed_upgrade() {
276
+ $count = 0;
277
+
278
+ do {
279
+ $count ++;
280
+ usleep( 50000 );
281
+
282
+ if ( ITSEC_Core::get_plugin_build() === ITSEC_Core::get_saved_plugin_build( false ) ) {
283
+ return true;
284
+ }
285
+
286
+ } while ( $count < 20 );
287
+
288
+ return false;
289
+ }
290
+
291
+ /**
292
+ * Handle when an upgrade has not been able to be completed.
293
+ */
294
+ private static function handle_uncompleted_upgrade() {
295
+ if ( 'cli' === php_sapi_name() ) {
296
+ die( 'Upgrade in progress. Please try again later' );
297
+ }
298
+
299
+ wp_safe_redirect( $_SERVER['REQUEST_URI'] );
300
+ die;
301
  }
302
 
303
  private static function deactivate() {
404
 
405
  if ( is_multisite() ) {
406
  $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
407
+ $active_plugins = array_merge( $active_plugins, array_keys( $network_plugins ) );
408
  }
409
 
410
  return $active_plugins;
dist/8.min.js ADDED
@@ -0,0 +1 @@
 
1
+ (window.itsecWebpackJsonP=window.itsecWebpackJsonP||[]).push([[8],{"+8qv":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.css=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.css=function(e){var t=e.isDisabled,n=e.theme,r=n.spacing,o=n.colors;return{color:t?o.neutral40:o.neutral80,marginLeft:r.baseUnit/2,marginRight:r.baseUnit/2,maxWidth:"calc(100% - ".concat(2*r.baseUnit,"px)"),overflow:"hidden",position:"absolute",textOverflow:"ellipsis",whiteSpace:"nowrap",top:"50%",transform:"translateY(-50%)"}};var a=function(e){var t=e.children,n=e.className,r=e.cx,a=e.getStyles,s=e.isDisabled,l=e.innerProps;return o.default.createElement("div",i({className:r((0,u.css)(a("singleValue",e)),{"single-value":!0,"single-value--is-disabled":s},n)},l),t)};t.default=a},"+URl":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.LoadingIndicator=t.loadingIndicatorCSS=t.IndicatorSeparator=t.indicatorSeparatorCSS=t.ClearIndicator=t.clearIndicatorCSS=t.DropdownIndicator=t.dropdownIndicatorCSS=t.DownChevron=t.CrossIcon=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function a(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var s=function(e){var t=e.size,n=a(e,["size"]);return o.default.createElement("svg",i({height:t,width:t,viewBox:"0 0 20 20","aria-hidden":"true",focusable:"false",className:(0,u.css)({display:"inline-block",fill:"currentColor",lineHeight:1,stroke:"currentColor",strokeWidth:0})},n))},l=function(e){return o.default.createElement(s,i({size:20},e),o.default.createElement("path",{d:"M14.348 14.849c-0.469 0.469-1.229 0.469-1.697 0l-2.651-3.030-2.651 3.029c-0.469 0.469-1.229 0.469-1.697 0-0.469-0.469-0.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-0.469-0.469-0.469-1.228 0-1.697s1.228-0.469 1.697 0l2.652 3.031 2.651-3.031c0.469-0.469 1.228-0.469 1.697 0s0.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c0.469 0.469 0.469 1.229 0 1.698z"}))};t.CrossIcon=l;var c=function(e){return o.default.createElement(s,i({size:20},e),o.default.createElement("path",{d:"M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"}))};t.DownChevron=c;var p=function(e){var t=e.isFocused,n=e.theme,r=n.spacing.baseUnit,o=n.colors;return{color:t?o.neutral60:o.neutral20,display:"flex",padding:2*r,transition:"color 150ms",":hover":{color:t?o.neutral80:o.neutral40}}},f=p;t.dropdownIndicatorCSS=f;t.DropdownIndicator=function(e){var t=e.children,n=e.className,r=e.cx,a=e.getStyles,s=e.innerProps;return o.default.createElement("div",i({},s,{className:r((0,u.css)(a("dropdownIndicator",e)),{indicator:!0,"dropdown-indicator":!0},n)}),t||o.default.createElement(c,null))};var d=p;t.clearIndicatorCSS=d;t.ClearIndicator=function(e){var t=e.children,n=e.className,r=e.cx,a=e.getStyles,s=e.innerProps;return o.default.createElement("div",i({},s,{className:r((0,u.css)(a("clearIndicator",e)),{indicator:!0,"clear-indicator":!0},n)}),t||o.default.createElement(l,null))};t.indicatorSeparatorCSS=function(e){var t=e.isDisabled,n=e.theme,r=n.spacing.baseUnit,o=n.colors;return{alignSelf:"stretch",backgroundColor:t?o.neutral10:o.neutral20,marginBottom:2*r,marginTop:2*r,width:1}};t.IndicatorSeparator=function(e){var t=e.className,n=e.cx,r=e.getStyles,a=e.innerProps;return o.default.createElement("span",i({},a,{className:n((0,u.css)(r("indicatorSeparator",e)),{"indicator-separator":!0},t)}))};var h=!1;t.loadingIndicatorCSS=function(e){var t=e.isFocused,n=e.size,r=e.theme,o=r.colors,u=r.spacing.baseUnit;return{color:t?o.neutral60:o.neutral20,display:"flex",padding:2*u,transition:"color 150ms",alignSelf:"center",fontSize:n,lineHeight:1,marginRight:n,textAlign:"center",verticalAlign:"middle"}};var b=function(e){var t=e.color,n=e.delay,r=e.offset;return o.default.createElement("span",{className:(0,u.css)({animationDuration:"1s",animationDelay:"".concat(n,"ms"),animationIterationCount:"infinite",animationName:"react-select-loading-indicator",animationTimingFunction:"ease-in-out",backgroundColor:t,borderRadius:"1em",display:"inline-block",marginLeft:r?"1em":null,height:"1em",verticalAlign:"top",width:"1em"})})},m=function(e){var t=e.className,n=e.cx,r=e.getStyles,a=e.innerProps,s=e.isFocused,l=e.isRtl,c=e.theme.colors,p=s?c.neutral80:c.neutral20;return h||((0,u.injectGlobal)("@keyframes ","react-select-loading-indicator","{0%,80%,100%{opacity:0;}40%{opacity:1;}};"),h=!0),o.default.createElement("div",i({},a,{className:n((0,u.css)(r("loadingIndicator",e)),{indicator:!0,"loading-indicator":!0},t)}),o.default.createElement(b,{color:p,delay:0,offset:l}),o.default.createElement(b,{color:p,delay:160,offset:!0}),o.default.createElement(b,{color:p,delay:320,offset:!l}))};t.LoadingIndicator=m,m.defaultProps={size:4}},"/+00":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}var a=function(e){return o.default.createElement("span",i({className:(0,u.css)({zIndex:9999,border:0,clip:"rect(1px, 1px, 1px, 1px)",height:1,width:1,position:"absolute",overflow:"hidden",padding:0,whiteSpace:"nowrap",backgroundColor:"red",color:"blue"})},e))};t.default=a},"2cv3":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IndicatorsContainer=t.indicatorsContainerCSS=t.ValueContainer=t.valueContainerCSS=t.SelectContainer=t.containerCSS=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=n("PAeb");function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function a(e,t){return!t||"object"!==u(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function c(){return(c=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.containerCSS=function(e){var t=e.isDisabled;return{direction:e.isRtl?"rtl":null,pointerEvents:t?"none":null,position:"relative"}};t.SelectContainer=function(e){var t=e.children,n=e.className,u=e.cx,i=e.getStyles,a=e.innerProps,s=e.isDisabled,l=e.isRtl;return r.default.createElement("div",c({className:u((0,o.css)(i("container",e)),{"--is-disabled":s,"--is-rtl":l},n)},a),t)};t.valueContainerCSS=function(e){var t=e.theme.spacing;return{alignItems:"center",display:"flex",flex:1,flexWrap:"wrap",padding:"".concat(t.baseUnit/2,"px ").concat(2*t.baseUnit,"px"),WebkitOverflowScrolling:"touch",position:"relative",overflow:"hidden"}};var p=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),a(this,s(t).apply(this,arguments))}var n,u,c;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)}(t,r.Component),n=t,(u=[{key:"render",value:function(){var e=this.props,t=e.children,n=e.className,u=e.cx,i=e.isMulti,a=e.getStyles,s=e.hasValue;return r.default.createElement("div",{className:u((0,o.css)(a("valueContainer",this.props)),{"value-container":!0,"value-container--is-multi":i,"value-container--has-value":s},n)},t)}}])&&i(n.prototype,u),c&&i(n,c),t}();t.ValueContainer=p;t.indicatorsContainerCSS=function(){return{alignItems:"center",alignSelf:"stretch",display:"flex",flexShrink:0}};t.IndicatorsContainer=function(e){var t=e.children,n=e.className,u=e.cx,i=e.getStyles;return r.default.createElement("div",{className:u((0,o.css)(i("indicatorsContainer",e)),{indicators:!0},n)},t)}},"3lEq":function(e,t,n){"use strict";function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){try{return function e(t,n){if(t===n)return!0;if(t&&n&&"object"==r(t)&&"object"==r(n)){var a,s,l,c=o(t),p=o(n);if(c&&p){if((s=t.length)!=n.length)return!1;for(a=s;0!=a--;)if(!e(t[a],n[a]))return!1;return!0}if(c!=p)return!1;var f=t instanceof Date,d=n instanceof Date;if(f!=d)return!1;if(f&&d)return t.getTime()==n.getTime();var h=t instanceof RegExp,b=n instanceof RegExp;if(h!=b)return!1;if(h&&b)return t.toString()==n.toString();var m=u(t);if((s=m.length)!==u(n).length)return!1;for(a=s;0!=a--;)if(!i.call(n,m[a]))return!1;for(a=s;0!=a--;)if(!("_owner"===(l=m[a])&&t.$$typeof||e(t[l],n[l])))return!1;return!0}return t!=t&&n!=n}(e,t)}catch(e){if(e.message&&e.message.match(/stack|recursion/i))return console.warn("Warning: react-fast-compare does not handle circular references.",e.name,e.message),!1;throw e}};var o=Array.isArray,u=Object.keys,i=Object.prototype.hasOwnProperty},"59S1":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resultsAriaMessage=t.optionFocusAriaMessage=t.valueFocusAriaMessage=t.valueEventAriaMessage=t.instructionsAriaMessage=void 0;t.instructionsAriaMessage=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.isSearchable,r=t.isMulti,o=t.label;switch(e){case"menu":return"Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.";case"input":return"".concat(o||"Select"," is focused ").concat(n?",type to refine list":"",", press Down to open the menu, ").concat(r?" press left to focus selected values":"");case"value":return"Use left and right to toggle between focused values, press Backspace to remove the currently focused value"}};t.valueEventAriaMessage=function(e,t){var n=t.value;if(n)switch(e){case"deselect-option":case"pop-value":case"remove-value":return"option ".concat(n,", deselected.");case"select-option":return"option ".concat(n,", selected.")}};t.valueFocusAriaMessage=function(e){var t=e.focusedValue,n=e.getOptionLabel,r=e.selectValue;return"value ".concat(n(t)," focused, ").concat(r.indexOf(t)+1," of ").concat(r.length,".")};t.optionFocusAriaMessage=function(e){var t=e.focusedOption,n=e.getOptionLabel,r=e.options;return"option ".concat(n(t)," focused, ").concat(r.indexOf(t)+1," of ").concat(r.length,".")};t.resultsAriaMessage=function(e){var t=e.inputValue,n=e.screenReaderMessage;return"".concat(n).concat(t?" for search term "+t:"",".")}},"5bE3":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultComponents=t.components=void 0;var r=n("2cv3"),o=n("+URl"),u=h(n("rc6b")),i=d(n("n929")),a=h(n("KIQE")),s=d(n("Avrx")),l=d(n("lQ6M")),c=h(n("SrCC")),p=h(n("BCie")),f=h(n("+8qv"));function d(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}function h(e){return e&&e.__esModule?e:{default:e}}function b(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var m={ClearIndicator:o.ClearIndicator,Control:u.default,DropdownIndicator:o.DropdownIndicator,DownChevron:o.DownChevron,CrossIcon:o.CrossIcon,Group:i.default,GroupHeading:i.GroupHeading,IndicatorsContainer:r.IndicatorsContainer,IndicatorSeparator:o.IndicatorSeparator,Input:a.default,LoadingIndicator:o.LoadingIndicator,Menu:s.default,MenuList:s.MenuList,MenuPortal:s.MenuPortal,LoadingMessage:s.LoadingMessage,NoOptionsMessage:s.NoOptionsMessage,MultiValue:l.default,MultiValueContainer:l.MultiValueContainer,MultiValueLabel:l.MultiValueLabel,MultiValueRemove:l.MultiValueRemove,Option:c.default,Placeholder:p.default,SelectContainer:r.SelectContainer,SingleValue:f.default,ValueContainer:r.ValueContainer};t.components=m;t.defaultComponents=function(e){return function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){b(e,t,n[t])})}return e}({},m,e.components)}},"8oxB":function(e,t){var n,r,o=e.exports={};function u(){throw new Error("setTimeout has not been defined")}function i(){throw new Error("clearTimeout has not been defined")}function a(e){if(n===setTimeout)return setTimeout(e,0);if((n===u||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:u}catch(e){n=u}try{r="function"==typeof clearTimeout?clearTimeout:i}catch(e){r=i}}();var s,l=[],c=!1,p=-1;function f(){c&&s&&(c=!1,s.length?l=s.concat(l):p=-1,l.length&&d())}function d(){if(!c){var e=a(f);c=!0;for(var t=l.length;t;){for(s=l,l=[];++p<t;)s&&s[p].run();p=-1,t=l.length}s=null,c=!1,function(e){if(r===clearTimeout)return clearTimeout(e);if((r===i||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(e);try{r(e)}catch(t){try{return r.call(null,e)}catch(t){return r.call(this,e)}}}(e)}}function h(e,t){this.fun=e,this.array=t}function b(){}o.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];l.push(new h(e,t)),1!==l.length||c||a(d)},h.prototype.run=function(){this.fun.apply(null,this.array)},o.title="browser",o.browser=!0,o.env={},o.argv=[],o.version="",o.versions={},o.on=b,o.addListener=b,o.once=b,o.off=b,o.removeListener=b,o.removeAllListeners=b,o.emit=b,o.prependListener=b,o.prependOnceListener=b,o.listeners=function(e){return[]},o.binding=function(e){throw new Error("process.binding is not supported")},o.cwd=function(){return"/"},o.chdir=function(e){throw new Error("process.chdir is not supported")},o.umask=function(){return 0}},Avrx:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getMenuPlacement=O,t.MenuPortal=t.menuPortalCSS=t.LoadingMessage=t.NoOptionsMessage=t.loadingMessageCSS=t.noOptionsMessageCSS=t.MenuList=t.menuListCSS=t.default=t.MenuPlacer=t.menuCSS=void 0;var r,o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),u=n("PAeb"),i=n("faye"),a=(r=n("17x9"))&&r.__esModule?r:{default:r},s=n("b+PA");function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function c(){return(c=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function p(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function f(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function d(e,t,n){return t&&f(e.prototype,t),n&&f(e,n),e}function h(e,t){return!t||"object"!==l(t)&&"function"!=typeof t?g(e):t}function b(e){return(b=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function m(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&v(e,t)}function v(e,t){return(v=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function g(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function y(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function O(e){var t=e.maxHeight,n=e.menuEl,r=e.minHeight,o=e.placement,u=e.shouldScroll,i=e.isFixedPosition,a=e.theme.spacing,l=(0,s.getScrollParent)(n),c={placement:"bottom",maxHeight:t};if(!n||!n.offsetParent)return c;var p=l.getBoundingClientRect().height,f=n.getBoundingClientRect(),d=f.bottom,h=f.height,b=f.top,m=n.offsetParent.getBoundingClientRect().top,v=window.innerHeight,g=(0,s.getScrollTop)(l),y=parseInt(getComputedStyle(n).marginBottom,10),O=parseInt(getComputedStyle(n).marginTop,10),E=m-O,S=v-b,C=E+g,w=p-g-b,P=d-v+g+y,A=g+b-O;switch(o){case"auto":case"bottom":if(S>=h)return{placement:"bottom",maxHeight:t};if(w>=h&&!i)return u&&(0,s.animatedScrollTo)(l,P,160),{placement:"bottom",maxHeight:t};if(!i&&w>=r||i&&S>=r)return u&&(0,s.animatedScrollTo)(l,P,160),{placement:"bottom",maxHeight:i?S-y:w-y};if("auto"===o||i){var j=t,D=i?E:C;return D>=r&&(j=Math.min(D-y-a.controlHeight,t)),{placement:"top",maxHeight:j}}if("bottom"===o)return(0,s.scrollTo)(l,P),{placement:"bottom",maxHeight:t};break;case"top":if(E>=h)return{placement:"top",maxHeight:t};if(C>=h&&!i)return u&&(0,s.animatedScrollTo)(l,A,160),{placement:"top",maxHeight:t};if(!i&&C>=r||i&&E>=r){var M=t;return(!i&&C>=r||i&&E>=r)&&(M=i?E-O:C-O),u&&(0,s.animatedScrollTo)(l,A,160),{placement:"top",maxHeight:M}}return{placement:"bottom",maxHeight:t};default:throw new Error('Invalid placement provided "'.concat(o,'".'))}return c}var E=function(e){return"auto"===e?"bottom":e};t.menuCSS=function(e){var t,n=e.placement,r=e.theme,o=r.borderRadius,u=r.spacing,i=r.colors;return y(t={},function(e){return e?{bottom:"top",top:"bottom"}[e]:"bottom"}(n),"100%"),y(t,"backgroundColor",i.neutral0),y(t,"borderRadius",o),y(t,"boxShadow","0 0 0 1px hsla(0, 0%, 0%, 0.1), 0 4px 11px hsla(0, 0%, 0%, 0.1)"),y(t,"marginBottom",u.menuGutter),y(t,"marginTop",u.menuGutter),y(t,"position","absolute"),y(t,"width","100%"),y(t,"zIndex",1),t};var S=function(e){function t(){var e,n;p(this,t);for(var r=arguments.length,o=new Array(r),u=0;u<r;u++)o[u]=arguments[u];return y(g(g(n=h(this,(e=b(t)).call.apply(e,[this].concat(o))))),"state",{maxHeight:n.props.maxMenuHeight,placement:null}),y(g(g(n)),"getPlacement",function(e){var t=n.props,r=t.minMenuHeight,o=t.maxMenuHeight,u=t.menuPlacement,i=t.menuPosition,a=t.menuShouldScrollIntoView,s=t.theme,l=n.context.getPortalPlacement;if(e){var c="fixed"===i,p=O({maxHeight:o,menuEl:e,minHeight:r,placement:u,shouldScroll:a&&!c,isFixedPosition:c,theme:s});l&&l(p),n.setState(p)}}),y(g(g(n)),"getUpdatedProps",function(){var e=n.props.menuPlacement,t=n.state.placement||E(e);return function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){y(e,t,n[t])})}return e}({},n.props,{placement:t,maxHeight:n.state.maxHeight})}),n}return m(t,o.Component),d(t,[{key:"render",value:function(){return(0,this.props.children)({ref:this.getPlacement,placerProps:this.getUpdatedProps()})}}]),t}();t.MenuPlacer=S,y(S,"contextTypes",{getPortalPlacement:a.default.func});var C=function(e){var t=e.children,n=e.className,r=e.cx,i=e.getStyles,a=e.innerRef,s=e.innerProps,l=r((0,u.css)(i("menu",e)),{menu:!0},n);return o.default.createElement("div",c({className:l},s,{ref:a}),t)};t.default=C;t.menuListCSS=function(e){var t=e.maxHeight,n=e.theme.spacing.baseUnit;return{maxHeight:t,overflowY:"auto",paddingBottom:n,paddingTop:n,position:"relative",WebkitOverflowScrolling:"touch"}};t.MenuList=function(e){var t=e.children,n=e.className,r=e.cx,i=e.getStyles,a=e.isMulti,s=e.innerRef;return o.default.createElement("div",{className:r((0,u.css)(i("menuList",e)),{"menu-list":!0,"menu-list--is-multi":a},n),ref:s},t)};var w=function(e){var t=e.theme,n=t.spacing.baseUnit;return{color:t.colors.neutral40,padding:"".concat(2*n,"px ").concat(3*n,"px"),textAlign:"center"}},P=w;t.noOptionsMessageCSS=P;var A=w;t.loadingMessageCSS=A;var j=function(e){var t=e.children,n=e.className,r=e.cx,i=e.getStyles,a=e.innerProps;return o.default.createElement("div",c({className:r((0,u.css)(i("noOptionsMessage",e)),{"menu-notice":!0,"menu-notice--no-options":!0},n)},a),t)};t.NoOptionsMessage=j,j.defaultProps={children:"No options"};var D=function(e){var t=e.children,n=e.className,r=e.cx,i=e.getStyles,a=e.innerProps;return o.default.createElement("div",c({className:r((0,u.css)(i("loadingMessage",e)),{"menu-notice":!0,"menu-notice--loading":!0},n)},a),t)};t.LoadingMessage=D,D.defaultProps={children:"Loading..."};t.menuPortalCSS=function(e){var t=e.rect,n=e.offset,r=e.position;return{left:t.left,position:r,top:n,width:t.width,zIndex:1}};var M=function(e){function t(){var e,n;p(this,t);for(var r=arguments.length,o=new Array(r),u=0;u<r;u++)o[u]=arguments[u];return y(g(g(n=h(this,(e=b(t)).call.apply(e,[this].concat(o))))),"state",{placement:null}),y(g(g(n)),"getPortalPlacement",function(e){var t=e.placement;t!==E(n.props.menuPlacement)&&n.setState({placement:t})}),n}return m(t,o.Component),d(t,[{key:"getChildContext",value:function(){return{getPortalPlacement:this.getPortalPlacement}}},{key:"render",value:function(){var e=this.props,t=e.appendTo,n=e.children,r=e.controlElement,a=e.menuPlacement,l=e.menuPosition,c=e.getStyles,p="fixed"===l;if(!t&&!p||!r)return null;var f=this.state.placement||E(a),d=(0,s.getBoundingClientObj)(r),h=p?0:window.pageYOffset,b={offset:d[f]+h,position:l,rect:d},m=o.default.createElement("div",{className:(0,u.css)(c("menuPortal",b))},n);return t?(0,i.createPortal)(m,t):m}}]),t}();t.MenuPortal=M,y(M,"childContextTypes",{getPortalPlacement:a.default.func})},BCie:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.placeholderCSS=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.placeholderCSS=function(e){var t=e.theme,n=t.spacing;return{color:t.colors.neutral50,marginLeft:n.baseUnit/2,marginRight:n.baseUnit/2,position:"absolute",top:"50%",transform:"translateY(-50%)"}};var a=function(e){var t=e.children,n=e.className,r=e.cx,a=e.getStyles,s=e.innerProps;return o.default.createElement("div",i({className:r((0,u.css)(a("placeholder",e)),{placeholder:!0},n)},s),t)};t.default=a},CXKz:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n("cDcd"),o=n("GlKT"),u=n("SHH1");function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function c(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function p(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var f=!("undefined"==typeof window||!window.document||!window.document.createElement),d=0,h=function(e){function t(){var e,n,r,o;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);for(var u=arguments.length,a=new Array(u),l=0;l<u;l++)a[l]=arguments[l];return r=this,n=!(o=(e=s(t)).call.apply(e,[this].concat(a)))||"object"!==i(o)&&"function"!=typeof o?c(r):o,p(c(c(n)),"originalStyles",{}),p(c(c(n)),"listenerOptions",{capture:!1,passive:!1}),n}var n,h,b;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)}(t,r.Component),n=t,(h=[{key:"componentDidMount",value:function(){var e=this;if(f){var t=this.props,n=t.accountForScrollbars,r=t.touchScrollTarget,i=document.body,a=i&&i.style;if(n&&o.STYLE_KEYS.forEach(function(t){var n=a&&a[t];e.originalStyles[t]=n}),n&&d<1){var s=parseInt(this.originalStyles.paddingRight,10)||0,l=document.body?document.body.clientWidth:0,c=window.innerWidth-l+s||0;Object.keys(o.LOCK_STYLES).forEach(function(e){var t=o.LOCK_STYLES[e];a&&(a[e]=t)}),a&&(a.paddingRight="".concat(c,"px"))}i&&(0,u.isTouchDevice)()&&(i.addEventListener("touchmove",u.preventTouchMove,this.listenerOptions),r&&(r.addEventListener("touchstart",u.preventInertiaScroll,this.listenerOptions),r.addEventListener("touchmove",u.allowTouchMove,this.listenerOptions))),d+=1}}},{key:"componentWillUnmount",value:function(){var e=this;if(f){var t=this.props,n=t.accountForScrollbars,r=t.touchScrollTarget,i=document.body,a=i&&i.style;d=Math.max(d-1,0),n&&d<1&&o.STYLE_KEYS.forEach(function(t){var n=e.originalStyles[t];a&&(a[t]=n)}),i&&(0,u.isTouchDevice)()&&(i.removeEventListener("touchmove",u.preventTouchMove,this.listenerOptions),r&&(r.removeEventListener("touchstart",u.preventInertiaScroll,this.listenerOptions),r.removeEventListener("touchmove",u.allowTouchMove,this.listenerOptions)))}}},{key:"render",value:function(){return null}}])&&a(n.prototype,h),b&&a(n,b),t}();t.default=h,p(h,"defaultProps",{accountForScrollbars:!0})},FUBA:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},o=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n("cDcd"),i=s(u),a=s(n("17x9"));function s(e){return e&&e.__esModule?e:{default:e}}var l={position:"absolute",top:0,left:0,visibility:"hidden",height:0,overflow:"scroll",whiteSpace:"pre"},c=["extraWidth","injectStyles","inputClassName","inputRef","inputStyle","minWidth","onAutosize","placeholderIsMinWidth"],p=function(e,t){t.style.fontSize=e.fontSize,t.style.fontFamily=e.fontFamily,t.style.fontWeight=e.fontWeight,t.style.fontStyle=e.fontStyle,t.style.letterSpacing=e.letterSpacing,t.style.textTransform=e.textTransform},f=!("undefined"==typeof window||!window.navigator)&&/MSIE |Trident\/|Edge\//.test(window.navigator.userAgent),d=function(){return f?"_"+Math.random().toString(36).substr(2,12):void 0},h=function(e){function t(e){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var n=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.inputRef=function(e){n.input=e,"function"==typeof n.props.inputRef&&n.props.inputRef(e)},n.placeHolderSizerRef=function(e){n.placeHolderSizer=e},n.sizerRef=function(e){n.sizer=e},n.state={inputWidth:e.minWidth,inputId:e.id||d()},n}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,u.Component),o(t,[{key:"componentDidMount",value:function(){this.mounted=!0,this.copyInputStyles(),this.updateInputWidth()}},{key:"componentWillReceiveProps",value:function(e){var t=e.id;t!==this.props.id&&this.setState({inputId:t||d()})}},{key:"componentDidUpdate",value:function(e,t){t.inputWidth!==this.state.inputWidth&&"function"==typeof this.props.onAutosize&&this.props.onAutosize(this.state.inputWidth),this.updateInputWidth()}},{key:"componentWillUnmount",value:function(){this.mounted=!1}},{key:"copyInputStyles",value:function(){if(this.mounted&&window.getComputedStyle){var e=this.input&&window.getComputedStyle(this.input);e&&(p(e,this.sizer),this.placeHolderSizer&&p(e,this.placeHolderSizer))}}},{key:"updateInputWidth",value:function(){if(this.mounted&&this.sizer&&void 0!==this.sizer.scrollWidth){var e=void 0;e=this.props.placeholder&&(!this.props.value||this.props.value&&this.props.placeholderIsMinWidth)?Math.max(this.sizer.scrollWidth,this.placeHolderSizer.scrollWidth)+2:this.sizer.scrollWidth+2,(e+="number"===this.props.type&&void 0===this.props.extraWidth?16:parseInt(this.props.extraWidth)||0)<this.props.minWidth&&(e=this.props.minWidth),e!==this.state.inputWidth&&this.setState({inputWidth:e})}}},{key:"getInput",value:function(){return this.input}},{key:"focus",value:function(){this.input.focus()}},{key:"blur",value:function(){this.input.blur()}},{key:"select",value:function(){this.input.select()}},{key:"renderStyles",value:function(){var e=this.props.injectStyles;return f&&e?i.default.createElement("style",{dangerouslySetInnerHTML:{__html:"input#"+this.state.inputId+"::-ms-clear {display: none;}"}}):null}},{key:"render",value:function(){var e=[this.props.defaultValue,this.props.value,""].reduce(function(e,t){return null!=e?e:t}),t=r({},this.props.style);t.display||(t.display="inline-block");var n=r({boxSizing:"content-box",width:this.state.inputWidth+"px"},this.props.inputStyle),o=function(e,t){var n={};for(var r in e)t.indexOf(r)>=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}(this.props,[]);return function(e){c.forEach(function(t){return delete e[t]})}(o),o.className=this.props.inputClassName,o.id=this.state.inputId,o.style=n,i.default.createElement("div",{className:this.props.className,style:t},this.renderStyles(),i.default.createElement("input",r({},o,{ref:this.inputRef})),i.default.createElement("div",{ref:this.sizerRef,style:l},e),this.props.placeholder?i.default.createElement("div",{ref:this.placeHolderSizerRef,style:l},this.props.placeholder):null)}}]),t}();h.propTypes={className:a.default.string,defaultValue:a.default.any,extraWidth:a.default.oneOfType([a.default.number,a.default.string]),id:a.default.string,injectStyles:a.default.bool,inputClassName:a.default.string,inputRef:a.default.func,inputStyle:a.default.object,minWidth:a.default.oneOfType([a.default.number,a.default.string]),onAutosize:a.default.func,onChange:a.default.func,placeholder:a.default.string,placeholderIsMinWidth:a.default.bool,style:a.default.object,value:a.default.any},h.defaultProps={minWidth:1,injectStyles:!0},t.default=h},GlKT:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.LOCK_STYLES=t.STYLE_KEYS=void 0;t.STYLE_KEYS=["boxSizing","height","overflow","paddingRight","position"];t.LOCK_STYLES={boxSizing:"border-box",overflow:"hidden",position:"relative",height:"100%"}},H0AD:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.makeAsyncSelect=t.defaultProps=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=a(n("jMYa")),u=n("b+PA"),i=a(n("oUUL"));function a(e){return e&&e.__esModule?e:{default:e}}function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function l(){return(l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function c(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function p(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function f(e){return(f=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function d(e,t){return(d=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function h(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function b(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var m={cacheOptions:!1,defaultOptions:!1,filterOption:null};t.defaultProps=m;var v=function(e){var t,n;return n=t=function(t){function n(e){var t,r,o;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),r=this,o=f(n).call(this),t=!o||"object"!==s(o)&&"function"!=typeof o?h(r):o,b(h(h(t)),"select",void 0),b(h(h(t)),"lastRequest",void 0),b(h(h(t)),"mounted",!1),b(h(h(t)),"optionsCache",{}),b(h(h(t)),"handleInputChange",function(e,n){var r=t.props,o=r.cacheOptions,i=r.onInputChange,a=(0,u.handleInputChange)(e,n,i);if(!a)return delete t.lastRequest,void t.setState({inputValue:"",loadedInputValue:"",loadedOptions:[],isLoading:!1,passEmptyOptions:!1});if(o&&t.optionsCache[a])t.setState({inputValue:a,loadedInputValue:a,loadedOptions:t.optionsCache[a],isLoading:!1,passEmptyOptions:!1});else{var s=t.lastRequest={};t.setState({inputValue:a,isLoading:!0,passEmptyOptions:!t.state.loadedInputValue},function(){t.loadOptions(a,function(e){t.mounted&&(e&&(t.optionsCache[a]=e),s===t.lastRequest&&(delete t.lastRequest,t.setState({isLoading:!1,loadedInputValue:a,loadedOptions:e||[],passEmptyOptions:!1})))})})}return a}),t.state={defaultOptions:Array.isArray(e.defaultOptions)?e.defaultOptions:void 0,inputValue:void 0!==e.inputValue?e.inputValue:"",isLoading:!0===e.defaultOptions,loadedOptions:[],passEmptyOptions:!1},t}var o,i,a;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&d(e,t)}(n,r.Component),o=n,(i=[{key:"componentDidMount",value:function(){var e=this;this.mounted=!0;var t=this.props.defaultOptions,n=this.state.inputValue;!0===t&&this.loadOptions(n,function(t){if(e.mounted){var n=!!e.lastRequest;e.setState({defaultOptions:t||[],isLoading:n})}})}},{key:"componentWillReceiveProps",value:function(e){e.cacheOptions!==this.props.cacheOptions&&(this.optionsCache={}),e.defaultOptions!==this.props.defaultOptions&&this.setState({defaultOptions:Array.isArray(e.defaultOptions)?e.defaultOptions:void 0})}},{key:"componentWillUnmount",value:function(){this.mounted=!1}},{key:"focus",value:function(){this.select.focus()}},{key:"blur",value:function(){this.select.blur()}},{key:"loadOptions",value:function(e,t){var n=this.props.loadOptions;if(!n)return t();var r=n(e,t);r&&"function"==typeof r.then&&r.then(t,function(){return t()})}},{key:"render",value:function(){var t=this,n=this.props,o=(n.loadOptions,c(n,["loadOptions"])),u=this.state,i=u.defaultOptions,a=u.inputValue,s=u.isLoading,p=u.loadedInputValue,f=u.loadedOptions,d=u.passEmptyOptions?[]:a&&p?f:i||[];return r.default.createElement(e,l({},o,{ref:function(e){t.select=e},options:d,isLoading:s,onInputChange:this.handleInputChange}))}}])&&p(o.prototype,i),a&&p(o,a),n}(),b(t,"defaultProps",m),n};t.makeAsyncSelect=v;var g=v((0,i.default)(o.default));t.default=g},H6Zs:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=n("PAeb");function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function a(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function s(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function l(e,t){return!t||"object"!==u(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function p(e,t){return(p=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}var f=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),l(this,c(t).apply(this,arguments))}var n,u,f;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&p(e,t)}(t,r.Component),n=t,(u=[{key:"render",value:function(){var e=this.props,t=(e.in,e.out,e.onExited,e.appear,e.enter,e.exit,e.innerRef),n=(e.emotion,a(e,["in","out","onExited","appear","enter","exit","innerRef","emotion"]));return r.default.createElement("input",i({ref:t},n,{className:(0,o.css)({background:0,border:0,fontSize:"inherit",outline:0,padding:0,width:1,color:"transparent",left:-100,opacity:0,position:"relative",transform:"scale(0)"})}))}}])&&s(n.prototype,u),f&&s(n,f),t}();t.default=f},KIQE:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.inputCSS=void 0;var r=i(n("cDcd")),o=n("PAeb"),u=i(n("FUBA"));function i(e){return e&&e.__esModule?e:{default:e}}function a(){return(a=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function s(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){l(e,t,n[t])})}return e}function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function c(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}t.inputCSS=function(e){var t=e.isDisabled,n=e.theme,r=n.spacing,o=n.colors;return{margin:r.baseUnit/2,paddingBottom:r.baseUnit/2,paddingTop:r.baseUnit/2,visibility:t?"hidden":"visible",color:o.neutral80}};var p=function(e){return{background:0,border:0,fontSize:"inherit",opacity:e?0:1,outline:0,padding:0,color:"inherit"}},f=function(e){var t=e.className,n=e.cx,i=e.getStyles,l=e.innerRef,f=e.isHidden,d=e.isDisabled,h=e.theme,b=(e.selectProps,c(e,["className","cx","getStyles","innerRef","isHidden","isDisabled","theme","selectProps"]));return r.default.createElement("div",{className:(0,o.css)(i("input",s({theme:h},b)))},r.default.createElement(u.default,a({className:n(null,{input:!0},t),inputRef:l,inputStyle:p(f),disabled:d},b)))};t.default=f},KXCe:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultTheme=t.spacing=t.colors=void 0;var r={primary:"#2684FF",primary75:"#4C9AFF",primary50:"#B2D4FF",primary25:"#DEEBFF",danger:"#DE350B",dangerLight:"#FFBDAD",neutral0:"hsl(0, 0%, 100%)",neutral5:"hsl(0, 0%, 95%)",neutral10:"hsl(0, 0%, 90%)",neutral20:"hsl(0, 0%, 80%)",neutral30:"hsl(0, 0%, 70%)",neutral40:"hsl(0, 0%, 60%)",neutral50:"hsl(0, 0%, 50%)",neutral60:"hsl(0, 0%, 40%)",neutral70:"hsl(0, 0%, 30%)",neutral80:"hsl(0, 0%, 20%)",neutral90:"hsl(0, 0%, 10%)"};t.colors=r;var o={baseUnit:4,controlHeight:38,menuGutter:8};t.spacing=o;var u={borderRadius:4,colors:r,spacing:o};t.defaultTheme=u},N3bB:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=n("cDcd"),o=n("faye");function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function i(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function a(e,t){return!t||"object"!==u(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}var c=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),a(this,s(t).apply(this,arguments))}var n,u,c;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)}(t,r.Component),n=t,(u=[{key:"componentDidMount",value:function(){this.props.innerRef((0,o.findDOMNode)(this))}},{key:"componentWillUnmount",value:function(){this.props.innerRef(null)}},{key:"render",value:function(){return this.props.children}}])&&i(n.prototype,u),c&&i(n,c),t}();t.default=c},PAeb:function(e,t,n){"use strict";n.r(t),function(e){n.d(t,"flush",function(){return i}),n.d(t,"hydrate",function(){return a}),n.d(t,"cx",function(){return s}),n.d(t,"merge",function(){return l}),n.d(t,"getRegisteredStyles",function(){return c}),n.d(t,"injectGlobal",function(){return p}),n.d(t,"keyframes",function(){return f}),n.d(t,"css",function(){return d}),n.d(t,"sheet",function(){return h}),n.d(t,"caches",function(){return b});var r=n("oj4i"),o=void 0!==e?e:{},u=Object(r.a)(o),i=u.flush,a=u.hydrate,s=u.cx,l=u.merge,c=u.getRegisteredStyles,p=u.injectGlobal,f=u.keyframes,d=u.css,h=u.sheet,b=u.caches}.call(this,n("yLpj"))},QcsS:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=n("PAeb"),u=a(n("N3bB")),i=a(n("CXKz"));function a(e){return e&&e.__esModule?e:{default:e}}function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function l(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function c(e){return(c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function p(e,t){return(p=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function f(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function d(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var h=function(e){function t(){var e,n,r,o;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);for(var u=arguments.length,i=new Array(u),a=0;a<u;a++)i[a]=arguments[a];return r=this,o=(e=c(t)).call.apply(e,[this].concat(i)),n=!o||"object"!==s(o)&&"function"!=typeof o?f(r):o,d(f(f(n)),"state",{touchScrollTarget:null}),d(f(f(n)),"getScrollTarget",function(e){e!==n.state.touchScrollTarget&&n.setState({touchScrollTarget:e})}),d(f(f(n)),"blurSelectInput",function(){document.activeElement&&document.activeElement.blur()}),n}var n,a,h;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&p(e,t)}(t,r.PureComponent),n=t,(a=[{key:"render",value:function(){var e=this.props,t=e.children,n=e.isEnabled,a=this.state.touchScrollTarget;return n?r.default.createElement("div",null,r.default.createElement("div",{onClick:this.blurSelectInput,className:(0,o.css)({position:"fixed",left:0,bottom:0,right:0,top:0})}),r.default.createElement(u.default,{innerRef:this.getScrollTarget},t),a?r.default.createElement(i.default,{touchScrollTarget:a}):null):t}}])&&l(n.prototype,a),h&&l(n,h),t}();t.default=h},SHH1:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preventTouchMove=function(e){e.preventDefault()},t.allowTouchMove=function(e){e.stopPropagation()},t.preventInertiaScroll=function(){var e=this.scrollTop,t=this.scrollHeight,n=e+this.offsetHeight;0===e?this.scrollTop=1:n===t&&(this.scrollTop=e-1)},t.isTouchDevice=function(){return"ontouchstart"in window||navigator.maxTouchPoints}},SrCC:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.optionCSS=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.optionCSS=function(e){var t=e.isDisabled,n=e.isFocused,r=e.isSelected,o=e.theme,u=o.spacing,i=o.colors;return{backgroundColor:r?i.primary:n?i.primary25:"transparent",color:t?i.neutral20:r?i.neutral0:"inherit",cursor:"default",display:"block",fontSize:"inherit",padding:"".concat(2*u.baseUnit,"px ").concat(3*u.baseUnit,"px"),width:"100%",userSelect:"none",WebkitTapHighlightColor:"rgba(0, 0, 0, 0)",":active":{backgroundColor:r?i.primary:i.primary50}}};var a=function(e){var t=e.children,n=e.className,r=e.cx,a=e.getStyles,s=e.isDisabled,l=e.isFocused,c=e.isSelected,p=e.innerRef,f=e.innerProps;return o.default.createElement("div",i({ref:p,className:r((0,u.css)(a("option",e)),{option:!0,"option--is-disabled":s,"option--is-focused":l,"option--is-selected":c},n)},f),t)};t.default=a},TAZq:function(e,t,n){e.exports=function(){"use strict";return function(e){function t(t){if(t)try{e(t+"}")}catch(e){}}return function(n,r,o,u,i,a,s,l,c,p){switch(n){case 1:if(0===c&&64===r.charCodeAt(0))return e(r+";"),"";break;case 2:if(0===l)return r+"/*|*/";break;case 3:switch(l){case 102:case 112:return e(o[0]+r),"";default:return r+(0===p?"/*|*/":"")}case-2:r.split("/*|*/}").forEach(t)}}}}()},WvHe:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mergeStyles=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){d(e,t,n[t])})}return e}({},e);return Object.keys(t).forEach(function(r){e[r]?n[r]=function(n,o){return t[r](e[r](n,o),o)}:n[r]=t[r]}),n},t.defaultStyles=void 0;var r=n("2cv3"),o=n("rc6b"),u=n("n929"),i=n("+URl"),a=n("KIQE"),s=n("BCie"),l=n("SrCC"),c=n("Avrx"),p=n("+8qv"),f=n("lQ6M");function d(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var h={clearIndicator:i.clearIndicatorCSS,container:r.containerCSS,control:o.css,dropdownIndicator:i.dropdownIndicatorCSS,group:u.groupCSS,groupHeading:u.groupHeadingCSS,indicatorsContainer:r.indicatorsContainerCSS,indicatorSeparator:i.indicatorSeparatorCSS,input:a.inputCSS,loadingIndicator:i.loadingIndicatorCSS,loadingMessage:c.loadingMessageCSS,menu:c.menuCSS,menuList:c.menuListCSS,menuPortal:c.menuPortalCSS,multiValue:f.multiValueCSS,multiValueLabel:f.multiValueLabelCSS,multiValueRemove:f.multiValueRemoveCSS,noOptionsMessage:c.noOptionsMessageCSS,option:l.optionCSS,placeholder:s.placeholderCSS,singleValue:p.css,valueContainer:r.valueContainerCSS};t.defaultStyles=h},Wwog:function(e,t,n){"use strict";n.r(t);var r=function(e,t){return e.length===t.length&&e.every(function(e,n){return r=e,o=t[n],r===o;var r,o})};t.default=function(e,t){var n;void 0===t&&(t=r);var o,u=[],i=!1;return function(){for(var r=arguments.length,a=new Array(r),s=0;s<r;s++)a[s]=arguments[s];return i&&n===this&&t(a,u)?o:(o=e.apply(this,a),i=!0,n=this,u=a,o)}}},"b+PA":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.classNames=function(e,t,n,r){var o=[t,r];if(n&&e)for(var u in n)n.hasOwnProperty(u)&&n[u]&&o.push("".concat(a(e,u)));return o.filter(function(e){return e}).map(function(e){return String(e).trim()}).join(" ")},t.handleInputChange=function(e,t,n){if(n){var r=n(e,t);if("string"==typeof r)return r}return e},t.isDocumentElement=s,t.normalizedHeight=function(e){if(s(e))return window.innerHeight;return e.clientHeight},t.getScrollTop=l,t.scrollTo=c,t.getScrollParent=function(e){var t=getComputedStyle(e),n="absolute"===t.position,r=/(auto|scroll)/,o=document.documentElement;if("fixed"===t.position)return o;for(var u=e;u=u.parentElement;)if(t=getComputedStyle(u),(!n||"static"!==t.position)&&r.test(t.overflow+t.overflowY+t.overflowX))return u;return o},t.animatedScrollTo=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:200,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:i,u=l(e),a=t-u,s=10,p=0;!function t(){p+=s;var i=(l=p,f=u,d=a,h=n,d*((l=l/h-1)*l*l+1)+f);var l,f,d,h;c(e,i);p<n?(0,o.default)(t):r(e)}()},t.scrollIntoView=function(e,t){var n=e.getBoundingClientRect(),r=t.getBoundingClientRect(),o=t.offsetHeight/3;r.bottom+o>n.bottom?c(e,Math.min(t.offsetTop+t.clientHeight-e.offsetHeight+o,e.scrollHeight)):r.top-o<n.top&&c(e,Math.max(t.offsetTop-o,0))},t.getBoundingClientObj=function(e){var t=e.getBoundingClientRect();return{bottom:t.bottom,height:t.height,left:t.left,right:t.right,top:t.top,width:t.width}},t.toKey=function(e){return e.replace(/\W/g,"-")},t.isTouchCapable=function(){try{return document.createEvent("TouchEvent"),!0}catch(e){return!1}},t.isMobileDevice=function(){try{return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)}catch(e){return!1}},t.cleanValue=t.emptyString=t.noop=void 0;var r,o=(r=n("xEkU"))&&r.__esModule?r:{default:r};function u(e){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var i=function(){};t.noop=i;function a(e,t){return t?"-"===t[0]?e+t:e+"__"+t:e}t.emptyString=function(){return""};function s(e){return[document.documentElement,document.body,window].indexOf(e)>-1}function l(e){return s(e)?window.pageYOffset:e.scrollTop}function c(e,t){s(e)?window.scrollTo(0,t):e.scrollTop=t}t.cleanValue=function(e){return Array.isArray(e)?e.filter(Boolean):"object"===u(e)&&null!==e?[e]:[]}},bQgK:function(e,t,n){(function(t){(function(){var n,r,o,u,i,a;"undefined"!=typeof performance&&null!==performance&&performance.now?e.exports=function(){return performance.now()}:null!=t&&t.hrtime?(e.exports=function(){return(n()-i)/1e6},r=t.hrtime,u=(n=function(){var e;return 1e9*(e=r())[0]+e[1]})(),a=1e9*t.uptime(),i=u-a):Date.now?(e.exports=function(){return Date.now()-o},o=Date.now()):(e.exports=function(){return(new Date).getTime()-o},o=(new Date).getTime())}).call(this)}).call(this,n("8oxB"))},"c+lM":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.stripDiacritics=void 0;var r=[{base:"A",letters:/[\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F]/g},{base:"AA",letters:/[\uA732]/g},{base:"AE",letters:/[\u00C6\u01FC\u01E2]/g},{base:"AO",letters:/[\uA734]/g},{base:"AU",letters:/[\uA736]/g},{base:"AV",letters:/[\uA738\uA73A]/g},{base:"AY",letters:/[\uA73C]/g},{base:"B",letters:/[\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181]/g},{base:"C",letters:/[\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E]/g},{base:"D",letters:/[\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779]/g},{base:"DZ",letters:/[\u01F1\u01C4]/g},{base:"Dz",letters:/[\u01F2\u01C5]/g},{base:"E",letters:/[\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E]/g},{base:"F",letters:/[\u0046\u24BB\uFF26\u1E1E\u0191\uA77B]/g},{base:"G",letters:/[\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E]/g},{base:"H",letters:/[\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D]/g},{base:"I",letters:/[\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197]/g},{base:"J",letters:/[\u004A\u24BF\uFF2A\u0134\u0248]/g},{base:"K",letters:/[\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2]/g},{base:"L",letters:/[\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780]/g},{base:"LJ",letters:/[\u01C7]/g},{base:"Lj",letters:/[\u01C8]/g},{base:"M",letters:/[\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C]/g},{base:"N",letters:/[\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4]/g},{base:"NJ",letters:/[\u01CA]/g},{base:"Nj",letters:/[\u01CB]/g},{base:"O",letters:/[\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C]/g},{base:"OI",letters:/[\u01A2]/g},{base:"OO",letters:/[\uA74E]/g},{base:"OU",letters:/[\u0222]/g},{base:"P",letters:/[\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754]/g},{base:"Q",letters:/[\u0051\u24C6\uFF31\uA756\uA758\u024A]/g},{base:"R",letters:/[\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782]/g},{base:"S",letters:/[\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784]/g},{base:"T",letters:/[\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786]/g},{base:"TZ",letters:/[\uA728]/g},{base:"U",letters:/[\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244]/g},{base:"V",letters:/[\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245]/g},{base:"VY",letters:/[\uA760]/g},{base:"W",letters:/[\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72]/g},{base:"X",letters:/[\u0058\u24CD\uFF38\u1E8A\u1E8C]/g},{base:"Y",letters:/[\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE]/g},{base:"Z",letters:/[\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762]/g},{base:"a",letters:/[\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250]/g},{base:"aa",letters:/[\uA733]/g},{base:"ae",letters:/[\u00E6\u01FD\u01E3]/g},{base:"ao",letters:/[\uA735]/g},{base:"au",letters:/[\uA737]/g},{base:"av",letters:/[\uA739\uA73B]/g},{base:"ay",letters:/[\uA73D]/g},{base:"b",letters:/[\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253]/g},{base:"c",letters:/[\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184]/g},{base:"d",letters:/[\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A]/g},{base:"dz",letters:/[\u01F3\u01C6]/g},{base:"e",letters:/[\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD]/g},{base:"f",letters:/[\u0066\u24D5\uFF46\u1E1F\u0192\uA77C]/g},{base:"g",letters:/[\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F]/g},{base:"h",letters:/[\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265]/g},{base:"hv",letters:/[\u0195]/g},{base:"i",letters:/[\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131]/g},{base:"j",letters:/[\u006A\u24D9\uFF4A\u0135\u01F0\u0249]/g},{base:"k",letters:/[\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3]/g},{base:"l",letters:/[\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747]/g},{base:"lj",letters:/[\u01C9]/g},{base:"m",letters:/[\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F]/g},{base:"n",letters:/[\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5]/g},{base:"nj",letters:/[\u01CC]/g},{base:"o",letters:/[\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275]/g},{base:"oi",letters:/[\u01A3]/g},{base:"ou",letters:/[\u0223]/g},{base:"oo",letters:/[\uA74F]/g},{base:"p",letters:/[\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755]/g},{base:"q",letters:/[\u0071\u24E0\uFF51\u024B\uA757\uA759]/g},{base:"r",letters:/[\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783]/g},{base:"s",letters:/[\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B]/g},{base:"t",letters:/[\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787]/g},{base:"tz",letters:/[\uA729]/g},{base:"u",letters:/[\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289]/g},{base:"v",letters:/[\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C]/g},{base:"vy",letters:/[\uA761]/g},{base:"w",letters:/[\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73]/g},{base:"x",letters:/[\u0078\u24E7\uFF58\u1E8B\u1E8D]/g},{base:"y",letters:/[\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF]/g},{base:"z",letters:/[\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763]/g}];t.stripDiacritics=function(e){for(var t=0;t<r.length;t++)e=e.replace(r[t].letters,r[t].base);return e}},d6Hc:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r,o=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),u=(r=n("N3bB"))&&r.__esModule?r:{default:r};function i(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function l(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function c(e,t,n){return t&&l(e.prototype,t),n&&l(e,n),e}function p(e,t){return!t||"object"!==a(t)&&"function"!=typeof t?b(e):t}function f(e){return(f=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function d(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&h(e,t)}function h(e,t){return(h=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function b(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var v=function(e){function t(){var e,n;s(this,t);for(var r=arguments.length,o=new Array(r),u=0;u<r;u++)o[u]=arguments[u];return m(b(b(n=p(this,(e=f(t)).call.apply(e,[this].concat(o))))),"isBottom",!1),m(b(b(n)),"isTop",!1),m(b(b(n)),"scrollTarget",void 0),m(b(b(n)),"touchStart",void 0),m(b(b(n)),"cancelScroll",function(e){e.preventDefault(),e.stopPropagation()}),m(b(b(n)),"handleEventDelta",function(e,t){var r=n.props,o=r.onBottomArrive,u=r.onBottomLeave,i=r.onTopArrive,a=r.onTopLeave,s=n.scrollTarget,l=s.scrollTop,c=s.scrollHeight,p=s.clientHeight,f=n.scrollTarget,d=t>0,h=c-p-l,b=!1;h>t&&n.isBottom&&(u&&u(e),n.isBottom=!1),d&&n.isTop&&(a&&a(e),n.isTop=!1),d&&t>h?(o&&!n.isBottom&&o(e),f.scrollTop=c,b=!0,n.isBottom=!0):!d&&-t>l&&(i&&!n.isTop&&i(e),f.scrollTop=0,b=!0,n.isTop=!0),b&&n.cancelScroll(e)}),m(b(b(n)),"onWheel",function(e){n.handleEventDelta(e,e.deltaY)}),m(b(b(n)),"onTouchStart",function(e){n.touchStart=e.changedTouches[0].clientY}),m(b(b(n)),"onTouchMove",function(e){var t=n.touchStart-e.changedTouches[0].clientY;n.handleEventDelta(e,t)}),m(b(b(n)),"getScrollTarget",function(e){n.scrollTarget=e}),n}return d(t,o.Component),c(t,[{key:"componentDidMount",value:function(){this.startListening(this.scrollTarget)}},{key:"componentWillUnmount",value:function(){this.stopListening(this.scrollTarget)}},{key:"startListening",value:function(e){e&&(e.scrollHeight<=e.clientHeight||("function"==typeof e.addEventListener&&e.addEventListener("wheel",this.onWheel,!1),"function"==typeof e.addEventListener&&e.addEventListener("touchstart",this.onTouchStart,!1),"function"==typeof e.addEventListener&&e.addEventListener("touchmove",this.onTouchMove,!1)))}},{key:"stopListening",value:function(e){e.scrollHeight<=e.clientHeight||("function"==typeof e.removeEventListener&&e.removeEventListener("wheel",this.onWheel,!1),"function"==typeof e.removeEventListener&&e.removeEventListener("touchstart",this.onTouchStart,!1),"function"==typeof e.removeEventListener&&e.removeEventListener("touchmove",this.onTouchMove,!1))}},{key:"render",value:function(){return o.default.createElement(u.default,{innerRef:this.getScrollTarget},this.props.children)}}]),t}(),g=function(e){function t(){return s(this,t),p(this,f(t).apply(this,arguments))}return d(t,o.Component),c(t,[{key:"render",value:function(){var e=this.props,t=e.isEnabled,n=i(e,["isEnabled"]);return t?o.default.createElement(v,n):this.props.children}}]),t}();t.default=g,m(g,"defaultProps",{isEnabled:!0})},grMF:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createFilter=void 0;var r=n("c+lM");function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var u=function(e){return e.replace(/^\s+|\s+$/g,"")},i=function(e){return"".concat(e.label," ").concat(e.value)};t.createFilter=function(e){return function(t,n){var a=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){o(e,t,n[t])})}return e}({ignoreCase:!0,ignoreAccents:!0,stringify:i,trim:!0,matchFrom:"any"},e),s=a.ignoreCase,l=a.ignoreAccents,c=a.stringify,p=a.trim,f=a.matchFrom,d=p?u(n):n,h=p?u(c(t)):c(t);return s&&(d=d.toLowerCase(),h=h.toLowerCase()),l&&(d=(0,r.stripDiacritics)(d),h=(0,r.stripDiacritics)(h)),"start"===f?h.substr(0,d.length)===d:h.indexOf(d)>-1}}},jMYa:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.defaultProps=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=b(n("Wwog")),u=n("Avrx"),i=b(n("3lEq")),a=n("grMF"),s=n("tJT6"),l=n("59S1"),c=n("b+PA"),p=n("oZmp"),f=n("5bE3"),d=n("WvHe"),h=n("KXCe");function b(e){return e&&e.__esModule?e:{default:e}}function m(e){return(m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function v(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function g(){return(g=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function y(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}(e)||function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}function O(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){P(e,t,n[t])})}return e}function E(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function S(e){return(S=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function C(e,t){return(C=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function w(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function P(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var A={backspaceRemovesValue:!0,blurInputOnSelect:(0,c.isTouchCapable)(),captureMenuScroll:!(0,c.isTouchCapable)(),closeMenuOnSelect:!0,closeMenuOnScroll:!1,components:{},controlShouldRenderValue:!0,escapeClearsValue:!1,filterOption:(0,a.createFilter)(),formatGroupLabel:p.formatGroupLabel,getOptionLabel:p.getOptionLabel,getOptionValue:p.getOptionValue,isDisabled:!1,isLoading:!1,isMulti:!1,isRtl:!1,isSearchable:!0,isOptionDisabled:p.isOptionDisabled,loadingMessage:function(){return"Loading..."},maxMenuHeight:300,minMenuHeight:140,menuIsOpen:!1,menuPlacement:"bottom",menuPosition:"absolute",menuShouldBlockScroll:!1,menuShouldScrollIntoView:!(0,c.isMobileDevice)(),noOptionsMessage:function(){return"No options"},openMenuOnFocus:!1,openMenuOnClick:!0,options:[],pageSize:5,placeholder:"Select...",screenReaderStatus:function(e){var t=e.count;return"".concat(t," result").concat(1!==t?"s":""," available")},styles:{},tabIndex:"0",tabSelectsValue:!0};t.defaultProps=A;var j=1,D=function(e){function t(e){var n,r,u;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),r=this,u=S(t).call(this,e),n=!u||"object"!==m(u)&&"function"!=typeof u?w(r):u,P(w(w(n)),"state",{ariaLiveSelection:"",ariaLiveContext:"",focusedOption:null,focusedValue:null,inputIsHidden:!1,isFocused:!1,isComposing:!1,menuOptions:{render:[],focusable:[]},selectValue:[]}),P(w(w(n)),"blockOptionHover",!1),P(w(w(n)),"clearFocusValueOnUpdate",!1),P(w(w(n)),"commonProps",void 0),P(w(w(n)),"components",void 0),P(w(w(n)),"hasGroups",!1),P(w(w(n)),"initialTouchX",0),P(w(w(n)),"initialTouchY",0),P(w(w(n)),"inputIsHiddenAfterUpdate",void 0),P(w(w(n)),"instancePrefix",""),P(w(w(n)),"openAfterFocus",!1),P(w(w(n)),"scrollToFocusedOptionOnUpdate",!1),P(w(w(n)),"userIsDragging",void 0),P(w(w(n)),"controlRef",null),P(w(w(n)),"getControlRef",function(e){n.controlRef=e}),P(w(w(n)),"focusedOptionRef",null),P(w(w(n)),"getFocusedOptionRef",function(e){n.focusedOptionRef=e}),P(w(w(n)),"menuListRef",null),P(w(w(n)),"getMenuListRef",function(e){n.menuListRef=e}),P(w(w(n)),"inputRef",null),P(w(w(n)),"getInputRef",function(e){n.inputRef=e}),P(w(w(n)),"cacheComponents",function(e){n.components=(0,f.defaultComponents)({components:e})}),P(w(w(n)),"focus",n.focusInput),P(w(w(n)),"blur",n.blurInput),P(w(w(n)),"onChange",function(e,t){var r=n.props;(0,r.onChange)(e,O({},t,{name:r.name}))}),P(w(w(n)),"setValue",function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"set-value",r=arguments.length>2?arguments[2]:void 0,o=n.props,u=o.closeMenuOnSelect,i=o.isMulti;n.onInputChange("",{action:"set-value"}),u&&(n.inputIsHiddenAfterUpdate=!i,n.onMenuClose()),n.clearFocusValueOnUpdate=!0,n.onChange(e,{action:t,option:r})}),P(w(w(n)),"selectOption",function(e){var t=n.props,r=t.blurInputOnSelect;if(t.isMulti){var o=n.state.selectValue;if(n.isOptionSelected(e,o)){var u=n.getOptionValue(e);n.setValue(o.filter(function(e){return n.getOptionValue(e)!==u}),"deselect-option",e),n.announceAriaLiveSelection({event:"deselect-option",context:{value:n.getOptionLabel(e)}})}else n.setValue([].concat(y(o),[e]),"select-option",e),n.announceAriaLiveSelection({event:"select-option",context:{value:n.getOptionLabel(e)}})}else n.setValue(e,"select-option"),n.announceAriaLiveSelection({event:"select-option",context:{value:n.getOptionLabel(e)}});r&&n.blurInput()}),P(w(w(n)),"removeValue",function(e){var t=n.state.selectValue,r=n.getOptionValue(e);n.onChange(t.filter(function(e){return n.getOptionValue(e)!==r}),{action:"remove-value",removedValue:e}),n.announceAriaLiveSelection({event:"remove-value",context:{value:e?n.getOptionLabel(e):""}}),n.focusInput()}),P(w(w(n)),"clearValue",function(){var e=n.props.isMulti;n.onChange(e?[]:null,{action:"clear"})}),P(w(w(n)),"popValue",function(){var e=n.state.selectValue,t=e[e.length-1];n.announceAriaLiveSelection({event:"pop-value",context:{value:t?n.getOptionLabel(t):""}}),n.onChange(e.slice(0,e.length-1),{action:"pop-value",removedValue:t})}),P(w(w(n)),"getOptionLabel",function(e){return n.props.getOptionLabel(e)}),P(w(w(n)),"getOptionValue",function(e){return n.props.getOptionValue(e)}),P(w(w(n)),"getStyles",function(e,t){var r=d.defaultStyles[e](t);r.boxSizing="border-box";var o=n.props.styles[e];return o?o(r,t):r}),P(w(w(n)),"getElementId",function(e){return"".concat(n.instancePrefix,"-").concat(e)}),P(w(w(n)),"getActiveDescendentId",function(){var e=n.props.menuIsOpen,t=n.state,r=t.menuOptions,o=t.focusedOption;if(o&&e){var u=r.focusable.indexOf(o),i=r.render[u];return i&&i.key}}),P(w(w(n)),"announceAriaLiveSelection",function(e){var t=e.event,r=e.context;n.setState({ariaLiveSelection:(0,l.valueEventAriaMessage)(t,r)})}),P(w(w(n)),"announceAriaLiveContext",function(e){var t=e.event,r=e.context;n.setState({ariaLiveContext:(0,l.instructionsAriaMessage)(t,O({},r,{label:n.props["aria-label"]}))})}),P(w(w(n)),"onMenuMouseDown",function(e){0===e.button&&(e.stopPropagation(),e.preventDefault(),n.focusInput())}),P(w(w(n)),"onMenuMouseMove",function(e){n.blockOptionHover=!1}),P(w(w(n)),"onControlMouseDown",function(e){var t=n.props.openMenuOnClick;n.state.isFocused?n.props.menuIsOpen?"INPUT"!==e.currentTarget.tagName&&n.onMenuClose():t&&n.openMenu("first"):(t&&(n.openAfterFocus=!0),n.focusInput()),"INPUT"!==e.currentTarget.tagName&&e.preventDefault()}),P(w(w(n)),"onDropdownIndicatorMouseDown",function(e){if(!(e&&"mousedown"===e.type&&0!==e.button||n.props.isDisabled)){var t=n.props,r=t.isMulti,o=t.menuIsOpen;n.focusInput(),o?(n.inputIsHiddenAfterUpdate=!r,n.onMenuClose()):n.openMenu("first"),e.preventDefault(),e.stopPropagation()}}),P(w(w(n)),"onClearIndicatorMouseDown",function(e){e&&"mousedown"===e.type&&0!==e.button||(n.clearValue(),e.stopPropagation(),n.openAfterFocus=!1,setTimeout(function(){return n.focusInput()}))}),P(w(w(n)),"onScroll",function(e){"boolean"==typeof n.props.closeMenuOnScroll?e.target instanceof HTMLElement&&(0,c.isDocumentElement)(e.target)&&n.props.onMenuClose():"function"==typeof n.props.closeMenuOnScroll&&n.props.closeMenuOnScroll(e)&&n.props.onMenuClose()}),P(w(w(n)),"onCompositionStart",function(){n.setState({isComposing:!0})}),P(w(w(n)),"onCompositionEnd",function(){n.setState({isComposing:!1})}),P(w(w(n)),"onTouchStart",function(e){var t=e.touches.item(0);t&&(n.initialTouchX=t.clientX,n.initialTouchY=t.clientY,n.userIsDragging=!1)}),P(w(w(n)),"onTouchMove",function(e){var t=e.touches.item(0);if(t){var r=Math.abs(t.clientX-n.initialTouchX),o=Math.abs(t.clientY-n.initialTouchY);n.userIsDragging=r>5||o>5}}),P(w(w(n)),"onTouchEnd",function(e){n.userIsDragging||(n.controlRef&&!n.controlRef.contains(e.target)&&n.menuListRef&&!n.menuListRef.contains(e.target)&&n.blurInput(),n.initialTouchX=0,n.initialTouchY=0)}),P(w(w(n)),"onControlTouchEnd",function(e){n.userIsDragging||n.onControlMouseDown(e)}),P(w(w(n)),"onClearIndicatorTouchEnd",function(e){n.userIsDragging||n.onClearIndicatorMouseDown(e)}),P(w(w(n)),"onDropdownIndicatorTouchEnd",function(e){n.userIsDragging||n.onDropdownIndicatorMouseDown(e)}),P(w(w(n)),"handleInputChange",function(e){var t=e.currentTarget.value;n.inputIsHiddenAfterUpdate=!1,n.onInputChange(t,{action:"input-change"}),n.onMenuOpen()}),P(w(w(n)),"onInputFocus",function(e){var t=n.props,r=t.isSearchable,o=t.isMulti;n.props.onFocus&&n.props.onFocus(e),n.inputIsHiddenAfterUpdate=!1,n.announceAriaLiveContext({event:"input",context:{isSearchable:r,isMulti:o}}),n.setState({isFocused:!0}),(n.openAfterFocus||n.props.openMenuOnFocus)&&n.openMenu("first"),n.openAfterFocus=!1}),P(w(w(n)),"onInputBlur",function(e){n.menuListRef&&n.menuListRef.contains(document.activeElement)?n.inputRef.focus():(n.props.onBlur&&n.props.onBlur(e),n.onInputChange("",{action:"input-blur"}),n.onMenuClose(),n.setState({focusedValue:null,isFocused:!1}))}),P(w(w(n)),"onOptionHover",function(e){n.blockOptionHover||n.state.focusedOption===e||n.setState({focusedOption:e})}),P(w(w(n)),"shouldHideSelectedOptions",function(){var e=n.props,t=e.hideSelectedOptions,r=e.isMulti;return void 0===t?r:t}),P(w(w(n)),"onKeyDown",function(e){var t=n.props,r=t.isMulti,o=t.backspaceRemovesValue,u=t.escapeClearsValue,i=t.inputValue,a=t.isClearable,s=t.isDisabled,l=t.menuIsOpen,c=t.onKeyDown,p=t.tabSelectsValue,f=t.openMenuOnFocus,d=n.state,h=d.isComposing,b=d.focusedOption,m=d.focusedValue,v=d.selectValue;if(!(s||"function"==typeof c&&(c(e),e.defaultPrevented))){switch(n.blockOptionHover=!0,e.key){case"ArrowLeft":if(!r||i)return;n.focusValue("previous");break;case"ArrowRight":if(!r||i)return;n.focusValue("next");break;case"Delete":case"Backspace":if(i)return;if(m)n.removeValue(m);else{if(!o)return;r?n.popValue():a&&n.clearValue()}break;case"Tab":if(h)return;if(e.shiftKey||!l||!p||!b||f&&n.isOptionSelected(b,v))return;n.selectOption(b);break;case"Enter":if(l){if(!b)return;if(h)return;n.selectOption(b);break}return;case"Escape":l?(n.inputIsHiddenAfterUpdate=!1,n.onInputChange("",{action:"menu-close"}),n.onMenuClose()):a&&u&&n.clearValue();break;case" ":if(i)return;if(!l){n.openMenu("first");break}if(!b)return;n.selectOption(b);break;case"ArrowUp":l?n.focusOption("up"):n.openMenu("last");break;case"ArrowDown":l?n.focusOption("down"):n.openMenu("first");break;case"PageUp":if(!l)return;n.focusOption("pageup");break;case"PageDown":if(!l)return;n.focusOption("pagedown");break;case"Home":if(!l)return;n.focusOption("first");break;case"End":if(!l)return;n.focusOption("last");break;default:return}e.preventDefault()}});var a=e.value;n.cacheComponents=(0,o.default)(n.cacheComponents,i.default).bind(w(w(n))),n.cacheComponents(e.components),n.instancePrefix="react-select-"+(n.props.instanceId||++j);var s=(0,c.cleanValue)(a),p=n.buildMenuOptions(e,s);return n.state.menuOptions=p,n.state.selectValue=s,n}var n,a,p;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&C(e,t)}(t,r.Component),n=t,(a=[{key:"componentDidMount",value:function(){this.startListeningComposition(),this.startListeningToTouch(),this.props.closeMenuOnScroll&&document&&document.addEventListener&&document.addEventListener("scroll",this.onScroll,!0),this.props.autoFocus&&this.focusInput()}},{key:"componentWillReceiveProps",value:function(e){var t=this.props,n=t.options,r=t.value,o=t.inputValue;if(this.cacheComponents(e.components),e.value!==r||e.options!==n||e.inputValue!==o){var u=(0,c.cleanValue)(e.value),i=this.buildMenuOptions(e,u),a=this.getNextFocusedValue(u),s=this.getNextFocusedOption(i.focusable);this.setState({menuOptions:i,selectValue:u,focusedOption:s,focusedValue:a})}null!=this.inputIsHiddenAfterUpdate&&(this.setState({inputIsHidden:this.inputIsHiddenAfterUpdate}),delete this.inputIsHiddenAfterUpdate)}},{key:"componentDidUpdate",value:function(e){var t=this.props,n=t.isDisabled,r=t.menuIsOpen,o=this.state.isFocused;(o&&!n&&e.isDisabled||o&&r&&!e.menuIsOpen)&&this.focusInput(),this.menuListRef&&this.focusedOptionRef&&this.scrollToFocusedOptionOnUpdate&&(0,c.scrollIntoView)(this.menuListRef,this.focusedOptionRef),this.scrollToFocusedOptionOnUpdate=!1}},{key:"componentWillUnmount",value:function(){this.stopListeningComposition(),this.stopListeningToTouch(),document.removeEventListener("scroll",this.onScroll,!0)}},{key:"onMenuOpen",value:function(){this.props.onMenuOpen()}},{key:"onMenuClose",value:function(){var e=this.props,t=e.isSearchable,n=e.isMulti;this.announceAriaLiveContext({event:"input",context:{isSearchable:t,isMulti:n}}),this.onInputChange("",{action:"menu-close"}),this.props.onMenuClose()}},{key:"onInputChange",value:function(e,t){this.props.onInputChange(e,t)}},{key:"focusInput",value:function(){this.inputRef&&this.inputRef.focus()}},{key:"blurInput",value:function(){this.inputRef&&this.inputRef.blur()}},{key:"openMenu",value:function(e){var t=this.state,n=t.menuOptions,r=t.selectValue,o=t.isFocused,u=this.props.isMulti,i="first"===e?0:n.focusable.length-1;if(!u){var a=n.focusable.indexOf(r[0]);a>-1&&(i=a)}this.scrollToFocusedOptionOnUpdate=!(o&&this.menuListRef),this.inputIsHiddenAfterUpdate=!1,this.onMenuOpen(),this.setState({focusedValue:null,focusedOption:n.focusable[i]}),this.announceAriaLiveContext({event:"menu"})}},{key:"focusValue",value:function(e){var t=this.props,n=t.isMulti,r=t.isSearchable,o=this.state,u=o.selectValue,i=o.focusedValue;if(n){this.setState({focusedOption:null});var a=u.indexOf(i);i||(a=-1,this.announceAriaLiveContext({event:"value"}));var s=u.length-1,l=-1;if(u.length){switch(e){case"previous":l=0===a?0:-1===a?s:a-1;break;case"next":a>-1&&a<s&&(l=a+1)}-1===l&&this.announceAriaLiveContext({event:"input",context:{isSearchable:r,isMulti:n}}),this.setState({inputIsHidden:-1!==l,focusedValue:u[l]})}}}},{key:"focusOption",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"first",t=this.props.pageSize,n=this.state,r=n.focusedOption,o=n.menuOptions.focusable;if(o.length){var u=0,i=o.indexOf(r);r||(i=-1,this.announceAriaLiveContext({event:"menu"})),"up"===e?u=i>0?i-1:o.length-1:"down"===e?u=(i+1)%o.length:"pageup"===e?(u=i-t)<0&&(u=0):"pagedown"===e?(u=i+t)>o.length-1&&(u=o.length-1):"last"===e&&(u=o.length-1),this.scrollToFocusedOptionOnUpdate=!0,this.setState({focusedOption:o[u],focusedValue:null})}}},{key:"getTheme",value:function(){return this.props.theme?"function"==typeof this.props.theme?this.props.theme(h.defaultTheme):O({},h.defaultTheme,this.props.theme):h.defaultTheme}},{key:"getCommonProps",value:function(){var e=this.clearValue,t=this.getStyles,n=this.setValue,r=this.selectOption,o=this.props,u=o.classNamePrefix,i=o.isMulti,a=o.isRtl,s=o.options,l=this.state.selectValue,p=this.hasValue();return{cx:c.classNames.bind(null,u),clearValue:e,getStyles:t,getValue:function(){return l},hasValue:p,isMulti:i,isRtl:a,options:s,selectOption:r,setValue:n,selectProps:o,theme:this.getTheme()}}},{key:"getNextFocusedValue",value:function(e){if(this.clearFocusValueOnUpdate)return this.clearFocusValueOnUpdate=!1,null;var t=this.state,n=t.focusedValue,r=t.selectValue.indexOf(n);if(r>-1){if(e.indexOf(n)>-1)return n;if(r<e.length)return e[r]}return null}},{key:"getNextFocusedOption",value:function(e){var t=this.state.focusedOption;return t&&e.indexOf(t)>-1?t:e[0]}},{key:"hasValue",value:function(){return this.state.selectValue.length>0}},{key:"hasOptions",value:function(){return!!this.state.menuOptions.render.length}},{key:"countOptions",value:function(){return this.state.menuOptions.focusable.length}},{key:"isClearable",value:function(){var e=this.props,t=e.isClearable,n=e.isMulti;return void 0===t?n:t}},{key:"isOptionDisabled",value:function(e,t){return"function"==typeof this.props.isOptionDisabled&&this.props.isOptionDisabled(e,t)}},{key:"isOptionSelected",value:function(e,t){var n=this;if(t.indexOf(e)>-1)return!0;if("function"==typeof this.props.isOptionSelected)return this.props.isOptionSelected(e,t);var r=this.getOptionValue(e);return t.some(function(e){return n.getOptionValue(e)===r})}},{key:"filterOption",value:function(e,t){return!this.props.filterOption||this.props.filterOption(e,t)}},{key:"formatOptionLabel",value:function(e,t){if("function"==typeof this.props.formatOptionLabel){var n=this.props.inputValue,r=this.state.selectValue;return this.props.formatOptionLabel(e,{context:t,inputValue:n,selectValue:r})}return this.getOptionLabel(e)}},{key:"formatGroupLabel",value:function(e){return this.props.formatGroupLabel(e)}},{key:"startListeningComposition",value:function(){document&&document.addEventListener&&(document.addEventListener("compositionstart",this.onCompositionStart,!1),document.addEventListener("compositionend",this.onCompositionEnd,!1))}},{key:"stopListeningComposition",value:function(){document&&document.removeEventListener&&(document.removeEventListener("compositionstart",this.onCompositionStart),document.removeEventListener("compositionend",this.onCompositionEnd))}},{key:"startListeningToTouch",value:function(){document&&document.addEventListener&&(document.addEventListener("touchstart",this.onTouchStart,!1),document.addEventListener("touchmove",this.onTouchMove,!1),document.addEventListener("touchend",this.onTouchEnd,!1))}},{key:"stopListeningToTouch",value:function(){document&&document.removeEventListener&&(document.removeEventListener("touchstart",this.onTouchStart),document.removeEventListener("touchmove",this.onTouchMove),document.removeEventListener("touchend",this.onTouchEnd))}},{key:"buildMenuOptions",value:function(e,t){var n=this,r=e.inputValue,o=void 0===r?"":r,u=e.options,i=function(e,r){var u=n.isOptionDisabled(e,t),i=n.isOptionSelected(e,t),a=n.getOptionLabel(e),s=n.getOptionValue(e);if(!(n.shouldHideSelectedOptions()&&i||!n.filterOption({label:a,value:s,data:e},o))){var l=u?void 0:function(){return n.onOptionHover(e)},c=u?void 0:function(){return n.selectOption(e)},p="".concat(n.getElementId("option"),"-").concat(r);return{innerProps:{id:p,onClick:c,onMouseMove:l,onMouseOver:l,tabIndex:-1},data:e,isDisabled:u,isSelected:i,key:p,label:a,type:"option",value:s}}};return u.reduce(function(e,t,r){if(t.options){n.hasGroups||(n.hasGroups=!0);var o=t.options.map(function(t,n){var o=i(t,"".concat(r,"-").concat(n));return o&&!o.isDisabled&&e.focusable.push(t),o}).filter(Boolean);if(o.length){var u="".concat(n.getElementId("group"),"-").concat(r);e.render.push({type:"group",key:u,data:t,options:o})}}else{var a=i(t,"".concat(r));a&&(e.render.push(a),a.isDisabled||e.focusable.push(t))}return e},{render:[],focusable:[]})}},{key:"constructAriaLiveMessage",value:function(){var e=this.state,t=e.ariaLiveContext,n=e.selectValue,r=e.focusedValue,o=e.focusedOption,u=this.props,i=u.options,a=u.menuIsOpen,s=u.inputValue,c=u.screenReaderStatus,p=r?(0,l.valueFocusAriaMessage)({focusedValue:r,getOptionLabel:this.getOptionLabel,selectValue:n}):"",f=o&&a?(0,l.optionFocusAriaMessage)({focusedOption:o,getOptionLabel:this.getOptionLabel,options:i}):"",d=(0,l.resultsAriaMessage)({inputValue:s,screenReaderMessage:c({count:this.countOptions()})});return"".concat(p," ").concat(f," ").concat(d," ").concat(t)}},{key:"renderInput",value:function(){var e=this.props,t=e.isDisabled,n=e.isSearchable,o=e.inputId,u=e.inputValue,i=e.tabIndex,a=this.components.Input,l=this.state.inputIsHidden,p=o||this.getElementId("input");if(!n)return r.default.createElement(s.DummyInput,{id:p,innerRef:this.getInputRef,onBlur:this.onInputBlur,onChange:c.noop,onFocus:this.onInputFocus,readOnly:!0,disabled:t,tabIndex:i,value:""});var f={"aria-autocomplete":"list","aria-label":this.props["aria-label"],"aria-labelledby":this.props["aria-labelledby"]},d=this.commonProps,h=d.cx,b=d.theme,m=d.selectProps;return r.default.createElement(a,g({autoCapitalize:"none",autoComplete:"off",autoCorrect:"off",cx:h,getStyles:this.getStyles,id:p,innerRef:this.getInputRef,isDisabled:t,isHidden:l,onBlur:this.onInputBlur,onChange:this.handleInputChange,onFocus:this.onInputFocus,selectProps:m,spellCheck:"false",tabIndex:i,theme:b,type:"text",value:u},f))}},{key:"renderPlaceholderOrValue",value:function(){var e=this,t=this.components,n=t.MultiValue,o=t.MultiValueContainer,u=t.MultiValueLabel,i=t.MultiValueRemove,a=t.SingleValue,s=t.Placeholder,l=this.commonProps,c=this.props,p=c.controlShouldRenderValue,f=c.isDisabled,d=c.isMulti,h=c.inputValue,b=c.placeholder,m=this.state,v=m.selectValue,y=m.focusedValue,O=m.isFocused;if(!this.hasValue()||!p)return h?null:r.default.createElement(s,g({},l,{key:"placeholder",isDisabled:f,isFocused:O}),b);if(d)return v.map(function(t){var a=t===y;return r.default.createElement(n,g({},l,{components:{Container:o,Label:u,Remove:i},isFocused:a,isDisabled:f,key:e.getOptionValue(t),removeProps:{onClick:function(){return e.removeValue(t)},onTouchEnd:function(){return e.removeValue(t)},onMouseDown:function(e){e.preventDefault(),e.stopPropagation()}},data:t}),e.formatOptionLabel(t,"value"))});if(h)return null;var E=v[0];return r.default.createElement(a,g({},l,{data:E,isDisabled:f}),this.formatOptionLabel(E,"value"))}},{key:"renderClearIndicator",value:function(){var e=this.components.ClearIndicator,t=this.commonProps,n=this.props,o=n.isDisabled,u=n.isLoading,i=this.state.isFocused;if(!this.isClearable()||!e||o||!this.hasValue()||u)return null;var a={onMouseDown:this.onClearIndicatorMouseDown,onTouchEnd:this.onClearIndicatorTouchEnd,"aria-hidden":"true"};return r.default.createElement(e,g({},t,{innerProps:a,isFocused:i}))}},{key:"renderLoadingIndicator",value:function(){var e=this.components.LoadingIndicator,t=this.commonProps,n=this.props,o=n.isDisabled,u=n.isLoading,i=this.state.isFocused;if(!e||!u)return null;return r.default.createElement(e,g({},t,{innerProps:{"aria-hidden":"true"},isDisabled:o,isFocused:i}))}},{key:"renderIndicatorSeparator",value:function(){var e=this.components,t=e.DropdownIndicator,n=e.IndicatorSeparator;if(!t||!n)return null;var o=this.commonProps,u=this.props.isDisabled,i=this.state.isFocused;return r.default.createElement(n,g({},o,{isDisabled:u,isFocused:i}))}},{key:"renderDropdownIndicator",value:function(){var e=this.components.DropdownIndicator;if(!e)return null;var t=this.commonProps,n=this.props.isDisabled,o=this.state.isFocused,u={onMouseDown:this.onDropdownIndicatorMouseDown,onTouchEnd:this.onDropdownIndicatorTouchEnd,"aria-hidden":"true"};return r.default.createElement(e,g({},t,{innerProps:u,isDisabled:n,isFocused:o}))}},{key:"renderMenu",value:function(){var e=this,t=this.components,n=t.Group,o=t.GroupHeading,i=t.Menu,a=t.MenuList,l=t.MenuPortal,c=t.LoadingMessage,p=t.NoOptionsMessage,f=t.Option,d=this.commonProps,h=this.state,b=h.focusedOption,m=h.menuOptions,y=this.props,O=y.captureMenuScroll,E=y.inputValue,S=y.isLoading,C=y.loadingMessage,w=y.minMenuHeight,P=y.maxMenuHeight,A=y.menuIsOpen,j=y.menuPlacement,D=y.menuPosition,M=y.menuPortalTarget,k=y.menuShouldBlockScroll,F=y.menuShouldScrollIntoView,x=y.noOptionsMessage,_=y.onMenuScrollToTop,I=y.onMenuScrollToBottom;if(!A)return null;var T,L=function(t){var n=b===t.data;return t.innerRef=n?e.getFocusedOptionRef:void 0,r.default.createElement(f,g({},d,t,{isFocused:n}),e.formatOptionLabel(t.data,"menu"))};if(this.hasOptions())T=m.render.map(function(t){if("group"===t.type){t.type;var u=v(t,["type"]),i="".concat(t.key,"-heading");return r.default.createElement(n,g({},d,u,{Heading:o,headingProps:{id:i},label:e.formatGroupLabel(t.data)}),t.options.map(function(e){return L(e)}))}if("option"===t.type)return L(t)});else if(S){var V=C({inputValue:E});if(null===V)return null;T=r.default.createElement(c,d,V)}else{var R=x({inputValue:E});if(null===R)return null;T=r.default.createElement(p,d,R)}var B={minMenuHeight:w,maxMenuHeight:P,menuPlacement:j,menuPosition:D,menuShouldScrollIntoView:F},H=r.default.createElement(u.MenuPlacer,g({},d,B),function(t){var n=t.ref,o=t.placerProps,u=o.placement,l=o.maxHeight;return r.default.createElement(i,g({},d,B,{innerRef:n,innerProps:{onMouseDown:e.onMenuMouseDown,onMouseMove:e.onMenuMouseMove},isLoading:S,placement:u}),r.default.createElement(s.ScrollCaptor,{isEnabled:O,onTopArrive:_,onBottomArrive:I},r.default.createElement(s.ScrollBlock,{isEnabled:k},r.default.createElement(a,g({},d,{innerRef:e.getMenuListRef,isLoading:S,maxHeight:l}),T))))});return M||"fixed"===D?r.default.createElement(l,g({},d,{appendTo:M,controlElement:this.controlRef,menuPlacement:j,menuPosition:D}),H):H}},{key:"renderFormField",value:function(){var e=this,t=this.props,n=t.delimiter,o=t.isDisabled,u=t.isMulti,i=t.name,a=this.state.selectValue;if(i&&!o){if(u){if(n){var s=a.map(function(t){return e.getOptionValue(t)}).join(n);return r.default.createElement("input",{name:i,type:"hidden",value:s})}var l=a.length>0?a.map(function(t,n){return r.default.createElement("input",{key:"i-".concat(n),name:i,type:"hidden",value:e.getOptionValue(t)})}):r.default.createElement("input",{name:i,type:"hidden"});return r.default.createElement("div",null,l)}var c=a[0]?this.getOptionValue(a[0]):"";return r.default.createElement("input",{name:i,type:"hidden",value:c})}}},{key:"renderLiveRegion",value:function(){return this.state.isFocused?r.default.createElement(s.A11yText,{"aria-live":"assertive"},r.default.createElement("p",{id:"aria-selection-event"}," ",this.state.ariaLiveSelection),r.default.createElement("p",{id:"aria-context"}," ",this.constructAriaLiveMessage())):null}},{key:"render",value:function(){var e=this.components,t=e.Control,n=e.IndicatorsContainer,o=e.SelectContainer,u=e.ValueContainer,i=this.props,a=i.className,s=i.id,l=i.isDisabled,c=i.menuIsOpen,p=this.state.isFocused,f=this.commonProps=this.getCommonProps();return r.default.createElement(o,g({},f,{className:a,innerProps:{id:s,onKeyDown:this.onKeyDown},isDisabled:l,isFocused:p}),this.renderLiveRegion(),r.default.createElement(t,g({},f,{innerRef:this.getControlRef,innerProps:{onMouseDown:this.onControlMouseDown,onTouchEnd:this.onControlTouchEnd},isDisabled:l,isFocused:p,menuIsOpen:c}),r.default.createElement(u,g({},f,{isDisabled:l}),this.renderPlaceholderOrValue(),this.renderInput()),r.default.createElement(n,g({},f,{isDisabled:l}),this.renderClearIndicator(),this.renderLoadingIndicator(),this.renderIndicatorSeparator(),this.renderDropdownIndicator())),this.renderMenu(),this.renderFormField())}}])&&E(n.prototype,a),p&&E(n,p),t}();t.default=D,P(D,"defaultProps",A)},lQ6M:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.MultiValueRemove=t.MultiValueLabel=t.MultiValueContainer=t.MultiValueGeneric=t.multiValueRemoveCSS=t.multiValueLabelCSS=t.multiValueCSS=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd")),o=n("PAeb"),u=n("+URl");function i(e){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){s(e,t,n[t])})}return e}function s(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function c(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function p(e,t,n){return t&&c(e.prototype,t),n&&c(e,n),e}function f(e,t){return!t||"object"!==i(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function d(e){return(d=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function h(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&b(e,t)}function b(e,t){return(b=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}t.multiValueCSS=function(e){var t=e.theme,n=t.spacing,r=t.borderRadius;return{backgroundColor:t.colors.neutral10,borderRadius:r/2,display:"flex",margin:n.baseUnit/2,minWidth:0}};t.multiValueLabelCSS=function(e){var t=e.theme,n=t.borderRadius,r=t.colors,o=e.cropWithEllipsis;return{borderRadius:n/2,color:r.neutral80,fontSize:"85%",overflow:"hidden",padding:3,paddingLeft:6,textOverflow:o?"ellipsis":null,whiteSpace:"nowrap"}};t.multiValueRemoveCSS=function(e){var t=e.theme,n=t.spacing,r=t.borderRadius,o=t.colors;return{alignItems:"center",borderRadius:r/2,backgroundColor:e.isFocused&&o.dangerLight,display:"flex",paddingLeft:n.baseUnit,paddingRight:n.baseUnit,":hover":{backgroundColor:o.dangerLight,color:o.danger}}};var m=function(e){var t=e.children,n=e.innerProps;return r.default.createElement("div",n,t)};t.MultiValueGeneric=m;var v=m;t.MultiValueContainer=v;var g=m;t.MultiValueLabel=g;var y=function(e){function t(){return l(this,t),f(this,d(t).apply(this,arguments))}return h(t,r.Component),p(t,[{key:"render",value:function(){var e=this.props,t=e.children,n=e.innerProps;return r.default.createElement("div",n,t||r.default.createElement(u.CrossIcon,{size:14}))}}]),t}();t.MultiValueRemove=y;var O=function(e){function t(){return l(this,t),f(this,d(t).apply(this,arguments))}return h(t,r.Component),p(t,[{key:"render",value:function(){var e=this.props,t=e.children,n=e.className,u=e.components,i=e.cx,s=e.data,l=e.getStyles,c=e.innerProps,p=e.isDisabled,f=e.removeProps,d=e.selectProps,h=u.Container,b=u.Label,m=u.Remove,v=a({className:i((0,o.css)(l("multiValue",this.props)),{"multi-value":!0,"multi-value--is-disabled":p},n)},c),g={className:i((0,o.css)(l("multiValueLabel",this.props)),{"multi-value__label":!0},n)},y=a({className:i((0,o.css)(l("multiValueRemove",this.props)),{"multi-value__remove":!0},n)},f);return r.default.createElement(h,{data:s,innerProps:v,selectProps:d},r.default.createElement(b,{data:s,innerProps:g,selectProps:d},t),r.default.createElement(m,{data:s,innerProps:y,selectProps:d}))}}]),t}();s(O,"defaultProps",{cropWithEllipsis:!0});var E=O;t.default=E},n929:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.GroupHeading=t.groupHeadingCSS=t.groupCSS=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},r=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(r=r.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),r.forEach(function(t){a(e,t,n[t])})}return e}function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function s(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function l(){return(l=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.groupCSS=function(e){var t=e.theme.spacing;return{paddingBottom:2*t.baseUnit,paddingTop:2*t.baseUnit}};t.groupHeadingCSS=function(e){var t=e.theme.spacing;return{color:"#999",cursor:"default",display:"block",fontSize:"75%",fontWeight:"500",marginBottom:"0.25em",paddingLeft:3*t.baseUnit,paddingRight:3*t.baseUnit,textTransform:"uppercase"}};t.GroupHeading=function(e){var t=e.className,n=e.cx,r=e.getStyles,a=e.theme,c=(e.selectProps,s(e,["className","cx","getStyles","theme","selectProps"]));return o.default.createElement("div",l({className:n((0,u.css)(r("groupHeading",i({theme:a},c))),{"group-heading":!0},t)},c))};var c=function(e){var t=e.children,n=e.className,r=e.cx,i=e.getStyles,a=e.Heading,s=e.headingProps,c=e.label,p=e.theme,f=e.selectProps;return o.default.createElement("div",{className:r((0,u.css)(i("group",e)),{group:!0},n)},o.default.createElement(a,l({},s,{selectProps:f,theme:p,getStyles:i,cx:r}),c),o.default.createElement("div",null,t))};t.default=c},oUUL:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.defaultProps=void 0;var r=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)if(Object.prototype.hasOwnProperty.call(e,n)){var r=Object.defineProperty&&Object.getOwnPropertyDescriptor?Object.getOwnPropertyDescriptor(e,n):{};r.get||r.set?Object.defineProperty(t,n,r):t[n]=e[n]}return t.default=e,t}(n("cDcd"));function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function u(){return(u=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function i(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},u=Object.keys(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var u=Object.getOwnPropertySymbols(e);for(r=0;r<u.length;r++)n=u[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function a(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function s(e){return(s=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function l(e,t){return(l=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function c(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function p(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var f={defaultInputValue:"",defaultMenuIsOpen:!1,defaultValue:null};t.defaultProps=f;var d=function(e){var t,n;return n=t=function(t){function n(){var e,t,r,u;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n);for(var i=arguments.length,a=new Array(i),l=0;l<i;l++)a[l]=arguments[l];return r=this,u=(e=s(n)).call.apply(e,[this].concat(a)),t=!u||"object"!==o(u)&&"function"!=typeof u?c(r):u,p(c(c(t)),"select",void 0),p(c(c(t)),"state",{inputValue:void 0!==t.props.inputValue?t.props.inputValue:t.props.defaultInputValue,menuIsOpen:void 0!==t.props.menuIsOpen?t.props.menuIsOpen:t.props.defaultMenuIsOpen,value:void 0!==t.props.value?t.props.value:t.props.defaultValue}),p(c(c(t)),"onChange",function(e,n){t.callProp("onChange",e,n),t.setState({value:e})}),p(c(c(t)),"onInputChange",function(e,n){var r=t.callProp("onInputChange",e,n);t.setState({inputValue:void 0!==r?r:e})}),p(c(c(t)),"onMenuOpen",function(){t.callProp("onMenuOpen"),t.setState({menuIsOpen:!0})}),p(c(c(t)),"onMenuClose",function(){t.callProp("onMenuClose"),t.setState({menuIsOpen:!1})}),t}var f,d,h;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&l(e,t)}(n,r.Component),f=n,(d=[{key:"focus",value:function(){this.select.focus()}},{key:"blur",value:function(){this.select.blur()}},{key:"getProp",value:function(e){return void 0!==this.props[e]?this.props[e]:this.state[e]}},{key:"callProp",value:function(e){if("function"==typeof this.props[e]){for(var t,n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return(t=this.props)[e].apply(t,r)}}},{key:"render",value:function(){var t=this,n=this.props,o=(n.defaultInputValue,n.defaultMenuIsOpen,n.defaultValue,i(n,["defaultInputValue","defaultMenuIsOpen","defaultValue"]));return r.default.createElement(e,u({},o,{ref:function(e){t.select=e},inputValue:this.getProp("inputValue"),menuIsOpen:this.getProp("menuIsOpen"),onChange:this.onChange,onInputChange:this.onInputChange,onMenuClose:this.onMenuClose,onMenuOpen:this.onMenuOpen,value:this.getProp("value")}))}}])&&a(f.prototype,d),h&&a(f,h),n}(),p(t,"defaultProps",f),n};t.default=d},oZmp:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isOptionDisabled=t.getOptionValue=t.getOptionLabel=t.formatGroupLabel=void 0;t.formatGroupLabel=function(e){return e.label};t.getOptionLabel=function(e){return e.label};t.getOptionValue=function(e){return e.value};t.isOptionDisabled=function(e){return!!e.isDisabled}},oj4i:function(e,t,n){"use strict";var r=function(e){var t={};return function(n){return void 0===t[n]&&(t[n]=e(n)),t[n]}},o={animationIterationCount:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1};var u=function(e){for(var t,n=e.length,r=n^n,o=0;n>=4;)t=1540483477*(65535&(t=255&e.charCodeAt(o)|(255&e.charCodeAt(++o))<<8|(255&e.charCodeAt(++o))<<16|(255&e.charCodeAt(++o))<<24))+((1540483477*(t>>>16)&65535)<<16),r=1540483477*(65535&r)+((1540483477*(r>>>16)&65535)<<16)^(t=1540483477*(65535&(t^=t>>>24))+((1540483477*(t>>>16)&65535)<<16)),n-=4,++o;switch(n){case 3:r^=(255&e.charCodeAt(o+2))<<16;case 2:r^=(255&e.charCodeAt(o+1))<<8;case 1:r=1540483477*(65535&(r^=255&e.charCodeAt(o)))+((1540483477*(r>>>16)&65535)<<16)}return r=1540483477*(65535&(r^=r>>>13))+((1540483477*(r>>>16)&65535)<<16),((r^=r>>>15)>>>0).toString(36)};var i=function(e){function t(e,t,r){var o=t.trim().split(h);t=o;var u=o.length,i=e.length;switch(i){case 0:case 1:var a=0;for(e=0===i?"":e[0]+" ";a<u;++a)t[a]=n(e,t[a],r).trim();break;default:var s=a=0;for(t=[];a<u;++a)for(var l=0;l<i;++l)t[s++]=n(e[l]+" ",o[a],r).trim()}return t}function n(e,t,n){var r=t.charCodeAt(0);switch(33>r&&(r=(t=t.trim()).charCodeAt(0)),r){case 38:return t.replace(b,"$1"+e.trim());case 58:return e.trim()+t.replace(b,"$1"+e.trim());default:if(0<1*n&&0<t.indexOf("\f"))return t.replace(b,(58===e.charCodeAt(0)?"":"$1")+e.trim())}return e+t}function r(e,t,n,u){var i=e+";",a=2*t+3*n+4*u;if(944===a){e=i.indexOf(":",9)+1;var s=i.substring(e,i.length-1).trim();return s=i.substring(0,e).trim()+s+";",1===M||2===M&&o(s,1)?"-webkit-"+s+s:s}if(0===M||2===M&&!o(i,1))return i;switch(a){case 1015:return 97===i.charCodeAt(10)?"-webkit-"+i+i:i;case 951:return 116===i.charCodeAt(3)?"-webkit-"+i+i:i;case 963:return 110===i.charCodeAt(5)?"-webkit-"+i+i:i;case 1009:if(100!==i.charCodeAt(4))break;case 969:case 942:return"-webkit-"+i+i;case 978:return"-webkit-"+i+"-moz-"+i+i;case 1019:case 983:return"-webkit-"+i+"-moz-"+i+"-ms-"+i+i;case 883:if(45===i.charCodeAt(8))return"-webkit-"+i+i;if(0<i.indexOf("image-set(",11))return i.replace(P,"$1-webkit-$2")+i;break;case 932:if(45===i.charCodeAt(4))switch(i.charCodeAt(5)){case 103:return"-webkit-box-"+i.replace("-grow","")+"-webkit-"+i+"-ms-"+i.replace("grow","positive")+i;case 115:return"-webkit-"+i+"-ms-"+i.replace("shrink","negative")+i;case 98:return"-webkit-"+i+"-ms-"+i.replace("basis","preferred-size")+i}return"-webkit-"+i+"-ms-"+i+i;case 964:return"-webkit-"+i+"-ms-flex-"+i+i;case 1023:if(99!==i.charCodeAt(8))break;return"-webkit-box-pack"+(s=i.substring(i.indexOf(":",15)).replace("flex-","").replace("space-between","justify"))+"-webkit-"+i+"-ms-flex-pack"+s+i;case 1005:return f.test(i)?i.replace(p,":-webkit-")+i.replace(p,":-moz-")+i:i;case 1e3:switch(t=(s=i.substring(13).trim()).indexOf("-")+1,s.charCodeAt(0)+s.charCodeAt(t)){case 226:s=i.replace(y,"tb");break;case 232:s=i.replace(y,"tb-rl");break;case 220:s=i.replace(y,"lr");break;default:return i}return"-webkit-"+i+"-ms-"+s+i;case 1017:if(-1===i.indexOf("sticky",9))break;case 975:switch(t=(i=e).length-10,a=(s=(33===i.charCodeAt(t)?i.substring(0,t):i).substring(e.indexOf(":",7)+1).trim()).charCodeAt(0)+(0|s.charCodeAt(7))){case 203:if(111>s.charCodeAt(8))break;case 115:i=i.replace(s,"-webkit-"+s)+";"+i;break;case 207:case 102:i=i.replace(s,"-webkit-"+(102<a?"inline-":"")+"box")+";"+i.replace(s,"-webkit-"+s)+";"+i.replace(s,"-ms-"+s+"box")+";"+i}return i+";";case 938:if(45===i.charCodeAt(5))switch(i.charCodeAt(6)){case 105:return s=i.replace("-items",""),"-webkit-"+i+"-webkit-box-"+s+"-ms-flex-"+s+i;case 115:return"-webkit-"+i+"-ms-flex-item-"+i.replace(S,"")+i;default:return"-webkit-"+i+"-ms-flex-line-pack"+i.replace("align-content","").replace(S,"")+i}break;case 973:case 989:if(45!==i.charCodeAt(3)||122===i.charCodeAt(4))break;case 931:case 953:if(!0===w.test(e))return 115===(s=e.substring(e.indexOf(":")+1)).charCodeAt(0)?r(e.replace("stretch","fill-available"),t,n,u).replace(":fill-available",":stretch"):i.replace(s,"-webkit-"+s)+i.replace(s,"-moz-"+s.replace("fill-",""))+i;break;case 962:if(i="-webkit-"+i+(102===i.charCodeAt(5)?"-ms-"+i:"")+i,211===n+u&&105===i.charCodeAt(13)&&0<i.indexOf("transform",10))return i.substring(0,i.indexOf(";",27)+1).replace(d,"$1-webkit-$2")+i}return i}function o(e,t){var n=e.indexOf(1===t?":":"{"),r=e.substring(0,3!==t?n:10);return n=e.substring(n+1,e.length-1),_(2!==t?r:r.replace(C,"$1"),n,t)}function u(e,t){var n=r(t,t.charCodeAt(0),t.charCodeAt(1),t.charCodeAt(2));return n!==t+";"?n.replace(E," or ($1)").substring(4):"("+t+")"}function i(e,t,n,r,o,u,i,a,l,c){for(var p,f=0,d=t;f<x;++f)switch(p=F[f].call(s,e,d,n,r,o,u,i,a,l,c)){case void 0:case!1:case!0:case null:break;default:d=p}if(d!==t)return d}function a(e){return void 0!==(e=e.prefix)&&(_=null,e?"function"!=typeof e?M=1:(M=2,_=e):M=0),a}function s(e,n){var a=e;if(33>a.charCodeAt(0)&&(a=a.trim()),a=[a],0<x){var s=i(-1,n,a,a,j,A,0,0,0,0);void 0!==s&&"string"==typeof s&&(n=s)}var p=function e(n,a,s,p,f){for(var d,h,b,y,E,S=0,C=0,w=0,P=0,F=0,_=0,T=b=d=0,L=0,V=0,R=0,B=0,H=s.length,N=H-1,U="",W="",z="",G="";L<H;){if(h=s.charCodeAt(L),L===N&&0!==C+P+w+S&&(0!==C&&(h=47===C?10:47),P=w=S=0,H++,N++),0===C+P+w+S){if(L===N&&(0<V&&(U=U.replace(c,"")),0<U.trim().length)){switch(h){case 32:case 9:case 59:case 13:case 10:break;default:U+=s.charAt(L)}h=59}switch(h){case 123:for(d=(U=U.trim()).charCodeAt(0),b=1,B=++L;L<H;){switch(h=s.charCodeAt(L)){case 123:b++;break;case 125:b--;break;case 47:switch(h=s.charCodeAt(L+1)){case 42:case 47:e:{for(T=L+1;T<N;++T)switch(s.charCodeAt(T)){case 47:if(42===h&&42===s.charCodeAt(T-1)&&L+2!==T){L=T+1;break e}break;case 10:if(47===h){L=T+1;break e}}L=T}}break;case 91:h++;case 40:h++;case 34:case 39:for(;L++<N&&s.charCodeAt(L)!==h;);}if(0===b)break;L++}switch(b=s.substring(B,L),0===d&&(d=(U=U.replace(l,"").trim()).charCodeAt(0)),d){case 64:switch(0<V&&(U=U.replace(c,"")),h=U.charCodeAt(1)){case 100:case 109:case 115:case 45:V=a;break;default:V=k}if(B=(b=e(a,V,b,h,f+1)).length,0<x&&(E=i(3,b,V=t(k,U,R),a,j,A,B,h,f,p),U=V.join(""),void 0!==E&&0===(B=(b=E.trim()).length)&&(h=0,b="")),0<B)switch(h){case 115:U=U.replace(O,u);case 100:case 109:case 45:b=U+"{"+b+"}";break;case 107:b=(U=U.replace(m,"$1 $2"))+"{"+b+"}",b=1===M||2===M&&o("@"+b,3)?"@-webkit-"+b+"@"+b:"@"+b;break;default:b=U+b,112===p&&(W+=b,b="")}else b="";break;default:b=e(a,t(a,U,R),b,p,f+1)}z+=b,b=R=V=T=d=0,U="",h=s.charCodeAt(++L);break;case 125:case 59:if(1<(B=(U=(0<V?U.replace(c,""):U).trim()).length))switch(0===T&&(d=U.charCodeAt(0),45===d||96<d&&123>d)&&(B=(U=U.replace(" ",":")).length),0<x&&void 0!==(E=i(1,U,a,n,j,A,W.length,p,f,p))&&0===(B=(U=E.trim()).length)&&(U="\0\0"),d=U.charCodeAt(0),h=U.charCodeAt(1),d){case 0:break;case 64:if(105===h||99===h){G+=U+s.charAt(L);break}default:58!==U.charCodeAt(B-1)&&(W+=r(U,d,h,U.charCodeAt(2)))}R=V=T=d=0,U="",h=s.charCodeAt(++L)}}switch(h){case 13:case 10:47===C?C=0:0===1+d&&107!==p&&0<U.length&&(V=1,U+="\0"),0<x*I&&i(0,U,a,n,j,A,W.length,p,f,p),A=1,j++;break;case 59:case 125:if(0===C+P+w+S){A++;break}default:switch(A++,y=s.charAt(L),h){case 9:case 32:if(0===P+S+C)switch(F){case 44:case 58:case 9:case 32:y="";break;default:32!==h&&(y=" ")}break;case 0:y="\\0";break;case 12:y="\\f";break;case 11:y="\\v";break;case 38:0===P+C+S&&(V=R=1,y="\f"+y);break;case 108:if(0===P+C+S+D&&0<T)switch(L-T){case 2:112===F&&58===s.charCodeAt(L-3)&&(D=F);case 8:111===_&&(D=_)}break;case 58:0===P+C+S&&(T=L);break;case 44:0===C+w+P+S&&(V=1,y+="\r");break;case 34:case 39:0===C&&(P=P===h?0:0===P?h:P);break;case 91:0===P+C+w&&S++;break;case 93:0===P+C+w&&S--;break;case 41:0===P+C+S&&w--;break;case 40:if(0===P+C+S){if(0===d)switch(2*F+3*_){case 533:break;default:d=1}w++}break;case 64:0===C+w+P+S+T+b&&(b=1);break;case 42:case 47:if(!(0<P+S+w))switch(C){case 0:switch(2*h+3*s.charCodeAt(L+1)){case 235:C=47;break;case 220:B=L,C=42}break;case 42:47===h&&42===F&&B+2!==L&&(33===s.charCodeAt(B+2)&&(W+=s.substring(B,L+1)),y="",C=0)}}0===C&&(U+=y)}_=F,F=h,L++}if(0<(B=W.length)){if(V=a,0<x&&void 0!==(E=i(2,W,V,n,j,A,B,p,f,p))&&0===(W=E).length)return G+W+z;if(W=V.join(",")+"{"+W+"}",0!=M*D){switch(2!==M||o(W,2)||(D=0),D){case 111:W=W.replace(g,":-moz-$1")+W;break;case 112:W=W.replace(v,"::-webkit-input-$1")+W.replace(v,"::-moz-$1")+W.replace(v,":-ms-input-$1")+W}D=0}}return G+W+z}(k,a,n,0,0);return 0<x&&void 0!==(s=i(-2,p,a,a,j,A,p.length,0,0,0))&&(p=s),D=0,A=j=1,p}var l=/^\0+/g,c=/[\0\r\f]/g,p=/: */g,f=/zoo|gra/,d=/([,: ])(transform)/g,h=/,\r+?/g,b=/([\t\r\n ])*\f?&/g,m=/@(k\w+)\s*(\S*)\s*/,v=/::(place)/g,g=/:(read-only)/g,y=/[svh]\w+-[tblr]{2}/,O=/\(\s*(.*)\s*\)/g,E=/([\s\S]*?);/g,S=/-self|flex-/g,C=/[^]*?(:[rp][el]a[\w-]+)[^]*/,w=/stretch|:\s*\w+\-(?:conte|avail)/,P=/([^-])(image-set\()/,A=1,j=1,D=0,M=1,k=[],F=[],x=0,_=null,I=0;return s.use=function e(t){switch(t){case void 0:case null:x=F.length=0;break;default:switch(t.constructor){case Array:for(var n=0,r=t.length;n<r;++n)e(t[n]);break;case Function:F[x++]=t;break;case Boolean:I=0|!!t}}return e},s.set=a,void 0!==e&&a(e),s},a=n("TAZq"),s=n.n(a),l=/[A-Z]|^ms/g,c=r(function(e){return e.replace(l,"-$&").toLowerCase()}),p=function(e,t){return null==t||"boolean"==typeof t?"":1===o[e]||45===e.charCodeAt(1)||isNaN(t)||0===t?t:t+"px"},f=function e(t){for(var n=t.length,r=0,o="";r<n;r++){var u=t[r];if(null!=u){var i=void 0;switch(typeof u){case"boolean":break;case"function":0,i=e([u()]);break;case"object":if(Array.isArray(u))i=e(u);else for(var a in i="",u)u[a]&&a&&(i&&(i+=" "),i+=a);break;default:i=u}i&&(o&&(o+=" "),o+=i)}}return o},d="undefined"!=typeof document;function h(e){var t=document.createElement("style");return t.setAttribute("data-emotion",e.key||""),void 0!==e.nonce&&t.setAttribute("nonce",e.nonce),t.appendChild(document.createTextNode("")),(void 0!==e.container?e.container:document.head).appendChild(t),t}var b=function(){function e(e){this.isSpeedy=!0,this.tags=[],this.ctr=0,this.opts=e}var t=e.prototype;return t.inject=function(){if(this.injected)throw new Error("already injected!");this.tags[0]=h(this.opts),this.injected=!0},t.speedy=function(e){if(0!==this.ctr)throw new Error("cannot change speedy now");this.isSpeedy=!!e},t.insert=function(e,t){if(this.isSpeedy){var n=function(e){if(e.sheet)return e.sheet;for(var t=0;t<document.styleSheets.length;t++)if(document.styleSheets[t].ownerNode===e)return document.styleSheets[t]}(this.tags[this.tags.length-1]);try{n.insertRule(e,n.cssRules.length)}catch(e){0}}else{var r=h(this.opts);this.tags.push(r),r.appendChild(document.createTextNode(e+(t||"")))}this.ctr++,this.ctr%65e3==0&&this.tags.push(h(this.opts))},t.flush=function(){this.tags.forEach(function(e){return e.parentNode.removeChild(e)}),this.tags=[],this.ctr=0,this.injected=!1},e}();t.a=function(e,t){if(void 0!==e.__SECRET_EMOTION__)return e.__SECRET_EMOTION__;void 0===t&&(t={});var n,r,o=t.key||"css",a=s()(function(e){n+=e,d&&h.insert(e,v)});void 0!==t.prefix&&(r={prefix:t.prefix});var l={registered:{},inserted:{},nonce:t.nonce,key:o},h=new b(t);d&&h.inject();var m=new i(r);m.use(t.stylisPlugins)(a);var v="";function g(e,t){if(null==e)return"";switch(typeof e){case"boolean":return"";case"function":if(void 0!==e.__emotion_styles){var n=e.toString();return n}return g.call(this,void 0===this?e():e(this.mergedProps,this.context),t);case"object":return function(e){if(E.has(e))return E.get(e);var t="";return Array.isArray(e)?e.forEach(function(e){t+=g.call(this,e,!1)},this):Object.keys(e).forEach(function(n){"object"!=typeof e[n]?void 0!==l.registered[e[n]]?t+=n+"{"+l.registered[e[n]]+"}":t+=c(n)+":"+p(n,e[n])+";":Array.isArray(e[n])&&"string"==typeof e[n][0]&&void 0===l.registered[e[n][0]]?e[n].forEach(function(e){t+=c(n)+":"+p(n,e)+";"}):t+=n+"{"+g.call(this,e[n],!1)+"}"},this),E.set(e,t),t}.call(this,e);default:var r=l.registered[e];return!1===t&&void 0!==r?r:e}}var y,O,E=new WeakMap,S=/label:\s*([^\s;\n{]+)\s*;/g,C=function(e){var t=!0,n="",r="";null==e||void 0===e.raw?(t=!1,n+=g.call(this,e,!1)):n+=e[0];for(var o=arguments.length,i=new Array(o>1?o-1:0),a=1;a<o;a++)i[a-1]=arguments[a];return i.forEach(function(r,o){n+=g.call(this,r,46===n.charCodeAt(n.length-1)),!0===t&&void 0!==e[o+1]&&(n+=e[o+1])},this),O=n,n=n.replace(S,function(e,t){return r+="-"+t,""}),y=function(e,t){return u(e+t)+t}(n,r),n};function w(e,t){void 0===l.inserted[y]&&(n="",m(e,t),l.inserted[y]=n)}var P=function(){var e=C.apply(this,arguments),t=o+"-"+y;return void 0===l.registered[t]&&(l.registered[t]=O),w("."+t,e),t};function A(e,t){var n="";return t.split(" ").forEach(function(t){void 0!==l.registered[t]?e.push(t):n+=t+" "}),n}function j(e,t){var n=[],r=A(n,e);return n.length<2?e:r+P(n,t)}function D(e){l.inserted[e]=!0}if(d){var M=document.querySelectorAll("[data-emotion-"+o+"]");Array.prototype.forEach.call(M,function(e){h.tags[0].parentNode.insertBefore(e,h.tags[0]),e.getAttribute("data-emotion-"+o).split(" ").forEach(D)})}var k={flush:function(){d&&(h.flush(),h.inject()),l.inserted={},l.registered={}},hydrate:function(e){e.forEach(D)},cx:function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return j(f(t))},merge:j,getRegisteredStyles:A,injectGlobal:function(){w("",C.apply(this,arguments))},keyframes:function(){var e=C.apply(this,arguments),t="animation-"+y;return w("","@keyframes "+t+"{"+e+"}"),t},css:P,sheet:h,caches:l};return e.__SECRET_EMOTION__=k,k}},rc6b:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.css=void 0;var r,o=(r=n("cDcd"))&&r.__esModule?r:{default:r},u=n("PAeb");function i(){return(i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}t.css=function(e){var t=e.isDisabled,n=e.isFocused,r=e.theme,o=r.colors,u=r.borderRadius,i=r.spacing;return{alignItems:"center",backgroundColor:t?o.neutral5:o.neutral0,borderColor:t?o.neutral10:n?o.primary:o.neutral20,borderRadius:u,borderStyle:"solid",borderWidth:1,boxShadow:n?"0 0 0 1px ".concat(o.primary):null,cursor:"default",display:"flex",flexWrap:"wrap",justifyContent:"space-between",minHeight:i.controlHeight,outline:"0 !important",position:"relative",transition:"all 100ms","&:hover":{borderColor:n?o.primary:o.neutral30}}};var a=function(e){var t=e.children,n=e.cx,r=e.getStyles,a=e.className,s=e.isDisabled,l=e.isFocused,c=e.innerRef,p=e.innerProps,f=e.menuIsOpen;return o.default.createElement("div",i({ref:c,className:n((0,u.css)(r("control",e)),{control:!0,"control--is-disabled":s,"control--is-focused":l,"control--menu-is-open":f},a)},p),t)};t.default=a},tJT6:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"A11yText",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(t,"DummyInput",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"NodeResolver",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"ScrollBlock",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"ScrollCaptor",{enumerable:!0,get:function(){return a.default}});var r=s(n("/+00")),o=s(n("H6Zs")),u=s(n("N3bB")),i=s(n("QcsS")),a=s(n("d6Hc"));function s(e){return e&&e.__esModule?e:{default:e}}},xEkU:function(e,t,n){(function(t){for(var r=n("bQgK"),o="undefined"==typeof window?t:window,u=["moz","webkit"],i="AnimationFrame",a=o["request"+i],s=o["cancel"+i]||o["cancelRequest"+i],l=0;!a&&l<u.length;l++)a=o[u[l]+"Request"+i],s=o[u[l]+"Cancel"+i]||o[u[l]+"CancelRequest"+i];if(!a||!s){var c=0,p=0,f=[];a=function(e){if(0===f.length){var t=r(),n=Math.max(0,1e3/60-(t-c));c=n+t,setTimeout(function(){var e=f.slice(0);f.length=0;for(var t=0;t<e.length;t++)if(!e[t].cancelled)try{e[t].callback(c)}catch(e){setTimeout(function(){throw e},0)}},Math.round(n))}return f.push({handle:++p,callback:e,cancelled:!1}),p},s=function(e){for(var t=0;t<f.length;t++)f[t].handle===e&&(f[t].cancelled=!0)}}e.exports=function(e){return a.call(o,e)},e.exports.cancel=function(){s.apply(o,arguments)},e.exports.polyfill=function(e){e||(e=o),e.requestAnimationFrame=a,e.cancelAnimationFrame=s}}).call(this,n("yLpj"))},yLpj:function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n}}]);
dist/core/admin-notices-api.min.js CHANGED
@@ -1 +1 @@
1
- this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["admin-notices-api"]=function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s="M/oR")}({"1ZqX":function(t,e){!function(){t.exports=this.wp.data}()},"7W2i":function(t,e,n){var r=n("SksO");t.exports=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&r(t,e)}},"92Nh":function(t,e){t.exports=function(t,e,n){if(!e.has(t))throw new TypeError("attempted to set private field on non-instance");var r=e.get(t);if(!r.writable)throw new TypeError("attempted to set read only private field");return r.value=n,n}},Bnag:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},EbDI:function(t,e){t.exports=function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}},Ijbi:function(t,e){t.exports=function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}},J4zp:function(t,e,n){var r=n("wTVA"),o=n("m0LI"),i=n("wkBT");t.exports=function(t,e){return r(t)||o(t,e)||i()}},"M/oR":function(t,e,n){"use strict";n.r(e);var r={};n.r(r),n.d(r,"receiveNotices",function(){return U}),n.d(r,"startNoticeAction",function(){return C}),n.d(r,"finishNoticeAction",function(){return F}),n.d(r,"failedNoticeAction",function(){return L}),n.d(r,"receiveMutedHighlights",function(){return G}),n.d(r,"startUpdateMutedHighlight",function(){return J}),n.d(r,"finishUpdateMutedHighlight",function(){return V}),n.d(r,"failedUpdateMutedHighlight",function(){return q}),n.d(r,"doNoticeAction",function(){return W}),n.d(r,"updateMutedHighlight",function(){return Z}),n.d(r,"RECEIVE_NOTICES",function(){return B}),n.d(r,"START_NOTICE_ACTION",function(){return Y}),n.d(r,"FINISH_NOTICE_ACTION",function(){return X}),n.d(r,"FAILED_NOTICE_ACTION",function(){return z}),n.d(r,"RECEIVE_MUTED_HIGHLIGHTS",function(){return K}),n.d(r,"START_UPDATE_MUTED_HIGHLIGHT",function(){return Q}),n.d(r,"FINISH_UPDATE_MUTED_HIGHLIGHT",function(){return $}),n.d(r,"FAILED_UPDATE_MUTED_HIGHLIGHT",function(){return tt});var o={};n.r(o),n.d(o,"isResolving",function(){return et}),n.d(o,"isResolved",function(){return nt}),n.d(o,"getNotices",function(){return rt}),n.d(o,"areNoticesLoaded",function(){return ot}),n.d(o,"isDoingAction",function(){return it}),n.d(o,"getInProgressActions",function(){return ct}),n.d(o,"getMutedHighlights",function(){return st}),n.d(o,"getMutedHighlightUpdatesInFlight",function(){return at});var i={};n.r(i),n.d(i,"getNotices",function(){return pt}),n.d(i,"getMutedHighlights",function(){return dt});var u=n("1ZqX"),c=n("RIqP"),s=n.n(c),a=n("MVZn"),f=n.n(a),l=n("YLtl"),p=n("ywyh"),d=n.n(p),h=(n("J4zp"),n("lwsE")),g=n.n(h),y=n("W8MJ"),m=n.n(y),b=n("lSNA"),v=n.n(b),I=(n("92Nh"),n("tmk3"),new WeakMap,new WeakMap,n("a1gu")),T=n.n(I),x=n("Nsbk"),w=n.n(x),E=n("7W2i"),_=n.n(E),O=n("PJYZ"),A=n.n(O),S=n("oShl"),H=n.n(S),N=n("l3Sj"),j=function(t){function e(t){var n,r;g()(this,e);for(var o=arguments.length,i=new Array(o>1?o-1:0),u=1;u<o;u++)i[u-1]=arguments[u];for(var c in r=T()(this,(n=w()(e)).call.apply(n,[this,t.message||Object(N.__)("An unknown error occurred.","better-wp-security")].concat(i))),Error.captureStackTrace&&Error.captureStackTrace(A()(A()(r)),e),r.__response=t,t)t.hasOwnProperty(c)&&Object.defineProperty(A()(A()(r)),c,{value:t[c],configurable:!0,enumerable:!0,writable:!0});return r}return _()(e,t),m()(e,[{key:"toString",value:function(){return this.__response.toString()}},{key:"getResponse",value:function(){return this.__response}}]),e}(H()(Error));function P(t){if(t instanceof Error)throw t;throw new j(t)}function R(t){return{type:"API_FETCH",request:t}}var M={API_FETCH:function(t){var e=t.request;return d()(e).catch(P)},SELECT:function(t){var e,n=t.selectorName,r=t.args;return(e=Object(u.select)("ithemes-security/admin-notices"))[n].apply(e,s()(r))},CREATE_NOTICE:function(t){var e=t.status,n=t.content,r=t.options;r.autoDismiss&&(r.id=r.id||Object(l.uniqueId)("itsec-auto-dismiss-"),setTimeout(function(){return Object(u.dispatch)("core/notices").removeNotice(r.id,r.context)},r.autoDismiss)),Object(u.dispatch)("core/notices").createNotice(e,n,r)}},D=regeneratorRuntime.mark(W),k=regeneratorRuntime.mark(Z);function U(t){return{type:B,notices:t}}function C(t,e){return{type:Y,noticeId:t,actionId:e}}function F(t,e,n){return{type:X,noticeId:t,actionId:e,response:n}}function L(t,e,n){return{type:z,noticeId:t,actionId:e,error:n}}function G(t){return{type:K,mutedHighlights:t}}function J(t,e){return{type:Q,slug:t,mute:e}}function V(t,e){return{type:$,slug:t,mute:e}}function q(t,e,n){return{type:tt,slug:t,mute:e,error:n}}function W(t,e){var n,r,o=arguments;return regeneratorRuntime.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:return n=o.length>2&&void 0!==o[2]?o[2]:{},i.next=3,C(t,e);case 3:return i.prev=3,i.next=6,R({path:"/ithemes-security/v1/admin-notices/".concat(t,"/").concat(e),method:"POST",data:n});case 6:r=i.sent,i.next=14;break;case 9:return i.prev=9,i.t0=i.catch(3),i.next=13,L(t,e,i.t0);case 13:return i.abrupt("return",i.t0);case 14:return i.next=16,F(t,e,r);case 16:return i.abrupt("return",r);case 17:case"end":return i.stop()}},D,this,[[3,9]])}function Z(t,e){var n;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,J(t,e);case 2:return r.prev=2,r.next=5,R({path:"/ithemes-security/v1/admin-notices/settings",method:"PUT",data:{muted_highlights:v()({},t,e)}});case 5:n=r.sent,r.next=13;break;case 8:return r.prev=8,r.t0=r.catch(2),r.next=12,q(t,e,r.t0);case 12:return r.abrupt("return",r.t0);case 13:return r.next=15,V(t,e);case 15:return r.abrupt("return",n);case 16:case"end":return r.stop()}},k,this,[[2,8]])}var B="RECEIVE_NOTICES",Y="START_NOTICE_ACTION",X="FINISH_NOTICE_ACTION",z="FAILED_NOTICE_ACTION",K="RECEIVE_MUTED_HIGHLIGHTS",Q="START_UPDATE_MUTED_HIGHLIGHT",$="FINISH_UPDATE_MUTED_HIGHLIGHT",tt="FAILED_UPDATE_MUTED_HIGHLIGHT";function et(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];return Object(u.select)("core/data").isResolving("ithemes-security/admin-notices",t,n)}function nt(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];return Object(u.select)("core/data").hasFinishedResolution("ithemes-security/admin-notices",t,n)}function rt(t){return t.notices}function ot(){return nt("getNotices")}function it(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return!!t.doingActions[e]&&(""===n||t.doingActions[e].includes(n))}var ut=[];function ct(t,e){return t.doingActions[e]||ut}function st(t){return t.mutedHighlights}function at(t){return t.mutedHighlightUpdatesInFlight}var ft={notices:[],doingActions:{},mutedHighlights:{},mutedHighlightUpdatesInFlight:{}};var lt=regeneratorRuntime.mark(dt),pt={fulfill:regeneratorRuntime.mark(function t(){var e;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,R({path:"/ithemes-security/v1/admin-notices"});case 2:return e=t.sent,t.next=5,U(e);case 5:case"end":return t.stop()}},t,this)}),shouldInvalidate:function(t){return t.type===X||t.type===$}};function dt(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,R({path:"/ithemes-security/v1/admin-notices/settings"});case 2:return t=e.sent,e.next=5,G(Object(l.isEmpty)(t.muted_highlights)?{}:t.muted_highlights);case 5:case"end":return e.stop()}},lt,this)}Object(u.registerStore)("ithemes-security/admin-notices",{controls:M,actions:r,selectors:o,resolvers:i,reducer:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:ft,e=arguments.length>1?arguments[1]:void 0;switch(e.type){case B:return f()({},t,{notices:s()(e.notices)});case Y:return f()({},t,{doingActions:f()({},t.doingActions,v()({},e.noticeId,s()(t.doingActions[e.noticeId]||[]).concat([e.actionId])))});case X:case z:return f()({},t,{doingActions:f()({},t.doingActions,v()({},e.noticeId,(t.doingActions[e.noticeId]||[]).filter(function(t){return t!==e.actionId})))});case K:return f()({},t,{mutedHighlights:e.mutedHighlights});case Q:return f()({},t,{mutedHighlightUpdatesInFlight:f()({},t.mutedHighlightUpdatesInFlight,v()({},e.slug,{mute:e.mute}))});case $:return f()({},t,{mutedHighlightUpdatesInFlight:Object(l.omit)(t.mutedHighlightUpdatesInFlight,e.slug),mutedHighlights:f()({},t.mutedHighlights,v()({},e.slug,e.mute))});case tt:return f()({},t,{mutedHighlightUpdatesInFlight:Object(l.omit)(t.mutedHighlightUpdatesInFlight,e.slug)});default:return t}}});n.p=window.itsecWebpackPublicPath},MVZn:function(t,e,n){var r=n("lSNA");t.exports=function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),o.forEach(function(e){r(t,e,n[e])})}return t}},Nsbk:function(t,e){function n(e){return t.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},n(e)}t.exports=n},PJYZ:function(t,e){t.exports=function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}},RIqP:function(t,e,n){var r=n("Ijbi"),o=n("EbDI"),i=n("Bnag");t.exports=function(t){return r(t)||o(t)||i()}},SksO:function(t,e){function n(e,r){return t.exports=n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},n(e,r)}t.exports=n},W8MJ:function(t,e){function n(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}t.exports=function(t,e,r){return e&&n(t.prototype,e),r&&n(t,r),t}},YLtl:function(t,e){!function(){t.exports=this.lodash}()},a1gu:function(t,e,n){var r=n("cDf5"),o=n("PJYZ");t.exports=function(t,e){return!e||"object"!==r(e)&&"function"!=typeof e?o(t):e}},cDf5:function(t,e){function n(t){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(e){return"function"==typeof Symbol&&"symbol"===n(Symbol.iterator)?t.exports=r=function(t){return n(t)}:t.exports=r=function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":n(t)},r(e)}t.exports=r},l3Sj:function(t,e){!function(){t.exports=this.wp.i18n}()},lSNA:function(t,e){t.exports=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}},lwsE:function(t,e){t.exports=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},m0LI:function(t,e){t.exports=function(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var u,c=t[Symbol.iterator]();!(r=(u=c.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{r||null==c.return||c.return()}finally{if(o)throw i}}return n}},oShl:function(t,e,n){var r=n("Nsbk"),o=n("SksO"),i=n("xfeJ"),u=n("sXyB");function c(e){var n="function"==typeof Map?new Map:void 0;return t.exports=c=function(t){if(null===t||!i(t))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==n){if(n.has(t))return n.get(t);n.set(t,e)}function e(){return u(t,arguments,r(this).constructor)}return e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),o(e,t)},c(e)}t.exports=c},sXyB:function(t,e,n){var r=n("SksO");function o(e,n,i){return!function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}()?t.exports=o=function(t,e,n){var o=[null];o.push.apply(o,e);var i=new(Function.bind.apply(t,o));return n&&r(i,n.prototype),i}:t.exports=o=Reflect.construct,o.apply(null,arguments)}t.exports=o},tmk3:function(t,e){t.exports=function(t,e){if(!e.has(t))throw new TypeError("attempted to get private field on non-instance");return e.get(t).value}},wTVA:function(t,e){t.exports=function(t){if(Array.isArray(t))return t}},wkBT:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},xfeJ:function(t,e){t.exports=function(t){return-1!==Function.toString.call(t).indexOf("[native code]")}},ywyh:function(t,e){!function(){t.exports=this.wp.apiFetch}()}});
1
+ this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["admin-notices-api"]=function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s="M/oR")}({"1ZqX":function(t,e){!function(){t.exports=this.wp.data}()},"7W2i":function(t,e,n){var r=n("SksO");t.exports=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&r(t,e)}},"92Nh":function(t,e){t.exports=function(t,e,n){if(!e.has(t))throw new TypeError("attempted to set private field on non-instance");var r=e.get(t);if(!r.writable)throw new TypeError("attempted to set read only private field");return r.value=n,n}},Bnag:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},EbDI:function(t,e){t.exports=function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}},Ijbi:function(t,e){t.exports=function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}},J4zp:function(t,e,n){var r=n("wTVA"),o=n("m0LI"),i=n("wkBT");t.exports=function(t,e){return r(t)||o(t,e)||i()}},"M/oR":function(t,e,n){"use strict";n.r(e);var r={};n.r(r),n.d(r,"receiveNotices",function(){return E}),n.d(r,"startNoticeAction",function(){return w}),n.d(r,"finishNoticeAction",function(){return O}),n.d(r,"failedNoticeAction",function(){return T}),n.d(r,"receiveMutedHighlights",function(){return _}),n.d(r,"startUpdateMutedHighlight",function(){return x}),n.d(r,"finishUpdateMutedHighlight",function(){return A}),n.d(r,"failedUpdateMutedHighlight",function(){return S}),n.d(r,"doNoticeAction",function(){return H}),n.d(r,"updateMutedHighlight",function(){return j}),n.d(r,"RECEIVE_NOTICES",function(){return P}),n.d(r,"START_NOTICE_ACTION",function(){return N}),n.d(r,"FINISH_NOTICE_ACTION",function(){return k}),n.d(r,"FAILED_NOTICE_ACTION",function(){return M}),n.d(r,"RECEIVE_MUTED_HIGHLIGHTS",function(){return R}),n.d(r,"START_UPDATE_MUTED_HIGHLIGHT",function(){return C}),n.d(r,"FINISH_UPDATE_MUTED_HIGHLIGHT",function(){return D}),n.d(r,"FAILED_UPDATE_MUTED_HIGHLIGHT",function(){return U});var o={};n.r(o),n.d(o,"isResolving",function(){return F}),n.d(o,"isResolved",function(){return L}),n.d(o,"getNotices",function(){return G}),n.d(o,"areNoticesLoaded",function(){return J}),n.d(o,"isDoingAction",function(){return q}),n.d(o,"getInProgressActions",function(){return W}),n.d(o,"getMutedHighlights",function(){return Z}),n.d(o,"getMutedHighlightUpdatesInFlight",function(){return B});var i={};n.r(i),n.d(i,"getNotices",function(){return z}),n.d(i,"getMutedHighlights",function(){return K});var u=n("1ZqX"),c=n("RIqP"),s=n.n(c),a=n("MVZn"),f=n.n(a),l=n("YLtl"),d=n("ywyh"),p=n.n(d),h=n("Td6G");function g(t){return{type:"API_FETCH",request:t}}var y={API_FETCH:function(t){var e=t.request;return p()(e).catch(h.e)},SELECT:function(t){var e,n=t.selectorName,r=t.args;return(e=Object(u.select)("ithemes-security/admin-notices"))[n].apply(e,s()(r))},CREATE_NOTICE:function(t){var e=t.status,n=t.content,r=t.options;r.autoDismiss&&(r.id=r.id||Object(l.uniqueId)("itsec-auto-dismiss-"),setTimeout(function(){return Object(u.dispatch)("core/notices").removeNotice(r.id,r.context)},r.autoDismiss)),Object(u.dispatch)("core/notices").createNotice(e,n,r)}},v=n("lSNA"),m=n.n(v),b=regeneratorRuntime.mark(H),I=regeneratorRuntime.mark(j);function E(t){return{type:P,notices:t}}function w(t,e){return{type:N,noticeId:t,actionId:e}}function O(t,e,n){return{type:k,noticeId:t,actionId:e,response:n}}function T(t,e,n){return{type:M,noticeId:t,actionId:e,error:n}}function _(t){return{type:R,mutedHighlights:t}}function x(t,e){return{type:C,slug:t,mute:e}}function A(t,e){return{type:D,slug:t,mute:e}}function S(t,e,n){return{type:U,slug:t,mute:e,error:n}}function H(t,e){var n,r,o=arguments;return regeneratorRuntime.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:return n=o.length>2&&void 0!==o[2]?o[2]:{},i.next=3,w(t,e);case 3:return i.prev=3,i.next=6,g({path:"/ithemes-security/v1/admin-notices/".concat(t,"/").concat(e),method:"POST",data:n});case 6:r=i.sent,i.next=14;break;case 9:return i.prev=9,i.t0=i.catch(3),i.next=13,T(t,e,i.t0);case 13:return i.abrupt("return",i.t0);case 14:return i.next=16,O(t,e,r);case 16:return i.abrupt("return",r);case 17:case"end":return i.stop()}},b,this,[[3,9]])}function j(t,e){var n;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,x(t,e);case 2:return r.prev=2,r.next=5,g({path:"/ithemes-security/v1/admin-notices/settings",method:"PUT",data:{muted_highlights:m()({},t,e)}});case 5:n=r.sent,r.next=13;break;case 8:return r.prev=8,r.t0=r.catch(2),r.next=12,S(t,e,r.t0);case 12:return r.abrupt("return",r.t0);case 13:return r.next=15,A(t,e);case 15:return r.abrupt("return",n);case 16:case"end":return r.stop()}},I,this,[[2,8]])}var P="RECEIVE_NOTICES",N="START_NOTICE_ACTION",k="FINISH_NOTICE_ACTION",M="FAILED_NOTICE_ACTION",R="RECEIVE_MUTED_HIGHLIGHTS",C="START_UPDATE_MUTED_HIGHLIGHT",D="FINISH_UPDATE_MUTED_HIGHLIGHT",U="FAILED_UPDATE_MUTED_HIGHLIGHT";function F(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];return Object(u.select)("core/data").isResolving("ithemes-security/admin-notices",t,n)}function L(t){for(var e=arguments.length,n=new Array(e>1?e-1:0),r=1;r<e;r++)n[r-1]=arguments[r];return Object(u.select)("core/data").hasFinishedResolution("ithemes-security/admin-notices",t,n)}function G(t){return t.notices}function J(){return L("getNotices")}function q(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return!!t.doingActions[e]&&(""===n||t.doingActions[e].includes(n))}var V=[];function W(t,e){return t.doingActions[e]||V}function Z(t){return t.mutedHighlights}function B(t){return t.mutedHighlightUpdatesInFlight}var Y={notices:[],doingActions:{},mutedHighlights:{},mutedHighlightUpdatesInFlight:{}};var X=regeneratorRuntime.mark(K),z={fulfill:regeneratorRuntime.mark(function t(){var e;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,g({path:"/ithemes-security/v1/admin-notices"});case 2:return e=t.sent,t.next=5,E(e);case 5:case"end":return t.stop()}},t,this)}),shouldInvalidate:function(t){return t.type===k||t.type===D}};function K(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,g({path:"/ithemes-security/v1/admin-notices/settings"});case 2:return t=e.sent,e.next=5,_(Object(l.isEmpty)(t.muted_highlights)?{}:t.muted_highlights);case 5:case"end":return e.stop()}},X,this)}Object(u.registerStore)("ithemes-security/admin-notices",{controls:y,actions:r,selectors:o,resolvers:i,reducer:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Y,e=arguments.length>1?arguments[1]:void 0;switch(e.type){case P:return f()({},t,{notices:s()(e.notices)});case N:return f()({},t,{doingActions:f()({},t.doingActions,m()({},e.noticeId,s()(t.doingActions[e.noticeId]||[]).concat([e.actionId])))});case k:case M:return f()({},t,{doingActions:f()({},t.doingActions,m()({},e.noticeId,(t.doingActions[e.noticeId]||[]).filter(function(t){return t!==e.actionId})))});case R:return f()({},t,{mutedHighlights:e.mutedHighlights});case C:return f()({},t,{mutedHighlightUpdatesInFlight:f()({},t.mutedHighlightUpdatesInFlight,m()({},e.slug,{mute:e.mute}))});case D:return f()({},t,{mutedHighlightUpdatesInFlight:Object(l.omit)(t.mutedHighlightUpdatesInFlight,e.slug),mutedHighlights:f()({},t.mutedHighlights,m()({},e.slug,e.mute))});case U:return f()({},t,{mutedHighlightUpdatesInFlight:Object(l.omit)(t.mutedHighlightUpdatesInFlight,e.slug)});default:return t}}});n.p=window.itsecWebpackPublicPath},MVZn:function(t,e,n){var r=n("lSNA");t.exports=function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),o.forEach(function(e){r(t,e,n[e])})}return t}},Nsbk:function(t,e){function n(e){return t.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},n(e)}t.exports=n},PJYZ:function(t,e){t.exports=function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}},RIqP:function(t,e,n){var r=n("Ijbi"),o=n("EbDI"),i=n("Bnag");t.exports=function(t){return r(t)||o(t)||i()}},SksO:function(t,e){function n(e,r){return t.exports=n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},n(e,r)}t.exports=n},Td6G:function(t,e,n){"use strict";n("J4zp");var r=n("YLtl"),o=n("RIqP"),i=n.n(o),u=n("lwsE"),c=n.n(u),s=n("W8MJ"),a=n.n(s),f=n("lSNA"),l=n.n(f),d=n("92Nh"),p=n.n(d),h=n("tmk3"),g=n.n(h),y=function(){function t(){var e=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;c()(this,t),v.set(this,{writable:!0,value:{}}),m.set(this,{writable:!0,value:{}}),l()(this,"getErrorCodes",function(){return Object.keys(g()(e,v))}),l()(this,"getErrorCode",function(){return e.getErrorCodes()[0]}),l()(this,"getErrorMessages",function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;if(t)return g()(e,v)[t];var n=[];for(var r in g()(e,v))g()(e,v).hasOwnProperty(r)&&n.concat(g()(e,v)[r]);return n}),l()(this,"getErrorMessage",function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return t=t||e.getErrorCode(),e.getErrorMessages(t)[0]}),l()(this,"getErrorData",function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return t=t||e.getErrorCode(),g()(e,m)[t]}),l()(this,"getAllErrorMessages",function(){var t=[];for(var n in g()(e,v))g()(e,v).hasOwnProperty(n)&&t.push.apply(t,i()(g()(e,v)[n]));return t}),n&&(r&&(g()(this,v)[n]=[r]),o&&(g()(this,m)[n]=o))}return a()(t,null,[{key:"fromPHPObject",value:function(e){var n=new t;return p()(n,v,e.errors),p()(n,m,e.error_data),n}},{key:"fromApiError",value:function(e){var n=new t;if(g()(n,v)[e.code]=[e.message],g()(n,m)[e.code]=e.data,e.additional_errors){var r=!0,o=!1,i=void 0;try{for(var u,c=e.additional_errors[Symbol.iterator]();!(r=(u=c.next()).done);r=!0){var s=u.value;g()(n,v)[s.code]=[s.message],g()(n,m)[s.code]=s.data}}catch(t){o=!0,i=t}finally{try{r||null==c.return||c.return()}finally{if(o)throw i}}}return n}}]),t}(),v=new WeakMap,m=new WeakMap,b=n("a1gu"),I=n.n(b),E=n("Nsbk"),w=n.n(E),O=n("7W2i"),T=n.n(O),_=n("PJYZ"),x=n.n(_),A=n("oShl"),S=n.n(A),H=n("l3Sj"),j=function(t){function e(t){var n,r;c()(this,e);for(var o=arguments.length,i=new Array(o>1?o-1:0),u=1;u<o;u++)i[u-1]=arguments[u];for(var s in r=I()(this,(n=w()(e)).call.apply(n,[this,t.message||Object(H.__)("An unknown error occurred.","better-wp-security")].concat(i))),Error.captureStackTrace&&Error.captureStackTrace(x()(x()(r)),e),r.__response=t,t)t.hasOwnProperty(s)&&Object.defineProperty(x()(x()(r)),s,{value:t[s],configurable:!0,enumerable:!0,writable:!0});return r}return T()(e,t),a()(e,[{key:"toString",value:function(){return this.__response.toString()}},{key:"getResponse",value:function(){return this.__response}}]),e}(S()(Error));function P(t){if(!Object(r.isPlainObject)(t))return!1;var e=Object.keys(t);return 2===e.length&&(e.includes("errors")&&e.includes("error_data"))}function N(t){return P(t)?y.fromPHPObject(t):function(t){if(!Object(r.isPlainObject)(t))return!1;var e=Object.keys(t);return(3===e.length||4===e.length)&&!(4===e.length&&!e.includes("additional_errors"))&&e.includes("code")&&e.includes("message")&&e.includes("data")}(t)?y.fromApiError(t):new y}function k(t,e){var n=[[],[]],r=!0,o=!1,i=void 0;try{for(var u,c=t[Symbol.iterator]();!(r=(u=c.next()).done);r=!0){var s=u.value;n[e(s)?0:1].push(s)}}catch(t){o=!0,i=t}finally{try{r||null==c.return||c.return()}finally{if(o)throw i}}return n}function M(t){if(t instanceof Error)throw t;throw new j(t)}n.d(e,"d",function(){return P}),n.d(e,"b",function(){return N}),n.d(e,"a",function(){return k}),n.d(e,"e",function(){return M}),n.d(e,"c",function(){return R});function R(t,e){if(t&&t.links){var n=!0,r=!1,o=void 0;try{for(var i,u=t.links[Symbol.iterator]();!(n=(i=u.next()).done);n=!0){var c=i.value;if(c.rel===e)return c}}catch(t){r=!0,o=t}finally{try{n||null==u.return||u.return()}finally{if(r)throw o}}}}},W8MJ:function(t,e){function n(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}t.exports=function(t,e,r){return e&&n(t.prototype,e),r&&n(t,r),t}},YLtl:function(t,e){!function(){t.exports=this.lodash}()},a1gu:function(t,e,n){var r=n("cDf5"),o=n("PJYZ");t.exports=function(t,e){return!e||"object"!==r(e)&&"function"!=typeof e?o(t):e}},cDf5:function(t,e){function n(t){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(e){return"function"==typeof Symbol&&"symbol"===n(Symbol.iterator)?t.exports=r=function(t){return n(t)}:t.exports=r=function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":n(t)},r(e)}t.exports=r},l3Sj:function(t,e){!function(){t.exports=this.wp.i18n}()},lSNA:function(t,e){t.exports=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}},lwsE:function(t,e){t.exports=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},m0LI:function(t,e){t.exports=function(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var u,c=t[Symbol.iterator]();!(r=(u=c.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{r||null==c.return||c.return()}finally{if(o)throw i}}return n}},oShl:function(t,e,n){var r=n("Nsbk"),o=n("SksO"),i=n("xfeJ"),u=n("sXyB");function c(e){var n="function"==typeof Map?new Map:void 0;return t.exports=c=function(t){if(null===t||!i(t))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==n){if(n.has(t))return n.get(t);n.set(t,e)}function e(){return u(t,arguments,r(this).constructor)}return e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),o(e,t)},c(e)}t.exports=c},sXyB:function(t,e,n){var r=n("SksO");function o(e,n,i){return!function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}()?t.exports=o=function(t,e,n){var o=[null];o.push.apply(o,e);var i=new(Function.bind.apply(t,o));return n&&r(i,n.prototype),i}:t.exports=o=Reflect.construct,o.apply(null,arguments)}t.exports=o},tmk3:function(t,e){t.exports=function(t,e){if(!e.has(t))throw new TypeError("attempted to get private field on non-instance");return e.get(t).value}},wTVA:function(t,e){t.exports=function(t){if(Array.isArray(t))return t}},wkBT:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},xfeJ:function(t,e){t.exports=function(t){return-1!==Function.toString.call(t).indexOf("[native code]")}},ywyh:function(t,e){!function(){t.exports=this.wp.apiFetch}()}});
dist/core/admin-notices-dashboard-admin-bar.min.css CHANGED
@@ -4,7 +4,7 @@
4
 
5
  .itsec-admin-notice-list{margin:0}.itsec-admin-notice-list-item-container{display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:1em;margin-bottom:2em}.itsec-admin-notice-list-item-container:last-child{margin-bottom:0}
6
 
7
- @keyframes itsec-animation-fade-in-constant{from{opacity:0}to{opacity:1}}.itsec-admin-bar-admin-notices__content.components-popover::after{border-color:#E5EAEE !important}.itsec-admin-notice-panel{padding:1em;background:#E5EAEE;width:420px;box-sizing:border-box}.itsec-admin-notice-panel .itsec-admin-notice-panel__header{border-bottom:1px solid #7ABEED;margin-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__header h3{color:#0081E3;text-align:center}.itsec-admin-notice-panel .itsec-admin-notice-panel__header p{text-align:center;font-style:oblique}.is-mobile .itsec-admin-notice-panel{width:100%;overflow-x:auto;height:100%}.is-mobile .itsec-admin-notice-panel header h3{display:none}.is-mobile .itsec-admin-notice-panel header p{margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-icon-button{padding:5px;height:30px;position:absolute;right:1em;top:1em;transition:opacity 400ms}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-icon-button:hover{opacity:.5;box-shadow:none !important}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-icon-button{color:#0081E3}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-icon-button:hover{color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs{border-bottom:1px solid #7ABEED;margin-bottom:1em;padding-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li{display:flex;justify-content:space-between;border-bottom:1px solid #ccc;margin-bottom:1em;padding-bottom:1em;padding-top:0;margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li label{margin-left:calc(24px + 1em);font-size:14px;font-weight:bold}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle.is-checked .components-form-toggle__track{background-color:#0081E3;border-color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle__input:disabled+.components-form-toggle__track{opacity:.5}
8
 
9
- .itsec-admin-bar__admin-notices{margin-left:auto}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button{color:#0081E3}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button:hover{color:#4ab1ff}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button svg{margin-right:.5em}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger .components-button{position:relative}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger .components-button::before{content:'';background:#d8514f;height:5px;width:5px;border-radius:5px;vertical-align:middle;border:1px solid #f1f1f1;z-index:1;position:absolute;left:4px;top:3px;opacity:0;transition:opacity 1000ms ease-in-out}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger--has-notices .components-button::before{opacity:1}.itsec-admin-bar-admin-notices__content .components-popover__content{box-shadow:rgba(0,0,0,0.19) 0 4px 5px}@media screen and (max-width: 600px){.itsec-admin-bar .itsec-admin-bar__admin-notices{margin-left:0}}
10
 
4
 
5
  .itsec-admin-notice-list{margin:0}.itsec-admin-notice-list-item-container{display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:1em;margin-bottom:2em}.itsec-admin-notice-list-item-container:last-child{margin-bottom:0}
6
 
7
+ @keyframes itsec-animation-fade-in-constant{from{opacity:0}to{opacity:1}}.itsec-admin-bar-admin-notices__content.components-popover::after{border-color:#E5EAEE !important}.itsec-admin-notice-panel{padding:1em;background:#E5EAEE;width:420px;box-sizing:border-box}.itsec-admin-notice-panel .itsec-admin-notice-panel__header{border-bottom:1px solid #7ABEED;margin-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__header h3{color:#0081E3;text-align:center}.itsec-admin-notice-panel .itsec-admin-notice-panel__header p{text-align:center;font-style:oblique}.is-mobile .itsec-admin-notice-panel{width:100%;overflow-x:auto;height:100%}.is-mobile .itsec-admin-notice-panel header h3{display:none}.is-mobile .itsec-admin-notice-panel header p{margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-button{padding:5px;height:30px;position:absolute;right:1em;top:1em;transition:opacity 400ms}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-button:hover{opacity:.5;box-shadow:none !important}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-button{color:#0081E3}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-button:hover{color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs{border-bottom:1px solid #7ABEED;margin-bottom:1em;padding-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li{display:flex;justify-content:space-between;border-bottom:1px solid #ccc;margin-bottom:1em;padding-bottom:1em;padding-top:0;margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li label{margin-left:calc(24px + 1em);font-size:14px;font-weight:bold}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle.is-checked .components-form-toggle__track{background-color:#0081E3;border-color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle__input:disabled+.components-form-toggle__track{opacity:.5}
8
 
9
+ .itsec-admin-bar__admin-notices{margin-left:auto}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button{color:#0081E3;border:none;background:transparent}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button:hover{color:#4ab1ff}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger>.components-button svg{margin-right:.5em}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger .components-button{position:relative}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger .components-button::before{content:'';background:#d8514f;height:5px;width:5px;border-radius:5px;vertical-align:middle;border:1px solid #f1f1f1;z-index:1;position:absolute;left:4px;top:3px;opacity:0;transition:opacity 1000ms ease-in-out}.itsec-admin-bar__admin-notices .itsec-admin-bar-admin-notices__trigger--has-notices .components-button::before{opacity:1}.itsec-admin-bar-admin-notices__content .components-popover__content{box-shadow:rgba(0,0,0,0.19) 0 4px 5px}@media screen and (max-width: 600px){.itsec-admin-bar .itsec-admin-bar__admin-notices{margin-left:0}}
10
 
dist/core/admin-notices-dashboard-admin-bar.min.js CHANGED
@@ -1,4 +1,4 @@
1
- this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["admin-notices-dashboard-admin-bar"]=function(e){var t={};function n(i){if(t[i])return t[i].exports;var c=t[i]={i:i,l:!1,exports:{}};return e[i].call(c.exports,c,c.exports,n),c.l=!0,c.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var c in e)n.d(i,c,function(t){return e[t]}.bind(null,c));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s="Pesb")}({"1ZqX":function(e,t){!function(){e.exports=this.wp.data}()},CDPQ:function(e,t,n){},Ckse:function(e,t,n){},GRId:function(e,t){!function(){e.exports=this.wp.element}()},Hz6T:function(e,t){!function(){e.exports=this.itsec.core["admin-notices-api"]}()},K9lf:function(e,t){!function(){e.exports=this.wp.compose}()},KTI5:function(e,t,n){},Pesb:function(e,t,n){"use strict";n.r(t);var i=n("TvNi"),c=(n("Hz6T"),n("GRId")),r=n("TSYQ"),a=n.n(r),o=n("tI+e"),s=n("l3Sj"),l=n("K9lf"),u=n("1ZqX"),m=n("cruf"),d=n("vYuV"),f=n("rE5z");n("CDPQ");var b=Object(l.compose)([Object(u.withSelect)(function(e){return{notices:e("ithemes-security/admin-notices").getNotices(),noticesLoaded:e("ithemes-security/admin-notices").areNoticesLoaded()}}),Object(l.withState)({isToggled:!1})])(function(e){var t=e.notices,n=e.noticesLoaded,i=e.isToggled,r=e.setState;return Object(c.createElement)(m.AdminBarFill,null,Object(c.createElement)("div",{className:"itsec-admin-bar__admin-notices"},Object(c.createElement)("div",{className:a()("itsec-admin-bar-admin-notices__trigger",{"itsec-admin-bar-admin-notices__trigger--has-notices":t.length>0})},Object(c.createElement)(o.Button,{"aria-expanded":i,onClick:function(){return r({isToggled:!i})}},Object(c.createElement)(o.Dashicon,{icon:"megaphone",size:15}),Object(s.__)("Notifications","better-wp-security")),i&&Object(c.createElement)(o.Popover,{className:"itsec-admin-bar-admin-notices__content",expandOnMobile:!0,focusOnMount:"container",position:"bottom left",headerTitle:Object(s.__)("Notifications","better-wp-security"),onClose:function(){return r({isToggled:!1})},onClickOutside:function(e){"itsec-admin-notices-toolbar-trigger"===e.target.id||"itsec-admin-notices-toolbar-trigger"===e.target.parentNode.id||Object(d.a)(e.target)||r({isToggled:!1})}},Object(c.createElement)(f.a,{notices:t,loaded:n,close:function(){return r({isToggled:!1})}})))))});n.p=window.itsecWebpackPublicPath,Object(i.registerPlugin)("itsec-admin-notices-dashboard-admin-bar",{render:b})},TSYQ:function(e,t,n){
2
  /*!
3
  Copyright (c) 2017 Jed Watson.
4
  Licensed under the MIT License (MIT), see
1
+ this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["admin-notices-dashboard-admin-bar"]=function(e){var t={};function n(i){if(t[i])return t[i].exports;var c=t[i]={i:i,l:!1,exports:{}};return e[i].call(c.exports,c,c.exports,n),c.l=!0,c.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var c in e)n.d(i,c,function(t){return e[t]}.bind(null,c));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s="Pesb")}({"1ZqX":function(e,t){!function(){e.exports=this.wp.data}()},CDPQ:function(e,t,n){},Ckse:function(e,t,n){},GRId:function(e,t){!function(){e.exports=this.wp.element}()},Hz6T:function(e,t){!function(){e.exports=this.itsec.core["admin-notices-api"]}()},K9lf:function(e,t){!function(){e.exports=this.wp.compose}()},KTI5:function(e,t,n){},Pesb:function(e,t,n){"use strict";n.r(t);var i=n("TvNi"),c=(n("Hz6T"),n("GRId")),r=n("TSYQ"),a=n.n(r),o=n("tI+e"),s=n("l3Sj"),l=n("K9lf"),u=n("1ZqX"),m=n("cruf"),d=n("vYuV"),f=n("rE5z");n("CDPQ");var b=Object(l.compose)([Object(u.withSelect)(function(e){return{notices:e("ithemes-security/admin-notices").getNotices(),noticesLoaded:e("ithemes-security/admin-notices").areNoticesLoaded()}}),Object(l.withState)({isToggled:!1})])(function(e){var t=e.notices,n=e.noticesLoaded,i=e.isToggled,r=e.setState;return Object(c.createElement)(m.AdminBarFill,null,Object(c.createElement)("div",{className:"itsec-admin-bar__admin-notices"},Object(c.createElement)("div",{className:a()("itsec-admin-bar-admin-notices__trigger",{"itsec-admin-bar-admin-notices__trigger--has-notices":t.length>0})},Object(c.createElement)(o.Button,{"aria-expanded":i,onClick:function(){return r({isToggled:!i})},isSecondary:!0},Object(c.createElement)(o.Dashicon,{icon:"megaphone",size:15}),Object(s.__)("Notifications","better-wp-security")),i&&Object(c.createElement)(o.Popover,{className:"itsec-admin-bar-admin-notices__content",expandOnMobile:!0,focusOnMount:"container",position:"bottom left",headerTitle:Object(s.__)("Notifications","better-wp-security"),onClose:function(){return r({isToggled:!1})},onClickOutside:function(e){e.target&&("itsec-admin-notices-toolbar-trigger"===e.target.id||"itsec-admin-notices-toolbar-trigger"===e.target.parentNode.id||Object(d.a)(e.target))||r({isToggled:!1})},onFocusOutside:function(){var e=document.activeElement;"itsec-admin-notices-toolbar-trigger"===e.id||e.parentNode&&"itsec-admin-notices-toolbar-trigger"===e.parentNode.id||Object(d.a)(e)||r({isToggled:!1})}},Object(c.createElement)(f.a,{notices:t,loaded:n,close:function(){return r({isToggled:!1})}})))))});n.p=window.itsecWebpackPublicPath,Object(i.registerPlugin)("itsec-admin-notices-dashboard-admin-bar",{render:b})},TSYQ:function(e,t,n){
2
  /*!
3
  Copyright (c) 2017 Jed Watson.
4
  Licensed under the MIT License (MIT), see
dist/core/admin-notices.min.css CHANGED
@@ -4,7 +4,7 @@
4
 
5
  .itsec-admin-notice-list{margin:0}.itsec-admin-notice-list-item-container{display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:1em;margin-bottom:2em}.itsec-admin-notice-list-item-container:last-child{margin-bottom:0}
6
 
7
- @keyframes itsec-animation-fade-in-constant{from{opacity:0}to{opacity:1}}.itsec-admin-bar-admin-notices__content.components-popover::after{border-color:#E5EAEE !important}.itsec-admin-notice-panel{padding:1em;background:#E5EAEE;width:420px;box-sizing:border-box}.itsec-admin-notice-panel .itsec-admin-notice-panel__header{border-bottom:1px solid #7ABEED;margin-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__header h3{color:#0081E3;text-align:center}.itsec-admin-notice-panel .itsec-admin-notice-panel__header p{text-align:center;font-style:oblique}.is-mobile .itsec-admin-notice-panel{width:100%;overflow-x:auto;height:100%}.is-mobile .itsec-admin-notice-panel header h3{display:none}.is-mobile .itsec-admin-notice-panel header p{margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-icon-button{padding:5px;height:30px;position:absolute;right:1em;top:1em;transition:opacity 400ms}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-icon-button:hover{opacity:.5;box-shadow:none !important}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-icon-button{color:#0081E3}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-icon-button:hover{color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs{border-bottom:1px solid #7ABEED;margin-bottom:1em;padding-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li{display:flex;justify-content:space-between;border-bottom:1px solid #ccc;margin-bottom:1em;padding-bottom:1em;padding-top:0;margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li label{margin-left:calc(24px + 1em);font-size:14px;font-weight:bold}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle.is-checked .components-form-toggle__track{background-color:#0081E3;border-color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle__input:disabled+.components-form-toggle__track{opacity:.5}
8
 
9
  #wpadminbar #wp-admin-bar-itsec_admin_bar_menu .ab-sub-wrapper{background:transparent;box-shadow:none}#wpadminbar .itsec-admin-notices-toolbar-bubble{display:inline-block;vertical-align:top;margin:8px 0 0 5px;padding:0 5px;min-width:7px;height:17px;border-radius:11px;font-size:9px;line-height:17px;text-align:center;z-index:26;background-color:#ca4a1f;color:#fff;animation:itsec-admin-notices-toolbar-bubble-fade-in 400ms}#wpadminbar .itsec-admin-notices-toolbar-bubble .itsec-admin-notices-toolbar-bubble__count{line-height:17px;font-size:9px}@keyframes itsec-admin-notices-toolbar-bubble-fade-in{from{opacity:0}to{opacity:1}}#itsec-admin-notices-root .itsec-admin-notice-panel{border:1px solid #e2e4e7;box-shadow:0 3px 30px rgba(25,30,35,0.1)}#itsec-admin-notices-root .components-popover:not(.is-mobile).is-bottom{z-index:99999}#itsec-admin-notices-root .itsec-admin-notices-toolbar__popover .components-popover__content{box-shadow:rgba(0,0,0,0.19) 0 4px 5px}#wp-admin-bar-itsec_admin_bar_menu .components-button{cursor:pointer}#wp-admin-bar-itsec_admin_bar_menu .it-icon-itsec{display:none;margin-top:8px;color:rgba(240,245,250,0.6);height:20px;width:52px;line-height:32px;font-size:32px;text-align:center}@media screen and (max-width: 782px){#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu{display:list-item}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .it-icon-itsec{display:inline-block}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-toolbar-text{display:none}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar-bubble{display:none}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar--has-notices{position:relative}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar--has-notices::before{content:'';background:#d8514f;height:5px;width:5px;border-radius:5px;vertical-align:middle;border:1px solid #23282d;z-index:1;position:absolute;left:4px;top:3px;animation:itsec-admin-notices-toolbar-bubble-fade-in 400ms}}
10
 
4
 
5
  .itsec-admin-notice-list{margin:0}.itsec-admin-notice-list-item-container{display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:1em;margin-bottom:2em}.itsec-admin-notice-list-item-container:last-child{margin-bottom:0}
6
 
7
+ @keyframes itsec-animation-fade-in-constant{from{opacity:0}to{opacity:1}}.itsec-admin-bar-admin-notices__content.components-popover::after{border-color:#E5EAEE !important}.itsec-admin-notice-panel{padding:1em;background:#E5EAEE;width:420px;box-sizing:border-box}.itsec-admin-notice-panel .itsec-admin-notice-panel__header{border-bottom:1px solid #7ABEED;margin-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__header h3{color:#0081E3;text-align:center}.itsec-admin-notice-panel .itsec-admin-notice-panel__header p{text-align:center;font-style:oblique}.is-mobile .itsec-admin-notice-panel{width:100%;overflow-x:auto;height:100%}.is-mobile .itsec-admin-notice-panel header h3{display:none}.is-mobile .itsec-admin-notice-panel header p{margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-button{padding:5px;height:30px;position:absolute;right:1em;top:1em;transition:opacity 400ms}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-trigger.components-button:hover{opacity:.5;box-shadow:none !important}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-button{color:#0081E3}.itsec-admin-notice-panel.itsec-admin-notice-panel--is-configuring .itsec-admin-notice-panel__configure-trigger.components-button:hover{color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs{border-bottom:1px solid #7ABEED;margin-bottom:1em;padding-bottom:1em}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li{display:flex;justify-content:space-between;border-bottom:1px solid #ccc;margin-bottom:1em;padding-bottom:1em;padding-top:0;margin-top:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li label{margin-left:calc(24px + 1em);font-size:14px;font-weight:bold}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle.is-checked .components-form-toggle__track{background-color:#0081E3;border-color:#0081E3}.itsec-admin-notice-panel .itsec-admin-notice-panel__configure-highlighted-logs li .components-form-toggle__input:disabled+.components-form-toggle__track{opacity:.5}
8
 
9
  #wpadminbar #wp-admin-bar-itsec_admin_bar_menu .ab-sub-wrapper{background:transparent;box-shadow:none}#wpadminbar .itsec-admin-notices-toolbar-bubble{display:inline-block;vertical-align:top;margin:8px 0 0 5px;padding:0 5px;min-width:7px;height:17px;border-radius:11px;font-size:9px;line-height:17px;text-align:center;z-index:26;background-color:#ca4a1f;color:#fff;animation:itsec-admin-notices-toolbar-bubble-fade-in 400ms}#wpadminbar .itsec-admin-notices-toolbar-bubble .itsec-admin-notices-toolbar-bubble__count{line-height:17px;font-size:9px}@keyframes itsec-admin-notices-toolbar-bubble-fade-in{from{opacity:0}to{opacity:1}}#itsec-admin-notices-root .itsec-admin-notice-panel{border:1px solid #e2e4e7;box-shadow:0 3px 30px rgba(25,30,35,0.1)}#itsec-admin-notices-root .components-popover:not(.is-mobile).is-bottom{z-index:99999}#itsec-admin-notices-root .itsec-admin-notices-toolbar__popover .components-popover__content{box-shadow:rgba(0,0,0,0.19) 0 4px 5px}#wp-admin-bar-itsec_admin_bar_menu .components-button{cursor:pointer}#wp-admin-bar-itsec_admin_bar_menu .it-icon-itsec{display:none;margin-top:8px;color:rgba(240,245,250,0.6);height:20px;width:52px;line-height:32px;font-size:32px;text-align:center}@media screen and (max-width: 782px){#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu{display:list-item}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .it-icon-itsec{display:inline-block}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-toolbar-text{display:none}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar-bubble{display:none}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar--has-notices{position:relative}#wp-toolbar>ul>li#wp-admin-bar-itsec_admin_bar_menu .itsec-admin-notices-toolbar--has-notices::before{content:'';background:#d8514f;height:5px;width:5px;border-radius:5px;vertical-align:middle;border:1px solid #23282d;z-index:1;position:absolute;left:4px;top:3px;animation:itsec-admin-notices-toolbar-bubble-fade-in 400ms}}
10
 
dist/core/admin-notices.min.js CHANGED
@@ -4,4 +4,4 @@ this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["a
4
  Licensed under the MIT License (MIT), see
5
  http://jedwatson.github.io/classnames
6
  */
7
- !function(){"use strict";var t={}.hasOwnProperty;function n(){for(var e=[],i=0;i<arguments.length;i++){var c=arguments[i];if(c){var r=typeof c;if("string"===r||"number"===r)e.push(c);else if(Array.isArray(c)&&c.length){var a=n.apply(null,c);a&&e.push(a)}else if("object"===r)for(var o in c)t.call(c,o)&&c[o]&&e.push(o)}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return n}):window.classNames=n}()},UuzZ:function(e,t){!function(){e.exports=this.wp.autop}()},VwaH:function(e,t,n){},Y8OO:function(e,t){!function(){e.exports=this.wp.domReady}()},YLtl:function(e,t){!function(){e.exports=this.lodash}()},dwGD:function(e,t,n){"use strict";n.r(t);var i=n("GRId"),c=n("l3Sj"),r=n("Y8OO"),a=n.n(r),o=n("tI+e"),s=(n("Hz6T"),n("TSYQ")),l=n.n(s),u=n("K9lf"),m=n("vYuV"),d=n("rE5z"),b=(n("6IbE"),n("1ZqX"));var f=Object(u.compose)([Object(b.withSelect)(function(e){return{notices:e("ithemes-security/admin-notices").getNotices(),noticesLoaded:e("ithemes-security/admin-notices").areNoticesLoaded()}}),Object(u.withState)({isToggled:!1})])(function(e){var t=e.notices,n=e.noticesLoaded,r=e.isToggled,a=e.setState;return Object(i.createElement)(i.Fragment,null,Object(i.createElement)(o.Button,{id:"itsec-admin-notices-toolbar-trigger",className:l()("ab-item ab-empty-item",{"itsec-admin-notices-toolbar--has-notices":t.length>0}),onClick:function(){return a({isToggled:!r})},"aria-expanded":r},Object(i.createElement)("span",{className:"it-icon-itsec"}),Object(i.createElement)("span",{className:"itsec-toolbar-text"},Object(c.__)("Security","better-wp-security")),t.length>0&&Object(i.createElement)("span",{className:"itsec-admin-notices-toolbar-bubble"},Object(i.createElement)("span",{className:"itsec-admin-notices-toolbar-bubble__count"},t.length))),r&&Object(i.createElement)(o.Popover,{className:"itsec-admin-notices-toolbar__popover",noArrow:!0,expandOnMobile:!0,focusOnMount:"container",position:"bottom center",headerTitle:Object(c.__)("Security","better-wp-security"),onClose:function(){return a({isToggled:!1})},onClickOutside:function(e){"itsec-admin-notices-toolbar-trigger"===e.target.id||"itsec-admin-notices-toolbar-trigger"===e.target.parentNode.id||Object(m.a)(e.target)||a({isToggled:!1})}},Object(i.createElement)(d.a,{notices:t,loaded:n,close:function(){return a({isToggled:!1})}})))});n("1Zi1");var p=function(e){var t=e.portalEl;return Object(i.createElement)(o.SlotFillProvider,null,Object(i.createPortal)(Object(i.createElement)(o.Popover.Slot,null),t),Object(i.createElement)(f,null))};n.p=window.itsecWebpackPublicPath,Object(c.setLocaleData)({"":{}},"better-wp-security"),a()(function(){var e=document.getElementById("wp-admin-bar-itsec_admin_bar_menu"),t=document.getElementById("itsec-admin-notices-root");return Object(i.render)(Object(i.createElement)(p,{portalEl:t}),e)})},l3Sj:function(e,t){!function(){e.exports=this.wp.i18n}()},"o/Pk":function(e,t,n){},rE5z:function(e,t,n){"use strict";var i=n("GRId"),c=n("TSYQ"),r=n.n(c),a=n("YLtl"),o=n("l3Sj"),s=n("tI+e"),l=n("K9lf"),u=n("1ZqX"),m=n("UuzZ");n("VwaH");function d(e){var t=e.notice;return Object(i.createElement)("article",{className:"itsec-admin-notice itsec-admin-notice--severity-".concat(t.severity)},Object(i.createElement)("header",{className:"itsec-admin-notice__header"},Object(i.createElement)("div",{className:"itsec-admin-notice__header-inset"},Object(i.createElement)("h4",{dangerouslySetInnerHTML:{__html:t.title||b(t.message,t)}}),Object(a.map)(t.actions,function(e,t){return"primary"===e.style&&Object(i.createElement)(s.Button,{key:t,href:e.uri},e.title)}))),t.title&&t.message&&Object(i.createElement)("section",{className:"itsec-admin-notice__message",dangerouslySetInnerHTML:{__html:Object(m.autop)(b(t.message,t))}}),function(e){if(Object(a.isEmpty)(e.meta))return!1;if(1===Object(a.size)(e.meta)&&e.meta.hasOwnProperty("created_at"))return!1;return!0}(t)&&Object(i.createElement)("dl",{className:"itsec-admin-notice__meta"},Object(a.map)(t.meta,function(e,t){return"created_at"!==t&&Object(i.createElement)(i.Fragment,{key:t},Object(i.createElement)("dt",null,e.label),Object(i.createElement)("dd",null,e.formatted))})),t.meta.created_at&&Object(i.createElement)("footer",{className:"itsec-admin-notice__footer"},Object(i.createElement)("time",{dateTime:t.meta.created_at.value},t.meta.created_at.formatted)))}function b(e,t){for(var n in t.actions)t.actions.hasOwnProperty(n)&&""!==t.actions[n].uri&&(e=e.replace("{{ $"+n+" }}",t.actions[n].uri));return e}n("o/Pk");var f=Object(l.compose)([Object(u.withDispatch)(function(e){return{doAction:e("ithemes-security/admin-notices").doNoticeAction}}),Object(u.withSelect)(function(e,t){return{inProgress:e("ithemes-security/admin-notices").getInProgressActions(t.notice.id)}})])(function(e){var t=e.notice,n=e.doAction,c=e.inProgress,r=[],l=function(e){if(!t.actions.hasOwnProperty(e))return"continue";var a=t.actions[e];"close"===a.style&&r.push(Object(i.createElement)("li",{key:e},Object(i.createElement)(s.IconButton,{icon:"dismiss",label:a.title,onClick:function(){return n(t.id,e)},isBusy:c.includes(e)})))};for(var u in t.actions)l(u);var m=function(e){var t={};for(var n in e.actions)if(e.actions.hasOwnProperty(n)){var i=e.actions[n];"close"!==i.style&&"primary"!==i.style&&(t[n]=i)}return t}(t);return Object(a.isEmpty)(m)||r.push(Object(i.createElement)("li",{key:"more"},Object(i.createElement)(s.Dropdown,{position:"bottom right",className:"itsec-admin-notice-list-actions__more-menu",contentClassName:"itsec-admin-notice-list-actions__more-menu-items",renderToggle:function(e){var t=e.isOpen,n=e.onToggle;return Object(i.createElement)(s.IconButton,{icon:"ellipsis",label:Object(o.__)("More Actions","better-wp-security"),onClick:n,"aria-haspopup":!0,"aria-expanded":t})},renderContent:function(){return Object(i.createElement)(s.NavigableMenu,{role:"menu"},Object(a.map)(m,function(e,r){return e.uri?Object(i.createElement)(s.Button,{key:r,href:e.uri},e.title):Object(i.createElement)(s.Button,{key:r,onClick:function(){return n(t.id,r)},disabled:c.includes(r)},e.title,c.includes(r)&&Object(i.createElement)(s.Spinner,null))}))}}))),Object(i.createElement)("ul",{className:"itsec-admin-notice-list-actions"},r)});n("Ckse");var p=function(e){var t=e.notices;return Object(i.createElement)("ul",{className:"itsec-admin-notice-list"},t.map(function(e){return Object(i.createElement)("li",{className:"itsec-admin-notice-list-item-container",key:e.id},Object(i.createElement)(f,{notice:e}),Object(i.createElement)(d,{notice:e,noticeId:e.id}))}))};n("KTI5");t.a=Object(l.compose)([Object(l.withState)({isConfiguring:!1,checked:{}}),Object(u.withSelect)(function(e){return{mutedHighlights:e("ithemes-security/admin-notices").getMutedHighlights(),mutedHighlightUpdatesInFlight:e("ithemes-security/admin-notices").getMutedHighlightUpdatesInFlight()}}),Object(u.withDispatch)(function(e){return{updateMutedHighlight:e("ithemes-security/admin-notices").updateMutedHighlight}})])(function(e){var t=e.notices,n=e.loaded,c=e.mutedHighlights,l=e.mutedHighlightUpdatesInFlight,u=e.updateMutedHighlight,m=e.isConfiguring,d=e.setState;return Object(i.createElement)("div",{className:r()("itsec-admin-notice-panel",{"itsec-admin-notice-panel--is-configuring":m})},Object(i.createElement)(s.IconButton,{icon:"admin-generic",label:Object(o.__)("Configure","better-wp-security"),className:"itsec-admin-notice-panel__configure-trigger",style:{opacity:Object(a.size)(c)>0?1:0},onClick:function(){return d({isConfiguring:!m})}}),Object(i.createElement)("header",{className:"itsec-admin-notice-panel__header"},Object(i.createElement)("h3",null,Object(o.__)("Security Admin Messages","better-wp-security")),Object(i.createElement)("p",null,Object(o.__)("Important notices from iThemes Security","better-wp-security"))),m&&Object(i.createElement)("ul",{className:"itsec-admin-notice-panel__configure-highlighted-logs"},[{slug:"file-change-report",label:Object(o.__)("File Change Report","better-wp-security")},{slug:"notification-center-send-failed",label:Object(o.__)("Notification Center Errors","better-wp-security")},{slug:"malware-scan-report",label:Object(o.__)("Malware Scan Report","better-wp-security")},{slug:"malware-scan-failed",label:Object(o.__)("Malware Scan Failed","better-wp-security")}].map(function(e){var t=e.slug,r=e.label;return void 0!==c[t]&&Object(i.createElement)("li",null,Object(i.createElement)("label",{htmlFor:"itsec-mute-highlight-".concat(t)},r),Object(i.createElement)(s.FormToggle,{id:"itsec-mute-highlight-".concat(t),disabled:!n||l[t],checked:!Object(a.get)(l,[t,"mute"],c[t]),onChange:function(){return u(t,!c[t])}}))})),t.length>0?Object(i.createElement)(p,{notices:t}):n&&Object(i.createElement)("span",null,Object(o.__)("No notices at the moment.","better-wp-security")))})},"tI+e":function(e,t){!function(){e.exports=this.wp.components}()},vYuV:function(e,t,n){"use strict";function i(e){for(var t=e.parentNode;null!==t;){if(t.classList&&t.classList.contains("itsec-admin-notice-list-actions__more-menu-items"))return!0;t=t.parentNode}return!1}n.d(t,"a",function(){return i})}});
4
  Licensed under the MIT License (MIT), see
5
  http://jedwatson.github.io/classnames
6
  */
7
+ !function(){"use strict";var t={}.hasOwnProperty;function n(){for(var e=[],i=0;i<arguments.length;i++){var c=arguments[i];if(c){var r=typeof c;if("string"===r||"number"===r)e.push(c);else if(Array.isArray(c)&&c.length){var o=n.apply(null,c);o&&e.push(o)}else if("object"===r)for(var a in c)t.call(c,a)&&c[a]&&e.push(a)}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return n}):window.classNames=n}()},UuzZ:function(e,t){!function(){e.exports=this.wp.autop}()},VwaH:function(e,t,n){},Y8OO:function(e,t){!function(){e.exports=this.wp.domReady}()},YLtl:function(e,t){!function(){e.exports=this.lodash}()},dwGD:function(e,t,n){"use strict";n.r(t);var i=n("GRId"),c=n("l3Sj"),r=n("Y8OO"),o=n.n(r),a=n("tI+e"),s=(n("Hz6T"),n("TSYQ")),l=n.n(s),u=n("K9lf"),m=n("vYuV"),d=n("rE5z"),b=(n("6IbE"),n("1ZqX"));var f=Object(u.compose)([Object(b.withSelect)(function(e){return{notices:e("ithemes-security/admin-notices").getNotices(),noticesLoaded:e("ithemes-security/admin-notices").areNoticesLoaded()}}),Object(u.withState)({isToggled:!1})])(function(e){var t=e.notices,n=e.noticesLoaded,r=e.isToggled,o=e.setState;return Object(i.createElement)(i.Fragment,null,Object(i.createElement)(a.Button,{id:"itsec-admin-notices-toolbar-trigger",className:l()("ab-item ab-empty-item",{"itsec-admin-notices-toolbar--has-notices":t.length>0}),onClick:function(){return o({isToggled:!r})},"aria-expanded":r},Object(i.createElement)("span",{className:"it-icon-itsec"}),Object(i.createElement)("span",{className:"itsec-toolbar-text"},Object(c.__)("Security","better-wp-security")),t.length>0&&Object(i.createElement)("span",{className:"itsec-admin-notices-toolbar-bubble"},Object(i.createElement)("span",{className:"itsec-admin-notices-toolbar-bubble__count"},t.length))),r&&Object(i.createElement)(a.Popover,{className:"itsec-admin-notices-toolbar__popover",noArrow:!0,expandOnMobile:!0,focusOnMount:"container",position:"bottom center",headerTitle:Object(c.__)("Security","better-wp-security"),onClose:function(){return o({isToggled:!1})},onClickOutside:function(e){e.target&&("itsec-admin-notices-toolbar-trigger"===e.target.id||"itsec-admin-notices-toolbar-trigger"===e.target.parentNode.id||Object(m.a)(e.target))||o({isToggled:!1})},onFocusOutside:function(){var e=document.activeElement;"itsec-admin-notices-toolbar-trigger"===e.id||e.parentNode&&"itsec-admin-notices-toolbar-trigger"===e.parentNode.id||Object(m.a)(e)||o({isToggled:!1})}},Object(i.createElement)(d.a,{notices:t,loaded:n,close:function(){return o({isToggled:!1})}})))});n("1Zi1");var p=function(e){var t=e.portalEl;return Object(i.createElement)(a.SlotFillProvider,null,Object(i.createPortal)(Object(i.createElement)(a.Popover.Slot,null),t),Object(i.createElement)(f,null))};n.p=window.itsecWebpackPublicPath,Object(c.setLocaleData)({"":{}},"better-wp-security"),o()(function(){var e=document.getElementById("wp-admin-bar-itsec_admin_bar_menu"),t=document.getElementById("itsec-admin-notices-root");return Object(i.render)(Object(i.createElement)(p,{portalEl:t}),e)})},l3Sj:function(e,t){!function(){e.exports=this.wp.i18n}()},"o/Pk":function(e,t,n){},rE5z:function(e,t,n){"use strict";var i=n("GRId"),c=n("TSYQ"),r=n.n(c),o=n("YLtl"),a=n("l3Sj"),s=n("tI+e"),l=n("K9lf"),u=n("1ZqX"),m=n("UuzZ");n("VwaH");function d(e){var t=e.notice;return Object(i.createElement)("article",{className:"itsec-admin-notice itsec-admin-notice--severity-".concat(t.severity)},Object(i.createElement)("header",{className:"itsec-admin-notice__header"},Object(i.createElement)("div",{className:"itsec-admin-notice__header-inset"},Object(i.createElement)("h4",{dangerouslySetInnerHTML:{__html:t.title||b(t.message,t)}}),Object(o.map)(t.actions,function(e,t){return"primary"===e.style&&Object(i.createElement)(s.Button,{key:t,href:e.uri},e.title)}))),t.title&&t.message&&Object(i.createElement)("section",{className:"itsec-admin-notice__message",dangerouslySetInnerHTML:{__html:Object(m.autop)(b(t.message,t))}}),function(e){if(Object(o.isEmpty)(e.meta))return!1;if(1===Object(o.size)(e.meta)&&e.meta.hasOwnProperty("created_at"))return!1;return!0}(t)&&Object(i.createElement)("dl",{className:"itsec-admin-notice__meta"},Object(o.map)(t.meta,function(e,t){return"created_at"!==t&&Object(i.createElement)(i.Fragment,{key:t},Object(i.createElement)("dt",null,e.label),Object(i.createElement)("dd",null,e.formatted))})),t.meta.created_at&&Object(i.createElement)("footer",{className:"itsec-admin-notice__footer"},Object(i.createElement)("time",{dateTime:t.meta.created_at.value},t.meta.created_at.formatted)))}function b(e,t){for(var n in t.actions)t.actions.hasOwnProperty(n)&&""!==t.actions[n].uri&&(e=e.replace("{{ $"+n+" }}",t.actions[n].uri));return e}n("o/Pk");var f=Object(l.compose)([Object(u.withDispatch)(function(e){return{doAction:e("ithemes-security/admin-notices").doNoticeAction}}),Object(u.withSelect)(function(e,t){return{inProgress:e("ithemes-security/admin-notices").getInProgressActions(t.notice.id)}})])(function(e){var t=e.notice,n=e.doAction,c=e.inProgress,r=[],l=function(e){if(!t.actions.hasOwnProperty(e))return"continue";var o=t.actions[e];"close"===o.style&&r.push(Object(i.createElement)("li",{key:e},Object(i.createElement)(s.IconButton,{icon:"dismiss",label:o.title,onClick:function(){return n(t.id,e)},isBusy:c.includes(e)})))};for(var u in t.actions)l(u);var m=function(e){var t={};for(var n in e.actions)if(e.actions.hasOwnProperty(n)){var i=e.actions[n];"close"!==i.style&&"primary"!==i.style&&(t[n]=i)}return t}(t);return Object(o.isEmpty)(m)||r.push(Object(i.createElement)("li",{key:"more"},Object(i.createElement)(s.Dropdown,{position:"bottom right",className:"itsec-admin-notice-list-actions__more-menu",contentClassName:"itsec-admin-notice-list-actions__more-menu-items",renderToggle:function(e){var t=e.isOpen,n=e.onToggle;return Object(i.createElement)(s.IconButton,{icon:"ellipsis",label:Object(a.__)("More Actions","better-wp-security"),onClick:n,"aria-haspopup":!0,"aria-expanded":t})},renderContent:function(){return Object(i.createElement)(s.NavigableMenu,{role:"menu"},Object(o.map)(m,function(e,r){return e.uri?Object(i.createElement)(s.Button,{key:r,href:e.uri},e.title):Object(i.createElement)(s.Button,{key:r,onClick:function(){return n(t.id,r)},disabled:c.includes(r)},e.title,c.includes(r)&&Object(i.createElement)(s.Spinner,null))}))}}))),Object(i.createElement)("ul",{className:"itsec-admin-notice-list-actions"},r)});n("Ckse");var p=function(e){var t=e.notices;return Object(i.createElement)("ul",{className:"itsec-admin-notice-list"},t.map(function(e){return Object(i.createElement)("li",{className:"itsec-admin-notice-list-item-container",key:e.id},Object(i.createElement)(f,{notice:e}),Object(i.createElement)(d,{notice:e,noticeId:e.id}))}))};n("KTI5");t.a=Object(l.compose)([Object(l.withState)({isConfiguring:!1,checked:{}}),Object(u.withSelect)(function(e){return{mutedHighlights:e("ithemes-security/admin-notices").getMutedHighlights(),mutedHighlightUpdatesInFlight:e("ithemes-security/admin-notices").getMutedHighlightUpdatesInFlight()}}),Object(u.withDispatch)(function(e){return{updateMutedHighlight:e("ithemes-security/admin-notices").updateMutedHighlight}})])(function(e){var t=e.notices,n=e.loaded,c=e.mutedHighlights,l=e.mutedHighlightUpdatesInFlight,u=e.updateMutedHighlight,m=e.isConfiguring,d=e.setState;return Object(i.createElement)("div",{className:r()("itsec-admin-notice-panel",{"itsec-admin-notice-panel--is-configuring":m})},Object(i.createElement)(s.IconButton,{icon:"admin-generic",label:Object(a.__)("Configure","better-wp-security"),className:"itsec-admin-notice-panel__configure-trigger",style:{opacity:Object(o.size)(c)>0?1:0},onClick:function(){return d({isConfiguring:!m})}}),Object(i.createElement)("header",{className:"itsec-admin-notice-panel__header"},Object(i.createElement)("h3",null,Object(a.__)("Security Admin Messages","better-wp-security")),Object(i.createElement)("p",null,Object(a.__)("Important notices from iThemes Security","better-wp-security"))),m&&Object(i.createElement)("ul",{className:"itsec-admin-notice-panel__configure-highlighted-logs"},[{slug:"file-change-report",label:Object(a.__)("File Change Report","better-wp-security")},{slug:"notification-center-send-failed",label:Object(a.__)("Notification Center Errors","better-wp-security")},{slug:"malware-scan-report",label:Object(a.__)("Malware Scan Report","better-wp-security")},{slug:"malware-scan-failed",label:Object(a.__)("Malware Scan Failed","better-wp-security")}].map(function(e){var t=e.slug,r=e.label;return void 0!==c[t]&&Object(i.createElement)("li",null,Object(i.createElement)("label",{htmlFor:"itsec-mute-highlight-".concat(t)},r),Object(i.createElement)(s.FormToggle,{id:"itsec-mute-highlight-".concat(t),disabled:!n||l[t],checked:!Object(o.get)(l,[t,"mute"],c[t]),onChange:function(){return u(t,!c[t])}}))})),t.length>0?Object(i.createElement)(p,{notices:t}):n&&Object(i.createElement)("span",null,Object(a.__)("No notices at the moment.","better-wp-security")))})},"tI+e":function(e,t){!function(){e.exports=this.wp.components}()},vYuV:function(e,t,n){"use strict";function i(e){for(var t=e.parentNode;null!==t;){if(t.classList&&t.classList.contains("itsec-admin-notice-list-actions__more-menu-items"))return!0;t=t.parentNode}return!1}n.d(t,"a",function(){return i})}});
dist/manifest.php CHANGED
@@ -7,11 +7,11 @@
7
  0 => 'core/admin-notices.css',
8
  1 => 'core/admin-notices.js',
9
  ),
10
- 'hash' => '9d0cac63105d6f0bcc30d06f969d8be7',
11
  'contentHash' =>
12
  array (
13
- 'css/mini-extract' => 'd5d74d576a086d437a59',
14
- 'javascript' => '78a2c380e8a93d8e10f7',
15
  ),
16
  'vendors' =>
17
  array (
@@ -36,11 +36,11 @@
36
  array (
37
  0 => 'core/admin-notices-api.js',
38
  ),
39
- 'hash' => '4830a04fca439f907049c335efb143f9',
40
  'contentHash' =>
41
  array (
42
  'css/mini-extract' => '31d6cfe0d16ae931b73c',
43
- 'javascript' => 'f64242c0f2acc73e4f26',
44
  ),
45
  'vendors' =>
46
  array (
@@ -61,11 +61,11 @@
61
  0 => 'core/admin-notices-dashboard-admin-bar.css',
62
  1 => 'core/admin-notices-dashboard-admin-bar.js',
63
  ),
64
- 'hash' => 'a95fc2fb1e30fed9c7938f08c12b6d7b',
65
  'contentHash' =>
66
  array (
67
- 'css/mini-extract' => '732388055deb8942488c',
68
- 'javascript' => 'b6d1b12859cd32d64740',
69
  ),
70
  'vendors' =>
71
  array (
@@ -105,4 +105,106 @@
105
  array (
106
  ),
107
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  );
7
  0 => 'core/admin-notices.css',
8
  1 => 'core/admin-notices.js',
9
  ),
10
+ 'hash' => '44625005dbb4e6a5d67ec06d66bb6318',
11
  'contentHash' =>
12
  array (
13
+ 'css/mini-extract' => '6fb57999347c20cedea4',
14
+ 'javascript' => 'b7be7e1b3d28502c2bbb',
15
  ),
16
  'vendors' =>
17
  array (
36
  array (
37
  0 => 'core/admin-notices-api.js',
38
  ),
39
+ 'hash' => '2c3247aed3d49189aa60feffcad7abed',
40
  'contentHash' =>
41
  array (
42
  'css/mini-extract' => '31d6cfe0d16ae931b73c',
43
+ 'javascript' => '1e901721411a447176e1',
44
  ),
45
  'vendors' =>
46
  array (
61
  0 => 'core/admin-notices-dashboard-admin-bar.css',
62
  1 => 'core/admin-notices-dashboard-admin-bar.js',
63
  ),
64
+ 'hash' => '0cd8b080ffd0f85953652b206e7440c1',
65
  'contentHash' =>
66
  array (
67
+ 'css/mini-extract' => 'fb0322849abdb99593d3',
68
+ 'javascript' => 'd0732cd3721b6c937acc',
69
  ),
70
  'vendors' =>
71
  array (
105
  array (
106
  ),
107
  ),
108
+ 'packages/preload' =>
109
+ array (
110
+ 'runtime' => true,
111
+ 'files' =>
112
+ array (
113
+ 0 => 'packages/preload.js',
114
+ ),
115
+ 'hash' => '2c905e0398d2196dc4279540a39e10a8',
116
+ 'contentHash' =>
117
+ array (
118
+ 'css/mini-extract' => '31d6cfe0d16ae931b73c',
119
+ 'javascript' => '53821da2f6c3984fa5e1',
120
+ ),
121
+ 'vendors' =>
122
+ array (
123
+ ),
124
+ 'dependencies' =>
125
+ array (
126
+ ),
127
+ ),
128
+ 'user-groups/api' =>
129
+ array (
130
+ 'runtime' => true,
131
+ 'files' =>
132
+ array (
133
+ 0 => 'user-groups/api.js',
134
+ ),
135
+ 'hash' => 'c9198440d6d93418b23f8fc09c747a38',
136
+ 'contentHash' =>
137
+ array (
138
+ 'css/mini-extract' => '31d6cfe0d16ae931b73c',
139
+ 'javascript' => '4f635c3e11bebc2913be',
140
+ ),
141
+ 'vendors' =>
142
+ array (
143
+ 0 => 'vendors/user-groups/api',
144
+ ),
145
+ 'dependencies' =>
146
+ array (
147
+ 0 => 'lodash',
148
+ 1 => 'wp-api-fetch',
149
+ 2 => 'wp-data',
150
+ 3 => 'wp-i18n',
151
+ 4 => 'wp-url',
152
+ ),
153
+ ),
154
+ 'user-groups/settings' =>
155
+ array (
156
+ 'runtime' => true,
157
+ 'files' =>
158
+ array (
159
+ 0 => 'user-groups/settings.css',
160
+ 1 => 'user-groups/settings.js',
161
+ ),
162
+ 'hash' => '305ce83e45716bef87675e4537acb854',
163
+ 'contentHash' =>
164
+ array (
165
+ 'css/mini-extract' => '59a0798149a770d90ff7',
166
+ 'javascript' => '91ae72c9f77f23fe2c64',
167
+ ),
168
+ 'vendors' =>
169
+ array (
170
+ ),
171
+ 'dependencies' =>
172
+ array (
173
+ 0 => '@ithemes/security.user-groups.api',
174
+ 1 => 'lodash',
175
+ 2 => 'react',
176
+ 3 => 'react-dom',
177
+ 4 => 'wp-api-fetch',
178
+ 5 => 'wp-components',
179
+ 6 => 'wp-compose',
180
+ 7 => 'wp-data',
181
+ 8 => 'wp-dom-ready',
182
+ 9 => 'wp-element',
183
+ 10 => 'wp-html-entities',
184
+ 11 => 'wp-i18n',
185
+ 12 => 'wp-is-shallow-equal',
186
+ 13 => 'wp-notices',
187
+ 14 => 'wp-url',
188
+ ),
189
+ ),
190
+ 'vendors/user-groups/api' =>
191
+ array (
192
+ 'runtime' => false,
193
+ 'files' =>
194
+ array (
195
+ 0 => 'vendors/user-groups/api.js',
196
+ ),
197
+ 'hash' => '6e34397d56216708356fb62fb92c1b6c',
198
+ 'contentHash' =>
199
+ array (
200
+ 'css/mini-extract' => '31d6cfe0d16ae931b73c',
201
+ 'javascript' => 'a4cac444b9ecf3c4bda3',
202
+ ),
203
+ 'vendors' =>
204
+ array (
205
+ ),
206
+ 'dependencies' =>
207
+ array (
208
+ ),
209
+ ),
210
  );
dist/packages/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
dist/packages/preload.min.js ADDED
@@ -0,0 +1 @@
 
1
+ this.itsec=this.itsec||{},this.itsec.packages=this.itsec.packages||{},this.itsec.packages.preload=function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s="aJ6B")}({aJ6B:function(e,t,r){"use strict";r.r(t),r.d(t,"getStablePath",function(){return o}),r.d(t,"createMiddleware",function(){return i}),r.d(t,"invalidatePreload",function(){return u}),r.d(t,"CONTROL",function(){return a}),r.d(t,"invalidatePreloadControl",function(){return c}),r.p=window.itsecWebpackPublicPath;var n={};function o(e){var t=e.split("?"),r=t[1],n=t[0];return r?n+"?"+r.split("&").map(function(e){return e.split("=")}).sort(function(e,t){return e[0].localeCompare(t[0])}).map(function(e){return e.join("=")}).join("&"):n}function i(e){return Object.keys(e).reduce(function(t,r){return t[o(r)]=e[r],t},n),function(e,t){var r=e.parse,i=void 0===r||r;if("string"==typeof e.path){var u=e.method||"GET",a=o(e.path);if(i&&"GET"===u&&n[a])return Promise.resolve(n[a].body);if("OPTIONS"===u&&n[u]&&n[u][a])return Promise.resolve(n[u][a])}return t(e)}}function u(e){var t=e.path;delete n[o(t)]}var a="INVALIDATE_API_FETCH_PRELOAD";function c(e){return{type:a,path:e}}}});
dist/user-groups/api.min.js ADDED
@@ -0,0 +1 @@
 
1
+ this.itsec=this.itsec||{},this.itsec["user-groups"]=this.itsec["user-groups"]||{},this.itsec["user-groups"].api=function(e){function t(t){for(var n,a,c=t[0],s=t[1],o=t[2],f=0,p=[];f<c.length;f++)a=c[f],u[a]&&p.push(u[a][0]),u[a]=0;for(n in s)Object.prototype.hasOwnProperty.call(s,n)&&(e[n]=s[n]);for(d&&d(t);p.length;)p.shift()();return i.push.apply(i,o||[]),r()}function r(){for(var e,t=0;t<i.length;t++){for(var r=i[t],n=!0,c=1;c<r.length;c++){var s=r[c];0!==u[s]&&(n=!1)}n&&(i.splice(t--,1),e=a(a.s=r[0]))}return e}var n={},u={5:0},i=[];function a(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,a),r.l=!0,r.exports}a.m=e,a.c=n,a.d=function(e,t,r){a.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,t){if(1&t&&(e=a(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(a.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)a.d(r,n,function(t){return e[t]}.bind(null,n));return r},a.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(t,"a",t),t},a.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},a.p="";var c=window.itsecWebpackJsonP=window.itsecWebpackJsonP||[],s=c.push.bind(c);c.push=t,c=c.slice();for(var o=0;o<c.length;o++)t(c[o]);var d=s;return i.push(["Ntz8",7]),r()}({"1ZqX":function(e,t){!function(){e.exports=this.wp.data}()},Mmq9:function(e,t){!function(){e.exports=this.wp.url}()},Ntz8:function(e,t,r){"use strict";r.r(t);var n={};r.r(n),r.d(n,"path",function(){return G}),r.d(n,"query",function(){return R}),r.d(n,"appendToQuery",function(){return k}),r.d(n,"receiveQuery",function(){return U}),r.d(n,"processItem",function(){return A}),r.d(n,"receiveGroup",function(){return w}),r.d(n,"receiveMatchables",function(){return j}),r.d(n,"startCreateGroup",function(){return N}),r.d(n,"failedCreateGroup",function(){return C}),r.d(n,"finishCreateGroup",function(){return D}),r.d(n,"startUpdateGroup",function(){return F}),r.d(n,"failedUpdateGroup",function(){return H}),r.d(n,"finishUpdateGroup",function(){return L}),r.d(n,"startDeleteGroup",function(){return B}),r.d(n,"failedDeleteGroup",function(){return q}),r.d(n,"finishDeleteGroup",function(){return M}),r.d(n,"receiveGroupSettings",function(){return Y}),r.d(n,"startUpdateGroupSettings",function(){return Q}),r.d(n,"failedUpdateGroupSettings",function(){return K}),r.d(n,"finishUpdateGroupSettings",function(){return V}),r.d(n,"createGroup",function(){return W}),r.d(n,"updateGroup",function(){return J}),r.d(n,"deleteGroup",function(){return Z}),r.d(n,"updateGroupSettings",function(){return z}),r.d(n,"fetchGroupsSettings",function(){return X}),r.d(n,"startFetchGroupsSettings",function(){return $}),r.d(n,"finishFetchGroupsSettings",function(){return ee}),r.d(n,"failedFetchGroupsSettings",function(){return te}),r.d(n,"patchBulkGroupSettings",function(){return re}),r.d(n,"startPatchBulkGroupSettings",function(){return ne}),r.d(n,"finishPatchBulkGroupSettings",function(){return ue}),r.d(n,"failedPatchBulkGroupSettings",function(){return ie}),r.d(n,"RECEIVE_QUERY",function(){return ae}),r.d(n,"APPEND_TO_QUERY",function(){return ce}),r.d(n,"RECEIVE_MATCHABLES",function(){return se}),r.d(n,"START_CREATE_GROUP",function(){return oe}),r.d(n,"FINISH_CREATE_GROUP",function(){return de}),r.d(n,"FAILED_CREATE_GROUP",function(){return fe}),r.d(n,"RECEIVE_GROUP",function(){return pe}),r.d(n,"START_UPDATE_GROUP",function(){return le}),r.d(n,"FINISH_UPDATE_GROUP",function(){return ge}),r.d(n,"FAILED_UPDATE_GROUP",function(){return he}),r.d(n,"START_DELETE_GROUP",function(){return be}),r.d(n,"FINISH_DELETE_GROUP",function(){return ve}),r.d(n,"FAILED_DELETE_GROUP",function(){return ye}),r.d(n,"RECEIVE_GROUP_SETTINGS",function(){return me}),r.d(n,"START_UPDATE_GROUP_SETTINGS",function(){return _e}),r.d(n,"FINISH_UPDATE_GROUP_SETTINGS",function(){return Ee}),r.d(n,"FAILED_UPDATE_GROUP_SETTINGS",function(){return Se}),r.d(n,"START_FETCH_GROUPS_SETTINGS",function(){return Ie}),r.d(n,"FINISH_FETCH_GROUPS_SETTINGS",function(){return Te}),r.d(n,"FAILED_FETCH_GROUPS_SETTINGS",function(){return Pe}),r.d(n,"START_PATCH_BULK_GROUP_SETTINGS",function(){return Oe}),r.d(n,"FINISH_PATCH_BULK_GROUP_SETTINGS",function(){return xe}),r.d(n,"FAILED_PATCH_BULK_GROUP_SETTINGS",function(){return Ge});var u={};r.r(u),r.d(u,"getMatchables",function(){return ke}),r.d(u,"getMatchableType",function(){return Ue}),r.d(u,"getMatchableLabel",function(){return Ae}),r.d(u,"getGroups",function(){return we}),r.d(u,"getQueriedObjectIds",function(){return Ne}),r.d(u,"getGroup",function(){return Ce}),r.d(u,"getGroupAttribute",function(){return De}),r.d(u,"isUpdating",function(){return Fe}),r.d(u,"isDeleting",function(){return He}),r.d(u,"getGroupSettings",function(){return Le}),r.d(u,"getGroupSetting",function(){return Be}),r.d(u,"isUpdatingSettings",function(){return qe}),r.d(u,"isBulkPatchingSettings",function(){return Me}),r.d(u,"getGroupsBySetting",function(){return Ye});var i={};r.r(i),r.d(i,"getGroup",function(){return Ze}),r.d(i,"getMatchables",function(){return ze}),r.d(i,"getGroupSettings",function(){return Xe});var a=r("1ZqX"),c=r("RIqP"),s=r.n(c),o=r("ywyh"),d=r.n(o),f=r("Td6G");function p(e){return{type:"API_FETCH",request:e}}function l(e,t){for(var r=arguments.length,n=new Array(r>2?r-2:0),u=2;u<r;u++)n[u-2]=arguments[u];return{type:"DISPATCH",storeKey:e,actionName:t,args:n}}var g={API_FETCH:function(e){var t=e.request;return d()(t).catch(f.e)},SELECT:function(e){var t,r=e.storeKey,n=e.selectorName,u=e.args;return(t=Object(a.select)(r))[n].apply(t,s()(u))},DISPATCH:function(e){var t,r=e.storeKey,n=e.actionName,u=e.args;return(t=Object(a.dispatch)(r))[n].apply(t,s()(u))}},h=r("YLtl"),b=r("qPad"),v=r.n(b),y=r("Mmq9"),m=regeneratorRuntime.mark(R),_=regeneratorRuntime.mark(k),E=regeneratorRuntime.mark(A),S=regeneratorRuntime.mark(W),I=regeneratorRuntime.mark(J),T=regeneratorRuntime.mark(Z),P=regeneratorRuntime.mark(z),O=regeneratorRuntime.mark(X),x=regeneratorRuntime.mark(re),G="/ithemes-security/v1/user-groups";function R(e,t){var r,n,u,i,a,c,s;return regeneratorRuntime.wrap(function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,p({path:Object(y.addQueryArgs)(G,t)});case 2:return r=o.sent,o.next=5,U(e,r);case 5:n=!0,u=!1,i=void 0,o.prev=8,a=r[Symbol.iterator]();case 10:if(n=(c=a.next()).done){o.next=16;break}return s=c.value,o.delegateYield(A(s),"t0",13);case 13:n=!0,o.next=10;break;case 16:o.next=22;break;case 18:o.prev=18,o.t1=o.catch(8),u=!0,i=o.t1;case 22:o.prev=22,o.prev=23,n||null==a.return||a.return();case 25:if(o.prev=25,!u){o.next=28;break}throw i;case 28:return o.finish(25);case 29:return o.finish(22);case 30:return o.abrupt("return",r);case 31:case"end":return o.stop()}},m,this,[[8,18,22,30],[23,,25,29]])}function k(e,t){return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,{type:ce,queryId:e,item:t};case 2:return r.delegateYield(A(t),"t0",3);case 3:case"end":return r.stop()}},_,this)}function U(e,t){return{type:ae,queryId:e,items:t}}function A(e){var t,r,n,u,i,a,c,s;return regeneratorRuntime.wrap(function(o){for(;;)switch(o.prev=o.next){case 0:t=Object(h.get)(e,["_embedded","ithemes-security:user-group-member"],[]),r=Object(h.get)(e,["_embedded","ithemes-security:user-matchable-settings",0]),n=!0,u=!1,i=void 0,o.prev=5,a=t[Symbol.iterator]();case 7:if(n=(c=a.next()).done){o.next=14;break}return s=c.value,o.next=11,l("ithemes-security/core","receiveUser",s);case 11:n=!0,o.next=7;break;case 14:o.next=20;break;case 16:o.prev=16,o.t0=o.catch(5),u=!0,i=o.t0;case 20:o.prev=20,o.prev=21,n||null==a.return||a.return();case 23:if(o.prev=23,!u){o.next=26;break}throw i;case 26:return o.finish(23);case 27:return o.finish(20);case 28:if(!r){o.next=31;break}return o.next=31,Y(e.id,r);case 31:case"end":return o.stop()}},E,this,[[5,16,20,28],[21,,23,27]])}function w(e){return{type:pe,group:e}}function j(e){return{type:se,matchables:e}}function N(e){return{type:oe,group:e}}function C(e,t){return{type:fe,group:e,error:t}}function D(e,t){return{type:de,group:e,response:t}}function F(e,t){return{type:le,id:e,group:t}}function H(e,t){return{type:he,id:e,error:t}}function L(e,t){return{type:ge,id:e,response:t}}function B(e){return{type:be,id:e}}function q(e,t){return{type:ye,id:e,error:t}}function M(e){return{type:ve,id:e}}function Y(e,t){return{type:me,id:e,settings:t}}function Q(e,t){return{type:_e,id:e,settings:t}}function K(e,t){return{type:Se,id:e,error:t}}function V(e,t){return{type:Ee,id:e,response:t}}function W(e){var t;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,N(e);case 2:return r.prev=2,r.next=5,p({path:Object(y.addQueryArgs)(G,{_embed:1}),method:"POST",data:e});case 5:t=r.sent,r.next=13;break;case 8:return r.prev=8,r.t0=r.catch(2),r.next=12,C(e,r.t0);case 12:return r.abrupt("return",r.t0);case 13:return r.next=15,D(e,t);case 15:return r.next=17,w(t);case 17:return r.delegateYield(A(t),"t1",18);case 18:return r.abrupt("return",t);case 19:case"end":return r.stop()}},S,this,[[2,8]])}function J(e,t){var r;return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,F(e,t);case 2:return n.prev=2,n.next=5,p({path:G+"/"+e,method:"PUT",data:t});case 5:r=n.sent,n.next=13;break;case 8:return n.prev=8,n.t0=n.catch(2),n.next=12,H(e,n.t0);case 12:return n.abrupt("return",n.t0);case 13:return n.next=15,L(e,r);case 15:return n.next=17,w(r);case 17:return n.abrupt("return",r);case 18:case"end":return n.stop()}},I,this,[[2,8]])}function Z(e){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,B(e);case 2:return t.prev=2,t.next=5,p({path:"".concat(G,"/").concat(e),method:"DELETE"});case 5:t.next=12;break;case 7:return t.prev=7,t.t0=t.catch(2),t.next=11,q(e,t.t0);case 11:return t.abrupt("return",t.t0);case 12:return t.next=14,M(e);case 14:return t.abrupt("return",null);case 15:case"end":return t.stop()}},T,this,[[2,7]])}function z(e,t){var r;return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,Q(e,t);case 2:return n.prev=2,n.next=5,p({path:"ithemes-security/v1/user-matchable-settings/".concat(e),method:"PUT",data:t});case 5:r=n.sent,n.next=13;break;case 8:return n.prev=8,n.t0=n.catch(2),n.next=12,K(e,n.t0);case 12:return n.abrupt("return",n.t0);case 13:return n.next=15,V(e,r);case 15:return n.next=17,Y(e,r);case 17:return n.abrupt("return",r);case 18:case"end":return n.stop()}},P,this,[[2,8]])}function X(){var e,t,r,n,u=arguments;return regeneratorRuntime.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:return e=u.length>0&&void 0!==u[0]?u[0]:[],i.next=3,$(e);case 3:return i.prev=3,r="ithemes-security/v1/user-matchable-settings",e.length>0&&(r=Object(y.addQueryArgs)(r,{include:e})),i.next=8,p({path:r});case 8:t=i.sent,i.next=16;break;case 11:return i.prev=11,i.t0=i.catch(3),i.next=15,te(e,i.t0);case 15:return i.abrupt("return",i.t0);case 16:return i.next=18,ee(e,t);case 18:i.t1=regeneratorRuntime.keys(t);case 19:if((i.t2=i.t1()).done){i.next=27;break}if(n=i.t2.value,t.hasOwnProperty(n)){i.next=23;break}return i.abrupt("continue",19);case 23:return i.next=25,Y(n,t[n]);case 25:i.next=19;break;case 27:return i.abrupt("return",t);case 28:case"end":return i.stop()}},O,this,[[3,11]])}function $(e){return{type:Ie,groupIds:e}}function ee(e,t){return{type:Te,groupIds:e,response:t}}function te(e,t){return{type:Pe,groupIds:e,error:t}}function re(e,t){var r,n,u,i,c,s,o,d,l,g,h;return regeneratorRuntime.wrap(function(b){for(;;)switch(b.prev=b.next){case 0:return b.next=2,ne(e,t);case 2:return b.prev=2,b.next=5,p({path:Object(y.addQueryArgs)("ithemes-security/v1/user-matchable-settings",{include:e}),method:"PATCH",data:t});case 5:r=b.sent,b.next=13;break;case 8:return b.prev=8,b.t0=b.catch(2),b.next=12,ie(e,t,b.t0);case 12:return b.abrupt("return",b.t0);case 13:return b.next=15,ue(e,t,r);case 15:if(n=Object(a.select)("ithemes-security/core").getSchema("ithemes-security-user-group-settings"),u=Object(f.c)(n,"self")){b.next=19;break}return b.abrupt("return",r);case 19:i=new v.a(u.href),c=!0,s=!1,o=void 0,b.prev=23,d=r[Symbol.iterator]();case 25:if(c=(l=d.next()).done){b.next=37;break}if(200===(g=l.value).status){b.next=29;break}return b.abrupt("continue",34);case 29:if((h=i.fromUri(g.href)).id){b.next=32;break}return b.abrupt("continue",34);case 32:return b.next=34,Y(h.id,g.response);case 34:c=!0,b.next=25;break;case 37:b.next=43;break;case 39:b.prev=39,b.t1=b.catch(23),s=!0,o=b.t1;case 43:b.prev=43,b.prev=44,c||null==d.return||d.return();case 46:if(b.prev=46,!s){b.next=49;break}throw o;case 49:return b.finish(46);case 50:return b.finish(43);case 51:return b.abrupt("return",r);case 52:case"end":return b.stop()}},x,this,[[2,8],[23,39,43,51],[44,,46,50]])}function ne(e,t){return{type:Oe,groupIds:e,patch:t}}function ue(e,t,r){return{type:xe,groupIds:e,patch:t,response:r}}function ie(e,t,r){return{type:Ge,groupIds:e,patch:t,error:r}}var ae="RECEIVE_QUERY",ce="APPEND_TO_QUERY",se="RECEIVE_MATCHABLES",oe="START_CREATE_GROUP",de="FINISH_CREATE_GROUP",fe="FAILED_CREATE_GROUP",pe="RECEIVE_GROUP",le="START_UPDATE_GROUP",ge="FINISH_UPDATE_GROUP",he="FAILED_UPDATE_GROUP",be="START_DELETE_GROUP",ve="FINISH_DELETE_GROUP",ye="FAILED_DELETE_GROUP",me="RECEIVE_GROUP_SETTINGS",_e="START_UPDATE_GROUP_SETTINGS",Ee="FINISH_UPDATE_GROUP_SETTINGS",Se="FAILED_UPDATE_GROUP_SETTINGS",Ie="START_FETCH_GROUPS_SETTINGS",Te="FINISH_FETCH_GROUPS_SETTINGS",Pe="FAILED_FETCH_GROUPS_SETTINGS",Oe="START_PATCH_BULK_GROUP_SETTINGS",xe="FINISH_PATCH_BULK_GROUP_SETTINGS",Ge="FAILED_PATCH_BULK_GROUP_SETTINGS",Re=r("pPDe"),ke=Object(Re.a)(function(e){return Object(h.filter)(Object(h.map)(e.matchableIds,function(t){return e.matchablesById[t]}),h.isObject)},function(e){return[e.matchablesById,e.matchableIds]});function Ue(e,t){return(e.matchablesById[t]||{}).type}function Ae(e,t){return(e.matchablesById[t]||{}).label}var we=Object(Re.a)(function(e,t){return Object(h.filter)(Object(h.map)(e.queries[t],function(t){return e.byId[t]}),h.isObject)},function(e,t){return[e.queries[t],e.byId]}),je=[];function Ne(e,t){return e.queries[t]||je}function Ce(e,t){return e.byId[t]}function De(e,t,r){var n=Object(a.select)("ithemes-security/user-groups").getGroup(t);return n?n[r]:void 0}function Fe(e,t){return e.updating.includes(t)}function He(e,t){return e.deleting.includes(t)}function Le(e,t){return e.settings[t]}function Be(e,t,r,n){var u=Object(a.select)("ithemes-security/user-groups").getGroupSettings(t);return Object(h.get)(u,[r,n])}function qe(e,t){return e.updatingSettings.includes(t)}function Me(e,t,r){var n=t.join("_");return e.bulkPatchingSettings[n]===r}function Ye(e){var t={};for(var r in e.settings)if(e.settings.hasOwnProperty(r))for(var n in e.settings[r])if(e.settings[r].hasOwnProperty(n))for(var u in e.settings[r][n])e.settings[r][n].hasOwnProperty(u)&&(t[n]||(t[n]={}),t[n][u]||(t[n][u]=[]),e.settings[r][n][u]&&t[n][u].push(r));return t}var Qe=r("lSNA"),Ke=r.n(Qe),Ve=r("MVZn"),We=r.n(Ve),Je={matchablesById:{},matchableIds:[],byId:{},queries:{},creating:[],updating:[],deleting:[],settings:{},updatingSettings:[],bulkPatchingSettings:{}};var Ze={fulfill:regeneratorRuntime.mark(function e(t){var r;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,p({path:"".concat(G,"/").concat(t,"?_embed=1")});case 2:return r=e.sent,e.next=5,w(r);case 5:return e.delegateYield(A(r),"t0",6);case 6:case"end":return e.stop()}},e,this)}),isFulfilled:function(e,t){return e.byId.hasOwnProperty(t)}},ze={fulfill:regeneratorRuntime.mark(function e(){var t,r,n,u,i,a,c,s,o;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,p({path:"/ithemes-security/v1/user-matchables?_embed=1"});case 2:return t=e.sent,e.next=5,j(t);case 5:r=!0,n=!1,u=void 0,e.prev=8,i=t[Symbol.iterator]();case 10:if(r=(a=i.next()).done){e.next=23;break}if(c=a.value,s=Object(h.get)(c,["_embedded","self",0]),o=Object(h.get)(c,["_embedded","ithemes-security:user-matchable-settings",0]),!s){e.next=17;break}return e.next=17,w(s);case 17:if(!o){e.next=20;break}return e.next=20,Y(c.id,o);case 20:r=!0,e.next=10;break;case 23:e.next=29;break;case 25:e.prev=25,e.t0=e.catch(8),n=!0,u=e.t0;case 29:e.prev=29,e.prev=30,r||null==i.return||i.return();case 32:if(e.prev=32,!n){e.next=35;break}throw u;case 35:return e.finish(32);case 36:return e.finish(29);case 37:case"end":return e.stop()}},e,this,[[8,25,29,37],[30,,32,36]])}),isFulfilled:function(e){return!Object(h.isEmpty)(e.matchablesById)}},Xe={fulfill:regeneratorRuntime.mark(function e(t){var r;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,p({path:"ithemes-security/v1/user-matchable-settings/".concat(t)});case 2:return r=e.sent,e.next=5,Y(t,r);case 5:case"end":return e.stop()}},e,this)}),isFulfilled:function(e,t){return e.settings.hasOwnProperty(t)}},$e=Object(a.registerStore)("ithemes-security/user-groups",{controls:g,actions:n,selectors:u,resolvers:i,reducer:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Je,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case se:return We()({},e,{matchableIds:Object(h.map)(t.matchables,"id"),matchablesById:Object(h.keyBy)(t.matchables,"id")});case ae:return We()({},e,{byId:We()({},e.byId,Object(h.keyBy)(t.items,"id")),queries:We()({},e.queries,Ke()({},t.queryId,Object(h.map)(t.items,"id")))});case ce:return We()({},e,{byId:We()({},e.byId,Ke()({},t.item.id,t.item)),queries:We()({},e.queries,Ke()({},t.queryId,s()(e.queries[t.queryId]||[]).concat([t.item.id])))});case pe:return We()({},e,{byId:We()({},e.byId,Ke()({},t.group.id,t.group)),matchablesById:e.matchablesById[t.group.id]?We()({},e.matchablesById,Ke()({},t.group.id,We()({},e.matchablesById[t.group.id],{label:t.group.label}))):e.matchablesById});case oe:return We()({},e,{creating:s()(e.creating).concat([t.group])});case de:return We()({},e,{creating:e.creating.filter(function(e){return e!==t.group}),matchablesById:We()({},e.matchablesById,Ke()({},t.response.id,{id:t.response.id,label:t.response.label,type:"user-group"})),matchableIds:s()(e.matchableIds).concat([[t.response.id]])});case fe:return We()({},e,{creating:e.creating.filter(function(e){return e!==t.group})});case le:return We()({},e,{updating:s()(e.updating).concat([t.id])});case ge:case he:return We()({},e,{updating:e.updating.filter(function(e){return e!==t.id})});case be:return We()({},e,{deleting:s()(e.deleting).concat([t.id])});case ve:return We()({},e,{deleting:e.deleting.filter(function(e){return e!==t.id}),byId:Object(h.omit)(e.byId,[t.id]),matchablesById:Object(h.omit)(e.matchablesById,[t.id]),matchableIds:e.matchableIds.filter(function(e){return e!==t.id}),settings:Object(h.omit)(e.settings,[t.id])});case ye:return We()({},e,{deleting:e.deleting.filter(function(e){return e!==t.id})});case me:return We()({},e,{settings:We()({},e.settings,Ke()({},t.id,t.settings))});case _e:return We()({},e,{updatingSettings:s()(e.updatingSettings).concat([t.id])});case Ee:case Se:return We()({},e,{updatingSettings:e.updatingSettings.filter(function(e){return e!==t.id})});case Oe:return We()({},e,{bulkPatchingSettings:We()({},e.bulkPatchingSettings,Ke()({},t.groupIds.join("_"),t.patch))});case xe:case Ge:return We()({},e,{bulkPatchingSettings:Object(h.omit)(e.bulkPatchingSettings,[t.groupIds.join("_")])});default:return e}}});r.d(t,"store",function(){return $e}),r.p=window.itsecWebpackPublicPath},Td6G:function(e,t,r){"use strict";r("J4zp");var n=r("YLtl"),u=r("RIqP"),i=r.n(u),a=r("lwsE"),c=r.n(a),s=r("W8MJ"),o=r.n(s),d=r("lSNA"),f=r.n(d),p=r("92Nh"),l=r.n(p),g=r("tmk3"),h=r.n(g),b=function(){function e(){var t=this,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,u=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;c()(this,e),v.set(this,{writable:!0,value:{}}),y.set(this,{writable:!0,value:{}}),f()(this,"getErrorCodes",function(){return Object.keys(h()(t,v))}),f()(this,"getErrorCode",function(){return t.getErrorCodes()[0]}),f()(this,"getErrorMessages",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;if(e)return h()(t,v)[e];var r=[];for(var n in h()(t,v))h()(t,v).hasOwnProperty(n)&&r.concat(h()(t,v)[n]);return r}),f()(this,"getErrorMessage",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return e=e||t.getErrorCode(),t.getErrorMessages(e)[0]}),f()(this,"getErrorData",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return e=e||t.getErrorCode(),h()(t,y)[e]}),f()(this,"getAllErrorMessages",function(){var e=[];for(var r in h()(t,v))h()(t,v).hasOwnProperty(r)&&e.push.apply(e,i()(h()(t,v)[r]));return e}),r&&(n&&(h()(this,v)[r]=[n]),u&&(h()(this,y)[r]=u))}return o()(e,null,[{key:"fromPHPObject",value:function(t){var r=new e;return l()(r,v,t.errors),l()(r,y,t.error_data),r}},{key:"fromApiError",value:function(t){var r=new e;if(h()(r,v)[t.code]=[t.message],h()(r,y)[t.code]=t.data,t.additional_errors){var n=!0,u=!1,i=void 0;try{for(var a,c=t.additional_errors[Symbol.iterator]();!(n=(a=c.next()).done);n=!0){var s=a.value;h()(r,v)[s.code]=[s.message],h()(r,y)[s.code]=s.data}}catch(e){u=!0,i=e}finally{try{n||null==c.return||c.return()}finally{if(u)throw i}}}return r}}]),e}(),v=new WeakMap,y=new WeakMap,m=r("a1gu"),_=r.n(m),E=r("Nsbk"),S=r.n(E),I=r("7W2i"),T=r.n(I),P=r("PJYZ"),O=r.n(P),x=r("oShl"),G=r.n(x),R=r("l3Sj"),k=function(e){function t(e){var r,n;c()(this,t);for(var u=arguments.length,i=new Array(u>1?u-1:0),a=1;a<u;a++)i[a-1]=arguments[a];for(var s in n=_()(this,(r=S()(t)).call.apply(r,[this,e.message||Object(R.__)("An unknown error occurred.","better-wp-security")].concat(i))),Error.captureStackTrace&&Error.captureStackTrace(O()(O()(n)),t),n.__response=e,e)e.hasOwnProperty(s)&&Object.defineProperty(O()(O()(n)),s,{value:e[s],configurable:!0,enumerable:!0,writable:!0});return n}return T()(t,e),o()(t,[{key:"toString",value:function(){return this.__response.toString()}},{key:"getResponse",value:function(){return this.__response}}]),t}(G()(Error));function U(e){if(!Object(n.isPlainObject)(e))return!1;var t=Object.keys(e);return 2===t.length&&(t.includes("errors")&&t.includes("error_data"))}function A(e){return U(e)?b.fromPHPObject(e):function(e){if(!Object(n.isPlainObject)(e))return!1;var t=Object.keys(e);return(3===t.length||4===t.length)&&!(4===t.length&&!t.includes("additional_errors"))&&t.includes("code")&&t.includes("message")&&t.includes("data")}(e)?b.fromApiError(e):new b}function w(e,t){var r=[[],[]],n=!0,u=!1,i=void 0;try{for(var a,c=e[Symbol.iterator]();!(n=(a=c.next()).done);n=!0){var s=a.value;r[t(s)?0:1].push(s)}}catch(e){u=!0,i=e}finally{try{n||null==c.return||c.return()}finally{if(u)throw i}}return r}function j(e){if(e instanceof Error)throw e;throw new k(e)}r.d(t,"d",function(){return U}),r.d(t,"b",function(){return A}),r.d(t,"a",function(){return w}),r.d(t,"e",function(){return j}),r.d(t,"c",function(){return N});function N(e,t){if(e&&e.links){var r=!0,n=!1,u=void 0;try{for(var i,a=e.links[Symbol.iterator]();!(r=(i=a.next()).done);r=!0){var c=i.value;if(c.rel===t)return c}}catch(e){n=!0,u=e}finally{try{r||null==a.return||a.return()}finally{if(n)throw u}}}}},YLtl:function(e,t){!function(){e.exports=this.lodash}()},l3Sj:function(e,t){!function(){e.exports=this.wp.i18n}()},ywyh:function(e,t){!function(){e.exports=this.wp.apiFetch}()}});
dist/user-groups/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
dist/user-groups/settings.min.css ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-log-modal .components-modal__frame{min-width:80%;height:100%}.itsec-log-modal .itsec-log-modal__content{margin:2em 1em}.itsec-log-modal .itsec-log-modal__content .itsec-log-raw-details-toggle{display:none}.itsec-log-modal .itsec-log-modal__content tr:first-child th,.itsec-log-modal .itsec-log-modal__content tr:first-child td{margin-top:0;padding-top:0}
2
+
3
+ .itsec-malware-scan-results h4{margin-top:5px}.itsec-malware-scan-results ul{margin-left:20px}.itsec-malware-scan-results ul li{line-height:16px;list-style:outside none disc}.itsec-malware-scan-results .itsec-malware-scan-details pre{background-color:#eaeaea;padding:1em;white-space:pre-wrap}.itsec-malware-scan-results .itsec-malware-scan-results-section{border:1px solid #ddd;border-bottom-color:transparent;padding:0 1em}.itsec-malware-scan-results .itsec-malware-scan-results-section:last-child{border-bottom-color:#ddd}.itsec-malware-scan-results .itsec-malware-scan-clean,.itsec-malware-scan-results .itsec-malware-scan-warn,.itsec-malware-scan-results .itsec-malware-scan-error{padding:2px 6px;color:#fff;margin-right:1em;width:60px;text-align:center}.itsec-malware-scan-results span.itsec-malware-scan-clean,.itsec-malware-scan-results span.itsec-malware-scan-warn,.itsec-malware-scan-results span.itsec-malware-scan-error{display:inline-block}.itsec-malware-scan-results .itsec-malware-scan-clean{background:#7ad03a}.itsec-malware-scan-results .itsec-malware-scan-warn,.itsec-malware-scan-results .itsec-malware-scan-error{background:#dd3d36}.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-warn,.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-error,.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-clean{background:none;padding:0;color:inherit;margin:0;width:100%;text-align:left}.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-warn,.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-error{color:#dd3d36}.itsec-malware-scan-results .itsec-malware-scan-details .itsec-malware-scan-clean{color:#7ad03a}.itsec-malware-scan-results .itsec-malware-scan-results-section .itsec-malware-scan-details li{margin-bottom:.25em;padding-bottom:.5em;border-bottom:1px solid #ddd}.itsec-malware-scan-results .itsec-malware-scan-results-section .itsec-malware-scan-details li:last-child{border:none}.itsec-malware-scan-results .itsec-malware-scan-results-section .itsec-malware-scan-details li span{color:#444}.itsec-malware-scan-results .itsec-malware-scan-toggle-details{margin-left:1em}
4
+
5
+ .itsec-site-scan-results h4{margin-top:5px}.itsec-site-scan-results .itsec-site-scan-details pre{background-color:#eaeaea;padding:1em;white-space:pre-wrap}.itsec-site-scan-results .itsec-site-scan-results-section{border:1px solid #ddd;border-bottom-color:transparent;padding:1em}.itsec-site-scan-results .itsec-site-scan-results-section>p{margin:0}.itsec-site-scan-results .itsec-site-scan-results-section:last-child{border-bottom-color:#ddd}.itsec-site-scan-results .itsec-site-scan__status{display:inline-block;padding:2px 6px;color:#fff;margin-right:1em;width:60px;text-align:center}.itsec-site-scan-results .itsec-site-scan__status.itsec-site-scan__status--clean{background:#7ad03a}.itsec-site-scan-results .itsec-site-scan__status.itsec-site-scan__status--warn{background:#dd3d36}.itsec-site-scan-results .itsec-site-scan__status.itsec-site-scan__status--error{background:#dd3d36}.itsec-site-scan-results .itsec-site-scan__details ul{margin-left:2.5em;list-style:disc outside}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail{width:100%;margin-bottom:.25em;padding-bottom:.5em;color:inherit;border-bottom:1px solid #ddd}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail:last-child{border:none}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail.itsec-site-scan__detail--warn{color:#dd3d36}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail.itsec-site-scan__detail--error{color:#dd3d36}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail.itsec-site-scan__detail--clean{color:#7ad03a}.itsec-site-scan-results .itsec-site-scan__details .itsec-site-scan__detail span{color:#444}.itsec-site-scan-results .itsec-site-scan-toggle-details{margin-left:1em}
6
+
7
+ .itsec-component-print-r{background:#f1f1f1;overflow:scroll;padding:1em;color:black;font-family:"Courier New", Courier, monospace;font-size:12px;white-space:pre-wrap;text-align:left;max-width:100%}
8
+
9
+ .itsec-close-button.components-icon-button{padding:0;height:20px;position:absolute;right:1em;top:1em}.itsec-close-button.components-icon-button:hover{opacity:.5;box-shadow:none !important}.components-popover.is-mobile .itsec-close-button.components-icon-button{display:none}
10
+
11
+ .itsec-loader{margin:0 auto;display:flex;align-items:center;justify-content:center;height:100%}.itsec-loader svg{animation:itsec-loader__grow 3.0s infinite ease-in}@keyframes itsec-loader__grow{0%{transform:scale(0)}100%{transform:scale(1);opacity:0}}
12
+
13
+ .components-base-control>ul.components-hierarchical-checkbox-control__group{padding-left:0}ul.components-hierarchical-checkbox-control__group{padding-left:1em}.components-hierarchical-checkbox-control__group .components-hierarchical-checkbox-control__option{list-style-type:none}.components-hierarchical-checkbox-control__option--has-children{margin-bottom:1em}.components-hierarchical-checkbox-control__option--has-children>.components-base-control{font-weight:bold}
14
+
15
+ .itsec-module-settings-notice-list .notice .notice__action{margin-left:.5em}.itsec-module-settings-notice-list{position:relative;padding-bottom:1em;font-size:13px}.itsec-module-settings-notice-list:empty{display:none}.itsec-module-settings-content .components-editor-notices__snackbar{position:fixed;bottom:100px;padding-left:16px;padding-right:16px;width:-webkit-max-content;width:-moz-max-content;width:max-content}.itsec-module-settings-content .components-snackbar{width:-webkit-max-content;width:-moz-max-content;width:max-content}
16
+
17
+ .components-form-toggle__input:disabled+.components-form-toggle__track{opacity:.5}
18
+
19
+ .components-checkbox-control__input[type="checkbox"]{border:1px solid #b4b9be;background:#fff;color:#555;clear:none;cursor:pointer;display:inline-block;line-height:0;margin:0 4px 0 0;outline:0;padding:0 !important;text-align:center;vertical-align:top;width:25px;height:25px;-webkit-appearance:none;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);transition:0.05s border-color ease-in-out}@media (min-width: 600px){.components-checkbox-control__input[type="checkbox"]{height:16px;width:16px}}.components-checkbox-control__input[type="checkbox"]:focus{border-color:#5b9dd9;box-shadow:0 0 2px rgba(30,140,190,0.8);outline:2px solid transparent}.components-checkbox-control__input[type="checkbox"]:checked{background:#11a0d2;border-color:#11a0d2}.components-checkbox-control__input[type="checkbox"]:checked::-ms-check{opacity:0}.components-checkbox-control__input[type="checkbox"]:focus:checked{border:none}.components-checkbox-control__input[type="checkbox"]:checked::before{content:none}.components-checkbox-control__input-container{position:relative;display:inline-block;margin-right:12px;vertical-align:middle;width:25px;height:25px}@media (min-width: 600px){.components-checkbox-control__input-container{width:16px;height:16px}}svg.dashicon.components-checkbox-control__checked{fill:#fff;cursor:pointer;position:absolute;left:-4px;top:-2px;width:31px;height:31px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}@media (min-width: 600px){svg.dashicon.components-checkbox-control__checked{width:21px;height:21px;left:-3px}}.components-checkbox-control__input[type="checkbox"]:indeterminate{background:#11a0d2;border-color:#11a0d2}.components-checkbox-control__input[type="checkbox"]:indeterminate::-ms-check{opacity:0}.components-checkbox-control__input[type="checkbox"]:focus:checked,.components-checkbox-control__input[type="checkbox"]:focus:indeterminate{border:none}.components-checkbox-control__input[type="checkbox"]:checked::before,.components-checkbox-control__input[type="checkbox"]:indeterminate::before{content:none}svg.dashicon.components-checkbox-control__checked.components-checkbox-control__checked--indeterminate{width:16px;left:0}
20
+
21
+ .itsec-user-groups-list{display:grid;grid-template-columns:200px minmax(0, auto);grid-gap:1.25em;padding-bottom:2em}.itsec-user-groups-list>.components-tab-panel__tabs{display:flex;flex-direction:column;margin-top:2.2em}.itsec-user-groups-list__item{padding:1em .5em;margin:1.25em 0 0;border:1px solid #7e8993;border-radius:4px;text-align:left;height:auto}.itsec-user-groups-list__item.is-active{border-color:#007cba;box-shadow:0 0 0 1px #007cba;outline:2px solid transparent}.itsec-user-groups-list__item:first-child{margin-top:0}.itsec-user-groups-list__item:last-child{margin-bottom:0}.itsec-user-groups-list__item--new{justify-content:center;align-items:center}.itsec-user-groups-list__item--new .dashicon{margin-right:10px}
22
+
23
+ .itsec-manage-user-group-tabs{border:1px solid #7e8993;border-radius:4px}.itsec-manage-user-group-tabs .components-tab-panel__tabs{background:#f3f4f5;border-radius:4px 4px 0 0;display:flex;flex-basis:0}.itsec-manage-user-group-tabs__tab.components-button{flex-grow:1;flex-basis:0;height:40px;padding:3px 15px 0;border-bottom:1px solid #7e8993;border-right:1px solid #7e8993;cursor:pointer;font-weight:400;justify-content:center;align-items:center;text-transform:uppercase;outline-offset:-1px;border-radius:0}.itsec-manage-user-group-tabs__tab.components-button:hover{box-shadow:none !important}.itsec-manage-user-group-tabs__tab.components-button:first-child{border-top-left-radius:3px}.itsec-manage-user-group-tabs__tab.components-button:last-child{border-top-right-radius:3px;border-right:none}.itsec-manage-user-group-tabs__tab.components-button.is-active{padding-top:0;background:white;border-top:3px solid #0081E3;border-bottom-color:transparent;font-weight:600;outline:none}
24
+
25
+ .itsec-user-group-label h4{margin-top:0}
26
+
27
+ .itsec-user-groups-group-tab__row{padding:1em}.itsec-user-groups-group-tab__row--save{border-top:1px solid #7e8993;text-align:right}.itsec-user-groups-group-tab--is-loading{opacity:.5;pointer-events:none}
28
+
29
+ .itsec-user-groups-group-tab--settings .itsec-user-groups-group-tab__modules-list{padding-left:0}.itsec-user-groups-group-tab--settings ul{list-style-type:none}.itsec-user-groups-group-tab--settings legend{font-weight:600;margin-bottom:1em;font-size:1.2em}
30
+
31
+ .itsec-user-group-panel-users__selected span{padding-right:.5em}.components-itsec-async-select-control__input input{min-height:0}.components-itsec-async-select-control__input input:focus{box-shadow:none}
32
+
33
+ .itsec-user-group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1em;height:1.2em}.itsec-user-group-header__label{margin:0 .5em 0 0;font-size:1.2em;line-height:1.2em;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.itsec-user-group-header .components-button{flex-shrink:0}
34
+
35
+ .itsec-user-groups-group-tab__row--edit-fields>.components-base-control:first-child{margin-bottom:16px}.itsec-user-groups-group-tab__row--edit-fields .components-base-control__label{font-weight:600;margin-bottom:1em;font-size:1.2em}
36
+
37
+
dist/user-groups/settings.min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ this.itsec=this.itsec||{},this.itsec["user-groups"]=this.itsec["user-groups"]||{},this.itsec["user-groups"].settings=function(e){function t(t){for(var n,i,o=t[0],s=t[1],c=0,u=[];c<o.length;c++)i=o[c],r[i]&&u.push(r[i][0]),r[i]=0;for(n in s)Object.prototype.hasOwnProperty.call(s,n)&&(e[n]=s[n]);for(a&&a(t);u.length;)u.shift()()}var n={},r={6:0,3:0};function i(t){if(n[t])return n[t].exports;var r=n[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(e){var t=[],n=r[e];if(0!==n)if(n)t.push(n[2]);else{var o=new Promise(function(t,i){n=r[e]=[t,i]});t.push(n[2]=o);var s,c=document.createElement("script");c.charset="utf-8",c.timeout=120,i.nc&&c.setAttribute("nonce",i.nc),c.src=function(e){return i.p+""+({}[e]||e)+".min.js"}(e),s=function(t){c.onerror=c.onload=null,clearTimeout(a);var n=r[e];if(0!==n){if(n){var i=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src,s=new Error("Loading chunk "+e+" failed.\n("+i+": "+o+")");s.type=i,s.request=o,n[1](s)}r[e]=void 0}};var a=setTimeout(function(){s({type:"timeout",target:c})},12e4);c.onerror=c.onload=s,document.head.appendChild(c)}return Promise.all(t)},i.m=e,i.c=n,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var o=window.itsecWebpackJsonP=window.itsecWebpackJsonP||[],s=o.push.bind(o);o.push=t,o=o.slice();for(var c=0;c<o.length;c++)t(o[c]);var a=s;return i(i.s="epig")}({"+VN0":function(e,t,n){},"16Al":function(e,t,n){"use strict";var r=n("WbBG");function i(){}e.exports=function(){function e(e,t,n,i,o,s){if(s!==r){var c=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw c.name="Invariant Violation",c}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return n.checkPropTypes=i,n.PropTypes=n,n}},"17x9":function(e,t,n){e.exports=n("16Al")()},"1ZqX":function(e,t){!function(){e.exports=this.wp.data}()},"31KZ":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.withErrorBoundary=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i=n("cDcd"),o=c(i),s=c(n("Xvx9"));function c(e){return e&&e.__esModule?e:{default:e}}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}var u=n("cDcd").babelPluginFlowReactPropTypes_proptype_ComponentType||n("17x9").any,l=function(e){function t(){var e,n,r;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);for(var i=arguments.length,o=Array(i),s=0;s<i;s++)o[s]=arguments[s];return n=r=a(this,(e=t.__proto__||Object.getPrototypeOf(t)).call.apply(e,[this].concat(o))),r.state={error:null,info:null},a(r,n)}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,i.Component),r(t,[{key:"componentDidCatch",value:function(e,t){var n=this.props.onError;if("function"==typeof n)try{n.call(this,e,t?t.componentStack:"")}catch(e){}this.setState({error:e,info:t})}},{key:"render",value:function(){var e=this.props,t=e.children,n=e.FallbackComponent,r=this.state,i=r.error,s=r.info;return null!==i?o.default.createElement(n,{componentStack:s?s.componentStack:"",error:i}):t}}]),t}();l.defaultProps={FallbackComponent:s.default},l.propTypes={error:"function"==typeof Error?n("17x9").instanceOf(Error):n("17x9").any,info:n("17x9").shape({componentStack:n("17x9").string.isRequired})},(t.withErrorBoundary=function(e,t,n){return function(r){return o.default.createElement(l,{FallbackComponent:t,onError:n},o.default.createElement(e,r))}}).propTypes=u===n("17x9").any?{}:u,t.default=l},"4eJC":function(e,t,n){e.exports=function(e,t){var n,r,i,o=0;function s(){var t,s,c=r,a=arguments.length;e:for(;c;){if(c.args.length===arguments.length){for(s=0;s<a;s++)if(c.args[s]!==arguments[s]){c=c.next;continue e}return c!==r&&(c===i&&(i=c.prev),c.prev.next=c.next,c.next&&(c.next.prev=c.prev),c.next=r,c.prev=null,r.prev=c,r=c),c.val}c=c.next}for(t=new Array(a),s=0;s<a;s++)t[s]=arguments[s];return c={args:t,val:e.apply(null,t)},r?(r.prev=c,c.next=r):i=c,o===n?(i=i.prev).next=null:o++,r=c,c.val}return t&&t.maxSize&&(n=t.maxSize),s.clear=function(){r=null,i=null,o=0},s}},"6ECA":function(e,t,n){},"7W2i":function(e,t,n){var r=n("SksO");e.exports=function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&r(e,t)}},"8OQS":function(e,t){e.exports=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;r<o.length;r++)n=o[r],t.indexOf(n)>=0||(i[n]=e[n]);return i}},"92Nh":function(e,t){e.exports=function(e,t,n){if(!t.has(e))throw new TypeError("attempted to set private field on non-instance");var r=t.get(e);if(!r.writable)throw new TypeError("attempted to set read only private field");return r.value=n,n}},A6yB:function(e,t,n){},Bnag:function(e,t){e.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},DfSy:function(e,t,n){},Dfre:function(e,t,n){},ELjz:function(e,t,n){},EbDI:function(e,t){e.exports=function(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}},GRId:function(e,t){!function(){e.exports=this.wp.element}()},"GjY+":function(e,t,n){},GrEf:function(e,t,n){},IXp0:function(e,t,n){},Ijbi:function(e,t){e.exports=function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t<e.length;t++)n[t]=e[t];return n}}},J4zp:function(e,t,n){var r=n("wTVA"),i=n("m0LI"),o=n("wkBT");e.exports=function(e,t){return r(e)||i(e,t)||o()}},JVTk:function(e,t,n){var r=n("cDcd");function i(e){return r.createElement("svg",e,[r.createElement("defs",{key:0},r.createElement("style",null,".itsec-icon-logo-12f8d__path{fill:#1072ba;}.itsec-icon-logo-12f8d__path{fill:#69c;}")),r.createElement("g",{key:1},r.createElement("g",null,[r.createElement("path",{className:"itsec-icon-logo-12f8d__path",d:"M203.28,95V14.26a1.73,1.73,0,0,0-1.11-1.55c-4.38-1.44-21.5-6.6-46.22-8a1.1,1.1,0,0,0-1.17,1.11V20.1a1,1,0,0,1-1.16,1c-8-1.24-16.88-2.29-26.39-3a1.27,1.27,0,0,1-1.17-1.25V3a1.37,1.37,0,0,0-1.16-1.32A211.61,211.61,0,0,0,101,0,178.14,178.14,0,0,0,78.51,1.87a1.4,1.4,0,0,0-1.16,1.35V16.91a1.28,1.28,0,0,1-1.16,1.26c-9.51.72-18.34,1.78-26.38,3a1,1,0,0,1-1.16-1V5.79a1.1,1.1,0,0,0-1.17-1.11c-24.74,1.43-41.94,6.59-46.34,8A1.73,1.73,0,0,0,0,14.26V95s-2.3,54.32,34.61,97.07c32.69,37.86,60,46.91,65.84,48.5a5.16,5.16,0,0,0,2.28,0c5.82-1.58,33.16-10.63,65.83-48.5C205.51,149.35,203.28,95,203.28,95Z",key:0}),r.createElement("path",{className:"itsec-icon-logo-12f8d__path",d:"M101.38,68.5a21.75,21.75,0,1,0,21.7,21.81A21.77,21.77,0,0,0,101.38,68.5Z",key:1}),r.createElement("path",{className:"itsec-icon-logo-12f8d__path",d:"M182.44,87V42.75A1.92,1.92,0,0,0,181,41a385.2,385.2,0,0,0-78.26-8.05h-1.66a398.43,398.43,0,0,0-78.71,8.21,1.9,1.9,0,0,0-1.45,1.78l0,44.2v.24c0,3.27.09,54.82,33.38,93.35,18.71,21.67,35,33.81,46.15,40.44a2.79,2.79,0,0,0,2.56,0c11.14-6.61,27.36-18.74,46.12-40.45C183.22,141.12,182.46,87.63,182.44,87Zm-50.57,79.1c-2.69,3.13-5.32,6-7.87,8.69-.59.61-1.07.41-1.07-.44V155.78c0-14.05,13.64-21.19,18.14-23.17a2.49,2.49,0,0,0,1.42-2.11V116.39a1,1,0,0,0-1.41-.94c-37.93,15.49-72.25,3-79.51,0a.93.93,0,0,0-1.41.91V130.5a2.37,2.37,0,0,0,1.44,2.06c14.94,5.72,18,15.46,18,23.4v18.25c0,.85-.48,1-1.06.43-2.51-2.63-5.09-5.46-7.76-8.53-28.11-32.58-27.95-78.24-27.93-78.7V61.47a1.86,1.86,0,0,1,1.51-1.77,374.15,374.15,0,0,1,56.51-4.51h1.69a364.08,364.08,0,0,1,55.75,4.32,1.86,1.86,0,0,1,1.52,1.78v25.3C159.83,87.44,160.12,133.44,131.87,166.11Z",key:2})]))])}i.defaultProps={width:"200px",viewBox:"0 0 203.31 240.72"},e.exports=i,i.default=i},K9lf:function(e,t){!function(){e.exports=this.wp.compose}()},MVZn:function(e,t,n){var r=n("lSNA");e.exports=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},i=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(i=i.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),i.forEach(function(t){r(e,t,n[t])})}return e}},Mmq9:function(e,t){!function(){e.exports=this.wp.url}()},Nsbk:function(e,t){function n(t){return e.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},n(t)}e.exports=n},PJYZ:function(e,t){e.exports=function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}},QILm:function(e,t,n){var r=n("8OQS");e.exports=function(e,t){if(null==e)return{};var n,i,o=r(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(i=0;i<s.length;i++)n=s[i],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}},QmYb:function(e,t,n){},QxRw:function(e,t,n){},RIqP:function(e,t,n){var r=n("Ijbi"),i=n("EbDI"),o=n("Bnag");e.exports=function(e){return r(e)||i(e)||o()}},SksO:function(e,t){function n(t,r){return e.exports=n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},n(t,r)}e.exports=n},TSYQ:function(e,t,n){
2
+ /*!
3
+ Copyright (c) 2017 Jed Watson.
4
+ Licensed under the MIT License (MIT), see
5
+ http://jedwatson.github.io/classnames
6
+ */
7
+ !function(){"use strict";var t={}.hasOwnProperty;function n(){for(var e=[],r=0;r<arguments.length;r++){var i=arguments[r];if(i){var o=typeof i;if("string"===o||"number"===o)e.push(i);else if(Array.isArray(i)&&i.length){var s=n.apply(null,i);s&&e.push(s)}else if("object"===o)for(var c in i)t.call(i,c)&&i[c]&&e.push(c)}}return e.join(" ")}e.exports?(n.default=n,e.exports=n):"function"==typeof define&&"object"==typeof define.amd&&define.amd?define("classnames",[],function(){return n}):window.classNames=n}()},Td6G:function(e,t,n){"use strict";n("J4zp");var r=n("YLtl"),i=n("RIqP"),o=n.n(i),s=n("lwsE"),c=n.n(s),a=n("W8MJ"),u=n.n(a),l=n("lSNA"),d=n.n(l),p=n("92Nh"),f=n.n(p),h=n("tmk3"),b=n.n(h),m=function(){function e(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;c()(this,e),v.set(this,{writable:!0,value:{}}),g.set(this,{writable:!0,value:{}}),d()(this,"getErrorCodes",function(){return Object.keys(b()(t,v))}),d()(this,"getErrorCode",function(){return t.getErrorCodes()[0]}),d()(this,"getErrorMessages",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;if(e)return b()(t,v)[e];var n=[];for(var r in b()(t,v))b()(t,v).hasOwnProperty(r)&&n.concat(b()(t,v)[r]);return n}),d()(this,"getErrorMessage",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return e=e||t.getErrorCode(),t.getErrorMessages(e)[0]}),d()(this,"getErrorData",function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return e=e||t.getErrorCode(),b()(t,g)[e]}),d()(this,"getAllErrorMessages",function(){var e=[];for(var n in b()(t,v))b()(t,v).hasOwnProperty(n)&&e.push.apply(e,o()(b()(t,v)[n]));return e}),n&&(r&&(b()(this,v)[n]=[r]),i&&(b()(this,g)[n]=i))}return u()(e,null,[{key:"fromPHPObject",value:function(t){var n=new e;return f()(n,v,t.errors),f()(n,g,t.error_data),n}},{key:"fromApiError",value:function(t){var n=new e;if(b()(n,v)[t.code]=[t.message],b()(n,g)[t.code]=t.data,t.additional_errors){var r=!0,i=!1,o=void 0;try{for(var s,c=t.additional_errors[Symbol.iterator]();!(r=(s=c.next()).done);r=!0){var a=s.value;b()(n,v)[a.code]=[a.message],b()(n,g)[a.code]=a.data}}catch(e){i=!0,o=e}finally{try{r||null==c.return||c.return()}finally{if(i)throw o}}}return n}}]),e}(),v=new WeakMap,g=new WeakMap,y=n("a1gu"),O=n.n(y),E=n("Nsbk"),j=n.n(E),_=n("7W2i"),w=n.n(_),S=n("PJYZ"),k=n.n(S),I=n("oShl"),x=n.n(I),N=n("l3Sj"),C=function(e){function t(e){var n,r;c()(this,t);for(var i=arguments.length,o=new Array(i>1?i-1:0),s=1;s<i;s++)o[s-1]=arguments[s];for(var a in r=O()(this,(n=j()(t)).call.apply(n,[this,e.message||Object(N.__)("An unknown error occurred.","better-wp-security")].concat(o))),Error.captureStackTrace&&Error.captureStackTrace(k()(k()(r)),t),r.__response=e,e)e.hasOwnProperty(a)&&Object.defineProperty(k()(k()(r)),a,{value:e[a],configurable:!0,enumerable:!0,writable:!0});return r}return w()(t,e),u()(t,[{key:"toString",value:function(){return this.__response.toString()}},{key:"getResponse",value:function(){return this.__response}}]),t}(x()(Error));function T(e){if(!Object(r.isPlainObject)(e))return!1;var t=Object.keys(e);return 2===t.length&&(t.includes("errors")&&t.includes("error_data"))}function R(e){return T(e)?m.fromPHPObject(e):function(e){if(!Object(r.isPlainObject)(e))return!1;var t=Object.keys(e);return(3===t.length||4===t.length)&&!(4===t.length&&!t.includes("additional_errors"))&&t.includes("code")&&t.includes("message")&&t.includes("data")}(e)?m.fromApiError(e):new m}function L(e,t){var n=[[],[]],r=!0,i=!1,o=void 0;try{for(var s,c=e[Symbol.iterator]();!(r=(s=c.next()).done);r=!0){var a=s.value;n[t(a)?0:1].push(a)}}catch(e){i=!0,o=e}finally{try{r||null==c.return||c.return()}finally{if(i)throw o}}return n}function P(e){if(e instanceof Error)throw e;throw new C(e)}n.d(t,"d",function(){return T}),n.d(t,"b",function(){return R}),n.d(t,"a",function(){return L}),n.d(t,"e",function(){return P}),n.d(t,"c",function(){return G});function G(e,t){if(e&&e.links){var n=!0,r=!1,i=void 0;try{for(var o,s=e.links[Symbol.iterator]();!(n=(o=s.next()).done);n=!0){var c=o.value;if(c.rel===t)return c}}catch(e){r=!0,i=e}finally{try{n||null==s.return||s.return()}finally{if(r)throw i}}}}},W8MJ:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}e.exports=function(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),e}},WbBG:function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},XO7p:function(e,t,n){},Xvx9:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,i=n("cDcd"),o=(r=i)&&r.__esModule?r:{default:r};var s=function(e,t){return e.toString()+"\n\nThis is located at:"+t},c=function(e){var t=e.componentStack,n=e.error;return o.default.createElement("div",{style:a,title:s(n,t)},o.default.createElement("svg",{style:u,viewBox:"0 0 24 24",preserveAspectRatio:"xMidYMid"},o.default.createElement("path",{d:"M20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,\n 12M22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2A10,10 0 0,1 22,\n 12M15.5,8C16.3,8 17,8.7 17,9.5C17,10.3 16.3,11 15.5,11C14.7,11 14,10.3 14,\n 9.5C14,8.7 14.7,8 15.5,8M10,9.5C10,10.3 9.3,11 8.5,11C7.7,11 7,10.3 7,9.5C7,\n 8.7 7.7,8 8.5,8C9.3,8 10,8.7 10,9.5M12,14C13.75,14 15.29,14.72 16.19,\n 15.81L14.77,17.23C14.32,16.5 13.25,16 12,16C10.75,16 9.68,16.5 9.23,\n 17.23L7.81,15.81C8.71,14.72 10.25,14 12,14Z"})))};c.propTypes={componentStack:n("17x9").string.isRequired,error:"function"==typeof Error?n("17x9").instanceOf(Error).isRequired:n("17x9").any.isRequired};var a={height:"100%",maxHeight:"100vh",width:"100%",maxWidth:"100vw",display:"flex",flexDirection:"column",alignItems:"center",textAlign:"center",backgroundColor:"#C00",color:"#FFF",boxSizing:"border-box",cursor:"help"},u={fill:"currentColor",flex:"1 1 auto"};t.default=c},Y0od:function(e,t,n){},Y8OO:function(e,t){!function(){e.exports=this.wp.domReady}()},YLtl:function(e,t){!function(){e.exports=this.lodash}()},ZRc3:function(e,t,n){},a1gu:function(e,t,n){var r=n("cDf5"),i=n("PJYZ");e.exports=function(e,t){return!t||"object"!==r(t)&&"function"!=typeof t?i(e):t}},"bJT+":function(e,t,n){},cDcd:function(e,t){!function(){e.exports=this.React}()},cDf5:function(e,t){function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function r(t){return"function"==typeof Symbol&&"symbol"===n(Symbol.iterator)?e.exports=r=function(e){return n(e)}:e.exports=r=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":n(e)},r(t)}e.exports=r},epig:function(e,t,n){"use strict";n.r(t);var r={};n.r(r),n.d(r,"getUser",function(){return w}),n.d(r,"getIndex",function(){return S}),n.d(r,"getSchema",function(){return k}),n.d(r,"getRoles",function(){return I});var i={};n.r(i),n.d(i,"fetchIndex",function(){return N}),n.d(i,"receiveIndex",function(){return C}),n.d(i,"receiveUser",function(){return T}),n.d(i,"RECEIVE_INDEX",function(){return R}),n.d(i,"RECEIVE_USER",function(){return L});var o={};n.r(o),n.d(o,"getIndex",function(){return G}),n.d(o,"getUser",function(){return A});var s={};n.r(s),n.d(s,"selectGroup",function(){return ze}),n.d(s,"editGroup",function(){return Je}),n.d(s,"saveGroup",function(){return Ye}),n.d(s,"resetEdits",function(){return Ze}),n.d(s,"createGroup",function(){return Xe}),n.d(s,"editGroupSetting",function(){return Qe}),n.d(s,"saveGroupSettings",function(){return et}),n.d(s,"bulkEditGroupSetting",function(){return tt}),n.d(s,"resetBulkGroupSettingEdit",function(){return nt}),n.d(s,"resetBulkGroupSettingEdits",function(){return rt}),n.d(s,"saveBulkEdits",function(){return it}),n.d(s,"SELECT_GROUP",function(){return ot}),n.d(s,"EDIT_GROUP",function(){return st}),n.d(s,"RESET_EDITS",function(){return ct}),n.d(s,"START_SAVE_GROUP",function(){return at}),n.d(s,"FINISH_SAVE_GROUP",function(){return ut}),n.d(s,"FAILED_SAVE_GROUP",function(){return lt}),n.d(s,"START_CREATE_GROUP",function(){return dt}),n.d(s,"FINISH_CREATE_GROUP",function(){return pt}),n.d(s,"FAILED_CREATE_GROUP",function(){return ft}),n.d(s,"EDIT_GROUP_SETTING",function(){return ht}),n.d(s,"START_SAVE_GROUP_SETTINGS",function(){return bt}),n.d(s,"FINISH_SAVE_GROUP_SETTINGS",function(){return mt}),n.d(s,"FAILED_SAVE_GROUP_SETTINGS",function(){return vt}),n.d(s,"BULK_EDIT_GROUP_SETTING",function(){return gt}),n.d(s,"RESET_BULK_GROUP_SETTING_EDIT",function(){return yt}),n.d(s,"RESET_BULK_GROUP_SETTING_EDITS",function(){return Ot});var c={};n.r(c),n.d(c,"getSelectedGroup",function(){return Et}),n.d(c,"isCreating",function(){return jt}),n.d(c,"getEditedGroup",function(){return _t}),n.d(c,"getEditedGroupAttribute",function(){return wt}),n.d(c,"hasEdits",function(){return St}),n.d(c,"settingHasEdits",function(){return kt}),n.d(c,"getEditedGroupSettings",function(){return It}),n.d(c,"getEditedGroupSetting",function(){return xt}),n.d(c,"hasBulkSettingEdits",function(){return Nt}),n.d(c,"getBulkSettingEdits",function(){return Ct}),n.d(c,"getBulkSettingEdit",function(){return Tt}),n.d(c,"getBulkSettingValue",function(){return Rt}),n.d(c,"isSavingBulkEdits",function(){return Lt}),n.d(c,"getAvailableGroups",function(){return Pt});var a={};n.r(a),n.d(a,"getAvailableGroups",function(){return At});var u=n("GRId"),l=n("l3Sj"),d=n("Y8OO"),p=n.n(d),f=(n("onLe"),n("1ZqX")),h=n("RIqP"),b=n.n(h),m=n("MVZn"),v=n.n(m),g=n("YLtl"),y=n("ywyh"),O=n.n(y),E=n("Td6G");function j(e){return{type:"API_FETCH",request:e}}var _={API_FETCH:function(e){var t=e.request;return O()(t).catch(E.e)},SELECT:function(e){var t=e.storeKey,n=e.selectorName,r=e.args,i=Object(f.select)(t)[n];return i.hasResolver?function(e){var t=e.storeKey,n=e.selectorName,r=e.args;return new Promise(function(e){var i=function(){return Object(f.select)("core/data").hasFinishedResolution(t,n,r)},o=function(){return Object(f.select)(t)[n].apply(null,r)},s=o();if(i())return e(s);var c=Object(f.subscribe)(function(){i()&&(c(),e(o()))})})}({storeKey:t,selectorName:n,args:r}):i.apply(void 0,b()(r))},DISPATCH:function(e){var t,n=e.storeKey,r=e.actionName,i=e.args;return(t=Object(f.dispatch)(n))[r].apply(t,b()(i))},CREATE_NOTICE:function(e){var t=e.status,n=e.content,r=e.options;r.autoDismiss&&(r.id=r.id||Object(g.uniqueId)("itsec-auto-dismiss-"),setTimeout(function(){return Object(f.dispatch)("core/notices").removeNotice(r.id,r.context)},r.autoDismiss)),Object(f.dispatch)("core/notices").createNotice(t,n,r)}};function w(e,t){return e.users.byId[t]}function S(e){return e.index}function k(e,t){var n=Object(f.select)("ithemes-security/core").getIndex();if(!n)return null;for(var r in n.routes)if(n.routes.hasOwnProperty(r)){var i=n.routes[r].schema;if(i&&i.title===t)return i}return null}function I(){var e=Object(f.select)("ithemes-security/core").getIndex();return e?e.roles:null}var x=regeneratorRuntime.mark(N);function N(){var e,t,n,r=arguments;return regeneratorRuntime.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:return e=r.length>0&&void 0!==r[0]&&r[0],t="/ithemes-security/v1?context=help",e&&(t+="&_="+Date.now()),i.next=5,j({path:t});case 5:return n=i.sent,i.next=8,C(n);case 8:return i.abrupt("return",n);case 9:case"end":return i.stop()}},x,this)}function C(e){return{type:R,index:e}}function T(e){return{type:L,user:e}}var R="RECEIVE_INDEX",L="RECEIVE_USER",P=regeneratorRuntime.mark(G);function G(){var e;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,j({path:"/ithemes-security/v1?context=help"});case 2:return e=t.sent,t.next=5,C(e);case 5:case"end":return t.stop()}},P,this)}var A={fulfill:regeneratorRuntime.mark(function e(t){var n;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,j({path:"/wp/v2/users/".concat(t)});case 2:return n=e.sent,e.next=5,T(n);case 5:case"end":return e.stop()}},e,this)}),isFulfilled:function(e,t){return!!e.users.byId[t]}},D=n("lSNA"),M=n.n(D),B={users:{byId:{}},index:null};Object(f.registerStore)("ithemes-security/core",{controls:_,selectors:r,resolvers:o,actions:i,reducer:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:B,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case R:return v()({},e,{index:t.index});case L:return v()({},e,{users:v()({},e.users,{byId:v()({},e.users.byId,M()({},t.user.id,t.user))})});default:return e}}});n("sPxh");var U=n("lwsE"),H=n.n(U),V=n("W8MJ"),F=n.n(V),K=n("a1gu"),W=n.n(K),$=n("Nsbk"),q=n.n($),z=n("7W2i"),J=n.n(z),Y=n("PJYZ"),Z=n.n(Y),X=n("tI+e");n("6ECA");function Q(e,t,n){if(e!==t)throw new TypeError("Private static access of wrong provenance");return n.value}u.Component;var ee={writable:!0,value:{}},te=n("TSYQ"),ne=n.n(te),re=n("K9lf");Object(re.compose)([Object(re.withState)({isShowing:!1}),re.withInstanceId])(function(e){var t,n=e.type,r=e.status,i=e.description,o=e.isShowing,s=e.setState,c=e.instanceId,a=e.children;switch(r){case"clean":t=Object(l.__)("Clean","better-wp-security");break;case"warn":t=Object(l.__)("Warn","better-wp-security");break;case"error":t=Object(l.__)("Error","better-wp-security");break;default:t=r}var d=Object(u.createElement)("span",{className:"itsec-malware-scan-".concat(r)},t);return Object(u.createElement)("div",{className:ne()("itsec-malware-scan-results-section","itsec-malware-scan-results-".concat(n,"-section"))},Object(g.isEmpty)(a)?Object(u.createElement)("p",null,d," ",i):Object(u.createElement)(u.Fragment,null,Object(u.createElement)("p",null,d,i,Object(u.createElement)(X.Button,{isLink:!0,className:"itsec-malware-scan-toggle-details",onClick:function(){return s({isShowing:!o})},"aria-expanded":o,"aria-controls":"itsec-malware-scan-details--".concat(c)},o?Object(l.__)("Hide Details","better-wp-security"):Object(l.__)("Show Details","better-wp-security"))),Object(u.createElement)("div",{className:"itsec-malware-scan-details",id:"itsec-malware-scan-details--".concat(c),style:{display:o?"block":"none"}},a)))});var ie=n("J4zp"),oe=n.n(ie);n("rmEH");n("A6yB");function se(e){var t=e.id,n=e.isVisible,r=e.children;return Object(u.createElement)("div",{className:"itsec-site-scan__details",id:t,style:{display:n?"block":"none"}},Object(u.createElement)("ul",null,r))}Object(re.compose)([Object(re.withState)({isShowing:!1}),re.withInstanceId])(function(e){var t,n=e.type,r=e.status,i=e.description,o=e.isShowing,s=e.setState,c=e.instanceId,a=e.children;switch(r){case"clean":t=Object(l.__)("Clean","better-wp-security");break;case"warn":t=Object(l.__)("Warn","better-wp-security");break;case"error":t=Object(l.__)("Error","better-wp-security");break;default:t=r}var d=Object(u.createElement)("span",{className:"itsec-site-scan__status itsec-site-scan__status--".concat(r)},t);return Object(u.createElement)("div",{className:ne()("itsec-site-scan-results-section","itsec-site-scan-results-".concat(n,"-section"))},Object(g.isEmpty)(a)?Object(u.createElement)("p",null,d," ",i):Object(u.createElement)(u.Fragment,null,Object(u.createElement)("p",null,d,i,Object(u.createElement)(X.Button,{isLink:!0,className:"itsec-site-scan-toggle-details",onClick:function(){return s({isShowing:!o})},"aria-expanded":o,"aria-controls":"itsec-site-scan__details--".concat(c)},o?Object(l.__)("Hide Details","better-wp-security"):Object(l.__)("Show Details","better-wp-security"))),Object(u.createElement)(se,{id:"itsec-site-scan__details--".concat(c),isVisible:o},a)))});var ce=n("4eJC"),ae=n.n(ce);ae()(function(e){return b()(e).sort(function(e,t){return"blacklisted"===e.status&&"blacklisted"!==t.status?-1:"blacklisted"!==e.status&&"blacklisted"===t.status?1:0})});n("oaS/");n("bJT+");var ue=n("QILm"),le=n.n(ue),de=n("cDcd"),pe=n("urxu"),fe=n.n(pe),he=Object(de.lazy)(function(){return n.e(8).then(n.t.bind(null,"H0AD",7))});function be(){return Object(u.createElement)("span",null,Object(l.__)("Error when loading. Please refresh.","better-wp-security"))}function me(e){var t=e.addErrorBoundary,n=void 0===t||t,r=le()(e,["addErrorBoundary"]),i=Object(u.createElement)(de.Suspense,{fallback:Object(u.createElement)(X.Spinner,null)},Object(u.createElement)(he,r));return n?Object(u.createElement)(fe.a,{FallbackComponent:be},i):i}var ve=function(){},ge=function(e){function t(e){var n;return H()(this,t),(n=W()(this,q()(t).call(this,e))).state={isHovering:!1},n.onMouseEnter=n.onMouseEnter.bind(Z()(Z()(n))),n.onMouseLeave=n.onMouseLeave.bind(Z()(Z()(n))),n.onMouseOver=n.onMouseOver.bind(Z()(Z()(n))),n.onMouseOut=n.onMouseOut.bind(Z()(Z()(n))),n.setIsHovering=n.setIsHovering.bind(Z()(Z()(n))),n.unsetIsHovering=n.unsetIsHovering.bind(Z()(Z()(n))),n.componentWillUnmount=n.componentWillUnmount.bind(Z()(Z()(n))),n.timerIds=[],n}return J()(t,e),F()(t,[{key:"onMouseEnter",value:function(e){this.props.onMouseEnter({e:e,setIsHovering:this.setIsHovering,unsetIsHovering:this.unsetIsHovering})}},{key:"onMouseLeave",value:function(e){this.props.onMouseLeave({e:e,setIsHovering:this.setIsHovering,unsetIsHovering:this.unsetIsHovering})}},{key:"onMouseOver",value:function(e){this.props.onMouseOver({e:e,setIsHovering:this.setIsHovering,unsetIsHovering:this.unsetIsHovering})}},{key:"onMouseOut",value:function(e){this.props.onMouseOut({e:e,setIsHovering:this.setIsHovering,unsetIsHovering:this.unsetIsHovering})}},{key:"componentWillUnmount",value:function(){this.clearTimers()}},{key:"setIsHovering",value:function(){var e=this;this.clearTimers();var t=setTimeout(function(){var t={isHovering:!0};e.setState(t,function(){e.props.onHoverChanged(t)})},this.props.hoverDelayInMs);this.timerIds.push(t)}},{key:"unsetIsHovering",value:function(){var e=this;this.clearTimers();var t=setTimeout(function(){var t={isHovering:!1};e.setState(t,function(){e.props.onHoverChanged(t)})},this.props.hoverOffDelayInMs);this.timerIds.push(t)}},{key:"clearTimers",value:function(){for(var e=this.timerIds;e.length;)clearTimeout(e.pop())}},{key:"render",value:function(){var e=this.props,t=e.children,n=e.className;return Object(u.createElement)("div",{className:n,onMouseEnter:this.onMouseEnter,onMouseLeave:this.onMouseLeave,onMouseOver:this.onMouseOver,onMouseOut:this.onMouseOut},t)}}]),t}(u.Component);M()(ge,"displayName","HoverDetector"),M()(ge,"defaultProps",{hoverDelayInMs:0,hoverOffDelayInMs:0,onHoverChanged:ve,onMouseEnter:function(e){return(0,e.setIsHovering)()},onMouseLeave:function(e){return(0,e.unsetIsHovering)()},onMouseOver:ve,onMouseOut:ve,shouldDecorateChildren:!0});n("QxRw");n("JVTk"),n("lSb6");var ye=n("pVnL"),Oe=n.n(ye);n("Dfre");var Ee=Symbol.iterator,je=function(){function e(t,n,r){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:[];H()(this,e),M()(this,"tree",void 0),M()(this,"name",void 0),M()(this,"data",void 0),M()(this,"parent",void 0),M()(this,"children",void 0),this.tree=t,this.name=n,this.data=r,this.parent=i,this.children=o}return F()(e,[{key:"getParent",value:function(){return this.parent?this.tree.nodes[this.parent]:null}},{key:"getAllParents",value:function(){for(var e=[],t=this.getParent();t;)e.push(t.name),t=t.getParent();return e}},{key:"hasChildren",value:function(){return this.children.length>0}},{key:"getAllChildren",value:function(){var e=[];if(!this.hasChildren())return e;var t=!0,n=!1,r=void 0;try{for(var i,o=this[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var s=i.value;e.push.apply(e,[s.name].concat(b()(s.getAllChildren())))}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}return e}},{key:Ee,value:regeneratorRuntime.mark(function e(){var t,n;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=0;case 1:if(!(t<this.children.length)){e.next=8;break}return n=this.children[t],e.next=5,this.tree.nodes[n];case 5:t++,e.next=1;break;case 8:case"end":return e.stop()}},e,this)})}]),e}(),_e=Symbol.iterator,we=function(){function e(){H()(this,e),M()(this,"nodes",{}),M()(this,"ordered",[])}return F()(e,[{key:"add",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.ordered.push(e),this.nodes[e]?(this.nodes[e].data=t,this.nodes[e].parent=n):this.nodes[e]=new je(this,e,t,n),n&&(this.nodes[n]?this.nodes[n].children.push(e):this.nodes[n]=new je(this,n))}},{key:_e,value:regeneratorRuntime.mark(function e(){var t,n;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=0;case 1:if(!(t<this.ordered.length)){e.next=9;break}if(n=this.ordered[t],this.nodes[n].parent){e.next=6;break}return e.next=6,this.nodes[n];case 6:t++,e.next=1;break;case 9:case"end":return e.stop()}},e,this)})}]),e}(),Se=ae()(function(e){var t=new we,n=!0,r=!1,i=void 0;try{for(var o,s=e[Symbol.iterator]();!(n=(o=s.next()).done);n=!0){var c=o.value;t.add(c.value,c,c.parent)}}catch(e){r=!0,i=e}finally{try{n||null==s.return||s.return()}finally{if(r)throw i}}return t}),ke=function(e){function t(){var e;return H()(this,t),e=W()(this,q()(t).apply(this,arguments)),M()(Z()(Z()(e)),"props",void 0),e.renderOption=e.renderOption.bind(Z()(Z()(e))),e.isChecked=e.isChecked.bind(Z()(Z()(e))),e.isIndeterminate=e.isIndeterminate.bind(Z()(Z()(e))),e.onChange=e.onChange.bind(Z()(Z()(e))),e}return J()(t,e),F()(t,[{key:"indeterminate",value:function(e){e.indeterminate=!0}},{key:"isChecked",value:function(e){return!!e&&(Object(g.isArray)(this.props.value)?this.props.value.includes(e.name)||this.isChecked(e.getParent()):this.props.value[e.name]||this.isChecked(e.getParent()))}},{key:"isIndeterminate",value:function(e){if(!e.hasChildren())return!1;var t=!0,n=!1,r=void 0;try{for(var i,o=e[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var s=i.value;if(this.isChecked(s))return!0;if(this.isIndeterminate(s))return!0}}catch(e){n=!0,r=e}finally{try{t||null==o.return||o.return()}finally{if(n)throw r}}return!1}},{key:"onChange",value:function(e,t){var n,r=[e.name].concat(b()(e.getAllChildren())),i=t?[]:e.getAllParents();Object(g.isArray)(this.props.value)?(n=t?b()(this.props.value).concat(b()(r)):this.props.value.filter(function(e){return!r.includes(e)&&!i.includes(e)}),this.props.onChange(n)):this.props.onChange(v()({},this.props.value,r.reduce(function(e,n){return e[n]=t},{}),i.reduce(function(e,t){return e[t]=!1},{})))}},{key:"render",value:function(){var e=this.props,t=e.label,n=e.help,r=e.options,i=Se(r);return Object(u.createElement)("fieldset",{className:"components-base-control"},Object(u.createElement)("div",{className:"components-base-control__field"},Object(u.createElement)("legend",{className:"components-base-control__label"},t),n&&Object(u.createElement)("p",{className:"components-base-control__help"},n)),Object(u.createElement)("ul",{className:"components-hierarchical-checkbox-control__group"},Array.from(i,this.renderOption)))}},{key:"renderOption",value:function(e){var t=this,n=e.data,r=n.value,i=n.selectable,o=void 0===i||i,s=le()(n,["value","selectable"]),c=this.isChecked(e),a=!c&&this.isIndeterminate(e);return Object(u.createElement)("li",{key:r,className:ne()("components-hierarchical-checkbox-control__option",{"components-hierarchical-checkbox-control__option--has-children":e.hasChildren()})},Object(u.createElement)(Ce,Oe()({},s,{checked:!!o&&c,disabled:!o||this.props.disabled,indeterminate:a,onChange:function(n){return t.onChange(e,n)}})),e.hasChildren()&&Object(u.createElement)("ul",{className:"components-hierarchical-checkbox-control__group"},Array.from(e,this.renderOption)))}}]),t}(u.Component);Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.context,r=void 0===n?"ithemes-security":n;return{notices:e("core/notices").getNotices(r)}}),Object(f.withDispatch)(function(e,t){var n=t.context,r=void 0===n?"ithemes-security":n;return{onRemove:function(t){return e("core/notices").removeNotice(t,r)}}})])(function(e){var t=e.notices,n=e.onRemove,r=Object(g.filter)(t,function(e){return e.isDismissible&&(!e.type||"default"===e.type)}),i=Object(g.filter)(t,function(e){return!(e.isDismissible||e.type&&"default"!==e.type)}),o=X.SnackbarList?Object(g.filter)(t,{type:"snackbar"}):[];return Object(u.createElement)(u.Fragment,null,Object(u.createElement)(X.NoticeList,{notices:i,className:"components-editor-notices__pinned"}),Object(u.createElement)(X.NoticeList,{notices:r,className:"components-editor-notices__dismissible",onRemove:n}),X.SnackbarList&&Object(u.createElement)(X.SnackbarList,{notices:o,className:"components-editor-notices__snackbar",onRemove:n}))});var Ie=function(e){var t=e.className,n=e.status,r=e.children,i=e.onRemove,o=void 0===i?g.noop:i,s=e.isDismissible,c=void 0===s||s,a=e.actions,d=void 0===a?[]:a,p=ne()(t,"notice","notice-alt","notice-"+n,{"is-dismissible":c});return Object(u.createElement)("div",{className:p},Object(u.createElement)("p",null,r,d.map(function(e,t){var n=e.className,r=e.label,i=e.onClick,s=e.url,c=e.isLink,a=void 0!==c&&c;return Object(u.createElement)(X.Button,{key:t,href:s,isSmall:!a&&!s,isLink:a||s,onClick:s?void 0:function(){o(),i()},className:ne()("notice__action",n)},r)})),c&&Object(u.createElement)("button",{type:"button",className:"notice-dismiss",onClick:o},Object(u.createElement)("span",{className:"screen-reader-text"},Object(l.__)("Dismiss this notice","better-wp-security"))))};n("+VN0");var xe=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.context,r=void 0===n?"ithemes-security":n;return{notices:e("core/notices").getNotices(r)}}),Object(f.withDispatch)(function(e,t){var n=t.context,r=void 0===n?"ithemes-security":n;return{onRemove:function(t){return e("core/notices").removeNotice(t,r)}}})])(function(e){var t,n,r=e.notices,i=e.onRemove,o=function(e){if(!X.SnackbarList)return e.length;var t=0,n=!0,r=!1,i=void 0;try{for(var o,s=e[Symbol.iterator]();!(n=(o=s.next()).done);n=!0)"snackbar"!==o.value.type&&t++}catch(e){r=!0,i=e}finally{try{n||null==s.return||s.return()}finally{if(r)throw i}}return t}(r),s=(t=o,n=Object(u.useRef)(),Object(u.useEffect)(function(){n.current=t}),n.current);Object(u.useEffect)(function(){o>s&&window.itsecSettingsPage&&window.itsecSettingsPage.scrollTop()},[o,s]);var c=X.SnackbarList?Object(g.filter)(r,{type:"snackbar"}):[];return Object(u.createElement)(u.Fragment,null,Object(u.createElement)("div",{className:"itsec-module-settings-notice-list"},r.map(function(e){return"snackbar"===e.type&&X.SnackbarList?null:Object(u.createElement)(Ie,Oe()({},Object(g.omit)(e,["content"]),{key:e.id,onRemove:(t=e.id,function(){return i(t)})}),e.content);var t})),X.SnackbarList&&Object(u.createElement)(X.SnackbarList,{notices:c,className:"components-editor-notices__snackbar",onRemove:i}))}),Ne=(n("DfSy"),function(e){function t(){var e;return H()(this,t),(e=W()(this,q()(t).apply(this,arguments))).onChange=e.onChange.bind(Z()(Z()(e))),e}return J()(t,e),F()(t,[{key:"onChange",value:function(e){this.props.onChange&&this.props.onChange(e.target.checked)}},{key:"render",value:function(){var e,t,n=this.props,r=n.label,i=n.checked,o=n.help,s=n.instanceId,c=le()(n,["label","checked","help","instanceId"]),a="inspector-toggle-control-".concat(s);return o&&(e=a+"__help",t=Object(g.isFunction)(o)?o(i):o),Object(u.createElement)(X.BaseControl,{id:a,help:t,className:"components-toggle-control"},Object(u.createElement)(X.FormToggle,Oe()({},c,{id:a,checked:i,onChange:this.onChange,"aria-describedby":e})),Object(u.createElement)("label",{htmlFor:a,className:"components-toggle-control__label"},r))}}]),t}(u.Component));Object(re.withInstanceId)(Ne),n("IXp0");var Ce=Object(re.withInstanceId)(function(e){var t=e.label,n=e.className,r=e.heading,i=e.checked,o=e.help,s=e.instanceId,c=e.onChange,a=e.indeterminate,l=le()(e,["label","className","heading","checked","help","instanceId","onChange","indeterminate"]),d="inspector-checkbox-control-".concat(s);return Object(u.createElement)(X.BaseControl,{label:r,id:d,help:o,className:n},Object(u.createElement)("span",{className:"components-checkbox-control__input-container"},Object(u.createElement)("input",Oe()({id:d,className:"components-checkbox-control__input",type:"checkbox",value:"1",onChange:function(e){return c(e.target.checked)},checked:i,"aria-describedby":o?d+"__help":void 0,ref:function(e){e&&(e.indeterminate=a)}},l)),i?Object(u.createElement)(X.Dashicon,{icon:"yes",className:"components-checkbox-control__checked",role:"presentation"}):null,a?Object(u.createElement)(X.Dashicon,{icon:"minus",className:"components-checkbox-control__checked components-checkbox-control__checked--indeterminate",role:"presentation"}):null),Object(u.createElement)("label",{className:"components-checkbox-control__label",htmlFor:d},t))});function Te(e){var t=e.tabId,n=e.onClick,r=e.children,i=e.selected,o=le()(e,["tabId","onClick","children","selected"]);return Object(u.createElement)(X.Button,Oe()({role:"tab",tabIndex:i?null:-1,"aria-selected":i,id:t,onClick:n},o),r)}var Re=function(e){function t(){var e;return H()(this,t),(e=W()(this,q()(t).apply(this,arguments))).handleClick=e.handleClick.bind(Z()(Z()(e))),e.onNavigate=e.onNavigate.bind(Z()(Z()(e))),e.onKeyDown=e.onKeyDown.bind(Z()(Z()(e))),e}return J()(t,e),F()(t,[{key:"handleClick",value:function(e){var t=this.props.onSelect;(void 0===t?g.noop:t)(e)}},{key:"onNavigate",value:function(e,t){var n=this.event;n&&"tab"===n.target.getAttribute("role")&&n.preventDefault(),t.click()}},{key:"onKeyDown",value:function(e){this.event=e}},{key:"render",value:function(){var e=this,t=this.props,n=t.activeClass,r=void 0===n?"is-active":n,i=t.className,o=t.instanceId,s=t.orientation,c=void 0===s?"horizontal":s,a=t.tabs,l=t.selected,d=Object(g.find)(a,{name:l})||a[0],p=o+"-"+d.name;return Object(u.createElement)("div",{className:i},Object(u.createElement)(X.NavigableMenu,{role:"tablist",orientation:c,onNavigate:this.onNavigate,onKeyDown:this.onKeyDown,className:"components-tab-panel__tabs"},a.map(function(t){return Object(u.createElement)(Te,{className:ne()(t.className,M()({},r,t.name===d.name)),tabId:o+"-"+t.name,"aria-controls":o+"-"+t.name+"-view",selected:t.name===d.name,key:t.name,onClick:Object(g.partial)(e.handleClick,t.name)},t.title)})),d&&Object(u.createElement)("div",{"aria-labelledby":p,role:"tabpanel",id:p+"-view",className:"components-tab-panel__tab-content",tabIndex:"0"},this.props.children(d)))}}]),t}(u.Component),Le=Object(re.withInstanceId)(Re),Pe=n("rl8x"),Ge=n.n(Pe);Object(re.createHigherOrderComponent)(function(e){var t,n;return n=t=function(t){function n(){var e,t;H()(this,n);for(var r=arguments.length,i=new Array(r),o=0;o<r;o++)i[o]=arguments[o];return t=W()(this,(e=q()(n)).call.apply(e,[this].concat(i))),M()(Z()(Z()(t)),"state",{width:1280}),M()(Z()(Z()(t)),"mounted",!1),M()(Z()(Z()(t)),"ref",null),M()(Z()(Z()(t)),"onWindowResize",function(){if(t.mounted){var e=Object(u.findDOMNode)(Z()(Z()(t)));if(e instanceof window.HTMLElement){var n=e.offsetWidth;t.setState({width:n})}}}),t}return J()(n,t),F()(n,[{key:"componentDidMount",value:function(){this.mounted=!0,window.addEventListener("resize",this.onWindowResize),document.getElementById("collapse-button").addEventListener("click",this.onWindowResize),this.onWindowResize()}},{key:"componentWillUnmount",value:function(){this.mounted=!1,window.removeEventListener("resize",this.onWindowResize),document.getElementById("collapse-button").removeEventListener("click",this.onWindowResize)}},{key:"render",value:function(){var t=this.props,n=t.measureBeforeMount,r=le()(t,["measureBeforeMount"]);return n&&!this.mounted?Object(u.createElement)("div",{className:this.props.className,style:this.props.style}):Object(u.createElement)(e,Oe()({},r,{width:this.state.width+20}))}}]),n}(u.Component),M()(t,"defaultProps",{measureBeforeMount:!1}),n},"withWidth");var Ae=Object(re.createHigherOrderComponent)(function(e){return function(t){function n(){var e;return H()(this,n),e=W()(this,q()(n).apply(this,arguments)),M()(Z()(Z()(e)),"state",{pressed:{shift:!1,ctrl:!1,meta:!1,alt:!1}}),M()(Z()(Z()(e)),"mounted",!1),e.listener=e.listener.bind(Z()(Z()(e))),e.onBlur=e.onBlur.bind(Z()(Z()(e))),e}return J()(n,t),F()(n,[{key:"componentDidMount",value:function(){this.mounted=!0,window.addEventListener("keydown",this.listener),window.addEventListener("keyup",this.listener),window.addEventListener("click",this.listener),window.addEventListener("blur",this.onBlur)}},{key:"componentWillUnmount",value:function(){this.mounted=!1,window.removeEventListener("keydown",this.listener),window.removeEventListener("keyup",this.listener),window.removeEventListener("click",this.listener),window.removeEventListener("blur",this.onBlur)}},{key:"listener",value:function(e){this.mounted&&this.setState({pressed:{shift:e.shiftKey,ctrl:e.ctrlKey,meta:e.metaKey,alt:e.altKey}})}},{key:"onBlur",value:function(){this.setState({pressed:{shift:!1,ctrl:!1,meta:!1,alt:!1}})}},{key:"render",value:function(){return Object(u.createElement)(e,Oe()({pressedModifierKeys:this.state.pressed},this.props))}}]),n}(u.Component)},"withPressedModifierKeys"),De=function(e){function t(){var e;return H()(this,t),(e=W()(this,q()(t).apply(this,arguments))).handleClick=e.handleClick.bind(Z()(Z()(e))),e.onNavigate=e.onNavigate.bind(Z()(Z()(e))),e.onKeyDown=e.onKeyDown.bind(Z()(Z()(e))),e.toggleTab=e.toggleTab.bind(Z()(Z()(e))),e.getSelectedTabs=e.getSelectedTabs.bind(Z()(Z()(e))),e.isSelected=e.isSelected.bind(Z()(Z()(e))),e.getSelectedId=e.getSelectedId.bind(Z()(Z()(e))),e.getLabelledBy=e.getLabelledBy.bind(Z()(Z()(e))),e.getTabId=e.getTabId.bind(Z()(Z()(e))),e.getTabPanelId=e.getTabPanelId.bind(Z()(Z()(e))),e.isTabDisabled=e.isTabDisabled.bind(Z()(Z()(e))),e.isNonMultiSelectableTabSelected=e.isNonMultiSelectableTabSelected.bind(Z()(Z()(e))),e}return J()(t,e),F()(t,[{key:"handleClick",value:function(e,t){t.metaKey||t.ctrlKey?this.toggleTab(e):this.props.onSelect([e])}},{key:"onNavigate",value:function(e,t){var n=this.event;if(n){if("tab"===n.target.getAttribute("role")&&n.preventDefault(),n.ctrlKey)return;if(n.shiftKey){if(this.isTabDisabled(this.props.tabs[e]))return;var r=this.props.tabs[e].name;return void this.toggleTab(r)}}t.click()}},{key:"onKeyDown",value:function(e){if(!e.nativeEvent&&(this.event=e,e.ctrlKey&&("Space"===e.code||32===e.keyCode))){e.preventDefault();var t=e.target.dataset.tabname;t&&this.toggleTab(t)}}},{key:"toggleTab",value:function(e){var t=Object(g.find)(this.props.tabs,{name:e});t&&!1===t.allowMultiple||(this.props.selected.includes(e)?this.props.onSelect(this.props.selected.filter(function(t){return t!==e})):this.props.onSelect(b()(this.props.selected).concat([e])))}},{key:"getSelectedTabs",value:function(){var e=this,t=this.props.selected;!t.length&&this.props.initialTab&&t.push(this.props.initialTab);var n=[];return this.props.tabs.forEach(function(t){e.props.selected.includes(t.name)&&n.push(t)}),n}},{key:"isSelected",value:function(e,t){return e.some(function(e){return e.name===t.name})}},{key:"isTabDisabled",value:function(e){var t=this.props.pressedModifierKeys;if(this.props.selected.includes(e.name))return!1;if(!1!==e.allowMultiple&&!this.isNonMultiSelectableTabSelected())return!1;if(t.meta||t.ctrl)return!0;if(t.shift){var n=document.activeElement;if(n.parentElement&&n.parentElement.id==="components-tab-panel__tabs-".concat(this.props.instanceId))return!0}return!1}},{key:"isNonMultiSelectableTabSelected",value:function(){if(1!==this.props.selected.length)return!1;var e=Object(g.find)(this.props.tabs,{name:this.props.selected[0]});return e&&!1===e.allowMultiple}},{key:"getSelectedId",value:function(e){return 1===e.length?this.getTabPanelId(e[0].name):"components-tab-panel__panel-".concat(this.props.instanceId,"-").concat(Object(g.map)(e,"name").join("-"))}},{key:"getLabelledBy",value:function(e){var t=this;return e.map(function(e){return t.getTabId(e.name)}).join(",")}},{key:"getTabId",value:function(e){return"components-tab-panel__tab-".concat(this.props.instanceId,"-").concat(e)}},{key:"getTabPanelId",value:function(e){return"components-tab-panel__panel-".concat(this.props.instanceId,"-").concat(e)}},{key:"componentDidUpdate",value:function(e){if(1===this.props.selected.length&&Ge()(this.props.selected,e.selected)){var t=this.props.selected[0];if(!Object(g.find)(this.props.tabs,{name:t})){var n=Object(g.findIndex)(e.tabs,{name:t});if(-1!==n){var r=Math.max(n-1,0),i=this.props.tabs[r];i&&this.props.onSelect([i.name])}}}}},{key:"render",value:function(){var e=this,t=this.props,n=t.tabs,r=t.className,i=t.activeClass,o=void 0===i?"is-active":i,s=t.orientation,c=void 0===s?"horizontal":s,a=this.getSelectedTabs(),l=this.getSelectedId(a);return Object(u.createElement)("div",{className:r},Object(u.createElement)(X.NavigableMenu,{role:"tablist","aria-multiselectable":!0,orientation:c,onNavigate:this.onNavigate,onKeyDown:this.onKeyDown,className:"components-tab-panel__tabs",id:"components-tab-panel__tabs-".concat(this.props.instanceId)},n.map(function(t){var n=e.isSelected(a,t),r=n&&a.length>1?l:e.getTabPanelId(t.name);return Object(u.createElement)(Te,{className:ne()(t.className,M()({},o,n)),tabId:e.getTabId(t.name),"aria-controls":r,selected:n,disabled:e.isTabDisabled(t),key:t.name,onClick:Object(g.partial)(e.handleClick,t.name),"data-tabname":t.name},t.title)})),a.length>0&&Object(u.createElement)("div",{"aria-labelledby":this.getLabelledBy(a),role:"tabpanel",id:l,className:"components-tab-panel__tab-content",tabIndex:"0"},this.props.children(a)))}}]),t}(u.Component),Me=Object(re.compose)([re.withInstanceId,Ae])(De),Be=function(e){function t(){var e;return H()(this,t),e=W()(this,q()(t).apply(this,arguments)),M()(Z()(Z()(e)),"onSelect",function(t){e.setState({selected:t})}),e.state={selected:e.props.initialTab||""},e}return J()(t,e),F()(t,[{key:"render",value:function(){return Object(u.createElement)(Le,Oe()({},this.props,{selected:this.state.selected,onSelect:this.onSelect}))}}]),t}(u.Component);function Ue(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),i=2;i<n;i++)r[i-2]=arguments[i];return{type:"SELECT",storeKey:e,selectorName:t,args:r}}function He(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),i=2;i<n;i++)r[i-2]=arguments[i];return{type:"DISPATCH",storeKey:e,actionName:t,args:r}}function Ve(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"info",t=arguments.length>1?arguments[1]:void 0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return{type:"CREATE_NOTICE",status:e,content:t,options:v()({context:"ithemes-security"},n)}}var Fe={API_FETCH:function(e){var t=e.request;return O()(t).catch(E.e)},SELECT:function(e){var t,n=e.storeKey,r=e.selectorName,i=e.args;return(t=Object(f.select)(n))[r].apply(t,b()(i))},DISPATCH:function(e){var t,n=e.storeKey,r=e.actionName,i=e.args;return(t=Object(f.dispatch)(n))[r].apply(t,b()(i))},CREATE_NOTICE:function(e){var t=e.status,n=e.content,r=e.options;r.autoDismiss&&(r.id=r.id||Object(g.uniqueId)("itsec-auto-dismiss-"),setTimeout(function(){return Object(f.dispatch)("core/notices").removeNotice(r.id,r.context)},r.autoDismiss)),Object(f.dispatch)("core/notices").createNotice(t,n,r)}},Ke=regeneratorRuntime.mark(Ye),We=regeneratorRuntime.mark(Xe),$e=regeneratorRuntime.mark(et),qe=regeneratorRuntime.mark(it);function ze(e){return{type:ot,ids:e}}function Je(e,t){return{type:st,id:e,edit:t}}function Ye(e){var t,n;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,Ue("ithemes-security/user-groups-editor","getEditedGroup",e);case 2:if(t=r.sent){r.next=5;break}return r.abrupt("return");case 5:return r.next=7,{type:at,id:e};case 7:return r.next=9,He("ithemes-security/user-groups","updateGroup",e,t);case 9:if(!((n=r.sent)instanceof Error)){r.next=17;break}return r.next=13,Ve("error",n.message);case 13:return r.next=15,{type:lt,id:e,error:n};case 15:r.next=21;break;case 17:return r.next=19,{type:ut,id:e,updated:n};case 19:return r.next=21,Ve("success",Object(l.__)("Updated group.","better-wp-security"),{type:"snackbar"});case 21:return r.abrupt("return",n);case 22:case"end":return r.stop()}},Ke,this)}function Ze(e){return{type:ct,id:e}}function Xe(){var e,t,n,r=arguments;return regeneratorRuntime.wrap(function(i){for(;;)switch(i.prev=i.next){case 0:return e=r.length>0&&void 0!==r[0]?r[0]:{},i.next=3,Ue("ithemes-security/user-groups-editor","getEditedGroup","new");case 3:if(t=i.sent){i.next=6;break}return i.abrupt("return");case 6:return i.next=8,{type:dt};case 8:return i.next=10,He("ithemes-security/user-groups","createGroup",v()({},t,e));case 10:if(!((n=i.sent)instanceof Error)){i.next=23;break}if("rest_duplicate_user_group"!==n.code){i.next=17;break}return i.next=15,Ve("error",n.message,{actions:[{label:Object(l.__)("View Duplicate","better-wp-security"),isLink:!0,onClick:function(){var e=Object(g.get)(n,["_links","duplicate",0,"href"]).split("/").pop();Object(f.dispatch)("ithemes-security/user-groups-editor").selectGroup([e])}},{label:Object(l.__)("Create Anyway","better-wp-security"),onClick:function(){Object(f.dispatch)("ithemes-security/user-groups-editor").createGroup({ignore_duplicate:!0})}}]});case 15:i.next=19;break;case 17:return i.next=19,Ve("error",n.message);case 19:return i.next=21,{type:ft,error:n};case 21:i.next=31;break;case 23:return i.next=25,Ze("new");case 25:return i.next=27,{type:pt,created:n};case 27:return i.next=29,He("ithemes-security/user-groups-editor","selectGroup",n.id);case 29:return i.next=31,Ve("success",Object(l.__)("Created group.","better-wp-security"),{type:"snackbar"});case 31:return i.abrupt("return",n);case 32:case"end":return i.stop()}},We,this)}function Qe(e,t,n,r){return{type:ht,id:e,module:t,setting:n,value:r}}function et(e){var t,n;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return r.next=2,Ue("ithemes-security/user-groups-editor","getEditedGroupSettings",e);case 2:if(t=r.sent){r.next=5;break}return r.abrupt("return");case 5:return r.next=7,{type:bt,id:e};case 7:return r.next=9,He("ithemes-security/user-groups","updateGroupSettings",e,t);case 9:if(!((n=r.sent)instanceof Error)){r.next=17;break}return r.next=13,Ve("error",n.message);case 13:return r.next=15,{type:vt,id:e,error:n};case 15:r.next=21;break;case 17:return r.next=19,{type:mt,id:e,updated:n};case 19:return r.next=21,Ve("success",Object(l.__)("Updated group settings.","better-wp-security"),{type:"snackbar"});case 21:return r.abrupt("return",n);case 22:case"end":return r.stop()}},$e,this)}function tt(e,t,n){return{type:gt,module:e,setting:t,value:n}}function nt(e,t){return{type:yt,module:e,setting:t}}function rt(){return{type:Ot}}function it(e){var t,n,r,i;return regeneratorRuntime.wrap(function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,Ue("ithemes-security/user-groups-editor","getBulkSettingEdits");case 2:return t=o.sent,o.next=5,He("ithemes-security/user-groups","patchBulkGroupSettings",e,t);case 5:if(!((n=o.sent)instanceof Error)){o.next=11;break}return o.next=9,Ve("error",n.message);case 9:o.next=25;break;case 11:if(!Object(g.map)(n,"status").every(function(e){return 200===e})){o.next=16;break}return o.next=14,Ve("success",Object(l.__)("Updated group settings.","better-wp-security"),{type:"snackbar"});case 14:o.next=25;break;case 16:if(r=n.filter(function(e){return 200!==e.status}).map(function(e){var t=e.error;return Object(E.b)(t)}),i=r.map(function(e){return e.getAllErrorMessages().join(" ")}).join(" "),r.length!==n.length){o.next=23;break}return o.next=21,Ve("error",i);case 21:o.next=25;break;case 23:return o.next=25,Ve("warning",Object(l.sprintf)(Object(l._n)("%1$d group was not updated: %2$s","%1$d groups were not updated: %2$s",r.length,"better-wp-security"),r.length,i));case 25:return o.next=27,rt();case 27:return o.abrupt("return",n);case 28:case"end":return o.stop()}},qe,this)}var ot="SELECT_GROUP",st="EDIT_GROUP",ct="RESET_EDITS",at="START_SAVE_GROUP",ut="FINISH_SAVE_GROUP",lt="FAILED_SAVE_GROUP",dt="START_CREATE_GROUP",pt="FINISH_CREATE_GROUP",ft="FAILED_CREATE_GROUP",ht="EDIT_GROUP_SETTING",bt="START_SAVE_GROUP_SETTINGS",mt="FINISH_SAVE_GROUP_SETTINGS",vt="FAILED_SAVE_GROUP_SETTINGS",gt="BULK_EDIT_GROUP_SETTING",yt="RESET_BULK_GROUP_SETTING_EDIT",Ot="RESET_BULK_GROUP_SETTING_EDITS";function Et(e){return e.selectedGroup}function jt(e){return e.creating}function _t(e,t){return e.edits[t]}function wt(e,t,n){var r=Object(g.get)(e,["edits",t,n]);return void 0!==r?r:"new"!==t?Object(f.select)("ithemes-security/user-groups").getGroupAttribute(t,n):void 0}function St(e,t){return!!e.edits[t]}function kt(e,t){return!!e.settingEdits[t]}function It(e,t){return e.settingEdits[t]}function xt(e,t,n,r){var i=Object(g.get)(e,["settingEdits",t,n,r]);return void 0!==i?i:Object(f.select)("ithemes-security/user-groups").getGroupSetting(t,n,r)}function Nt(e){return!Object(g.isEmpty)(e.bulkSettingEdits)}function Ct(e){return e.bulkSettingEdits}function Tt(e,t,n){return Object(g.get)(e,["bulkSettingEdits",t,n])}function Rt(e,t,n,r){var i=Tt(e,n,r);if(void 0!==i)return i;var o=function(e){return Object(f.select)("ithemes-security/user-groups").getGroupSetting(e,n,r)},s=o(t[0]);return t.every(function(e){return o(e)===s})?s:null}function Lt(e,t){var n=Ct(e);return Object(f.select)("ithemes-security/user-groups").isBulkPatchingSettings(t,n)}function Pt(){return Object(f.select)("ithemes-security/user-groups").getGroups("available")}var Gt=regeneratorRuntime.mark(At);function At(){var e;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,He("ithemes-security/user-groups","query","available",{_embed:1});case 2:if(!((e=t.sent).length>0)){t.next=6;break}return t.next=6,He("ithemes-security/user-groups-editor","selectGroup",[e[0].id]);case 6:return t.abrupt("return",e);case 7:case"end":return t.stop()}},Gt,this)}var Dt={edits:{},settingEdits:{},bulkSettingEdits:{},creating:!1,selectedGroup:[]};Object(f.registerStore)("ithemes-security/user-groups-editor",{controls:Fe,actions:s,selectors:c,resolvers:a,reducer:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Dt,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case ot:return v()({},e,{selectedGroup:t.ids});case st:return v()({},e,{edits:v()({},e.edits,M()({},t.id,v()({},e.edits[t.id]||{},t.edit)))});case ut:return v()({},e,{edits:Object(g.omit)(e.edits,[t.id])});case ht:return v()({},e,{settingEdits:v()({},e.settingEdits,M()({},t.id,v()({},Object(g.get)(e,["settingEdits",t.id],{}),M()({},t.module,v()({},Object(g.get)(e,["settingEdits",t.id,t.module],{}),M()({},t.setting,t.value))))))});case mt:return v()({},e,{settingEdits:Object(g.omit)(e.settingEdits,[t.id])});case dt:return v()({},e,{creating:!0});case ft:case pt:return v()({},e,{creating:!1});case ct:return v()({},e,{edits:Object(g.omit)(e.edits,[t.id])});case gt:return v()({},e,{bulkSettingEdits:v()({},e.bulkSettingEdits,M()({},t.module,v()({},e.bulkSettingEdits[t.module]||{},M()({},t.setting,t.value))))});case yt:return v()({},e,{bulkSettingEdits:Object(g.omit)(e.bulkSettingEdits,"".concat(t.module,".").concat(t.setting))});case Ot:return v()({},e,{bulkSettingEdits:{}});default:return e}}});function Mt(){return Object(u.createElement)("div",null,Object(u.createElement)(Ut,null))}n("GjY+");var Bt=ae()(function(e){return e.sort(function(e,t){return e.type===t.type?0:"user-group"===e.type?-1:"user-group"===t.type?1:0}).map(function(e){return{name:e.id,title:e.label,className:"itsec-user-groups-list__item",group:e}}).concat({name:"new",title:Object(u.createElement)(u.Fragment,null,Object(u.createElement)(X.Icon,{icon:"plus"}),Object(l.__)("New Group","better-wp-security")),className:"itsec-user-groups-list__item itsec-user-groups-list__item--new",allowMultiple:!1})});var Ut=Object(re.compose)([Object(f.withSelect)(function(e){return{matchables:e("ithemes-security/user-groups").getMatchables(),resolvingMatchables:e("core/data").isResolving("ithemes-security/user-groups","getMatchables"),selectedGroup:e("ithemes-security/user-groups-editor").getSelectedGroup()}}),Object(f.withDispatch)(function(e){return{selectGroup:e("ithemes-security/user-groups-editor").selectGroup}})])(function(e){var t=e.matchables,n=e.resolvingMatchables,r=e.selectedGroup,i=e.selectGroup;if(Object(u.useEffect)(function(){!n&&t.length&&0===r.length&&i([t[0].id])},[n]),n&&!t.length)return null;var o=Bt(t);return Object(u.createElement)(Me,{tabs:o,selected:r,onSelect:i,allowMultiple:!0,orientation:"vertical",className:"itsec-user-groups-list"},function(e){if(e.length>1){var t=Object(g.filter)(e.map(function(e){var t=e.group;return t&&t.id}));return Object(u.createElement)(Kt,{groupIds:t})}return e[0]?Object(u.createElement)(Vt,{groupId:e[0].name,isNew:"new"===e[0].name}):null})}),Ht=(n("Y0od"),ae()(function(e,t){if("new"===e)return[{name:"create",title:Object(l.__)("Edit Group","better-wp-security"),className:"itsec-manage-user-group-tabs__tab",Component:qt}];var n=[{name:"settings",title:Object(l.__)("Features","better-wp-security"),className:"itsec-manage-user-group-tabs__tab",Component:Yt}];return"user-group"===t&&n.push({name:"edit",title:Object(l.__)("Edit Group","better-wp-security"),className:"itsec-manage-user-group-tabs__tab",Component:zt}),n}));var Vt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{type:e("ithemes-security/user-groups").getMatchableType(n)}})])(function(e){var t=e.groupId,n=e.type,r=e.isNew;return Object(u.createElement)("div",{className:"itsec-manage-user-group"},r?Object(u.createElement)(cn,null):Object(u.createElement)(sn,{groupId:t}),Object(u.createElement)(Be,{tabs:Ht(t,n),className:"itsec-manage-user-group-tabs"},function(e){var n=e.Component;return Object(u.createElement)(n,{groupId:t})}))}),Ft=ae()(function(){return[{name:"settings",title:Object(l.__)("Features","better-wp-security"),className:"itsec-manage-user-group-tabs__tab",Component:Xt}]});var Kt=function(e){var t=e.groupIds;return Object(u.createElement)("div",{className:"itsec-manage-multiple-user-groups"},Object(u.createElement)(an,{groupIds:t}),Object(u.createElement)(Be,{tabs:Ft(),className:"itsec-manage-user-group-tabs"},function(e){var n=e.Component;return Object(u.createElement)(n,{groupIds:t})}))};n("QmYb");var Wt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{label:e("ithemes-security/user-groups-editor").getEditedGroupAttribute(n,"label")||""}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{edit:function(t){return e("ithemes-security/user-groups-editor").editGroup(n,t)}}})])(function(e){var t=e.label,n=e.edit,r=e.disabled,i=void 0!==r&&r;return Object(u.createElement)(X.TextControl,{label:Object(l.__)("Group Name","better-wp-security"),value:t,maxLength:50,disabled:i,onChange:function(e){return n({label:e})}})});n("ZRc3");function $t(e){var t,n=e.name,r=e.isLoading,i=e.children,o=ne()("itsec-user-groups-group-tab",(t={},M()(t,"itsec-user-groups-group-tab--".concat(n),n),M()(t,"itsec-user-groups-group-tab--is-loading",r),t));return Object(u.createElement)("div",{className:o},i)}$t.Row=function(e){var t=e.name,n=e.children;return Object(u.createElement)("div",{className:"itsec-user-groups-group-tab__row itsec-user-groups-group-tab__row--".concat(t)},n)};var qt=Object(re.compose)([Object(f.withSelect)(function(e){return{hasEdits:e("ithemes-security/user-groups-editor").hasEdits("new"),isSaving:e("ithemes-security/user-groups-editor").isCreating("new")}}),Object(f.withDispatch)(function(e){return{save:function(){e("ithemes-security/user-groups-editor").createGroup()}}})])(function(e){var t=e.hasEdits,n=e.save,r=e.isSaving;return Object(u.createElement)($t,{name:"create-group"},Object(u.createElement)(ln,{groupId:"new"}),Object(u.createElement)($t.Row,{name:"save"},Object(u.createElement)(X.Button,{disabled:!t,isPrimary:!0,onClick:n,isBusy:r},Object(l.__)("Create","better-wp-security"))))});var zt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{isLoading:e("core/data").isResolving("ithemes-security/user-groups","getGroup",[n])||e("core/data").isResolving("ithemes-security/core","getIndex"),hasEdits:e("ithemes-security/user-groups-editor").hasEdits(n),isSaving:e("ithemes-security/user-groups").isUpdating(n)}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{save:function(){return e("ithemes-security/user-groups-editor").saveGroup(n)}}})])(function(e){var t=e.groupId,n=e.hasEdits,r=e.save,i=e.isSaving,o=e.isLoading;return Object(u.createElement)($t,{name:"edit-group",isLoading:o},Object(u.createElement)(ln,{groupId:t,disabled:o}),Object(u.createElement)($t.Row,{name:"save"},Object(u.createElement)(X.Button,{disabled:!n,isPrimary:!0,onClick:r,isBusy:i},Object(l.__)("Save","better-wp-security"))))});var Jt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId,r=t.module,i=t.setting;return{value:e("ithemes-security/user-groups-editor").getEditedGroupSetting(n,r,i)}}),Object(f.withDispatch)(function(e,t){var n=t.groupId,r=t.module,i=t.setting;return{edit:function(t){return e("ithemes-security/user-groups-editor").editGroupSetting(n,r,i,t)}}})])(function(e){var t=e.schema,n=e.value,r=e.edit,i=e.disabled,o=void 0!==i&&i;return Object(u.createElement)(X.ToggleControl,{checked:!0===n,label:t.title,help:t.description,disabled:o,onChange:function(e){return r(e)}})});n("GrEf");var Yt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{groupSettings:e("ithemes-security/user-groups").getGroupSettings(n),isLoading:e("core/data").isResolving("ithemes-security/user-groups","getGroupSettings",[n]),schema:e("ithemes-security/core").getSchema("ithemes-security-user-group-settings"),hasEdits:e("ithemes-security/user-groups-editor").settingHasEdits(n),isSaving:e("ithemes-security/user-groups").isUpdatingSettings(n)}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{save:function(){return e("ithemes-security/user-groups-editor").saveGroupSettings(n)}}})])(function(e){var t=e.schema,n=e.groupId,r=e.hasEdits,i=e.save,o=e.isSaving,s=e.isLoading;return t?Object(u.createElement)($t,{name:"settings",isLoading:s},Object(u.createElement)($t.Row,null,Object(u.createElement)(dn,{schema:t,settingComponent:Jt,groupId:n,disabled:s})),Object(u.createElement)($t.Row,{name:"save"},Object(u.createElement)(X.Button,{disabled:!r,isPrimary:!0,onClick:i,isBusy:o},Object(l.__)("Save","better-wp-security")))):null});var Zt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.module,r=t.setting,i=t.groupIds;return{value:e("ithemes-security/user-groups-editor").getBulkSettingValue(i,n,r)}}),Object(f.withDispatch)(function(e,t){var n=t.module,r=t.setting;return{edit:function(t){return e("ithemes-security/user-groups-editor").bulkEditGroupSetting(n,r,t)}}})])(function(e){var t=e.schema,n=e.value,r=e.edit,i=e.disabled,o=void 0!==i&&i;return Object(u.createElement)(Ce,{checked:!0===n,indeterminate:null==n,label:t.title,help:t.description,disabled:o,onChange:function(e){return r(e)}})});var Xt=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupIds;return{schema:e("ithemes-security/core").getSchema("ithemes-security-user-group-settings"),hasEdits:e("ithemes-security/user-groups-editor").hasBulkSettingEdits(),isSaving:e("ithemes-security/user-groups-editor").isSavingBulkEdits(n)}}),Object(f.withDispatch)(function(e,t){var n=t.groupIds;return{save:function(){return e("ithemes-security/user-groups-editor").saveBulkEdits(n)}}})])(function(e){var t=e.schema,n=e.hasEdits,r=e.save,i=e.isSaving,o=e.groupIds;return t?Object(u.createElement)($t,{name:"settings"},Object(u.createElement)($t.Row,null,Object(u.createElement)(dn,{schema:t,settingComponent:Zt,groupIds:o})),Object(u.createElement)($t.Row,{name:"save"},Object(u.createElement)(X.Button,{disabled:!n,isPrimary:!0,onClick:r,isBusy:i},Object(l.__)("Save","better-wp-security")))):null});Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{minRole:e("ithemes-security/user-groups-editor").getEditedGroupAttribute(n,"min_role")}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{onChange:function(t){return e("ithemes-security/user-groups-editor").editGroup(n,t)}}})])(function(e){var t=e.minRole,n=e.onChange;return Object(u.createElement)("div",null,Object(u.createElement)(X.SelectControl,{options:[{value:"administrator",label:Object(l._x)("Administrator","User role","default")},{value:"editor",label:Object(l._x)("Editor","User role","default")},{value:"author",label:Object(l._x)("Author","User role","default")},{value:"contributor",label:Object(l._x)("Contributor","User role","default")},{value:"subscriber",label:Object(l._x)("Subscriber","User role","default")}],label:Object(l.__)("Minimum Role","better-wp-security"),value:t,onChange:function(e){return n({min_role:e})},help:Object(l.__)("Add users with the selected minimum role to this group. To edit roles, go to Users in your WordPress Dashboard.","better-wp-security")}))});var Qt=ae()(function(e,t){var n=[{value:"$administrator$",label:Object(l.__)("Administrator Capabilities","better-wp-security")},{value:"$editor$",label:Object(l.__)("Editor Capabilities","better-wp-security")},{value:"$author$",label:Object(l.__)("Author Capabilities","better-wp-security")},{value:"$contributor$",label:Object(l.__)("Contributor Capabilities","better-wp-security")},{value:"$subscriber$",label:Object(l.__)("Subscriber Capabilities","better-wp-security")}];for(var r in t&&n.unshift({value:"$super-admin$",label:Object(l.__)("Super Admin","better-wp-security")}),Object(g.some)(e,function(e){return""===e.canonical})&&n.push({value:"$other$",label:Object(l.__)("Other","better-wp-security"),selectable:!1}),e)if(e.hasOwnProperty(r)){var i=e[r],o=i.canonical,s=i.label;n.push({value:r,parent:o.length>0?"$".concat(o,"$"):"$other$",label:s})}return Object.values(n)});var en=Object(re.compose)([Object(f.withSelect)(function(e,t){var n=t.groupId;return{roles:e("ithemes-security/user-groups-editor").getEditedGroupAttribute(n,"roles")||[],canonical:e("ithemes-security/user-groups-editor").getEditedGroupAttribute(n,"canonical")||[],available:e("ithemes-security/core").getRoles(),schema:e("ithemes-security/core").getSchema("ithemes-security-user-group")}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{onChange:function(t){return e("ithemes-security/user-groups-editor").editGroup(n,t)}}})])(function(e){var t=e.canonical,n=e.roles,r=e.onChange,i=e.available,o=e.schema,s=e.disabled,c=void 0!==s&&s,a=Object(g.get)(o,["properties","canonical","items","enum"],[]).includes("super-admin"),d=b()(n).concat(b()(t.map(function(e){return"$".concat(e,"$")})));return Object(u.createElement)(ke,{label:Object(l.__)("Select Roles","better-wp-security"),help:Object(l.__)("Add users with the selected roles to this group.","better-wp-security"),value:d,disabled:c,options:Qt(i,a),onChange:function(e){var t=Object(E.a)(e,function(e){return e.startsWith("$")&&e.endsWith("$")}),n=oe()(t,2),i=n[0],o=n[1];r({roles:o,canonical:Object(g.without)(i.map(function(e){return e.slice(1,-1)}),"other")})}})}),tn=n("Mmq9");n("ELjz");function nn(e){return{value:e.id,label:e.name,user:e}}var rn=function(e){return new Promise(function(t,n){O()({path:Object(tn.addQueryArgs)("/wp/v2/users",{search:e,per_page:100,context:"embed",itsec_global:!0})}).then(function(e){return e.forEach(Object(f.dispatch)("ithemes-security/core").receiveUser),e}).then(function(e){return t(e.map(nn))}).catch(n)})};var on=Object(re.compose)([Object(re.withState)({selectSearch:""}),Object(f.withSelect)(function(e,t){var n=t.groupId,r=e("ithemes-security/user-groups-editor").getEditedGroupAttribute(n,"users")||[],i=[],o=!1;return r.forEach(function(t){var n=e("ithemes-security/core").getUser(t);n?i.push(n):e("core/data").isResolving("ithemes-security/core","getUser",[t])&&(o=!0)}),{users:i,userIds:r,loading:o}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{receiveUser:e("ithemes-security/core").receiveUser,onChange:function(t){return e("ithemes-security/user-groups-editor").editGroup(n,t)}}}),re.withInstanceId])(function(e){var t=e.instanceId,n=e.users,r=e.loading,i=e.onChange,o=e.disabled,s=void 0!==o&&o,c=e.selectSearch,a=e.setState,d="itsec-user-group-panel-users__select-".concat(t),p=r?[]:n.map(nn);return Object(u.createElement)(X.BaseControl,{className:"itsec-user-group-panel-users__select-control",label:Object(l.__)("Select Users","better-wp-security"),help:Object(l.__)("Select specific users to add to this group.","better-wp-security"),id:d},Object(u.createElement)(me,{classNamePrefix:"components-itsec-async-select-control",inputId:d,isDisabled:s||r,isLoading:r,isMulti:!0,cacheOptions:!0,defaultOptions:!0,loadOptions:rn,value:p,onChange:function(e){return i({users:Object(g.map)(e,"value")})},inputValue:c,onInputChange:function(e){return a({selectSearch:e})}}))});n("mzAq");var sn=Object(re.compose)([Object(f.withSelect)(function(e,t){var n,r=t.groupId,i=e("ithemes-security/user-groups").getMatchableType(r),o="user-group"===i&&e("ithemes-security/user-groups").isDeleting(r);return"user-group"===i&&(n=e("ithemes-security/user-groups-editor").getEditedGroupAttribute(r,"label")),void 0===n&&(n=e("ithemes-security/user-groups").getMatchableLabel(r)),{type:i,label:n,isDeleting:o}}),Object(f.withDispatch)(function(e,t){var n=t.groupId;return{deleteGroup:function(){return e("ithemes-security/user-groups").deleteGroup(n)}}})])(function(e){var t=e.type,n=e.label,r=e.isDeleting,i=e.deleteGroup,o="user-group"===t;return n&&n.length||(n=Object(l.__)("Untitled","better-wp-security")),Object(u.createElement)(un,{label:n},o&&Object(u.createElement)(X.Button,{onClick:i,isBusy:r,isLink:!0,isDestructive:!0},Object(l.__)("Delete Group","better-wp-security")))});var cn=Object(re.compose)([Object(f.withSelect)(function(e){return{label:e("ithemes-security/user-groups-editor").getEditedGroupAttribute("new","label")}})])(function(e){var t=e.label;return t&&t.length||(t=Object(l.__)("New Group","better-wp-security")),Object(u.createElement)(un,{label:t})});var an=Object(re.compose)([Object(f.withSelect)(function(e,t){return{label:t.groupIds.map(e("ithemes-security/user-groups").getMatchableLabel).join(", ")}})])(function(e){var t=e.label;return Object(u.createElement)(un,{label:t})});function un(e){var t=e.label,n=e.children;return Object(u.createElement)("div",{className:"itsec-user-group-header"},Object(u.createElement)("h4",{className:"itsec-user-group-header__label"},t),n)}n("XO7p");var ln=function(e){var t=e.groupId,n=e.disabled;return Object(u.createElement)($t.Row,{name:"edit-fields"},Object(u.createElement)(Wt,{groupId:t,disabled:n}),Object(u.createElement)(en,{groupId:t,disabled:n}),Object(u.createElement)(on,{groupId:t,disabled:n}))};function dn(e){var t=e.schema,n=e.settingComponent,r=le()(e,["schema","settingComponent"]);return Object(u.createElement)("ul",{className:"itsec-user-groups-group-tab__modules-list"},Object(g.map)(t.properties,function(e,t){return Object(u.createElement)("li",{key:t},Object(u.createElement)("fieldset",null,Object(u.createElement)("legend",null,e.title),Object(u.createElement)("ul",null,Object(g.map)(e.properties,function(e,i){return Object(u.createElement)("li",{key:i},Object(u.createElement)(n,Oe()({schema:e,module:t,setting:i},r)))}))))}))}n("wM0a");var pn=function(e){var t=e.noticeEl;return Object(u.createElement)("div",{className:"itsec-user-groups-app"},Object(u.createPortal)(Object(u.createElement)(xe,null),t),Object(u.createElement)(Mt,null))};function fn(){var e=document.getElementById("itsec-user-groups-settings-root"),t=document.getElementById("itsec-module-messages-container-user-groups");return Object(u.render)(Object(u.createElement)(pn,{noticeEl:t}),e)}n.p=window.itsecWebpackPublicPath,Object(l.setLocaleData)({"":{}},"ithemes-security-pro"),p()(function(){fn(),window.itsecSettingsPage&&(window.itsecSettingsPage.events.on("modulesReloaded",fn),window.itsecSettingsPage.events.on("moduleReloaded",function(e,t){"user-groups"===t&&fn()}))})},faye:function(e,t){!function(){e.exports=this.ReactDOM}()},l3Sj:function(e,t){!function(){e.exports=this.wp.i18n}()},lSNA:function(e,t){e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}},lSb6:function(e,t,n){},lwsE:function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},m0LI:function(e,t){e.exports=function(e,t){var n=[],r=!0,i=!1,o=void 0;try{for(var s,c=e[Symbol.iterator]();!(r=(s=c.next()).done)&&(n.push(s.value),!t||n.length!==t);r=!0);}catch(e){i=!0,o=e}finally{try{r||null==c.return||c.return()}finally{if(i)throw o}}return n}},mzAq:function(e,t,n){},oShl:function(e,t,n){var r=n("Nsbk"),i=n("SksO"),o=n("xfeJ"),s=n("sXyB");function c(t){var n="function"==typeof Map?new Map:void 0;return e.exports=c=function(e){if(null===e||!o(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==n){if(n.has(e))return n.get(e);n.set(e,t)}function t(){return s(e,arguments,r(this).constructor)}return t.prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),i(t,e)},c(t)}e.exports=c},"oaS/":function(e,t,n){},onLe:function(e,t){!function(){e.exports=this.wp.notices}()},pVnL:function(e,t){function n(){return e.exports=n=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},n.apply(this,arguments)}e.exports=n},rl8x:function(e,t){!function(){e.exports=this.wp.isShallowEqual}()},rmEH:function(e,t){!function(){e.exports=this.wp.htmlEntities}()},sPxh:function(e,t){!function(){e.exports=this.itsec["user-groups"].api}()},sXyB:function(e,t,n){var r=n("SksO");function i(t,n,o){return!function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(e){return!1}}()?e.exports=i=function(e,t,n){var i=[null];i.push.apply(i,t);var o=new(Function.bind.apply(e,i));return n&&r(o,n.prototype),o}:e.exports=i=Reflect.construct,i.apply(null,arguments)}e.exports=i},"tI+e":function(e,t){!function(){e.exports=this.wp.components}()},tmk3:function(e,t){e.exports=function(e,t){if(!t.has(e))throw new TypeError("attempted to get private field on non-instance");return t.get(e).value}},urxu:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ErrorBoundaryFallbackComponent=t.withErrorBoundary=t.ErrorBoundary=void 0;var r=s(n("Xvx9")),i=n("31KZ"),o=s(i);function s(e){return e&&e.__esModule?e:{default:e}}t.default=o.default,t.ErrorBoundary=o.default,t.withErrorBoundary=i.withErrorBoundary,t.ErrorBoundaryFallbackComponent=r.default},wM0a:function(e,t,n){},wTVA:function(e,t){e.exports=function(e){if(Array.isArray(e))return e}},wkBT:function(e,t){e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},xfeJ:function(e,t){e.exports=function(e){return-1!==Function.toString.call(e).indexOf("[native code]")}},ywyh:function(e,t){!function(){e.exports=this.wp.apiFetch}()}});
dist/vendors/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
dist/vendors/user-groups/api.min.js ADDED
@@ -0,0 +1 @@
 
1
+ (window.itsecWebpackJsonP=window.itsecWebpackJsonP||[]).push([[7],{"7W2i":function(t,e,n){var r=n("SksO");t.exports=function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&r(t,e)}},"92Nh":function(t,e){t.exports=function(t,e,n){if(!e.has(t))throw new TypeError("attempted to set private field on non-instance");var r=e.get(t);if(!r.writable)throw new TypeError("attempted to set read only private field");return r.value=n,n}},Bnag:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},EbDI:function(t,e){t.exports=function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}},Ijbi:function(t,e){t.exports=function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}},J4zp:function(t,e,n){var r=n("wTVA"),o=n("m0LI"),i=n("wkBT");t.exports=function(t,e){return r(t)||o(t,e)||i()}},MVZn:function(t,e,n){var r=n("lSNA");t.exports=function(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(t){return Object.getOwnPropertyDescriptor(n,t).enumerable}))),o.forEach(function(e){r(t,e,n[e])})}return t}},Nsbk:function(t,e){function n(e){return t.exports=n=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)},n(e)}t.exports=n},PJYZ:function(t,e){t.exports=function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}},RIqP:function(t,e,n){var r=n("Ijbi"),o=n("EbDI"),i=n("Bnag");t.exports=function(t){return r(t)||o(t)||i()}},SksO:function(t,e){function n(e,r){return t.exports=n=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t},n(e,r)}t.exports=n},W8MJ:function(t,e){function n(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}t.exports=function(t,e,r){return e&&n(t.prototype,e),r&&n(t,r),t}},a1gu:function(t,e,n){var r=n("cDf5"),o=n("PJYZ");t.exports=function(t,e){return!e||"object"!==r(e)&&"function"!=typeof e?o(t):e}},cDf5:function(t,e){function n(t){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(e){return"function"==typeof Symbol&&"symbol"===n(Symbol.iterator)?t.exports=r=function(t){return n(t)}:t.exports=r=function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":n(t)},r(e)}t.exports=r},lSNA:function(t,e){t.exports=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}},lwsE:function(t,e){t.exports=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}},m0LI:function(t,e){t.exports=function(t,e){var n=[],r=!0,o=!1,i=void 0;try{for(var u,f=t[Symbol.iterator]();!(r=(u=f.next()).done)&&(n.push(u.value),!e||n.length!==e);r=!0);}catch(t){o=!0,i=t}finally{try{r||null==f.return||f.return()}finally{if(o)throw i}}return n}},oShl:function(t,e,n){var r=n("Nsbk"),o=n("SksO"),i=n("xfeJ"),u=n("sXyB");function f(e){var n="function"==typeof Map?new Map:void 0;return t.exports=f=function(t){if(null===t||!i(t))return t;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==n){if(n.has(t))return n.get(t);n.set(t,e)}function e(){return u(t,arguments,r(this).constructor)}return e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),o(e,t)},f(e)}t.exports=f},pPDe:function(t,e,n){"use strict";var r,o;function i(t){return[t]}function u(){var t={clear:function(){t.head=null}};return t}function f(t,e,n){var r;if(t.length!==e.length)return!1;for(r=n;r<t.length;r++)if(t[r]!==e[r])return!1;return!0}r={},o="undefined"!=typeof WeakMap,e.a=function(t,e){var n,s;function a(){n=o?new WeakMap:u()}function c(){var n,r,o,i,u,a=arguments.length;for(i=new Array(a),o=0;o<a;o++)i[o]=arguments[o];for(u=e.apply(null,i),(n=s(u)).isUniqueByDependants||(n.lastDependants&&!f(u,n.lastDependants,0)&&n.clear(),n.lastDependants=u),r=n.head;r;){if(f(r.args,i,1))return r!==n.head&&(r.prev.next=r.next,r.next&&(r.next.prev=r.prev),r.next=n.head,r.prev=null,n.head.prev=r,n.head=r),r.val;r=r.next}return r={val:t.apply(null,i)},i[0]=null,r.args=i,n.head&&(n.head.prev=r,r.next=n.head),n.head=r,r.val}return e||(e=i),s=o?function(t){var e,o,i,f,s,a=n,c=!0;for(e=0;e<t.length;e++){if(o=t[e],!(s=o)||"object"!=typeof s){c=!1;break}a.has(o)?a=a.get(o):(i=new WeakMap,a.set(o,i),a=i)}return a.has(r)||((f=u()).isUniqueByDependants=c,a.set(r,f)),a.get(r)}:function(){return n},c.getDependants=e,c.clear=a,a(),c}},qPad:function(t,e,n){var r,o;r=this,o=function(){var t={"+":!0,"#":!0,".":!0,"/":!0,";":!0,"?":!0,"&":!0},e={"*":!0};function n(t){return encodeURI(t).replace(/%25[0-9][0-9]/g,function(t){return"%"+t.substring(3)})}function r(t){return t=t.replace(/%../g,""),encodeURIComponent(t)===t}function o(o){var i="";t[o.charAt(0)]&&(i=o.charAt(0),o=o.substring(1));var u="",f="",s=!0,a=!1,c=!1;"+"==i?s=!1:"."==i?(f=".",u="."):"/"==i?(f="/",u="/"):"#"==i?(f="#",s=!1):";"==i?(f=";",u=";",a=!0,c=!0):"?"==i?(f="?",u="&",a=!0):"&"==i&&(f="&",u="&",a=!0);for(var l=[],p=o.split(","),h=[],g={},y=0;y<p.length;y++){var b=p[y],v=null;if(-1!=b.indexOf(":")){var d=b.split(":");b=d[0],v=parseInt(d[1])}for(var m={};e[b.charAt(b.length-1)];)m[b.charAt(b.length-1)]=!0,b=b.substring(0,b.length-1);var x={truncate:v,name:b,suffices:m};h.push(x),g[b]=x,l.push(b)}return{varNames:l,prefix:f,substitution:function(t){for(var e="",r=0,o=0;o<h.length;o++){var i=h[o],l=t(i.name);if(null==l||Array.isArray(l)&&0==l.length||"object"==typeof l&&0==Object.keys(l).length)r++;else if(e+=o==r?f:u||",",Array.isArray(l)){a&&(e+=i.name+"=");for(var p=0;p<l.length;p++)p>0&&(e+=i.suffices["*"]&&u||",",i.suffices["*"]&&a&&(e+=i.name+"=")),e+=s?encodeURIComponent(l[p]).replace(/!/g,"%21"):n(l[p])}else if("object"==typeof l){a&&!i.suffices["*"]&&(e+=i.name+"=");var g=!0;for(var y in l)g||(e+=i.suffices["*"]&&u||","),g=!1,e+=s?encodeURIComponent(y).replace(/!/g,"%21"):n(y),e+=i.suffices["*"]?"=":",",e+=s?encodeURIComponent(l[y]).replace(/!/g,"%21"):n(l[y])}else a&&(e+=i.name,c&&""==l||(e+="=")),null!=i.truncate&&(l=l.substring(0,i.truncate)),e+=s?encodeURIComponent(l).replace(/!/g,"%21"):n(l)}return e},unSubstitution:function(t,e,n){if(f&&(t=t.substring(f.length)),1==h.length&&h[0].suffices["*"]){for(var o=(S=h[0]).name,i=S.suffices["*"]?t.split(u||","):[t],c=s&&-1!=t.indexOf("="),l=1;l<i.length;l++)t=i[l],c&&-1==t.indexOf("=")&&(i[l-1]+=(u||",")+t,i.splice(l,1),l--);for(l=0;l<i.length;l++)t=i[l],s&&-1!=t.indexOf("=")&&(c=!0),1==(d=t.split(",")).length?i[l]=d[0]:i[l]=d;if(a||c){for(var p=e[o]||{},y=0;y<i.length;y++){var b=t;if(!a||b){if("string"==typeof i[y]){var v=(t=i[y]).split("=",1)[0];if(t=t.substring(v.length+1),s){if(n&&!r(t))return;t=decodeURIComponent(t)}b=t}else{if(v=(t=i[y][0]).split("=",1)[0],t=t.substring(v.length+1),s){if(n&&!r(t))return;t=decodeURIComponent(t)}i[y][0]=t,b=i[y]}if(s){if(n&&!r(v))return;v=decodeURIComponent(v)}void 0!==p[v]?Array.isArray(p[v])?p[v].push(b):p[v]=[p[v],b]:p[v]=b}}1==Object.keys(p).length&&void 0!==p[o]?e[o]=p[o]:e[o]=p}else{if(s)for(y=0;y<i.length;y++){var d=i[y];if(Array.isArray(d))for(var m=0;m<d.length;m++){if(n&&!r(d[m]))return;d[m]=decodeURIComponent(d[m])}else{if(n&&!r(d))return;i[y]=decodeURIComponent(d)}}void 0!==e[o]?Array.isArray(e[o])?e[o]=e[o].concat(i):e[o]=[e[o]].concat(i):1!=i.length||S.suffices["*"]?e[o]=i:e[o]=i[0]}}else{i=1==h.length?[t]:t.split(u||",");var x={};for(l=0;l<i.length;l++){for(var w=0;w<h.length-1&&w<l&&!h[w].suffices["*"];w++);if(w!=l){for(var O=h.length-1;O>0&&h.length-O<i.length-l&&!h[O].suffices["*"];O--);h.length-O!=i.length-l?x[l]=w:x[l]=O}else x[l]=l}for(l=0;l<i.length;l++)if((t=i[l])||!a){if(d=t.split(","),c=!1,a){o=(t=d[0]).split("=",1)[0],t=t.substring(o.length+1),d[0]=t;var S=g[o]||h[0]}else o=(S=h[x[l]]).name;for(y=0;y<d.length;y++)if(s){if(n&&!r(d[y]))return;d[y]=decodeURIComponent(d[y])}(a||S.suffices["*"])&&void 0!==e[o]?Array.isArray(e[o])?e[o]=e[o].concat(d):e[o]=[e[o]].concat(d):1!=d.length||S.suffices["*"]?e[o]=d:e[o]=d[0]}}return 1}}}function i(t){if(!(this instanceof i))return new i(t);for(var e=t.split("{"),n=[e.shift()],r=[],u=[],f=[],s=[];e.length>0;){var a=e.shift(),c=a.split("}")[0],l=a.substring(c.length+1),p=o(c);u.push(p.substitution),f.push(p.unSubstitution),r.push(p.prefix),n.push(l),s=s.concat(p.varNames)}this.fill=function(t){if(t&&"function"!=typeof t){var e=t;t=function(t){return e[t]}}for(var r=n[0],o=0;o<u.length;o++){r+=(0,u[o])(t),r+=n[o+1]}return r},this.fromUri=function(t,e){e=e||{};for(var o={},i=0;i<n.length;i++){var u=n[i];if(t.substring(0,u.length)!==u)return;if(t=t.substring(u.length),i>=n.length-1){if(""==t)break;return}var s=r[i];if(!s||t.substring(0,s.length)===s){for(var a=n[i+1],c=i;;){if(c==n.length-2){var l=t.substring(t.length-a.length);if(l!==a)return;var p=t.substring(0,t.length-a.length);t=l}else if(a){var h=t.indexOf(a);p=t.substring(0,h);t=t.substring(h)}else if(r[c+1]){-1===(h=t.indexOf(r[c+1]))&&(h=t.length);p=t.substring(0,h);t=t.substring(h)}else{if(n.length>c+2){a=n[++c+1];continue}p=t;t=""}break}if(!f[i](p,o,e.strict))return}}return o},this.varNames=s,this.template=t}return i.prototype={toString:function(){return this.template},fillFromObject:function(t){return this.fill(t)},test:function(t,e){return!!this.fromUri(t,e)}},i},"function"==typeof define&&define.amd?define("uri-templates",[],o):t.exports?t.exports=o():r.UriTemplate=o()},sXyB:function(t,e,n){var r=n("SksO");function o(e,n,i){return!function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}()?t.exports=o=function(t,e,n){var o=[null];o.push.apply(o,e);var i=new(Function.bind.apply(t,o));return n&&r(i,n.prototype),i}:t.exports=o=Reflect.construct,o.apply(null,arguments)}t.exports=o},tmk3:function(t,e){t.exports=function(t,e){if(!e.has(t))throw new TypeError("attempted to get private field on non-instance");return e.get(t).value}},wTVA:function(t,e){t.exports=function(t){if(Array.isArray(t))return t}},wkBT:function(t,e){t.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}},xfeJ:function(t,e){t.exports=function(t){return-1!==Function.toString.call(t).indexOf("[native code]")}}}]);
dist/vendors/user-groups/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
history.txt CHANGED
@@ -864,5 +864,23 @@
864
  New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
865
  Enhancement: Run Security Check Pro IP Detection automatically once a day.
866
  Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
867
- 7.6.1 - 2019-12-10 - Timothy Jacobs
868
- Bug Fix: Properly notate that iThemes Security requires PHP 5.5 or greater.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
864
  New Feature: iThemes Security now includes Security Check Pro to automatically and correctly determine your visitors IP addresses. Enable this scan by running Security Check and opting in to Security Check Pro or activate the Security Check Pro module in Advanced Modules. H/t Jeremy Voisin
865
  Enhancement: Run Security Check Pro IP Detection automatically once a day.
866
  Enhancement: Manually re-run Security Check Pro IP Detection from the Global Settings page.
867
+ 7.7.0 - 2020-04-15 - Timothy Jacobs, Josh Oakes
868
+ Important: iThemes Security requires PHP 5.6 or greater and WordPress 5.2 or greater.
869
+ New Feature: Save Time Securing WordPress With User Groups!
870
+ New Feature: Simplified connection flow when setting up iThemes Sync.
871
+ Enhancement: Add a warning if a WordPress Salt is set to an invalid value.
872
+ Enhancement: Include child log items in the logs list table. These are helpful for debugging issues.
873
+ Enhancement: Improve performance of the logs page on sites with large number of log items.
874
+ Enhancement: Check tables exist after completing a DB upgrade.
875
+ Tweak: When logging $_SERVER, only log a snapshot of available properties.
876
+ Bug Fix: The "Mulisite Tweaks -> Hide Updates" setting prevented auto-updates from running with WP Cron.
877
+ Bug Fix: Backup event was not added when the WP Cron Scheduler was reset manually.
878
+ Bug Fix: Admin Notices Popover was not being hidden when clicking outside the Popover on WP 5.3.
879
+ Bug Fix: New Password Requirements for already created accounts were not enforced until the second login.
880
+ Bug Fix: Update admin notices styling to be compatible with WordPress 5.4.
881
+ Bug Fix: Periodically clear expired opaque tokens.
882
+ Bug Fix: Don't block registration page when "wp-signup.php" is the Hide Backend register slug.
883
+ Bug Fix: Users with weak passwords would not be forced to change their password if the strong password requirement had been enabled after their password strength was checked.
884
+ Bug Fix: Remove "get_magic_quotes()" call that existed for backwards compatibility with PHP versions 5.3 and earlier. This function call was causing a warning on PHP 7.4.
885
+ Bug Fix: Warning when loading the settings page on PHP 7.4.
886
+ Bug Fix: Warning when loading the debug page on PHP 7.4.
package.json CHANGED
@@ -9,28 +9,28 @@
9
  "dependencies": {
10
  "@wordpress/a11y": "*",
11
  "@wordpress/api-fetch": "*",
12
- "@wordpress/autop": "^2.2.0",
13
- "@wordpress/components": "^7.0.8",
14
- "@wordpress/compose": "^3.0.1",
15
- "@wordpress/data": "^4.2.1",
16
- "@wordpress/date": "^3.0.1",
17
  "@wordpress/dom-ready": "*",
18
  "@wordpress/element": "*",
19
  "@wordpress/hooks": "*",
20
  "@wordpress/html-entities": "*",
21
  "@wordpress/i18n": "*",
22
  "@wordpress/is-shallow-equal": "*",
23
- "@wordpress/keycodes": "^2.0.6",
24
  "@wordpress/notices": "*",
25
- "@wordpress/plugins": "^2.2.0",
26
  "@wordpress/redux-routine": "*",
27
- "@wordpress/rich-text": "^3.0.7",
28
  "@wordpress/url": "*",
29
  "@wordpress/viewport": "*",
30
  "classnames": "^2.2.6",
31
  "contrast": "^1.0.1",
32
  "li": "^1.3.0",
33
- "lodash": "^4.17.11",
34
  "memize": "^1.0.5",
35
  "react": "^16.6.3",
36
  "react-error-boundary": "^1.2.3",
@@ -38,11 +38,14 @@
38
  "react-select": "^2.4.1",
39
  "react-transition-group": "^2.0.0",
40
  "recharts": "^1.5.0",
41
- "rememo": "^3.0.0"
 
 
42
  },
43
  "devDependencies": {
44
  "@babel/core": "^7.3.3",
45
  "@babel/plugin-proposal-class-properties": "^7.3.3",
 
46
  "@babel/plugin-syntax-dynamic-import": "^7.2.0",
47
  "@babel/plugin-transform-react-jsx": "^7.3.0",
48
  "@babel/runtime-corejs2": "^7.3.1",
@@ -110,8 +113,11 @@
110
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
111
  "test-unit:watch": "npm run test-unit -- --watch",
112
  "watch": "./node_modules/.bin/webpack --watch",
 
113
  "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
114
  "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
115
- "test-acceptance:build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
 
 
116
  }
117
  }
9
  "dependencies": {
10
  "@wordpress/a11y": "*",
11
  "@wordpress/api-fetch": "*",
12
+ "@wordpress/autop": "*",
13
+ "@wordpress/components": "*",
14
+ "@wordpress/compose": "*",
15
+ "@wordpress/data": "*",
16
+ "@wordpress/date": "*",
17
  "@wordpress/dom-ready": "*",
18
  "@wordpress/element": "*",
19
  "@wordpress/hooks": "*",
20
  "@wordpress/html-entities": "*",
21
  "@wordpress/i18n": "*",
22
  "@wordpress/is-shallow-equal": "*",
23
+ "@wordpress/keycodes": "*",
24
  "@wordpress/notices": "*",
25
+ "@wordpress/plugins": "*",
26
  "@wordpress/redux-routine": "*",
27
+ "@wordpress/rich-text": "*",
28
  "@wordpress/url": "*",
29
  "@wordpress/viewport": "*",
30
  "classnames": "^2.2.6",
31
  "contrast": "^1.0.1",
32
  "li": "^1.3.0",
33
+ "lodash": "^4.17.15",
34
  "memize": "^1.0.5",
35
  "react": "^16.6.3",
36
  "react-error-boundary": "^1.2.3",
38
  "react-select": "^2.4.1",
39
  "react-transition-group": "^2.0.0",
40
  "recharts": "^1.5.0",
41
+ "rememo": "^3.0.0",
42
+ "uri-templates": "^0.2.0",
43
+ "uuid": "^3.3.3"
44
  },
45
  "devDependencies": {
46
  "@babel/core": "^7.3.3",
47
  "@babel/plugin-proposal-class-properties": "^7.3.3",
48
+ "@babel/plugin-proposal-export-default-from": "^7.8.3",
49
  "@babel/plugin-syntax-dynamic-import": "^7.2.0",
50
  "@babel/plugin-transform-react-jsx": "^7.3.0",
51
  "@babel/runtime-corejs2": "^7.3.1",
113
  "test-unit:update": "npm run test-unit -- --updateSnapshot",
114
  "test-unit:watch": "npm run test-unit -- --watch",
115
  "watch": "./node_modules/.bin/webpack --watch",
116
+ "test-up": "./bin/test-up.sh",
117
  "test-wpunit": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpunit",
118
  "test-acceptance": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
119
+ "test-cli": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run wpcli",
120
+ "test-upgrade": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run upgrade",
121
+ "test-build": "docker-compose exec -T -w /var/www/html/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
122
  }
123
  }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === iThemes Security (formerly Better WP Security) ===
2
  Contributors: ithemes, chrisjean, mattdanner, timothyblynjacobs
3
  Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
4
- Requires at least: 4.7
5
- Tested up to: 5.3
6
- Stable tag: 7.6.1
7
- Requires PHP: 5.5
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -189,6 +189,27 @@ Free support may be available with the help of the community in the <a href="htt
189
 
190
  == Changelog ==
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  = 7.6.1 =
193
  * Bug Fix: Properly notate that iThemes Security requires PHP 5.5 or greater.
194
 
@@ -569,5 +590,5 @@ Free support may be available with the help of the community in the <a href="htt
569
 
570
  == Upgrade Notice ==
571
 
572
- = 7.6.1 =
573
- Version 7.6.1 contains new features and bug fixes. It is recommended for all users.
1
  === iThemes Security (formerly Better WP Security) ===
2
  Contributors: ithemes, chrisjean, mattdanner, timothyblynjacobs
3
  Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
4
+ Requires at least: 5.2
5
+ Tested up to: 5.4
6
+ Stable tag: 7.7.0
7
+ Requires PHP: 5.6
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
189
 
190
  == Changelog ==
191
 
192
+ = 7.7.0 =
193
+ * Important: iThemes Security requires PHP 5.6 or greater and WordPress 5.2 or greater.
194
+ * New Feature: Save Time Securing WordPress With User Groups!
195
+ * New Feature: Simplified connection flow when setting up iThemes Sync.
196
+ * Enhancement: Add a warning if a WordPress Salt is set to an invalid value.
197
+ * Enhancement: Include child log items in the logs list table. These are helpful for debugging issues.
198
+ * Enhancement: Improve performance of the logs page on sites with large number of log items.
199
+ * Enhancement: Check tables exist after completing a DB upgrade.
200
+ * Tweak: When logging $_SERVER, only log a snapshot of available properties.
201
+ * Bug Fix: The "Mulisite Tweaks -> Hide Updates" setting prevented auto-updates from running with WP Cron.
202
+ * Bug Fix: Backup event was not added when the WP Cron Scheduler was reset manually.
203
+ * Bug Fix: Admin Notices Popover was not being hidden when clicking outside the Popover on WP 5.3.
204
+ * Bug Fix: New Password Requirements for already created accounts were not enforced until the second login.
205
+ * Bug Fix: Update admin notices styling to be compatible with WordPress 5.4.
206
+ * Bug Fix: Periodically clear expired opaque tokens.
207
+ * Bug Fix: Don't block registration page when "wp-signup.php" is the Hide Backend register slug.
208
+ * Bug Fix: Users with weak passwords would not be forced to change their password if the strong password requirement had been enabled after their password strength was checked.
209
+ * Bug Fix: Remove "get_magic_quotes()" call that existed for backwards compatibility with PHP versions 5.3 and earlier. This function call was causing a warning on PHP 7.4.
210
+ * Bug Fix: Warning when loading the settings page on PHP 7.4.
211
+ * Bug Fix: Warning when loading the debug page on PHP 7.4.
212
+
213
  = 7.6.1 =
214
  * Bug Fix: Properly notate that iThemes Security requires PHP 5.5 or greater.
215
 
590
 
591
  == Upgrade Notice ==
592
 
593
+ = 7.7.0 =
594
+ Version 7.7.0 contains new features and bug fixes. It is recommended for all users.
vendor-prod/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit35a2bd4feb347da0d3ea2d8ef023082f::getLoader();
vendor-prod/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
vendor-prod/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
vendor-prod/composer/autoload_classmap.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'ITSEC_404_Detection_Settings_Page' => $baseDir . '/core/modules/404-detection/settings-page.php',
10
+ 'ITSEC_Admin_Notice' => $baseDir . '/core/lib/admin-notices/interface-itsec-admin-notice.php',
11
+ 'ITSEC_Admin_Notice_Action' => $baseDir . '/core/lib/admin-notices/actions/interface-itsec-admin-notice-action.php',
12
+ 'ITSEC_Admin_Notice_Action_Callback' => $baseDir . '/core/lib/admin-notices/actions/class-itsec-admin-notice-action-callback.php',
13
+ 'ITSEC_Admin_Notice_Action_Link' => $baseDir . '/core/lib/admin-notices/actions/class-itsec-admin-notice-action-link.php',
14
+ 'ITSEC_Admin_Notice_Callback' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-callback.php',
15
+ 'ITSEC_Admin_Notice_Context' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-context.php',
16
+ 'ITSEC_Admin_Notice_Globally_Dismissible' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-globally-dismissible.php',
17
+ 'ITSEC_Admin_Notice_Highlighted_Log' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-highlighted-log.php',
18
+ 'ITSEC_Admin_Notice_Managers_Only' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-managers-only.php',
19
+ 'ITSEC_Admin_Notice_Network_Brute_Force_Promo' => $baseDir . '/core/modules/global/notices.php',
20
+ 'ITSEC_Admin_Notice_New_Feature_Core' => $baseDir . '/core/modules/core/notices.php',
21
+ 'ITSEC_Admin_Notice_Remind_Me' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-remind-me.php',
22
+ 'ITSEC_Admin_Notice_Screen_Blacklist' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-screen-blacklist.php',
23
+ 'ITSEC_Admin_Notice_Static' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-static.php',
24
+ 'ITSEC_Admin_Notice_User_Dismissible' => $baseDir . '/core/lib/admin-notices/class-itsec-admin-notice-user-dismissible.php',
25
+ 'ITSEC_Admin_Notices' => $baseDir . '/core/modules/core/class-itsec-admin-notices.php',
26
+ 'ITSEC_Admin_User_Settings' => $baseDir . '/core/modules/admin-user/settings.php',
27
+ 'ITSEC_Admin_User_Settings_Page' => $baseDir . '/core/modules/admin-user/settings-page.php',
28
+ 'ITSEC_Admin_User_Validator' => $baseDir . '/core/modules/admin-user/validator.php',
29
+ 'ITSEC_Away_Mode' => $baseDir . '/core/modules/away-mode/class-itsec-away-mode.php',
30
+ 'ITSEC_Away_Mode_Logs' => $baseDir . '/core/modules/away-mode/logs.php',
31
+ 'ITSEC_Away_Mode_Settings' => $baseDir . '/core/modules/away-mode/settings.php',
32
+ 'ITSEC_Away_Mode_Settings_Page' => $baseDir . '/core/modules/away-mode/settings-page.php',
33
+ 'ITSEC_Away_Mode_Setup' => $baseDir . '/core/modules/away-mode/setup.php',
34
+ 'ITSEC_Away_Mode_Utilities' => $baseDir . '/core/modules/away-mode/utilities.php',
35
+ 'ITSEC_Away_Mode_Validator' => $baseDir . '/core/modules/away-mode/validator.php',
36
+ 'ITSEC_Backup' => $baseDir . '/core/modules/backup/class-itsec-backup.php',
37
+ 'ITSEC_Backup_Logs' => $baseDir . '/core/modules/backup/logs.php',
38
+ 'ITSEC_Backup_Privacy' => $baseDir . '/core/modules/backup/privacy.php',
39
+ 'ITSEC_Backup_Settings' => $baseDir . '/core/modules/backup/settings.php',
40
+ 'ITSEC_Backup_Settings_Page' => $baseDir . '/core/modules/backup/settings-page.php',
41
+ 'ITSEC_Backup_Setup' => $baseDir . '/core/modules/backup/setup.php',
42
+ 'ITSEC_Backup_Validator' => $baseDir . '/core/modules/backup/validator.php',
43
+ 'ITSEC_Ban_Users' => $baseDir . '/core/modules/ban-users/class-itsec-ban-users.php',
44
+ 'ITSEC_Ban_Users_Config_Generators' => $baseDir . '/core/modules/ban-users/config-generators.php',
45
+ 'ITSEC_Ban_Users_Settings' => $baseDir . '/core/modules/ban-users/settings.php',
46
+ 'ITSEC_Ban_Users_Settings_Page' => $baseDir . '/core/modules/ban-users/settings-page.php',
47
+ 'ITSEC_Ban_Users_Setup' => $baseDir . '/core/modules/ban-users/setup.php',
48
+ 'ITSEC_Ban_Users_Validator' => $baseDir . '/core/modules/ban-users/validator.php',
49
+ 'ITSEC_Brute_Force' => $baseDir . '/core/modules/brute-force/class-itsec-brute-force.php',
50
+ 'ITSEC_Brute_Force_Logs' => $baseDir . '/core/modules/brute-force/logs.php',
51
+ 'ITSEC_Brute_Force_Settings' => $baseDir . '/core/modules/brute-force/settings.php',
52
+ 'ITSEC_Brute_Force_Settings_Page' => $baseDir . '/core/modules/brute-force/settings-page.php',
53
+ 'ITSEC_Brute_Force_Setup' => $baseDir . '/core/modules/brute-force/setup.php',
54
+ 'ITSEC_Brute_Force_Validator' => $baseDir . '/core/modules/brute-force/validator.php',
55
+ 'ITSEC_Content_Directory_Settings_Page' => $baseDir . '/core/modules/content-directory/settings-page.php',
56
+ 'ITSEC_Content_Directory_Utility' => $baseDir . '/core/modules/content-directory/utility.php',
57
+ 'ITSEC_Core_Active' => $baseDir . '/core/modules/core/class-itsec-core-active.php',
58
+ 'ITSEC_Core_Admin' => $baseDir . '/core/modules/core/class-itsec-core-admin.php',
59
+ 'ITSEC_Core_Server_Config_Rules_Settings_Page' => $baseDir . '/core/modules/file-writing/settings-page.php',
60
+ 'ITSEC_Core_WPConfig_File_Settings_Page' => $baseDir . '/core/modules/file-writing/settings-page.php',
61
+ 'ITSEC_Database_Prefix_Settings_Page' => $baseDir . '/core/modules/database-prefix/settings-page.php',
62
+ 'ITSEC_Database_Prefix_Utility' => $baseDir . '/core/modules/database-prefix/utility.php',
63
+ 'ITSEC_Debug' => $baseDir . '/core/lib/debug.php',
64
+ 'ITSEC_Email_Confirmation' => $baseDir . '/core/modules/email-confirmation/class-itsec-email-confirmation.php',
65
+ 'ITSEC_Feature_Flags_Settings_Page' => $baseDir . '/core/modules/feature-flags/settings-page.php',
66
+ 'ITSEC_File_Change' => $baseDir . '/core/modules/file-change/class-itsec-file-change.php',
67
+ 'ITSEC_File_Change_Admin' => $baseDir . '/core/modules/file-change/admin.php',
68
+ 'ITSEC_File_Change_Chunk_Scanner' => $baseDir . '/core/modules/file-change/lib/chunk-scanner.php',
69
+ 'ITSEC_File_Change_Hash_Comparator' => $baseDir . '/core/modules/file-change/lib/hash-comparator.php',
70
+ 'ITSEC_File_Change_Hash_Comparator_Chain' => $baseDir . '/core/modules/file-change/lib/hash-comparator-chain.php',
71
+ 'ITSEC_File_Change_Hash_Comparator_Loadable' => $baseDir . '/core/modules/file-change/lib/hash-comparator-loadable.php',
72
+ 'ITSEC_File_Change_Hash_Comparator_Managed_Files' => $baseDir . '/core/modules/file-change/lib/hash-comparator-managed-files.php',
73
+ 'ITSEC_File_Change_Hash_Loading_Failed_Exception' => $baseDir . '/core/modules/file-change/lib/hash-loading-failed-exception.php',
74
+ 'ITSEC_File_Change_Logs' => $baseDir . '/core/modules/file-change/logs.php',
75
+ 'ITSEC_File_Change_Package' => $baseDir . '/core/modules/file-change/lib/package.php',
76
+ 'ITSEC_File_Change_Package_Core' => $baseDir . '/core/modules/file-change/lib/package-core.php',
77
+ 'ITSEC_File_Change_Package_Factory' => $baseDir . '/core/modules/file-change/lib/package-factory.php',
78
+ 'ITSEC_File_Change_Package_Plugin' => $baseDir . '/core/modules/file-change/lib/package-plugin.php',
79
+ 'ITSEC_File_Change_Package_System' => $baseDir . '/core/modules/file-change/lib/package-system.php',
80
+ 'ITSEC_File_Change_Package_Theme' => $baseDir . '/core/modules/file-change/lib/package-theme.php',
81
+ 'ITSEC_File_Change_Package_Unknown' => $baseDir . '/core/modules/file-change/lib/package-unknown.php',
82
+ 'ITSEC_File_Change_Scanner' => $baseDir . '/core/modules/file-change/scanner.php',
83
+ 'ITSEC_File_Change_Settings' => $baseDir . '/core/modules/file-change/settings.php',
84
+ 'ITSEC_File_Change_Settings_Page' => $baseDir . '/core/modules/file-change/settings-page.php',
85
+ 'ITSEC_File_Change_Setup' => $baseDir . '/core/modules/file-change/setup.php',
86
+ 'ITSEC_File_Change_Validator' => $baseDir . '/core/modules/file-change/validator.php',
87
+ 'ITSEC_File_Permissions_Settings_Page' => $baseDir . '/core/modules/file-permissions/settings-page.php',
88
+ 'ITSEC_Fingerprint' => $baseDir . '/core/lib/fingerprinting/class-itsec-fingerprint.php',
89
+ 'ITSEC_Fingerprint_Comparison' => $baseDir . '/core/lib/fingerprinting/class-itsec-fingerprint-comparison.php',
90
+ 'ITSEC_Fingerprint_Source' => $baseDir . '/core/lib/fingerprinting/interface-itsec-fingerprint-source.php',
91
+ 'ITSEC_Fingerprint_Value' => $baseDir . '/core/lib/fingerprinting/class-itsec-fingerprint-value.php',
92
+ 'ITSEC_Form' => $baseDir . '/core/lib/form.php',
93
+ 'ITSEC_Four_Oh_Four' => $baseDir . '/core/modules/404-detection/class-itsec-four-oh-four.php',
94
+ 'ITSEC_Four_Oh_Four_Logs' => $baseDir . '/core/modules/404-detection/logs.php',
95
+ 'ITSEC_Four_Oh_Four_Settings' => $baseDir . '/core/modules/404-detection/settings.php',
96
+ 'ITSEC_Four_Oh_Four_Setup' => $baseDir . '/core/modules/404-detection/setup.php',
97
+ 'ITSEC_Four_Oh_Four_Validator' => $baseDir . '/core/modules/404-detection/validator.php',
98
+ 'ITSEC_Geolocator' => $baseDir . '/core/lib/geolocation/interface-itsec-geolocator.php',
99
+ 'ITSEC_Geolocator_Chain' => $baseDir . '/core/lib/geolocation/class-itsec-geolocator-chain.php',
100
+ 'ITSEC_Geolocator_Page_Cache' => $baseDir . '/core/lib/geolocation/class-itsec-geolocator-page-cache.php',
101
+ 'ITSEC_Global_Logs' => $baseDir . '/core/modules/global/logs.php',
102
+ 'ITSEC_Global_Privacy' => $baseDir . '/core/modules/global/privacy.php',
103
+ 'ITSEC_Global_Settings_New' => $baseDir . '/core/modules/global/settings.php',
104
+ 'ITSEC_Global_Settings_Page' => $baseDir . '/core/modules/global/settings-page.php',
105
+ 'ITSEC_Global_Setup' => $baseDir . '/core/modules/global/setup.php',
106
+ 'ITSEC_Global_Validator' => $baseDir . '/core/modules/global/validator.php',
107
+ 'ITSEC_Hide_Backend' => $baseDir . '/core/modules/hide-backend/class-itsec-hide-backend.php',
108
+ 'ITSEC_Hide_Backend_Privacy' => $baseDir . '/core/modules/hide-backend/privacy.php',
109
+ 'ITSEC_Hide_Backend_Settings' => $baseDir . '/core/modules/hide-backend/settings.php',
110
+ 'ITSEC_Hide_Backend_Settings_Page' => $baseDir . '/core/modules/hide-backend/settings-page.php',
111
+ 'ITSEC_Hide_Backend_Setup' => $baseDir . '/core/modules/hide-backend/setup.php',
112
+ 'ITSEC_Hide_Backend_Validator' => $baseDir . '/core/modules/hide-backend/validator.php',
113
+ 'ITSEC_IPCheck' => $baseDir . '/core/modules/ipcheck/class-itsec-ipcheck.php',
114
+ 'ITSEC_IPCheck_Logs' => $baseDir . '/core/modules/ipcheck/logs.php',
115
+ 'ITSEC_IPCheck_Setup' => $baseDir . '/core/modules/ipcheck/setup.php',
116
+ 'ITSEC_IP_Detector' => $baseDir . '/core/lib/class-itsec-ip-detector.php',
117
+ 'ITSEC_Import_Export_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
118
+ 'ITSEC_Ithemes_Sync_Upgrader_Skin' => $baseDir . '/core/modules/sync-connect/includes/upgrader-skin.php',
119
+ 'ITSEC_Job' => $baseDir . '/core/lib/class-itsec-job.php',
120
+ 'ITSEC_Lib_Admin_Notices' => $baseDir . '/core/lib/class-itsec-lib-admin-notices.php',
121
+ 'ITSEC_Lib_Browser' => $baseDir . '/core/lib/class-itsec-lib-browser.php',
122
+ 'ITSEC_Lib_Canonical_Roles' => $baseDir . '/core/lib/class-itsec-lib-canonical-roles.php',
123
+ 'ITSEC_Lib_Config_File' => $baseDir . '/core/lib/class-itsec-lib-config-file.php',
124
+ 'ITSEC_Lib_Directory' => $baseDir . '/core/lib/class-itsec-lib-directory.php',
125
+ 'ITSEC_Lib_Distributed_Storage' => $baseDir . '/core/lib/class-itsec-lib-distributed-storage.php',
126
+ 'ITSEC_Lib_Distributed_Storage_Cursor' => $baseDir . '/core/lib/class-itsec-lib-distributed-storage.php',
127
+ 'ITSEC_Lib_Email_Confirmation' => $baseDir . '/core/lib/class-itsec-lib-email-confirmation.php',
128
+ 'ITSEC_Lib_Feature_Flags' => $baseDir . '/core/lib/class-itsec-lib-feature-flags.php',
129
+ 'ITSEC_Lib_File' => $baseDir . '/core/lib/class-itsec-lib-file.php',
130
+ 'ITSEC_Lib_Fingerprinting' => $baseDir . '/core/lib/class-itsec-lib-fingerprinting.php',
131
+ 'ITSEC_Lib_Geolocation' => $baseDir . '/core/lib/class-itsec-lib-geolocation.php',
132
+ 'ITSEC_Lib_Highlighted_Logs' => $baseDir . '/core/lib/class-itsec-lib-highlighted-logs.php',
133
+ 'ITSEC_Lib_IP_Detector' => $baseDir . '/core/lib/class-itsec-lib-ip-detector.php',
134
+ 'ITSEC_Lib_IP_Tools' => $baseDir . '/core/lib/class-itsec-lib-ip-tools.php',
135
+ 'ITSEC_Lib_Login' => $baseDir . '/core/lib/class-itsec-lib-login.php',
136
+ 'ITSEC_Lib_Login_Interstitial' => $baseDir . '/core/lib/class-itsec-lib-login-interstitial.php',
137
+ 'ITSEC_Lib_Opaque_Tokens' => $baseDir . '/core/lib/class-itsec-lib-opaque-tokens.php',
138
+ 'ITSEC_Lib_Password_Requirements' => $baseDir . '/core/lib/class-itsec-lib-password-requirements.php',
139
+ 'ITSEC_Lib_REST' => $baseDir . '/core/lib/class-itsec-lib-rest.php',
140
+ 'ITSEC_Lib_Remote_Messages' => $baseDir . '/core/lib/class-itsec-lib-remote-messages.php',
141
+ 'ITSEC_Lib_Static_Map_API' => $baseDir . '/core/lib/class-itsec-lib-static-map-api.php',
142
+ 'ITSEC_Lib_User_Activity' => $baseDir . '/core/lib/class-itsec-lib-user-activity.php',
143
+ 'ITSEC_Lib_Utility' => $baseDir . '/core/lib/class-itsec-lib-utility.php',
144
+ 'ITSEC_Log' => $baseDir . '/core/lib/log.php',
145
+ 'ITSEC_Log_Util' => $baseDir . '/core/lib/log-util.php',
146
+ 'ITSEC_Login_Interstitial' => $baseDir . '/core/lib/login-interstitial/abstract-itsec-login-interstitial.php',
147
+ 'ITSEC_Login_Interstitial_Config_Driven' => $baseDir . '/core/lib/login-interstitial/class-itsec-login-interstitial-config-driven.php',
148
+ 'ITSEC_Login_Interstitial_Session' => $baseDir . '/core/lib/login-interstitial/class-itsec-login-interstitial-session.php',
149
+ 'ITSEC_Magic_Links_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
150
+ 'ITSEC_Mail' => $baseDir . '/core/lib/class-itsec-mail.php',
151
+ 'ITSEC_Malware' => $baseDir . '/core/modules/malware/class-itsec-malware.php',
152
+ 'ITSEC_Malware_Logs' => $baseDir . '/core/modules/malware/logs.php',
153
+ 'ITSEC_Malware_Privacy' => $baseDir . '/core/modules/malware/privacy.php',
154
+ 'ITSEC_Malware_Scan_Results_Template' => $baseDir . '/core/modules/malware/class-itsec-malware-scan-results-template.php',
155
+ 'ITSEC_Malware_Scanner' => $baseDir . '/core/modules/malware/class-itsec-malware-scanner.php',
156
+ 'ITSEC_Malware_Scheduling_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
157
+ 'ITSEC_Malware_Setup' => $baseDir . '/core/modules/malware/setup.php',
158
+ 'ITSEC_Multisite_Tweaks' => $baseDir . '/core/modules/multisite-tweaks/class-itsec-multisite-tweaks.php',
159
+ 'ITSEC_Multisite_Tweaks_Settings' => $baseDir . '/core/modules/multisite-tweaks/settings.php',
160
+ 'ITSEC_Multisite_Tweaks_Settings_Page' => $baseDir . '/core/modules/multisite-tweaks/settings-page.php',
161
+ 'ITSEC_Multisite_Tweaks_Setup' => $baseDir . '/core/modules/multisite-tweaks/setup.php',
162
+ 'ITSEC_Multisite_Tweaks_Validator' => $baseDir . '/core/modules/multisite-tweaks/validator.php',
163
+ 'ITSEC_Mutex' => $baseDir . '/core/lib/class-itsec-mutex.php',
164
+ 'ITSEC_Network_Brute_Force_Settings' => $baseDir . '/core/modules/ipcheck/settings.php',
165
+ 'ITSEC_Network_Brute_Force_Settings_Page' => $baseDir . '/core/modules/ipcheck/settings-page.php',
166
+ 'ITSEC_Network_Brute_Force_Utilities' => $baseDir . '/core/modules/ipcheck/utilities.php',
167
+ 'ITSEC_Network_Brute_Force_Validator' => $baseDir . '/core/modules/ipcheck/validator.php',
168
+ 'ITSEC_Network_Bruteforce_Privacy' => $baseDir . '/core/modules/ipcheck/privacy.php',
169
+ 'ITSEC_Notification_Center' => $baseDir . '/core/modules/notification-center/class-notification-center.php',
170
+ 'ITSEC_Notification_Center_Debug' => $baseDir . '/core/modules/notification-center/debug.php',
171
+ 'ITSEC_Notification_Center_Logs' => $baseDir . '/core/modules/notification-center/logs.php',
172
+ 'ITSEC_Notification_Center_Settings' => $baseDir . '/core/modules/notification-center/settings.php',
173
+ 'ITSEC_Notification_Center_Settings_Page' => $baseDir . '/core/modules/notification-center/settings-page.php',
174
+ 'ITSEC_Notification_Center_Setup' => $baseDir . '/core/modules/notification-center/setup.php',
175
+ 'ITSEC_Notification_Center_Validator' => $baseDir . '/core/modules/notification-center/validator.php',
176
+ 'ITSEC_Password_Requirements' => $baseDir . '/core/modules/password-requirements/class-itsec-password-requirements.php',
177
+ 'ITSEC_Password_Requirements_Settings' => $baseDir . '/core/modules/password-requirements/settings.php',
178
+ 'ITSEC_Password_Requirements_Settings_Page' => $baseDir . '/core/modules/password-requirements/settings-page.php',
179
+ 'ITSEC_Password_Requirements_Validator' => $baseDir . '/core/modules/password-requirements/validator.php',
180
+ 'ITSEC_Passwordless_Login_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
181
+ 'ITSEC_Privacy' => $baseDir . '/core/modules/privacy/class-itsec-privacy.php',
182
+ 'ITSEC_Privacy_Util' => $baseDir . '/core/modules/privacy/util.php',
183
+ 'ITSEC_Privilege_Escalation_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
184
+ 'ITSEC_Pro_Setup' => $baseDir . '/core/modules/pro/setup.php',
185
+ 'ITSEC_REST_Core_Admin_Notices_Controller' => $baseDir . '/core/modules/core/class-rest-core-admin-notices-controller.php',
186
+ 'ITSEC_Recaptcha_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
187
+ 'ITSEC_SSL' => $baseDir . '/core/modules/ssl/class-itsec-ssl.php',
188
+ 'ITSEC_SSL_Admin' => $baseDir . '/core/modules/ssl/class-itsec-ssl-admin.php',
189
+ 'ITSEC_SSL_Settings' => $baseDir . '/core/modules/ssl/settings.php',
190
+ 'ITSEC_SSL_Settings_Page' => $baseDir . '/core/modules/ssl/settings-page.php',
191
+ 'ITSEC_SSL_Setup' => $baseDir . '/core/modules/ssl/setup.php',
192
+ 'ITSEC_SSL_Validator' => $baseDir . '/core/modules/ssl/validator.php',
193
+ 'ITSEC_Salts_Setup' => $baseDir . '/core/modules/salts/setup.php',
194
+ 'ITSEC_Scheduler' => $baseDir . '/core/lib/class-itsec-scheduler.php',
195
+ 'ITSEC_Scheduler_Cron' => $baseDir . '/core/lib/class-itsec-scheduler-cron.php',
196
+ 'ITSEC_Scheduler_Page_Load' => $baseDir . '/core/lib/class-itsec-scheduler-page-load.php',
197
+ 'ITSEC_Schema' => $baseDir . '/core/lib/schema.php',
198
+ 'ITSEC_Security_Check_Feedback' => $baseDir . '/core/modules/security-check/feedback.php',
199
+ 'ITSEC_Security_Check_Feedback_Renderer' => $baseDir . '/core/modules/security-check/feedback-renderer.php',
200
+ 'ITSEC_Security_Check_Pro' => $baseDir . '/core/modules/security-check-pro/class-itsec-security-check-pro.php',
201
+ 'ITSEC_Security_Check_Pro_Debug' => $baseDir . '/core/modules/security-check-pro/debug.php',
202
+ 'ITSEC_Security_Check_Pro_Privacy' => $baseDir . '/core/modules/security-check-pro/privacy.php',
203
+ 'ITSEC_Security_Check_Pro_Settings' => $baseDir . '/core/modules/security-check-pro/settings.php',
204
+ 'ITSEC_Security_Check_Pro_Settings_Page' => $baseDir . '/core/modules/security-check-pro/settings-page.php',
205
+ 'ITSEC_Security_Check_Pro_Utility' => $baseDir . '/core/modules/security-check-pro/utility.php',
206
+ 'ITSEC_Security_Check_Pro_Validator' => $baseDir . '/core/modules/security-check-pro/validator.php',
207
+ 'ITSEC_Security_Check_Scanner' => $baseDir . '/core/modules/security-check/scanner.php',
208
+ 'ITSEC_Security_Check_Settings_Page' => $baseDir . '/core/modules/security-check/settings-page.php',
209
+ 'ITSEC_Settings' => $baseDir . '/core/lib/settings.php',
210
+ 'ITSEC_Settings_Page_Sidebar_Widget_BackupBuddy_Cross_Promo' => $baseDir . '/core/modules/core/sidebar-widget-backupbuddy-cross-promo.php',
211
+ 'ITSEC_Settings_Page_Sidebar_Widget_Free_Support' => $baseDir . '/core/modules/core/sidebar-widget-support.php',
212
+ 'ITSEC_Settings_Page_Sidebar_Widget_Mail_List_Signup' => $baseDir . '/core/modules/core/sidebar-widget-mail-list-signup.php',
213
+ 'ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan' => $baseDir . '/core/modules/malware/settings-page.php',
214
+ 'ITSEC_Settings_Page_Sidebar_Widget_Pro_Upsell' => $baseDir . '/core/modules/core/sidebar-widget-pro-upsell.php',
215
+ 'ITSEC_Settings_Page_Sidebar_Widget_Sync_Cross_Promo' => $baseDir . '/core/modules/core/sidebar-widget-sync-cross-promo.php',
216
+ 'ITSEC_Static_Map_API' => $baseDir . '/core/lib/static-map-api/interface-itsec-static-map-api.php',
217
+ 'ITSEC_Storage' => $baseDir . '/core/lib/storage.php',
218
+ 'ITSEC_Strong_Passwords' => $baseDir . '/core/modules/strong-passwords/class-itsec-strong-passwords.php',
219
+ 'ITSEC_Strong_Passwords_Settings' => $baseDir . '/core/modules/strong-passwords/settings.php',
220
+ 'ITSEC_Strong_Passwords_Setup' => $baseDir . '/core/modules/strong-passwords/setup.php',
221
+ 'ITSEC_Strong_Passwords_Validator' => $baseDir . '/core/modules/strong-passwords/validator.php',
222
+ 'ITSEC_Sync_Connect' => $baseDir . '/core/modules/sync-connect/class-itsec-sync-connect.php',
223
+ 'ITSEC_Sync_Connect_Interstitial' => $baseDir . '/core/modules/sync-connect/class-itsec-sync-connect-interstitial.php',
224
+ 'ITSEC_System_Tweaks' => $baseDir . '/core/modules/system-tweaks/class-itsec-system-tweaks.php',
225
+ 'ITSEC_System_Tweaks_Config_Generators' => $baseDir . '/core/modules/system-tweaks/config-generators.php',
226
+ 'ITSEC_System_Tweaks_Settings' => $baseDir . '/core/modules/system-tweaks/settings.php',
227
+ 'ITSEC_System_Tweaks_Settings_Page' => $baseDir . '/core/modules/system-tweaks/settings-page.php',
228
+ 'ITSEC_System_Tweaks_Setup' => $baseDir . '/core/modules/system-tweaks/setup.php',
229
+ 'ITSEC_System_Tweaks_Validator' => $baseDir . '/core/modules/system-tweaks/validator.php',
230
+ 'ITSEC_Two_Factor_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
231
+ 'ITSEC_User_Logging_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
232
+ 'ITSEC_User_Security_Check_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
233
+ 'ITSEC_Validator' => $baseDir . '/core/lib/validator.php',
234
+ 'ITSEC_Version_Management_Settings_Page' => $baseDir . '/core/modules/pro/settings-page.php',
235
+ 'ITSEC_WP_List_Table' => $baseDir . '/core/lib/class-itsec-wp-list-table.php',
236
+ 'ITSEC_WordPress_Salts_Settings' => $baseDir . '/core/modules/salts/settings.php',
237
+ 'ITSEC_WordPress_Salts_Settings_Page' => $baseDir . '/core/modules/salts/settings-page.php',
238
+ 'ITSEC_WordPress_Salts_Utilities' => $baseDir . '/core/modules/salts/utilities.php',
239
+ 'ITSEC_WordPress_Salts_Validator' => $baseDir . '/core/modules/salts/validator.php',
240
+ 'ITSEC_WordPress_Tweaks' => $baseDir . '/core/modules/wordpress-tweaks/class-itsec-wordpress-tweaks.php',
241
+ 'ITSEC_WordPress_Tweaks_Config_Generators' => $baseDir . '/core/modules/wordpress-tweaks/config-generators.php',
242
+ 'ITSEC_WordPress_Tweaks_Settings_Page' => $baseDir . '/core/modules/wordpress-tweaks/settings-page.php',
243
+ 'ITSEC_WordPress_Tweaks_Setup' => $baseDir . '/core/modules/wordpress-tweaks/setup.php',
244
+ 'ITSEC_WordPress_Tweaks_Validator' => $baseDir . '/core/modules/wordpress-tweaks/validator.php',
245
+ 'ITSEC_Wordpress_Tweaks_Settings' => $baseDir . '/core/modules/wordpress-tweaks/settings.php',
246
+ 'Ithemes_Sync_Verb_ITSEC_Do_Security_Check' => $baseDir . '/core/modules/security-check/sync-verbs/itsec-do-security-check.php',
247
+ 'Ithemes_Sync_Verb_ITSEC_Get_Away_Mode' => $baseDir . '/core/modules/away-mode/sync-verbs/itsec-get-away-mode.php',
248
+ 'Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Log' => $baseDir . '/core/modules/malware/sync-verbs/itsec-get-malware-scan-log.php',
249
+ 'Ithemes_Sync_Verb_ITSEC_Get_Security_Check_Feedback_Response' => $baseDir . '/core/modules/security-check/sync-verbs/itsec-get-security-check-feedback-response.php',
250
+ 'Ithemes_Sync_Verb_ITSEC_Get_Security_Check_Modules' => $baseDir . '/core/modules/security-check/sync-verbs/itsec-get-security-check-modules.php',
251
+ 'Ithemes_Sync_Verb_ITSEC_Latest_File_Scan' => $baseDir . '/core/modules/file-change/sync-verbs/itsec-latest-file-scan.php',
252
+ 'Ithemes_Sync_Verb_ITSEC_Malware_Do_Scan' => $baseDir . '/core/modules/malware/sync-verbs/itsec-do-malware-scan.php',
253
+ 'Ithemes_Sync_Verb_ITSEC_Override_Away_Mode' => $baseDir . '/core/modules/away-mode/sync-verbs/itsec-override-away-mode.php',
254
+ 'Pimple\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Container.php',
255
+ 'Pimple\\Exception\\ExpectedInvokableException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php',
256
+ 'Pimple\\Exception\\FrozenServiceException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php',
257
+ 'Pimple\\Exception\\InvalidServiceIdentifierException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php',
258
+ 'Pimple\\Exception\\UnknownIdentifierException' => $vendorDir . '/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php',
259
+ 'Pimple\\Psr11\\Container' => $vendorDir . '/pimple/pimple/src/Pimple/Psr11/Container.php',
260
+ 'Pimple\\Psr11\\ServiceLocator' => $vendorDir . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php',
261
+ 'Pimple\\ServiceIterator' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceIterator.php',
262
+ 'Pimple\\ServiceProviderInterface' => $vendorDir . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php',
263
+ 'Pimple\\Tests\\Fixtures\\Invokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php',
264
+ 'Pimple\\Tests\\Fixtures\\NonInvokable' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php',
265
+ 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php',
266
+ 'Pimple\\Tests\\Fixtures\\Service' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php',
267
+ 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php',
268
+ 'Pimple\\Tests\\PimpleTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php',
269
+ 'Pimple\\Tests\\Psr11\\ContainerTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php',
270
+ 'Pimple\\Tests\\Psr11\\ServiceLocatorTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php',
271
+ 'Pimple\\Tests\\ServiceIteratorTest' => $vendorDir . '/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php',
272
+ 'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
273
+ 'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',
274
+ 'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php',
275
+ 'Wikimedia\\Composer\\Logger' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Logger.php',
276
+ 'Wikimedia\\Composer\\MergePlugin' => $vendorDir . '/wikimedia/composer-merge-plugin/src/MergePlugin.php',
277
+ 'Wikimedia\\Composer\\Merge\\ExtraPackage' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php',
278
+ 'Wikimedia\\Composer\\Merge\\MissingFileException' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php',
279
+ 'Wikimedia\\Composer\\Merge\\NestedArray' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Merge/NestedArray.php',
280
+ 'Wikimedia\\Composer\\Merge\\PluginState' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Merge/PluginState.php',
281
+ 'Wikimedia\\Composer\\Merge\\StabilityFlags' => $vendorDir . '/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php',
282
+ 'iThemesSecurity\\Contracts\\Runnable' => $baseDir . '/core/Contracts/Runnable.php',
283
+ 'iThemesSecurity\\Exception\\Exception' => $baseDir . '/core/Exception/Exception.php',
284
+ 'iThemesSecurity\\Exception\\Invalid_Argument_Exception' => $baseDir . '/core/Exception/Invalid_Argument_Exception.php',
285
+ 'iThemesSecurity\\Exception\\Unsatisfied_Module_Dependencies_Exception' => $baseDir . '/core/Exception/Unsatisfied_Module_Dependencies_Exception.php',
286
+ 'iThemesSecurity\\Exception\\WP_Error' => $baseDir . '/core/Exception/WP_Error.php',
287
+ 'iThemesSecurity\\Lib\\Lockout\\Context' => $baseDir . '/core/lib/lockout/abstract-context.php',
288
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Context' => $baseDir . '/core/lib/lockout/execute-lock/abstract-context.php',
289
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Host_Context' => $baseDir . '/core/lib/lockout/execute-lock/class-host-context.php',
290
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Configurable' => $baseDir . '/core/lib/lockout/execute-lock/source/class-configurable.php',
291
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Lockout_Module' => $baseDir . '/core/lib/lockout/execute-lock/source/class-lockout-module.php',
292
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Source' => $baseDir . '/core/lib/lockout/execute-lock/source/interface-source.php',
293
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\User_Context' => $baseDir . '/core/lib/lockout/execute-lock/class-user-context.php',
294
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Username_Context' => $baseDir . '/core/lib/lockout/execute-lock/class-username-context.php',
295
+ 'iThemesSecurity\\Lib\\Lockout\\Host_Context' => $baseDir . '/core/lib/lockout/class-host-context.php',
296
+ 'iThemesSecurity\\Lib\\Lockout\\Lockout' => $baseDir . '/core/lib/lockout/class-lockout.php',
297
+ 'iThemesSecurity\\Lib\\Lockout\\User_Context' => $baseDir . '/core/lib/lockout/class-user-context.php',
298
+ 'iThemesSecurity\\Lib\\Lockout\\Username_Context' => $baseDir . '/core/lib/lockout/class-username-context.php',
299
+ 'iThemesSecurity\\User_Groups\\All_Users' => $baseDir . '/core/modules/user-groups/All_Users.php',
300
+ 'iThemesSecurity\\User_Groups\\Default_Matcher' => $baseDir . '/core/modules/user-groups/Match/Default_Matcher.php',
301
+ 'iThemesSecurity\\User_Groups\\Everybody_Else' => $baseDir . '/core/modules/user-groups/Everybody_Else.php',
302
+ 'iThemesSecurity\\User_Groups\\Match_Target' => $baseDir . '/core/modules/user-groups/Match/Match_Target.php',
303
+ 'iThemesSecurity\\User_Groups\\Matchable' => $baseDir . '/core/modules/user-groups/Matchable.php',
304
+ 'iThemesSecurity\\User_Groups\\Matchable_Not_Found' => $baseDir . '/core/modules/user-groups/Match/Matchable_Not_Found.php',
305
+ 'iThemesSecurity\\User_Groups\\Matchables_Source' => $baseDir . '/core/modules/user-groups/Matchables_Source.php',
306
+ 'iThemesSecurity\\User_Groups\\Matcher' => $baseDir . '/core/modules/user-groups/Match/Matcher.php',
307
+ 'iThemesSecurity\\User_Groups\\Module\\Module' => $baseDir . '/core/modules/user-groups/Module/Module.php',
308
+ 'iThemesSecurity\\User_Groups\\Module\\Settings' => $baseDir . '/core/modules/user-groups/Module/Settings.php',
309
+ 'iThemesSecurity\\User_Groups\\Module\\Validator' => $baseDir . '/core/modules/user-groups/Module/Validator.php',
310
+ 'iThemesSecurity\\User_Groups\\REST\\Matchables' => $baseDir . '/core/modules/user-groups/REST/Matchables.php',
311
+ 'iThemesSecurity\\User_Groups\\REST\\REST' => $baseDir . '/core/modules/user-groups/REST/REST.php',
312
+ 'iThemesSecurity\\User_Groups\\REST\\Settings' => $baseDir . '/core/modules/user-groups/REST/Settings.php',
313
+ 'iThemesSecurity\\User_Groups\\REST\\User_Groups' => $baseDir . '/core/modules/user-groups/REST/User_Groups.php',
314
+ 'iThemesSecurity\\User_Groups\\Repository\\DB_Repository' => $baseDir . '/core/modules/user-groups/Repository/DB_Repository.php',
315
+ 'iThemesSecurity\\User_Groups\\Repository\\Decorator' => $baseDir . '/core/modules/user-groups/Repository/Decorator.php',
316
+ 'iThemesSecurity\\User_Groups\\Repository\\Eager_Loading_Decorator' => $baseDir . '/core/modules/user-groups/Repository/Eager_Loading_Decorator.php',
317
+ 'iThemesSecurity\\User_Groups\\Repository\\In_Memory_Repository' => $baseDir . '/core/modules/user-groups/Repository/In_Memory_Repository.php',
318
+ 'iThemesSecurity\\User_Groups\\Repository\\Object_Caching_Decorator' => $baseDir . '/core/modules/user-groups/Repository/Object_Caching_Decorator.php',
319
+ 'iThemesSecurity\\User_Groups\\Repository\\Repository' => $baseDir . '/core/modules/user-groups/Repository/Repository.php',
320
+ 'iThemesSecurity\\User_Groups\\Repository\\User_Group_Not_Found' => $baseDir . '/core/modules/user-groups/Repository/User_Group_Not_Found.php',
321
+ 'iThemesSecurity\\User_Groups\\Settings_Page' => $baseDir . '/core/modules/user-groups/settings-page.php',
322
+ 'iThemesSecurity\\User_Groups\\Settings_Proxy' => $baseDir . '/core/modules/user-groups/Settings/Settings_Proxy.php',
323
+ 'iThemesSecurity\\User_Groups\\Settings_Registration' => $baseDir . '/core/modules/user-groups/Settings/Settings_Registration.php',
324
+ 'iThemesSecurity\\User_Groups\\Settings_Registry' => $baseDir . '/core/modules/user-groups/Settings/Settings_Registry.php',
325
+ 'iThemesSecurity\\User_Groups\\Upgrader' => $baseDir . '/core/modules/user-groups/Upgrader.php',
326
+ 'iThemesSecurity\\User_Groups\\User_Group' => $baseDir . '/core/modules/user-groups/User_Group.php',
327
+ );
vendor-prod/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Pimple' => array($vendorDir . '/pimple/pimple/src'),
10
+ );
vendor-prod/composer/autoload_psr4.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'Wikimedia\\Composer\\' => array($vendorDir . '/wikimedia/composer-merge-plugin/src'),
10
+ 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
11
+ );
vendor-prod/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit35a2bd4feb347da0d3ea2d8ef023082f
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInit35a2bd4feb347da0d3ea2d8ef023082f', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit35a2bd4feb347da0d3ea2d8ef023082f', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
vendor-prod/composer/autoload_static.php ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'W' =>
11
+ array (
12
+ 'Wikimedia\\Composer\\' => 19,
13
+ ),
14
+ 'P' =>
15
+ array (
16
+ 'Psr\\Container\\' => 14,
17
+ ),
18
+ );
19
+
20
+ public static $prefixDirsPsr4 = array (
21
+ 'Wikimedia\\Composer\\' =>
22
+ array (
23
+ 0 => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src',
24
+ ),
25
+ 'Psr\\Container\\' =>
26
+ array (
27
+ 0 => __DIR__ . '/..' . '/psr/container/src',
28
+ ),
29
+ );
30
+
31
+ public static $prefixesPsr0 = array (
32
+ 'P' =>
33
+ array (
34
+ 'Pimple' =>
35
+ array (
36
+ 0 => __DIR__ . '/..' . '/pimple/pimple/src',
37
+ ),
38
+ ),
39
+ );
40
+
41
+ public static $classMap = array (
42
+ 'ITSEC_404_Detection_Settings_Page' => __DIR__ . '/../..' . '/core/modules/404-detection/settings-page.php',
43
+ 'ITSEC_Admin_Notice' => __DIR__ . '/../..' . '/core/lib/admin-notices/interface-itsec-admin-notice.php',
44
+ 'ITSEC_Admin_Notice_Action' => __DIR__ . '/../..' . '/core/lib/admin-notices/actions/interface-itsec-admin-notice-action.php',
45
+ 'ITSEC_Admin_Notice_Action_Callback' => __DIR__ . '/../..' . '/core/lib/admin-notices/actions/class-itsec-admin-notice-action-callback.php',
46
+ 'ITSEC_Admin_Notice_Action_Link' => __DIR__ . '/../..' . '/core/lib/admin-notices/actions/class-itsec-admin-notice-action-link.php',
47
+ 'ITSEC_Admin_Notice_Callback' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-callback.php',
48
+ 'ITSEC_Admin_Notice_Context' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-context.php',
49
+ 'ITSEC_Admin_Notice_Globally_Dismissible' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-globally-dismissible.php',
50
+ 'ITSEC_Admin_Notice_Highlighted_Log' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-highlighted-log.php',
51
+ 'ITSEC_Admin_Notice_Managers_Only' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-managers-only.php',
52
+ 'ITSEC_Admin_Notice_Network_Brute_Force_Promo' => __DIR__ . '/../..' . '/core/modules/global/notices.php',
53
+ 'ITSEC_Admin_Notice_New_Feature_Core' => __DIR__ . '/../..' . '/core/modules/core/notices.php',
54
+ 'ITSEC_Admin_Notice_Remind_Me' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-remind-me.php',
55
+ 'ITSEC_Admin_Notice_Screen_Blacklist' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-screen-blacklist.php',
56
+ 'ITSEC_Admin_Notice_Static' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-static.php',
57
+ 'ITSEC_Admin_Notice_User_Dismissible' => __DIR__ . '/../..' . '/core/lib/admin-notices/class-itsec-admin-notice-user-dismissible.php',
58
+ 'ITSEC_Admin_Notices' => __DIR__ . '/../..' . '/core/modules/core/class-itsec-admin-notices.php',
59
+ 'ITSEC_Admin_User_Settings' => __DIR__ . '/../..' . '/core/modules/admin-user/settings.php',
60
+ 'ITSEC_Admin_User_Settings_Page' => __DIR__ . '/../..' . '/core/modules/admin-user/settings-page.php',
61
+ 'ITSEC_Admin_User_Validator' => __DIR__ . '/../..' . '/core/modules/admin-user/validator.php',
62
+ 'ITSEC_Away_Mode' => __DIR__ . '/../..' . '/core/modules/away-mode/class-itsec-away-mode.php',
63
+ 'ITSEC_Away_Mode_Logs' => __DIR__ . '/../..' . '/core/modules/away-mode/logs.php',
64
+ 'ITSEC_Away_Mode_Settings' => __DIR__ . '/../..' . '/core/modules/away-mode/settings.php',
65
+ 'ITSEC_Away_Mode_Settings_Page' => __DIR__ . '/../..' . '/core/modules/away-mode/settings-page.php',
66
+ 'ITSEC_Away_Mode_Setup' => __DIR__ . '/../..' . '/core/modules/away-mode/setup.php',
67
+ 'ITSEC_Away_Mode_Utilities' => __DIR__ . '/../..' . '/core/modules/away-mode/utilities.php',
68
+ 'ITSEC_Away_Mode_Validator' => __DIR__ . '/../..' . '/core/modules/away-mode/validator.php',
69
+ 'ITSEC_Backup' => __DIR__ . '/../..' . '/core/modules/backup/class-itsec-backup.php',
70
+ 'ITSEC_Backup_Logs' => __DIR__ . '/../..' . '/core/modules/backup/logs.php',
71
+ 'ITSEC_Backup_Privacy' => __DIR__ . '/../..' . '/core/modules/backup/privacy.php',
72
+ 'ITSEC_Backup_Settings' => __DIR__ . '/../..' . '/core/modules/backup/settings.php',
73
+ 'ITSEC_Backup_Settings_Page' => __DIR__ . '/../..' . '/core/modules/backup/settings-page.php',
74
+ 'ITSEC_Backup_Setup' => __DIR__ . '/../..' . '/core/modules/backup/setup.php',
75
+ 'ITSEC_Backup_Validator' => __DIR__ . '/../..' . '/core/modules/backup/validator.php',
76
+ 'ITSEC_Ban_Users' => __DIR__ . '/../..' . '/core/modules/ban-users/class-itsec-ban-users.php',
77
+ 'ITSEC_Ban_Users_Config_Generators' => __DIR__ . '/../..' . '/core/modules/ban-users/config-generators.php',
78
+ 'ITSEC_Ban_Users_Settings' => __DIR__ . '/../..' . '/core/modules/ban-users/settings.php',
79
+ 'ITSEC_Ban_Users_Settings_Page' => __DIR__ . '/../..' . '/core/modules/ban-users/settings-page.php',
80
+ 'ITSEC_Ban_Users_Setup' => __DIR__ . '/../..' . '/core/modules/ban-users/setup.php',
81
+ 'ITSEC_Ban_Users_Validator' => __DIR__ . '/../..' . '/core/modules/ban-users/validator.php',
82
+ 'ITSEC_Brute_Force' => __DIR__ . '/../..' . '/core/modules/brute-force/class-itsec-brute-force.php',
83
+ 'ITSEC_Brute_Force_Logs' => __DIR__ . '/../..' . '/core/modules/brute-force/logs.php',
84
+ 'ITSEC_Brute_Force_Settings' => __DIR__ . '/../..' . '/core/modules/brute-force/settings.php',
85
+ 'ITSEC_Brute_Force_Settings_Page' => __DIR__ . '/../..' . '/core/modules/brute-force/settings-page.php',
86
+ 'ITSEC_Brute_Force_Setup' => __DIR__ . '/../..' . '/core/modules/brute-force/setup.php',
87
+ 'ITSEC_Brute_Force_Validator' => __DIR__ . '/../..' . '/core/modules/brute-force/validator.php',
88
+ 'ITSEC_Content_Directory_Settings_Page' => __DIR__ . '/../..' . '/core/modules/content-directory/settings-page.php',
89
+ 'ITSEC_Content_Directory_Utility' => __DIR__ . '/../..' . '/core/modules/content-directory/utility.php',
90
+ 'ITSEC_Core_Active' => __DIR__ . '/../..' . '/core/modules/core/class-itsec-core-active.php',
91
+ 'ITSEC_Core_Admin' => __DIR__ . '/../..' . '/core/modules/core/class-itsec-core-admin.php',
92
+ 'ITSEC_Core_Server_Config_Rules_Settings_Page' => __DIR__ . '/../..' . '/core/modules/file-writing/settings-page.php',
93
+ 'ITSEC_Core_WPConfig_File_Settings_Page' => __DIR__ . '/../..' . '/core/modules/file-writing/settings-page.php',
94
+ 'ITSEC_Database_Prefix_Settings_Page' => __DIR__ . '/../..' . '/core/modules/database-prefix/settings-page.php',
95
+ 'ITSEC_Database_Prefix_Utility' => __DIR__ . '/../..' . '/core/modules/database-prefix/utility.php',
96
+ 'ITSEC_Debug' => __DIR__ . '/../..' . '/core/lib/debug.php',
97
+ 'ITSEC_Email_Confirmation' => __DIR__ . '/../..' . '/core/modules/email-confirmation/class-itsec-email-confirmation.php',
98
+ 'ITSEC_Feature_Flags_Settings_Page' => __DIR__ . '/../..' . '/core/modules/feature-flags/settings-page.php',
99
+ 'ITSEC_File_Change' => __DIR__ . '/../..' . '/core/modules/file-change/class-itsec-file-change.php',
100
+ 'ITSEC_File_Change_Admin' => __DIR__ . '/../..' . '/core/modules/file-change/admin.php',
101
+ 'ITSEC_File_Change_Chunk_Scanner' => __DIR__ . '/../..' . '/core/modules/file-change/lib/chunk-scanner.php',
102
+ 'ITSEC_File_Change_Hash_Comparator' => __DIR__ . '/../..' . '/core/modules/file-change/lib/hash-comparator.php',
103
+ 'ITSEC_File_Change_Hash_Comparator_Chain' => __DIR__ . '/../..' . '/core/modules/file-change/lib/hash-comparator-chain.php',
104
+ 'ITSEC_File_Change_Hash_Comparator_Loadable' => __DIR__ . '/../..' . '/core/modules/file-change/lib/hash-comparator-loadable.php',
105
+ 'ITSEC_File_Change_Hash_Comparator_Managed_Files' => __DIR__ . '/../..' . '/core/modules/file-change/lib/hash-comparator-managed-files.php',
106
+ 'ITSEC_File_Change_Hash_Loading_Failed_Exception' => __DIR__ . '/../..' . '/core/modules/file-change/lib/hash-loading-failed-exception.php',
107
+ 'ITSEC_File_Change_Logs' => __DIR__ . '/../..' . '/core/modules/file-change/logs.php',
108
+ 'ITSEC_File_Change_Package' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package.php',
109
+ 'ITSEC_File_Change_Package_Core' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-core.php',
110
+ 'ITSEC_File_Change_Package_Factory' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-factory.php',
111
+ 'ITSEC_File_Change_Package_Plugin' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-plugin.php',
112
+ 'ITSEC_File_Change_Package_System' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-system.php',
113
+ 'ITSEC_File_Change_Package_Theme' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-theme.php',
114
+ 'ITSEC_File_Change_Package_Unknown' => __DIR__ . '/../..' . '/core/modules/file-change/lib/package-unknown.php',
115
+ 'ITSEC_File_Change_Scanner' => __DIR__ . '/../..' . '/core/modules/file-change/scanner.php',
116
+ 'ITSEC_File_Change_Settings' => __DIR__ . '/../..' . '/core/modules/file-change/settings.php',
117
+ 'ITSEC_File_Change_Settings_Page' => __DIR__ . '/../..' . '/core/modules/file-change/settings-page.php',
118
+ 'ITSEC_File_Change_Setup' => __DIR__ . '/../..' . '/core/modules/file-change/setup.php',
119
+ 'ITSEC_File_Change_Validator' => __DIR__ . '/../..' . '/core/modules/file-change/validator.php',
120
+ 'ITSEC_File_Permissions_Settings_Page' => __DIR__ . '/../..' . '/core/modules/file-permissions/settings-page.php',
121
+ 'ITSEC_Fingerprint' => __DIR__ . '/../..' . '/core/lib/fingerprinting/class-itsec-fingerprint.php',
122
+ 'ITSEC_Fingerprint_Comparison' => __DIR__ . '/../..' . '/core/lib/fingerprinting/class-itsec-fingerprint-comparison.php',
123
+ 'ITSEC_Fingerprint_Source' => __DIR__ . '/../..' . '/core/lib/fingerprinting/interface-itsec-fingerprint-source.php',
124
+ 'ITSEC_Fingerprint_Value' => __DIR__ . '/../..' . '/core/lib/fingerprinting/class-itsec-fingerprint-value.php',
125
+ 'ITSEC_Form' => __DIR__ . '/../..' . '/core/lib/form.php',
126
+ 'ITSEC_Four_Oh_Four' => __DIR__ . '/../..' . '/core/modules/404-detection/class-itsec-four-oh-four.php',
127
+ 'ITSEC_Four_Oh_Four_Logs' => __DIR__ . '/../..' . '/core/modules/404-detection/logs.php',
128
+ 'ITSEC_Four_Oh_Four_Settings' => __DIR__ . '/../..' . '/core/modules/404-detection/settings.php',
129
+ 'ITSEC_Four_Oh_Four_Setup' => __DIR__ . '/../..' . '/core/modules/404-detection/setup.php',
130
+ 'ITSEC_Four_Oh_Four_Validator' => __DIR__ . '/../..' . '/core/modules/404-detection/validator.php',
131
+ 'ITSEC_Geolocator' => __DIR__ . '/../..' . '/core/lib/geolocation/interface-itsec-geolocator.php',
132
+ 'ITSEC_Geolocator_Chain' => __DIR__ . '/../..' . '/core/lib/geolocation/class-itsec-geolocator-chain.php',
133
+ 'ITSEC_Geolocator_Page_Cache' => __DIR__ . '/../..' . '/core/lib/geolocation/class-itsec-geolocator-page-cache.php',
134
+ 'ITSEC_Global_Logs' => __DIR__ . '/../..' . '/core/modules/global/logs.php',
135
+ 'ITSEC_Global_Privacy' => __DIR__ . '/../..' . '/core/modules/global/privacy.php',
136
+ 'ITSEC_Global_Settings_New' => __DIR__ . '/../..' . '/core/modules/global/settings.php',
137
+ 'ITSEC_Global_Settings_Page' => __DIR__ . '/../..' . '/core/modules/global/settings-page.php',
138
+ 'ITSEC_Global_Setup' => __DIR__ . '/../..' . '/core/modules/global/setup.php',
139
+ 'ITSEC_Global_Validator' => __DIR__ . '/../..' . '/core/modules/global/validator.php',
140
+ 'ITSEC_Hide_Backend' => __DIR__ . '/../..' . '/core/modules/hide-backend/class-itsec-hide-backend.php',
141
+ 'ITSEC_Hide_Backend_Privacy' => __DIR__ . '/../..' . '/core/modules/hide-backend/privacy.php',
142
+ 'ITSEC_Hide_Backend_Settings' => __DIR__ . '/../..' . '/core/modules/hide-backend/settings.php',
143
+ 'ITSEC_Hide_Backend_Settings_Page' => __DIR__ . '/../..' . '/core/modules/hide-backend/settings-page.php',
144
+ 'ITSEC_Hide_Backend_Setup' => __DIR__ . '/../..' . '/core/modules/hide-backend/setup.php',
145
+ 'ITSEC_Hide_Backend_Validator' => __DIR__ . '/../..' . '/core/modules/hide-backend/validator.php',
146
+ 'ITSEC_IPCheck' => __DIR__ . '/../..' . '/core/modules/ipcheck/class-itsec-ipcheck.php',
147
+ 'ITSEC_IPCheck_Logs' => __DIR__ . '/../..' . '/core/modules/ipcheck/logs.php',
148
+ 'ITSEC_IPCheck_Setup' => __DIR__ . '/../..' . '/core/modules/ipcheck/setup.php',
149
+ 'ITSEC_IP_Detector' => __DIR__ . '/../..' . '/core/lib/class-itsec-ip-detector.php',
150
+ 'ITSEC_Import_Export_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
151
+ 'ITSEC_Ithemes_Sync_Upgrader_Skin' => __DIR__ . '/../..' . '/core/modules/sync-connect/includes/upgrader-skin.php',
152
+ 'ITSEC_Job' => __DIR__ . '/../..' . '/core/lib/class-itsec-job.php',
153
+ 'ITSEC_Lib_Admin_Notices' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-admin-notices.php',
154
+ 'ITSEC_Lib_Browser' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-browser.php',
155
+ 'ITSEC_Lib_Canonical_Roles' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-canonical-roles.php',
156
+ 'ITSEC_Lib_Config_File' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-config-file.php',
157
+ 'ITSEC_Lib_Directory' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-directory.php',
158
+ 'ITSEC_Lib_Distributed_Storage' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-distributed-storage.php',
159
+ 'ITSEC_Lib_Distributed_Storage_Cursor' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-distributed-storage.php',
160
+ 'ITSEC_Lib_Email_Confirmation' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-email-confirmation.php',
161
+ 'ITSEC_Lib_Feature_Flags' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-feature-flags.php',
162
+ 'ITSEC_Lib_File' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-file.php',
163
+ 'ITSEC_Lib_Fingerprinting' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-fingerprinting.php',
164
+ 'ITSEC_Lib_Geolocation' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-geolocation.php',
165
+ 'ITSEC_Lib_Highlighted_Logs' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-highlighted-logs.php',
166
+ 'ITSEC_Lib_IP_Detector' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-ip-detector.php',
167
+ 'ITSEC_Lib_IP_Tools' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-ip-tools.php',
168
+ 'ITSEC_Lib_Login' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-login.php',
169
+ 'ITSEC_Lib_Login_Interstitial' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-login-interstitial.php',
170
+ 'ITSEC_Lib_Opaque_Tokens' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-opaque-tokens.php',
171
+ 'ITSEC_Lib_Password_Requirements' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-password-requirements.php',
172
+ 'ITSEC_Lib_REST' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-rest.php',
173
+ 'ITSEC_Lib_Remote_Messages' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-remote-messages.php',
174
+ 'ITSEC_Lib_Static_Map_API' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-static-map-api.php',
175
+ 'ITSEC_Lib_User_Activity' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-user-activity.php',
176
+ 'ITSEC_Lib_Utility' => __DIR__ . '/../..' . '/core/lib/class-itsec-lib-utility.php',
177
+ 'ITSEC_Log' => __DIR__ . '/../..' . '/core/lib/log.php',
178
+ 'ITSEC_Log_Util' => __DIR__ . '/../..' . '/core/lib/log-util.php',
179
+ 'ITSEC_Login_Interstitial' => __DIR__ . '/../..' . '/core/lib/login-interstitial/abstract-itsec-login-interstitial.php',
180
+ 'ITSEC_Login_Interstitial_Config_Driven' => __DIR__ . '/../..' . '/core/lib/login-interstitial/class-itsec-login-interstitial-config-driven.php',
181
+ 'ITSEC_Login_Interstitial_Session' => __DIR__ . '/../..' . '/core/lib/login-interstitial/class-itsec-login-interstitial-session.php',
182
+ 'ITSEC_Magic_Links_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
183
+ 'ITSEC_Mail' => __DIR__ . '/../..' . '/core/lib/class-itsec-mail.php',
184
+ 'ITSEC_Malware' => __DIR__ . '/../..' . '/core/modules/malware/class-itsec-malware.php',
185
+ 'ITSEC_Malware_Logs' => __DIR__ . '/../..' . '/core/modules/malware/logs.php',
186
+ 'ITSEC_Malware_Privacy' => __DIR__ . '/../..' . '/core/modules/malware/privacy.php',
187
+ 'ITSEC_Malware_Scan_Results_Template' => __DIR__ . '/../..' . '/core/modules/malware/class-itsec-malware-scan-results-template.php',
188
+ 'ITSEC_Malware_Scanner' => __DIR__ . '/../..' . '/core/modules/malware/class-itsec-malware-scanner.php',
189
+ 'ITSEC_Malware_Scheduling_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
190
+ 'ITSEC_Malware_Setup' => __DIR__ . '/../..' . '/core/modules/malware/setup.php',
191
+ 'ITSEC_Multisite_Tweaks' => __DIR__ . '/../..' . '/core/modules/multisite-tweaks/class-itsec-multisite-tweaks.php',
192
+ 'ITSEC_Multisite_Tweaks_Settings' => __DIR__ . '/../..' . '/core/modules/multisite-tweaks/settings.php',
193
+ 'ITSEC_Multisite_Tweaks_Settings_Page' => __DIR__ . '/../..' . '/core/modules/multisite-tweaks/settings-page.php',
194
+ 'ITSEC_Multisite_Tweaks_Setup' => __DIR__ . '/../..' . '/core/modules/multisite-tweaks/setup.php',
195
+ 'ITSEC_Multisite_Tweaks_Validator' => __DIR__ . '/../..' . '/core/modules/multisite-tweaks/validator.php',
196
+ 'ITSEC_Mutex' => __DIR__ . '/../..' . '/core/lib/class-itsec-mutex.php',
197
+ 'ITSEC_Network_Brute_Force_Settings' => __DIR__ . '/../..' . '/core/modules/ipcheck/settings.php',
198
+ 'ITSEC_Network_Brute_Force_Settings_Page' => __DIR__ . '/../..' . '/core/modules/ipcheck/settings-page.php',
199
+ 'ITSEC_Network_Brute_Force_Utilities' => __DIR__ . '/../..' . '/core/modules/ipcheck/utilities.php',
200
+ 'ITSEC_Network_Brute_Force_Validator' => __DIR__ . '/../..' . '/core/modules/ipcheck/validator.php',
201
+ 'ITSEC_Network_Bruteforce_Privacy' => __DIR__ . '/../..' . '/core/modules/ipcheck/privacy.php',
202
+ 'ITSEC_Notification_Center' => __DIR__ . '/../..' . '/core/modules/notification-center/class-notification-center.php',
203
+ 'ITSEC_Notification_Center_Debug' => __DIR__ . '/../..' . '/core/modules/notification-center/debug.php',
204
+ 'ITSEC_Notification_Center_Logs' => __DIR__ . '/../..' . '/core/modules/notification-center/logs.php',
205
+ 'ITSEC_Notification_Center_Settings' => __DIR__ . '/../..' . '/core/modules/notification-center/settings.php',
206
+ 'ITSEC_Notification_Center_Settings_Page' => __DIR__ . '/../..' . '/core/modules/notification-center/settings-page.php',
207
+ 'ITSEC_Notification_Center_Setup' => __DIR__ . '/../..' . '/core/modules/notification-center/setup.php',
208
+ 'ITSEC_Notification_Center_Validator' => __DIR__ . '/../..' . '/core/modules/notification-center/validator.php',
209
+ 'ITSEC_Password_Requirements' => __DIR__ . '/../..' . '/core/modules/password-requirements/class-itsec-password-requirements.php',
210
+ 'ITSEC_Password_Requirements_Settings' => __DIR__ . '/../..' . '/core/modules/password-requirements/settings.php',
211
+ 'ITSEC_Password_Requirements_Settings_Page' => __DIR__ . '/../..' . '/core/modules/password-requirements/settings-page.php',
212
+ 'ITSEC_Password_Requirements_Validator' => __DIR__ . '/../..' . '/core/modules/password-requirements/validator.php',
213
+ 'ITSEC_Passwordless_Login_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
214
+ 'ITSEC_Privacy' => __DIR__ . '/../..' . '/core/modules/privacy/class-itsec-privacy.php',
215
+ 'ITSEC_Privacy_Util' => __DIR__ . '/../..' . '/core/modules/privacy/util.php',
216
+ 'ITSEC_Privilege_Escalation_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
217
+ 'ITSEC_Pro_Setup' => __DIR__ . '/../..' . '/core/modules/pro/setup.php',
218
+ 'ITSEC_REST_Core_Admin_Notices_Controller' => __DIR__ . '/../..' . '/core/modules/core/class-rest-core-admin-notices-controller.php',
219
+ 'ITSEC_Recaptcha_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
220
+ 'ITSEC_SSL' => __DIR__ . '/../..' . '/core/modules/ssl/class-itsec-ssl.php',
221
+ 'ITSEC_SSL_Admin' => __DIR__ . '/../..' . '/core/modules/ssl/class-itsec-ssl-admin.php',
222
+ 'ITSEC_SSL_Settings' => __DIR__ . '/../..' . '/core/modules/ssl/settings.php',
223
+ 'ITSEC_SSL_Settings_Page' => __DIR__ . '/../..' . '/core/modules/ssl/settings-page.php',
224
+ 'ITSEC_SSL_Setup' => __DIR__ . '/../..' . '/core/modules/ssl/setup.php',
225
+ 'ITSEC_SSL_Validator' => __DIR__ . '/../..' . '/core/modules/ssl/validator.php',
226
+ 'ITSEC_Salts_Setup' => __DIR__ . '/../..' . '/core/modules/salts/setup.php',
227
+ 'ITSEC_Scheduler' => __DIR__ . '/../..' . '/core/lib/class-itsec-scheduler.php',
228
+ 'ITSEC_Scheduler_Cron' => __DIR__ . '/../..' . '/core/lib/class-itsec-scheduler-cron.php',
229
+ 'ITSEC_Scheduler_Page_Load' => __DIR__ . '/../..' . '/core/lib/class-itsec-scheduler-page-load.php',
230
+ 'ITSEC_Schema' => __DIR__ . '/../..' . '/core/lib/schema.php',
231
+ 'ITSEC_Security_Check_Feedback' => __DIR__ . '/../..' . '/core/modules/security-check/feedback.php',
232
+ 'ITSEC_Security_Check_Feedback_Renderer' => __DIR__ . '/../..' . '/core/modules/security-check/feedback-renderer.php',
233
+ 'ITSEC_Security_Check_Pro' => __DIR__ . '/../..' . '/core/modules/security-check-pro/class-itsec-security-check-pro.php',
234
+ 'ITSEC_Security_Check_Pro_Debug' => __DIR__ . '/../..' . '/core/modules/security-check-pro/debug.php',
235
+ 'ITSEC_Security_Check_Pro_Privacy' => __DIR__ . '/../..' . '/core/modules/security-check-pro/privacy.php',
236
+ 'ITSEC_Security_Check_Pro_Settings' => __DIR__ . '/../..' . '/core/modules/security-check-pro/settings.php',
237
+ 'ITSEC_Security_Check_Pro_Settings_Page' => __DIR__ . '/../..' . '/core/modules/security-check-pro/settings-page.php',
238
+ 'ITSEC_Security_Check_Pro_Utility' => __DIR__ . '/../..' . '/core/modules/security-check-pro/utility.php',
239
+ 'ITSEC_Security_Check_Pro_Validator' => __DIR__ . '/../..' . '/core/modules/security-check-pro/validator.php',
240
+ 'ITSEC_Security_Check_Scanner' => __DIR__ . '/../..' . '/core/modules/security-check/scanner.php',
241
+ 'ITSEC_Security_Check_Settings_Page' => __DIR__ . '/../..' . '/core/modules/security-check/settings-page.php',
242
+ 'ITSEC_Settings' => __DIR__ . '/../..' . '/core/lib/settings.php',
243
+ 'ITSEC_Settings_Page_Sidebar_Widget_BackupBuddy_Cross_Promo' => __DIR__ . '/../..' . '/core/modules/core/sidebar-widget-backupbuddy-cross-promo.php',
244
+ 'ITSEC_Settings_Page_Sidebar_Widget_Free_Support' => __DIR__ . '/../..' . '/core/modules/core/sidebar-widget-support.php',
245
+ 'ITSEC_Settings_Page_Sidebar_Widget_Mail_List_Signup' => __DIR__ . '/../..' . '/core/modules/core/sidebar-widget-mail-list-signup.php',
246
+ 'ITSEC_Settings_Page_Sidebar_Widget_Malware_Scan' => __DIR__ . '/../..' . '/core/modules/malware/settings-page.php',
247
+ 'ITSEC_Settings_Page_Sidebar_Widget_Pro_Upsell' => __DIR__ . '/../..' . '/core/modules/core/sidebar-widget-pro-upsell.php',
248
+ 'ITSEC_Settings_Page_Sidebar_Widget_Sync_Cross_Promo' => __DIR__ . '/../..' . '/core/modules/core/sidebar-widget-sync-cross-promo.php',
249
+ 'ITSEC_Static_Map_API' => __DIR__ . '/../..' . '/core/lib/static-map-api/interface-itsec-static-map-api.php',
250
+ 'ITSEC_Storage' => __DIR__ . '/../..' . '/core/lib/storage.php',
251
+ 'ITSEC_Strong_Passwords' => __DIR__ . '/../..' . '/core/modules/strong-passwords/class-itsec-strong-passwords.php',
252
+ 'ITSEC_Strong_Passwords_Settings' => __DIR__ . '/../..' . '/core/modules/strong-passwords/settings.php',
253
+ 'ITSEC_Strong_Passwords_Setup' => __DIR__ . '/../..' . '/core/modules/strong-passwords/setup.php',
254
+ 'ITSEC_Strong_Passwords_Validator' => __DIR__ . '/../..' . '/core/modules/strong-passwords/validator.php',
255
+ 'ITSEC_Sync_Connect' => __DIR__ . '/../..' . '/core/modules/sync-connect/class-itsec-sync-connect.php',
256
+ 'ITSEC_Sync_Connect_Interstitial' => __DIR__ . '/../..' . '/core/modules/sync-connect/class-itsec-sync-connect-interstitial.php',
257
+ 'ITSEC_System_Tweaks' => __DIR__ . '/../..' . '/core/modules/system-tweaks/class-itsec-system-tweaks.php',
258
+ 'ITSEC_System_Tweaks_Config_Generators' => __DIR__ . '/../..' . '/core/modules/system-tweaks/config-generators.php',
259
+ 'ITSEC_System_Tweaks_Settings' => __DIR__ . '/../..' . '/core/modules/system-tweaks/settings.php',
260
+ 'ITSEC_System_Tweaks_Settings_Page' => __DIR__ . '/../..' . '/core/modules/system-tweaks/settings-page.php',
261
+ 'ITSEC_System_Tweaks_Setup' => __DIR__ . '/../..' . '/core/modules/system-tweaks/setup.php',
262
+ 'ITSEC_System_Tweaks_Validator' => __DIR__ . '/../..' . '/core/modules/system-tweaks/validator.php',
263
+ 'ITSEC_Two_Factor_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
264
+ 'ITSEC_User_Logging_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
265
+ 'ITSEC_User_Security_Check_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
266
+ 'ITSEC_Validator' => __DIR__ . '/../..' . '/core/lib/validator.php',
267
+ 'ITSEC_Version_Management_Settings_Page' => __DIR__ . '/../..' . '/core/modules/pro/settings-page.php',
268
+ 'ITSEC_WP_List_Table' => __DIR__ . '/../..' . '/core/lib/class-itsec-wp-list-table.php',
269
+ 'ITSEC_WordPress_Salts_Settings' => __DIR__ . '/../..' . '/core/modules/salts/settings.php',
270
+ 'ITSEC_WordPress_Salts_Settings_Page' => __DIR__ . '/../..' . '/core/modules/salts/settings-page.php',
271
+ 'ITSEC_WordPress_Salts_Utilities' => __DIR__ . '/../..' . '/core/modules/salts/utilities.php',
272
+ 'ITSEC_WordPress_Salts_Validator' => __DIR__ . '/../..' . '/core/modules/salts/validator.php',
273
+ 'ITSEC_WordPress_Tweaks' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/class-itsec-wordpress-tweaks.php',
274
+ 'ITSEC_WordPress_Tweaks_Config_Generators' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/config-generators.php',
275
+ 'ITSEC_WordPress_Tweaks_Settings_Page' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/settings-page.php',
276
+ 'ITSEC_WordPress_Tweaks_Setup' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/setup.php',
277
+ 'ITSEC_WordPress_Tweaks_Validator' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/validator.php',
278
+ 'ITSEC_Wordpress_Tweaks_Settings' => __DIR__ . '/../..' . '/core/modules/wordpress-tweaks/settings.php',
279
+ 'Ithemes_Sync_Verb_ITSEC_Do_Security_Check' => __DIR__ . '/../..' . '/core/modules/security-check/sync-verbs/itsec-do-security-check.php',
280
+ 'Ithemes_Sync_Verb_ITSEC_Get_Away_Mode' => __DIR__ . '/../..' . '/core/modules/away-mode/sync-verbs/itsec-get-away-mode.php',
281
+ 'Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Log' => __DIR__ . '/../..' . '/core/modules/malware/sync-verbs/itsec-get-malware-scan-log.php',
282
+ 'Ithemes_Sync_Verb_ITSEC_Get_Security_Check_Feedback_Response' => __DIR__ . '/../..' . '/core/modules/security-check/sync-verbs/itsec-get-security-check-feedback-response.php',
283
+ 'Ithemes_Sync_Verb_ITSEC_Get_Security_Check_Modules' => __DIR__ . '/../..' . '/core/modules/security-check/sync-verbs/itsec-get-security-check-modules.php',
284
+ 'Ithemes_Sync_Verb_ITSEC_Latest_File_Scan' => __DIR__ . '/../..' . '/core/modules/file-change/sync-verbs/itsec-latest-file-scan.php',
285
+ 'Ithemes_Sync_Verb_ITSEC_Malware_Do_Scan' => __DIR__ . '/../..' . '/core/modules/malware/sync-verbs/itsec-do-malware-scan.php',
286
+ 'Ithemes_Sync_Verb_ITSEC_Override_Away_Mode' => __DIR__ . '/../..' . '/core/modules/away-mode/sync-verbs/itsec-override-away-mode.php',
287
+ 'Pimple\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Container.php',
288
+ 'Pimple\\Exception\\ExpectedInvokableException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php',
289
+ 'Pimple\\Exception\\FrozenServiceException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php',
290
+ 'Pimple\\Exception\\InvalidServiceIdentifierException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php',
291
+ 'Pimple\\Exception\\UnknownIdentifierException' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php',
292
+ 'Pimple\\Psr11\\Container' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Psr11/Container.php',
293
+ 'Pimple\\Psr11\\ServiceLocator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php',
294
+ 'Pimple\\ServiceIterator' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceIterator.php',
295
+ 'Pimple\\ServiceProviderInterface' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/ServiceProviderInterface.php',
296
+ 'Pimple\\Tests\\Fixtures\\Invokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Invokable.php',
297
+ 'Pimple\\Tests\\Fixtures\\NonInvokable' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/NonInvokable.php',
298
+ 'Pimple\\Tests\\Fixtures\\PimpleServiceProvider' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/PimpleServiceProvider.php',
299
+ 'Pimple\\Tests\\Fixtures\\Service' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Fixtures/Service.php',
300
+ 'Pimple\\Tests\\PimpleServiceProviderInterfaceTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleServiceProviderInterfaceTest.php',
301
+ 'Pimple\\Tests\\PimpleTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/PimpleTest.php',
302
+ 'Pimple\\Tests\\Psr11\\ContainerTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Psr11/ContainerTest.php',
303
+ 'Pimple\\Tests\\Psr11\\ServiceLocatorTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/Psr11/ServiceLocatorTest.php',
304
+ 'Pimple\\Tests\\ServiceIteratorTest' => __DIR__ . '/..' . '/pimple/pimple/src/Pimple/Tests/ServiceIteratorTest.php',
305
+ 'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php',
306
+ 'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php',
307
+ 'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php',
308
+ 'Wikimedia\\Composer\\Logger' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Logger.php',
309
+ 'Wikimedia\\Composer\\MergePlugin' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/MergePlugin.php',
310
+ 'Wikimedia\\Composer\\Merge\\ExtraPackage' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Merge/ExtraPackage.php',
311
+ 'Wikimedia\\Composer\\Merge\\MissingFileException' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Merge/MissingFileException.php',
312
+ 'Wikimedia\\Composer\\Merge\\NestedArray' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Merge/NestedArray.php',
313
+ 'Wikimedia\\Composer\\Merge\\PluginState' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Merge/PluginState.php',
314
+ 'Wikimedia\\Composer\\Merge\\StabilityFlags' => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src/Merge/StabilityFlags.php',
315
+ 'iThemesSecurity\\Contracts\\Runnable' => __DIR__ . '/../..' . '/core/Contracts/Runnable.php',
316
+ 'iThemesSecurity\\Exception\\Exception' => __DIR__ . '/../..' . '/core/Exception/Exception.php',
317
+ 'iThemesSecurity\\Exception\\Invalid_Argument_Exception' => __DIR__ . '/../..' . '/core/Exception/Invalid_Argument_Exception.php',
318
+ 'iThemesSecurity\\Exception\\Unsatisfied_Module_Dependencies_Exception' => __DIR__ . '/../..' . '/core/Exception/Unsatisfied_Module_Dependencies_Exception.php',
319
+ 'iThemesSecurity\\Exception\\WP_Error' => __DIR__ . '/../..' . '/core/Exception/WP_Error.php',
320
+ 'iThemesSecurity\\Lib\\Lockout\\Context' => __DIR__ . '/../..' . '/core/lib/lockout/abstract-context.php',
321
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Context' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/abstract-context.php',
322
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Host_Context' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/class-host-context.php',
323
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Configurable' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/source/class-configurable.php',
324
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Lockout_Module' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/source/class-lockout-module.php',
325
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Source\\Source' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/source/interface-source.php',
326
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\User_Context' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/class-user-context.php',
327
+ 'iThemesSecurity\\Lib\\Lockout\\Execute_Lock\\Username_Context' => __DIR__ . '/../..' . '/core/lib/lockout/execute-lock/class-username-context.php',
328
+ 'iThemesSecurity\\Lib\\Lockout\\Host_Context' => __DIR__ . '/../..' . '/core/lib/lockout/class-host-context.php',
329
+ 'iThemesSecurity\\Lib\\Lockout\\Lockout' => __DIR__ . '/../..' . '/core/lib/lockout/class-lockout.php',
330
+ 'iThemesSecurity\\Lib\\Lockout\\User_Context' => __DIR__ . '/../..' . '/core/lib/lockout/class-user-context.php',
331
+ 'iThemesSecurity\\Lib\\Lockout\\Username_Context' => __DIR__ . '/../..' . '/core/lib/lockout/class-username-context.php',
332
+ 'iThemesSecurity\\User_Groups\\All_Users' => __DIR__ . '/../..' . '/core/modules/user-groups/All_Users.php',
333
+ 'iThemesSecurity\\User_Groups\\Default_Matcher' => __DIR__ . '/../..' . '/core/modules/user-groups/Match/Default_Matcher.php',
334
+ 'iThemesSecurity\\User_Groups\\Everybody_Else' => __DIR__ . '/../..' . '/core/modules/user-groups/Everybody_Else.php',
335
+ 'iThemesSecurity\\User_Groups\\Match_Target' => __DIR__ . '/../..' . '/core/modules/user-groups/Match/Match_Target.php',
336
+ 'iThemesSecurity\\User_Groups\\Matchable' => __DIR__ . '/../..' . '/core/modules/user-groups/Matchable.php',
337
+ 'iThemesSecurity\\User_Groups\\Matchable_Not_Found' => __DIR__ . '/../..' . '/core/modules/user-groups/Match/Matchable_Not_Found.php',
338
+ 'iThemesSecurity\\User_Groups\\Matchables_Source' => __DIR__ . '/../..' . '/core/modules/user-groups/Matchables_Source.php',
339
+ 'iThemesSecurity\\User_Groups\\Matcher' => __DIR__ . '/../..' . '/core/modules/user-groups/Match/Matcher.php',
340
+ 'iThemesSecurity\\User_Groups\\Module\\Module' => __DIR__ . '/../..' . '/core/modules/user-groups/Module/Module.php',
341
+ 'iThemesSecurity\\User_Groups\\Module\\Settings' => __DIR__ . '/../..' . '/core/modules/user-groups/Module/Settings.php',
342
+ 'iThemesSecurity\\User_Groups\\Module\\Validator' => __DIR__ . '/../..' . '/core/modules/user-groups/Module/Validator.php',
343
+ 'iThemesSecurity\\User_Groups\\REST\\Matchables' => __DIR__ . '/../..' . '/core/modules/user-groups/REST/Matchables.php',
344
+ 'iThemesSecurity\\User_Groups\\REST\\REST' => __DIR__ . '/../..' . '/core/modules/user-groups/REST/REST.php',
345
+ 'iThemesSecurity\\User_Groups\\REST\\Settings' => __DIR__ . '/../..' . '/core/modules/user-groups/REST/Settings.php',
346
+ 'iThemesSecurity\\User_Groups\\REST\\User_Groups' => __DIR__ . '/../..' . '/core/modules/user-groups/REST/User_Groups.php',
347
+ 'iThemesSecurity\\User_Groups\\Repository\\DB_Repository' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/DB_Repository.php',
348
+ 'iThemesSecurity\\User_Groups\\Repository\\Decorator' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/Decorator.php',
349
+ 'iThemesSecurity\\User_Groups\\Repository\\Eager_Loading_Decorator' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/Eager_Loading_Decorator.php',
350
+ 'iThemesSecurity\\User_Groups\\Repository\\In_Memory_Repository' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/In_Memory_Repository.php',
351
+ 'iThemesSecurity\\User_Groups\\Repository\\Object_Caching_Decorator' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/Object_Caching_Decorator.php',
352
+ 'iThemesSecurity\\User_Groups\\Repository\\Repository' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/Repository.php',
353
+ 'iThemesSecurity\\User_Groups\\Repository\\User_Group_Not_Found' => __DIR__ . '/../..' . '/core/modules/user-groups/Repository/User_Group_Not_Found.php',
354
+ 'iThemesSecurity\\User_Groups\\Settings_Page' => __DIR__ . '/../..' . '/core/modules/user-groups/settings-page.php',
355
+ 'iThemesSecurity\\User_Groups\\Settings_Proxy' => __DIR__ . '/../..' . '/core/modules/user-groups/Settings/Settings_Proxy.php',
356
+ 'iThemesSecurity\\User_Groups\\Settings_Registration' => __DIR__ . '/../..' . '/core/modules/user-groups/Settings/Settings_Registration.php',
357
+ 'iThemesSecurity\\User_Groups\\Settings_Registry' => __DIR__ . '/../..' . '/core/modules/user-groups/Settings/Settings_Registry.php',
358
+ 'iThemesSecurity\\User_Groups\\Upgrader' => __DIR__ . '/../..' . '/core/modules/user-groups/Upgrader.php',
359
+ 'iThemesSecurity\\User_Groups\\User_Group' => __DIR__ . '/../..' . '/core/modules/user-groups/User_Group.php',
360
+ );
361
+
362
+ public static function getInitializer(ClassLoader $loader)
363
+ {
364
+ return \Closure::bind(function () use ($loader) {
365
+ $loader->prefixLengthsPsr4 = ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f::$prefixLengthsPsr4;
366
+ $loader->prefixDirsPsr4 = ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f::$prefixDirsPsr4;
367
+ $loader->prefixesPsr0 = ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f::$prefixesPsr0;
368
+ $loader->classMap = ComposerStaticInit35a2bd4feb347da0d3ea2d8ef023082f::$classMap;
369
+
370
+ }, null, ClassLoader::class);
371
+ }
372
+ }
vendor-prod/composer/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
vendor-prod/composer/installed.json ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "pimple/pimple",
4
+ "version": "v3.2.3",
5
+ "version_normalized": "3.2.3.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/silexphp/Pimple.git",
9
+ "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
14
+ "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
15
+ "shasum": ""
16
+ },
17
+ "require": {
18
+ "php": ">=5.3.0",
19
+ "psr/container": "^1.0"
20
+ },
21
+ "require-dev": {
22
+ "symfony/phpunit-bridge": "^3.2"
23
+ },
24
+ "time": "2018-01-21T07:42:36+00:00",
25
+ "type": "library",
26
+ "extra": {
27
+ "branch-alias": {
28
+ "dev-master": "3.2.x-dev"
29
+ }
30
+ },
31
+ "installation-source": "dist",
32
+ "autoload": {
33
+ "psr-0": {
34
+ "Pimple": "src/"
35
+ }
36
+ },
37
+ "notification-url": "https://packagist.org/downloads/",
38
+ "license": [
39
+ "MIT"
40
+ ],
41
+ "authors": [
42
+ {
43
+ "name": "Fabien Potencier",
44
+ "email": "fabien@symfony.com"
45
+ }
46
+ ],
47
+ "description": "Pimple, a simple Dependency Injection Container",
48
+ "homepage": "http://pimple.sensiolabs.org",
49
+ "keywords": [
50
+ "container",
51
+ "dependency injection"
52
+ ]
53
+ },
54
+ {
55
+ "name": "psr/container",
56
+ "version": "1.0.0",
57
+ "version_normalized": "1.0.0.0",
58
+ "source": {
59
+ "type": "git",
60
+ "url": "https://github.com/php-fig/container.git",
61
+ "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
62
+ },
63
+ "dist": {
64
+ "type": "zip",
65
+ "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
66
+ "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
67
+ "shasum": ""
68
+ },
69
+ "require": {
70
+ "php": ">=5.3.0"
71
+ },
72
+ "time": "2017-02-14T16:28:37+00:00",
73
+ "type": "library",
74
+ "extra": {
75
+ "branch-alias": {
76
+ "dev-master": "1.0.x-dev"
77
+ }
78
+ },
79
+ "installation-source": "dist",
80
+ "autoload": {
81
+ "psr-4": {
82
+ "Psr\\Container\\": "src/"
83
+ }
84
+ },
85
+ "notification-url": "https://packagist.org/downloads/",
86
+ "license": [
87
+ "MIT"
88
+ ],
89
+ "authors": [
90
+ {
91
+ "name": "PHP-FIG",
92
+ "homepage": "http://www.php-fig.org/"
93
+ }
94
+ ],
95
+ "description": "Common Container Interface (PHP FIG PSR-11)",
96
+ "homepage": "https://github.com/php-fig/container",
97
+ "keywords": [
98
+ "PSR-11",
99
+ "container",
100
+ "container-interface",
101
+ "container-interop",
102
+ "psr"
103
+ ]
104
+ },
105
+ {
106
+ "name": "wikimedia/composer-merge-plugin",
107
+ "version": "v1.4.1",
108
+ "version_normalized": "1.4.1.0",
109
+ "source": {
110
+ "type": "git",
111
+ "url": "https://github.com/wikimedia/composer-merge-plugin.git",
112
+ "reference": "81c6ac72a24a67383419c7eb9aa2b3437f2ab100"
113
+ },
114
+ "dist": {
115
+ "type": "zip",
116
+ "url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/81c6ac72a24a67383419c7eb9aa2b3437f2ab100",
117
+ "reference": "81c6ac72a24a67383419c7eb9aa2b3437f2ab100",
118
+ "shasum": ""
119
+ },
120
+ "require": {
121
+ "composer-plugin-api": "^1.0",
122
+ "php": ">=5.3.2"
123
+ },
124
+ "require-dev": {
125
+ "composer/composer": "~1.0.0",
126
+ "jakub-onderka/php-parallel-lint": "~0.8",
127
+ "phpunit/phpunit": "~4.8|~5.0",
128
+ "squizlabs/php_codesniffer": "~2.1.0"
129
+ },
130
+ "time": "2017-04-25T02:31:25+00:00",
131
+ "type": "composer-plugin",
132
+ "extra": {
133
+ "branch-alias": {
134
+ "dev-master": "1.3.x-dev"
135
+ },
136
+ "class": "Wikimedia\\Composer\\MergePlugin"
137
+ },
138
+ "installation-source": "dist",
139
+ "autoload": {
140
+ "psr-4": {
141
+ "Wikimedia\\Composer\\": "src/"
142
+ }
143
+ },
144
+ "notification-url": "https://packagist.org/downloads/",
145
+ "license": [
146
+ "MIT"
147
+ ],
148
+ "authors": [
149
+ {
150
+ "name": "Bryan Davis",
151
+ "email": "bd808@wikimedia.org"
152
+ }
153
+ ],
154
+ "description": "Composer plugin to merge multiple composer.json files"
155
+ }
156
+ ]
vendor-prod/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
vendor-prod/pimple/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden.
vendor-prod/pimple/pimple/CHANGELOG ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * 3.2.3 (2017-XX-XX)
2
+
3
+ * n/a
4
+
5
+ * 3.2.2 (2017-07-23)
6
+
7
+ * reverted extending a protected closure throws an exception (deprecated it instead)
8
+
9
+ * 3.2.1 (2017-07-17)
10
+
11
+ * fixed PHP error
12
+
13
+ * 3.2.0 (2017-07-17)
14
+
15
+ * added a PSR-11 service locator
16
+ * added a PSR-11 wrapper
17
+ * added ServiceIterator
18
+ * fixed extending a protected closure (now throws InvalidServiceIdentifierException)
19
+
20
+ * 3.1.0 (2017-07-03)
21
+
22
+ * deprecated the C extension
23
+ * added support for PSR-11 exceptions
24
+
25
+ * 3.0.2 (2015-09-11)
26
+
27
+ * refactored the C extension
28
+ * minor non-significant changes
29
+
30
+ * 3.0.1 (2015-07-30)
31
+
32
+ * simplified some code
33
+ * fixed a segfault in the C extension
34
+
35
+ * 3.0.0 (2014-07-24)
36
+
37
+ * removed the Pimple class alias (use Pimple\Container instead)
38
+
39
+ * 2.1.1 (2014-07-24)
40
+
41
+ * fixed compiler warnings for the C extension
42
+ * fixed code when dealing with circular references
43
+
44
+ * 2.1.0 (2014-06-24)
45
+
46
+ * moved the Pimple to Pimple\Container (with a BC layer -- Pimple is now a
47
+ deprecated alias which will be removed in Pimple 3.0)
48
+ * added Pimple\ServiceProviderInterface (and Pimple::register())
49
+
50
+ * 2.0.0 (2014-02-10)
51
+
52
+ * changed extend to automatically re-assign the extended service and keep it as shared or factory
53
+ (to keep BC, extend still returns the extended service)
54
+ * changed services to be shared by default (use factory() for factory
55
+ services)
56
+
57
+ * 1.0.0
58
+
59
+ * initial version
vendor-prod/pimple/pimple/LICENSE ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2009-2017 Fabien Potencier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished
8
+ to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
vendor-prod/pimple/pimple/README.rst ADDED
@@ -0,0 +1,326 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Pimple
2
+ ======
3
+
4
+ .. caution::
5
+
6
+ This is the documentation for Pimple 3.x. If you are using Pimple 1.x, read
7
+ the `Pimple 1.x documentation`_. Reading the Pimple 1.x code is also a good
8
+ way to learn more about how to create a simple Dependency Injection
9
+ Container (recent versions of Pimple are more focused on performance).
10
+
11
+ Pimple is a small Dependency Injection Container for PHP.
12
+
13
+ Installation
14
+ ------------
15
+
16
+ Before using Pimple in your project, add it to your ``composer.json`` file:
17
+
18
+ .. code-block:: bash
19
+
20
+ $ ./composer.phar require pimple/pimple "^3.0"
21
+
22
+ Usage
23
+ -----
24
+
25
+ Creating a container is a matter of creating a ``Container`` instance:
26
+
27
+ .. code-block:: php
28
+
29
+ use Pimple\Container;
30
+
31
+ $container = new Container();
32
+
33
+ As many other dependency injection containers, Pimple manages two different
34
+ kind of data: **services** and **parameters**.
35
+
36
+ Defining Services
37
+ ~~~~~~~~~~~~~~~~~
38
+
39
+ A service is an object that does something as part of a larger system. Examples
40
+ of services: a database connection, a templating engine, or a mailer. Almost
41
+ any **global** object can be a service.
42
+
43
+ Services are defined by **anonymous functions** that return an instance of an
44
+ object:
45
+
46
+ .. code-block:: php
47
+
48
+ // define some services
49
+ $container['session_storage'] = function ($c) {
50
+ return new SessionStorage('SESSION_ID');
51
+ };
52
+
53
+ $container['session'] = function ($c) {
54
+ return new Session($c['session_storage']);
55
+ };
56
+
57
+ Notice that the anonymous function has access to the current container
58
+ instance, allowing references to other services or parameters.
59
+
60
+ As objects are only created when you get them, the order of the definitions
61
+ does not matter.
62
+
63
+ Using the defined services is also very easy:
64
+
65
+ .. code-block:: php
66
+
67
+ // get the session object
68
+ $session = $container['session'];
69
+
70
+ // the above call is roughly equivalent to the following code:
71
+ // $storage = new SessionStorage('SESSION_ID');
72
+ // $session = new Session($storage);
73
+
74
+ Defining Factory Services
75
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
76
+
77
+ By default, each time you get a service, Pimple returns the **same instance**
78
+ of it. If you want a different instance to be returned for all calls, wrap your
79
+ anonymous function with the ``factory()`` method
80
+
81
+ .. code-block:: php
82
+
83
+ $container['session'] = $container->factory(function ($c) {
84
+ return new Session($c['session_storage']);
85
+ });
86
+
87
+ Now, each call to ``$container['session']`` returns a new instance of the
88
+ session.
89
+
90
+ Defining Parameters
91
+ ~~~~~~~~~~~~~~~~~~~
92
+
93
+ Defining a parameter allows to ease the configuration of your container from
94
+ the outside and to store global values:
95
+
96
+ .. code-block:: php
97
+
98
+ // define some parameters
99
+ $container['cookie_name'] = 'SESSION_ID';
100
+ $container['session_storage_class'] = 'SessionStorage';
101
+
102
+ If you change the ``session_storage`` service definition like below:
103
+
104
+ .. code-block:: php
105
+
106
+ $container['session_storage'] = function ($c) {
107
+ return new $c['session_storage_class']($c['cookie_name']);
108
+ };
109
+
110
+ You can now easily change the cookie name by overriding the
111
+ ``cookie_name`` parameter instead of redefining the service
112
+ definition.
113
+
114
+ Protecting Parameters
115
+ ~~~~~~~~~~~~~~~~~~~~~
116
+
117
+ Because Pimple sees anonymous functions as service definitions, you need to
118
+ wrap anonymous functions with the ``protect()`` method to store them as
119
+ parameters:
120
+
121
+ .. code-block:: php
122
+
123
+ $container['random_func'] = $container->protect(function () {
124
+ return rand();
125
+ });
126
+
127
+ Modifying Services after Definition
128
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129
+
130
+ In some cases you may want to modify a service definition after it has been
131
+ defined. You can use the ``extend()`` method to define additional code to be
132
+ run on your service just after it is created:
133
+
134
+ .. code-block:: php
135
+
136
+ $container['session_storage'] = function ($c) {
137
+ return new $c['session_storage_class']($c['cookie_name']);
138
+ };
139
+
140
+ $container->extend('session_storage', function ($storage, $c) {
141
+ $storage->...();
142
+
143
+ return $storage;
144
+ });
145
+
146
+ The first argument is the name of the service to extend, the second a function
147
+ that gets access to the object instance and the container.
148
+
149
+ Extending a Container
150
+ ~~~~~~~~~~~~~~~~~~~~~
151
+
152
+ If you use the same libraries over and over, you might want to reuse some
153
+ services from one project to the next one; package your services into a
154
+ **provider** by implementing ``Pimple\ServiceProviderInterface``:
155
+
156
+ .. code-block:: php
157
+
158
+ use Pimple\Container;
159
+
160
+ class FooProvider implements Pimple\ServiceProviderInterface
161
+ {
162
+ public function register(Container $pimple)
163
+ {
164
+ // register some services and parameters
165
+ // on $pimple
166
+ }
167
+ }
168
+
169
+ Then, register the provider on a Container:
170
+
171
+ .. code-block:: php
172
+
173
+ $pimple->register(new FooProvider());
174
+
175
+ Fetching the Service Creation Function
176
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177
+
178
+ When you access an object, Pimple automatically calls the anonymous function
179
+ that you defined, which creates the service object for you. If you want to get
180
+ raw access to this function, you can use the ``raw()`` method:
181
+
182
+ .. code-block:: php
183
+
184
+ $container['session'] = function ($c) {
185
+ return new Session($c['session_storage']);
186
+ };
187
+
188
+ $sessionFunction = $container->raw('session');
189
+
190
+ PSR-11 compatibility
191
+ --------------------
192
+
193
+ For historical reasons, the ``Container`` class does not implement the PSR-11
194
+ ``ContainerInterface``. However, Pimple provides a helper class that will let
195
+ you decouple your code from the Pimple container class.
196
+
197
+ The PSR-11 container class
198
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
199
+
200
+ The ``Pimple\Psr11\Container`` class lets you access the content of an
201
+ underlying Pimple container using ``Psr\Container\ContainerInterface``
202
+ methods:
203
+
204
+ .. code-block:: php
205
+
206
+ use Pimple\Container;
207
+ use Pimple\Psr11\Container as PsrContainer;
208
+
209
+ $container = new Container();
210
+ $container['service'] = function ($c) {
211
+ return new Service();
212
+ };
213
+ $psr11 = new PsrContainer($container);
214
+
215
+ $controller = function (PsrContainer $container) {
216
+ $service = $container->get('service');
217
+ };
218
+ $controller($psr11);
219
+
220
+ Using the PSR-11 ServiceLocator
221
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
222
+
223
+ Sometimes, a service needs access to several other services without being sure
224
+ that all of them will actually be used. In those cases, you may want the
225
+ instantiation of the services to be lazy.
226
+
227
+ The traditional solution is to inject the entire service container to get only
228
+ the services really needed. However, this is not recommended because it gives
229
+ services a too broad access to the rest of the application and it hides their
230
+ actual dependencies.
231
+
232
+ The ``ServiceLocator`` is intended to solve this problem by giving access to a
233
+ set of predefined services while instantiating them only when actually needed.
234
+
235
+ It also allows you to make your services available under a different name than
236
+ the one used to register them. For instance, you may want to use an object
237
+ that expects an instance of ``EventDispatcherInterface`` to be available under
238
+ the name ``event_dispatcher`` while your event dispatcher has been
239
+ registered under the name ``dispatcher``:
240
+
241
+ .. code-block:: php
242
+
243
+ use Monolog\Logger;
244
+ use Pimple\Psr11\ServiceLocator;
245
+ use Psr\Container\ContainerInterface;
246
+ use Symfony\Component\EventDispatcher\EventDispatcher;
247
+
248
+ class MyService
249
+ {
250
+ /**
251
+ * "logger" must be an instance of Psr\Log\LoggerInterface
252
+ * "event_dispatcher" must be an instance of Symfony\Component\EventDispatcher\EventDispatcherInterface
253
+ */
254
+ private $services;
255
+
256
+ public function __construct(ContainerInterface $services)
257
+ {
258
+ $this->services = $services;
259
+ }
260
+ }
261
+
262
+ $container['logger'] = function ($c) {
263
+ return new Monolog\Logger();
264
+ };
265
+ $container['dispatcher'] = function () {
266
+ return new EventDispatcher();
267
+ };
268
+
269
+ $container['service'] = function ($c) {
270
+ $locator = new ServiceLocator($c, array('logger', 'event_dispatcher' => 'dispatcher'));
271
+
272
+ return new MyService($locator);
273
+ };
274
+
275
+ Referencing a Collection of Services Lazily
276
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277
+
278
+ Passing a collection of services instances in an array may prove inefficient
279
+ if the class that consumes the collection only needs to iterate over it at a
280
+ later stage, when one of its method is called. It can also lead to problems
281
+ if there is a circular dependency between one of the services stored in the
282
+ collection and the class that consumes it.
283
+
284
+ The ``ServiceIterator`` class helps you solve these issues. It receives a
285
+ list of service names during instantiation and will retrieve the services
286
+ when iterated over:
287
+
288
+ .. code-block:: php
289
+
290
+ use Pimple\Container;
291
+ use Pimple\ServiceIterator;
292
+
293
+ class AuthorizationService
294
+ {
295
+ private $voters;
296
+
297
+ public function __construct($voters)
298
+ {
299
+ $this->voters = $voters;
300
+ }
301
+
302
+ public function canAccess($resource)
303
+ {
304
+ foreach ($this->voters as $voter) {
305
+ if (true === $voter->canAccess($resource) {
306
+ return true;
307
+ }
308
+ }
309
+
310
+ return false;
311
+ }
312
+ }
313
+
314
+ $container = new Container();
315
+