Version Description
- New: iThemes Security Admin Notices are now conveniently located in the new Security Messages Menu. Check your notices in the Security menu on the WordPress Admin Bar.
- Enhancement: Add Security Message when a Notification Center email fails to send.
- Enhancement: Replace Trace IP with IP Tracker Online.
- Tweak: Remove 'DELETE' method from "System Tweaks -> Filter Request Methods"
Download this release
Release Info
Developer | TimothyBlynJacobs |
Plugin | iThemes Security (formerly Better WP Security) |
Version | 7.4.0 |
Comparing to | |
See all releases |
Code changes from version 7.3.3 to 7.4.0
- better-wp-security.php +1 -1
- core/admin-pages/css/style.css +1 -1
- core/admin-pages/module-settings.php +1 -1
- core/admin-pages/page-debug.php +10 -3
- core/core.php +34 -60
- core/history.txt +6 -0
- core/lib.php +39 -2
- core/lib/admin-notices/actions/class-itsec-admin-notice-action-callback.php +37 -0
- core/lib/admin-notices/actions/class-itsec-admin-notice-action-link.php +47 -0
- core/lib/admin-notices/actions/index.php +1 -0
- core/lib/admin-notices/actions/interface-itsec-admin-notice-action.php +41 -0
- core/lib/admin-notices/class-itsec-admin-notice-callback.php +51 -0
- core/lib/admin-notices/class-itsec-admin-notice-context.php +63 -0
- core/lib/admin-notices/class-itsec-admin-notice-globally-dismissible.php +75 -0
- core/lib/admin-notices/class-itsec-admin-notice-highlighted-log.php +142 -0
- core/lib/admin-notices/class-itsec-admin-notice-managers-only.php +45 -0
- core/lib/admin-notices/class-itsec-admin-notice-remind-me.php +83 -0
- core/lib/admin-notices/class-itsec-admin-notice-screen-blacklist.php +53 -0
- core/lib/admin-notices/class-itsec-admin-notice-static.php +52 -0
- core/lib/admin-notices/class-itsec-admin-notice-user-dismissible.php +76 -0
- core/lib/admin-notices/index.php +1 -0
- core/lib/admin-notices/interface-itsec-admin-notice.php +72 -0
- core/lib/class-itsec-lib-admin-notices.php +85 -0
- core/lib/class-itsec-lib-feature-flags.php +174 -0
- core/lib/class-itsec-lib-highlighted-logs.php +200 -0
- core/lib/class-itsec-lib-remote-messages.php +34 -8
- core/lib/class-itsec-mail.php +8 -0
- core/lib/log.php +1 -1
- core/modules/core/active.php +12 -4
- core/modules/core/class-itsec-admin-notices.php +198 -0
- core/modules/core/class-itsec-core-active.php +129 -0
- core/modules/core/class-itsec-core-admin.php +52 -2
- core/modules/core/class-rest-core-admin-notices-controller.php +201 -0
- core/modules/core/entries/admin-notices-api.js +1 -0
- core/modules/core/entries/admin-notices-dashboard-admin-bar.js +13 -0
- core/modules/core/entries/admin-notices.js +21 -0
- core/modules/core/entries/admin-notices/app.js +23 -0
- core/modules/core/entries/admin-notices/components/admin-bar/index.js +64 -0
- core/modules/core/entries/admin-notices/components/admin-bar/index.php +1 -0
- core/modules/core/entries/admin-notices/components/admin-bar/style.scss +52 -0
- core/modules/core/entries/admin-notices/components/index.php +1 -0
- core/modules/core/entries/admin-notices/components/notice-actions/index.js +108 -0
- core/modules/core/entries/admin-notices/components/notice-actions/index.php +1 -0
- core/modules/core/entries/admin-notices/components/notice-actions/style.scss +42 -0
- core/modules/core/entries/admin-notices/components/notice-list/index.js +21 -0
- core/modules/core/entries/admin-notices/components/notice-list/index.php +1 -0
- core/modules/core/entries/admin-notices/components/notice-list/style.scss +14 -0
- core/modules/core/entries/admin-notices/components/notice/index.js +96 -0
- core/modules/core/entries/admin-notices/components/notice/index.php +1 -0
- core/modules/core/entries/admin-notices/components/notice/style.scss +104 -0
- core/modules/core/entries/admin-notices/components/panel/index.js +87 -0
- core/modules/core/entries/admin-notices/components/panel/index.php +1 -0
- core/modules/core/entries/admin-notices/components/panel/style.scss +103 -0
- core/modules/core/entries/admin-notices/components/toolbar/index.js +74 -0
- core/modules/core/entries/admin-notices/components/toolbar/index.php +1 -0
- core/modules/core/entries/admin-notices/components/toolbar/style.scss +104 -0
- core/modules/core/entries/admin-notices/index.php +1 -0
- core/modules/core/entries/admin-notices/store/actions.js +126 -0
- core/modules/core/entries/admin-notices/store/controls.js +101 -0
- core/modules/core/entries/admin-notices/store/index.js +23 -0
- core/modules/core/entries/admin-notices/store/index.php +1 -0
- core/modules/core/entries/admin-notices/store/reducers.js +86 -0
- core/modules/core/entries/admin-notices/store/resolvers.js +33 -0
- core/modules/core/entries/admin-notices/store/selectors.js +55 -0
- core/modules/core/entries/admin-notices/style.scss +0 -0
- core/modules/core/entries/admin-notices/utils.js +19 -0
- core/modules/core/entries/index.php +1 -0
- core/modules/core/js/admin-notices.js +73 -0
- core/modules/core/settings.php +0 -13
- core/modules/core/setup.php +0 -70
- core/modules/core/sidebar-widget-backupbuddy-cross-promo.php +1 -1
- core/modules/core/sidebar-widget-support.php +2 -2
- core/modules/core/validator.php +0 -17
- core/modules/feature-flags/index.php +1 -0
- core/modules/feature-flags/settings-page.php +90 -0
- core/modules/file-change/admin.php +0 -97
- core/modules/file-change/class-itsec-file-change.php +14 -0
- core/modules/file-change/js/script.js +0 -36
- core/modules/file-change/logs.php +21 -6
- core/modules/file-change/scanner.php +3 -7
- core/modules/file-change/settings-page.php +0 -8
- core/modules/file-change/settings.php +0 -2
- core/modules/file-change/setup.php +7 -0
- core/modules/file-change/validator.php +3 -4
- core/modules/global/active.php +2 -64
- core/modules/global/notices.php +64 -0
- core/modules/global/settings-page.php +1 -0
- core/modules/global/settings.php +1 -0
- core/modules/global/validator.php +3 -2
- core/modules/ipcheck/active.php +1 -1
- core/modules/ipcheck/class-itsec-ipcheck.php +7 -183
- core/modules/ipcheck/settings-page.php +2 -1
- core/modules/ipcheck/settings.php +1 -1
- core/modules/ipcheck/utilities.php +203 -18
- core/modules/malware/class-itsec-malware-scan-results-template.php +4 -0
- core/modules/malware/class-itsec-malware-scanner.php +5 -0
- core/modules/malware/class-itsec-malware.php +6 -1
- core/modules/malware/js/malware.js +35 -19
- core/modules/malware/js/settings-page.js +16 -0
- core/modules/malware/logs.php +1 -1
- core/modules/notification-center/class-notification-center.php +11 -0
- core/modules/notification-center/logs.php +28 -1
- core/modules/security-check/scanner.php +2 -2
- core/modules/system-tweaks/config-generators.php +2 -2
- core/modules/system-tweaks/settings-page.php +1 -1
- core/package.json +114 -0
- core/packages/components/index.php +1 -0
- core/packages/components/src/async-select/index.js +30 -0
- core/packages/components/src/async-select/index.php +1 -0
- core/packages/components/src/close-button/index.js +19 -0
- core/packages/components/src/close-button/index.php +1 -0
- core/packages/components/src/close-button/style.scss +16 -0
- core/packages/components/src/hover-detector/index.js +124 -0
- core/packages/components/src/hover-detector/index.php +1 -0
- core/packages/components/src/index.js +8 -0
- core/packages/components/src/index.php +1 -0
- core/packages/components/src/loader/icon.svg +19 -0
- core/packages/components/src/loader/index.js +13 -0
- core/packages/components/src/loader/index.php +1 -0
- core/packages/components/src/loader/logo.svg +14 -0
- core/packages/components/src/loader/style.scss +21 -0
- core/packages/components/src/log-modal/index.js +68 -0
- core/packages/components/src/log-modal/index.php +1 -0
- core/packages/components/src/log-modal/style.scss +20 -0
- core/packages/components/src/malware-scan-results/blacklist-details.js +41 -0
- core/packages/components/src/malware-scan-results/index.js +42 -0
- core/packages/components/src/malware-scan-results/index.php +1 -0
- core/packages/components/src/malware-scan-results/malware-details.js +103 -0
- core/packages/components/src/malware-scan-results/style.scss +93 -0
- core/packages/components/src/malware-scan-results/system-error-details.js +30 -0
- core/packages/components/src/malware-scan-results/wp-error-details.js +33 -0
- core/packages/components/src/malware-scan-results/wrapped-section.js +62 -0
- core/packages/components/src/print-r/index.js +102 -0
- core/packages/components/src/print-r/index.php +1 -0
- core/packages/components/src/print-r/style.scss +11 -0
- core/packages/components/src/site-scan-results/blacklist-details.js +52 -0
- core/packages/components/src/site-scan-results/detail.js +9 -0
- core/packages/components/src/site-scan-results/details.js +7 -0
- core/packages/components/src/site-scan-results/index.js +37 -0
- core/packages/components/src/site-scan-results/index.php +1 -0
- core/packages/components/src/site-scan-results/known-vulnerabilities.js +30 -0
- core/packages/components/src/site-scan-results/malware-details.js +64 -0
- core/packages/components/src/site-scan-results/style.scss +89 -0
- core/packages/components/src/site-scan-results/system-error-details.js +24 -0
- core/packages/components/src/site-scan-results/wp-error-details.js +33 -0
- core/packages/components/src/site-scan-results/wrapped-section.js +67 -0
- core/packages/hocs/index.php +1 -0
- core/packages/hocs/src/index.js +5 -0
- core/packages/hocs/src/index.php +1 -0
- core/packages/hocs/src/with-debounce-handler.js +57 -0
- core/packages/hocs/src/with-interval.js +46 -0
- core/packages/hocs/src/with-prop-change-callback.js +43 -0
- core/packages/hocs/src/with-props.js +22 -0
- core/packages/hocs/src/with-width.js +64 -0
- core/packages/index.php +1 -0
- core/packages/style-guide/index.php +1 -0
- core/packages/style-guide/src/animations.scss +9 -0
- core/packages/style-guide/src/breakpoints.scss +6 -0
- core/packages/style-guide/src/colors.js +13 -0
- core/packages/style-guide/src/colors.scss +89 -0
- core/packages/style-guide/src/index.js +1 -0
- core/packages/style-guide/src/index.php +1 -0
- core/packages/style-guide/src/mixins.scss +76 -0
- core/packages/utils/index.php +1 -0
- core/packages/utils/src/error-response.js +35 -0
- core/packages/utils/src/index.js +165 -0
- core/packages/utils/src/index.php +1 -0
- core/packages/utils/src/test/index.js +41 -0
- core/packages/utils/src/test/index.php +1 -0
- core/packages/utils/src/wp-error.js +127 -0
- core/packages/webpack/index.php +1 -0
- core/packages/webpack/src/babel.js +15 -0
- core/packages/webpack/src/config/index.js +211 -0
- core/packages/webpack/src/config/index.php +1 -0
- core/packages/webpack/src/custom-templated-path-webpack-plugin/index.js +52 -0
- core/packages/webpack/src/custom-templated-path-webpack-plugin/index.php +1 -0
- core/packages/webpack/src/dynamic-public-path/index.js +25 -0
- core/packages/webpack/src/dynamic-public-path/index.php +1 -0
- core/packages/webpack/src/dynamic-public-path/loader.js +10 -0
- core/packages/webpack/src/index.php +1 -0
- core/packages/webpack/src/manifest/index.js +80 -0
- core/packages/webpack/src/manifest/index.php +1 -0
- core/packages/webpack/src/manifest/json-to-php.php +20 -0
- core/packages/webpack/src/split-chunk-name/index.js +92 -0
- core/packages/webpack/src/split-chunk-name/index.php +1 -0
- core/packages/webpack/src/style-only-entry-plugin/index.js +41 -0
- core/packages/webpack/src/style-only-entry-plugin/index.php +1 -0
- core/packages/webpack/src/wp-externals/index.js +39 -0
- core/packages/webpack/src/wp-externals/index.php +1 -0
- core/setup.php +13 -0
- dist/core/admin-notices-api.min.js +1 -0
- dist/core/admin-notices-dashboard-admin-bar.min.css +10 -0
- dist/core/admin-notices-dashboard-admin-bar.min.js +7 -0
- dist/core/admin-notices.min.css +11 -0
- dist/core/admin-notices.min.js +7 -0
- dist/core/index.php +1 -0
- dist/core/packages/components/index.php +1 -0
- dist/core/packages/components/site-scan-results/index.php +1 -0
- dist/core/packages/components/site-scan-results/style.min.css +2 -0
- dist/core/packages/index.php +1 -0
- dist/index.php +1 -0
- dist/manifest.php +108 -0
- history.txt +5 -0
- package.json +114 -0
- readme.txt +10 -4
- webpack.config.js +2 -0
better-wp-security.php
CHANGED
@@ -6,7 +6,7 @@
|
|
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.
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
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.4.0
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
core/admin-pages/css/style.css
CHANGED
@@ -34,7 +34,7 @@
|
|
34 |
cursor: inherit;
|
35 |
}
|
36 |
|
37 |
-
ul {
|
38 |
list-style: outside;
|
39 |
padding-left: 1em;
|
40 |
}
|
34 |
cursor: inherit;
|
35 |
}
|
36 |
|
37 |
+
.itsec-module-settings-container ul {
|
38 |
list-style: outside;
|
39 |
padding-left: 1em;
|
40 |
}
|
core/admin-pages/module-settings.php
CHANGED
@@ -36,7 +36,7 @@ class ITSEC_Module_Settings_Page {
|
|
36 |
* @access protected
|
37 |
* @var string
|
38 |
*/
|
39 |
-
protected $type = 'recommended'; // "
|
40 |
|
41 |
/**
|
42 |
* Whether the settings require resaving after activation in order to fully-activate the module.
|
36 |
* @access protected
|
37 |
* @var string
|
38 |
*/
|
39 |
+
protected $type = 'recommended'; // "advanced" or "recommended"
|
40 |
|
41 |
/**
|
42 |
* Whether the settings require resaving after activation in order to fully-activate the module.
|
core/admin-pages/page-debug.php
CHANGED
@@ -225,7 +225,9 @@ final class ITSEC_Debug_Page {
|
|
225 |
<th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
|
226 |
<th><?php esc_html_e( 'Fire At', 'better-wp-security' ) ?></th>
|
227 |
<th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
|
228 |
-
<th
|
|
|
|
|
229 |
<th></th>
|
230 |
</tr>
|
231 |
</thead>
|
@@ -235,7 +237,9 @@ final class ITSEC_Debug_Page {
|
|
235 |
<td><?php echo esc_html( $event['id'] ); ?></td>
|
236 |
<td><?php echo date( 'Y-m-d H:i:s', $event['fire_at'] ); ?> (<?php echo esc_html( human_time_diff( $event['fire_at'] ) ) ?>)</td>
|
237 |
<td><?php echo isset( $event['schedule'] ) ? $event['schedule'] : '–'; ?></td>
|
238 |
-
<td
|
|
|
|
|
239 |
<td>
|
240 |
<button class="button" data-id="<?php echo esc_attr( $event['id'] ); ?>"
|
241 |
data-data="<?php echo isset( $event['schedule'] ) ? '' : esc_attr( $event['hash'] ); ?>">
|
@@ -310,6 +314,8 @@ final class ITSEC_Debug_Page {
|
|
310 |
'ITSEC_DISABLE_INACTIVE_USER_CHECK',
|
311 |
);
|
312 |
|
|
|
|
|
313 |
$info['iThemes Security'] = array(
|
314 |
'Build' => ITSEC_Core::get_plugin_build(),
|
315 |
'Pro' => ITSEC_Core::is_pro(),
|
@@ -317,6 +323,7 @@ final class ITSEC_Debug_Page {
|
|
317 |
'Cron' => ITSEC_Lib::use_cron(),
|
318 |
'Cron Status' => ITSEC_Lib::is_cron_working(),
|
319 |
'Scheduler' => get_class( ITSEC_Core::get_scheduler() ),
|
|
|
320 |
);
|
321 |
|
322 |
foreach ( $defines as $define ) {
|
@@ -422,4 +429,4 @@ final class ITSEC_Debug_Page {
|
|
422 |
}
|
423 |
}
|
424 |
|
425 |
-
new ITSEC_Debug_Page();
|
225 |
<th><?php esc_html_e( 'ID', 'better-wp-security' ) ?></th>
|
226 |
<th><?php esc_html_e( 'Fire At', 'better-wp-security' ) ?></th>
|
227 |
<th><?php esc_html_e( 'Schedule', 'better-wp-security' ) ?></th>
|
228 |
+
<th>
|
229 |
+
<button class="button-link" id="itsec-events-data-toggle"><?php esc_html_e( 'Data', 'better-wp-security' ) ?></button>
|
230 |
+
</th>
|
231 |
<th></th>
|
232 |
</tr>
|
233 |
</thead>
|
237 |
<td><?php echo esc_html( $event['id'] ); ?></td>
|
238 |
<td><?php echo date( 'Y-m-d H:i:s', $event['fire_at'] ); ?> (<?php echo esc_html( human_time_diff( $event['fire_at'] ) ) ?>)</td>
|
239 |
<td><?php echo isset( $event['schedule'] ) ? $event['schedule'] : '–'; ?></td>
|
240 |
+
<td>
|
241 |
+
<div class="hidden itsec-events-data"><?php $event['data'] ? ITSEC_Lib::print_r( $event['data'] ) : print( '–' ); ?></div>
|
242 |
+
</td>
|
243 |
<td>
|
244 |
<button class="button" data-id="<?php echo esc_attr( $event['id'] ); ?>"
|
245 |
data-data="<?php echo isset( $event['schedule'] ) ? '' : esc_attr( $event['hash'] ); ?>">
|
314 |
'ITSEC_DISABLE_INACTIVE_USER_CHECK',
|
315 |
);
|
316 |
|
317 |
+
ITSEC_Lib::load( 'feature-flags' );
|
318 |
+
|
319 |
$info['iThemes Security'] = array(
|
320 |
'Build' => ITSEC_Core::get_plugin_build(),
|
321 |
'Pro' => ITSEC_Core::is_pro(),
|
323 |
'Cron' => ITSEC_Lib::use_cron(),
|
324 |
'Cron Status' => ITSEC_Lib::is_cron_working(),
|
325 |
'Scheduler' => get_class( ITSEC_Core::get_scheduler() ),
|
326 |
+
'Features' => wp_sprintf( '%l', ITSEC_Lib_Feature_Flags::get_enabled() ),
|
327 |
);
|
328 |
|
329 |
foreach ( $defines as $define ) {
|
429 |
}
|
430 |
}
|
431 |
|
432 |
+
new ITSEC_Debug_Page();
|
core/core.php
CHANGED
@@ -24,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
-
private $plugin_build =
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
@@ -159,11 +159,6 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
159 |
$this->setup_scheduler();
|
160 |
ITSEC_Modules::run_active_modules();
|
161 |
|
162 |
-
//Admin bar links
|
163 |
-
if ( ! ITSEC_Modules::get_setting( 'global', 'hide_admin_bar' ) ) {
|
164 |
-
add_action( 'admin_bar_menu', array( $this, 'modify_admin_bar' ), 99 );
|
165 |
-
}
|
166 |
-
|
167 |
$this->login_interstitial = new ITSEC_Lib_Login_Interstitial();
|
168 |
$this->login_interstitial->run();
|
169 |
|
@@ -343,10 +338,9 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
343 |
ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
|
344 |
ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
|
345 |
ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
|
346 |
-
|
347 |
ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
|
348 |
-
|
349 |
ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
|
|
|
350 |
|
351 |
if ( ! ITSEC_Core::is_pro() ) {
|
352 |
ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
|
@@ -404,58 +398,6 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
404 |
return $meta;
|
405 |
}
|
406 |
|
407 |
-
/**
|
408 |
-
* Add admin bar items
|
409 |
-
*
|
410 |
-
* @since 4.0
|
411 |
-
*
|
412 |
-
* @param WP_Admin_Bar $wp_admin_bar
|
413 |
-
*
|
414 |
-
* @return void
|
415 |
-
*/
|
416 |
-
public function modify_admin_bar( $wp_admin_bar ) {
|
417 |
-
|
418 |
-
if ( ! ITSEC_Core::current_user_can_manage() ) {
|
419 |
-
return;
|
420 |
-
}
|
421 |
-
|
422 |
-
// Add the Parent link.
|
423 |
-
$wp_admin_bar->add_node(
|
424 |
-
array(
|
425 |
-
'title' => __( 'Security', 'better-wp-security' ),
|
426 |
-
'href' => self::get_settings_page_url(),
|
427 |
-
'id' => 'itsec_admin_bar_menu',
|
428 |
-
)
|
429 |
-
);
|
430 |
-
|
431 |
-
$wp_admin_bar->add_node(
|
432 |
-
array(
|
433 |
-
'parent' => 'itsec_admin_bar_menu',
|
434 |
-
'title' => __( 'Settings', 'better-wp-security' ),
|
435 |
-
'href' => self::get_settings_page_url(),
|
436 |
-
'id' => 'itsec_admin_bar_settings',
|
437 |
-
)
|
438 |
-
);
|
439 |
-
|
440 |
-
$wp_admin_bar->add_node(
|
441 |
-
array(
|
442 |
-
'parent' => 'itsec_admin_bar_menu',
|
443 |
-
'title' => __( 'Security Check', 'better-wp-security' ),
|
444 |
-
'href' => self::get_security_check_page_url(),
|
445 |
-
'id' => 'itsec_admin_bar_security_check',
|
446 |
-
)
|
447 |
-
);
|
448 |
-
|
449 |
-
$wp_admin_bar->add_node(
|
450 |
-
array(
|
451 |
-
'parent' => 'itsec_admin_bar_menu',
|
452 |
-
'title' => __( 'Logs', 'better-wp-security' ),
|
453 |
-
'href' => self::get_logs_page_url(),
|
454 |
-
'id' => 'itsec_admin_bar_logs',
|
455 |
-
)
|
456 |
-
);
|
457 |
-
}
|
458 |
-
|
459 |
/**
|
460 |
* Dispatch a request to upgrade the data schema to another version.
|
461 |
*
|
@@ -495,6 +437,8 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
495 |
* @param bool $all_pages Display the notice on all pages or only on ITSEC, plugins, and upgrade page.
|
496 |
*/
|
497 |
public static function add_notice( $callback, $all_pages = false ) {
|
|
|
|
|
498 |
global $pagenow, $plugin_page;
|
499 |
|
500 |
if ( ! $all_pages && ! in_array( $pagenow, array( 'plugins.php', 'update-core.php' ) ) && ( ! isset( $plugin_page ) || ! in_array( $plugin_page, array( 'itsec', 'itsec-logs' ) ) ) ) {
|
@@ -576,6 +520,36 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
576 |
return is_dir( self::get_plugin_dir() . 'pro' );
|
577 |
}
|
578 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
579 |
/**
|
580 |
* Get the current local timestamp.
|
581 |
*
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
+
private $plugin_build = 4114;
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
159 |
$this->setup_scheduler();
|
160 |
ITSEC_Modules::run_active_modules();
|
161 |
|
|
|
|
|
|
|
|
|
|
|
162 |
$this->login_interstitial = new ITSEC_Lib_Login_Interstitial();
|
163 |
$this->login_interstitial->run();
|
164 |
|
338 |
ITSEC_Modules::register_module( 'system-tweaks', "$path/modules/system-tweaks" );
|
339 |
ITSEC_Modules::register_module( 'wordpress-salts', "$path/modules/salts", 'always-active' );
|
340 |
ITSEC_Modules::register_module( 'wordpress-tweaks', "$path/modules/wordpress-tweaks", 'default-active' );
|
|
|
341 |
ITSEC_Modules::register_module( 'file-writing', "$path/modules/file-writing", 'always-active' );
|
|
|
342 |
ITSEC_Modules::register_module( 'malware', "$path/modules/malware", 'always-active' );
|
343 |
+
ITSEC_Modules::register_module( 'feature-flags', "$path/modules/feature-flags", 'always-active' );
|
344 |
|
345 |
if ( ! ITSEC_Core::is_pro() ) {
|
346 |
ITSEC_Modules::register_module( 'pro-module-upsells', "$path/modules/pro", 'always-active' );
|
398 |
return $meta;
|
399 |
}
|
400 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
401 |
/**
|
402 |
* Dispatch a request to upgrade the data schema to another version.
|
403 |
*
|
437 |
* @param bool $all_pages Display the notice on all pages or only on ITSEC, plugins, and upgrade page.
|
438 |
*/
|
439 |
public static function add_notice( $callback, $all_pages = false ) {
|
440 |
+
_deprecated_function( __METHOD__, '6.0.0', 'ITSEC_Lib_Admin_Notices::register' );
|
441 |
+
|
442 |
global $pagenow, $plugin_page;
|
443 |
|
444 |
if ( ! $all_pages && ! in_array( $pagenow, array( 'plugins.php', 'update-core.php' ) ) && ( ! isset( $plugin_page ) || ! in_array( $plugin_page, array( 'itsec', 'itsec-logs' ) ) ) ) {
|
520 |
return is_dir( self::get_plugin_dir() . 'pro' );
|
521 |
}
|
522 |
|
523 |
+
/**
|
524 |
+
* Is this an actively licensed Pro installation.
|
525 |
+
*
|
526 |
+
* @return bool
|
527 |
+
*/
|
528 |
+
public static function is_licensed() {
|
529 |
+
if ( ! self::is_pro() ) {
|
530 |
+
return false;
|
531 |
+
}
|
532 |
+
|
533 |
+
if ( ! isset( $GLOBALS['ithemes_updater_path'] ) ) {
|
534 |
+
return false;
|
535 |
+
}
|
536 |
+
|
537 |
+
include_once( $GLOBALS['ithemes_updater_path'] . '/keys.php' );
|
538 |
+
include_once( $GLOBALS['ithemes_updater_path'] . '/packages.php' );
|
539 |
+
|
540 |
+
if ( ! class_exists( 'Ithemes_Updater_Packages' ) ) {
|
541 |
+
return false;
|
542 |
+
}
|
543 |
+
|
544 |
+
$package_details = Ithemes_Updater_Packages::get_full_details();
|
545 |
+
|
546 |
+
if ( empty( $package_details['packages']['ithemes-security-pro/ithemes-security-pro.php']['status'] ) ) {
|
547 |
+
return false;
|
548 |
+
}
|
549 |
+
|
550 |
+
return 'active' === $package_details['packages']['ithemes-security-pro/ithemes-security-pro.php']['status'];
|
551 |
+
}
|
552 |
+
|
553 |
/**
|
554 |
* Get the current local timestamp.
|
555 |
*
|
core/history.txt
CHANGED
@@ -798,3 +798,9 @@
|
|
798 |
Bug Fix: Resolve warning when a user is set to "No Role".
|
799 |
5.1.4 - 2019-03-22 - Chris Jean & Timothy Jacobs
|
800 |
Bug Fix: Hide backend bypass.
|
|
|
|
|
|
|
|
|
|
|
|
798 |
Bug Fix: Resolve warning when a user is set to "No Role".
|
799 |
5.1.4 - 2019-03-22 - Chris Jean & Timothy Jacobs
|
800 |
Bug Fix: Hide backend bypass.
|
801 |
+
5.2.0 - 2019-05-30 - Chris Jean & Timothy Jacobs
|
802 |
+
New: iThemes Security Admin Notices are now conveniently located in the new Security Messages Menu. Check your notices in the Security menu on the WordPress Admin Bar.
|
803 |
+
Tweak: Remove 'DELETE' method from "System Tweaks -> Filter Request Methods"
|
804 |
+
5.2.1 - 2019-06-06 - Chris Jean & Timothy Jacobs
|
805 |
+
Enhancement: Add Security Message when a Notification Center email fails to send.
|
806 |
+
Enhancement: Replace Trace IP with IP Tracker Online.
|
core/lib.php
CHANGED
@@ -587,9 +587,9 @@ final class ITSEC_Lib {
|
|
587 |
*/
|
588 |
public static function get_trace_ip_link( $ip = false ) {
|
589 |
if ( empty( $ip ) ) {
|
590 |
-
return '
|
591 |
} else {
|
592 |
-
return 'http://www.
|
593 |
}
|
594 |
}
|
595 |
|
@@ -1161,6 +1161,34 @@ final class ITSEC_Lib {
|
|
1161 |
return $array;
|
1162 |
}
|
1163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1164 |
/**
|
1165 |
* Insert an element after a given key.
|
1166 |
*
|
@@ -1701,4 +1729,13 @@ final class ITSEC_Lib {
|
|
1701 |
public static function str_ends_with( $haystack, $needle ) {
|
1702 |
return '' === $needle || substr_compare( $haystack, $needle, - strlen( $needle ) ) === 0;
|
1703 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1704 |
}
|
587 |
*/
|
588 |
public static function get_trace_ip_link( $ip = false ) {
|
589 |
if ( empty( $ip ) ) {
|
590 |
+
return 'https://www.iptrackeronline.com/ithemes.php';
|
591 |
} else {
|
592 |
+
return 'http://www.iptrackeronline.com/ithemes.php?ip_address=' . urlencode( $ip );
|
593 |
}
|
594 |
}
|
595 |
|
1161 |
return $array;
|
1162 |
}
|
1163 |
|
1164 |
+
/**
|
1165 |
+
* Inserts a new key/value before the key in the array.
|
1166 |
+
*
|
1167 |
+
* @param string $key The key to insert before.
|
1168 |
+
* @param array $array An array to insert in to.
|
1169 |
+
* @param string $new_key The key to insert.
|
1170 |
+
* @param mixed $new_value The value to insert.
|
1171 |
+
*
|
1172 |
+
* @return array
|
1173 |
+
*/
|
1174 |
+
public static function array_insert_before( $key, $array, $new_key, $new_value) {
|
1175 |
+
if ( array_key_exists( $key, $array ) ) {
|
1176 |
+
$new = array();
|
1177 |
+
foreach ( $array as $k => $value ) {
|
1178 |
+
if ( $k === $key ) {
|
1179 |
+
$new[ $new_key ] = $new_value;
|
1180 |
+
}
|
1181 |
+
$new[ $k ] = $value;
|
1182 |
+
}
|
1183 |
+
|
1184 |
+
return $new;
|
1185 |
+
}
|
1186 |
+
|
1187 |
+
$array[ $new_key ] = $new_value;
|
1188 |
+
|
1189 |
+
return $array;
|
1190 |
+
}
|
1191 |
+
|
1192 |
/**
|
1193 |
* Insert an element after a given key.
|
1194 |
*
|
1729 |
public static function str_ends_with( $haystack, $needle ) {
|
1730 |
return '' === $needle || substr_compare( $haystack, $needle, - strlen( $needle ) ) === 0;
|
1731 |
}
|
1732 |
+
|
1733 |
+
/**
|
1734 |
+
* Load a library class definition.
|
1735 |
+
*
|
1736 |
+
* @param string $name
|
1737 |
+
*/
|
1738 |
+
public static function load( $name ) {
|
1739 |
+
require_once( dirname( __FILE__ ) . "/lib/class-itsec-lib-{$name}.php" );
|
1740 |
+
}
|
1741 |
}
|
core/lib/admin-notices/actions/class-itsec-admin-notice-action-callback.php
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Action_Callback implements ITSEC_Admin_Notice_Action {
|
4 |
+
|
5 |
+
private $style;
|
6 |
+
private $title;
|
7 |
+
private $callable;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* ITSEC_Admin_Notice_Action_Button constructor.
|
11 |
+
*
|
12 |
+
* @param string $style
|
13 |
+
* @param string $title
|
14 |
+
* @param callable $callable
|
15 |
+
*/
|
16 |
+
public function __construct( $style, $title, $callable ) {
|
17 |
+
$this->style = $style;
|
18 |
+
$this->title = $title;
|
19 |
+
$this->callable = $callable;
|
20 |
+
}
|
21 |
+
|
22 |
+
public function handle( WP_User $user, array $data ) {
|
23 |
+
return call_user_func( $this->callable, $user, $data );
|
24 |
+
}
|
25 |
+
|
26 |
+
public function get_title() {
|
27 |
+
return $this->title;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_style() {
|
31 |
+
return $this->style;
|
32 |
+
}
|
33 |
+
|
34 |
+
public function get_uri() {
|
35 |
+
return '';
|
36 |
+
}
|
37 |
+
}
|
core/lib/admin-notices/actions/class-itsec-admin-notice-action-link.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Admin_Notice_Action_Link implements ITSEC_Admin_Notice_Action {
|
4 |
+
|
5 |
+
/** @var string */
|
6 |
+
private $uri;
|
7 |
+
|
8 |
+
/** @var string */
|
9 |
+
private $title;
|
10 |
+
|
11 |
+
/** @var string */
|
12 |
+
private $style;
|
13 |
+
|
14 |
+
/** @var callable|null */
|
15 |
+
private $callback;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* ITSEC_Admin_Notice_Action_Link constructor.
|
19 |
+
*
|
20 |
+
* @param string $uri
|
21 |
+
* @param string $title
|
22 |
+
* @param string $style
|
23 |
+
* @param callable|null $callback
|
24 |
+
*/
|
25 |
+
public function __construct( $uri, $title, $style = self::S_LINK, $callback = null ) {
|
26 |
+
$this->uri = $uri;
|
27 |
+
$this->title = $title;
|
28 |
+
$this->style = $style;
|
29 |
+
$this->callback = $callback;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function handle( WP_User $user, array $data ) {
|
33 |
+
return $this->callback ? call_user_func( $this->callback, $user, $data ) : null;
|
34 |
+
}
|
35 |
+
|
36 |
+
public function get_title() {
|
37 |
+
return $this->title;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function get_style() {
|
41 |
+
return $this->style;
|
42 |
+
}
|
43 |
+
|
44 |
+
public function get_uri() {
|
45 |
+
return $this->uri;
|
46 |
+
}
|
47 |
+
}
|
core/lib/admin-notices/actions/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/lib/admin-notices/actions/interface-itsec-admin-notice-action.php
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface ITSEC_Admin_Notice_Action {
|
4 |
+
|
5 |
+
const S_LINK = 'link';
|
6 |
+
const S_BUTTON = 'button';
|
7 |
+
const S_PRIMARY = 'primary';
|
8 |
+
const S_CLOSE = 'close';
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Handle the action.
|
12 |
+
*
|
13 |
+
* @param WP_User $user
|
14 |
+
* @param array $data
|
15 |
+
*
|
16 |
+
* @return WP_Error|null
|
17 |
+
*/
|
18 |
+
public function handle( WP_User $user, array $data );
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Get the title of this action.
|
22 |
+
*
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
public function get_title();
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Get the action presentation style.
|
29 |
+
*
|
30 |
+
* @return string
|
31 |
+
*/
|
32 |
+
public function get_style();
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Get the URI that should be navigated to when triggering this action.
|
36 |
+
*
|
37 |
+
* @return string URI or empty string
|
38 |
+
*/
|
39 |
+
public function get_uri();
|
40 |
+
}
|
41 |
+
|
core/lib/admin-notices/class-itsec-admin-notice-callback.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Callback implements ITSEC_Admin_Notice {
|
4 |
+
private $id;
|
5 |
+
private $message;
|
6 |
+
private $title;
|
7 |
+
private $severity;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* ITSEC_Admin_Notice_Concrete constructor.
|
11 |
+
*
|
12 |
+
* @param string $id Globally unique notice ID.
|
13 |
+
* @param callable $message Callable to return the message to display to the user.
|
14 |
+
* @param callable $title Callable to return the title.
|
15 |
+
* @param string $severity The message's severity.
|
16 |
+
*/
|
17 |
+
public function __construct( $id, $message, $title = null, $severity = self::S_INFO ) {
|
18 |
+
$this->id = $id;
|
19 |
+
$this->message = $message;
|
20 |
+
$this->title = $title;
|
21 |
+
$this->severity = $severity;
|
22 |
+
}
|
23 |
+
|
24 |
+
public function get_id() {
|
25 |
+
return $this->id;
|
26 |
+
}
|
27 |
+
|
28 |
+
public function get_title() {
|
29 |
+
return $this->title ? call_user_func( $this->title ) : '';
|
30 |
+
}
|
31 |
+
|
32 |
+
public function get_message() {
|
33 |
+
return call_user_func( $this->message );
|
34 |
+
}
|
35 |
+
|
36 |
+
public function get_severity() {
|
37 |
+
return $this->severity;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function get_meta() {
|
41 |
+
return array();
|
42 |
+
}
|
43 |
+
|
44 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
45 |
+
return true;
|
46 |
+
}
|
47 |
+
|
48 |
+
public function get_actions() {
|
49 |
+
return array();
|
50 |
+
}
|
51 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-context.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Admin_Notice_Context {
|
4 |
+
|
5 |
+
const AJAX = 'ajax';
|
6 |
+
const ADMIN_ACTION = 'admin-action';
|
7 |
+
const FRONT = 'front';
|
8 |
+
|
9 |
+
/** @var WP_User */
|
10 |
+
private $user;
|
11 |
+
|
12 |
+
/** @var string */
|
13 |
+
private $screen_id;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ITSEC_Admin_Notice_Context constructor.
|
17 |
+
*
|
18 |
+
* @param WP_User $user
|
19 |
+
* @param string $screen_id
|
20 |
+
*/
|
21 |
+
public function __construct( WP_User $user, $screen_id ) {
|
22 |
+
$this->user = $user;
|
23 |
+
$this->screen_id = $screen_id;
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Create the context from the globally defined state.
|
28 |
+
*
|
29 |
+
* @return ITSEC_Admin_Notice_Context
|
30 |
+
* @throws Exception
|
31 |
+
*/
|
32 |
+
public static function from_global_state() {
|
33 |
+
if ( function_exists( 'get_current_screen' ) ) {
|
34 |
+
if ( ! $screen = get_current_screen() ) {
|
35 |
+
throw new Exception( 'Cannot instantiate a notice context from global state before the WP_Screen object is available.' );
|
36 |
+
}
|
37 |
+
|
38 |
+
$screen_id = $screen->id;
|
39 |
+
} else {
|
40 |
+
$screen_id = self::FRONT;
|
41 |
+
}
|
42 |
+
|
43 |
+
return new self( wp_get_current_user(), $screen_id );
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get the user the notice is being interacted with.
|
48 |
+
*
|
49 |
+
* @return WP_User
|
50 |
+
*/
|
51 |
+
public function get_user() {
|
52 |
+
return $this->user;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Get the ID of the screen being displayed.
|
57 |
+
*
|
58 |
+
* @return string
|
59 |
+
*/
|
60 |
+
public function get_screen_id() {
|
61 |
+
return $this->screen_id;
|
62 |
+
}
|
63 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-globally-dismissible.php
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Globally_Dismissible implements ITSEC_Admin_Notice {
|
4 |
+
/** @var ITSEC_Admin_Notice */
|
5 |
+
private $notice;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* ITSEC_Admin_Notice_Dismissible constructor.
|
9 |
+
*
|
10 |
+
* @param ITSEC_Admin_Notice $notice
|
11 |
+
*/
|
12 |
+
public function __construct( ITSEC_Admin_Notice $notice ) { $this->notice = $notice; }
|
13 |
+
|
14 |
+
public function get_id() {
|
15 |
+
return $this->notice->get_id();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function get_title() {
|
19 |
+
return $this->notice->get_title();
|
20 |
+
}
|
21 |
+
|
22 |
+
public function get_message() {
|
23 |
+
return $this->notice->get_message();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function get_severity() {
|
27 |
+
return $this->notice->get_severity();
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_meta() {
|
31 |
+
return $this->notice->get_meta();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
35 |
+
$dismissed = $this->get_storage();
|
36 |
+
|
37 |
+
if ( in_array( $this->get_id(), $dismissed, true ) ) {
|
38 |
+
return false;
|
39 |
+
}
|
40 |
+
|
41 |
+
return $this->notice->show_for_context( $context );
|
42 |
+
}
|
43 |
+
|
44 |
+
public function get_actions() {
|
45 |
+
return array_merge( $this->notice->get_actions(), array(
|
46 |
+
'dismiss' => new ITSEC_Admin_Notice_Action_Callback(
|
47 |
+
ITSEC_Admin_Notice_Action::S_CLOSE,
|
48 |
+
esc_html__( 'Dismiss', 'better-wp-security' ),
|
49 |
+
array( $this, '_handle_dismiss' )
|
50 |
+
),
|
51 |
+
) );
|
52 |
+
}
|
53 |
+
|
54 |
+
public function _handle_dismiss( WP_User $user, array $data ) {
|
55 |
+
$dismissed = $this->get_storage();
|
56 |
+
$dismissed[] = $this->get_id();
|
57 |
+
$this->save_storage( $dismissed );
|
58 |
+
|
59 |
+
return null;
|
60 |
+
}
|
61 |
+
|
62 |
+
private function get_storage() {
|
63 |
+
$dismissed = get_site_option( 'itsec_dismissed_notices', array() );
|
64 |
+
|
65 |
+
if ( ! is_array( $dismissed ) ) {
|
66 |
+
$dismissed = array();
|
67 |
+
}
|
68 |
+
|
69 |
+
return $dismissed;
|
70 |
+
}
|
71 |
+
|
72 |
+
private function save_storage( $storage ) {
|
73 |
+
update_site_option( 'itsec_dismissed_notices', $storage );
|
74 |
+
}
|
75 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-highlighted-log.php
ADDED
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Highlighted_Log implements ITSEC_Admin_Notice {
|
4 |
+
|
5 |
+
private $highlight_id;
|
6 |
+
private $log;
|
7 |
+
private $filtered;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* ITSEC_Admin_Notice_Highlighted_Log constructor.
|
11 |
+
*
|
12 |
+
* @param string|int $id_or_slug
|
13 |
+
* @param array $log_item
|
14 |
+
*/
|
15 |
+
public function __construct( $id_or_slug, array $log_item ) {
|
16 |
+
$this->highlight_id = $id_or_slug;
|
17 |
+
$this->log = $log_item;
|
18 |
+
}
|
19 |
+
|
20 |
+
public function get_id() {
|
21 |
+
return "highlighted-log-{$this->highlight_id}";
|
22 |
+
}
|
23 |
+
|
24 |
+
public function get_title() {
|
25 |
+
ITSEC_Modules::load_module_file( 'logs.php' );
|
26 |
+
|
27 |
+
return apply_filters( "itsec_highlighted_log_{$this->highlight_id}_notice_title", '', $this->log );
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_message() {
|
31 |
+
ITSEC_Modules::load_module_file( 'logs.php' );
|
32 |
+
$filtered = $this->get_filtered_log();
|
33 |
+
|
34 |
+
$message = '<strong>' . esc_html( $filtered['module_display'] ) . '</strong>: ' . $filtered['description'];
|
35 |
+
|
36 |
+
return apply_filters( "itsec_highlighted_log_{$this->highlight_id}_notice_message", $message, $this->log );
|
37 |
+
}
|
38 |
+
|
39 |
+
public function get_severity() {
|
40 |
+
switch ( $this->log['type'] ) {
|
41 |
+
case 'critical-issue':
|
42 |
+
case 'fatal':
|
43 |
+
return self::S_ERROR;
|
44 |
+
case 'error':
|
45 |
+
case 'warning':
|
46 |
+
return self::S_WARN;
|
47 |
+
case 'action':
|
48 |
+
return self::S_SUCCESS;
|
49 |
+
default:
|
50 |
+
return self::S_INFO;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
public function get_meta() {
|
55 |
+
$filtered = $this->get_filtered_log();
|
56 |
+
|
57 |
+
return array(
|
58 |
+
self::M_CREATED_AT => array(
|
59 |
+
'label' => esc_html__( 'Created At', 'better-wp-security' ),
|
60 |
+
'value' => $this->log['init_timestamp'],
|
61 |
+
'formatted' => sprintf(
|
62 |
+
'%s – %s ago',
|
63 |
+
date_i18n( 'M d, Y g:i A', strtotime( $this->log['init_timestamp'] ) ),
|
64 |
+
human_time_diff( strtotime( $this->log['init_timestamp'] ) )
|
65 |
+
),
|
66 |
+
),
|
67 |
+
'module' => array(
|
68 |
+
'label' => esc_html__( 'Module', 'better-wp-security' ),
|
69 |
+
'value' => $this->log['module'],
|
70 |
+
'formatted' => $filtered['module_display'],
|
71 |
+
),
|
72 |
+
'description' => array(
|
73 |
+
'label' => esc_html__( 'Description', 'better-wp-security' ),
|
74 |
+
'value' => $this->log['code'],
|
75 |
+
'formatted' => $filtered['description'],
|
76 |
+
),
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
|
84 |
+
public function get_actions() {
|
85 |
+
$actions = array(
|
86 |
+
'dismiss' => new ITSEC_Admin_Notice_Action_Callback(
|
87 |
+
ITSEC_Admin_Notice_Action::S_CLOSE,
|
88 |
+
esc_html__( 'Dismiss', 'better-wp-security' ),
|
89 |
+
array( $this, '_handle_dismiss' )
|
90 |
+
),
|
91 |
+
'view' => new ITSEC_Admin_Notice_Action_Link(
|
92 |
+
add_query_arg( 'id', $this->log['id'], ITSEC_Core::get_logs_page_url() ),
|
93 |
+
esc_html__( 'View Details', 'better-wp-security' ),
|
94 |
+
ITSEC_Admin_Notice_Action::S_LINK,
|
95 |
+
array( $this, '_handle_dismiss' )
|
96 |
+
)
|
97 |
+
);
|
98 |
+
|
99 |
+
if ( ! is_numeric( $this->highlight_id ) ) {
|
100 |
+
$actions['mute'] = new ITSEC_Admin_Notice_Action_Callback(
|
101 |
+
ITSEC_Admin_Notice_Action::S_BUTTON,
|
102 |
+
esc_html__( 'Dismiss Permanently', 'better-wp-security' ),
|
103 |
+
array( $this, '_handle_mute' )
|
104 |
+
);
|
105 |
+
}
|
106 |
+
|
107 |
+
return $actions;
|
108 |
+
}
|
109 |
+
|
110 |
+
public function _handle_mute( WP_User $user, array $data ) {
|
111 |
+
$this->_handle_dismiss( $user, $data );
|
112 |
+
ITSEC_Lib_Highlighted_Logs::mute( $this->highlight_id );
|
113 |
+
}
|
114 |
+
|
115 |
+
public function _handle_dismiss( WP_User $user, array $data ) {
|
116 |
+
ITSEC_Lib_Highlighted_Logs::dismiss_highlight( $this->highlight_id );
|
117 |
+
}
|
118 |
+
|
119 |
+
private function get_filtered_log() {
|
120 |
+
if ( $this->filtered ) {
|
121 |
+
return $this->filtered;
|
122 |
+
}
|
123 |
+
|
124 |
+
$item = $this->log;
|
125 |
+
|
126 |
+
if ( false === strpos( $item['code'], '::' ) ) {
|
127 |
+
$code = $item['code'];
|
128 |
+
$data = array();
|
129 |
+
} else {
|
130 |
+
list( $code, $data ) = explode( '::', $item['code'], 2 );
|
131 |
+
$data = explode( ',', $data );
|
132 |
+
}
|
133 |
+
|
134 |
+
$item['description'] = $item['code'];
|
135 |
+
$item['module_display'] = $item['module'];
|
136 |
+
|
137 |
+
ITSEC_Modules::load_module_file( 'logs.php' );
|
138 |
+
$item = apply_filters( "itsec_logs_prepare_{$item['module']}_entry_for_list_display", $item, $code, $data );
|
139 |
+
|
140 |
+
return $this->filtered = $item;
|
141 |
+
}
|
142 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-managers-only.php
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Managers_Only implements ITSEC_Admin_Notice {
|
4 |
+
/** @var ITSEC_Admin_Notice */
|
5 |
+
private $notice;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* ITSEC_Admin_Notice_Admin_Only constructor.
|
9 |
+
*
|
10 |
+
* @param ITSEC_Admin_Notice $notice
|
11 |
+
*/
|
12 |
+
public function __construct( ITSEC_Admin_Notice $notice ) { $this->notice = $notice; }
|
13 |
+
|
14 |
+
public function get_id() {
|
15 |
+
return $this->notice->get_id();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function get_title() {
|
19 |
+
return $this->notice->get_title();
|
20 |
+
}
|
21 |
+
|
22 |
+
public function get_message() {
|
23 |
+
return $this->notice->get_message();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function get_severity() {
|
27 |
+
return $this->notice->get_severity();
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_meta() {
|
31 |
+
return $this->notice->get_meta();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
35 |
+
if ( ! user_can( $context->get_user()->ID, ITSEC_Core::get_required_cap() ) ) {
|
36 |
+
return false;
|
37 |
+
}
|
38 |
+
|
39 |
+
return $this->notice->show_for_context( $context );
|
40 |
+
}
|
41 |
+
|
42 |
+
public function get_actions() {
|
43 |
+
return $this->notice->get_actions();
|
44 |
+
}
|
45 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-remind-me.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Remind_Me implements ITSEC_Admin_Notice {
|
4 |
+
/** @var ITSEC_Admin_Notice */
|
5 |
+
private $notice;
|
6 |
+
|
7 |
+
/** @var int */
|
8 |
+
private $ttl;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* ITSEC_Admin_Notice_Remind_Me constructor.
|
12 |
+
*
|
13 |
+
* @param ITSEC_Admin_Notice $notice
|
14 |
+
* @param int $ttl
|
15 |
+
*/
|
16 |
+
public function __construct( ITSEC_Admin_Notice $notice, $ttl ) {
|
17 |
+
$this->notice = $notice;
|
18 |
+
$this->ttl = $ttl;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function get_id() {
|
22 |
+
return $this->notice->get_id();
|
23 |
+
}
|
24 |
+
|
25 |
+
public function get_title() {
|
26 |
+
return $this->notice->get_title();
|
27 |
+
}
|
28 |
+
|
29 |
+
public function get_message() {
|
30 |
+
return $this->notice->get_message();
|
31 |
+
}
|
32 |
+
|
33 |
+
public function get_meta() {
|
34 |
+
return $this->notice->get_meta();
|
35 |
+
}
|
36 |
+
|
37 |
+
public function get_severity() {
|
38 |
+
return $this->notice->get_severity();
|
39 |
+
}
|
40 |
+
|
41 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
42 |
+
$storage = $this->get_storage();
|
43 |
+
|
44 |
+
if ( isset( $storage[ $this->get_id() ] ) && $storage[ $this->get_id() ] + $this->ttl > ITSEC_Core::get_current_time_gmt() ) {
|
45 |
+
return false;
|
46 |
+
}
|
47 |
+
|
48 |
+
return $this->notice->show_for_context( $context );
|
49 |
+
}
|
50 |
+
|
51 |
+
public function get_actions() {
|
52 |
+
return array_merge( $this->notice->get_actions(), array(
|
53 |
+
'remind_me' => new ITSEC_Admin_Notice_Action_Callback(
|
54 |
+
ITSEC_Admin_Notice_Action::S_CLOSE,
|
55 |
+
esc_html__( 'Remind Me Later', 'better-wp-security' ),
|
56 |
+
array( $this, '_handle_remind_me' )
|
57 |
+
)
|
58 |
+
) );
|
59 |
+
}
|
60 |
+
|
61 |
+
public function _handle_remind_me() {
|
62 |
+
$storage = $this->get_storage();
|
63 |
+
|
64 |
+
$storage[ $this->get_id() ] = ITSEC_Core::get_current_time_gmt();
|
65 |
+
$this->save_storage( $storage );
|
66 |
+
|
67 |
+
return null;
|
68 |
+
}
|
69 |
+
|
70 |
+
private function get_storage() {
|
71 |
+
$dismissed = get_site_option( 'itsec_remind_me_notices', array() );
|
72 |
+
|
73 |
+
if ( ! is_array( $dismissed ) ) {
|
74 |
+
$dismissed = array();
|
75 |
+
}
|
76 |
+
|
77 |
+
return $dismissed;
|
78 |
+
}
|
79 |
+
|
80 |
+
private function save_storage( $storage ) {
|
81 |
+
update_site_option( 'itsec_remind_me_notices', $storage );
|
82 |
+
}
|
83 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-screen-blacklist.php
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Screen_Blacklist implements ITSEC_Admin_Notice {
|
4 |
+
|
5 |
+
/** @var ITSEC_Admin_Notice */
|
6 |
+
private $notice;
|
7 |
+
|
8 |
+
/** @var string[] */
|
9 |
+
private $blacklist;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* ITSEC_Admin_Notice_Screen_Blacklist constructor.
|
13 |
+
*
|
14 |
+
* @param ITSEC_Admin_Notice $notice
|
15 |
+
* @param string[] $blacklist Screen IDs to blacklist.
|
16 |
+
*/
|
17 |
+
public function __construct( ITSEC_Admin_Notice $notice, array $blacklist ) {
|
18 |
+
$this->notice = $notice;
|
19 |
+
$this->blacklist = $blacklist;
|
20 |
+
}
|
21 |
+
|
22 |
+
public function get_id() {
|
23 |
+
return $this->notice->get_id();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function get_title() {
|
27 |
+
return $this->notice->get_title();
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_message() {
|
31 |
+
return $this->notice->get_message();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function get_severity() {
|
35 |
+
return $this->notice->get_severity();
|
36 |
+
}
|
37 |
+
|
38 |
+
public function get_meta() {
|
39 |
+
return $this->notice->get_meta();
|
40 |
+
}
|
41 |
+
|
42 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
43 |
+
if ( in_array( $context->get_screen_id(), $this->blacklist, true ) ) {
|
44 |
+
return false;
|
45 |
+
}
|
46 |
+
|
47 |
+
return $this->notice->show_for_context( $context );
|
48 |
+
}
|
49 |
+
|
50 |
+
public function get_actions() {
|
51 |
+
return $this->notice->get_actions();
|
52 |
+
}
|
53 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-static.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Static implements ITSEC_Admin_Notice {
|
4 |
+
|
5 |
+
private $id;
|
6 |
+
private $message;
|
7 |
+
private $severity;
|
8 |
+
private $title;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* ITSEC_Admin_Notice_Concrete constructor.
|
12 |
+
*
|
13 |
+
* @param string $id Globally unique notice ID.
|
14 |
+
* @param string $message The message to display to the user.
|
15 |
+
* @param string $title
|
16 |
+
* @param string $severity The message's severity.
|
17 |
+
*/
|
18 |
+
public function __construct( $id, $message, $title = '', $severity = self::S_INFO ) {
|
19 |
+
$this->id = $id;
|
20 |
+
$this->message = $message;
|
21 |
+
$this->severity = $severity;
|
22 |
+
$this->title = $title;
|
23 |
+
}
|
24 |
+
|
25 |
+
public function get_id() {
|
26 |
+
return $this->id;
|
27 |
+
}
|
28 |
+
|
29 |
+
public function get_title() {
|
30 |
+
return $this->title;
|
31 |
+
}
|
32 |
+
|
33 |
+
public function get_message() {
|
34 |
+
return $this->message;
|
35 |
+
}
|
36 |
+
|
37 |
+
public function get_severity() {
|
38 |
+
return $this->severity;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function get_meta() {
|
42 |
+
return array();
|
43 |
+
}
|
44 |
+
|
45 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
46 |
+
return true;
|
47 |
+
}
|
48 |
+
|
49 |
+
public function get_actions() {
|
50 |
+
return array();
|
51 |
+
}
|
52 |
+
}
|
core/lib/admin-notices/class-itsec-admin-notice-user-dismissible.php
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_User_Dismissible implements ITSEC_Admin_Notice {
|
4 |
+
/** @var ITSEC_Admin_Notice */
|
5 |
+
private $notice;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* ITSEC_Admin_Notice_Dismissible constructor.
|
9 |
+
*
|
10 |
+
* @param ITSEC_Admin_Notice $notice
|
11 |
+
*/
|
12 |
+
public function __construct( ITSEC_Admin_Notice $notice ) { $this->notice = $notice; }
|
13 |
+
|
14 |
+
public function get_id() {
|
15 |
+
return $this->notice->get_id();
|
16 |
+
}
|
17 |
+
|
18 |
+
public function get_title() {
|
19 |
+
return $this->notice->get_title();
|
20 |
+
}
|
21 |
+
|
22 |
+
public function get_message() {
|
23 |
+
return $this->notice->get_message();
|
24 |
+
}
|
25 |
+
|
26 |
+
public function get_severity() {
|
27 |
+
return $this->notice->get_severity();
|
28 |
+
}
|
29 |
+
|
30 |
+
public function get_meta() {
|
31 |
+
return $this->notice->get_meta();
|
32 |
+
}
|
33 |
+
|
34 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
35 |
+
$dismissed = $this->get_storage( $context->get_user() );
|
36 |
+
|
37 |
+
|
38 |
+
if ( in_array( $this->get_id(), $dismissed, true ) ) {
|
39 |
+
return false;
|
40 |
+
}
|
41 |
+
|
42 |
+
return $this->notice->show_for_context( $context );
|
43 |
+
}
|
44 |
+
|
45 |
+
public function get_actions() {
|
46 |
+
return array_merge( $this->notice->get_actions(), array(
|
47 |
+
'dismiss' => new ITSEC_Admin_Notice_Action_Callback(
|
48 |
+
ITSEC_Admin_Notice_Action::S_CLOSE,
|
49 |
+
esc_html__( 'Dismiss', 'better-wp-security' ),
|
50 |
+
array( $this, '_handle_dismiss' )
|
51 |
+
),
|
52 |
+
) );
|
53 |
+
}
|
54 |
+
|
55 |
+
public function _handle_dismiss( WP_User $user, array $data ) {
|
56 |
+
$dismissed = $this->get_storage( $user );
|
57 |
+
$dismissed[] = $this->get_id();
|
58 |
+
$this->save_storage( $user, $dismissed );
|
59 |
+
|
60 |
+
return true;
|
61 |
+
}
|
62 |
+
|
63 |
+
private function get_storage( WP_User $user ) {
|
64 |
+
$dismissed = get_user_meta( $user->ID, '_itsec_dismissed_notices', array() );
|
65 |
+
|
66 |
+
if ( ! is_array( $dismissed ) ) {
|
67 |
+
$dismissed = array();
|
68 |
+
}
|
69 |
+
|
70 |
+
return $dismissed;
|
71 |
+
}
|
72 |
+
|
73 |
+
private function save_storage( WP_User $user, $storage ) {
|
74 |
+
update_user_meta( $user->ID, '_itsec_dismissed_notices', $storage );
|
75 |
+
}
|
76 |
+
}
|
core/lib/admin-notices/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/lib/admin-notices/interface-itsec-admin-notice.php
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
interface ITSEC_Admin_Notice {
|
4 |
+
|
5 |
+
const S_ERROR = 'error';
|
6 |
+
const S_WARN = 'warning';
|
7 |
+
const S_INFO = 'info';
|
8 |
+
const S_SUCCESS = 'success';
|
9 |
+
|
10 |
+
const M_CREATED_AT = 'created_at';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Get a unique id for this notice.
|
14 |
+
*
|
15 |
+
* @return string|int
|
16 |
+
*/
|
17 |
+
public function get_id();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Get the title or headline for a notice.
|
21 |
+
*
|
22 |
+
* May be an empty string.
|
23 |
+
*
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public function get_title();
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Get the message to display in the notice.
|
30 |
+
*
|
31 |
+
* May contain HTML.
|
32 |
+
*
|
33 |
+
* @return string
|
34 |
+
*/
|
35 |
+
public function get_message();
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Get meta information.
|
39 |
+
*
|
40 |
+
* Expected format:
|
41 |
+
* Array of the following keyed by a unique slug.
|
42 |
+
* - string $label
|
43 |
+
* - string $value
|
44 |
+
* - string $formatted
|
45 |
+
*
|
46 |
+
* @return array[]
|
47 |
+
*/
|
48 |
+
public function get_meta();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Get the severity level of the notice.
|
52 |
+
*
|
53 |
+
* @return string
|
54 |
+
*/
|
55 |
+
public function get_severity();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Should the notice be displayed to the given user.
|
59 |
+
*
|
60 |
+
* @param ITSEC_Admin_Notice_Context $context
|
61 |
+
*
|
62 |
+
* @return bool
|
63 |
+
*/
|
64 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context );
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Get a list of all the available actions that can be taken on this notice.
|
68 |
+
*
|
69 |
+
* @return ITSEC_Admin_Notice_Action[]
|
70 |
+
*/
|
71 |
+
public function get_actions();
|
72 |
+
}
|
core/lib/class-itsec-lib-admin-notices.php
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-context.php' );
|
4 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/interface-itsec-admin-notice.php' );
|
5 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-callback.php' );
|
6 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-globally-dismissible.php' );
|
7 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-highlighted-log.php' );
|
8 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-managers-only.php' );
|
9 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-remind-me.php' );
|
10 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-screen-blacklist.php' );
|
11 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-static.php' );
|
12 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/class-itsec-admin-notice-user-dismissible.php' );
|
13 |
+
|
14 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/actions/interface-itsec-admin-notice-action.php' );
|
15 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/actions/class-itsec-admin-notice-action-link.php' );
|
16 |
+
require_once( dirname( __FILE__ ) . '/admin-notices/actions/class-itsec-admin-notice-action-callback.php' );
|
17 |
+
|
18 |
+
class ITSEC_Lib_Admin_Notices {
|
19 |
+
|
20 |
+
const META = '_itsec_dismissed_notices';
|
21 |
+
|
22 |
+
/** @var ITSEC_Admin_Notice[] */
|
23 |
+
private static $notices = array();
|
24 |
+
|
25 |
+
/** @var bool */
|
26 |
+
private static $initialized = false;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Register a notice to be displayed.
|
30 |
+
*
|
31 |
+
* @param ITSEC_Admin_Notice $notice
|
32 |
+
*/
|
33 |
+
public static function register( ITSEC_Admin_Notice $notice ) {
|
34 |
+
self::$notices[] = $notice;
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Get all notices for the given user.
|
39 |
+
*
|
40 |
+
* @param ITSEC_Admin_Notice_Context $context
|
41 |
+
*
|
42 |
+
* @return ITSEC_Admin_Notice[]
|
43 |
+
*/
|
44 |
+
public static function get_notices( ITSEC_Admin_Notice_Context $context = null ) {
|
45 |
+
self::initialize();
|
46 |
+
|
47 |
+
$context = $context ? $context : ITSEC_Admin_Notice_Context::from_global_state();
|
48 |
+
$notices = array();
|
49 |
+
|
50 |
+
foreach ( self::$notices as $notice ) {
|
51 |
+
if ( $notice->show_for_context( $context ) ) {
|
52 |
+
$notices[] = $notice;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
return $notices;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Lazily initialize the admin notices lib.
|
61 |
+
*
|
62 |
+
* Will load the notices.php file for all active modules, and create notices
|
63 |
+
* for each highlighted log item.
|
64 |
+
*/
|
65 |
+
private static function initialize() {
|
66 |
+
if ( ! self::$initialized ) {
|
67 |
+
ITSEC_Lib::load( 'highlighted-logs' );
|
68 |
+
|
69 |
+
foreach ( ITSEC_Lib_Highlighted_Logs::get_highlights() as $id => $highlight ) {
|
70 |
+
self::register(
|
71 |
+
new ITSEC_Admin_Notice_Managers_Only(
|
72 |
+
new ITSEC_Admin_Notice_Screen_Blacklist(
|
73 |
+
new ITSEC_Admin_Notice_Highlighted_Log( $id, $highlight ),
|
74 |
+
array( 'security_page_itsec-logs' )
|
75 |
+
)
|
76 |
+
)
|
77 |
+
);
|
78 |
+
}
|
79 |
+
|
80 |
+
ITSEC_Modules::load_module_file( 'notices.php', ':active' );
|
81 |
+
|
82 |
+
self::$initialized = true;
|
83 |
+
}
|
84 |
+
}
|
85 |
+
}
|
core/lib/class-itsec-lib-feature-flags.php
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Lib_Feature_Flags {
|
4 |
+
|
5 |
+
/** @var bool */
|
6 |
+
private static $loaded = false;
|
7 |
+
|
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 |
+
/**
|
27 |
+
* Get a list of all the available feature flags.
|
28 |
+
*
|
29 |
+
* @return array
|
30 |
+
*/
|
31 |
+
public static function get_available_flags() {
|
32 |
+
self::load();
|
33 |
+
|
34 |
+
$flags = array();
|
35 |
+
|
36 |
+
foreach ( self::$flags as $flag => $_ ) {
|
37 |
+
$flags[ $flag ] = self::get_flag_config( $flag );
|
38 |
+
}
|
39 |
+
|
40 |
+
return $flags;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Get a list of all the enabled feature flags.
|
45 |
+
*
|
46 |
+
* @return string[]
|
47 |
+
*/
|
48 |
+
public static function get_enabled() {
|
49 |
+
$enabled = array();
|
50 |
+
|
51 |
+
foreach ( self::get_available_flags() as $flag => $_ ) {
|
52 |
+
if ( self::is_enabled( $flag ) ) {
|
53 |
+
$enabled[] = $flag;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
return $enabled;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Check if a flag is enabled.
|
62 |
+
*
|
63 |
+
* @param string $flag
|
64 |
+
*
|
65 |
+
* @return bool
|
66 |
+
*/
|
67 |
+
public static function is_enabled( $flag ) {
|
68 |
+
if ( ! $config = self::get_flag_config( $flag ) ) {
|
69 |
+
return false;
|
70 |
+
}
|
71 |
+
|
72 |
+
if ( defined( 'ITSEC_FF_' . $flag ) ) {
|
73 |
+
// A constant overrules everything.
|
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'] ) ) {
|
80 |
+
// If the flag is set as enabled, then enable it.
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
|
84 |
+
// If this is a gradual roll-out.
|
85 |
+
if ( $rate = $config['rate'] ) {
|
86 |
+
// If the flag has been manually disabled with `ITSEC_Lib_Feature_Flags::disable()`, then exclude them from the feature.
|
87 |
+
if ( isset( $flags[ $flag ]['enabled'] ) && ! $flags[ $flag ]['enabled'] && ! isset( $flags[ $flag ]['rate'] ) ) {
|
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(
|
96 |
+
'enabled' => $enabled,
|
97 |
+
'time' => ITSEC_Core::get_current_time_gmt(),
|
98 |
+
'rate' => $rate,
|
99 |
+
);
|
100 |
+
|
101 |
+
ITSEC_Modules::set_setting( 'global', 'feature_flags', $flags );
|
102 |
+
|
103 |
+
if ( $enabled ) {
|
104 |
+
return true;
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
return false;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Manually enable a feature flag.
|
114 |
+
*
|
115 |
+
* @param string $flag
|
116 |
+
*/
|
117 |
+
public static function enable( $flag ) {
|
118 |
+
$flags = ITSEC_Modules::get_setting( 'global', 'feature_flags' );
|
119 |
+
|
120 |
+
$flags[ $flag ] = array(
|
121 |
+
'enabled' => true,
|
122 |
+
'time' => ITSEC_Core::get_current_time_gmt(),
|
123 |
+
);
|
124 |
+
|
125 |
+
ITSEC_Modules::set_setting( 'global', 'feature_flags', $flags );
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Manually disable a feature flag.
|
130 |
+
*
|
131 |
+
* @param string $flag
|
132 |
+
*/
|
133 |
+
public static function disable( $flag ) {
|
134 |
+
$flags = ITSEC_Modules::get_setting( 'global', 'feature_flags' );
|
135 |
+
|
136 |
+
$flags[ $flag ] = array(
|
137 |
+
'enabled' => false,
|
138 |
+
'time' => ITSEC_Core::get_current_time_gmt(),
|
139 |
+
);
|
140 |
+
|
141 |
+
ITSEC_Modules::set_setting( 'global', 'feature_flags', $flags );
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Get the flag configuration.
|
146 |
+
*
|
147 |
+
* @param string $flag
|
148 |
+
*
|
149 |
+
* @return array|null
|
150 |
+
*/
|
151 |
+
public static function get_flag_config( $flag ) {
|
152 |
+
self::load();
|
153 |
+
|
154 |
+
|
155 |
+
if ( ! isset( self::$flags[ $flag ] ) ) {
|
156 |
+
return null;
|
157 |
+
}
|
158 |
+
|
159 |
+
$config = self::$flags[ $flag ];
|
160 |
+
|
161 |
+
if ( $config['remote'] && $remote = ITSEC_Lib_Remote_Messages::get_feature( $flag ) ) {
|
162 |
+
$config = array_merge( $config, $remote );
|
163 |
+
}
|
164 |
+
|
165 |
+
return $config;
|
166 |
+
}
|
167 |
+
|
168 |
+
private static function load() {
|
169 |
+
if ( ! self::$loaded ) {
|
170 |
+
ITSEC_Modules::load_module_file( 'feature-flags.php', ':active' );
|
171 |
+
self::$loaded = true;
|
172 |
+
}
|
173 |
+
}
|
174 |
+
}
|
core/lib/class-itsec-lib-highlighted-logs.php
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Lib_Highlighted_Logs {
|
4 |
+
|
5 |
+
const OPTION = 'itsec_highlighted_logs';
|
6 |
+
|
7 |
+
/** @var array */
|
8 |
+
private static $dynamics = array();
|
9 |
+
|
10 |
+
/** @var bool */
|
11 |
+
private static $initialized = false;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Is the highlighted logs library supported.
|
15 |
+
*
|
16 |
+
* This can only work when log items are stored in the database.
|
17 |
+
*
|
18 |
+
* @return bool
|
19 |
+
*/
|
20 |
+
public static function is_supported() {
|
21 |
+
return 'file' !== ITSEC_Modules::get_setting( 'global', 'log_type' );
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Register a dynamic highlight.
|
26 |
+
*
|
27 |
+
* Dynamic highlights display the latest item that matches the given query.
|
28 |
+
* Only log items that are more recent since the dynamic highlight has been
|
29 |
+
* dismissed are included.
|
30 |
+
*
|
31 |
+
* @param string $slug Unique identifier for this highlight.
|
32 |
+
* @param array $query Filters list.
|
33 |
+
*/
|
34 |
+
public static function register_dynamic_highlight( $slug, array $query ) {
|
35 |
+
self::$dynamics[ $slug ] = $query;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Highlight an individual log item.
|
40 |
+
*
|
41 |
+
* @param int $id The log item id.
|
42 |
+
*
|
43 |
+
* @return bool
|
44 |
+
*/
|
45 |
+
public static function highlight( $id ) {
|
46 |
+
$storage = self::get_storage();
|
47 |
+
|
48 |
+
$storage['highlighted'][ $id ] = true;
|
49 |
+
|
50 |
+
return self::save_storage( $storage );
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Get a list of all the log items that are highlighted.
|
55 |
+
*
|
56 |
+
* The full log data entry is not available, just the summary columns
|
57 |
+
*
|
58 |
+
* @return array[]
|
59 |
+
*/
|
60 |
+
public static function get_highlights() {
|
61 |
+
$storage = self::get_storage();
|
62 |
+
|
63 |
+
$items = array();
|
64 |
+
|
65 |
+
foreach ( self::get_dynamics() as $slug => $highlight ) {
|
66 |
+
if ( isset( $storage['muted'][ $slug ] ) ) {
|
67 |
+
continue;
|
68 |
+
}
|
69 |
+
|
70 |
+
$filters = $highlight;
|
71 |
+
|
72 |
+
if ( isset( $storage['markers'][ $slug ] ) ) {
|
73 |
+
$filters['__min_timestamp'] = $storage['markers'][ $slug ];
|
74 |
+
}
|
75 |
+
|
76 |
+
if ( $entries = ITSEC_Log::get_entries( $filters, 1, 1, 'id', 'DESC', 'all' ) ) {
|
77 |
+
$items[ $slug ] = $entries[0];
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
if ( $storage['highlighted'] ) {
|
82 |
+
$entries = ITSEC_Log::get_entries( array( 'id' => array_flip( $storage['highlighted'] ) ) );
|
83 |
+
|
84 |
+
$items = array_merge( $items, $entries );
|
85 |
+
}
|
86 |
+
|
87 |
+
return $items;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Dismiss a highlighted log item.
|
92 |
+
*
|
93 |
+
* @param int|string $id_or_slug Either the log id or the dynamic highlight slug.
|
94 |
+
*
|
95 |
+
* @return bool
|
96 |
+
*/
|
97 |
+
public static function dismiss_highlight( $id_or_slug ) {
|
98 |
+
$storage = self::get_storage();
|
99 |
+
|
100 |
+
if ( is_int( $id_or_slug ) ) {
|
101 |
+
if ( isset( $storage['highlighted'][ $id_or_slug ] ) ) {
|
102 |
+
unset( $storage['highlighted'][ $id_or_slug ] );
|
103 |
+
|
104 |
+
return self::save_storage( $storage );
|
105 |
+
}
|
106 |
+
|
107 |
+
return true;
|
108 |
+
}
|
109 |
+
|
110 |
+
$storage['markers'][ $id_or_slug ] = ITSEC_Core::get_current_time_gmt();
|
111 |
+
|
112 |
+
return self::save_storage( $storage );
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Mute a dynamic highlighted log.
|
117 |
+
*
|
118 |
+
* Muted items won't ever be returned in {@see ITSEC_Lib_Highlighted_Logs::get_highlights()}.
|
119 |
+
*
|
120 |
+
* @param string $slug Dynamic highlight slug.
|
121 |
+
*
|
122 |
+
* @return bool
|
123 |
+
*/
|
124 |
+
public static function mute( $slug ) {
|
125 |
+
$storage = self::get_storage();
|
126 |
+
|
127 |
+
$storage['muted'][ $slug ] = ITSEC_Core::get_current_time_gmt();
|
128 |
+
|
129 |
+
return self::save_storage( $storage );
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Unmute a dynamic highlighted log.
|
134 |
+
*
|
135 |
+
* @param string $slug Dynamic highlight slug.
|
136 |
+
*
|
137 |
+
* @return bool
|
138 |
+
*/
|
139 |
+
public static function unmute( $slug ) {
|
140 |
+
$storage = self::get_storage();
|
141 |
+
|
142 |
+
unset( $storage['muted'][ $slug ] );
|
143 |
+
|
144 |
+
return self::save_storage( $storage );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Is the given dynamic highlight muted.
|
149 |
+
*
|
150 |
+
* @param string $slug
|
151 |
+
*
|
152 |
+
* @return bool
|
153 |
+
*/
|
154 |
+
public static function is_muted( $slug ) {
|
155 |
+
$storage = self::get_storage();
|
156 |
+
|
157 |
+
return ! empty( $storage['muted'][ $slug ] );
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Get a list of all the registered dynamic highlights.
|
162 |
+
*
|
163 |
+
* On first call, this will fire an action to register the highlights.
|
164 |
+
*
|
165 |
+
* @return array
|
166 |
+
*/
|
167 |
+
public static function get_dynamics() {
|
168 |
+
if ( ! self::$initialized ) {
|
169 |
+
do_action( 'itsec_register_highlighted_logs' );
|
170 |
+
|
171 |
+
self::$initialized = true;
|
172 |
+
}
|
173 |
+
|
174 |
+
return self::$dynamics;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Get the storage data.
|
179 |
+
*
|
180 |
+
* @return array
|
181 |
+
*/
|
182 |
+
private static function get_storage() {
|
183 |
+
return get_site_option( self::OPTION, array(
|
184 |
+
'highlighted' => array(),
|
185 |
+
'markers' => array(),
|
186 |
+
'muted' => array(),
|
187 |
+
) );
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Update the storage data.
|
192 |
+
*
|
193 |
+
* @param array $storage
|
194 |
+
*
|
195 |
+
* @return bool
|
196 |
+
*/
|
197 |
+
private static function save_storage( array $storage ) {
|
198 |
+
return update_site_option( self::OPTION, $storage );
|
199 |
+
}
|
200 |
+
}
|
core/lib/class-itsec-lib-remote-messages.php
CHANGED
@@ -29,6 +29,12 @@ class ITSEC_Lib_Remote_Messages {
|
|
29 |
return in_array( $action, self::get_actions(), true );
|
30 |
}
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
public static function get_raw_messages() {
|
33 |
$response = self::get_response();
|
34 |
|
@@ -88,6 +94,7 @@ class ITSEC_Lib_Remote_Messages {
|
|
88 |
'ttl' => HOUR_IN_SECONDS,
|
89 |
'messages' => array(),
|
90 |
'actions' => array(),
|
|
|
91 |
) );
|
92 |
|
93 |
$sanitized = array(
|
@@ -132,12 +139,7 @@ class ITSEC_Lib_Remote_Messages {
|
|
132 |
return self::$_response;
|
133 |
}
|
134 |
|
135 |
-
$data =
|
136 |
-
$data = wp_parse_args( $data, array(
|
137 |
-
'response' => array(),
|
138 |
-
'requested' => 0,
|
139 |
-
'ttl' => 0,
|
140 |
-
) );
|
141 |
|
142 |
if ( ! $data['response'] ) {
|
143 |
self::schedule_check();
|
@@ -150,8 +152,21 @@ class ITSEC_Lib_Remote_Messages {
|
|
150 |
$events = ITSEC_Core::get_scheduler()->get_single_events();
|
151 |
|
152 |
foreach ( $events as $event ) {
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
}
|
156 |
}
|
157 |
|
@@ -161,6 +176,17 @@ class ITSEC_Lib_Remote_Messages {
|
|
161 |
return self::$_response = $data['response'];
|
162 |
}
|
163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
private static function schedule_check() {
|
165 |
$s = ITSEC_Core::get_scheduler();
|
166 |
|
29 |
return in_array( $action, self::get_actions(), true );
|
30 |
}
|
31 |
|
32 |
+
public static function get_feature( $flag ) {
|
33 |
+
$response = self::get_response();
|
34 |
+
|
35 |
+
return isset( $response['features'][ $flag ] ) ? $response['features'][ $flag ] : null;
|
36 |
+
}
|
37 |
+
|
38 |
public static function get_raw_messages() {
|
39 |
$response = self::get_response();
|
40 |
|
94 |
'ttl' => HOUR_IN_SECONDS,
|
95 |
'messages' => array(),
|
96 |
'actions' => array(),
|
97 |
+
'features' => array(),
|
98 |
) );
|
99 |
|
100 |
$sanitized = array(
|
139 |
return self::$_response;
|
140 |
}
|
141 |
|
142 |
+
$data = self::get_stored_response();
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
if ( ! $data['response'] ) {
|
145 |
self::schedule_check();
|
152 |
$events = ITSEC_Core::get_scheduler()->get_single_events();
|
153 |
|
154 |
foreach ( $events as $event ) {
|
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.
|
162 |
+
if ( $event['fire_at'] + DAY_IN_SECONDS > ITSEC_Core::get_current_time_gmt() ) {
|
163 |
+
ITSEC_Core::get_scheduler()->run_single_event( self::EVENT );
|
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 |
|
176 |
return self::$_response = $data['response'];
|
177 |
}
|
178 |
|
179 |
+
private static function get_stored_response() {
|
180 |
+
$data = get_site_option( self::OPTION, array() );
|
181 |
+
$data = wp_parse_args( $data, array(
|
182 |
+
'response' => array(),
|
183 |
+
'requested' => 0,
|
184 |
+
'ttl' => 0,
|
185 |
+
) );
|
186 |
+
|
187 |
+
return $data;
|
188 |
+
}
|
189 |
+
|
190 |
private static function schedule_check() {
|
191 |
$s = ITSEC_Core::get_scheduler();
|
192 |
|
core/lib/class-itsec-mail.php
CHANGED
@@ -469,6 +469,14 @@ final class ITSEC_Mail {
|
|
469 |
$this->add_html( $deferred, $group );
|
470 |
}
|
471 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
472 |
/**
|
473 |
* Include debug info in the email.
|
474 |
*
|
469 |
$this->add_html( $deferred, $group );
|
470 |
}
|
471 |
|
472 |
+
public function insert_before( $identifier, $html ) {
|
473 |
+
$this->groups = ITSEC_Lib::array_insert_before( $identifier, $this->groups, count( $this->groups ), $html );
|
474 |
+
}
|
475 |
+
|
476 |
+
public function insert_after( $identifier, $html ) {
|
477 |
+
$this->groups = ITSEC_Lib::array_insert_after( $identifier, $this->groups, count( $this->groups ), $html );
|
478 |
+
}
|
479 |
+
|
480 |
/**
|
481 |
* Include debug info in the email.
|
482 |
*
|
core/lib/log.php
CHANGED
@@ -221,7 +221,7 @@ final class ITSEC_Log {
|
|
221 |
|
222 |
$entries = ITSEC_Log_Util::get_entries( array( 'id' => $id ), 0, 1, 'id', 'DESC', 'all' );
|
223 |
|
224 |
-
return $entries[0];
|
225 |
}
|
226 |
|
227 |
public static function get_number_of_entries( $filters = array() ) {
|
221 |
|
222 |
$entries = ITSEC_Log_Util::get_entries( array( 'id' => $id ), 0, 1, 'id', 'DESC', 'all' );
|
223 |
|
224 |
+
return isset( $entries[0] ) ? $entries[0] : array();
|
225 |
}
|
226 |
|
227 |
public static function get_number_of_entries( $filters = array() ) {
|
core/modules/core/active.php
CHANGED
@@ -1,5 +1,13 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
-
require_once( 'class-itsec-core-admin.php' );
|
4 |
-
|
5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
<?php
|
2 |
+
require_once( dirname( __FILE__ ) . '/class-itsec-core-active.php' );
|
3 |
+
require_once( dirname( __FILE__ ) . '/class-itsec-core-admin.php' );
|
4 |
+
require_once( dirname( __FILE__ ) . '/class-itsec-admin-notices.php' );
|
5 |
+
|
6 |
+
$active = new ITSEC_Core_Active();
|
7 |
+
$active->run();
|
8 |
+
|
9 |
+
$admin = new ITSEC_Core_Admin();
|
10 |
+
$admin->run();
|
11 |
+
|
12 |
+
$notices = new ITSEC_Admin_Notices();
|
13 |
+
$notices->run();
|
core/modules/core/class-itsec-admin-notices.php
ADDED
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notices {
|
4 |
+
const ACTION = 'itsec-admin-notice';
|
5 |
+
|
6 |
+
/** @var WP_Error[] */
|
7 |
+
private $errors = array();
|
8 |
+
|
9 |
+
public function run() {
|
10 |
+
add_action( 'rest_api_init', array( $this, 'rest_api_init' ) );
|
11 |
+
add_action( 'wp_ajax_' . self::ACTION, array( $this, 'handle_ajax' ) );
|
12 |
+
|
13 |
+
if ( isset( $_GET['action'] ) && self::ACTION === $_GET['action'] ) {
|
14 |
+
add_action( 'admin_init', array( $this, 'handle_admin_action' ) );
|
15 |
+
}
|
16 |
+
|
17 |
+
if ( ITSEC_Modules::get_setting( 'global', 'hide_admin_bar' ) || version_compare( get_bloginfo( 'version' ), '5.0', '<' ) ) {
|
18 |
+
if ( is_multisite() ) {
|
19 |
+
add_action( 'network_admin_notices', array( $this, 'display_notices' ) );
|
20 |
+
} else {
|
21 |
+
add_action( 'admin_notices', array( $this, 'display_notices' ) );
|
22 |
+
}
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
public function rest_api_init() {
|
27 |
+
require_once( dirname( __FILE__ ) . '/class-rest-core-admin-notices-controller.php' );
|
28 |
+
|
29 |
+
$controller = new ITSEC_REST_Core_Admin_Notices_Controller();
|
30 |
+
$controller->register_routes();
|
31 |
+
}
|
32 |
+
|
33 |
+
public function display_notices() {
|
34 |
+
foreach ( $this->errors as $error ) {
|
35 |
+
echo '<div class="notice notice-error is-dismissible"><p>' . $error->get_error_message() . '</p></div>';
|
36 |
+
}
|
37 |
+
|
38 |
+
$hide = array( 'dashboard_page_itsec-dashboard', 'index_page_itsec-dashboard' );
|
39 |
+
|
40 |
+
if ( in_array( get_current_screen()->id, $hide, true ) ) {
|
41 |
+
return;
|
42 |
+
}
|
43 |
+
|
44 |
+
ITSEC_Lib::load( 'admin-notices' );
|
45 |
+
$notices = ITSEC_Lib_Admin_Notices::get_notices();
|
46 |
+
|
47 |
+
if ( ! $notices ) {
|
48 |
+
return;
|
49 |
+
}
|
50 |
+
|
51 |
+
$nonce = wp_create_nonce( self::ACTION );
|
52 |
+
|
53 |
+
wp_enqueue_script( 'itsec-admin-notices', plugin_dir_url( __FILE__ ) . 'js/admin-notices.js', array( 'jquery', 'wp-util' ) );
|
54 |
+
wp_localize_script( 'itsec-admin-notices', 'ITSECAdminNotices', array(
|
55 |
+
'nonce' => $nonce,
|
56 |
+
) );
|
57 |
+
|
58 |
+
foreach ( $notices as $notice ) {
|
59 |
+
$data = 'data-id="' . esc_attr( $notice->get_id() ) . '"';
|
60 |
+
$classes = array(
|
61 |
+
'itsec-notice',
|
62 |
+
'notice',
|
63 |
+
'notice-' . esc_attr( $notice->get_severity() ),
|
64 |
+
);
|
65 |
+
|
66 |
+
foreach ( $notice->get_actions() as $slug => $action ) {
|
67 |
+
if ( ITSEC_Admin_Notice_Action::S_CLOSE === $action->get_style() ) {
|
68 |
+
$classes[] = 'is-dismissible';
|
69 |
+
$data .= ' data-close="' . esc_attr( $slug ) . '"';
|
70 |
+
break;
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
echo '<div class="' . implode( ' ', $classes ) . '"' . $data . '>';
|
75 |
+
$html = trim( $notice->get_title() . ' ' . $notice->get_message() );
|
76 |
+
|
77 |
+
foreach ( $notice->get_actions() as $slug => $action ) {
|
78 |
+
if ( ITSEC_Admin_Notice_Action::S_CLOSE === $action->get_style() ) {
|
79 |
+
continue;
|
80 |
+
}
|
81 |
+
|
82 |
+
$html .= ' ';
|
83 |
+
|
84 |
+
if ( $action->get_uri() ) {
|
85 |
+
switch ( $action->get_style() ) {
|
86 |
+
case ITSEC_Admin_Notice_Action::S_BUTTON:
|
87 |
+
$class = 'button';
|
88 |
+
break;
|
89 |
+
case ITSEC_Admin_Notice_Action::S_PRIMARY:
|
90 |
+
$class = 'button button-primary';
|
91 |
+
break;
|
92 |
+
default:
|
93 |
+
$class = '';
|
94 |
+
break;
|
95 |
+
}
|
96 |
+
|
97 |
+
$href = add_query_arg( array(
|
98 |
+
'action' => self::ACTION,
|
99 |
+
'notice_id' => $notice->get_id(),
|
100 |
+
'itsec_action' => $slug,
|
101 |
+
'nonce' => $nonce,
|
102 |
+
), $action->get_uri() );
|
103 |
+
|
104 |
+
$html .= '<a href="' . esc_url( $href ) . '" class="' . esc_attr( $class ) . '">';
|
105 |
+
$html .= $action->get_title();
|
106 |
+
$html .= '</a>';
|
107 |
+
} else {
|
108 |
+
switch ( $action->get_style() ) {
|
109 |
+
case ITSEC_Admin_Notice_Action::S_BUTTON:
|
110 |
+
$class = 'button';
|
111 |
+
break;
|
112 |
+
case ITSEC_Admin_Notice_Action::S_PRIMARY:
|
113 |
+
$class = 'button button-primary';
|
114 |
+
break;
|
115 |
+
case ITSEC_Admin_Notice_Action::S_LINK:
|
116 |
+
$class = 'button-link';
|
117 |
+
break;
|
118 |
+
default:
|
119 |
+
$class = '';
|
120 |
+
break;
|
121 |
+
}
|
122 |
+
|
123 |
+
$html .= '<button data-action="' . esc_attr( $slug ) . '" class="' . esc_attr( $class ) . '">';
|
124 |
+
$html .= $action->get_title();
|
125 |
+
$html .= '</button>';
|
126 |
+
}
|
127 |
+
}
|
128 |
+
|
129 |
+
echo wpautop( $html );
|
130 |
+
|
131 |
+
echo '</div>';
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
public function handle_ajax() {
|
136 |
+
$error = $this->handle_action( $_POST );
|
137 |
+
|
138 |
+
if ( is_wp_error( $error ) ) {
|
139 |
+
wp_send_json_error( $error );
|
140 |
+
}
|
141 |
+
|
142 |
+
wp_send_json_success();
|
143 |
+
}
|
144 |
+
|
145 |
+
public function handle_admin_action() {
|
146 |
+
$error = $this->handle_action( $_GET );
|
147 |
+
|
148 |
+
if ( is_wp_error( $error ) ) {
|
149 |
+
$this->errors[] = $error;
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
private function handle_action( $request ) {
|
154 |
+
if ( ! isset( $request['notice_id'], $request['itsec_action'], $request['nonce'] ) ) {
|
155 |
+
return new WP_Error( 'itsec-admin-notices.invalid-request-format', esc_html__( 'Invalid request format.', 'better-wp-security' ) );
|
156 |
+
}
|
157 |
+
|
158 |
+
if ( ! wp_verify_nonce( $request['nonce'], self::ACTION ) ) {
|
159 |
+
return new WP_Error( 'itsec-admin-notices.invalid-nonce', esc_html__( 'Request Expired. Please refresh and try again.', 'better-wp-security' ) );
|
160 |
+
}
|
161 |
+
|
162 |
+
ITSEC_Lib::load( 'admin-notices' );
|
163 |
+
$notices = ITSEC_Lib_Admin_Notices::get_notices( new ITSEC_Admin_Notice_Context(
|
164 |
+
wp_get_current_user(),
|
165 |
+
wp_doing_ajax() ? ITSEC_Admin_Notice_Context::AJAX : ITSEC_Admin_Notice_Context::ADMIN_ACTION
|
166 |
+
) );
|
167 |
+
|
168 |
+
$notice = null;
|
169 |
+
foreach ( $notices as $maybe_notice ) {
|
170 |
+
if ( (string) $maybe_notice->get_id() === $request['notice_id'] ) {
|
171 |
+
$notice = $maybe_notice;
|
172 |
+
break;
|
173 |
+
}
|
174 |
+
}
|
175 |
+
|
176 |
+
if ( ! $notice ) {
|
177 |
+
return new WP_Error( 'itsec-admin-notices.invalid-notice', esc_html__( 'Notice not found.', 'better-wp-security' ) );
|
178 |
+
}
|
179 |
+
|
180 |
+
$actions = $notice->get_actions();
|
181 |
+
|
182 |
+
if ( ! isset( $actions[ $request['itsec_action'] ] ) ) {
|
183 |
+
return new WP_Error( 'itsec-admin-notices.invalid-action', esc_html__( 'Action not found.', 'better-wp-security' ) );
|
184 |
+
}
|
185 |
+
|
186 |
+
$data = $request;
|
187 |
+
|
188 |
+
unset( $data['notice_id'], $data['itsec_action'], $data['nonce'], $data['action'] );
|
189 |
+
|
190 |
+
$error = $actions[ $request['itsec_action'] ]->handle( wp_get_current_user(), $data );
|
191 |
+
|
192 |
+
if ( is_wp_error( $error ) ) {
|
193 |
+
return $error;
|
194 |
+
}
|
195 |
+
|
196 |
+
return null;
|
197 |
+
}
|
198 |
+
}
|
core/modules/core/class-itsec-core-active.php
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Core_Active {
|
4 |
+
|
5 |
+
public function run() {
|
6 |
+
add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ), 0 );
|
7 |
+
add_action( 'login_enqueue_scripts', array( $this, 'register_scripts' ), 0 );
|
8 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ), 0 );
|
9 |
+
}
|
10 |
+
|
11 |
+
public function register_scripts() {
|
12 |
+
$dir = ITSEC_Core::get_plugin_dir() . 'dist/';
|
13 |
+
$script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
|
14 |
+
|
15 |
+
if ( $script_debug && file_exists( $dir . 'manifest-dev.php' ) ) {
|
16 |
+
$manifest = require $dir . 'manifest-dev.php';
|
17 |
+
} else {
|
18 |
+
$manifest = require $dir . 'manifest.php';
|
19 |
+
}
|
20 |
+
|
21 |
+
foreach ( $manifest as $name => $config ) {
|
22 |
+
foreach ( $config['files'] as $file ) {
|
23 |
+
$handle = $this->name_to_handle( $name );
|
24 |
+
|
25 |
+
if ( $script_debug && file_exists( $dir . $file ) ) {
|
26 |
+
$path = 'dist/' . $file;
|
27 |
+
$is_debug = true;
|
28 |
+
} else {
|
29 |
+
$path = 'dist/' . str_replace( '.', '.min.', $file );
|
30 |
+
$is_debug = false;
|
31 |
+
}
|
32 |
+
|
33 |
+
$is_css = ITSEC_Lib::str_ends_with( $file, '.css' );
|
34 |
+
$is_js = ! $is_css;
|
35 |
+
|
36 |
+
if ( $is_debug ) {
|
37 |
+
$version = filemtime( $dir . $file );
|
38 |
+
} elseif ( $is_js && isset( $config['contentHash']['javascript'] ) ) {
|
39 |
+
$version = $config['contentHash']['javascript'];
|
40 |
+
} elseif ( $is_css && isset( $config['contentHash']['css/mini-extract'] ) ) {
|
41 |
+
$version = $config['contentHash']['css/mini-extract'];
|
42 |
+
} else {
|
43 |
+
$version = $config['hash'];
|
44 |
+
}
|
45 |
+
|
46 |
+
$deps = $is_js ? $config['dependencies'] : array();
|
47 |
+
|
48 |
+
if ( $is_css && in_array( 'wp-components', $config['dependencies'], true ) ) {
|
49 |
+
$deps[] = 'wp-components';
|
50 |
+
}
|
51 |
+
|
52 |
+
foreach ( $deps as $i => $dep ) {
|
53 |
+
if ( ! ITSEC_Lib::str_starts_with( $dep, '@ithemes/security.' ) ) {
|
54 |
+
continue;
|
55 |
+
}
|
56 |
+
|
57 |
+
$parts = explode( '.', $dep );
|
58 |
+
|
59 |
+
$deps[ $i ] = $this->name_to_handle( "{$parts[1]}/{$parts[2]}" );
|
60 |
+
}
|
61 |
+
|
62 |
+
if ( ! $is_debug ) {
|
63 |
+
foreach ( array_reverse( $config['vendors'] ) as $vendor ) {
|
64 |
+
if ( ! isset( $manifest[ $vendor ] ) ) {
|
65 |
+
continue;
|
66 |
+
}
|
67 |
+
|
68 |
+
if ( $is_js && $this->has_js( $manifest[ $vendor ]['files'] ) ) {
|
69 |
+
$deps[] = $this->name_to_handle( $vendor );
|
70 |
+
} elseif ( $is_css && $this->has_css( $manifest[ $vendor ]['files'] ) ) {
|
71 |
+
$deps[] = $this->name_to_handle( $vendor );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
if ( $is_css ) {
|
77 |
+
wp_register_style(
|
78 |
+
$handle,
|
79 |
+
plugins_url( $path, ITSEC_Core::get_plugin_file() ),
|
80 |
+
$deps,
|
81 |
+
$version
|
82 |
+
);
|
83 |
+
} else {
|
84 |
+
wp_register_script(
|
85 |
+
$handle,
|
86 |
+
plugins_url( $path, ITSEC_Core::get_plugin_file() ),
|
87 |
+
$deps,
|
88 |
+
$version
|
89 |
+
);
|
90 |
+
}
|
91 |
+
|
92 |
+
if ( function_exists( 'wp_set_script_translations' ) && ! ITSEC_Core::is_pro() && in_array( 'wp-i18n', $deps, true ) ) {
|
93 |
+
wp_set_script_translations( $handle, 'better-wp-security' );
|
94 |
+
}
|
95 |
+
|
96 |
+
if ( $is_js && ! empty( $config['runtime'] ) ) {
|
97 |
+
$public_path = esc_js( trailingslashit( plugins_url( 'dist', ITSEC_Core::get_plugin_file() ) ) );
|
98 |
+
wp_add_inline_script( $handle, "window.itsecWebpackPublicPath = window.itsecWebpackPublicPath || '{$public_path}';", 'before' );
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
private function has_js( $files ) {
|
105 |
+
foreach ( $files as $file ) {
|
106 |
+
if ( ITSEC_Lib::str_ends_with( $file, '.js' ) ) {
|
107 |
+
return true;
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
|
114 |
+
private function has_css( $files ) {
|
115 |
+
foreach ( $files as $file ) {
|
116 |
+
if ( ITSEC_Lib::str_ends_with( $file, '.css' ) ) {
|
117 |
+
return true;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
return false;
|
122 |
+
}
|
123 |
+
|
124 |
+
private function name_to_handle( $name ) {
|
125 |
+
$name = str_replace( '/dist/', '/entry/', $name );
|
126 |
+
|
127 |
+
return 'itsec-' . str_replace( '/', '-', $name );
|
128 |
+
}
|
129 |
+
}
|
core/modules/core/class-itsec-core-admin.php
CHANGED
@@ -2,17 +2,67 @@
|
|
2 |
|
3 |
class ITSEC_Core_Admin {
|
4 |
|
5 |
-
function run() {
|
6 |
-
|
|
|
|
|
|
|
|
|
7 |
|
8 |
add_action( 'itsec-settings-page-init', array( $this, 'init_settings_page' ) );
|
9 |
add_action( 'itsec-logs-page-init', array( $this, 'init_settings_page' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
}
|
11 |
|
12 |
public function init_settings_page() {
|
13 |
if ( ! class_exists( 'backupbuddy_api' ) ) {
|
14 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-backupbuddy-cross-promo.php' );
|
15 |
}
|
|
|
|
|
|
|
|
|
|
|
16 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-pro-upsell.php' );
|
17 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-sync-cross-promo.php' );
|
18 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-mail-list-signup.php' );
|
2 |
|
3 |
class ITSEC_Core_Admin {
|
4 |
|
5 |
+
public function run() {
|
6 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_notices' ) );
|
7 |
+
add_action( 'itsec_dashboard_enqueue_scripts', array( $this, 'enqueue_dashboard_notices_integration' ) );
|
8 |
+
|
9 |
+
add_action( 'admin_bar_menu', array( $this, 'admin_bar' ), 9999 );
|
10 |
+
add_action( 'admin_footer', array( $this, 'render_notices_root' ) );
|
11 |
|
12 |
add_action( 'itsec-settings-page-init', array( $this, 'init_settings_page' ) );
|
13 |
add_action( 'itsec-logs-page-init', array( $this, 'init_settings_page' ) );
|
14 |
+
|
15 |
+
if ( ! ITSEC_Core::is_pro() ) {
|
16 |
+
add_filter( 'itsec_meta_links', array( $this, 'add_plugin_meta_links' ) );
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
public function enqueue_admin_notices() {
|
21 |
+
if ( $this->should_render_admin_notices() ) {
|
22 |
+
wp_enqueue_script( 'itsec-core-admin-notices' );
|
23 |
+
wp_enqueue_style( 'itsec-core-admin-notices' );
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
public function enqueue_dashboard_notices_integration() {
|
28 |
+
wp_enqueue_script( 'itsec-core-admin-notices-dashboard-admin-bar' );
|
29 |
+
wp_enqueue_style( 'itsec-core-admin-notices-dashboard-admin-bar' );
|
30 |
+
wp_enqueue_style( 'itsec-core-admin-notices' );
|
31 |
+
}
|
32 |
+
|
33 |
+
public function render_notices_root() {
|
34 |
+
if ( $this->should_render_admin_notices() ) {
|
35 |
+
echo '<div id="itsec-admin-notices-root"></div>';
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Register the admin bar menu.
|
41 |
+
*
|
42 |
+
* @param WP_Admin_Bar $wp_admin_bar
|
43 |
+
*/
|
44 |
+
public function admin_bar( $wp_admin_bar ) {
|
45 |
+
if ( is_admin() && $this->should_render_admin_notices() ) {
|
46 |
+
$wp_admin_bar->add_node( array(
|
47 |
+
'title' => __( 'Security', 'better-wp-security' ),
|
48 |
+
'id' => 'itsec_admin_bar_menu',
|
49 |
+
) );
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
private function should_render_admin_notices() {
|
54 |
+
return ITSEC_Core::current_user_can_manage() && ! ITSEC_Modules::get_setting( 'global', 'hide_admin_bar' );
|
55 |
}
|
56 |
|
57 |
public function init_settings_page() {
|
58 |
if ( ! class_exists( 'backupbuddy_api' ) ) {
|
59 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-backupbuddy-cross-promo.php' );
|
60 |
}
|
61 |
+
|
62 |
+
if ( ITSEC_Core::is_pro() ) {
|
63 |
+
return;
|
64 |
+
}
|
65 |
+
|
66 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-pro-upsell.php' );
|
67 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-sync-cross-promo.php' );
|
68 |
require_once( dirname( __FILE__ ) . '/sidebar-widget-mail-list-signup.php' );
|
core/modules/core/class-rest-core-admin-notices-controller.php
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_REST_Core_Admin_Notices_Controller extends WP_REST_Controller {
|
4 |
+
|
5 |
+
public function register_routes() {
|
6 |
+
register_rest_route( 'ithemes-security/v1', 'admin-notices', array(
|
7 |
+
'methods' => WP_REST_Server::READABLE,
|
8 |
+
'callback' => array( $this, 'get_items' ),
|
9 |
+
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
10 |
+
) );
|
11 |
+
|
12 |
+
register_rest_route( 'ithemes-security/v1', 'admin-notices/(?P<notice>[\w\-]+)/(?P<action>[\w\-]+)', array(
|
13 |
+
'methods' => WP_REST_Server::EDITABLE,
|
14 |
+
'callback' => array( $this, 'update_item' ),
|
15 |
+
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
16 |
+
) );
|
17 |
+
|
18 |
+
register_rest_route( 'ithemes-security/v1', 'admin-notices/settings', array(
|
19 |
+
array(
|
20 |
+
'methods' => WP_REST_Server::READABLE,
|
21 |
+
'callback' => array( $this, 'get_settings' ),
|
22 |
+
'permission_callback' => array( 'ITSEC_Core', 'current_user_can_manage' ),
|
23 |
+
),
|
24 |
+
array(
|
25 |
+
'methods' => WP_REST_Server::EDITABLE,
|
26 |
+
'callback' => array( $this, 'update_settings' ),
|
27 |
+
'permission_callback' => array( 'ITSEC_Core', 'current_user_can_manage' ),
|
28 |
+
),
|
29 |
+
'schema' => array( $this, 'get_settings_schema' ),
|
30 |
+
) );
|
31 |
+
}
|
32 |
+
|
33 |
+
public function get_items_permissions_check( $request ) {
|
34 |
+
if ( ! is_user_logged_in() ) {
|
35 |
+
return new WP_Error( 'not_logged_in', esc_html__( 'You must be logged-in to view notices.', 'better-wp-security' ), array(
|
36 |
+
'status' => WP_Http::UNAUTHORIZED,
|
37 |
+
) );
|
38 |
+
}
|
39 |
+
|
40 |
+
return true;
|
41 |
+
}
|
42 |
+
|
43 |
+
public function get_items( $request ) {
|
44 |
+
ITSEC_Lib::load( 'admin-notices' );
|
45 |
+
|
46 |
+
$prepared = array();
|
47 |
+
$context = new ITSEC_Admin_Notice_Context( wp_get_current_user(), 'rest' );
|
48 |
+
|
49 |
+
foreach ( ITSEC_Lib_Admin_Notices::get_notices( $context ) as $notice ) {
|
50 |
+
$prepared[] = $this->prepare_item_for_response( $notice, $request );
|
51 |
+
}
|
52 |
+
|
53 |
+
return $prepared;
|
54 |
+
}
|
55 |
+
|
56 |
+
public function prepare_item_for_response( $item, $request ) {
|
57 |
+
if ( ! $item instanceof ITSEC_Admin_Notice ) {
|
58 |
+
return array();
|
59 |
+
}
|
60 |
+
|
61 |
+
$notice = array(
|
62 |
+
'id' => $item->get_id(),
|
63 |
+
'title' => $item->get_title(),
|
64 |
+
'message' => $item->get_message(),
|
65 |
+
'severity' => $item->get_severity(),
|
66 |
+
'meta' => $item->get_meta(),
|
67 |
+
'actions' => array(),
|
68 |
+
);
|
69 |
+
|
70 |
+
foreach ( $item->get_actions() as $slug => $action ) {
|
71 |
+
if ( $uri = $action->get_uri() ) {
|
72 |
+
$uri = add_query_arg( array(
|
73 |
+
'action' => ITSEC_Admin_Notices::ACTION,
|
74 |
+
'notice_id' => $item->get_id(),
|
75 |
+
'itsec_action' => $slug,
|
76 |
+
'nonce' => wp_create_nonce( ITSEC_Admin_Notices::ACTION ),
|
77 |
+
), $uri );
|
78 |
+
}
|
79 |
+
|
80 |
+
$notice['actions'][ $slug ] = array(
|
81 |
+
'title' => $action->get_title(),
|
82 |
+
'style' => $action->get_style(),
|
83 |
+
'uri' => $uri,
|
84 |
+
);
|
85 |
+
}
|
86 |
+
|
87 |
+
return $notice;
|
88 |
+
}
|
89 |
+
|
90 |
+
public function update_item( $request ) {
|
91 |
+
|
92 |
+
$notice = $this->get_notice_for_request( $request );
|
93 |
+
|
94 |
+
if ( ! $notice ) {
|
95 |
+
return new WP_REST_Response( null, 500 );
|
96 |
+
}
|
97 |
+
|
98 |
+
$actions = $notice->get_actions();
|
99 |
+
$action = $actions[ $request['action'] ];
|
100 |
+
|
101 |
+
$error = $action->handle( wp_get_current_user(), $request->get_json_params() ? $request->get_json_params() : $request->get_body_params() );
|
102 |
+
|
103 |
+
if ( is_wp_error( $error ) ) {
|
104 |
+
return $error;
|
105 |
+
}
|
106 |
+
|
107 |
+
return null;
|
108 |
+
}
|
109 |
+
|
110 |
+
public function update_item_permissions_check( $request ) {
|
111 |
+
if ( ! is_user_logged_in() ) {
|
112 |
+
return new WP_Error( 'not_logged_in', esc_html__( 'You must be logged-in to view notices.', 'better-wp-security' ) );
|
113 |
+
}
|
114 |
+
|
115 |
+
if ( ! $notice = $this->get_notice_for_request( $request ) ) {
|
116 |
+
return new WP_Error( 'notice_not_found', esc_html__( 'Notice not found.', 'better-wp-security' ), array(
|
117 |
+
'status' => WP_Http::NOT_FOUND,
|
118 |
+
) );
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( ! array_key_exists( $request['action'], $notice->get_actions() ) ) {
|
122 |
+
return new WP_Error( 'action_not_found', esc_html__( 'Action not found.', 'better-wp-security' ), array(
|
123 |
+
'status' => WP_Http::NOT_FOUND,
|
124 |
+
) );
|
125 |
+
}
|
126 |
+
|
127 |
+
return true;
|
128 |
+
}
|
129 |
+
|
130 |
+
private function get_notice_for_request( WP_REST_Request $request ) {
|
131 |
+
ITSEC_Lib::load( 'admin-notices' );
|
132 |
+
|
133 |
+
$context = new ITSEC_Admin_Notice_Context( wp_get_current_user(), 'rest' );
|
134 |
+
|
135 |
+
foreach ( ITSEC_Lib_Admin_Notices::get_notices( $context ) as $notice ) {
|
136 |
+
if ( $notice->get_id() === $request['notice'] ) {
|
137 |
+
return $notice;
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
return null;
|
142 |
+
}
|
143 |
+
|
144 |
+
public function get_settings( WP_REST_Request $request ) {
|
145 |
+
ITSEC_Lib::load( 'highlighted-logs' );
|
146 |
+
|
147 |
+
$settings = array(
|
148 |
+
'muted_highlights' => array(),
|
149 |
+
);
|
150 |
+
|
151 |
+
foreach ( ITSEC_Lib_Highlighted_Logs::get_dynamics() as $slug => $highlight ) {
|
152 |
+
$settings['muted_highlights'][ $slug ] = ITSEC_Lib_Highlighted_Logs::is_muted( $slug );
|
153 |
+
}
|
154 |
+
|
155 |
+
return new WP_REST_Response( $settings );
|
156 |
+
}
|
157 |
+
|
158 |
+
public function update_settings( WP_REST_Request $request ) {
|
159 |
+
ITSEC_Lib::load( 'highlighted-logs' );
|
160 |
+
|
161 |
+
if ( $highlights = $request['muted_highlights'] ) {
|
162 |
+
foreach ( $highlights as $highlight => $muted ) {
|
163 |
+
if ( $muted === ITSEC_Lib_Highlighted_Logs::is_muted( $highlight ) ) {
|
164 |
+
continue;
|
165 |
+
}
|
166 |
+
|
167 |
+
if ( $muted ) {
|
168 |
+
ITSEC_Lib_Highlighted_Logs::mute( $highlight );
|
169 |
+
} else {
|
170 |
+
ITSEC_Lib_Highlighted_Logs::unmute( $highlight );
|
171 |
+
}
|
172 |
+
}
|
173 |
+
}
|
174 |
+
|
175 |
+
return $this->get_settings( $request );
|
176 |
+
}
|
177 |
+
|
178 |
+
public function get_settings_schema() {
|
179 |
+
ITSEC_Lib::load( 'highlighted-logs' );
|
180 |
+
|
181 |
+
$muted_highlights = array();
|
182 |
+
|
183 |
+
foreach ( ITSEC_Lib_Highlighted_Logs::get_dynamics() as $slug => $query ) {
|
184 |
+
$muted_highlights[ $slug ] = array(
|
185 |
+
'type' => 'boolean',
|
186 |
+
);
|
187 |
+
}
|
188 |
+
|
189 |
+
return array(
|
190 |
+
'title' => 'itsec-admin-notices-settings',
|
191 |
+
'type' => 'object',
|
192 |
+
'properties' => array(
|
193 |
+
'muted_highlights' => array(
|
194 |
+
'type' => 'object',
|
195 |
+
'properties' => $muted_highlights,
|
196 |
+
'additionalProperties' => false,
|
197 |
+
)
|
198 |
+
)
|
199 |
+
);
|
200 |
+
}
|
201 |
+
}
|
core/modules/core/entries/admin-notices-api.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
import './admin-notices/store';
|
core/modules/core/entries/admin-notices-dashboard-admin-bar.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { registerPlugin } from '@wordpress/plugins';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies
|
8 |
+
*/
|
9 |
+
import '@ithemes/security.core.admin-notices-api';
|
10 |
+
import AdminBar from './admin-notices/components/admin-bar';
|
11 |
+
registerPlugin( 'itsec-admin-notices-dashboard-admin-bar', {
|
12 |
+
render: AdminBar,
|
13 |
+
} );
|
core/modules/core/entries/admin-notices.js
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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( { '': {} }, 'better-wp-security' );
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Internal dependencies
|
13 |
+
*/
|
14 |
+
import App from './admin-notices/app.js';
|
15 |
+
|
16 |
+
domReady( () => {
|
17 |
+
const containerEl = document.getElementById( 'wp-admin-bar-itsec_admin_bar_menu' );
|
18 |
+
const portalEl = document.getElementById( 'itsec-admin-notices-root' );
|
19 |
+
|
20 |
+
return render( <App portalEl={ portalEl } />, containerEl );
|
21 |
+
} );
|
core/modules/core/entries/admin-notices/app.js
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { createPortal } from '@wordpress/element';
|
5 |
+
import { Popover, SlotFillProvider } from '@wordpress/components';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import '@ithemes/security.core.admin-notices-api';
|
11 |
+
import Toolbar from './components/toolbar';
|
12 |
+
import './style.scss';
|
13 |
+
|
14 |
+
function App( { portalEl } ) {
|
15 |
+
return (
|
16 |
+
<SlotFillProvider>
|
17 |
+
{ createPortal( <Popover.Slot />, portalEl ) }
|
18 |
+
<Toolbar />
|
19 |
+
</SlotFillProvider>
|
20 |
+
);
|
21 |
+
}
|
22 |
+
|
23 |
+
export default App;
|
core/modules/core/entries/admin-notices/components/admin-bar/index.js
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import classnames from 'classnames';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Button, Dashicon, Popover } from '@wordpress/components';
|
10 |
+
import { __ } from '@wordpress/i18n';
|
11 |
+
import { compose, withState } from '@wordpress/compose';
|
12 |
+
import { withSelect } from '@wordpress/data';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Internal dependencies
|
16 |
+
*/
|
17 |
+
import { AdminBarFill } from '@ithemes/security.dashboard.api';
|
18 |
+
import { doesElementBelongToPanel } from '../../utils';
|
19 |
+
import Panel from '../panel';
|
20 |
+
import './style.scss';
|
21 |
+
|
22 |
+
function AdminBar( { notices, noticesLoaded, isToggled, setState } ) {
|
23 |
+
return (
|
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>
|
31 |
+
{ isToggled && (
|
32 |
+
<Popover
|
33 |
+
className="itsec-admin-bar-admin-notices__content"
|
34 |
+
expandOnMobile
|
35 |
+
focusOnMount="container"
|
36 |
+
position="bottom left"
|
37 |
+
headerTitle={ __( 'Notifications', 'better-wp-security' ) }
|
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 |
+
}
|
47 |
+
} }
|
48 |
+
>
|
49 |
+
<Panel notices={ notices } loaded={ noticesLoaded } close={ () => setState( { isToggled: false } ) } />
|
50 |
+
</Popover>
|
51 |
+
) }
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</AdminBarFill>
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
export default compose( [
|
59 |
+
withSelect( ( select ) => ( {
|
60 |
+
notices: select( 'ithemes-security/admin-notices' ).getNotices(),
|
61 |
+
noticesLoaded: select( 'ithemes-security/admin-notices' ).areNoticesLoaded(),
|
62 |
+
} ) ),
|
63 |
+
withState( { isToggled: false } ),
|
64 |
+
] )( AdminBar );
|
core/modules/core/entries/admin-notices/components/admin-bar/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/admin-bar/style.scss
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
@import "breakpoints.scss";
|
3 |
+
|
4 |
+
.itsec-admin-bar__admin-notices {
|
5 |
+
margin-left: auto;
|
6 |
+
|
7 |
+
& .itsec-admin-bar-admin-notices__trigger > .components-button {
|
8 |
+
color: $main-blue;
|
9 |
+
|
10 |
+
&:hover {
|
11 |
+
color: lighten($main-blue, 20%);
|
12 |
+
}
|
13 |
+
|
14 |
+
& svg {
|
15 |
+
margin-right: .5em
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
& .itsec-admin-bar-admin-notices__trigger .components-button {
|
20 |
+
position: relative;
|
21 |
+
}
|
22 |
+
|
23 |
+
& .itsec-admin-bar-admin-notices__trigger .components-button::before {
|
24 |
+
content: '';
|
25 |
+
background: #d8514f;
|
26 |
+
height: 5px;
|
27 |
+
width: 5px;
|
28 |
+
border-radius: 5px;
|
29 |
+
vertical-align: middle;
|
30 |
+
border: 1px solid #f1f1f1;
|
31 |
+
z-index: 1;
|
32 |
+
position: absolute;
|
33 |
+
left: 4px;
|
34 |
+
top: 3px;
|
35 |
+
opacity: 0;
|
36 |
+
transition: opacity 1000ms ease-in-out;
|
37 |
+
}
|
38 |
+
|
39 |
+
& .itsec-admin-bar-admin-notices__trigger--has-notices .components-button::before {
|
40 |
+
opacity: 1;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
.itsec-admin-bar-admin-notices__content .components-popover__content {
|
45 |
+
box-shadow: rgba(0, 0, 0, 0.19) 0 4px 5px;
|
46 |
+
}
|
47 |
+
|
48 |
+
@media screen and (max-width: $small) {
|
49 |
+
.itsec-admin-bar .itsec-admin-bar__admin-notices {
|
50 |
+
margin-left: 0;
|
51 |
+
}
|
52 |
+
}
|
core/modules/core/entries/admin-notices/components/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/notice-actions/index.js
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isEmpty, map } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Dropdown, NavigableMenu, IconButton, Button, Spinner } from '@wordpress/components';
|
10 |
+
import { __ } from '@wordpress/i18n';
|
11 |
+
import { compose } from '@wordpress/compose';
|
12 |
+
import { withDispatch, withSelect } from '@wordpress/data';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Internal dependencies
|
16 |
+
*/
|
17 |
+
import './style.scss';
|
18 |
+
|
19 |
+
function NoticeActions( { notice, doAction, inProgress } ) {
|
20 |
+
const actions = [];
|
21 |
+
|
22 |
+
for ( const slug in notice.actions ) {
|
23 |
+
if ( ! notice.actions.hasOwnProperty( slug ) ) {
|
24 |
+
continue;
|
25 |
+
}
|
26 |
+
|
27 |
+
const action = notice.actions[ slug ];
|
28 |
+
|
29 |
+
if ( action.style === 'close' ) {
|
30 |
+
actions.push( (
|
31 |
+
<li key={ slug } >
|
32 |
+
<IconButton icon="dismiss" label={ action.title } onClick={ () => doAction( notice.id, slug ) } isBusy={ inProgress.includes( slug ) } />
|
33 |
+
</li>
|
34 |
+
) );
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
const generic = getGenericActions( notice );
|
39 |
+
|
40 |
+
if ( ! isEmpty( generic ) ) {
|
41 |
+
actions.push( (
|
42 |
+
<li key="more">
|
43 |
+
<Dropdown
|
44 |
+
position="bottom right"
|
45 |
+
className="itsec-admin-notice-list-actions__more-menu"
|
46 |
+
contentClassName="itsec-admin-notice-list-actions__more-menu-items"
|
47 |
+
renderToggle={ ( { isOpen, onToggle } ) => (
|
48 |
+
<IconButton
|
49 |
+
icon="ellipsis"
|
50 |
+
label={ __( 'More Actions', 'better-wp-security' ) }
|
51 |
+
onClick={ onToggle }
|
52 |
+
aria-haspopup={ true }
|
53 |
+
aria-expanded={ isOpen }
|
54 |
+
/>
|
55 |
+
) }
|
56 |
+
renderContent={ () => (
|
57 |
+
<NavigableMenu role="menu">
|
58 |
+
{ map( generic, ( action, slug ) => (
|
59 |
+
action.uri ?
|
60 |
+
<Button key={ slug } href={ action.uri }>{ action.title }</Button> :
|
61 |
+
<Button key={ slug } onClick={ () => doAction( notice.id, slug ) } disabled={ inProgress.includes( slug ) }>
|
62 |
+
{ action.title }
|
63 |
+
{ inProgress.includes( slug ) && <Spinner /> }
|
64 |
+
</Button>
|
65 |
+
) ) }
|
66 |
+
</NavigableMenu>
|
67 |
+
) }
|
68 |
+
/>
|
69 |
+
|
70 |
+
</li>
|
71 |
+
) );
|
72 |
+
}
|
73 |
+
|
74 |
+
return <ul className="itsec-admin-notice-list-actions">{ actions }</ul>;
|
75 |
+
}
|
76 |
+
|
77 |
+
export default compose( [
|
78 |
+
withDispatch( ( dispatch ) => ( {
|
79 |
+
doAction: dispatch( 'ithemes-security/admin-notices' ).doNoticeAction,
|
80 |
+
} ) ),
|
81 |
+
withSelect( ( select, ownProps ) => ( {
|
82 |
+
inProgress: select( 'ithemes-security/admin-notices' ).getInProgressActions( ownProps.notice.id ),
|
83 |
+
} ) ),
|
84 |
+
] )( NoticeActions );
|
85 |
+
|
86 |
+
function getGenericActions( notice ) {
|
87 |
+
const generic = {};
|
88 |
+
|
89 |
+
for ( const slug in notice.actions ) {
|
90 |
+
if ( ! notice.actions.hasOwnProperty( slug ) ) {
|
91 |
+
continue;
|
92 |
+
}
|
93 |
+
|
94 |
+
const action = notice.actions[ slug ];
|
95 |
+
|
96 |
+
if ( action.style === 'close' ) {
|
97 |
+
continue;
|
98 |
+
}
|
99 |
+
|
100 |
+
if ( action.style === 'primary' ) {
|
101 |
+
continue;
|
102 |
+
}
|
103 |
+
|
104 |
+
generic[ slug ] = action;
|
105 |
+
}
|
106 |
+
|
107 |
+
return generic;
|
108 |
+
}
|
core/modules/core/entries/admin-notices/components/notice-actions/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/notice-actions/style.scss
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
|
3 |
+
.itsec-admin-notice-list-actions {
|
4 |
+
& .components-icon-button {
|
5 |
+
padding: 2px !important;
|
6 |
+
|
7 |
+
&:hover {
|
8 |
+
background-color: darken(#E5EAEE, 10) !important;
|
9 |
+
box-shadow: none !important;
|
10 |
+
}
|
11 |
+
}
|
12 |
+
}
|
13 |
+
|
14 |
+
.itsec-admin-notice-list-actions__more-menu-items .components-popover__content > div {
|
15 |
+
overflow: hidden;
|
16 |
+
padding: 7px 0;
|
17 |
+
}
|
18 |
+
|
19 |
+
.itsec-admin-notice-list-actions__more-menu-items .components-button {
|
20 |
+
width: 100%;
|
21 |
+
min-height: 38px;
|
22 |
+
box-sizing: border-box;
|
23 |
+
padding: 10px;
|
24 |
+
color: #0073aa;
|
25 |
+
justify-content: space-between;
|
26 |
+
align-items: center;
|
27 |
+
|
28 |
+
&:focus {
|
29 |
+
outline-offset: -2px;
|
30 |
+
outline: 1px dotted #555d66;
|
31 |
+
box-shadow: none;
|
32 |
+
}
|
33 |
+
|
34 |
+
&:hover {
|
35 |
+
color: #191e23;
|
36 |
+
background: #f3f4f5;
|
37 |
+
}
|
38 |
+
|
39 |
+
& .components-spinner {
|
40 |
+
margin: 0;
|
41 |
+
}
|
42 |
+
}
|
core/modules/core/entries/admin-notices/components/notice-list/index.js
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Internal dependencies
|
3 |
+
*/
|
4 |
+
import Notice from '../notice';
|
5 |
+
import NoticeActions from '../notice-actions';
|
6 |
+
import './style.scss';
|
7 |
+
|
8 |
+
function NoticeList( { notices } ) {
|
9 |
+
return (
|
10 |
+
<ul className="itsec-admin-notice-list">
|
11 |
+
{ notices.map( ( notice ) => (
|
12 |
+
<li className="itsec-admin-notice-list-item-container" key={ notice.id }>
|
13 |
+
<NoticeActions notice={ notice } />
|
14 |
+
<Notice notice={ notice } noticeId={ notice.id } />
|
15 |
+
</li>
|
16 |
+
) ) }
|
17 |
+
</ul>
|
18 |
+
);
|
19 |
+
}
|
20 |
+
|
21 |
+
export default NoticeList;
|
core/modules/core/entries/admin-notices/components/notice-list/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/notice-list/style.scss
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-admin-notice-list {
|
2 |
+
margin: 0;
|
3 |
+
}
|
4 |
+
|
5 |
+
.itsec-admin-notice-list-item-container {
|
6 |
+
display: grid;
|
7 |
+
grid-template: auto / min-content 1fr;
|
8 |
+
grid-gap: 1em;
|
9 |
+
margin-bottom: 2em;
|
10 |
+
|
11 |
+
&:last-child {
|
12 |
+
margin-bottom: 0;
|
13 |
+
}
|
14 |
+
}
|
core/modules/core/entries/admin-notices/components/notice/index.js
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isEmpty, size, map } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Fragment } from '@wordpress/element';
|
10 |
+
import { autop } from '@wordpress/autop';
|
11 |
+
import { Button } from '@wordpress/components';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Internal dependencies
|
15 |
+
*/
|
16 |
+
import './style.scss';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Notice Component.
|
20 |
+
*
|
21 |
+
* @param {string|number} noticeId
|
22 |
+
* @param {Object} notice
|
23 |
+
* @param {string} notice.severity
|
24 |
+
* @param {string} notice.title
|
25 |
+
* @param {string} notice.message
|
26 |
+
* @param {Object} notice.meta
|
27 |
+
* @param {Array.<{title: string, style: string, uri: string}>} notice.actions
|
28 |
+
* @return {Component} Notice component.
|
29 |
+
*/
|
30 |
+
export default function Notice( { notice } ) {
|
31 |
+
return (
|
32 |
+
<article className={ `itsec-admin-notice itsec-admin-notice--severity-${ notice.severity }` }>
|
33 |
+
<header className="itsec-admin-notice__header">
|
34 |
+
<div className="itsec-admin-notice__header-inset">
|
35 |
+
<h4 dangerouslySetInnerHTML={ { __html: notice.title || formatMessage( notice.message, notice ) } } />
|
36 |
+
{ map( notice.actions, ( action, slug ) => ( action.style === 'primary' && (
|
37 |
+
<Button key={ slug } href={ action.uri }>{ action.title }</Button>
|
38 |
+
) ) ) }
|
39 |
+
</div>
|
40 |
+
</header>
|
41 |
+
|
42 |
+
{ notice.title && notice.message && (
|
43 |
+
<section className="itsec-admin-notice__message" dangerouslySetInnerHTML={ { __html: autop( formatMessage( notice.message, notice ) ) } } />
|
44 |
+
) }
|
45 |
+
|
46 |
+
{ hasMeta( notice ) && (
|
47 |
+
<dl className="itsec-admin-notice__meta">
|
48 |
+
{ map( notice.meta, ( meta, key ) => (
|
49 |
+
key !== 'created_at' && (
|
50 |
+
<Fragment key={ key }>
|
51 |
+
<dt>{ meta.label }</dt>
|
52 |
+
<dd>{ meta.formatted }</dd>
|
53 |
+
</Fragment>
|
54 |
+
)
|
55 |
+
) ) }
|
56 |
+
</dl>
|
57 |
+
) }
|
58 |
+
|
59 |
+
{ notice.meta.created_at && (
|
60 |
+
<footer className="itsec-admin-notice__footer">
|
61 |
+
<time dateTime={ notice.meta.created_at.value }>
|
62 |
+
{ notice.meta.created_at.formatted }
|
63 |
+
</time>
|
64 |
+
</footer>
|
65 |
+
) }
|
66 |
+
</article>
|
67 |
+
);
|
68 |
+
}
|
69 |
+
|
70 |
+
function hasMeta( notice ) {
|
71 |
+
if ( isEmpty( notice.meta ) ) {
|
72 |
+
return false;
|
73 |
+
}
|
74 |
+
|
75 |
+
if ( size( notice.meta ) === 1 && notice.meta.hasOwnProperty( 'created_at' ) ) {
|
76 |
+
return false;
|
77 |
+
}
|
78 |
+
|
79 |
+
return true;
|
80 |
+
}
|
81 |
+
|
82 |
+
function formatMessage( message, notice ) {
|
83 |
+
for ( const action in notice.actions ) {
|
84 |
+
if ( ! notice.actions.hasOwnProperty( action ) ) {
|
85 |
+
continue;
|
86 |
+
}
|
87 |
+
|
88 |
+
if ( notice.actions[ action ].uri === '' ) {
|
89 |
+
continue;
|
90 |
+
}
|
91 |
+
|
92 |
+
message = message.replace( '{{ $' + action + ' }}', notice.actions[ action ].uri );
|
93 |
+
}
|
94 |
+
|
95 |
+
return message;
|
96 |
+
}
|
core/modules/core/entries/admin-notices/components/notice/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/notice/style.scss
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
@import "mixins.scss";
|
3 |
+
|
4 |
+
$border-radius: 3px;
|
5 |
+
$side-padding: 20px;
|
6 |
+
|
7 |
+
.itsec-admin-notice {
|
8 |
+
background: white;
|
9 |
+
border-radius: $border-radius;
|
10 |
+
box-shadow: 0 0 5px rgba(211, 211, 211, 0.35);
|
11 |
+
}
|
12 |
+
|
13 |
+
.itsec-admin-notice__header {
|
14 |
+
background: #eeecec;
|
15 |
+
padding: $side-padding / 2;
|
16 |
+
border-top-left-radius: $border-radius;
|
17 |
+
border-top-right-radius: $border-radius;
|
18 |
+
|
19 |
+
& .components-button {
|
20 |
+
margin-top: 1em;
|
21 |
+
|
22 |
+
@include bordered-button('blue');
|
23 |
+
}
|
24 |
+
|
25 |
+
.itsec-admin-notice--severity-error & {
|
26 |
+
background: $alert-red;
|
27 |
+
|
28 |
+
& .components-button {
|
29 |
+
@include bordered-button('red');
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
.itsec-admin-notice--severity-warning & {
|
34 |
+
background: $alert-orange;
|
35 |
+
|
36 |
+
& .components-button {
|
37 |
+
@include bordered-button('orange');
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
.itsec-admin-notice--severity-info & {
|
42 |
+
background: $alert-blue;
|
43 |
+
}
|
44 |
+
|
45 |
+
.itsec-admin-notice--severity-success & {
|
46 |
+
background: $alert-green;
|
47 |
+
|
48 |
+
& .components-button {
|
49 |
+
@include bordered-button('green');
|
50 |
+
}
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
.itsec-admin-notice .itsec-admin-notice__header:last-child {
|
55 |
+
border-bottom-left-radius: $border-radius;
|
56 |
+
border-bottom-right-radius: $border-radius;
|
57 |
+
}
|
58 |
+
|
59 |
+
.itsec-admin-notice__header .itsec-admin-notice__header-inset {
|
60 |
+
background: white;
|
61 |
+
border-radius: 5px;
|
62 |
+
padding: $side-padding / 2;
|
63 |
+
}
|
64 |
+
|
65 |
+
.itsec-admin-notice__header h4 {
|
66 |
+
font-size: 14px;
|
67 |
+
margin: 0;
|
68 |
+
}
|
69 |
+
|
70 |
+
.itsec-admin-notice__message {
|
71 |
+
padding: 10px $side-padding;
|
72 |
+
|
73 |
+
& p:first-child {
|
74 |
+
margin-top: 0;
|
75 |
+
}
|
76 |
+
|
77 |
+
& p:last-child {
|
78 |
+
margin-bottom: 0;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
.itsec-admin-notice__meta {
|
83 |
+
background: #eeecec;
|
84 |
+
padding: 5px $side-padding;
|
85 |
+
margin: 0;
|
86 |
+
display: grid;
|
87 |
+
grid-template: auto / min-content 1fr;
|
88 |
+
grid-gap: 5px 20px;
|
89 |
+
|
90 |
+
& dt,
|
91 |
+
& dd {
|
92 |
+
margin: 0;
|
93 |
+
}
|
94 |
+
|
95 |
+
& dt {
|
96 |
+
text-transform: uppercase;
|
97 |
+
color: darken(#444444, 25);
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
.itsec-admin-notice__footer {
|
102 |
+
padding: 5px $side-padding;
|
103 |
+
font-size: 11px;
|
104 |
+
}
|
core/modules/core/entries/admin-notices/components/panel/index.js
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import classnames from 'classnames';
|
5 |
+
import { get, size } from 'lodash';
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { __ } from '@wordpress/i18n';
|
10 |
+
import { IconButton, FormToggle } from '@wordpress/components';
|
11 |
+
import { compose, withState } from '@wordpress/compose';
|
12 |
+
import { withSelect, withDispatch } from '@wordpress/data';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Internal dependencies
|
16 |
+
*/
|
17 |
+
import NoticeList from '../notice-list';
|
18 |
+
import './style.scss';
|
19 |
+
|
20 |
+
function getAvailableHighlights() {
|
21 |
+
return [
|
22 |
+
{
|
23 |
+
slug: 'file-change-report',
|
24 |
+
label: __( 'File Change Report', 'better-wp-security' ),
|
25 |
+
},
|
26 |
+
{
|
27 |
+
slug: 'notification-center-send-failed',
|
28 |
+
label: __( 'Notification Center Errors', 'better-wp-security' ),
|
29 |
+
},
|
30 |
+
{
|
31 |
+
slug: 'malware-scan-report',
|
32 |
+
label: __( 'Malware Scan Report', 'better-wp-security' ),
|
33 |
+
},
|
34 |
+
{
|
35 |
+
slug: 'malware-scan-failed',
|
36 |
+
label: __( 'Malware Scan Failed', 'better-wp-security' ),
|
37 |
+
},
|
38 |
+
];
|
39 |
+
}
|
40 |
+
|
41 |
+
function Panel( { notices, loaded, mutedHighlights, mutedHighlightUpdatesInFlight, updateMutedHighlight, isConfiguring, setState } ) {
|
42 |
+
return (
|
43 |
+
<div className={ classnames( 'itsec-admin-notice-panel', {
|
44 |
+
'itsec-admin-notice-panel--is-configuring': isConfiguring,
|
45 |
+
} ) }>
|
46 |
+
<IconButton icon="admin-generic" label={ __( 'Configure', 'better-wp-security' ) }
|
47 |
+
className="itsec-admin-notice-panel__configure-trigger"
|
48 |
+
style={ { opacity: size( mutedHighlights ) > 0 ? 1 : 0 } }
|
49 |
+
onClick={ () => setState( { isConfiguring: ! isConfiguring } ) } />
|
50 |
+
<header className="itsec-admin-notice-panel__header">
|
51 |
+
<h3>{ __( 'Security Admin Messages', 'better-wp-security' ) }</h3>
|
52 |
+
<p>{ __( 'Important notices from iThemes Security', 'better-wp-security' ) }</p>
|
53 |
+
</header>
|
54 |
+
{ isConfiguring && (
|
55 |
+
<ul className="itsec-admin-notice-panel__configure-highlighted-logs">
|
56 |
+
{ getAvailableHighlights().map( ( { slug, label } ) => (
|
57 |
+
mutedHighlights[ slug ] !== undefined && (
|
58 |
+
<li>
|
59 |
+
<label htmlFor={ `itsec-mute-highlight-${ slug }` }>{ label }</label>
|
60 |
+
<FormToggle id={ `itsec-mute-highlight-${ slug }` }
|
61 |
+
disabled={ ! loaded || mutedHighlightUpdatesInFlight[ slug ] }
|
62 |
+
checked={ ! get( mutedHighlightUpdatesInFlight, [ slug, 'mute' ], mutedHighlights[ slug ] ) }
|
63 |
+
onChange={ () => updateMutedHighlight( slug, ! mutedHighlights[ slug ] ) }
|
64 |
+
/>
|
65 |
+
</li>
|
66 |
+
)
|
67 |
+
) ) }
|
68 |
+
</ul>
|
69 |
+
) }
|
70 |
+
{ notices.length > 0 ?
|
71 |
+
<NoticeList notices={ notices } /> :
|
72 |
+
loaded && <span>{ __( 'No notices at the moment.', 'better-wp-security' ) }</span>
|
73 |
+
}
|
74 |
+
</div>
|
75 |
+
);
|
76 |
+
}
|
77 |
+
|
78 |
+
export default compose( [
|
79 |
+
withState( { isConfiguring: false, checked: {} } ),
|
80 |
+
withSelect( ( select ) => ( {
|
81 |
+
mutedHighlights: select( 'ithemes-security/admin-notices' ).getMutedHighlights(),
|
82 |
+
mutedHighlightUpdatesInFlight: select( 'ithemes-security/admin-notices' ).getMutedHighlightUpdatesInFlight(),
|
83 |
+
} ) ),
|
84 |
+
withDispatch( ( dispatch ) => ( {
|
85 |
+
updateMutedHighlight: dispatch( 'ithemes-security/admin-notices' ).updateMutedHighlight,
|
86 |
+
} ) ),
|
87 |
+
] )( Panel );
|
core/modules/core/entries/admin-notices/components/panel/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/panel/style.scss
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
@import "animations.scss";
|
3 |
+
|
4 |
+
.itsec-admin-bar-admin-notices__content.components-popover::after {
|
5 |
+
border-color: #E5EAEE !important;
|
6 |
+
}
|
7 |
+
|
8 |
+
.itsec-admin-notice-panel {
|
9 |
+
padding: 1em;
|
10 |
+
background: #E5EAEE;
|
11 |
+
width: 420px;
|
12 |
+
box-sizing: border-box;
|
13 |
+
|
14 |
+
& .itsec-admin-notice-panel__header {
|
15 |
+
border-bottom: 1px solid $light-blue;
|
16 |
+
margin-bottom: 1em;
|
17 |
+
|
18 |
+
h3 {
|
19 |
+
color: $main-blue;
|
20 |
+
text-align: center;
|
21 |
+
}
|
22 |
+
|
23 |
+
p {
|
24 |
+
text-align: center;
|
25 |
+
font-style: oblique;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
.is-mobile & {
|
30 |
+
width: 100%;
|
31 |
+
overflow-x: auto;
|
32 |
+
height: 100%;
|
33 |
+
|
34 |
+
& header {
|
35 |
+
& h3 {
|
36 |
+
display: none;
|
37 |
+
}
|
38 |
+
|
39 |
+
& p {
|
40 |
+
margin-top: 0;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
& .itsec-admin-notice-panel__configure-trigger.components-icon-button {
|
46 |
+
padding: 5px;
|
47 |
+
height: 30px;
|
48 |
+
position: absolute;
|
49 |
+
right: 1em;
|
50 |
+
top: 1em;
|
51 |
+
transition: opacity 400ms;
|
52 |
+
|
53 |
+
&:hover {
|
54 |
+
opacity: .5;
|
55 |
+
box-shadow: none !important;
|
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 {
|
63 |
+
color: $main-blue;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
|
67 |
+
& .itsec-admin-notice-panel__configure-highlighted-logs {
|
68 |
+
border-bottom: 1px solid #7ABEED;
|
69 |
+
margin-bottom: 1em;
|
70 |
+
padding-bottom: 1em;
|
71 |
+
|
72 |
+
& li {
|
73 |
+
display: flex;
|
74 |
+
justify-content: space-between;
|
75 |
+
border-bottom: 1px solid #ccc;
|
76 |
+
margin-bottom: 1em;
|
77 |
+
padding-bottom: 1em;
|
78 |
+
padding-top: 0;
|
79 |
+
margin-top: 0;
|
80 |
+
|
81 |
+
&:last-child {
|
82 |
+
border-bottom: none;
|
83 |
+
margin-bottom: 0;
|
84 |
+
padding-bottom: 0;
|
85 |
+
}
|
86 |
+
|
87 |
+
& label {
|
88 |
+
margin-left: calc(24px + 1em);
|
89 |
+
font-size: 14px;
|
90 |
+
font-weight: bold;
|
91 |
+
}
|
92 |
+
|
93 |
+
& .components-form-toggle.is-checked .components-form-toggle__track {
|
94 |
+
background-color: $main-blue;
|
95 |
+
border-color: $main-blue;
|
96 |
+
}
|
97 |
+
|
98 |
+
& .components-form-toggle__input:disabled + .components-form-toggle__track {
|
99 |
+
opacity: .5;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
core/modules/core/entries/admin-notices/components/toolbar/index.js
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import classnames from 'classnames';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Fragment } from '@wordpress/element';
|
10 |
+
import { __ } from '@wordpress/i18n';
|
11 |
+
import { Popover, Button } from '@wordpress/components';
|
12 |
+
import { compose, withState } from '@wordpress/compose';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Internal dependencies
|
16 |
+
*/
|
17 |
+
import { doesElementBelongToPanel } from '../../utils';
|
18 |
+
import Panel from '../panel';
|
19 |
+
import './style.scss';
|
20 |
+
import { withSelect } from '@wordpress/data';
|
21 |
+
|
22 |
+
function Toolbar( { notices, noticesLoaded, isToggled, setState } ) {
|
23 |
+
return (
|
24 |
+
<Fragment>
|
25 |
+
<Button
|
26 |
+
id="itsec-admin-notices-toolbar-trigger"
|
27 |
+
className={ classnames( 'ab-item ab-empty-item', {
|
28 |
+
'itsec-admin-notices-toolbar--has-notices': notices.length > 0,
|
29 |
+
} ) }
|
30 |
+
onClick={ () => setState( { isToggled: ! isToggled } ) }
|
31 |
+
aria-expanded={ isToggled }>
|
32 |
+
<span className="it-icon-itsec" />
|
33 |
+
<span className="itsec-toolbar-text">{ __( 'Security', 'better-wp-security' ) }</span>
|
34 |
+
{ notices.length > 0 && (
|
35 |
+
<span className="itsec-admin-notices-toolbar-bubble">
|
36 |
+
<span className="itsec-admin-notices-toolbar-bubble__count">
|
37 |
+
{ notices.length }
|
38 |
+
</span>
|
39 |
+
</span>
|
40 |
+
) }
|
41 |
+
</Button>
|
42 |
+
{ isToggled && (
|
43 |
+
<Popover
|
44 |
+
className="itsec-admin-notices-toolbar__popover"
|
45 |
+
noArrow
|
46 |
+
expandOnMobile
|
47 |
+
focusOnMount="container"
|
48 |
+
position="bottom center"
|
49 |
+
headerTitle={ __( 'Security', 'better-wp-security' ) }
|
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 |
+
}
|
59 |
+
} }
|
60 |
+
>
|
61 |
+
<Panel notices={ notices } loaded={ noticesLoaded } close={ () => setState( { isToggled: false } ) } />
|
62 |
+
</Popover>
|
63 |
+
) }
|
64 |
+
</Fragment>
|
65 |
+
);
|
66 |
+
}
|
67 |
+
|
68 |
+
export default compose( [
|
69 |
+
withSelect( ( select ) => ( {
|
70 |
+
notices: select( 'ithemes-security/admin-notices' ).getNotices(),
|
71 |
+
noticesLoaded: select( 'ithemes-security/admin-notices' ).areNoticesLoaded(),
|
72 |
+
} ) ),
|
73 |
+
withState( { isToggled: false } ),
|
74 |
+
] )( Toolbar );
|
core/modules/core/entries/admin-notices/components/toolbar/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/components/toolbar/style.scss
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
@import "breakpoints.scss";
|
3 |
+
|
4 |
+
#wpadminbar #wp-admin-bar-itsec_admin_bar_menu .ab-sub-wrapper {
|
5 |
+
background: transparent;
|
6 |
+
box-shadow: none;
|
7 |
+
}
|
8 |
+
|
9 |
+
#wpadminbar .itsec-admin-notices-toolbar-bubble {
|
10 |
+
display: inline-block;
|
11 |
+
vertical-align: top;
|
12 |
+
margin: 8px 0 0 5px;
|
13 |
+
padding: 0 5px;
|
14 |
+
min-width: 7px;
|
15 |
+
height: 17px;
|
16 |
+
border-radius: 11px;
|
17 |
+
font-size: 9px;
|
18 |
+
line-height: 17px;
|
19 |
+
text-align: center;
|
20 |
+
z-index: 26;
|
21 |
+
background-color: #ca4a1f;
|
22 |
+
color: #fff;
|
23 |
+
animation: itsec-admin-notices-toolbar-bubble-fade-in 400ms;
|
24 |
+
|
25 |
+
& .itsec-admin-notices-toolbar-bubble__count {
|
26 |
+
line-height: 17px;
|
27 |
+
font-size: 9px;
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
@keyframes itsec-admin-notices-toolbar-bubble-fade-in {
|
32 |
+
from {
|
33 |
+
opacity: 0;
|
34 |
+
}
|
35 |
+
|
36 |
+
to {
|
37 |
+
opacity: 1;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
#itsec-admin-notices-root .itsec-admin-notice-panel {
|
42 |
+
border: 1px solid #e2e4e7;
|
43 |
+
box-shadow: 0 3px 30px rgba(25, 30, 35, .1);
|
44 |
+
}
|
45 |
+
|
46 |
+
#itsec-admin-notices-root .components-popover:not(.is-mobile).is-bottom {
|
47 |
+
z-index: 99999;
|
48 |
+
}
|
49 |
+
|
50 |
+
#itsec-admin-notices-root .itsec-admin-notices-toolbar__popover .components-popover__content {
|
51 |
+
box-shadow: rgba(0, 0, 0, 0.19) 0 4px 5px;
|
52 |
+
}
|
53 |
+
|
54 |
+
#wp-admin-bar-itsec_admin_bar_menu .components-button {
|
55 |
+
cursor: pointer;
|
56 |
+
}
|
57 |
+
|
58 |
+
#wp-admin-bar-itsec_admin_bar_menu .it-icon-itsec {
|
59 |
+
display: none;
|
60 |
+
margin-top: 8px;
|
61 |
+
color: rgba(240, 245, 250, 0.6);
|
62 |
+
height: 20px;
|
63 |
+
width: 52px;
|
64 |
+
line-height: 32px;
|
65 |
+
font-size: 32px;
|
66 |
+
text-align: center;
|
67 |
+
}
|
68 |
+
|
69 |
+
@media screen and (max-width: 782px) {
|
70 |
+
#wp-toolbar > ul > li#wp-admin-bar-itsec_admin_bar_menu {
|
71 |
+
display: list-item;
|
72 |
+
|
73 |
+
& .it-icon-itsec {
|
74 |
+
display: inline-block;
|
75 |
+
}
|
76 |
+
|
77 |
+
& .itsec-toolbar-text {
|
78 |
+
display: none;
|
79 |
+
}
|
80 |
+
|
81 |
+
& .itsec-admin-notices-toolbar-bubble {
|
82 |
+
display: none;
|
83 |
+
}
|
84 |
+
|
85 |
+
& .itsec-admin-notices-toolbar--has-notices {
|
86 |
+
position: relative;
|
87 |
+
|
88 |
+
&::before {
|
89 |
+
content: '';
|
90 |
+
background: #d8514f;
|
91 |
+
height: 5px;
|
92 |
+
width: 5px;
|
93 |
+
border-radius: 5px;
|
94 |
+
vertical-align: middle;
|
95 |
+
border: 1px solid #23282d;
|
96 |
+
z-index: 1;
|
97 |
+
position: absolute;
|
98 |
+
left: 4px;
|
99 |
+
top: 3px;
|
100 |
+
animation: itsec-admin-notices-toolbar-bubble-fade-in 400ms;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
}
|
104 |
+
}
|
core/modules/core/entries/admin-notices/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/store/actions.js
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Internal dependencies
|
3 |
+
*/
|
4 |
+
import { apiFetch } from './controls';
|
5 |
+
|
6 |
+
export function receiveNotices( notices ) {
|
7 |
+
return {
|
8 |
+
type: RECEIVE_NOTICES,
|
9 |
+
notices,
|
10 |
+
};
|
11 |
+
}
|
12 |
+
|
13 |
+
export function startNoticeAction( noticeId, actionId ) {
|
14 |
+
return {
|
15 |
+
type: START_NOTICE_ACTION,
|
16 |
+
noticeId,
|
17 |
+
actionId,
|
18 |
+
};
|
19 |
+
}
|
20 |
+
|
21 |
+
export function finishNoticeAction( noticeId, actionId, response ) {
|
22 |
+
return {
|
23 |
+
type: FINISH_NOTICE_ACTION,
|
24 |
+
noticeId,
|
25 |
+
actionId,
|
26 |
+
response,
|
27 |
+
};
|
28 |
+
}
|
29 |
+
|
30 |
+
export function failedNoticeAction( noticeId, actionId, error ) {
|
31 |
+
return {
|
32 |
+
type: FAILED_NOTICE_ACTION,
|
33 |
+
noticeId,
|
34 |
+
actionId,
|
35 |
+
error,
|
36 |
+
};
|
37 |
+
}
|
38 |
+
|
39 |
+
export function receiveMutedHighlights( mutedHighlights ) {
|
40 |
+
return {
|
41 |
+
type: RECEIVE_MUTED_HIGHLIGHTS,
|
42 |
+
mutedHighlights,
|
43 |
+
};
|
44 |
+
}
|
45 |
+
|
46 |
+
export function startUpdateMutedHighlight( slug, mute ) {
|
47 |
+
return {
|
48 |
+
type: START_UPDATE_MUTED_HIGHLIGHT,
|
49 |
+
slug,
|
50 |
+
mute,
|
51 |
+
};
|
52 |
+
}
|
53 |
+
|
54 |
+
export function finishUpdateMutedHighlight( slug, mute ) {
|
55 |
+
return {
|
56 |
+
type: FINISH_UPDATE_MUTED_HIGHLIGHT,
|
57 |
+
slug,
|
58 |
+
mute,
|
59 |
+
};
|
60 |
+
}
|
61 |
+
|
62 |
+
export function failedUpdateMutedHighlight( slug, mute, error ) {
|
63 |
+
return {
|
64 |
+
type: FAILED_UPDATE_MUTED_HIGHLIGHT,
|
65 |
+
slug,
|
66 |
+
mute,
|
67 |
+
error,
|
68 |
+
};
|
69 |
+
}
|
70 |
+
|
71 |
+
export function* doNoticeAction( noticeId, actionId, payload = {} ) {
|
72 |
+
yield startNoticeAction( noticeId, actionId );
|
73 |
+
|
74 |
+
let response;
|
75 |
+
|
76 |
+
try {
|
77 |
+
response = yield apiFetch( {
|
78 |
+
path: `/ithemes-security/v1/admin-notices/${ noticeId }/${ actionId }`,
|
79 |
+
method: 'POST',
|
80 |
+
data: payload,
|
81 |
+
} );
|
82 |
+
} catch ( e ) {
|
83 |
+
yield failedNoticeAction( noticeId, actionId, e );
|
84 |
+
|
85 |
+
return e;
|
86 |
+
}
|
87 |
+
|
88 |
+
yield finishNoticeAction( noticeId, actionId, response );
|
89 |
+
|
90 |
+
return response;
|
91 |
+
}
|
92 |
+
|
93 |
+
export function* updateMutedHighlight( slug, muted ) {
|
94 |
+
yield startUpdateMutedHighlight( slug, muted );
|
95 |
+
|
96 |
+
let response;
|
97 |
+
|
98 |
+
try {
|
99 |
+
response = yield apiFetch( {
|
100 |
+
path: '/ithemes-security/v1/admin-notices/settings',
|
101 |
+
method: 'PUT',
|
102 |
+
data: {
|
103 |
+
muted_highlights: {
|
104 |
+
[ slug ]: muted,
|
105 |
+
},
|
106 |
+
},
|
107 |
+
} );
|
108 |
+
} catch ( e ) {
|
109 |
+
yield failedUpdateMutedHighlight( slug, muted, e );
|
110 |
+
|
111 |
+
return e;
|
112 |
+
}
|
113 |
+
|
114 |
+
yield finishUpdateMutedHighlight( slug, muted );
|
115 |
+
|
116 |
+
return response;
|
117 |
+
}
|
118 |
+
|
119 |
+
export const RECEIVE_NOTICES = 'RECEIVE_NOTICES';
|
120 |
+
export const START_NOTICE_ACTION = 'START_NOTICE_ACTION';
|
121 |
+
export const FINISH_NOTICE_ACTION = 'FINISH_NOTICE_ACTION';
|
122 |
+
export const FAILED_NOTICE_ACTION = 'FAILED_NOTICE_ACTION';
|
123 |
+
export const RECEIVE_MUTED_HIGHLIGHTS = 'RECEIVE_MUTED_HIGHLIGHTS';
|
124 |
+
export const START_UPDATE_MUTED_HIGHLIGHT = 'START_UPDATE_MUTED_HIGHLIGHT';
|
125 |
+
export const FINISH_UPDATE_MUTED_HIGHLIGHT = 'FINISH_UPDATE_MUTED_HIGHLIGHT';
|
126 |
+
export const FAILED_UPDATE_MUTED_HIGHLIGHT = 'FAILED_UPDATE_MUTED_HIGHLIGHT';
|
core/modules/core/entries/admin-notices/store/controls.js
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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} selectorName Selector name.
|
33 |
+
* @param {Array} args Selector arguments.
|
34 |
+
*
|
35 |
+
* @return {Object} control descriptor.
|
36 |
+
*/
|
37 |
+
export function select( selectorName, ...args ) {
|
38 |
+
return {
|
39 |
+
type: 'SELECT',
|
40 |
+
selectorName,
|
41 |
+
args,
|
42 |
+
};
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Yields action objects used in signalling that a notice is to be created.
|
47 |
+
*
|
48 |
+
* @see @wordpress/notices#createNotice()
|
49 |
+
*
|
50 |
+
* @param {?string} status Notice status.
|
51 |
+
* Defaults to `info`.
|
52 |
+
* @param {string} content Notice message.
|
53 |
+
* @param {?Object} options Notice options.
|
54 |
+
* @param {?string} options.context Context under which to
|
55 |
+
* group notice.
|
56 |
+
* @param {?string} options.id Identifier for notice.
|
57 |
+
* Automatically assigned
|
58 |
+
* if not specified.
|
59 |
+
* @param {?boolean} options.isDismissible Whether the notice can
|
60 |
+
* be dismissed by user.
|
61 |
+
* Defaults to `true`.
|
62 |
+
* @param {?number} options.autoDismiss Whether the notice should
|
63 |
+
* by automatically dismissed
|
64 |
+
* after x milliseconds.
|
65 |
+
* Defaults to `false`.
|
66 |
+
* @param {?Array<WPNoticeAction>} options.actions User actions to be
|
67 |
+
* presented with notice.
|
68 |
+
*
|
69 |
+
* @return {Object} control descriptor.
|
70 |
+
*/
|
71 |
+
export function createNotice( status = 'info', content, options = {} ) {
|
72 |
+
return {
|
73 |
+
type: 'CREATE_NOTICE',
|
74 |
+
status,
|
75 |
+
content,
|
76 |
+
options: {
|
77 |
+
context: 'ithemes-security',
|
78 |
+
...options,
|
79 |
+
},
|
80 |
+
};
|
81 |
+
}
|
82 |
+
|
83 |
+
const controls = {
|
84 |
+
API_FETCH( { request } ) {
|
85 |
+
return triggerApiFetch( request ).catch( responseToError );
|
86 |
+
},
|
87 |
+
|
88 |
+
SELECT( { selectorName, args } ) {
|
89 |
+
return selectData( 'ithemes-security/admin-notices' )[ selectorName ]( ...args );
|
90 |
+
},
|
91 |
+
CREATE_NOTICE( { status, content, options } ) {
|
92 |
+
if ( options.autoDismiss ) {
|
93 |
+
options.id = options.id || uniqueId( 'itsec-auto-dismiss-' );
|
94 |
+
setTimeout( () => dispatchData( 'core/notices' ).removeNotice( options.id, options.context ), options.autoDismiss );
|
95 |
+
}
|
96 |
+
|
97 |
+
dispatchData( 'core/notices' ).createNotice( status, content, options );
|
98 |
+
},
|
99 |
+
};
|
100 |
+
|
101 |
+
export default controls;
|
core/modules/core/entries/admin-notices/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 adminNotices from './reducers';
|
13 |
+
import * as resolvers from './resolvers';
|
14 |
+
|
15 |
+
const store = registerStore( 'ithemes-security/admin-notices', {
|
16 |
+
controls,
|
17 |
+
actions,
|
18 |
+
selectors,
|
19 |
+
resolvers,
|
20 |
+
reducer: adminNotices,
|
21 |
+
} );
|
22 |
+
|
23 |
+
export default store;
|
core/modules/core/entries/admin-notices/store/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/entries/admin-notices/store/reducers.js
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { omit } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies
|
8 |
+
*/
|
9 |
+
import {
|
10 |
+
FAILED_NOTICE_ACTION,
|
11 |
+
FAILED_UPDATE_MUTED_HIGHLIGHT,
|
12 |
+
FINISH_NOTICE_ACTION,
|
13 |
+
FINISH_UPDATE_MUTED_HIGHLIGHT,
|
14 |
+
RECEIVE_MUTED_HIGHLIGHTS,
|
15 |
+
RECEIVE_NOTICES,
|
16 |
+
START_NOTICE_ACTION,
|
17 |
+
START_UPDATE_MUTED_HIGHLIGHT,
|
18 |
+
} from './actions';
|
19 |
+
|
20 |
+
const DEFAULT_STATE = {
|
21 |
+
notices: [],
|
22 |
+
doingActions: {},
|
23 |
+
mutedHighlights: {},
|
24 |
+
mutedHighlightUpdatesInFlight: {},
|
25 |
+
};
|
26 |
+
|
27 |
+
export default function adminNotices( state = DEFAULT_STATE, action ) {
|
28 |
+
switch ( action.type ) {
|
29 |
+
case RECEIVE_NOTICES:
|
30 |
+
return {
|
31 |
+
...state,
|
32 |
+
notices: [
|
33 |
+
...action.notices,
|
34 |
+
],
|
35 |
+
};
|
36 |
+
case START_NOTICE_ACTION:
|
37 |
+
return {
|
38 |
+
...state,
|
39 |
+
doingActions: {
|
40 |
+
...state.doingActions,
|
41 |
+
[ action.noticeId ]: [
|
42 |
+
...( state.doingActions[ action.noticeId ] || [] ),
|
43 |
+
action.actionId,
|
44 |
+
],
|
45 |
+
},
|
46 |
+
};
|
47 |
+
case FINISH_NOTICE_ACTION:
|
48 |
+
case FAILED_NOTICE_ACTION:
|
49 |
+
return {
|
50 |
+
...state,
|
51 |
+
doingActions: {
|
52 |
+
...state.doingActions,
|
53 |
+
[ action.noticeId ]: ( state.doingActions[ action.noticeId ] || [] ).filter( ( actionId ) => actionId !== action.actionId ),
|
54 |
+
},
|
55 |
+
};
|
56 |
+
case RECEIVE_MUTED_HIGHLIGHTS:
|
57 |
+
return {
|
58 |
+
...state,
|
59 |
+
mutedHighlights: action.mutedHighlights,
|
60 |
+
};
|
61 |
+
case START_UPDATE_MUTED_HIGHLIGHT:
|
62 |
+
return {
|
63 |
+
...state,
|
64 |
+
mutedHighlightUpdatesInFlight: {
|
65 |
+
...state.mutedHighlightUpdatesInFlight,
|
66 |
+
[ action.slug ]: { mute: action.mute },
|
67 |
+
},
|
68 |
+
};
|
69 |
+
case FINISH_UPDATE_MUTED_HIGHLIGHT:
|
70 |
+
return {
|
71 |
+
...state,
|
72 |
+
mutedHighlightUpdatesInFlight: omit( state.mutedHighlightUpdatesInFlight, action.slug ),
|
73 |
+
mutedHighlights: {
|
74 |
+
...state.mutedHighlights,
|
75 |
+
[ action.slug ]: action.mute,
|
76 |
+
},
|
77 |
+
};
|
78 |
+
case FAILED_UPDATE_MUTED_HIGHLIGHT:
|
79 |
+
return {
|
80 |
+
...state,
|
81 |
+
mutedHighlightUpdatesInFlight: omit( state.mutedHighlightUpdatesInFlight, action.slug ),
|
82 |
+
};
|
83 |
+
default:
|
84 |
+
return state;
|
85 |
+
}
|
86 |
+
}
|
core/modules/core/entries/admin-notices/store/resolvers.js
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isEmpty } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies
|
8 |
+
*/
|
9 |
+
import { apiFetch } from './controls';
|
10 |
+
import { FINISH_NOTICE_ACTION, FINISH_UPDATE_MUTED_HIGHLIGHT, receiveMutedHighlights, receiveNotices } from './actions';
|
11 |
+
|
12 |
+
const getNotices = {
|
13 |
+
*fulfill() {
|
14 |
+
const notices = yield apiFetch( {
|
15 |
+
path: '/ithemes-security/v1/admin-notices',
|
16 |
+
} );
|
17 |
+
|
18 |
+
yield receiveNotices( notices );
|
19 |
+
},
|
20 |
+
shouldInvalidate( action ) {
|
21 |
+
return action.type === FINISH_NOTICE_ACTION || action.type === FINISH_UPDATE_MUTED_HIGHLIGHT;
|
22 |
+
},
|
23 |
+
};
|
24 |
+
|
25 |
+
export { getNotices as getNotices };
|
26 |
+
|
27 |
+
export function *getMutedHighlights() {
|
28 |
+
const settings = yield apiFetch( {
|
29 |
+
path: '/ithemes-security/v1/admin-notices/settings',
|
30 |
+
} );
|
31 |
+
|
32 |
+
yield receiveMutedHighlights( isEmpty( settings.muted_highlights ) ? {} : settings.muted_highlights );
|
33 |
+
}
|
core/modules/core/entries/admin-notices/store/selectors.js
ADDED
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { select } from '@wordpress/data';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Returns true if resolution is in progress for the core selector of the given
|
8 |
+
* name and arguments.
|
9 |
+
*
|
10 |
+
* @param {string} selectorName Core data selector name.
|
11 |
+
* @param {...*} args Arguments passed to selector.
|
12 |
+
*
|
13 |
+
* @return {boolean} Whether resolution is in progress.
|
14 |
+
*/
|
15 |
+
export function isResolving( selectorName, ...args ) {
|
16 |
+
return select( 'core/data' ).isResolving( 'ithemes-security/admin-notices', selectorName, args );
|
17 |
+
}
|
18 |
+
|
19 |
+
export function isResolved( selectorName, ...args ) {
|
20 |
+
return select( 'core/data' ).hasFinishedResolution( 'ithemes-security/admin-notices', selectorName, args );
|
21 |
+
}
|
22 |
+
|
23 |
+
export function getNotices( state ) {
|
24 |
+
return state.notices;
|
25 |
+
}
|
26 |
+
|
27 |
+
export function areNoticesLoaded() {
|
28 |
+
return isResolved( 'getNotices' );
|
29 |
+
}
|
30 |
+
|
31 |
+
export function isDoingAction( state, noticeId, actionId = '' ) {
|
32 |
+
if ( ! state.doingActions[ noticeId ] ) {
|
33 |
+
return false;
|
34 |
+
}
|
35 |
+
|
36 |
+
if ( actionId === '' ) {
|
37 |
+
return true;
|
38 |
+
}
|
39 |
+
|
40 |
+
return state.doingActions[ noticeId ].includes( actionId );
|
41 |
+
}
|
42 |
+
|
43 |
+
const DEFAULT_IN_PROGRESS = [];
|
44 |
+
|
45 |
+
export function getInProgressActions( state, noticeId ) {
|
46 |
+
return state.doingActions[ noticeId ] || DEFAULT_IN_PROGRESS;
|
47 |
+
}
|
48 |
+
|
49 |
+
export function getMutedHighlights( state ) {
|
50 |
+
return state.mutedHighlights;
|
51 |
+
}
|
52 |
+
|
53 |
+
export function getMutedHighlightUpdatesInFlight( state ) {
|
54 |
+
return state.mutedHighlightUpdatesInFlight;
|
55 |
+
}
|
core/modules/core/entries/admin-notices/style.scss
ADDED
File without changes
|
core/modules/core/entries/admin-notices/utils.js
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Check if the given element belongs to the notices panel even if it is outside of the DOM tree.
|
3 |
+
*
|
4 |
+
* @param {HTMLElement} element
|
5 |
+
* @return {boolean} If the element is protected.
|
6 |
+
*/
|
7 |
+
export function doesElementBelongToPanel( element ) {
|
8 |
+
let node = element.parentNode;
|
9 |
+
|
10 |
+
while ( node !== null ) {
|
11 |
+
if ( node.classList && node.classList.contains( 'itsec-admin-notice-list-actions__more-menu-items' ) ) {
|
12 |
+
return true;
|
13 |
+
}
|
14 |
+
|
15 |
+
node = node.parentNode;
|
16 |
+
}
|
17 |
+
|
18 |
+
return false;
|
19 |
+
}
|
core/modules/core/entries/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/core/js/admin-notices.js
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
( function( $, wp, config ) {
|
2 |
+
$( function() {
|
3 |
+
$( '.itsec-notice .notice-dismiss' ).off( 'click.wp-dismiss-notice' );
|
4 |
+
|
5 |
+
$( document ).on( 'click', '.itsec-notice .notice-dismiss', function( e ) {
|
6 |
+
const $this = $( this ),
|
7 |
+
$notice = $this.parent( '.itsec-notice' );
|
8 |
+
|
9 |
+
$notice.fadeTo( 100, 0, function() {
|
10 |
+
$notice.slideUp( 100 );
|
11 |
+
} );
|
12 |
+
|
13 |
+
ajax( $notice, $notice.data( 'close' ) );
|
14 |
+
} );
|
15 |
+
|
16 |
+
$( document ).on( 'click', '.itsec-notice [data-action]', function( e ) {
|
17 |
+
const $this = $( this ),
|
18 |
+
$notice = $this.parent( '.itsec-notice' );
|
19 |
+
|
20 |
+
$this.prop( 'disabled', true );
|
21 |
+
|
22 |
+
ajax( $notice, action ).always( function() {
|
23 |
+
$this.prop( 'disabled', false );
|
24 |
+
} );
|
25 |
+
} );
|
26 |
+
} );
|
27 |
+
|
28 |
+
function ajax( $notice, action ) {
|
29 |
+
return wp.ajax.post( 'itsec-admin-notice', {
|
30 |
+
itsec_action: action,
|
31 |
+
notice_id : $notice.data( 'id' ),
|
32 |
+
nonce : config.nonce,
|
33 |
+
} )
|
34 |
+
.done( function() {
|
35 |
+
if ( $notice.css( 'opacity' ) !== '1' ) {
|
36 |
+
if ( $notice.css( 'opacity' ) === '0' ) {
|
37 |
+
$notice.remove();
|
38 |
+
} else {
|
39 |
+
setTimeout( function() {
|
40 |
+
$notice.remove();
|
41 |
+
}, 100 );
|
42 |
+
}
|
43 |
+
} else {
|
44 |
+
$notice.fadeTo( 100, 0, function() {
|
45 |
+
$notice.slideUp( 100, function() {
|
46 |
+
$notice.remove();
|
47 |
+
} );
|
48 |
+
} );
|
49 |
+
}
|
50 |
+
} )
|
51 |
+
.fail( function( response ) {
|
52 |
+
if ( response.message ) {
|
53 |
+
alert( response.message );
|
54 |
+
} else if ( Array.isArray( response ) ) {
|
55 |
+
const messages = [];
|
56 |
+
|
57 |
+
for ( let i = 0; i < response.length; i++ ) {
|
58 |
+
messages.push( response[ i ].message );
|
59 |
+
}
|
60 |
+
|
61 |
+
alert( messages.join( ' ' ) );
|
62 |
+
} else {
|
63 |
+
alert( 'An unexpected error occurred.' );
|
64 |
+
}
|
65 |
+
|
66 |
+
if ( $notice.css( 'opacity' ) !== '1' ) {
|
67 |
+
$notice.slideDown( 100, function() {
|
68 |
+
$notice.fadeTo( 100, 1 );
|
69 |
+
} );
|
70 |
+
}
|
71 |
+
} );
|
72 |
+
}
|
73 |
+
} )( jQuery, wp, window[ 'ITSECAdminNotices' ] );
|
core/modules/core/settings.php
DELETED
@@ -1,13 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
final class ITSEC_Core_Settings extends ITSEC_Settings {
|
4 |
-
public function get_id() {
|
5 |
-
return 'core';
|
6 |
-
}
|
7 |
-
|
8 |
-
public function get_defaults() {
|
9 |
-
return array();
|
10 |
-
}
|
11 |
-
}
|
12 |
-
|
13 |
-
ITSEC_Modules::register_settings( new ITSEC_Core_Settings() );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/core/setup.php
DELETED
@@ -1,70 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( ! class_exists( 'ITSEC_Core_Setup' ) ) {
|
4 |
-
|
5 |
-
class ITSEC_Core_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 |
-
* Execute module deactivation
|
27 |
-
*
|
28 |
-
* @return void
|
29 |
-
*/
|
30 |
-
public function execute_deactivate() {
|
31 |
-
|
32 |
-
delete_site_option( 'itsec_free_just_activated' );
|
33 |
-
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* Execute module uninstall
|
38 |
-
*
|
39 |
-
* @return void
|
40 |
-
*/
|
41 |
-
public function execute_uninstall() {
|
42 |
-
|
43 |
-
$this->execute_deactivate();
|
44 |
-
|
45 |
-
}
|
46 |
-
|
47 |
-
/**
|
48 |
-
* Execute module upgrade
|
49 |
-
*
|
50 |
-
* @return void
|
51 |
-
*/
|
52 |
-
public function execute_upgrade( $build ) {
|
53 |
-
if ( $build < 4069 ) {
|
54 |
-
delete_site_option( 'itsec_free_just_activated' );
|
55 |
-
}
|
56 |
-
|
57 |
-
if ( $build < 4076 ) {
|
58 |
-
$digest = wp_next_scheduled( 'itsec_digest_email' );
|
59 |
-
|
60 |
-
if ( $digest ) {
|
61 |
-
wp_unschedule_event( $digest, 'itsec_digest_email' );
|
62 |
-
}
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
}
|
67 |
-
|
68 |
-
}
|
69 |
-
|
70 |
-
new ITSEC_Core_Setup();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/core/sidebar-widget-backupbuddy-cross-promo.php
CHANGED
@@ -4,7 +4,7 @@ class ITSEC_Settings_Page_Sidebar_Widget_BackupBuddy_Cross_Promo extends ITSEC_S
|
|
4 |
public function __construct() {
|
5 |
$this->id = 'backupbuddy-cross-promo';
|
6 |
$this->title = __( 'Complete Your Security Strategy With BackupBuddy', 'better-wp-security' );
|
7 |
-
$this->priority = 7;
|
8 |
|
9 |
parent::__construct();
|
10 |
}
|
4 |
public function __construct() {
|
5 |
$this->id = 'backupbuddy-cross-promo';
|
6 |
$this->title = __( 'Complete Your Security Strategy With BackupBuddy', 'better-wp-security' );
|
7 |
+
$this->priority = ITSEC_Core::is_pro() ? 11 : 7;
|
8 |
|
9 |
parent::__construct();
|
10 |
}
|
core/modules/core/sidebar-widget-support.php
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
class
|
4 |
public function __construct() {
|
5 |
$this->id = 'support';
|
6 |
$this->title = __( 'Need Help Securing Your Site?', 'better-wp-security' );
|
@@ -17,4 +17,4 @@ class ITSEC_Settings_Page_Sidebar_Widget_Support extends ITSEC_Settings_Page_Sid
|
|
17 |
}
|
18 |
|
19 |
}
|
20 |
-
new
|
1 |
<?php
|
2 |
|
3 |
+
class ITSEC_Settings_Page_Sidebar_Widget_Free_Support extends ITSEC_Settings_Page_Sidebar_Widget {
|
4 |
public function __construct() {
|
5 |
$this->id = 'support';
|
6 |
$this->title = __( 'Need Help Securing Your Site?', 'better-wp-security' );
|
17 |
}
|
18 |
|
19 |
}
|
20 |
+
new ITSEC_Settings_Page_Sidebar_Widget_Free_Support();
|
core/modules/core/validator.php
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class ITSEC_Core_Validator extends ITSEC_Validator {
|
4 |
-
public function get_id() {
|
5 |
-
return 'core';
|
6 |
-
}
|
7 |
-
|
8 |
-
protected function preprocess_settings() {
|
9 |
-
|
10 |
-
}
|
11 |
-
|
12 |
-
protected function validate_settings() {
|
13 |
-
|
14 |
-
}
|
15 |
-
}
|
16 |
-
|
17 |
-
ITSEC_Modules::register_validator( new ITSEC_Core_Validator() );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/feature-flags/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/feature-flags/settings-page.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Feature_Flags_Settings_Page extends ITSEC_Module_Settings_Page {
|
4 |
+
|
5 |
+
public function __construct() {
|
6 |
+
parent::__construct();
|
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 |
+
|
28 |
+
protected function render_settings( $form ) {
|
29 |
+
ITSEC_Lib::load( 'feature-flags' );
|
30 |
+
|
31 |
+
foreach ( ITSEC_Lib_Feature_Flags::get_available_flags() as $flag => $config ) {
|
32 |
+
if ( is_callable( $config['title'] ) ) {
|
33 |
+
$title = call_user_func( $config['title'] );
|
34 |
+
} elseif ( $config['title'] ) {
|
35 |
+
$title = $config['title'];
|
36 |
+
} else {
|
37 |
+
$title = ucwords( str_replace( '_', ' ', $flag ) );
|
38 |
+
}
|
39 |
+
|
40 |
+
if ( is_callable( $config['description'] ) ) {
|
41 |
+
$description = call_user_func( $config['description'] );
|
42 |
+
} else {
|
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 |
+
?>
|
54 |
+
<table class="form-table itsec-settings-section">
|
55 |
+
<tbody>
|
56 |
+
<tr>
|
57 |
+
<th scope="row">
|
58 |
+
<label for="itsec-feature-flags-<?php echo esc_attr( $flag ); ?>"><?php echo $title; ?></label>
|
59 |
+
</th>
|
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>
|
68 |
+
</table>
|
69 |
+
<?php
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
public function handle_form_post( $data ) {
|
74 |
+
ITSEC_Lib::load( 'feature-flags' );
|
75 |
+
|
76 |
+
foreach ( $data as $flag => $enabled ) {
|
77 |
+
if ( defined( 'ITSEC_FF_' . $flag ) ) {
|
78 |
+
continue;
|
79 |
+
}
|
80 |
+
|
81 |
+
if ( $enabled ) {
|
82 |
+
ITSEC_Lib_Feature_Flags::enable( $flag );
|
83 |
+
} else {
|
84 |
+
ITSEC_Lib_Feature_Flags::disable( $flag );
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
new ITSEC_Feature_Flags_Settings_Page();
|
core/modules/file-change/admin.php
CHANGED
@@ -2,19 +2,6 @@
|
|
2 |
|
3 |
final class ITSEC_File_Change_Admin {
|
4 |
|
5 |
-
const AJAX = 'itsec_file_change_dismiss_warning';
|
6 |
-
|
7 |
-
private $script_version = 2;
|
8 |
-
private $dismiss_nonce;
|
9 |
-
|
10 |
-
|
11 |
-
public function __construct() {
|
12 |
-
|
13 |
-
if ( ITSEC_Modules::get_setting( 'file-change', 'show_warning' ) ) {
|
14 |
-
add_action( 'init', array( $this, 'init' ) );
|
15 |
-
}
|
16 |
-
}
|
17 |
-
|
18 |
public static function enqueue_scanner() {
|
19 |
$logs_page_url = ITSEC_Core::get_logs_page_url( 'file_change' );
|
20 |
|
@@ -29,90 +16,6 @@ final class ITSEC_File_Change_Admin {
|
|
29 |
'already_running' => sprintf( __( 'A scan is already in progress. Please check the <a href="%s" target="_blank" rel="noopener noreferrer">logs page</a> at a later time for the results of the scan.', 'better-wp-security' ), esc_url( $logs_page_url ) ),
|
30 |
) );
|
31 |
}
|
32 |
-
|
33 |
-
public function init() {
|
34 |
-
|
35 |
-
if ( ! ITSEC_Core::current_user_can_manage() ) {
|
36 |
-
return;
|
37 |
-
}
|
38 |
-
|
39 |
-
add_action( 'wp_ajax_' . self::AJAX, array( $this, 'dismiss_file_change_warning_ajax' ) );
|
40 |
-
|
41 |
-
if ( ! empty( $_GET['file_change_dismiss_warning'] ) ) {
|
42 |
-
$this->dismiss_file_change_warning();
|
43 |
-
} else {
|
44 |
-
add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
|
45 |
-
$this->dismiss_nonce = wp_create_nonce( 'itsec-file-change-dismiss-warning' );
|
46 |
-
|
47 |
-
if ( is_multisite() ) {
|
48 |
-
add_action( 'network_admin_notices', array( $this, 'show_file_change_warning' ) );
|
49 |
-
} else {
|
50 |
-
add_action( 'admin_notices', array( $this, 'show_file_change_warning' ) );
|
51 |
-
}
|
52 |
-
}
|
53 |
-
}
|
54 |
-
|
55 |
-
public function add_scripts() {
|
56 |
-
$vars = array(
|
57 |
-
'ajax_action' => self::AJAX,
|
58 |
-
'ajax_nonce' => $this->dismiss_nonce
|
59 |
-
);
|
60 |
-
|
61 |
-
wp_enqueue_script( 'itsec-file-change-script', plugins_url( 'js/script.js', __FILE__ ), array( 'jquery', 'common' ), $this->script_version, true );
|
62 |
-
wp_localize_script( 'itsec-file-change-script', 'itsec_file_change', $vars );
|
63 |
-
}
|
64 |
-
|
65 |
-
public function dismiss_file_change_warning_ajax() {
|
66 |
-
if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'itsec-file-change-dismiss-warning' ) ) {
|
67 |
-
wp_send_json_error( array(
|
68 |
-
'message' => __( 'Request expired. Please refresh and try again.', 'better-wp-security' ),
|
69 |
-
) );
|
70 |
-
}
|
71 |
-
|
72 |
-
$status = ITSEC_Modules::set_setting( 'file-change', 'show_warning', false );
|
73 |
-
|
74 |
-
if ( ! $status || empty( $status['saved'] ) ) {
|
75 |
-
wp_send_json_error( array(
|
76 |
-
'message' => __( 'Failed to dismiss warning.', 'better-wp-security' ),
|
77 |
-
) );
|
78 |
-
}
|
79 |
-
|
80 |
-
wp_send_json_success( array(
|
81 |
-
'message' => __( 'Warning dismissed.', 'better-wp-security' ),
|
82 |
-
) );
|
83 |
-
}
|
84 |
-
|
85 |
-
public function dismiss_file_change_warning() {
|
86 |
-
if ( empty( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'itsec-file-change-dismiss-warning' ) ) {
|
87 |
-
return;
|
88 |
-
}
|
89 |
-
|
90 |
-
ITSEC_Modules::set_setting( 'file-change', 'show_warning', false );
|
91 |
-
}
|
92 |
-
|
93 |
-
public function show_file_change_warning() {
|
94 |
-
|
95 |
-
$args = array(
|
96 |
-
'file_change_dismiss_warning' => '1',
|
97 |
-
'nonce' => $this->dismiss_nonce,
|
98 |
-
);
|
99 |
-
|
100 |
-
if ( $log_id = ITSEC_Modules::get_setting( 'file-change', 'last_scan' ) ) {
|
101 |
-
$args['id'] = $log_id;
|
102 |
-
}
|
103 |
-
|
104 |
-
$logs_url = add_query_arg( $args, ITSEC_Core::get_logs_page_url() );
|
105 |
-
$message = sprintf(
|
106 |
-
esc_html__( 'iThemes Security noticed file changes in your WordPress site. Please %1$s review the logs %2$s to make sure your system has not been compromised.', 'better-wp-security' ),
|
107 |
-
'<a href="' . esc_url( $logs_url ) . '">',
|
108 |
-
'</a>'
|
109 |
-
);
|
110 |
-
?>
|
111 |
-
<div id="itsec-file-change-warning-dialog" class="notice notice-error is-dismissible">
|
112 |
-
<p><?php echo $message; ?></p>
|
113 |
-
</div>
|
114 |
-
<?php
|
115 |
-
}
|
116 |
}
|
117 |
|
118 |
new ITSEC_File_Change_Admin();
|
2 |
|
3 |
final class ITSEC_File_Change_Admin {
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
public static function enqueue_scanner() {
|
6 |
$logs_page_url = ITSEC_Core::get_logs_page_url( 'file_change' );
|
7 |
|
16 |
'already_running' => sprintf( __( 'A scan is already in progress. Please check the <a href="%s" target="_blank" rel="noopener noreferrer">logs page</a> at a later time for the results of the scan.', 'better-wp-security' ), esc_url( $logs_page_url ) ),
|
17 |
) );
|
18 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
}
|
20 |
|
21 |
new ITSEC_File_Change_Admin();
|
core/modules/file-change/class-itsec-file-change.php
CHANGED
@@ -38,6 +38,10 @@ class ITSEC_File_Change {
|
|
38 |
add_action( 'itsec_scheduled_file-change-fast', array( $this, 'run_scan' ) );
|
39 |
ITSEC_Core::get_scheduler()->register_loop( 'file-change', ITSEC_Scheduler::S_DAILY, 60 );
|
40 |
ITSEC_Core::get_scheduler()->register_loop( 'file-change-fast', ITSEC_Scheduler::S_DAILY, 0 );
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
|
43 |
public function run_scan( $job ) {
|
@@ -175,6 +179,16 @@ class ITSEC_File_Change {
|
|
175 |
return $response;
|
176 |
}
|
177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
/**
|
179 |
* Get the latest change list.
|
180 |
*
|
38 |
add_action( 'itsec_scheduled_file-change-fast', array( $this, 'run_scan' ) );
|
39 |
ITSEC_Core::get_scheduler()->register_loop( 'file-change', ITSEC_Scheduler::S_DAILY, 60 );
|
40 |
ITSEC_Core::get_scheduler()->register_loop( 'file-change-fast', ITSEC_Scheduler::S_DAILY, 0 );
|
41 |
+
|
42 |
+
if ( ITSEC_Modules::get_setting( 'file-change', 'notify_admin' ) ) {
|
43 |
+
add_action( 'itsec_register_highlighted_logs', array( $this, 'register_highlight' ) );
|
44 |
+
}
|
45 |
}
|
46 |
|
47 |
public function run_scan( $job ) {
|
179 |
return $response;
|
180 |
}
|
181 |
|
182 |
+
/**
|
183 |
+
* Register a highlighted log whenever File Change finds changed files.
|
184 |
+
*/
|
185 |
+
public function register_highlight() {
|
186 |
+
ITSEC_Lib_Highlighted_Logs::register_dynamic_highlight( 'file-change-report', array(
|
187 |
+
'module' => 'file_change',
|
188 |
+
'code' => 'changes-found::%',
|
189 |
+
) );
|
190 |
+
}
|
191 |
+
|
192 |
/**
|
193 |
* Get the latest change list.
|
194 |
*
|
core/modules/file-change/js/script.js
DELETED
@@ -1,36 +0,0 @@
|
|
1 |
-
jQuery( document ).ready( function ( $ ) {
|
2 |
-
|
3 |
-
var $notice = $( '#itsec-file-change-warning-dialog' ),
|
4 |
-
$button = $('.notice-dismiss', $notice);
|
5 |
-
|
6 |
-
$button.off( 'click.wp-dismiss-notice' );
|
7 |
-
|
8 |
-
$button.click( function ( e ) {
|
9 |
-
e.preventDefault();
|
10 |
-
|
11 |
-
var $button = $( this );
|
12 |
-
|
13 |
-
$button.prop( 'disabled', true );
|
14 |
-
$button.css( 'opacity', .5 );
|
15 |
-
|
16 |
-
var data = {
|
17 |
-
action: itsec_file_change.ajax_action,
|
18 |
-
nonce : itsec_file_change.ajax_nonce,
|
19 |
-
};
|
20 |
-
|
21 |
-
$.post( ajaxurl, data, function ( response ) {
|
22 |
-
$button.prop( 'disabled', false );
|
23 |
-
$button.css( 'opacity', 1 );
|
24 |
-
|
25 |
-
if ( response.success ) {
|
26 |
-
$notice.fadeTo( 100, 0, function () {
|
27 |
-
$notice.slideUp( 100, function () {
|
28 |
-
$notice.remove();
|
29 |
-
} );
|
30 |
-
} );
|
31 |
-
} else {
|
32 |
-
alert( response.data.message )
|
33 |
-
}
|
34 |
-
} );
|
35 |
-
} );
|
36 |
-
} );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/file-change/logs.php
CHANGED
@@ -4,6 +4,8 @@ final class ITSEC_File_Change_Logs {
|
|
4 |
public function __construct() {
|
5 |
add_filter( 'itsec_logs_prepare_file_change_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
|
6 |
add_filter( 'itsec_logs_prepare_file_change_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
|
|
|
|
7 |
}
|
8 |
|
9 |
public function filter_entry_for_list_display( $entry, $code, $code_data ) {
|
@@ -11,9 +13,9 @@ final class ITSEC_File_Change_Logs {
|
|
11 |
|
12 |
if ( 'scan' === $code && 'process-start' === $entry['type'] ) {
|
13 |
$entry['description'] = esc_html__( 'Scan Performance', 'better-wp-security' );
|
14 |
-
}
|
15 |
$entry['description'] = esc_html__( 'No Changes Found', 'better-wp-security' );
|
16 |
-
}
|
17 |
if ( isset( $code_data[0] ) ) {
|
18 |
$entry['description'] = sprintf( esc_html__( '%1$d Added, %2$d Removed, %3$d Changed', 'better-wp-security' ), $code_data[0], $code_data[1], $code_data[2] );
|
19 |
} else {
|
@@ -44,7 +46,7 @@ final class ITSEC_File_Change_Logs {
|
|
44 |
} elseif ( 'recovery-scheduled' === $code ) {
|
45 |
$entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
|
46 |
} elseif ( 'file-scan-aborted' === $code ) {
|
47 |
-
if
|
48 |
if ( $user = get_userdata( $code_data[0] ) ) {
|
49 |
$by = $user->display_name;
|
50 |
} else {
|
@@ -71,7 +73,7 @@ final class ITSEC_File_Change_Logs {
|
|
71 |
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
72 |
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
73 |
|
74 |
-
$details['module']['content']
|
75 |
$details['description']['content'] = $entry['description'];
|
76 |
|
77 |
if ( 'changes-found' === $code || 'no-changes-found' === $code ) {
|
@@ -94,9 +96,9 @@ final class ITSEC_File_Change_Logs {
|
|
94 |
);
|
95 |
|
96 |
foreach ( $types as $type => $header ) {
|
97 |
-
$details[$type] = array(
|
98 |
'header' => $header,
|
99 |
-
'content' => '<pre>' . implode( "\n", array_keys( $entry['data'][$type] ) ) . '</pre>',
|
100 |
);
|
101 |
}
|
102 |
}
|
@@ -105,5 +107,18 @@ final class ITSEC_File_Change_Logs {
|
|
105 |
|
106 |
return $details;
|
107 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
}
|
|
|
109 |
new ITSEC_File_Change_Logs();
|
4 |
public function __construct() {
|
5 |
add_filter( 'itsec_logs_prepare_file_change_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
|
6 |
add_filter( 'itsec_logs_prepare_file_change_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
add_filter( 'itsec_highlighted_log_file-change-report_notice_title', array( $this, 'filter_highlight_title' ), 10, 2 );
|
8 |
+
add_filter( 'itsec_highlighted_log_file-change-report_notice_message', array( $this, 'filter_highlight_message' ), 10, 2 );
|
9 |
}
|
10 |
|
11 |
public function filter_entry_for_list_display( $entry, $code, $code_data ) {
|
13 |
|
14 |
if ( 'scan' === $code && 'process-start' === $entry['type'] ) {
|
15 |
$entry['description'] = esc_html__( 'Scan Performance', 'better-wp-security' );
|
16 |
+
} elseif ( 'no-changes-found' === $code ) {
|
17 |
$entry['description'] = esc_html__( 'No Changes Found', 'better-wp-security' );
|
18 |
+
} elseif ( 'changes-found' === $code ) {
|
19 |
if ( isset( $code_data[0] ) ) {
|
20 |
$entry['description'] = sprintf( esc_html__( '%1$d Added, %2$d Removed, %3$d Changed', 'better-wp-security' ), $code_data[0], $code_data[1], $code_data[2] );
|
21 |
} else {
|
46 |
} elseif ( 'recovery-scheduled' === $code ) {
|
47 |
$entry['description'] = esc_html__( 'Recovery Scheduled', 'better-wp-security' );
|
48 |
} elseif ( 'file-scan-aborted' === $code ) {
|
49 |
+
if ( ! empty( $code_data[0] ) ) {
|
50 |
if ( $user = get_userdata( $code_data[0] ) ) {
|
51 |
$by = $user->display_name;
|
52 |
} else {
|
73 |
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
74 |
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
75 |
|
76 |
+
$details['module']['content'] = $entry['module_display'];
|
77 |
$details['description']['content'] = $entry['description'];
|
78 |
|
79 |
if ( 'changes-found' === $code || 'no-changes-found' === $code ) {
|
96 |
);
|
97 |
|
98 |
foreach ( $types as $type => $header ) {
|
99 |
+
$details[ $type ] = array(
|
100 |
'header' => $header,
|
101 |
+
'content' => '<pre>' . implode( "\n", array_keys( $entry['data'][ $type ] ) ) . '</pre>',
|
102 |
);
|
103 |
}
|
104 |
}
|
107 |
|
108 |
return $details;
|
109 |
}
|
110 |
+
|
111 |
+
public function filter_highlight_title( $title, $entry ) {
|
112 |
+
return esc_html__( 'iThemes Security noticed file changes in your WordPress site.', 'better-wp-security' );
|
113 |
+
}
|
114 |
+
|
115 |
+
public function filter_highlight_message( $title, $entry ) {
|
116 |
+
return sprintf(
|
117 |
+
esc_html__( 'Please %1$sreview the logs%2$s to make sure your system has not been compromised.', 'better-wp-security' ),
|
118 |
+
'<a href="{{ $view }}">',
|
119 |
+
'</a>'
|
120 |
+
);
|
121 |
+
}
|
122 |
}
|
123 |
+
|
124 |
new ITSEC_File_Change_Logs();
|
core/modules/file-change/scanner.php
CHANGED
@@ -159,9 +159,9 @@ class ITSEC_File_Change_Scanner {
|
|
159 |
* Is there a scan scheduled.
|
160 |
*
|
161 |
* @param ITSEC_Scheduler $scheduler The scheduler to use.
|
162 |
-
* @param bool $user_initiated Whether the user initiated scan is running or the scheduled loop scan.
|
163 |
* Null to check either.
|
164 |
-
*
|
165 |
* @return bool Is it scheduled.
|
166 |
*/
|
167 |
private static function is_scheduled( $scheduler, $user_initiated = null ) {
|
@@ -796,10 +796,6 @@ class ITSEC_File_Change_Scanner {
|
|
796 |
ITSEC_Modules::set_setting( 'file-change', 'last_scan', $found_changes ? $id : 0 );
|
797 |
update_site_option( 'itsec_file_change_latest', $list );
|
798 |
|
799 |
-
if ( $found_changes && $this->settings['notify_admin'] ) {
|
800 |
-
ITSEC_Modules::set_setting( 'file-change', 'show_warning', true );
|
801 |
-
}
|
802 |
-
|
803 |
if ( $process = $storage->get( 'process' ) ) {
|
804 |
ITSEC_Log::add_process_stop( $process );
|
805 |
}
|
@@ -1144,4 +1140,4 @@ class ITSEC_File_Change_Scanner {
|
|
1144 |
|
1145 |
return $rows;
|
1146 |
}
|
1147 |
-
}
|
159 |
* Is there a scan scheduled.
|
160 |
*
|
161 |
* @param ITSEC_Scheduler $scheduler The scheduler to use.
|
162 |
+
* @param bool $user_initiated Whether the user initiated scan is running or the scheduled loop scan.
|
163 |
* Null to check either.
|
164 |
+
*
|
165 |
* @return bool Is it scheduled.
|
166 |
*/
|
167 |
private static function is_scheduled( $scheduler, $user_initiated = null ) {
|
796 |
ITSEC_Modules::set_setting( 'file-change', 'last_scan', $found_changes ? $id : 0 );
|
797 |
update_site_option( 'itsec_file_change_latest', $list );
|
798 |
|
|
|
|
|
|
|
|
|
799 |
if ( $process = $storage->get( 'process' ) ) {
|
800 |
ITSEC_Log::add_process_stop( $process );
|
801 |
}
|
1140 |
|
1141 |
return $rows;
|
1142 |
}
|
1143 |
+
}
|
core/modules/file-change/settings-page.php
CHANGED
@@ -132,14 +132,6 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
132 |
<label for="itsec-file-change-types"><?php _e( 'File types listed here will not be checked for changes. While it is possible to change files such as images it is quite rare and nearly all known WordPress attacks exploit php, js and other text files.', 'better-wp-security' ); ?></label>
|
133 |
</td>
|
134 |
</tr>
|
135 |
-
<tr>
|
136 |
-
<th scope="row"><label for="itsec-file-change-notify_admin"><?php _e( 'Display File Change Admin Warning', 'better-wp-security' ); ?></label></th>
|
137 |
-
<td>
|
138 |
-
<?php $form->add_checkbox( 'notify_admin' ); ?>
|
139 |
-
<label for="itsec-file-change-notify_admin"><?php _e( 'Display file change admin warning', 'better-wp-security' ); ?></label>
|
140 |
-
<p class="description"><?php _e( 'Disabling this feature will prevent the file change warning from displaying to the site administrator in the WordPress Dashboard. Note that disabling both the error message and the email notification will result in no notifications of file changes. The only way you will be able to tell is by manually checking the log files.', 'better-wp-security' ); ?></p>
|
141 |
-
</td>
|
142 |
-
</tr>
|
143 |
<?php do_action( 'itsec-file-change-settings-form', $form ); ?>
|
144 |
</table>
|
145 |
<?php
|
132 |
<label for="itsec-file-change-types"><?php _e( 'File types listed here will not be checked for changes. While it is possible to change files such as images it is quite rare and nearly all known WordPress attacks exploit php, js and other text files.', 'better-wp-security' ); ?></label>
|
133 |
</td>
|
134 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
<?php do_action( 'itsec-file-change-settings-form', $form ); ?>
|
136 |
</table>
|
137 |
<?php
|
core/modules/file-change/settings.php
CHANGED
@@ -19,8 +19,6 @@ final class ITSEC_File_Change_Settings extends ITSEC_Settings {
|
|
19 |
// Video
|
20 |
'.asf', '.avi', '.mkv', '.mov', '.mp4', '.mpe', '.mpeg', '.mpg', '.ogv', '.qt', '.rm', '.vob', '.webm', '.wm', '.wmv',
|
21 |
),
|
22 |
-
'notify_admin' => true,
|
23 |
-
'show_warning' => false,
|
24 |
'expected_hashes' => array(),
|
25 |
'last_scan' => 0,
|
26 |
);
|
19 |
// Video
|
20 |
'.asf', '.avi', '.mkv', '.mov', '.mp4', '.mpe', '.mpeg', '.mpg', '.ogv', '.qt', '.rm', '.vob', '.webm', '.wm', '.wmv',
|
21 |
),
|
|
|
|
|
22 |
'expected_hashes' => array(),
|
23 |
'last_scan' => 0,
|
24 |
);
|
core/modules/file-change/setup.php
CHANGED
@@ -317,6 +317,13 @@ if ( ! class_exists( 'ITSEC_File_Change_Setup' ) ) {
|
|
317 |
ITSEC_File_Change::make_progress_storage()->clear();
|
318 |
ITSEC_File_Change_Scanner::schedule_start( false );
|
319 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
}
|
321 |
|
322 |
/**
|
317 |
ITSEC_File_Change::make_progress_storage()->clear();
|
318 |
ITSEC_File_Change_Scanner::schedule_start( false );
|
319 |
}
|
320 |
+
|
321 |
+
if ( $itsec_old_version > 4114 ) {
|
322 |
+
if ( ! ITSEC_Modules::get_setting( 'file-change', 'notify_admin' ) ) {
|
323 |
+
ITSEC_Lib::load( 'highlighted-logs' );
|
324 |
+
ITSEC_Lib_Highlighted_Logs::mute( 'file-change-report' );
|
325 |
+
}
|
326 |
+
}
|
327 |
}
|
328 |
|
329 |
/**
|
core/modules/file-change/validator.php
CHANGED
@@ -9,13 +9,12 @@ class ITSEC_File_Change_Validator extends ITSEC_Validator {
|
|
9 |
|
10 |
unset( $this->settings['latest_changes'] );
|
11 |
|
12 |
-
$this->set_previous_if_empty( array( '
|
13 |
-
$this->preserve_setting_if_exists( array( 'email', 'split', 'last_run', 'last_chunk', 'method' ) );
|
14 |
-
$this->vars_to_skip_validate_matching_fields = array( 'email', 'split', 'last_run', 'last_chunk', 'method', 'latest_changes' );
|
15 |
|
16 |
$this->sanitize_setting( 'newline-separated-array', 'file_list', __( 'Files and Folders List', 'better-wp-security' ) );
|
17 |
$this->sanitize_setting( 'newline-separated-extensions', 'types', __( 'Ignore File Types', 'better-wp-security' ) );
|
18 |
-
$this->sanitize_setting( 'bool', 'notify_admin', __( 'Display File Change Admin Warning', 'better-wp-security' ) );
|
19 |
|
20 |
$this->settings = apply_filters( 'itsec-file-change-sanitize-settings', $this->settings );
|
21 |
}
|
9 |
|
10 |
unset( $this->settings['latest_changes'] );
|
11 |
|
12 |
+
$this->set_previous_if_empty( array( 'expected_hashes', 'last_scan' ) );
|
13 |
+
$this->preserve_setting_if_exists( array( 'email', 'split', 'last_run', 'last_chunk', 'method', 'notify_admin' ) );
|
14 |
+
$this->vars_to_skip_validate_matching_fields = array( 'email', 'split', 'last_run', 'last_chunk', 'method', 'latest_changes', 'show_warning', 'notify_admin' );
|
15 |
|
16 |
$this->sanitize_setting( 'newline-separated-array', 'file_list', __( 'Files and Folders List', 'better-wp-security' ) );
|
17 |
$this->sanitize_setting( 'newline-separated-extensions', 'types', __( 'Ignore File Types', 'better-wp-security' ) );
|
|
|
18 |
|
19 |
$this->settings = apply_filters( 'itsec-file-change-sanitize-settings', $this->settings );
|
20 |
}
|
core/modules/global/active.php
CHANGED
@@ -3,69 +3,7 @@
|
|
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 |
-
|
7 |
-
|
8 |
-
|
9 |
-
function itsec_global_add_notice() {
|
10 |
-
|
11 |
-
if ( ! defined( 'ITSEC_USE_CRON' ) && ITSEC_Core::current_user_can_manage() ) {
|
12 |
-
ITSEC_Core::add_notice( 'itsec_show_disable_cron_constants_notice' );
|
13 |
-
}
|
14 |
-
|
15 |
-
if ( ITSEC_Core::is_temp_disable_modules_set() && ITSEC_Core::current_user_can_manage() ) {
|
16 |
-
ITSEC_Core::add_notice( 'itsec_show_temp_disable_modules_notice', true );
|
17 |
-
}
|
18 |
-
|
19 |
-
}
|
20 |
-
add_action( 'admin_init', 'itsec_global_add_notice', 0 );
|
21 |
-
|
22 |
-
function itsec_network_brute_force_add_notice() {
|
23 |
-
if ( ITSEC_Modules::get_setting( 'network-brute-force', 'api_nag' ) && current_user_can( ITSEC_Core::get_required_cap() ) ) {
|
24 |
-
ITSEC_Core::add_notice( 'itsec_network_brute_force_show_notice' );
|
25 |
-
}
|
26 |
-
}
|
27 |
-
add_action( 'admin_init', 'itsec_network_brute_force_add_notice' );
|
28 |
-
|
29 |
-
function itsec_network_brute_force_show_notice() {
|
30 |
-
echo '<div id="itsec-notice-network-brute-force" class="updated itsec-notice"><span class="it-icon-itsec"></span>'
|
31 |
-
. __( 'New! Take your site security to the next level by activating iThemes Brute Force Network Protection.', 'better-wp-security' )
|
32 |
-
. '<a class="itsec-notice-button" href="' . esc_url( wp_nonce_url( add_query_arg( array( 'module' => 'network-brute-force', 'enable' => 'network-brute-force' ), ITSEC_Core::get_settings_page_url() ), 'itsec-enable-network-brute-force', 'itsec-enable-nonce' ) ) . '" onclick="document.location.href=\'?itsec_no_api_nag=off&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">' . __( 'Get Free API Key', 'better-wp-security' ) . '</a>'
|
33 |
-
. '<button class="itsec-notice-hide" data-nonce="' . wp_create_nonce( 'dismiss-brute-force-network-notice' ) . '" data-source="brute_force_network">×</button>'
|
34 |
-
. '</div>';
|
35 |
-
}
|
36 |
-
|
37 |
-
function itsec_network_brute_force_dismiss_notice() {
|
38 |
-
if ( wp_verify_nonce( $_REQUEST['notice_nonce'], 'dismiss-brute-force-network-notice' ) ) {
|
39 |
-
ITSEC_Modules::set_setting( 'network-brute-force', 'api_nag', false );
|
40 |
-
wp_send_json_success();
|
41 |
-
}
|
42 |
-
wp_send_json_error();
|
43 |
-
}
|
44 |
-
add_action( 'wp_ajax_itsec-dismiss-notice-brute_force_network', 'itsec_network_brute_force_dismiss_notice' );
|
45 |
-
|
46 |
-
function itsec_show_temp_disable_modules_notice() {
|
47 |
-
ITSEC_Lib::show_error_message( esc_html__( 'The ITSEC_DISABLE_MODULES define is set. All iThemes Security protections are disabled. Please make the necessary settings changes and remove the define as quickly as possible.', 'better-wp-security' ) );
|
48 |
-
}
|
49 |
-
|
50 |
-
function itsec_show_disable_cron_constants_notice() {
|
51 |
-
|
52 |
-
$check = array( 'ITSEC_BACKUP_CRON', 'ITSEC_FILE_CHECK_CRON' );
|
53 |
-
$using = array();
|
54 |
-
|
55 |
-
foreach ( $check as $constant ) {
|
56 |
-
if ( defined( $constant ) && constant( $constant ) ) {
|
57 |
-
$using[] = "<span class='code'>{$constant}</span>";
|
58 |
-
}
|
59 |
-
}
|
60 |
-
|
61 |
-
if ( $using ) {
|
62 |
-
$message = wp_sprintf( esc_html(
|
63 |
-
_n( 'The %l define is deprecated. Please use %s instead.', 'The %l defines are deprecated. Please use %s instead.', count( $using ), 'better-wp-security' )
|
64 |
-
), $using, '<span class="code">ITSEC_USE_CRON</span>' );
|
65 |
-
|
66 |
-
echo "<div class='notice notice-error'><p>{$message}</p></div>";
|
67 |
-
}
|
68 |
-
}
|
69 |
|
70 |
/**
|
71 |
* On every page load, check if the cron test has successfully fired in time.
|
@@ -176,4 +114,4 @@ function itsec_basename_attachment_thumbs( $data ) {
|
|
176 |
return $data;
|
177 |
}
|
178 |
|
179 |
-
add_filter( 'wp_update_attachment_metadata', 'itsec_basename_attachment_thumbs' );
|
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 |
/**
|
9 |
* On every page load, check if the cron test has successfully fired in time.
|
114 |
return $data;
|
115 |
}
|
116 |
|
117 |
+
add_filter( 'wp_update_attachment_metadata', 'itsec_basename_attachment_thumbs' );
|
core/modules/global/notices.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Admin_Notice_Network_Brute_Force_Promo implements ITSEC_Admin_Notice {
|
4 |
+
public function get_id() {
|
5 |
+
return 'network-brute-force-promo';
|
6 |
+
}
|
7 |
+
|
8 |
+
public function get_title() {
|
9 |
+
return '';
|
10 |
+
}
|
11 |
+
|
12 |
+
public function get_message() {
|
13 |
+
return esc_html__( 'New! Take your site security to the next level by activating iThemes Brute Force Network Protection.', 'better-wp-security' );
|
14 |
+
}
|
15 |
+
|
16 |
+
public function get_severity() {
|
17 |
+
return self::S_INFO;
|
18 |
+
}
|
19 |
+
|
20 |
+
public function get_meta() {
|
21 |
+
return array();
|
22 |
+
}
|
23 |
+
|
24 |
+
public function show_for_context( ITSEC_Admin_Notice_Context $context ) {
|
25 |
+
return true;
|
26 |
+
}
|
27 |
+
|
28 |
+
public function get_actions() {
|
29 |
+
$url = ITSEC_Core::get_settings_module_url( 'network-brute-force' );
|
30 |
+
|
31 |
+
if ( ! ITSEC_Modules::is_active( 'network-brute-force' ) ) {
|
32 |
+
$url = add_query_arg( 'enable', 'network-brute-force', $url );
|
33 |
+
$url = wp_nonce_url( $url, 'itsec-enable-network-brute-force', 'itsec-enable-nonce' );
|
34 |
+
}
|
35 |
+
|
36 |
+
return array(
|
37 |
+
'register' => new ITSEC_Admin_Notice_Action_Link(
|
38 |
+
$url,
|
39 |
+
esc_html__( 'Get Free API Key', 'better-wp-security' ),
|
40 |
+
ITSEC_Admin_Notice_Action::S_PRIMARY
|
41 |
+
),
|
42 |
+
);
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
if ( ITSEC_Core::is_temp_disable_modules_set() ) {
|
47 |
+
ITSEC_Lib_Admin_Notices::register(
|
48 |
+
new ITSEC_Admin_Notice_Managers_Only(
|
49 |
+
new ITSEC_Admin_Notice_Static(
|
50 |
+
'disable-modules', esc_html__( 'The ITSEC_DISABLE_MODULES define is set. All iThemes Security protections are disabled. Please make the necessary settings changes and remove the define as quickly as possible.', 'better-wp-security' ), '', ITSEC_Admin_Notice::S_WARN
|
51 |
+
)
|
52 |
+
)
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
if ( ! ITSEC_Modules::is_active( 'network-brute-force' ) || ! ITSEC_Modules::get_setting( 'network-brute-force', 'api_secret' ) ) {
|
57 |
+
ITSEC_Lib_Admin_Notices::register(
|
58 |
+
new ITSEC_Admin_Notice_Globally_Dismissible(
|
59 |
+
new ITSEC_Admin_Notice_Managers_Only(
|
60 |
+
new ITSEC_Admin_Notice_Network_Brute_Force_Promo()
|
61 |
+
)
|
62 |
+
)
|
63 |
+
);
|
64 |
+
}
|
core/modules/global/settings-page.php
CHANGED
@@ -300,6 +300,7 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
300 |
<td>
|
301 |
<?php $form->add_checkbox( 'hide_admin_bar' ); ?>
|
302 |
<label for="itsec-global-hide_admin_bar"><?php _e( 'Hide security menu in admin bar.', 'better-wp-security' ); ?></label>
|
|
|
303 |
</td>
|
304 |
</tr>
|
305 |
<tr>
|
300 |
<td>
|
301 |
<?php $form->add_checkbox( 'hide_admin_bar' ); ?>
|
302 |
<label for="itsec-global-hide_admin_bar"><?php _e( 'Hide security menu in admin bar.', 'better-wp-security' ); ?></label>
|
303 |
+
<p class="description"><?php esc_html_e( 'Remove the Security Messages Menu from the admin bar and receive the messages as traditional WordPress Admin Notices.', 'better-wp-security' ); ?></p>
|
304 |
</td>
|
305 |
</tr>
|
306 |
<tr>
|
core/modules/global/settings.php
CHANGED
@@ -39,6 +39,7 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
|
|
39 |
'cron_test_time' => 0,
|
40 |
'enable_grade_report' => false,
|
41 |
'server_ips' => array(),
|
|
|
42 |
);
|
43 |
}
|
44 |
|
39 |
'cron_test_time' => 0,
|
40 |
'enable_grade_report' => false,
|
41 |
'server_ips' => array(),
|
42 |
+
'feature_flags' => array(),
|
43 |
);
|
44 |
}
|
45 |
|
core/modules/global/validator.php
CHANGED
@@ -19,8 +19,8 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
|
|
19 |
}
|
20 |
|
21 |
|
22 |
-
$this->vars_to_skip_validate_matching_fields = array( 'digest_last_sent', 'digest_messages', 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'show_new_dashboard_notice', 'proxy_override', 'proxy', 'proxy_header', 'server_ips', 'initial_build' );
|
23 |
-
$this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_security_check', 'build', 'activation_timestamp', 'lock_file', 'cron_status', 'use_cron', 'cron_test_time', 'proxy', 'proxy_header', 'server_ips', 'initial_build' ) );
|
24 |
$this->set_default_if_empty( array( 'log_location', 'nginx_file', 'enable_grade_report' ) );
|
25 |
$this->preserve_setting_if_exists( array( 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'proxy_override' ) );
|
26 |
|
@@ -60,6 +60,7 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
|
|
60 |
$this->settings['community_lockout_message'] = trim( wp_kses( $this->settings['community_lockout_message'], $allowed_tags ) );
|
61 |
|
62 |
$this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
|
|
|
63 |
}
|
64 |
|
65 |
public function get_proxy_types() {
|
19 |
}
|
20 |
|
21 |
|
22 |
+
$this->vars_to_skip_validate_matching_fields = array( 'digest_last_sent', 'digest_messages', 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'show_new_dashboard_notice', 'proxy_override', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' );
|
23 |
+
$this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_security_check', 'build', 'activation_timestamp', 'lock_file', 'cron_status', 'use_cron', 'cron_test_time', 'proxy', 'proxy_header', 'server_ips', 'initial_build', 'feature_flags' ) );
|
24 |
$this->set_default_if_empty( array( 'log_location', 'nginx_file', 'enable_grade_report' ) );
|
25 |
$this->preserve_setting_if_exists( array( 'digest_email', 'email_notifications', 'notification_email', 'backup_email', 'proxy_override' ) );
|
26 |
|
60 |
$this->settings['community_lockout_message'] = trim( wp_kses( $this->settings['community_lockout_message'], $allowed_tags ) );
|
61 |
|
62 |
$this->sanitize_setting( 'newline-separated-ips', 'server_ips', __( 'Server IPs', 'better-wp-security' ) );
|
63 |
+
$this->sanitize_setting( 'array', 'feature_flags', __( 'Feature Flags', 'better-wp-security' ) );
|
64 |
}
|
65 |
|
66 |
public function get_proxy_types() {
|
core/modules/ipcheck/active.php
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<?php
|
2 |
|
3 |
require_once( 'class-itsec-ipcheck.php' );
|
4 |
-
$itsec_ip_check = new ITSEC_IPCheck(
|
5 |
$itsec_ip_check->run();
|
1 |
<?php
|
2 |
|
3 |
require_once( 'class-itsec-ipcheck.php' );
|
4 |
+
$itsec_ip_check = new ITSEC_IPCheck();
|
5 |
$itsec_ip_check->run();
|
core/modules/ipcheck/class-itsec-ipcheck.php
CHANGED
@@ -5,25 +5,16 @@
|
|
5 |
*
|
6 |
* Provides static calls to the iThemes IPCheck API
|
7 |
*
|
8 |
-
* @package iThemes_Security
|
9 |
-
*
|
10 |
* @since 4.5
|
11 |
*
|
|
|
|
|
12 |
*/
|
13 |
class ITSEC_IPCheck {
|
14 |
-
private $endpoint = 'http://ipcheck-api.ithemes.com/?action=';
|
15 |
-
private $settings;
|
16 |
-
|
17 |
public function run() {
|
18 |
add_filter( 'authenticate', array( $this, 'filter_authenticate' ), 10000, 3 ); // Set a very late priority so that we run after actual authentication takes place.
|
19 |
}
|
20 |
|
21 |
-
private function load_settings() {
|
22 |
-
if ( ! isset( $this->settings ) ) {
|
23 |
-
$this->settings = ITSEC_Modules::get_settings( 'network-brute-force' );
|
24 |
-
}
|
25 |
-
}
|
26 |
-
|
27 |
public function filter_authenticate( $user, $username, $password ) {
|
28 |
/** @var $itsec_lockout ITSEC_Lockout */
|
29 |
global $itsec_lockout;
|
@@ -33,187 +24,20 @@ class ITSEC_IPCheck {
|
|
33 |
return $user;
|
34 |
}
|
35 |
|
36 |
-
|
|
|
|
|
37 |
|
38 |
if ( is_wp_error( $user ) || null == $user ) {
|
39 |
-
if (
|
40 |
ITSEC_Log::add_notice( 'ipcheck', 'failed-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
41 |
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
42 |
}
|
43 |
-
}
|
44 |
ITSEC_Log::add_critical_issue( 'ipcheck', 'successful-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
45 |
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
46 |
}
|
47 |
|
48 |
return $user;
|
49 |
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Check visitor IP to see if it is banned by IPCheck.
|
53 |
-
*
|
54 |
-
* @since 3.0.0
|
55 |
-
*
|
56 |
-
* @return bool true if banned, false otherwise.
|
57 |
-
*/
|
58 |
-
private function is_ip_banned() {
|
59 |
-
return $this->get_server_response( 'check-ip' );
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Report visitor IP for blacklistable-offense to IPCheck.
|
64 |
-
*
|
65 |
-
* @since 3.0.0
|
66 |
-
*
|
67 |
-
* @return bool true if banned, false otherwise.
|
68 |
-
*/
|
69 |
-
private function report_ip() {
|
70 |
-
return $this->get_server_response( 'report-ip' );
|
71 |
-
}
|
72 |
-
|
73 |
-
private function get_server_response( $action ) {
|
74 |
-
$this->load_settings();
|
75 |
-
|
76 |
-
if ( empty( $this->settings['api_key'] ) || empty( $this->settings['api_secret'] ) ) {
|
77 |
-
return false;
|
78 |
-
}
|
79 |
-
|
80 |
-
|
81 |
-
$ip = ITSEC_Lib::get_ip();
|
82 |
-
|
83 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
84 |
-
|
85 |
-
if ( ! ITSEC_Lib_IP_Tools::validate( $ip ) || ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
|
86 |
-
return false;
|
87 |
-
}
|
88 |
-
|
89 |
-
|
90 |
-
$cache = $this->get_cache( $ip );
|
91 |
-
|
92 |
-
if ( 'check-ip' === $action ) {
|
93 |
-
if ( $cache['cache_ttl'] >= ITSEC_Core::get_current_time_gmt() ) {
|
94 |
-
return $cache['block'];
|
95 |
-
}
|
96 |
-
} else if ( 'report-ip' === $action ) {
|
97 |
-
if ( $cache['report_ttl'] >= ITSEC_Core::get_current_time_gmt() ) {
|
98 |
-
return $cache['block'];
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
|
103 |
-
$args = json_encode(
|
104 |
-
array(
|
105 |
-
'apikey' => $this->settings['api_key'],
|
106 |
-
'behavior' => 'brute-force-login',
|
107 |
-
'ip' => $ip,
|
108 |
-
'site' => home_url( '', 'http' ),
|
109 |
-
'timestamp' => ITSEC_Core::get_current_time_gmt(),
|
110 |
-
)
|
111 |
-
);
|
112 |
-
|
113 |
-
$request = array(
|
114 |
-
'body' => array(
|
115 |
-
'request' => $args,
|
116 |
-
'signature' => $this->hmac_sha1( $this->settings['api_secret'], $action . $args ),
|
117 |
-
),
|
118 |
-
);
|
119 |
-
|
120 |
-
|
121 |
-
$response = wp_remote_post( $this->endpoint . $action, $request );
|
122 |
-
|
123 |
-
if ( is_wp_error( $response ) || ! isset( $response['body'] ) ) {
|
124 |
-
return false;
|
125 |
-
}
|
126 |
-
|
127 |
-
|
128 |
-
$response = json_decode( $response['body'], true );
|
129 |
-
|
130 |
-
if ( ! is_array( $response ) || ! isset( $response['success'] ) || ! $response['success'] ) {
|
131 |
-
return false;
|
132 |
-
}
|
133 |
-
|
134 |
-
|
135 |
-
$this->set_cache( $ip, $response );
|
136 |
-
|
137 |
-
$cache_seconds = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
|
138 |
-
|
139 |
-
if ( isset( $response['block'] ) && $response['block'] ) {
|
140 |
-
$data = array(
|
141 |
-
'expires' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() + $cache_seconds ),
|
142 |
-
'expires_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() + $cache_seconds ),
|
143 |
-
'type' => 'host',
|
144 |
-
);
|
145 |
-
|
146 |
-
ITSEC_Log::add_action( 'ipcheck', 'ip-blocked', $data );
|
147 |
-
|
148 |
-
return true;
|
149 |
-
}
|
150 |
-
|
151 |
-
return false;
|
152 |
-
}
|
153 |
-
|
154 |
-
private function set_cache( $ip, $response ) {
|
155 |
-
$cache = $this->get_cache( $ip );
|
156 |
-
$time = ITSEC_Core::get_current_time_gmt();
|
157 |
-
|
158 |
-
if ( isset( $response['block'] ) ) {
|
159 |
-
$cache['block'] = (boolean) $response['block'];
|
160 |
-
}
|
161 |
-
|
162 |
-
if ( isset( $response['cache_ttl'] ) ) {
|
163 |
-
$cache['cache_ttl'] = intval( $response['cache_ttl'] ) + $time;
|
164 |
-
} else if ( 0 === $cache['cache_ttl'] ) {
|
165 |
-
$cache['cache_ttl'] = $time + HOUR_IN_SECONDS;
|
166 |
-
}
|
167 |
-
|
168 |
-
if ( isset( $response['report_ttl'] ) ) {
|
169 |
-
$cache['report_ttl'] = intval( $response['report_ttl'] ) + $time;
|
170 |
-
}
|
171 |
-
|
172 |
-
$transient_time = max( $cache['cache_ttl'], $cache['report_ttl'] ) - $time;
|
173 |
-
|
174 |
-
|
175 |
-
set_site_transient( "itsec_ipcheck_$ip", $cache, $transient_time );
|
176 |
-
}
|
177 |
-
|
178 |
-
private function get_cache( $ip ) {
|
179 |
-
$cache = get_site_transient( "itsec_ipcheck_$ip" );
|
180 |
-
|
181 |
-
$defaults = array(
|
182 |
-
'block' => false,
|
183 |
-
'cache_ttl' => 0,
|
184 |
-
'report_ttl' => 0,
|
185 |
-
);
|
186 |
-
|
187 |
-
if ( ! is_array( $cache ) ) {
|
188 |
-
return $defaults;
|
189 |
-
}
|
190 |
-
|
191 |
-
return array_merge( $defaults, $cache );
|
192 |
-
}
|
193 |
-
|
194 |
-
/**
|
195 |
-
* Calculates the HMAC of a string using SHA1.
|
196 |
-
*
|
197 |
-
* there is a native PHP hmac function, but we use this one for
|
198 |
-
* the widest compatibility with older PHP versions
|
199 |
-
*
|
200 |
-
* @param string $key the shared secret key used to generate the mac
|
201 |
-
* @param string $data data to be signed
|
202 |
-
*
|
203 |
-
*
|
204 |
-
* @return string base64 encoded hmac
|
205 |
-
*/
|
206 |
-
private function hmac_sha1( $key, $data ) {
|
207 |
-
if ( strlen( $key ) > 64 ) {
|
208 |
-
$key = pack( 'H*', sha1( $key ) );
|
209 |
-
}
|
210 |
-
|
211 |
-
$key = str_pad( $key, 64, chr( 0x00 ) );
|
212 |
-
$ipad = str_repeat( chr( 0x36 ), 64 );
|
213 |
-
$opad = str_repeat( chr( 0x5c ), 64 );
|
214 |
-
$hmac = pack( 'H*', sha1( ( $key ^ $opad ) . pack( 'H*', sha1( ( $key ^ $ipad ) . $data ) ) ) );
|
215 |
-
|
216 |
-
return base64_encode( $hmac );
|
217 |
-
}
|
218 |
-
|
219 |
}
|
5 |
*
|
6 |
* Provides static calls to the iThemes IPCheck API
|
7 |
*
|
|
|
|
|
8 |
* @since 4.5
|
9 |
*
|
10 |
+
* @package iThemes_Security
|
11 |
+
*
|
12 |
*/
|
13 |
class ITSEC_IPCheck {
|
|
|
|
|
|
|
14 |
public function run() {
|
15 |
add_filter( 'authenticate', array( $this, 'filter_authenticate' ), 10000, 3 ); // Set a very late priority so that we run after actual authentication takes place.
|
16 |
}
|
17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
public function filter_authenticate( $user, $username, $password ) {
|
19 |
/** @var $itsec_lockout ITSEC_Lockout */
|
20 |
global $itsec_lockout;
|
24 |
return $user;
|
25 |
}
|
26 |
|
27 |
+
require_once( dirname( __FILE__ ) . '/utilities.php' );
|
28 |
+
|
29 |
+
$enable_ban = ITSEC_Modules::get_setting( 'network-brute-force', 'enable_ban' );
|
30 |
|
31 |
if ( is_wp_error( $user ) || null == $user ) {
|
32 |
+
if ( ITSEC_Network_Brute_Force_Utilities::report_ip() && $enable_ban ) {
|
33 |
ITSEC_Log::add_notice( 'ipcheck', 'failed-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
34 |
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
35 |
}
|
36 |
+
} elseif ( $enable_ban && ITSEC_Network_Brute_Force_Utilities::is_ip_banned() ) {
|
37 |
ITSEC_Log::add_critical_issue( 'ipcheck', 'successful-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
38 |
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
39 |
}
|
40 |
|
41 |
return $user;
|
42 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
}
|
core/modules/ipcheck/settings-page.php
CHANGED
@@ -50,6 +50,7 @@ final class ITSEC_Network_Brute_Force_Settings_Page extends ITSEC_Module_Setting
|
|
50 |
}
|
51 |
|
52 |
protected function render_settings( $form ) {
|
|
|
53 |
$settings = $form->get_options();
|
54 |
|
55 |
?>
|
@@ -72,7 +73,7 @@ final class ITSEC_Network_Brute_Force_Settings_Page extends ITSEC_Module_Setting
|
|
72 |
<th scope="row"><label for="itsec-network-brute-force-updates_optin"><?php _e( 'Receive Email Updates', 'better-wp-security' ); ?></label></th>
|
73 |
<td>
|
74 |
<?php $form->add_checkbox( 'updates_optin' ); ?>
|
75 |
-
<label for="itsec-network-brute-force-updates_optin"><?php _e( 'Receive email updates about WordPress Security from iThemes.', 'better-wp-security' ); ?></label>
|
76 |
</td>
|
77 |
</tr>
|
78 |
</table>
|
50 |
}
|
51 |
|
52 |
protected function render_settings( $form ) {
|
53 |
+
$form->set_options( array( 'updates_optin' => false ) );
|
54 |
$settings = $form->get_options();
|
55 |
|
56 |
?>
|
73 |
<th scope="row"><label for="itsec-network-brute-force-updates_optin"><?php _e( 'Receive Email Updates', 'better-wp-security' ); ?></label></th>
|
74 |
<td>
|
75 |
<?php $form->add_checkbox( 'updates_optin' ); ?>
|
76 |
+
<label for="itsec-network-brute-force-updates_optin"><?php _e( 'Receive email updates about WordPress Security and marketing news from iThemes.', 'better-wp-security' ); ?></label>
|
77 |
</td>
|
78 |
</tr>
|
79 |
</table>
|
core/modules/ipcheck/settings.php
CHANGED
@@ -10,7 +10,7 @@ final class ITSEC_Network_Brute_Force_Settings extends ITSEC_Settings {
|
|
10 |
'api_key' => '',
|
11 |
'api_secret' => '',
|
12 |
'enable_ban' => true,
|
13 |
-
'updates_optin' =>
|
14 |
'api_nag' => true,
|
15 |
);
|
16 |
}
|
10 |
'api_key' => '',
|
11 |
'api_secret' => '',
|
12 |
'enable_ban' => true,
|
13 |
+
'updates_optin' => false,
|
14 |
'api_nag' => true,
|
15 |
);
|
16 |
}
|
core/modules/ipcheck/utilities.php
CHANGED
@@ -1,7 +1,179 @@
|
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Network_Brute_Force_Utilities {
|
4 |
-
private static $network_endpoint = '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
/**
|
7 |
* Retrieve an API key from the IPCheck server
|
@@ -20,15 +192,10 @@ final class ITSEC_Network_Brute_Force_Utilities {
|
|
20 |
return new WP_Error( 'itsec-network-brute-force-utilities-get-api-key-bad-email', sprintf( __( 'The supplied email address (%s) is invalid. A valid email address is required in order to sign up for the Network Bruteforce Protection by iThemes.', 'better-wp-security' ), $email ) );
|
21 |
}
|
22 |
|
23 |
-
$
|
24 |
-
'
|
25 |
-
'
|
26 |
-
|
27 |
-
);
|
28 |
-
|
29 |
-
$url = add_query_arg( $args, self::$network_endpoint );
|
30 |
-
|
31 |
-
$response = wp_remote_get( $url );
|
32 |
|
33 |
if ( is_wp_error( $response ) ) {
|
34 |
return $response;
|
@@ -40,10 +207,17 @@ final class ITSEC_Network_Brute_Force_Utilities {
|
|
40 |
|
41 |
$body = json_decode( $response['body'], true );
|
42 |
|
43 |
-
if ( ! is_array( $body )
|
44 |
return new WP_Error( 'itsec-network-brute-force-utilities-get-api-key-bad-response', __( 'An unknown error prevented the API key request from succeeding. The request for an API key returned an unrecognized response. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
45 |
}
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
$key = trim( sanitize_text_field( $body['apikey'] ) );
|
48 |
|
49 |
if ( empty( $key ) ) {
|
@@ -69,15 +243,10 @@ final class ITSEC_Network_Brute_Force_Utilities {
|
|
69 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-empty-key', __( 'An unknown error prevented the API key secret request from succeeding. The request for an API key submitted an empty key. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
70 |
}
|
71 |
|
72 |
-
$
|
73 |
-
'action' => 'activate-key',
|
74 |
'apikey' => $api_key,
|
75 |
'site' => home_url( '', 'http' ),
|
76 |
-
);
|
77 |
-
|
78 |
-
$url = add_query_arg( $args, self::$network_endpoint );
|
79 |
-
|
80 |
-
$response = wp_remote_get( $url );
|
81 |
|
82 |
if ( is_wp_error( $response ) ) {
|
83 |
return $response;
|
@@ -94,6 +263,7 @@ final class ITSEC_Network_Brute_Force_Utilities {
|
|
94 |
if ( ! empty( $body['error'] ) && ! empty( $body['error']['message'] ) ) {
|
95 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-error-response', sprintf( __( 'There was an error returned from the Network Brute Force Protection API: %1$s', 'better-wp-security' ), $body['error']['message'] ) );
|
96 |
}
|
|
|
97 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-bad-response', __( 'An unknown error prevented the API key secret request from succeeding. The request for an API key secret returned an unrecognized response. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
98 |
}
|
99 |
|
@@ -105,4 +275,19 @@ final class ITSEC_Network_Brute_Force_Utilities {
|
|
105 |
|
106 |
return $secret;
|
107 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
}
|
1 |
<?php
|
2 |
|
3 |
final class ITSEC_Network_Brute_Force_Utilities {
|
4 |
+
private static $network_endpoint = 'https://ipcheck-api.ithemes.com/';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Check visitor IP to see if it is banned by IPCheck.
|
8 |
+
*
|
9 |
+
* @param string $ip
|
10 |
+
*
|
11 |
+
* @return bool true if banned, false otherwise.
|
12 |
+
*/
|
13 |
+
public static function is_ip_banned( $ip = '' ) {
|
14 |
+
if ( ! $ip ) {
|
15 |
+
$ip = ITSEC_Lib::get_ip();
|
16 |
+
}
|
17 |
+
|
18 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
19 |
+
|
20 |
+
if ( ! ITSEC_Lib_IP_Tools::validate( $ip ) || ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
|
21 |
+
return false;
|
22 |
+
}
|
23 |
+
|
24 |
+
return self::get_server_response( 'check-ip', $ip );
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Report visitor IP for blacklistable-offense to IPCheck.
|
29 |
+
*
|
30 |
+
* @param string $ip
|
31 |
+
*
|
32 |
+
* @return bool true if banned, false otherwise.
|
33 |
+
*/
|
34 |
+
public static function report_ip( $ip = '' ) {
|
35 |
+
if ( ! $ip ) {
|
36 |
+
$ip = ITSEC_Lib::get_ip();
|
37 |
+
}
|
38 |
+
|
39 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
40 |
+
|
41 |
+
if ( ! ITSEC_Lib_IP_Tools::validate( $ip ) || ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
|
42 |
+
return false;
|
43 |
+
}
|
44 |
+
|
45 |
+
return self::get_server_response( 'report-ip', $ip );
|
46 |
+
}
|
47 |
+
|
48 |
+
private static function get_server_response( $action, $ip ) {
|
49 |
+
$api_key = ITSEC_Modules::get_setting( 'network-brute-force', 'api_key' );
|
50 |
+
$api_secret = ITSEC_Modules::get_setting( 'network-brute-force', 'api_secret' );
|
51 |
+
|
52 |
+
if ( ! $api_key || ! $api_secret ) {
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
|
56 |
+
$cache = self::get_cache( $ip );
|
57 |
+
|
58 |
+
if ( 'check-ip' === $action ) {
|
59 |
+
if ( $cache['cache_ttl'] >= ITSEC_Core::get_current_time_gmt() ) {
|
60 |
+
return $cache['block'];
|
61 |
+
}
|
62 |
+
} elseif ( 'report-ip' === $action ) {
|
63 |
+
if ( $cache['report_ttl'] >= ITSEC_Core::get_current_time_gmt() ) {
|
64 |
+
return $cache['block'];
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
$args = json_encode( array(
|
69 |
+
'apikey' => $api_key,
|
70 |
+
'behavior' => 'brute-force-login',
|
71 |
+
'ip' => $ip,
|
72 |
+
'site' => home_url( '', 'http' ),
|
73 |
+
'timestamp' => ITSEC_Core::get_current_time_gmt(),
|
74 |
+
) );
|
75 |
+
|
76 |
+
$response = self::call_api( $action, array(), array(
|
77 |
+
'method' => 'POST',
|
78 |
+
'body' => array(
|
79 |
+
'request' => $args,
|
80 |
+
'signature' => self::hmac_sha1( $api_secret, $action . $args ),
|
81 |
+
),
|
82 |
+
) );
|
83 |
+
|
84 |
+
if ( is_wp_error( $response ) || ! isset( $response['body'] ) ) {
|
85 |
+
return false;
|
86 |
+
}
|
87 |
+
|
88 |
+
$response = json_decode( $response['body'], true );
|
89 |
+
|
90 |
+
if ( ! is_array( $response ) || empty( $response['success'] ) ) {
|
91 |
+
return false;
|
92 |
+
}
|
93 |
+
|
94 |
+
self::set_cache( $ip, $response );
|
95 |
+
|
96 |
+
$cache_seconds = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
|
97 |
+
|
98 |
+
if ( ! empty( $response['block'] ) ) {
|
99 |
+
$data = array(
|
100 |
+
'expires' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() + $cache_seconds ),
|
101 |
+
'expires_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() + $cache_seconds ),
|
102 |
+
'type' => 'host',
|
103 |
+
);
|
104 |
+
|
105 |
+
ITSEC_Log::add_action( 'ipcheck', 'ip-blocked', $data );
|
106 |
+
|
107 |
+
return true;
|
108 |
+
}
|
109 |
+
|
110 |
+
return false;
|
111 |
+
}
|
112 |
+
|
113 |
+
private static function set_cache( $ip, $response ) {
|
114 |
+
$cache = self::get_cache( $ip );
|
115 |
+
$time = ITSEC_Core::get_current_time_gmt();
|
116 |
+
|
117 |
+
if ( isset( $response['block'] ) ) {
|
118 |
+
$cache['block'] = (boolean) $response['block'];
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( isset( $response['cache_ttl'] ) ) {
|
122 |
+
$cache['cache_ttl'] = intval( $response['cache_ttl'] ) + $time;
|
123 |
+
} elseif ( 0 === $cache['cache_ttl'] ) {
|
124 |
+
$cache['cache_ttl'] = $time + HOUR_IN_SECONDS;
|
125 |
+
}
|
126 |
+
|
127 |
+
if ( isset( $response['report_ttl'] ) ) {
|
128 |
+
$cache['report_ttl'] = intval( $response['report_ttl'] ) + $time;
|
129 |
+
}
|
130 |
+
|
131 |
+
$transient_time = max( $cache['cache_ttl'], $cache['report_ttl'] ) - $time;
|
132 |
+
|
133 |
+
|
134 |
+
set_site_transient( "itsec_ipcheck_$ip", $cache, $transient_time );
|
135 |
+
}
|
136 |
+
|
137 |
+
private static function get_cache( $ip ) {
|
138 |
+
$cache = get_site_transient( "itsec_ipcheck_$ip" );
|
139 |
+
|
140 |
+
$defaults = array(
|
141 |
+
'block' => false,
|
142 |
+
'cache_ttl' => 0,
|
143 |
+
'report_ttl' => 0,
|
144 |
+
);
|
145 |
+
|
146 |
+
if ( ! is_array( $cache ) ) {
|
147 |
+
return $defaults;
|
148 |
+
}
|
149 |
+
|
150 |
+
return array_merge( $defaults, $cache );
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* Calculates the HMAC of a string using SHA1.
|
155 |
+
*
|
156 |
+
* there is a native PHP hmac function, but we use this one for
|
157 |
+
* the widest compatibility with older PHP versions
|
158 |
+
*
|
159 |
+
* @param string $key the shared secret key used to generate the mac
|
160 |
+
* @param string $data data to be signed
|
161 |
+
*
|
162 |
+
*
|
163 |
+
* @return string base64 encoded hmac
|
164 |
+
*/
|
165 |
+
private static function hmac_sha1( $key, $data ) {
|
166 |
+
if ( strlen( $key ) > 64 ) {
|
167 |
+
$key = pack( 'H*', sha1( $key ) );
|
168 |
+
}
|
169 |
+
|
170 |
+
$key = str_pad( $key, 64, chr( 0x00 ) );
|
171 |
+
$ipad = str_repeat( chr( 0x36 ), 64 );
|
172 |
+
$opad = str_repeat( chr( 0x5c ), 64 );
|
173 |
+
$hmac = pack( 'H*', sha1( ( $key ^ $opad ) . pack( 'H*', sha1( ( $key ^ $ipad ) . $data ) ) ) );
|
174 |
+
|
175 |
+
return base64_encode( $hmac );
|
176 |
+
}
|
177 |
|
178 |
/**
|
179 |
* Retrieve an API key from the IPCheck server
|
192 |
return new WP_Error( 'itsec-network-brute-force-utilities-get-api-key-bad-email', sprintf( __( 'The supplied email address (%s) is invalid. A valid email address is required in order to sign up for the Network Bruteforce Protection by iThemes.', 'better-wp-security' ), $email ) );
|
193 |
}
|
194 |
|
195 |
+
$response = self::call_api( 'request-key', array(
|
196 |
+
'email' => $email,
|
197 |
+
'optin' => $optin,
|
198 |
+
) );
|
|
|
|
|
|
|
|
|
|
|
199 |
|
200 |
if ( is_wp_error( $response ) ) {
|
201 |
return $response;
|
207 |
|
208 |
$body = json_decode( $response['body'], true );
|
209 |
|
210 |
+
if ( ! is_array( $body ) ) {
|
211 |
return new WP_Error( 'itsec-network-brute-force-utilities-get-api-key-bad-response', __( 'An unknown error prevented the API key request from succeeding. The request for an API key returned an unrecognized response. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
212 |
}
|
213 |
|
214 |
+
if ( isset( $body['error']['message'] ) ) {
|
215 |
+
return new WP_Error(
|
216 |
+
'itsec-network-brute-force-utilities-get-api-key-' . ( isset( $body['error']['type'] ) ? $body['error']['type'] : 'unknown' ),
|
217 |
+
sprintf( __( 'There was an error returned from the Network Brute Force Protection API: %1$s', 'better-wp-security' ), $body['error']['message'] )
|
218 |
+
);
|
219 |
+
}
|
220 |
+
|
221 |
$key = trim( sanitize_text_field( $body['apikey'] ) );
|
222 |
|
223 |
if ( empty( $key ) ) {
|
243 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-empty-key', __( 'An unknown error prevented the API key secret request from succeeding. The request for an API key submitted an empty key. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
244 |
}
|
245 |
|
246 |
+
$response = self::call_api( 'activate-key', array(
|
|
|
247 |
'apikey' => $api_key,
|
248 |
'site' => home_url( '', 'http' ),
|
249 |
+
) );
|
|
|
|
|
|
|
|
|
250 |
|
251 |
if ( is_wp_error( $response ) ) {
|
252 |
return $response;
|
263 |
if ( ! empty( $body['error'] ) && ! empty( $body['error']['message'] ) ) {
|
264 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-error-response', sprintf( __( 'There was an error returned from the Network Brute Force Protection API: %1$s', 'better-wp-security' ), $body['error']['message'] ) );
|
265 |
}
|
266 |
+
|
267 |
return new WP_Error( 'itsec-network-brute-force-utilities-activate-api-key-bad-response', __( 'An unknown error prevented the API key secret request from succeeding. The request for an API key secret returned an unrecognized response. Please wait a few minutes and try again.', 'better-wp-security' ) );
|
268 |
}
|
269 |
|
275 |
|
276 |
return $secret;
|
277 |
}
|
278 |
+
|
279 |
+
private static function call_api( $action, $query = array(), $args = array() ) {
|
280 |
+
|
281 |
+
$url = self::$network_endpoint;
|
282 |
+
$url = add_query_arg( 'action', $action, $url );
|
283 |
+
|
284 |
+
if ( $query ) {
|
285 |
+
$url = add_query_arg( $query, $url );
|
286 |
+
}
|
287 |
+
|
288 |
+
$url = apply_filters( 'itsec_ipcheck_api_request_url', $url, $action, $query, $args );
|
289 |
+
$args = apply_filters( 'itsec_ipcheck_api_request_args', $args, $url, $action, $query );
|
290 |
+
|
291 |
+
return wp_remote_request( $url, $args );
|
292 |
+
}
|
293 |
}
|
core/modules/malware/class-itsec-malware-scan-results-template.php
CHANGED
@@ -3,6 +3,10 @@
|
|
3 |
class ITSEC_Malware_Scan_Results_Template {
|
4 |
public static function get_html( $results, $show_error_details = false ) {
|
5 |
|
|
|
|
|
|
|
|
|
6 |
if ( is_wp_error( $results ) && ( $edata = $results->get_error_data() ) && ! empty( $edata['itsec_site'] ) ) {
|
7 |
$site_url = $edata['itsec_site']['url'];
|
8 |
} elseif ( is_array( $results ) && ! empty( $results['itsec_site'] ) ) {
|
3 |
class ITSEC_Malware_Scan_Results_Template {
|
4 |
public static function get_html( $results, $show_error_details = false ) {
|
5 |
|
6 |
+
if ( ( $html = apply_filters( 'itsec_pre_malware_scanner_get_html', null, $results, $show_error_details ) ) !== null ) {
|
7 |
+
return $html;
|
8 |
+
}
|
9 |
+
|
10 |
if ( is_wp_error( $results ) && ( $edata = $results->get_error_data() ) && ! empty( $edata['itsec_site'] ) ) {
|
11 |
$site_url = $edata['itsec_site']['url'];
|
12 |
} elseif ( is_array( $results ) && ! empty( $results['itsec_site'] ) ) {
|
core/modules/malware/class-itsec-malware-scanner.php
CHANGED
@@ -4,6 +4,11 @@ final class ITSEC_Malware_Scanner {
|
|
4 |
protected static $transient_name = 'itsec_cached_sucuri_scan';
|
5 |
|
6 |
public static function scan( $site_id = 0 ) {
|
|
|
|
|
|
|
|
|
|
|
7 |
$process_id = ITSEC_Log::add_process_start( 'malware', 'scan', compact( 'site_id' ) );
|
8 |
|
9 |
if ( $site_id && ! is_main_site( $site_id ) ) {
|
4 |
protected static $transient_name = 'itsec_cached_sucuri_scan';
|
5 |
|
6 |
public static function scan( $site_id = 0 ) {
|
7 |
+
|
8 |
+
if ( ( $results = apply_filters( 'itsec_pre_malware_scanner_scan', null, $site_id ) ) !== null ) {
|
9 |
+
return $results;
|
10 |
+
}
|
11 |
+
|
12 |
$process_id = ITSEC_Log::add_process_start( 'malware', 'scan', compact( 'site_id' ) );
|
13 |
|
14 |
if ( $site_id && ! is_main_site( $site_id ) ) {
|
core/modules/malware/class-itsec-malware.php
CHANGED
@@ -5,6 +5,7 @@ class ITSEC_Malware {
|
|
5 |
function run() {
|
6 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
7 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
|
|
8 |
}
|
9 |
|
10 |
/**
|
@@ -24,7 +25,7 @@ class ITSEC_Malware {
|
|
24 |
*
|
25 |
* @since 3.6.0
|
26 |
*
|
27 |
-
* @param
|
28 |
*
|
29 |
* @return array Array of verbs.
|
30 |
*/
|
@@ -34,4 +35,8 @@ class ITSEC_Malware {
|
|
34 |
return $verbs;
|
35 |
}
|
36 |
|
|
|
|
|
|
|
|
|
37 |
}
|
5 |
function run() {
|
6 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
7 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
8 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ) );
|
9 |
}
|
10 |
|
11 |
/**
|
25 |
*
|
26 |
* @since 3.6.0
|
27 |
*
|
28 |
+
* @param array Array of verbs.
|
29 |
*
|
30 |
* @return array Array of verbs.
|
31 |
*/
|
35 |
return $verbs;
|
36 |
}
|
37 |
|
38 |
+
public function register_scripts() {
|
39 |
+
wp_register_script( 'itsec-malware-scan', plugin_dir_url( __FILE__ ) . 'js/malware.js', array( 'jquery' ), 3 );
|
40 |
+
}
|
41 |
+
|
42 |
}
|
core/modules/malware/js/malware.js
CHANGED
@@ -5,18 +5,19 @@
|
|
5 |
init: function() {
|
6 |
this.bindEvents();
|
7 |
},
|
8 |
-
|
9 |
bindEvents: function() {
|
10 |
$('#itsec-malware-scan').on('click', this.startScan);
|
11 |
$(document).on('click', '.itsec-malware-scan-results .itsec-malware-scan-toggle-details', this.toggleDetails);
|
|
|
12 |
},
|
13 |
-
|
14 |
toggleDetails: function( event ) {
|
15 |
event.preventDefault();
|
16 |
-
|
17 |
var $container = $(this).parents('.itsec-malware-scan-results-section');
|
18 |
var $details = $container.find('.itsec-malware-scan-details');
|
19 |
-
|
20 |
if ( $details.is(':visible') ) {
|
21 |
$(this).html('Show Details');
|
22 |
$details.hide();
|
@@ -25,21 +26,36 @@
|
|
25 |
$details.show();
|
26 |
}
|
27 |
},
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
startScan: function( event ) {
|
30 |
event.preventDefault();
|
31 |
-
|
32 |
itsecMalwareScanData.originalSubmitButtonText = $(this).val();
|
33 |
-
|
34 |
$(this)
|
35 |
.prop( 'disabled', true )
|
36 |
.val( itsecMalwareScanData.clickedButtonText );
|
37 |
-
|
38 |
var postData = {
|
39 |
action: 'itsec_malware_scan',
|
40 |
_wpnonce: itsecMalwareScanData.nonce
|
41 |
};
|
42 |
-
|
43 |
$.ajax( ajaxurl, {
|
44 |
type: 'POST',
|
45 |
data: postData,
|
@@ -48,10 +64,10 @@
|
|
48 |
timeout: 0
|
49 |
});
|
50 |
},
|
51 |
-
|
52 |
handleSuccessResponse: function( data, status, jqXHR ) {
|
53 |
$('#itsec-malware-scan').hide();
|
54 |
-
|
55 |
if ( 'string' !== typeof data ) {
|
56 |
itsecMalware.showError( itsecMalwareScanData.errorMessages.parseError );
|
57 |
} else if ( '-1' === data ) {
|
@@ -62,32 +78,32 @@
|
|
62 |
$('.itsec-malware-scan-results-wrapper').html( data );
|
63 |
}
|
64 |
},
|
65 |
-
|
66 |
handleErrorResponse: function( jqXHR, status, exception ) {
|
67 |
$('#itsec-malware-scan').hide();
|
68 |
-
|
69 |
var message = itsecMalwareScanData.errorMessages.ajaxUnknown;
|
70 |
-
|
71 |
if ( 'timeout' === status ) {
|
72 |
message = itsecMalwareScanData.errorMessages.ajaxTimeout;
|
73 |
} else if ( 'parsererror' === status ) {
|
74 |
message = itsecMalwareScanData.errorMessages.parseError;
|
75 |
}
|
76 |
-
|
77 |
itsecMalware.showError( message, '(' + status + ') ' + exception )
|
78 |
},
|
79 |
-
|
80 |
showError: function( message, replacement ) {
|
81 |
if ( 'string' === typeof replacement ) {
|
82 |
message = message.replace( '%1$s', replacement );
|
83 |
}
|
84 |
-
|
85 |
message = '<div class="error inline"><p><strong>' + message + '</strong></p></div>';
|
86 |
-
|
87 |
$('.itsec-malware-scan-results-wrapper').html( message );
|
88 |
}
|
89 |
};
|
90 |
-
|
91 |
$(document).ready(function() {
|
92 |
itsecMalware.init();
|
93 |
});
|
5 |
init: function() {
|
6 |
this.bindEvents();
|
7 |
},
|
8 |
+
|
9 |
bindEvents: function() {
|
10 |
$('#itsec-malware-scan').on('click', this.startScan);
|
11 |
$(document).on('click', '.itsec-malware-scan-results .itsec-malware-scan-toggle-details', this.toggleDetails);
|
12 |
+
$(document).on('click', '.itsec-site-scan-toggle-details', this.toggleSiteScanDetails );
|
13 |
},
|
14 |
+
|
15 |
toggleDetails: function( event ) {
|
16 |
event.preventDefault();
|
17 |
+
|
18 |
var $container = $(this).parents('.itsec-malware-scan-results-section');
|
19 |
var $details = $container.find('.itsec-malware-scan-details');
|
20 |
+
|
21 |
if ( $details.is(':visible') ) {
|
22 |
$(this).html('Show Details');
|
23 |
$details.hide();
|
26 |
$details.show();
|
27 |
}
|
28 |
},
|
29 |
+
|
30 |
+
toggleSiteScanDetails: function( e ) {
|
31 |
+
e.preventDefault();
|
32 |
+
|
33 |
+
var $container = $( this ).parents( '.itsec-site-scan-results-section' );
|
34 |
+
var $details = $container.find( '.itsec-site-scan__details' );
|
35 |
+
|
36 |
+
if ( $details.hasClass( 'hidden' ) ) {
|
37 |
+
$( this ).text( 'Hide Details' ).attr( 'aria-expanded', true );
|
38 |
+
$details.removeClass( 'hidden' );
|
39 |
+
} else {
|
40 |
+
$( this ).text( 'Show Details' ).attr( 'aria-expanded', false );
|
41 |
+
$details.addClass( 'hidden' );
|
42 |
+
}
|
43 |
+
},
|
44 |
+
|
45 |
startScan: function( event ) {
|
46 |
event.preventDefault();
|
47 |
+
|
48 |
itsecMalwareScanData.originalSubmitButtonText = $(this).val();
|
49 |
+
|
50 |
$(this)
|
51 |
.prop( 'disabled', true )
|
52 |
.val( itsecMalwareScanData.clickedButtonText );
|
53 |
+
|
54 |
var postData = {
|
55 |
action: 'itsec_malware_scan',
|
56 |
_wpnonce: itsecMalwareScanData.nonce
|
57 |
};
|
58 |
+
|
59 |
$.ajax( ajaxurl, {
|
60 |
type: 'POST',
|
61 |
data: postData,
|
64 |
timeout: 0
|
65 |
});
|
66 |
},
|
67 |
+
|
68 |
handleSuccessResponse: function( data, status, jqXHR ) {
|
69 |
$('#itsec-malware-scan').hide();
|
70 |
+
|
71 |
if ( 'string' !== typeof data ) {
|
72 |
itsecMalware.showError( itsecMalwareScanData.errorMessages.parseError );
|
73 |
} else if ( '-1' === data ) {
|
78 |
$('.itsec-malware-scan-results-wrapper').html( data );
|
79 |
}
|
80 |
},
|
81 |
+
|
82 |
handleErrorResponse: function( jqXHR, status, exception ) {
|
83 |
$('#itsec-malware-scan').hide();
|
84 |
+
|
85 |
var message = itsecMalwareScanData.errorMessages.ajaxUnknown;
|
86 |
+
|
87 |
if ( 'timeout' === status ) {
|
88 |
message = itsecMalwareScanData.errorMessages.ajaxTimeout;
|
89 |
} else if ( 'parsererror' === status ) {
|
90 |
message = itsecMalwareScanData.errorMessages.parseError;
|
91 |
}
|
92 |
+
|
93 |
itsecMalware.showError( message, '(' + status + ') ' + exception )
|
94 |
},
|
95 |
+
|
96 |
showError: function( message, replacement ) {
|
97 |
if ( 'string' === typeof replacement ) {
|
98 |
message = message.replace( '%1$s', replacement );
|
99 |
}
|
100 |
+
|
101 |
message = '<div class="error inline"><p><strong>' + message + '</strong></p></div>';
|
102 |
+
|
103 |
$('.itsec-malware-scan-results-wrapper').html( message );
|
104 |
}
|
105 |
};
|
106 |
+
|
107 |
$(document).ready(function() {
|
108 |
itsecMalware.init();
|
109 |
});
|
core/modules/malware/js/settings-page.js
CHANGED
@@ -9,6 +9,7 @@
|
|
9 |
bindEvents: function() {
|
10 |
$( document ).on( 'click', '#itsec-malware-scan-start', this.startScan );
|
11 |
$( document ).on( 'click', '.itsec-malware-scan-results-wrapper .itsec-malware-scan-toggle-details', this.toggleDetails );
|
|
|
12 |
},
|
13 |
|
14 |
toggleDetails: function( e ) {
|
@@ -26,6 +27,21 @@
|
|
26 |
}
|
27 |
},
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
startScan: function( e ) {
|
30 |
e.preventDefault();
|
31 |
|
9 |
bindEvents: function() {
|
10 |
$( document ).on( 'click', '#itsec-malware-scan-start', this.startScan );
|
11 |
$( document ).on( 'click', '.itsec-malware-scan-results-wrapper .itsec-malware-scan-toggle-details', this.toggleDetails );
|
12 |
+
$( document ).on('click', '.itsec-site-scan-toggle-details', this.toggleSiteScanDetails );
|
13 |
},
|
14 |
|
15 |
toggleDetails: function( e ) {
|
27 |
}
|
28 |
},
|
29 |
|
30 |
+
toggleSiteScanDetails: function(e) {
|
31 |
+
e.preventDefault();
|
32 |
+
|
33 |
+
var $container = $( this ).parents( '.itsec-site-scan-results-section' );
|
34 |
+
var $details = $container.find( '.itsec-site-scan__details' );
|
35 |
+
|
36 |
+
if ( $details.is( ':visible' ) ) {
|
37 |
+
$( this ).text( 'Show Details' ).attr( 'aria-expanded', false );
|
38 |
+
$details.hide();
|
39 |
+
} else {
|
40 |
+
$( this ).text( 'Hide Details' ).attr( 'aria-expanded', true );
|
41 |
+
$details.show();
|
42 |
+
}
|
43 |
+
},
|
44 |
+
|
45 |
startScan: function( e ) {
|
46 |
e.preventDefault();
|
47 |
|
core/modules/malware/logs.php
CHANGED
@@ -60,7 +60,7 @@ final class ITSEC_Malware_Logs {
|
|
60 |
}
|
61 |
|
62 |
public function enqueue() {
|
63 |
-
wp_enqueue_script( 'itsec-malware-scan
|
64 |
}
|
65 |
}
|
66 |
new ITSEC_Malware_Logs();
|
60 |
}
|
61 |
|
62 |
public function enqueue() {
|
63 |
+
wp_enqueue_script( 'itsec-malware-scan' );
|
64 |
}
|
65 |
}
|
66 |
new ITSEC_Malware_Logs();
|
core/modules/notification-center/class-notification-center.php
CHANGED
@@ -564,6 +564,7 @@ final class ITSEC_Notification_Center {
|
|
564 |
public function run() {
|
565 |
add_action( 'itsec_change_admin_user_id', array( $this, 'update_notification_user_id_on_admin_change' ) );
|
566 |
add_action( 'itsec_module_settings_after_title', array( $this, 'display_notification_center_link_for_module' ) );
|
|
|
567 |
$this->setup_scheduling();
|
568 |
}
|
569 |
|
@@ -646,6 +647,16 @@ final class ITSEC_Notification_Center {
|
|
646 |
}
|
647 |
}
|
648 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
649 |
/**
|
650 |
* Setup scheduling actions.
|
651 |
*/
|
564 |
public function run() {
|
565 |
add_action( 'itsec_change_admin_user_id', array( $this, 'update_notification_user_id_on_admin_change' ) );
|
566 |
add_action( 'itsec_module_settings_after_title', array( $this, 'display_notification_center_link_for_module' ) );
|
567 |
+
add_action( 'itsec_register_highlighted_logs', array( $this, 'register_highlighted_log' ) );
|
568 |
$this->setup_scheduling();
|
569 |
}
|
570 |
|
647 |
}
|
648 |
}
|
649 |
|
650 |
+
/**
|
651 |
+
* Register a highlighted log for mail send errors.
|
652 |
+
*/
|
653 |
+
public function register_highlighted_log() {
|
654 |
+
ITSEC_Lib_Highlighted_Logs::register_dynamic_highlight( 'notification-center-send-failed', array(
|
655 |
+
'module' => 'notification_center',
|
656 |
+
'code' => 'send_failed::%'
|
657 |
+
) );
|
658 |
+
}
|
659 |
+
|
660 |
/**
|
661 |
* Setup scheduling actions.
|
662 |
*/
|
core/modules/notification-center/logs.php
CHANGED
@@ -5,6 +5,8 @@ class ITSEC_Notification_Center_Logs {
|
|
5 |
public function __construct() {
|
6 |
add_filter( 'itsec_logs_prepare_notification_center_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
|
7 |
add_filter( 'itsec_logs_prepare_notification_center_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
|
|
|
|
8 |
}
|
9 |
|
10 |
public function filter_entry_for_list_display( $entry, $code, $data ) {
|
@@ -89,6 +91,31 @@ class ITSEC_Notification_Center_Logs {
|
|
89 |
|
90 |
return $details;
|
91 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
93 |
|
94 |
-
new ITSEC_Notification_Center_Logs();
|
5 |
public function __construct() {
|
6 |
add_filter( 'itsec_logs_prepare_notification_center_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
|
7 |
add_filter( 'itsec_logs_prepare_notification_center_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
8 |
+
add_filter( 'itsec_highlighted_log_notification-center-send-failed_notice_title', array( $this, 'filter_highlight_title' ), 10, 2 );
|
9 |
+
add_filter( 'itsec_highlighted_log_notification-center-send-failed_notice_message', array( $this, 'filter_highlight_message' ), 10, 2 );
|
10 |
}
|
11 |
|
12 |
public function filter_entry_for_list_display( $entry, $code, $data ) {
|
91 |
|
92 |
return $details;
|
93 |
}
|
94 |
+
|
95 |
+
public function filter_highlight_title( $title, $entry ) {
|
96 |
+
if ( false === strpos( $entry['code'], '::' ) ) {
|
97 |
+
$data = array();
|
98 |
+
} else {
|
99 |
+
list( , $data ) = explode( '::', $entry['code'], 2 );
|
100 |
+
$data = explode( ',', $data );
|
101 |
+
}
|
102 |
+
|
103 |
+
if ( empty( $data ) ) {
|
104 |
+
return esc_html__( 'Failed sending notification.', 'better-wp-security' );
|
105 |
+
}
|
106 |
+
|
107 |
+
list( $notification ) = $data;
|
108 |
+
|
109 |
+
if ( $strings = ITSEC_Core::get_notification_center()->get_notification_strings( $notification ) ) {
|
110 |
+
$notification = $strings['label'];
|
111 |
+
}
|
112 |
+
|
113 |
+
return sprintf( esc_html__( 'Failed sending %s notification.', 'better-wp-security' ), $notification );
|
114 |
+
}
|
115 |
+
|
116 |
+
public function filter_highlight_message( $message, $entry ) {
|
117 |
+
return wp_sprintf( '%l', ITSEC_Response::get_error_strings( $entry['data']['error'] ) );
|
118 |
+
}
|
119 |
}
|
120 |
|
121 |
+
new ITSEC_Notification_Center_Logs();
|
core/modules/security-check/scanner.php
CHANGED
@@ -123,9 +123,9 @@ final class ITSEC_Security_Check_Scanner {
|
|
123 |
'style_class' => 'regular-text',
|
124 |
) );
|
125 |
self::$feedback->add_input( 'select', 'updates_optin', array(
|
126 |
-
'format' => __( 'Receive email updates about WordPress Security from iThemes: %1$s', 'better-wp-security' ),
|
127 |
'options' => array( 'true' => __( 'Yes', 'better-wp-security' ), 'false' => __( 'No', 'better-wp-security' ) ),
|
128 |
-
'value' => '
|
129 |
) );
|
130 |
self::$feedback->add_input( 'hidden', 'method', array(
|
131 |
'value' => 'activate-network-brute-force',
|
123 |
'style_class' => 'regular-text',
|
124 |
) );
|
125 |
self::$feedback->add_input( 'select', 'updates_optin', array(
|
126 |
+
'format' => __( 'Receive email updates about WordPress Security and marketing news from iThemes: %1$s', 'better-wp-security' ),
|
127 |
'options' => array( 'true' => __( 'Yes', 'better-wp-security' ), 'false' => __( 'No', 'better-wp-security' ) ),
|
128 |
+
'value' => 'false',
|
129 |
) );
|
130 |
self::$feedback->add_input( 'hidden', 'method', array(
|
131 |
'value' => 'activate-network-brute-force',
|
core/modules/system-tweaks/config-generators.php
CHANGED
@@ -112,7 +112,7 @@ final class ITSEC_System_Tweaks_Config_Generators {
|
|
112 |
if ( $input['request_methods'] ) {
|
113 |
$rewrites .= "\n";
|
114 |
$rewrites .= "\t\t# " . __( 'Filter Request Methods - Security > Settings > System Tweaks > Request Methods', 'better-wp-security' ) . "\n";
|
115 |
-
$rewrites .= "\t\tRewriteCond %{REQUEST_METHOD} ^(TRACE|
|
116 |
$rewrites .= "\t\tRewriteRule ^.* - [F]\n";
|
117 |
}
|
118 |
|
@@ -240,7 +240,7 @@ final class ITSEC_System_Tweaks_Config_Generators {
|
|
240 |
if ( $input['request_methods'] ) {
|
241 |
$modification .= "\n";
|
242 |
$modification .= "\t# " . __( 'Filter Request Methods - Security > Settings > System Tweaks > Request Methods', 'better-wp-security' ) . "\n";
|
243 |
-
$modification .= "\tif ( \$request_method ~* ^(TRACE|
|
244 |
}
|
245 |
|
246 |
// Process suspicious query rules
|
112 |
if ( $input['request_methods'] ) {
|
113 |
$rewrites .= "\n";
|
114 |
$rewrites .= "\t\t# " . __( 'Filter Request Methods - Security > Settings > System Tweaks > Request Methods', 'better-wp-security' ) . "\n";
|
115 |
+
$rewrites .= "\t\tRewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK) [NC]\n";
|
116 |
$rewrites .= "\t\tRewriteRule ^.* - [F]\n";
|
117 |
}
|
118 |
|
240 |
if ( $input['request_methods'] ) {
|
241 |
$modification .= "\n";
|
242 |
$modification .= "\t# " . __( 'Filter Request Methods - Security > Settings > System Tweaks > Request Methods', 'better-wp-security' ) . "\n";
|
243 |
+
$modification .= "\tif ( \$request_method ~* ^(TRACE|TRACK)$ ) { return 403; }\n";
|
244 |
}
|
245 |
|
246 |
// Process suspicious query rules
|
core/modules/system-tweaks/settings-page.php
CHANGED
@@ -45,7 +45,7 @@ final class ITSEC_System_Tweaks_Settings_Page extends ITSEC_Module_Settings_Page
|
|
45 |
<td>
|
46 |
<?php $form->add_checkbox( 'request_methods' ); ?>
|
47 |
<label for="itsec-system-tweaks-request_methods"><?php esc_html_e( 'Filter Request Methods', 'better-wp-security' ); ?></label>
|
48 |
-
<p class="description"><?php
|
49 |
</td>
|
50 |
</tr>
|
51 |
<tr>
|
45 |
<td>
|
46 |
<?php $form->add_checkbox( 'request_methods' ); ?>
|
47 |
<label for="itsec-system-tweaks-request_methods"><?php esc_html_e( 'Filter Request Methods', 'better-wp-security' ); ?></label>
|
48 |
+
<p class="description"><?php esc_html_e( 'Filter out hits with the trace or track request methods.', 'better-wp-security' ); ?></p>
|
49 |
</td>
|
50 |
</tr>
|
51 |
<tr>
|
core/package.json
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "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 |
+
"version": "5.6.0",
|
5 |
+
"author": "iThemes",
|
6 |
+
"browserslist": [
|
7 |
+
"extends @wordpress/browserslist-config"
|
8 |
+
],
|
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",
|
37 |
+
"react-grid-layout": "^0.16.6",
|
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",
|
49 |
+
"@wordpress/babel-plugin-import-jsx-pragma": "^1.1.3",
|
50 |
+
"@wordpress/babel-preset-default": "^3.0.2",
|
51 |
+
"@wordpress/browserslist-config": "^2.2.3",
|
52 |
+
"@wordpress/custom-templated-path-webpack-plugin": "^1.2.0",
|
53 |
+
"@wordpress/jest-preset-default": "^3.0.0",
|
54 |
+
"@wordpress/scripts": "^2.4.4",
|
55 |
+
"acorn": "^6.0.5",
|
56 |
+
"autoprefixer": "^9.4.7",
|
57 |
+
"babel-loader": "^8.0.5",
|
58 |
+
"clean-webpack-plugin": "^2.0.2",
|
59 |
+
"concurrently": "^4.1.0",
|
60 |
+
"css-loader": "^2.1.0",
|
61 |
+
"escape-string-regexp": "^2.0.0",
|
62 |
+
"eslint-config-wordpress": "2.0.0",
|
63 |
+
"eslint-plugin-jest": "^22.3.0",
|
64 |
+
"eslint-plugin-jsx-a11y": "^6.2.1",
|
65 |
+
"eslint-plugin-react": "^7.12.4",
|
66 |
+
"eslint-plugin-wordpress": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1",
|
67 |
+
"glob": "^7.1.3",
|
68 |
+
"husky": "^1.3.1",
|
69 |
+
"jest": "^24.1.0",
|
70 |
+
"loader-utils": "^1.2.3",
|
71 |
+
"mini-css-extract-plugin": "^0.5.0",
|
72 |
+
"node-sass": "^4.12.0",
|
73 |
+
"postcss-loader": "^3.0.0",
|
74 |
+
"raw-loader": "^1.0.0",
|
75 |
+
"readdirp": "^2.2.1",
|
76 |
+
"rimraf": "^2.6.3",
|
77 |
+
"sass-loader": "^7.1.0",
|
78 |
+
"style-loader": "^0.23.1",
|
79 |
+
"svg-react-loader": "github:woutervanvliet/svg-react-loader",
|
80 |
+
"webpack": "^4.29.5",
|
81 |
+
"webpack-cli": "^3.2.3",
|
82 |
+
"webpack-filter-warnings-plugin": "^1.2.1",
|
83 |
+
"webpack-manifest-plugin": "^2.0.4",
|
84 |
+
"webpack-sources": "latest",
|
85 |
+
"webpack-webstorm-debugger-script": "^1.0.1"
|
86 |
+
},
|
87 |
+
"directories": {},
|
88 |
+
"homepage": "https://ithemes.com/security",
|
89 |
+
"husky": {
|
90 |
+
"hooks": {
|
91 |
+
"pre-commit": "node bin/pre-commit"
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"license": "GPL-2.0-or-later",
|
95 |
+
"private": true,
|
96 |
+
"repository": {
|
97 |
+
"type": "git",
|
98 |
+
"url": "git+ssh://git@bitbucket.org/ithemes/ithemes-security-pro.git"
|
99 |
+
},
|
100 |
+
"scripts": {
|
101 |
+
"build": "NODE_ENV=production ./node_modules/.bin/webpack",
|
102 |
+
"clean": "node ./bin/clean.js ",
|
103 |
+
"dev": "./node_modules/.bin/webpack",
|
104 |
+
"lint": "concurrently \"npm run lint-js\"",
|
105 |
+
"lint-js": "wp-scripts lint-js .",
|
106 |
+
"lint-js:fix": "wp-scripts lint-js . --fix",
|
107 |
+
"test": "concurrently \"npm run lint-js && npm run test-unit\"",
|
108 |
+
"test-unit": "wp-scripts test-unit-js --config tests/js/unit/jest.config.json",
|
109 |
+
"test-unit:coverage": "npm run test-unit -- --coverage",
|
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 |
+
}
|
114 |
+
}
|
core/packages/components/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/async-select/index.js
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { lazy, Suspense } from 'react';
|
5 |
+
import ErrorBoundary from 'react-error-boundary';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WordPress dependencies
|
9 |
+
*/
|
10 |
+
import { Spinner } from '@wordpress/components';
|
11 |
+
import { __ } from '@wordpress/i18n';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Async dependencies
|
15 |
+
*/
|
16 |
+
const Select = lazy( () => import( 'react-select/lib/Async' ) );
|
17 |
+
|
18 |
+
function LoadError() {
|
19 |
+
return ( <span>{ __( 'Error when loading. Please refresh.', 'better-wp-security' ) }</span> );
|
20 |
+
}
|
21 |
+
|
22 |
+
export default function AsyncSelect( { addErrorBoundary = true, ...rest } ) {
|
23 |
+
const s = (
|
24 |
+
<Suspense fallback={ <Spinner /> }>
|
25 |
+
<Select { ...rest } />
|
26 |
+
</Suspense>
|
27 |
+
);
|
28 |
+
|
29 |
+
return ( addErrorBoundary ? <ErrorBoundary FallbackComponent={ LoadError }>{ s }</ErrorBoundary> : s );
|
30 |
+
}
|
core/packages/components/src/async-select/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/close-button/index.js
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { IconButton } from '@wordpress/components';
|
5 |
+
import { __ } from '@wordpress/i18n';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import './style.scss';
|
11 |
+
|
12 |
+
export default function CloseButton( { close } ) {
|
13 |
+
return (
|
14 |
+
<IconButton className="itsec-close-button" icon="no-alt" onClick={ ( e ) => {
|
15 |
+
e.preventDefault();
|
16 |
+
close();
|
17 |
+
} } tooltip={ false } label={ __( 'Close', 'better-wp-security' ) } />
|
18 |
+
);
|
19 |
+
}
|
core/packages/components/src/close-button/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/close-button/style.scss
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-close-button.components-icon-button {
|
2 |
+
padding: 0;
|
3 |
+
height: 20px;
|
4 |
+
position: absolute;
|
5 |
+
right: 1em;
|
6 |
+
top: 1em;
|
7 |
+
|
8 |
+
&:hover {
|
9 |
+
opacity: .5;
|
10 |
+
box-shadow: none !important;
|
11 |
+
}
|
12 |
+
|
13 |
+
.components-popover.is-mobile & {
|
14 |
+
display: none;
|
15 |
+
}
|
16 |
+
}
|
core/packages/components/src/hover-detector/index.js
ADDED
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { Component } from '@wordpress/element';
|
5 |
+
|
6 |
+
const noop = () => {};
|
7 |
+
|
8 |
+
export default class extends Component {
|
9 |
+
constructor( props ) {
|
10 |
+
super( props );
|
11 |
+
|
12 |
+
this.state = {
|
13 |
+
isHovering: false,
|
14 |
+
};
|
15 |
+
|
16 |
+
this.onMouseEnter = this.onMouseEnter.bind( this );
|
17 |
+
this.onMouseLeave = this.onMouseLeave.bind( this );
|
18 |
+
this.onMouseOver = this.onMouseOver.bind( this );
|
19 |
+
this.onMouseOut = this.onMouseOut.bind( this );
|
20 |
+
this.setIsHovering = this.setIsHovering.bind( this );
|
21 |
+
this.unsetIsHovering = this.unsetIsHovering.bind( this );
|
22 |
+
this.componentWillUnmount = this.componentWillUnmount.bind( this );
|
23 |
+
|
24 |
+
this.timerIds = [];
|
25 |
+
}
|
26 |
+
|
27 |
+
static displayName = 'HoverDetector';
|
28 |
+
|
29 |
+
static defaultProps = {
|
30 |
+
hoverDelayInMs: 0,
|
31 |
+
hoverOffDelayInMs: 0,
|
32 |
+
onHoverChanged: noop,
|
33 |
+
onMouseEnter: ( { setIsHovering } ) => setIsHovering(),
|
34 |
+
onMouseLeave: ( { unsetIsHovering } ) => unsetIsHovering(),
|
35 |
+
onMouseOver: noop,
|
36 |
+
onMouseOut: noop,
|
37 |
+
shouldDecorateChildren: true,
|
38 |
+
};
|
39 |
+
|
40 |
+
onMouseEnter( e ) {
|
41 |
+
this.props.onMouseEnter( {
|
42 |
+
e,
|
43 |
+
setIsHovering: this.setIsHovering,
|
44 |
+
unsetIsHovering: this.unsetIsHovering,
|
45 |
+
} );
|
46 |
+
}
|
47 |
+
|
48 |
+
onMouseLeave( e ) {
|
49 |
+
this.props.onMouseLeave( {
|
50 |
+
e,
|
51 |
+
setIsHovering: this.setIsHovering,
|
52 |
+
unsetIsHovering: this.unsetIsHovering,
|
53 |
+
} );
|
54 |
+
}
|
55 |
+
|
56 |
+
onMouseOver( e ) {
|
57 |
+
this.props.onMouseOver( {
|
58 |
+
e,
|
59 |
+
setIsHovering: this.setIsHovering,
|
60 |
+
unsetIsHovering: this.unsetIsHovering,
|
61 |
+
} );
|
62 |
+
}
|
63 |
+
|
64 |
+
onMouseOut( e ) {
|
65 |
+
this.props.onMouseOut( {
|
66 |
+
e,
|
67 |
+
setIsHovering: this.setIsHovering,
|
68 |
+
unsetIsHovering: this.unsetIsHovering,
|
69 |
+
} );
|
70 |
+
}
|
71 |
+
|
72 |
+
componentWillUnmount() {
|
73 |
+
this.clearTimers();
|
74 |
+
}
|
75 |
+
|
76 |
+
setIsHovering() {
|
77 |
+
this.clearTimers();
|
78 |
+
|
79 |
+
const hoverScheduleId = setTimeout( () => {
|
80 |
+
const newState = { isHovering: true };
|
81 |
+
this.setState( newState, () => {
|
82 |
+
this.props.onHoverChanged( newState );
|
83 |
+
} );
|
84 |
+
}, this.props.hoverDelayInMs );
|
85 |
+
|
86 |
+
this.timerIds.push( hoverScheduleId );
|
87 |
+
}
|
88 |
+
|
89 |
+
unsetIsHovering() {
|
90 |
+
this.clearTimers();
|
91 |
+
|
92 |
+
const hoverOffScheduleId = setTimeout( () => {
|
93 |
+
const newState = { isHovering: false };
|
94 |
+
this.setState( newState, () => {
|
95 |
+
this.props.onHoverChanged( newState );
|
96 |
+
} );
|
97 |
+
}, this.props.hoverOffDelayInMs );
|
98 |
+
|
99 |
+
this.timerIds.push( hoverOffScheduleId );
|
100 |
+
}
|
101 |
+
|
102 |
+
clearTimers() {
|
103 |
+
const ids = this.timerIds;
|
104 |
+
while ( ids.length ) {
|
105 |
+
clearTimeout( ids.pop() );
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
render() {
|
110 |
+
const { children, className } = this.props;
|
111 |
+
|
112 |
+
return (
|
113 |
+
<div { ...{
|
114 |
+
className,
|
115 |
+
onMouseEnter: this.onMouseEnter,
|
116 |
+
onMouseLeave: this.onMouseLeave,
|
117 |
+
onMouseOver: this.onMouseOver,
|
118 |
+
onMouseOut: this.onMouseOut,
|
119 |
+
} }>
|
120 |
+
{ children }
|
121 |
+
</div>
|
122 |
+
);
|
123 |
+
}
|
124 |
+
}
|
core/packages/components/src/hover-detector/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/index.js
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export { default as LogModal } from './log-modal';
|
2 |
+
export { default as MalwareScanResults } from './malware-scan-results';
|
3 |
+
export { default as SiteScanResults } from './site-scan-results';
|
4 |
+
export { default as PrintR } from './print-r';
|
5 |
+
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';
|
core/packages/components/src/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/loader/icon.svg
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="200px" viewBox="0 0 580.58 684.67">
|
2 |
+
<defs>
|
3 |
+
<style>
|
4 |
+
.path{ stroke-dasharray: 2500; stroke-dashoffset: 2500; animation: itsec-loader__dash-animation 3000ms linear infinite; fill:none; stroke:#1072ba; stroke-miterlimit:10; stroke-width:15px; }
|
5 |
+
.path-outer { animation: itsec-loader__dash-animation 3000ms linear infinite }
|
6 |
+
</style>
|
7 |
+
</defs>
|
8 |
+
<title>itsec-loader</title>
|
9 |
+
<g>
|
10 |
+
<g>
|
11 |
+
<path class="path"
|
12 |
+
d="M522.48,126.08a4.71,4.71,0,0,0-3.32-4.33C471.9,109.4,391,93.14,292.66,93.14h-3.91A941.87,941.87,0,0,0,61.45,122a4.7,4.7,0,0,0-3.34,4.32l0,137.93c-.08,1.41-3.69,139.49,87.31,244.83,61.84,71.63,114.77,102.51,141.77,114.83a8.69,8.69,0,0,0,6.29,0c27-12.28,79.87-43.22,141.76-114.84,90.92-105.34,87.32-243.42,87.3-244.59Z"/>
|
13 |
+
</g>
|
14 |
+
<g>
|
15 |
+
<path class="path path-outer"
|
16 |
+
d="M573,271.88V47.17a4.82,4.82,0,0,0-3.08-4.31c-12.19-4-59.81-18.37-128.6-22.33a3,3,0,0,0-3.25,3.08v39.8a2.7,2.7,0,0,1-3.22,2.75c-22.38-3.45-47-6.37-73.42-8.33a3.55,3.55,0,0,1-3.25-3.48V15.74A3.82,3.82,0,0,0,355,12.06c-9.59-1.19-39-4.56-66.6-4.56-24,0-52.92,3.84-62.45,5.21a3.87,3.87,0,0,0-3.22,3.74l0,38.09a3.56,3.56,0,0,1-3.25,3.5c-26.44,2-51,5-73.38,8.39a2.69,2.69,0,0,1-3.21-2.75V23.61a3,3,0,0,0-3.25-3.08c-68.84,4-116.69,18.33-128.92,22.34a4.8,4.8,0,0,0-3.07,4.3V271.88S1.17,423,103.87,541.9C194.8,647.22,270.84,672.42,287,676.81a14.24,14.24,0,0,0,6.33,0c16.2-4.39,92.25-29.57,183.14-134.92C579.2,423,573,271.88,573,271.88Z"/>
|
17 |
+
</g>
|
18 |
+
</g>
|
19 |
+
</svg>
|
core/packages/components/src/loader/index.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Internal Dependencies
|
3 |
+
*/
|
4 |
+
import Logo from './logo.svg';
|
5 |
+
import './style.scss';
|
6 |
+
|
7 |
+
export default function Loader() {
|
8 |
+
return (
|
9 |
+
<div className="itsec-loader">
|
10 |
+
<Logo />
|
11 |
+
</div>
|
12 |
+
);
|
13 |
+
}
|
core/packages/components/src/loader/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/loader/logo.svg
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="200px" viewBox="0 0 203.31 240.72">
|
2 |
+
<defs>
|
3 |
+
<style>.path{fill:#1072ba;}.path{fill:#69c;}</style>
|
4 |
+
</defs>
|
5 |
+
<g>
|
6 |
+
<g>
|
7 |
+
<path class="path"
|
8 |
+
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"/>
|
9 |
+
<path class="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"/>
|
10 |
+
<path class="path"
|
11 |
+
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"/>
|
12 |
+
</g>
|
13 |
+
</g>
|
14 |
+
</svg>
|
core/packages/components/src/loader/style.scss
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-loader {
|
2 |
+
margin: 0 auto;
|
3 |
+
display: flex;
|
4 |
+
align-items: center;
|
5 |
+
justify-content: center;
|
6 |
+
height: 100%;
|
7 |
+
|
8 |
+
& svg {
|
9 |
+
animation: itsec-loader__grow 3.0s infinite ease-in;
|
10 |
+
}
|
11 |
+
}
|
12 |
+
|
13 |
+
@keyframes itsec-loader__grow {
|
14 |
+
0% {
|
15 |
+
transform: scale(0);
|
16 |
+
}
|
17 |
+
100% {
|
18 |
+
transform: scale(1.0);
|
19 |
+
opacity: 0;
|
20 |
+
}
|
21 |
+
}
|
core/packages/components/src/log-modal/index.js
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { RawHTML, Component } from '@wordpress/element';
|
5 |
+
import { Modal } from '@wordpress/components';
|
6 |
+
import { __ } from '@wordpress/i18n';
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Internal dependencies
|
10 |
+
*/
|
11 |
+
import './style.scss';
|
12 |
+
|
13 |
+
export default class LogModal extends Component {
|
14 |
+
static #html = {};
|
15 |
+
|
16 |
+
state = {
|
17 |
+
html: null,
|
18 |
+
};
|
19 |
+
|
20 |
+
componentDidMount() {
|
21 |
+
this.fetchHtml();
|
22 |
+
}
|
23 |
+
|
24 |
+
shouldComponentUpdate( nextProps, nextState ) {
|
25 |
+
return this.props.id !== nextProps.id || this.state.html !== nextState.html;
|
26 |
+
}
|
27 |
+
|
28 |
+
componentDidUpdate( prevProps ) {
|
29 |
+
if ( prevProps.id !== this.props.id ) {
|
30 |
+
this.fetchHtml();
|
31 |
+
}
|
32 |
+
}
|
33 |
+
|
34 |
+
fetchHtml = () => {
|
35 |
+
if ( LogModal.#html[ this.props.id ] ) {
|
36 |
+
this.setState( { html: LogModal.#html[ this.props.id ] } );
|
37 |
+
}
|
38 |
+
|
39 |
+
const form = new FormData();
|
40 |
+
form.set( 'id', this.props.id );
|
41 |
+
form.set( 'nonce', this.props.nonce );
|
42 |
+
form.set( 'action', 'itsec_logs_page' );
|
43 |
+
|
44 |
+
fetch( this.props.ajaxurl, {
|
45 |
+
method: 'POST',
|
46 |
+
credentials: 'same-origin',
|
47 |
+
body: form,
|
48 |
+
} ).then( ( response ) => response.json() ).then( ( response ) => {
|
49 |
+
LogModal.#html[ this.props.id ] = response.response;
|
50 |
+
this.setState( { html: response.response } );
|
51 |
+
} );
|
52 |
+
};
|
53 |
+
|
54 |
+
render() {
|
55 |
+
return (
|
56 |
+
<Modal
|
57 |
+
title={ __( 'Log Details', 'better-wp-security' ) }
|
58 |
+
overlayClassName="itsec-log-modal"
|
59 |
+
onRequestClose={ this.props.onClose }>
|
60 |
+
<div className="itsec-log-modal__content">
|
61 |
+
{ this.state.html ?
|
62 |
+
<RawHTML>{ this.state.html }</RawHTML> :
|
63 |
+
<span>{ __( 'Loading', 'better-wp-security' ) }</span> }
|
64 |
+
</div>
|
65 |
+
</Modal>
|
66 |
+
);
|
67 |
+
}
|
68 |
+
}
|
core/packages/components/src/log-modal/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/log-modal/style.scss
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-log-modal {
|
2 |
+
& .components-modal__frame {
|
3 |
+
min-width: 80%;
|
4 |
+
height: 100%;
|
5 |
+
}
|
6 |
+
|
7 |
+
& .itsec-log-modal__content {
|
8 |
+
margin: 2em 1em;
|
9 |
+
|
10 |
+
& .itsec-log-raw-details-toggle {
|
11 |
+
display: none;
|
12 |
+
}
|
13 |
+
|
14 |
+
& tr:first-child th,
|
15 |
+
& tr:first-child td {
|
16 |
+
margin-top: 0;
|
17 |
+
padding-top: 0;
|
18 |
+
}
|
19 |
+
}
|
20 |
+
}
|
core/packages/components/src/malware-scan-results/blacklist-details.js
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { has, get } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { __ } from '@wordpress/i18n';
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Internal dependencies
|
13 |
+
*/
|
14 |
+
import WrappedSection from './wrapped-section';
|
15 |
+
|
16 |
+
function BlacklistDetails( { results } ) {
|
17 |
+
const status = has( results, [ 'BLACKLIST', 'WARN' ] ) ? 'warn' : 'clean';
|
18 |
+
|
19 |
+
return (
|
20 |
+
<WrappedSection type="malware" status={ status } description={ __( 'Blacklist', 'better-wp-security' ) }>
|
21 |
+
<ul>
|
22 |
+
{ get( results, [ 'BLACKLIST', 'WARN' ], [] ).map( ( entry, i ) => (
|
23 |
+
<li className="itsec-malware-scan-warn" key={ i }>
|
24 |
+
<span>
|
25 |
+
<a href={ entry[ 1 ] }>{ entry[ 0 ] }</a>
|
26 |
+
</span>
|
27 |
+
</li>
|
28 |
+
) ) }
|
29 |
+
{ get( results, [ 'BLACKLIST', 'INFO' ], [] ).map( ( entry, i ) => (
|
30 |
+
<li className="itsec-malware-scan-clean" key={ i }>
|
31 |
+
<span>
|
32 |
+
<a href={ entry[ 1 ] }>{ entry[ 0 ] }</a>
|
33 |
+
</span>
|
34 |
+
</li>
|
35 |
+
) ) }
|
36 |
+
</ul>
|
37 |
+
</WrappedSection>
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
export default BlacklistDetails;
|
core/packages/components/src/malware-scan-results/index.js
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __, sprintf } from '@wordpress/i18n';
|
5 |
+
import { Fragment } from '@wordpress/element';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import { castWPError, isWPError } from '@ithemes/security-utils';
|
11 |
+
import WPErrorDetails from './wp-error-details';
|
12 |
+
import SystemErrorDetails from './system-error-details';
|
13 |
+
import MalwareDetails from './malware-details';
|
14 |
+
import BlacklistDetails from './blacklist-details';
|
15 |
+
import './style.scss';
|
16 |
+
|
17 |
+
function MalwareScanResults( { results, showErrorDetails = true } ) {
|
18 |
+
let siteUrl;
|
19 |
+
|
20 |
+
if ( isWPError( results ) ) {
|
21 |
+
const errorData = castWPError( results ).getErrorData();
|
22 |
+
siteUrl = ( errorData && errorData.itsec_site ) ? errorData.itsec_site.url : undefined;
|
23 |
+
} else if ( results.itsec_site ) {
|
24 |
+
siteUrl = results.itsec_site.url;
|
25 |
+
}
|
26 |
+
|
27 |
+
return (
|
28 |
+
<div className="itsec-malware-scan-results">
|
29 |
+
{ siteUrl && <h4>{ sprintf( __( 'Site: %s', 'better-wp-security' ), siteUrl ) }</h4> }
|
30 |
+
|
31 |
+
{ isWPError( results ) ? <WPErrorDetails results={ results } showErrorDetails={ showErrorDetails } /> : (
|
32 |
+
<Fragment>
|
33 |
+
<SystemErrorDetails results={ results } />
|
34 |
+
<MalwareDetails results={ results } />
|
35 |
+
<BlacklistDetails results={ results } />
|
36 |
+
</Fragment>
|
37 |
+
) }
|
38 |
+
</div>
|
39 |
+
);
|
40 |
+
}
|
41 |
+
|
42 |
+
export default MalwareScanResults;
|
core/packages/components/src/malware-scan-results/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/malware-scan-results/malware-details.js
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { has } from 'lodash';
|
5 |
+
import { decodeEntities } from '@wordpress/html-entities';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WordPress dependencies
|
9 |
+
*/
|
10 |
+
import { __, sprintf } from '@wordpress/i18n';
|
11 |
+
import { Fragment } from '@wordpress/element';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Internal dependencies
|
15 |
+
*/
|
16 |
+
import WrappedSection from './wrapped-section';
|
17 |
+
|
18 |
+
function MalwareDetails( { results } ) {
|
19 |
+
if ( ! has( results, [ 'MALWARE', 'WARN' ] ) ) {
|
20 |
+
return <WrappedSection type="malware" status="clean" description={ __( 'Malware', 'better-wp-security' ) } />;
|
21 |
+
}
|
22 |
+
|
23 |
+
return (
|
24 |
+
<WrappedSection type="malware" status="warn" description={ __( 'Malware', 'better-wp-security' ) }>
|
25 |
+
<ul>
|
26 |
+
{ results.MALWARE.WARN.map( ( entry, i ) => {
|
27 |
+
let [ message, url ] = entry[ 0 ].split( ':', 2 );
|
28 |
+
message = message.trim();
|
29 |
+
url = url.trim();
|
30 |
+
|
31 |
+
switch ( message.toLowerCase() ) {
|
32 |
+
case 'security warning in the url':
|
33 |
+
message = __( 'Security warning in the URL', 'better-wp-security' );
|
34 |
+
break;
|
35 |
+
case 'malware found on url':
|
36 |
+
message = __( 'Malware found on URL', 'better-wp-security' );
|
37 |
+
break;
|
38 |
+
}
|
39 |
+
|
40 |
+
let payload;
|
41 |
+
|
42 |
+
if ( url.substring( 0, 5 ) === '&' ) {
|
43 |
+
payload = decodeEntities( url );
|
44 |
+
url = results.itsec_site ? results.itsec_site.url : '/';
|
45 |
+
}
|
46 |
+
|
47 |
+
let details;
|
48 |
+
const parts = entry[ 1 ].split( '\n', 2 );
|
49 |
+
|
50 |
+
if ( parts[ 1 ] ) {
|
51 |
+
const detailsMatch = parts[ 0 ].match( /(.+)\. Details: (.+)/ );
|
52 |
+
|
53 |
+
if ( detailsMatch ) {
|
54 |
+
let type = detailsMatch[ 1 ];
|
55 |
+
const docsUrl = detailsMatch[ 2 ];
|
56 |
+
|
57 |
+
if ( '*Known Spam detected' === type ) {
|
58 |
+
type = __( '*Known Spam detected', 'better-wp-security' );
|
59 |
+
}
|
60 |
+
|
61 |
+
details = (
|
62 |
+
<Fragment>
|
63 |
+
<br />
|
64 |
+
{ sprintf( __( 'Type: %s', 'better-wp-security' ), type ) }
|
65 |
+
<br />
|
66 |
+
{ __( 'Documentation:', 'better-wp-security' ) }
|
67 |
+
<a href={ docsUrl } target="_blank" rel="noopener noreferrer">{ docsUrl }</a>
|
68 |
+
</Fragment>
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
const payloadMatch = decodeEntities( parts[ 1 ].trim() ).match( /<div id='HiddenDiv'>(.+)<\/div>/ );
|
73 |
+
|
74 |
+
if ( payloadMatch ) {
|
75 |
+
payload = payloadMatch[ 1 ];
|
76 |
+
}
|
77 |
+
}
|
78 |
+
|
79 |
+
return (
|
80 |
+
<li className="itsec-malware-scan-warn" key={ i }>
|
81 |
+
<span>
|
82 |
+
{ message }
|
83 |
+
<br />
|
84 |
+
{ __( 'Infected URL:', 'better-wp-security' ) }
|
85 |
+
<a href={ url } target="_blank" rel="noopener noreferrer">{ url }</a>
|
86 |
+
{ details }
|
87 |
+
{ payload && (
|
88 |
+
<Fragment>
|
89 |
+
<br />
|
90 |
+
{ __( 'Payload:', 'better-wp-security' ) }
|
91 |
+
<pre>{ payload }</pre>
|
92 |
+
</Fragment>
|
93 |
+
) }
|
94 |
+
</span>
|
95 |
+
</li>
|
96 |
+
);
|
97 |
+
} ) }
|
98 |
+
</ul>
|
99 |
+
</WrappedSection>
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
export default MalwareDetails;
|
core/packages/components/src/malware-scan-results/style.scss
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-malware-scan-results {
|
2 |
+
& h4 {
|
3 |
+
margin-top: 5px;
|
4 |
+
}
|
5 |
+
|
6 |
+
& ul {
|
7 |
+
margin-left: 20px;
|
8 |
+
}
|
9 |
+
|
10 |
+
& ul li {
|
11 |
+
line-height: 16px;
|
12 |
+
list-style: outside none disc;
|
13 |
+
}
|
14 |
+
|
15 |
+
& .itsec-malware-scan-details pre {
|
16 |
+
background-color: #eaeaea;
|
17 |
+
padding: 1em;
|
18 |
+
white-space: pre-wrap;
|
19 |
+
}
|
20 |
+
|
21 |
+
& .itsec-malware-scan-results-section {
|
22 |
+
border: 1px solid #ddd;
|
23 |
+
border-bottom-color: transparent;
|
24 |
+
padding: 0 1em;
|
25 |
+
}
|
26 |
+
|
27 |
+
& .itsec-malware-scan-results-section:last-child {
|
28 |
+
border-bottom-color: #ddd;
|
29 |
+
}
|
30 |
+
|
31 |
+
& .itsec-malware-scan-clean,
|
32 |
+
& .itsec-malware-scan-warn,
|
33 |
+
& .itsec-malware-scan-error {
|
34 |
+
padding: 2px 6px;
|
35 |
+
color: #fff;
|
36 |
+
margin-right: 1em;
|
37 |
+
width: 60px;
|
38 |
+
text-align: center;
|
39 |
+
}
|
40 |
+
|
41 |
+
& span.itsec-malware-scan-clean,
|
42 |
+
& span.itsec-malware-scan-warn,
|
43 |
+
& span.itsec-malware-scan-error {
|
44 |
+
display: inline-block;
|
45 |
+
}
|
46 |
+
|
47 |
+
& .itsec-malware-scan-clean {
|
48 |
+
background: #7ad03a;
|
49 |
+
}
|
50 |
+
|
51 |
+
& .itsec-malware-scan-warn,
|
52 |
+
& .itsec-malware-scan-error {
|
53 |
+
background: #dd3d36;
|
54 |
+
}
|
55 |
+
|
56 |
+
& .itsec-malware-scan-details .itsec-malware-scan-warn,
|
57 |
+
& .itsec-malware-scan-details .itsec-malware-scan-error,
|
58 |
+
& .itsec-malware-scan-details .itsec-malware-scan-clean {
|
59 |
+
background: none;
|
60 |
+
padding: 0;
|
61 |
+
color: inherit;
|
62 |
+
margin: 0;
|
63 |
+
width: 100%;
|
64 |
+
text-align: left;
|
65 |
+
}
|
66 |
+
|
67 |
+
& .itsec-malware-scan-details .itsec-malware-scan-warn,
|
68 |
+
& .itsec-malware-scan-details .itsec-malware-scan-error {
|
69 |
+
color: #dd3d36;
|
70 |
+
}
|
71 |
+
|
72 |
+
& .itsec-malware-scan-details .itsec-malware-scan-clean {
|
73 |
+
color: #7ad03a;
|
74 |
+
}
|
75 |
+
|
76 |
+
& .itsec-malware-scan-results-section .itsec-malware-scan-details li {
|
77 |
+
margin-bottom: .25em;
|
78 |
+
padding-bottom: .5em;
|
79 |
+
border-bottom: 1px solid #ddd;
|
80 |
+
}
|
81 |
+
|
82 |
+
& .itsec-malware-scan-results-section .itsec-malware-scan-details li:last-child {
|
83 |
+
border: none;
|
84 |
+
}
|
85 |
+
|
86 |
+
& .itsec-malware-scan-results-section .itsec-malware-scan-details li span {
|
87 |
+
color: #444;
|
88 |
+
}
|
89 |
+
|
90 |
+
& .itsec-malware-scan-toggle-details {
|
91 |
+
margin-left: 1em;
|
92 |
+
}
|
93 |
+
}
|
core/packages/components/src/malware-scan-results/system-error-details.js
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { has } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { __ } from '@wordpress/i18n';
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Internal dependencies.
|
13 |
+
*/
|
14 |
+
import WrappedSection from './wrapped-section';
|
15 |
+
|
16 |
+
function SystemErrorDetails( { results } ) {
|
17 |
+
return has( results, [ 'SYSTEM', 'ERROR' ] ) && (
|
18 |
+
<WrappedSection type="system-error" status="error" description={ __( 'The scan failed to properly scan the site.', 'better-wp-security' ) }>
|
19 |
+
<ul>
|
20 |
+
{ results.SYSTEM.ERROR.map( ( entry, i ) => (
|
21 |
+
<li className="itsec-malware-scan-error" key={ i }>
|
22 |
+
<span>{ entry }</span>
|
23 |
+
</li>
|
24 |
+
) ) }
|
25 |
+
</ul>
|
26 |
+
</WrappedSection>
|
27 |
+
);
|
28 |
+
}
|
29 |
+
|
30 |
+
export default SystemErrorDetails;
|
core/packages/components/src/malware-scan-results/wp-error-details.js
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __, sprintf } from '@wordpress/i18n';
|
5 |
+
import { Fragment } from '@wordpress/element';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import WrappedSection from './wrapped-section';
|
11 |
+
import { castWPError } from '@ithemes/security-utils';
|
12 |
+
|
13 |
+
function WPErrorDetails( { results, showErrorDetails = false } ) {
|
14 |
+
const wpError = castWPError( results );
|
15 |
+
|
16 |
+
return (
|
17 |
+
<WrappedSection status="error" description={ __( 'The scan failed to properly scan the site.', 'better-wp-security' ) }>
|
18 |
+
<p>{ sprintf( __( 'Error Message: %s', 'better-wp-security' ), wpError.getErrorMessage() ) }</p>
|
19 |
+
<p>{ sprintf( __( 'Error Code: %s', 'better-wp-security' ), wpError.getErrorCode() ) }</p>
|
20 |
+
|
21 |
+
{ showErrorDetails && wpError.getErrorData() && (
|
22 |
+
<Fragment>
|
23 |
+
<p>{ __( 'If you contact support about this error, please provide the following debug details:', 'better-wp-security' ) }</p>
|
24 |
+
<pre>
|
25 |
+
{ JSON.stringify( { code: wpError.getErrorCode(), data: wpError.getErrorData() }, null, 2 ) }
|
26 |
+
</pre>
|
27 |
+
</Fragment>
|
28 |
+
) }
|
29 |
+
</WrappedSection>
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
export default WPErrorDetails;
|
core/packages/components/src/malware-scan-results/wrapped-section.js
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import classnames from 'classnames';
|
5 |
+
import { isEmpty } from 'lodash';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WordPress dependencies
|
9 |
+
*/
|
10 |
+
import { __ } from '@wordpress/i18n';
|
11 |
+
import { Fragment } from '@wordpress/element';
|
12 |
+
import { Button } from '@wordpress/components';
|
13 |
+
import { compose, withState, withInstanceId } from '@wordpress/compose';
|
14 |
+
|
15 |
+
function WrappedSection( { type, status, description, isShowing, setState, instanceId, children } ) {
|
16 |
+
let statusText;
|
17 |
+
|
18 |
+
switch ( status ) {
|
19 |
+
case 'clean':
|
20 |
+
statusText = __( 'Clean', 'better-wp-security' );
|
21 |
+
break;
|
22 |
+
case 'warn':
|
23 |
+
statusText = __( 'Warn', 'better-wp-security' );
|
24 |
+
break;
|
25 |
+
case 'error':
|
26 |
+
statusText = __( 'Error', 'better-wp-security' );
|
27 |
+
break;
|
28 |
+
default:
|
29 |
+
statusText = status;
|
30 |
+
break;
|
31 |
+
}
|
32 |
+
|
33 |
+
const statusEl = ( <span className={ `itsec-malware-scan-${ status }` }>{ statusText }</span> );
|
34 |
+
|
35 |
+
return (
|
36 |
+
<div className={ classnames( 'itsec-malware-scan-results-section', `itsec-malware-scan-results-${ type }-section` ) }>
|
37 |
+
{ isEmpty( children ) ? ( <p>{ statusEl } { description }</p> ) : (
|
38 |
+
<Fragment>
|
39 |
+
<p>
|
40 |
+
{ statusEl }
|
41 |
+
{ description }
|
42 |
+
<Button isLink className="itsec-malware-scan-toggle-details" onClick={ () => setState( { isShowing: ! isShowing } ) }
|
43 |
+
aria-expanded={ isShowing } aria-controls={ `itsec-malware-scan-details--${ instanceId }` }>
|
44 |
+
{ isShowing ?
|
45 |
+
__( 'Hide Details', 'better-wp-security' ) :
|
46 |
+
__( 'Show Details', 'better-wp-security' )
|
47 |
+
}
|
48 |
+
</Button>
|
49 |
+
</p>
|
50 |
+
<div className="itsec-malware-scan-details" id={ `itsec-malware-scan-details--${ instanceId }` } style={ { display: isShowing ? 'block' : 'none' } }>
|
51 |
+
{ children }
|
52 |
+
</div>
|
53 |
+
</Fragment>
|
54 |
+
) }
|
55 |
+
</div>
|
56 |
+
);
|
57 |
+
}
|
58 |
+
|
59 |
+
export default compose( [
|
60 |
+
withState( { isShowing: false } ),
|
61 |
+
withInstanceId,
|
62 |
+
] )( WrappedSection );
|
core/packages/components/src/print-r/index.js
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isString, isBoolean, isNumber, isArray, isPlainObject, keys, forEach, toString, cloneDeep, size } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Fragment } from '@wordpress/element';
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Internal dependencies
|
13 |
+
*/
|
14 |
+
import './style.scss';
|
15 |
+
|
16 |
+
export default function PrintR( { json } ) {
|
17 |
+
return (
|
18 |
+
<pre className="itsec-component-print-r">
|
19 |
+
{ inspectDive( cloneDeep( json ) ) }
|
20 |
+
</pre>
|
21 |
+
);
|
22 |
+
}
|
23 |
+
|
24 |
+
function inspectDive( data, maxDepth = 10, depth = 0, showArrayHeader = true ) {
|
25 |
+
if ( isString( data ) ) {
|
26 |
+
if ( data.length === 0 ) {
|
27 |
+
return <strong>[empty string]</strong>;
|
28 |
+
}
|
29 |
+
|
30 |
+
return data;
|
31 |
+
}
|
32 |
+
|
33 |
+
if ( isNumber( data ) ) {
|
34 |
+
return <strong>{ `[number] ${ data }` }</strong>;
|
35 |
+
}
|
36 |
+
|
37 |
+
if ( isBoolean( data ) ) {
|
38 |
+
return <strong>{ data ? '[boolean] true' : '[boolean] false' }</strong>;
|
39 |
+
}
|
40 |
+
|
41 |
+
if ( data === null || data === undefined ) {
|
42 |
+
return <strong>null</strong>;
|
43 |
+
}
|
44 |
+
|
45 |
+
if ( isArray( data ) || isPlainObject( data ) ) {
|
46 |
+
const retval = [];
|
47 |
+
|
48 |
+
if ( showArrayHeader ) {
|
49 |
+
retval.push( <strong key="header">{ 'Array' }</strong> );
|
50 |
+
}
|
51 |
+
|
52 |
+
if ( 0 === size( data ) ) {
|
53 |
+
retval.push( '()' );
|
54 |
+
|
55 |
+
return retval;
|
56 |
+
}
|
57 |
+
|
58 |
+
if ( depth === maxDepth ) {
|
59 |
+
retval.push( `(${ data.length })` );
|
60 |
+
|
61 |
+
return retval;
|
62 |
+
}
|
63 |
+
|
64 |
+
let maxLength = 0;
|
65 |
+
|
66 |
+
for ( const key of keys( data ) ) {
|
67 |
+
if ( key.length > maxLength ) {
|
68 |
+
maxLength = key.length;
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
const padding = pad( depth );
|
73 |
+
forEach( data, ( value, key ) => {
|
74 |
+
retval.push(
|
75 |
+
<Fragment key={ key }>
|
76 |
+
{ '\n' }
|
77 |
+
{ padding }
|
78 |
+
{ key }
|
79 |
+
{ pad( maxLength - toString( key ).length, ' ' ) }
|
80 |
+
{ ' ' }
|
81 |
+
<strong>=></strong>
|
82 |
+
{ ' ' }
|
83 |
+
{ inspectDive( value, maxDepth, depth + 1 ) }
|
84 |
+
</Fragment>
|
85 |
+
);
|
86 |
+
} );
|
87 |
+
|
88 |
+
return retval;
|
89 |
+
}
|
90 |
+
|
91 |
+
return <strong>[*]</strong>;
|
92 |
+
}
|
93 |
+
|
94 |
+
function pad( depth, padding = ' ' ) {
|
95 |
+
let ret = '';
|
96 |
+
|
97 |
+
for ( let i = 0; i <= depth; i++ ) {
|
98 |
+
ret += padding;
|
99 |
+
}
|
100 |
+
|
101 |
+
return ret;
|
102 |
+
}
|
core/packages/components/src/print-r/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/print-r/style.scss
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-component-print-r {
|
2 |
+
background: #f1f1f1;
|
3 |
+
overflow: scroll;
|
4 |
+
padding: 1em;
|
5 |
+
color: black;
|
6 |
+
font-family: "Courier New", Courier, monospace;
|
7 |
+
font-size: 12px;
|
8 |
+
white-space: pre-wrap;
|
9 |
+
text-align: left;
|
10 |
+
max-width: 100%;
|
11 |
+
}
|
core/packages/components/src/site-scan-results/blacklist-details.js
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { get } from 'lodash';
|
5 |
+
import memize from 'memize';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WordPress dependencies
|
9 |
+
*/
|
10 |
+
import { __, sprintf } from '@wordpress/i18n';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Internal dependencies
|
14 |
+
*/
|
15 |
+
import WrappedSection from './wrapped-section';
|
16 |
+
import Detail from './detail';
|
17 |
+
|
18 |
+
const sortBlacklist = memize( ( blacklist ) => {
|
19 |
+
return [ ...blacklist ].sort( ( a, b ) => {
|
20 |
+
if ( a.status === 'blacklisted' && b.status !== 'blacklisted' ) {
|
21 |
+
return -1;
|
22 |
+
}
|
23 |
+
|
24 |
+
if ( a.status !== 'blacklisted' && b.status === 'blacklisted' ) {
|
25 |
+
return 1;
|
26 |
+
}
|
27 |
+
|
28 |
+
return 0;
|
29 |
+
} );
|
30 |
+
} );
|
31 |
+
|
32 |
+
function BlacklistDetails( { results } ) {
|
33 |
+
const blacklist = sortBlacklist( results.entries.blacklist );
|
34 |
+
const status = get( blacklist, [ 0, 'status' ] ) === 'blacklisted' ? 'warn' : 'clean';
|
35 |
+
|
36 |
+
return (
|
37 |
+
<WrappedSection type="malware" status={ status } description={ __( 'Blacklist', 'better-wp-security' ) }>
|
38 |
+
{ blacklist.map( ( entry, i ) => (
|
39 |
+
<Detail key={ i } status={ entry.status === 'blacklisted' ? 'warn' : 'clean' }>
|
40 |
+
<a href={ entry.report_details }>
|
41 |
+
{ entry.status === 'blacklisted' ?
|
42 |
+
sprintf( __( 'Domain blacklisted by %s', 'better-wp-security' ), entry.vendor.label ) :
|
43 |
+
sprintf( __( 'Domain clean by %s', 'better-wp-security' ), entry.vendor.label )
|
44 |
+
}
|
45 |
+
</a>
|
46 |
+
</Detail>
|
47 |
+
) ) }
|
48 |
+
</WrappedSection>
|
49 |
+
);
|
50 |
+
}
|
51 |
+
|
52 |
+
export default BlacklistDetails;
|
core/packages/components/src/site-scan-results/detail.js
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default function Detail( { status, children } ) {
|
2 |
+
return (
|
3 |
+
<li className={ `itsec-site-scan__detail itsec-site-scan__detail--${ status }` }>
|
4 |
+
<span>
|
5 |
+
{ children }
|
6 |
+
</span>
|
7 |
+
</li>
|
8 |
+
);
|
9 |
+
}
|
core/packages/components/src/site-scan-results/details.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default function Details( { id, isVisible, children } ) {
|
2 |
+
return (
|
3 |
+
<div className="itsec-site-scan__details" id={ id } style={ { display: isVisible ? 'block' : 'none' } }>
|
4 |
+
<ul>{ children }</ul>
|
5 |
+
</div>
|
6 |
+
);
|
7 |
+
}
|
core/packages/components/src/site-scan-results/index.js
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __, sprintf } from '@wordpress/i18n';
|
5 |
+
import { Fragment } from '@wordpress/element';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import { isWPError } from '@ithemes/security-utils';
|
11 |
+
import WPErrorDetails from './wp-error-details';
|
12 |
+
import SystemErrorDetails from './system-error-details';
|
13 |
+
import MalwareDetails from './malware-details';
|
14 |
+
import BlacklistDetails from './blacklist-details';
|
15 |
+
import KnownVulnerabilities from './known-vulnerabilities';
|
16 |
+
import './style.scss';
|
17 |
+
|
18 |
+
function SiteScanResults( { results, showSiteUrl = true, showErrorDetails = true } ) {
|
19 |
+
const siteUrl = results.url;
|
20 |
+
|
21 |
+
return (
|
22 |
+
<div className="itsec-site-scan-results">
|
23 |
+
{ showSiteUrl && siteUrl && <h4>{ sprintf( __( 'Site: %s', 'better-wp-security' ), siteUrl ) }</h4> }
|
24 |
+
|
25 |
+
{ isWPError( results ) ? <WPErrorDetails results={ results } showErrorDetails={ showErrorDetails } /> : (
|
26 |
+
<Fragment>
|
27 |
+
<SystemErrorDetails results={ results } />
|
28 |
+
<KnownVulnerabilities results={ results } />
|
29 |
+
<MalwareDetails results={ results } />
|
30 |
+
<BlacklistDetails results={ results } />
|
31 |
+
</Fragment>
|
32 |
+
) }
|
33 |
+
</div>
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
export default SiteScanResults;
|
core/packages/components/src/site-scan-results/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/components/src/site-scan-results/known-vulnerabilities.js
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __ } from '@wordpress/i18n';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies
|
8 |
+
*/
|
9 |
+
import WrappedSection from './wrapped-section';
|
10 |
+
import Detail from './detail';
|
11 |
+
|
12 |
+
function KnownVulnerabilities( { results } ) {
|
13 |
+
if ( ! results.entries.vulnerabilities.length ) {
|
14 |
+
return <WrappedSection type="vulnerabilities" status="clean" description={ __( 'Known Vulnerabilities', 'better-wp-security' ) } />;
|
15 |
+
}
|
16 |
+
|
17 |
+
return (
|
18 |
+
<WrappedSection type="vulnerabilities" status="warn" description={ __( 'Known Vulnerabilities', 'better-wp-security' ) }>
|
19 |
+
{ results.entries.vulnerabilities.map( ( entry, i ) => {
|
20 |
+
return entry.issues.map( ( issue, j ) => (
|
21 |
+
<Detail key={ `${ i }-${ j }` } status="warn">
|
22 |
+
<a href={ entry.link } target={ '_blank' }>{ issue.title }</a>
|
23 |
+
</Detail>
|
24 |
+
) );
|
25 |
+
} ) }
|
26 |
+
</WrappedSection>
|
27 |
+
);
|
28 |
+
}
|
29 |
+
|
30 |
+
export default KnownVulnerabilities;
|
core/packages/components/src/site-scan-results/malware-details.js
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __, sprintf } from '@wordpress/i18n';
|
5 |
+
import { Fragment } from '@wordpress/element';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import WrappedSection from './wrapped-section';
|
11 |
+
import Detail from './detail';
|
12 |
+
|
13 |
+
function MalwareDetails( { results } ) {
|
14 |
+
if ( ! results.entries.malware.length ) {
|
15 |
+
return <WrappedSection type="malware" status="clean" description={ __( 'Malware', 'better-wp-security' ) } />;
|
16 |
+
}
|
17 |
+
|
18 |
+
return (
|
19 |
+
<WrappedSection type="malware" status="warn" description={ __( 'Malware', 'better-wp-security' ) }>
|
20 |
+
{ results.entries.malware.map( ( entry, i ) => {
|
21 |
+
let location = entry.location;
|
22 |
+
const message = entry.message,
|
23 |
+
documentation = entry.documentation,
|
24 |
+
payload = entry.payload,
|
25 |
+
type = entry.type;
|
26 |
+
|
27 |
+
if ( ! location.match( /https?:\/\// ) ) {
|
28 |
+
location = false;
|
29 |
+
}
|
30 |
+
|
31 |
+
return (
|
32 |
+
<Detail key={ i } status="warn">
|
33 |
+
{ message }
|
34 |
+
{ location && (
|
35 |
+
<Fragment>
|
36 |
+
<br />
|
37 |
+
{ __( 'Infected URL:', 'better-wp-security' ) }
|
38 |
+
{ location && <a href={ location } target="_blank" rel="noopener noreferrer">{ location }</a> }
|
39 |
+
</Fragment>
|
40 |
+
) }
|
41 |
+
{ type.slug !== 'other' && (
|
42 |
+
<Fragment>
|
43 |
+
<br />
|
44 |
+
{ sprintf( __( 'Type: %s', 'better-wp-security' ), type.label ) }
|
45 |
+
<br />
|
46 |
+
{ __( 'Documentation:', 'better-wp-security' ) }
|
47 |
+
<a href={ documentation } target="_blank" rel="noopener noreferrer">{ documentation }</a>
|
48 |
+
</Fragment>
|
49 |
+
) }
|
50 |
+
{ payload && (
|
51 |
+
<Fragment>
|
52 |
+
<br />
|
53 |
+
{ __( 'Payload:', 'better-wp-security' ) }
|
54 |
+
<pre>{ payload }</pre>
|
55 |
+
</Fragment>
|
56 |
+
) }
|
57 |
+
</Detail>
|
58 |
+
);
|
59 |
+
} ) }
|
60 |
+
</WrappedSection>
|
61 |
+
);
|
62 |
+
}
|
63 |
+
|
64 |
+
export default MalwareDetails;
|
core/packages/components/src/site-scan-results/style.scss
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$clean: #7ad03a;
|
2 |
+
$warn: #dd3d36;
|
3 |
+
$error: #dd3d36;
|
4 |
+
|
5 |
+
.itsec-site-scan-results {
|
6 |
+
& h4 {
|
7 |
+
margin-top: 5px;
|
8 |
+
}
|
9 |
+
|
10 |
+
& .itsec-site-scan-details pre {
|
11 |
+
background-color: #eaeaea;
|
12 |
+
padding: 1em;
|
13 |
+
white-space: pre-wrap;
|
14 |
+
}
|
15 |
+
|
16 |
+
& .itsec-site-scan-results-section {
|
17 |
+
border: 1px solid #ddd;
|
18 |
+
border-bottom-color: transparent;
|
19 |
+
padding: 1em;
|
20 |
+
|
21 |
+
& > p {
|
22 |
+
margin: 0;
|
23 |
+
}
|
24 |
+
}
|
25 |
+
|
26 |
+
& .itsec-site-scan-results-section:last-child {
|
27 |
+
border-bottom-color: #ddd;
|
28 |
+
}
|
29 |
+
|
30 |
+
& .itsec-site-scan__status {
|
31 |
+
display: inline-block;
|
32 |
+
padding: 2px 6px;
|
33 |
+
color: #fff;
|
34 |
+
margin-right: 1em;
|
35 |
+
width: 60px;
|
36 |
+
text-align: center;
|
37 |
+
|
38 |
+
&.itsec-site-scan__status--clean {
|
39 |
+
background: $clean;
|
40 |
+
}
|
41 |
+
|
42 |
+
&.itsec-site-scan__status--warn {
|
43 |
+
background: $warn;
|
44 |
+
}
|
45 |
+
|
46 |
+
&.itsec-site-scan__status--error {
|
47 |
+
background: $error;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
|
51 |
+
& .itsec-site-scan__details {
|
52 |
+
& ul {
|
53 |
+
margin-left: 2.5em;
|
54 |
+
list-style: disc outside;
|
55 |
+
}
|
56 |
+
|
57 |
+
& .itsec-site-scan__detail {
|
58 |
+
width: 100%;
|
59 |
+
margin-bottom: .25em;
|
60 |
+
padding-bottom: .5em;
|
61 |
+
color: inherit;
|
62 |
+
border-bottom: 1px solid #ddd;
|
63 |
+
|
64 |
+
&:last-child {
|
65 |
+
border: none;
|
66 |
+
}
|
67 |
+
|
68 |
+
&.itsec-site-scan__detail--warn {
|
69 |
+
color: $warn;
|
70 |
+
}
|
71 |
+
|
72 |
+
&.itsec-site-scan__detail--error {
|
73 |
+
color: $error;
|
74 |
+
}
|
75 |
+
|
76 |
+
&.itsec-site-scan__detail--clean {
|
77 |
+
color: $clean;
|
78 |
+
}
|
79 |
+
|
80 |
+
& span {
|
81 |
+
color: #444;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
& .itsec-site-scan-toggle-details {
|
87 |
+
margin-left: 1em;
|
88 |
+
}
|
89 |
+
}
|
core/packages/components/src/site-scan-results/system-error-details.js
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __ } from '@wordpress/i18n';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies.
|
8 |
+
*/
|
9 |
+
import WrappedSection from './wrapped-section';
|
10 |
+
import Detail from './detail';
|
11 |
+
|
12 |
+
function SystemErrorDetails( { results } ) {
|
13 |
+
return results.errors.length > 0 && (
|
14 |
+
<WrappedSection type="system-error" status="error" description={ __( 'The scan failed to properly scan the site.', 'better-wp-security' ) }>
|
15 |
+
{ results.errors.map( ( entry, i ) => (
|
16 |
+
<Detail key={ i } status="error">
|
17 |
+
{ entry.message }
|
18 |
+
</Detail>
|
19 |
+
) ) }
|
20 |
+
</WrappedSection>
|
21 |
+
);
|
22 |
+
}
|
23 |
+
|
24 |
+
export default SystemErrorDetails;
|
core/packages/components/src/site-scan-results/wp-error-details.js
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __, sprintf } from '@wordpress/i18n';
|
5 |
+
import { Fragment } from '@wordpress/element';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Internal dependencies
|
9 |
+
*/
|
10 |
+
import WrappedSection from './wrapped-section';
|
11 |
+
import { castWPError } from '@ithemes/security-utils';
|
12 |
+
|
13 |
+
function WPErrorDetails( { results, showErrorDetails = false } ) {
|
14 |
+
const wpError = castWPError( results );
|
15 |
+
|
16 |
+
return (
|
17 |
+
<WrappedSection status="error" description={ __( 'The scan failed to properly scan the site.', 'better-wp-security' ) }>
|
18 |
+
<p>{ sprintf( __( 'Error Message: %s', 'better-wp-security' ), wpError.getErrorMessage() ) }</p>
|
19 |
+
<p>{ sprintf( __( 'Error Code: %s', 'better-wp-security' ), wpError.getErrorCode() ) }</p>
|
20 |
+
|
21 |
+
{ showErrorDetails && wpError.getErrorData() && (
|
22 |
+
<Fragment>
|
23 |
+
<p>{ __( 'If you contact support about this error, please provide the following debug details:', 'better-wp-security' ) }</p>
|
24 |
+
<pre>
|
25 |
+
{ JSON.stringify( { code: wpError.getErrorCode(), data: wpError.getErrorData() }, null, 2 ) }
|
26 |
+
</pre>
|
27 |
+
</Fragment>
|
28 |
+
) }
|
29 |
+
</WrappedSection>
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
export default WPErrorDetails;
|
core/packages/components/src/site-scan-results/wrapped-section.js
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import classnames from 'classnames';
|
5 |
+
import { isEmpty } from 'lodash';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* WordPress dependencies
|
9 |
+
*/
|
10 |
+
import { __ } from '@wordpress/i18n';
|
11 |
+
import { Fragment } from '@wordpress/element';
|
12 |
+
import { Button } from '@wordpress/components';
|
13 |
+
import { compose, withState, withInstanceId } from '@wordpress/compose';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Internal dependencies
|
17 |
+
*/
|
18 |
+
import Details from './details';
|
19 |
+
|
20 |
+
function WrappedSection( { type, status, description, isShowing, setState, instanceId, children } ) {
|
21 |
+
let statusText;
|
22 |
+
|
23 |
+
switch ( status ) {
|
24 |
+
case 'clean':
|
25 |
+
statusText = __( 'Clean', 'better-wp-security' );
|
26 |
+
break;
|
27 |
+
case 'warn':
|
28 |
+
statusText = __( 'Warn', 'better-wp-security' );
|
29 |
+
break;
|
30 |
+
case 'error':
|
31 |
+
statusText = __( 'Error', 'better-wp-security' );
|
32 |
+
break;
|
33 |
+
default:
|
34 |
+
statusText = status;
|
35 |
+
break;
|
36 |
+
}
|
37 |
+
|
38 |
+
const statusEl = ( <span className={ `itsec-site-scan__status itsec-site-scan__status--${ status }` }>{ statusText }</span> );
|
39 |
+
|
40 |
+
return (
|
41 |
+
<div className={ classnames( 'itsec-site-scan-results-section', `itsec-site-scan-results-${ type }-section` ) }>
|
42 |
+
{ isEmpty( children ) ? ( <p>{ statusEl } { description }</p> ) : (
|
43 |
+
<Fragment>
|
44 |
+
<p>
|
45 |
+
{ statusEl }
|
46 |
+
{ description }
|
47 |
+
<Button isLink className="itsec-site-scan-toggle-details" onClick={ () => setState( { isShowing: ! isShowing } ) }
|
48 |
+
aria-expanded={ isShowing } aria-controls={ `itsec-site-scan__details--${ instanceId }` }>
|
49 |
+
{ isShowing ?
|
50 |
+
__( 'Hide Details', 'better-wp-security' ) :
|
51 |
+
__( 'Show Details', 'better-wp-security' )
|
52 |
+
}
|
53 |
+
</Button>
|
54 |
+
</p>
|
55 |
+
<Details id={ `itsec-site-scan__details--${ instanceId }` } isVisible={ isShowing }>
|
56 |
+
{ children }
|
57 |
+
</Details>
|
58 |
+
</Fragment>
|
59 |
+
) }
|
60 |
+
</div>
|
61 |
+
);
|
62 |
+
}
|
63 |
+
|
64 |
+
export default compose( [
|
65 |
+
withState( { isShowing: false } ),
|
66 |
+
withInstanceId,
|
67 |
+
] )( WrappedSection );
|
core/packages/hocs/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/hocs/src/index.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export { default as withProps } from './with-props';
|
2 |
+
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';
|
core/packages/hocs/src/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/hocs/src/with-debounce-handler.js
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { debounce } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress dependencies
|
8 |
+
*/
|
9 |
+
import { Component } from '@wordpress/element';
|
10 |
+
import { createHigherOrderComponent } from '@wordpress/compose';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Higher-order component that debounces an action.
|
14 |
+
*
|
15 |
+
* @Link https://github.com/deepsweet/hocs/tree/master/packages/debounce-handler (MIT)
|
16 |
+
*
|
17 |
+
* @param {string} handlerName
|
18 |
+
* @param {number|Function} wait
|
19 |
+
* @param {Object} [options]
|
20 |
+
* @return {WPComponent} Debounced component.
|
21 |
+
*/
|
22 |
+
export default function withDebounceHandler( handlerName, wait, options = {} ) {
|
23 |
+
return createHigherOrderComponent( ( WrappedComponent ) => {
|
24 |
+
return class Wrapper extends Component {
|
25 |
+
constructor() {
|
26 |
+
super( ...arguments );
|
27 |
+
|
28 |
+
this.debouncedPropInvoke = debounce(
|
29 |
+
( ...args ) => this.props[ handlerName ]( ...args ),
|
30 |
+
typeof wait === 'function' ? wait( this.props ) : wait,
|
31 |
+
options
|
32 |
+
);
|
33 |
+
|
34 |
+
this.handler = ( e, ...rest ) => {
|
35 |
+
if ( e && typeof e.persist === 'function' ) {
|
36 |
+
e.persist();
|
37 |
+
}
|
38 |
+
|
39 |
+
return this.debouncedPropInvoke( e, ...rest );
|
40 |
+
};
|
41 |
+
}
|
42 |
+
|
43 |
+
componentWillUnmount() {
|
44 |
+
this.debouncedPropInvoke.cancel();
|
45 |
+
}
|
46 |
+
|
47 |
+
render() {
|
48 |
+
const props = {
|
49 |
+
...this.props,
|
50 |
+
[ handlerName ]: this.handler,
|
51 |
+
};
|
52 |
+
|
53 |
+
return <WrappedComponent { ...props } />;
|
54 |
+
}
|
55 |
+
};
|
56 |
+
}, 'withDebounceHandler' );
|
57 |
+
}
|
core/packages/hocs/src/with-interval.js
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isFunction } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* WordPress Dependencies
|
8 |
+
*/
|
9 |
+
import { Component } from '@wordpress/element';
|
10 |
+
import { createHigherOrderComponent } from '@wordpress/compose';
|
11 |
+
|
12 |
+
export default function withInterval( delay, cb ) {
|
13 |
+
let intervals;
|
14 |
+
|
15 |
+
if ( isFunction( cb ) ) {
|
16 |
+
intervals = [ { delay, cb } ];
|
17 |
+
} else {
|
18 |
+
intervals = delay;
|
19 |
+
}
|
20 |
+
|
21 |
+
return createHigherOrderComponent( ( WrappedComponent ) => {
|
22 |
+
return class Wrapper extends Component {
|
23 |
+
constructor() {
|
24 |
+
super( ...arguments );
|
25 |
+
|
26 |
+
this.intervalIds = [];
|
27 |
+
}
|
28 |
+
|
29 |
+
componentDidMount() {
|
30 |
+
for ( const interval of intervals ) {
|
31 |
+
( ( callback ) => {
|
32 |
+
this.intervalIds.push( setInterval( () => callback( this.props ), interval.delay ) );
|
33 |
+
} )( interval.cb );
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
componentWillUnmount() {
|
38 |
+
this.intervalIds.forEach( clearInterval );
|
39 |
+
}
|
40 |
+
|
41 |
+
render() {
|
42 |
+
return <WrappedComponent { ...this.props } />;
|
43 |
+
}
|
44 |
+
};
|
45 |
+
}, 'withInterval' );
|
46 |
+
}
|
core/packages/hocs/src/with-prop-change-callback.js
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isFunction } from 'lodash';
|
5 |
+
/**
|
6 |
+
* WordPress dependencies
|
7 |
+
*/
|
8 |
+
import { Component } from '@wordpress/element';
|
9 |
+
import { createHigherOrderComponent } from '@wordpress/compose';
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Higher-order component that allows for firing an action after certain props have changed.
|
13 |
+
*
|
14 |
+
* @param {...string|{prop: string, cb: Function}} prop Prop to listen to, or object with prop to listener to and callback to execute.
|
15 |
+
* @param {Function} [cb] Function to call when prop changes.
|
16 |
+
*
|
17 |
+
* @return {WPComponent} Component with prop change listeners.
|
18 |
+
*/
|
19 |
+
export default function withPropChangeCallback( prop, cb ) {
|
20 |
+
let listeners;
|
21 |
+
|
22 |
+
if ( isFunction( cb ) ) {
|
23 |
+
listeners = [ { prop, cb } ];
|
24 |
+
} else {
|
25 |
+
listeners = arguments;
|
26 |
+
}
|
27 |
+
|
28 |
+
return createHigherOrderComponent( ( WrappedComponent ) => {
|
29 |
+
return class Wrapper extends Component {
|
30 |
+
componentDidUpdate( prevProps ) {
|
31 |
+
for ( const listener of listeners ) {
|
32 |
+
if ( this.props[ listener.prop ] !== prevProps[ listener.prop ] ) {
|
33 |
+
listener.cb( prevProps[ listener.prop ], this.props );
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
render() {
|
39 |
+
return <WrappedComponent { ...this.props } />;
|
40 |
+
}
|
41 |
+
};
|
42 |
+
}, 'withProps' );
|
43 |
+
}
|
core/packages/hocs/src/with-props.js
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { Component } from '@wordpress/element';
|
5 |
+
import { createHigherOrderComponent } from '@wordpress/compose';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Higher-order component that applies props to the inner component.
|
9 |
+
*
|
10 |
+
* @param {Object} props
|
11 |
+
*
|
12 |
+
* @return {WPComponent} Debounced component.
|
13 |
+
*/
|
14 |
+
export default function withProps( props ) {
|
15 |
+
return createHigherOrderComponent( ( WrappedComponent ) => {
|
16 |
+
return class Wrapper extends Component {
|
17 |
+
render() {
|
18 |
+
return <WrappedComponent { ...this.props } { ...props } />;
|
19 |
+
}
|
20 |
+
};
|
21 |
+
}, 'withProps' );
|
22 |
+
}
|
core/packages/hocs/src/with-width.js
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { Component, findDOMNode } from '@wordpress/element';
|
5 |
+
import { createHigherOrderComponent } from '@wordpress/compose';
|
6 |
+
|
7 |
+
/*
|
8 |
+
* A simple HOC that provides facility for listening to container resizes.
|
9 |
+
*/
|
10 |
+
const withWidth = createHigherOrderComponent( ( WrappedComponent ) => {
|
11 |
+
return class WithWidth extends Component {
|
12 |
+
static defaultProps = {
|
13 |
+
measureBeforeMount: false,
|
14 |
+
};
|
15 |
+
|
16 |
+
state = {
|
17 |
+
width: 1280,
|
18 |
+
};
|
19 |
+
|
20 |
+
mounted = false;
|
21 |
+
ref = null;
|
22 |
+
|
23 |
+
componentDidMount() {
|
24 |
+
this.mounted = true;
|
25 |
+
|
26 |
+
window.addEventListener( 'resize', this.onWindowResize );
|
27 |
+
document.getElementById( 'collapse-button' ).addEventListener( 'click', this.onWindowResize );
|
28 |
+
this.onWindowResize();
|
29 |
+
}
|
30 |
+
|
31 |
+
componentWillUnmount() {
|
32 |
+
this.mounted = false;
|
33 |
+
window.removeEventListener( 'resize', this.onWindowResize );
|
34 |
+
document.getElementById( 'collapse-button' ).removeEventListener( 'click', this.onWindowResize );
|
35 |
+
}
|
36 |
+
|
37 |
+
onWindowResize = () => {
|
38 |
+
if ( ! this.mounted ) {
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
// eslint-disable-next-line react/no-find-dom-node
|
43 |
+
const node = findDOMNode( this );
|
44 |
+
|
45 |
+
if ( node instanceof window.HTMLElement ) {
|
46 |
+
const width = node.offsetWidth;
|
47 |
+
this.setState( { width } );
|
48 |
+
}
|
49 |
+
};
|
50 |
+
|
51 |
+
render() {
|
52 |
+
const { measureBeforeMount, ...rest } = this.props;
|
53 |
+
if ( measureBeforeMount && ! this.mounted ) {
|
54 |
+
return (
|
55 |
+
<div className={ this.props.className } style={ this.props.style } />
|
56 |
+
);
|
57 |
+
}
|
58 |
+
|
59 |
+
return <WrappedComponent { ...rest } width={ this.state.width + 20 } />;
|
60 |
+
}
|
61 |
+
};
|
62 |
+
}, 'withWidth' );
|
63 |
+
|
64 |
+
export default withWidth;
|
core/packages/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/style-guide/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/style-guide/src/animations.scss
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@keyframes itsec-animation-fade-in-constant {
|
2 |
+
from {
|
3 |
+
opacity: 0;
|
4 |
+
}
|
5 |
+
|
6 |
+
to {
|
7 |
+
opacity: 1;
|
8 |
+
}
|
9 |
+
}
|
core/packages/style-guide/src/breakpoints.scss
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$huge: 1440px;
|
2 |
+
$wide: 1280px;
|
3 |
+
$large: 960px;
|
4 |
+
$medium: 782px;
|
5 |
+
$small: 600px;
|
6 |
+
$mobile: 480px;
|
core/packages/style-guide/src/colors.js
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const PRIMARYS = Object.freeze( [
|
2 |
+
/*'#0083E3',
|
3 |
+
'#FFCE00',
|
4 |
+
'#52c551',
|
5 |
+
'#7ABEED',*/
|
6 |
+
'#e67e22', // carrot
|
7 |
+
'#2ecc71', // emerald
|
8 |
+
'#3498db', // peter river
|
9 |
+
'#e74c3c', // alizarin
|
10 |
+
'#8e44ad', // wisteria
|
11 |
+
'#1abc9c', // turquoise
|
12 |
+
'#2c3e50', // midnight blue
|
13 |
+
] );
|
core/packages/style-guide/src/colors.scss
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$main-blue: #0081E3;
|
2 |
+
$light-blue: #7ABEED;
|
3 |
+
|
4 |
+
// Hugo's new WordPress shades of gray, from http://codepen.io/hugobaeta/pen/grJjVp.
|
5 |
+
$black: #000;
|
6 |
+
$dark-gray-900: #191e23;
|
7 |
+
$dark-gray-800: #23282d;
|
8 |
+
$dark-gray-700: #32373c;
|
9 |
+
$dark-gray-600: #40464d;
|
10 |
+
$dark-gray-500: #555d66; // Use this most of the time for dark items.
|
11 |
+
$dark-gray-400: #606a73;
|
12 |
+
$dark-gray-300: #6c7781; // Lightest gray that can be used for AA text contrast.
|
13 |
+
$dark-gray-200: #7e8993;
|
14 |
+
$dark-gray-150: #8d96a0; // Lightest gray that can be used for AA non-text contrast.
|
15 |
+
$dark-gray-100: #8f98a1;
|
16 |
+
$light-gray-900: #a2aab2;
|
17 |
+
$light-gray-800: #b5bcc2;
|
18 |
+
$light-gray-700: #ccd0d4;
|
19 |
+
$light-gray-600: #d7dade;
|
20 |
+
$light-gray-500: #e2e4e7; // Good for "grayed" items and borders.
|
21 |
+
$light-gray-400: #e8eaeb; // Good for "readonly" input fields and special text selection.
|
22 |
+
$light-gray-300: #edeff0;
|
23 |
+
$light-gray-200: #f3f4f5;
|
24 |
+
$light-gray-100: #f8f9f9;
|
25 |
+
$white: #fff;
|
26 |
+
|
27 |
+
// Dark opacities, for use with light themes.
|
28 |
+
$dark-opacity-900: rgba(#000510, 0.9);
|
29 |
+
$dark-opacity-800: rgba(#00000a, 0.85);
|
30 |
+
$dark-opacity-700: rgba(#06060b, 0.8);
|
31 |
+
$dark-opacity-600: rgba(#000913, 0.75);
|
32 |
+
$dark-opacity-500: rgba(#0a1829, 0.7);
|
33 |
+
$dark-opacity-400: rgba(#0a1829, 0.65);
|
34 |
+
$dark-opacity-300: rgba(#0e1c2e, 0.62);
|
35 |
+
$dark-opacity-200: rgba(#162435, 0.55);
|
36 |
+
$dark-opacity-100: rgba(#223443, 0.5);
|
37 |
+
$dark-opacity-light-900: rgba(#304455, 0.45);
|
38 |
+
$dark-opacity-light-800: rgba(#425863, 0.4);
|
39 |
+
$dark-opacity-light-700: rgba(#667886, 0.35);
|
40 |
+
$dark-opacity-light-600: rgba(#7b86a2, 0.3);
|
41 |
+
$dark-opacity-light-500: rgba(#9197a2, 0.25);
|
42 |
+
$dark-opacity-light-400: rgba(#95959c, 0.2);
|
43 |
+
$dark-opacity-light-300: rgba(#829493, 0.15);
|
44 |
+
$dark-opacity-light-200: rgba(#8b8b96, 0.1);
|
45 |
+
$dark-opacity-light-100: rgba(#747474, 0.05);
|
46 |
+
|
47 |
+
// Light opacities, for use with dark themes.
|
48 |
+
$light-opacity-900: rgba($white, 1);
|
49 |
+
$light-opacity-800: rgba($white, 0.9);
|
50 |
+
$light-opacity-700: rgba($white, 0.85);
|
51 |
+
$light-opacity-600: rgba($white, 0.8);
|
52 |
+
$light-opacity-500: rgba($white, 0.75);
|
53 |
+
$light-opacity-400: rgba($white, 0.7);
|
54 |
+
$light-opacity-300: rgba($white, 0.65);
|
55 |
+
$light-opacity-200: rgba($white, 0.6);
|
56 |
+
$light-opacity-100: rgba($white, 0.55);
|
57 |
+
$light-opacity-light-900: rgba($white, 0.5);
|
58 |
+
$light-opacity-light-800: rgba($white, 0.45);
|
59 |
+
$light-opacity-light-700: rgba($white, 0.4);
|
60 |
+
$light-opacity-light-600: rgba($white, 0.35);
|
61 |
+
$light-opacity-light-500: rgba($white, 0.3);
|
62 |
+
$light-opacity-light-400: rgba($white, 0.25);
|
63 |
+
$light-opacity-light-300: rgba($white, 0.2);
|
64 |
+
$light-opacity-light-200: rgba($white, 0.15);
|
65 |
+
$light-opacity-light-100: rgba($white, 0.1);
|
66 |
+
|
67 |
+
// Additional colors.
|
68 |
+
// Some are from https://make.wordpress.org/design/handbook/foundations/colors/.
|
69 |
+
$blue-wordpress-700: #00669b;
|
70 |
+
$blue-dark-900: #0071a1;
|
71 |
+
|
72 |
+
$blue-medium-900: #006589;
|
73 |
+
$blue-medium-800: #00739c;
|
74 |
+
$blue-medium-700: #007fac;
|
75 |
+
$blue-medium-600: #008dbe;
|
76 |
+
$blue-medium-500: #00a0d2;
|
77 |
+
$blue-medium-400: #33b3db;
|
78 |
+
$blue-medium-300: #66c6e4;
|
79 |
+
$blue-medium-200: #bfe7f3;
|
80 |
+
$blue-medium-100: #e5f5fa;
|
81 |
+
$blue-medium-highlight: #b3e7fe;
|
82 |
+
$blue-medium-focus: #007cba;
|
83 |
+
|
84 |
+
// Alert colors.
|
85 |
+
$alert-yellow: #f0b849;
|
86 |
+
$alert-orange: #f78b53;
|
87 |
+
$alert-red: #d94f4f;
|
88 |
+
$alert-green: #4ab866;
|
89 |
+
$alert-blue: $blue-medium-500;
|
core/packages/style-guide/src/index.js
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
export * from './colors';
|
core/packages/style-guide/src/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/style-guide/src/mixins.scss
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import "colors.scss";
|
2 |
+
|
3 |
+
@mixin sticky-table($spacing: 1em) {
|
4 |
+
& thead th {
|
5 |
+
position: sticky;
|
6 |
+
top: 0;
|
7 |
+
padding-top: $spacing;
|
8 |
+
padding-bottom: $spacing;
|
9 |
+
background: white;
|
10 |
+
color: $dark-gray-500;
|
11 |
+
text-transform: uppercase;
|
12 |
+
font-weight: normal;
|
13 |
+
border-bottom: 1px solid $light-gray-500;
|
14 |
+
border-top: 1px solid $light-gray-500;
|
15 |
+
}
|
16 |
+
|
17 |
+
& th,
|
18 |
+
& td {
|
19 |
+
text-align: left;
|
20 |
+
padding: $spacing;
|
21 |
+
|
22 |
+
&:first-child {
|
23 |
+
}
|
24 |
+
|
25 |
+
&:last-child {
|
26 |
+
text-align: right;
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
& tbody th {
|
31 |
+
font-weight: normal;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
@mixin bordered-button($style: 'blue') {
|
36 |
+
$primary: $main-blue;
|
37 |
+
$alternate: #E1f2fc;
|
38 |
+
|
39 |
+
@if ($style == 'green') {
|
40 |
+
$primary: $alert-green;
|
41 |
+
$alternate: #e4f4e8;
|
42 |
+
}
|
43 |
+
|
44 |
+
@if ($style == 'red') {
|
45 |
+
$primary: $alert-red;
|
46 |
+
$alternate: #fcefef;
|
47 |
+
}
|
48 |
+
|
49 |
+
@if ($style == 'orange') {
|
50 |
+
$primary: $alert-orange;
|
51 |
+
$alternate: #fef1ea;
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
padding: .5em 1em;
|
56 |
+
background: $alternate;
|
57 |
+
color: $primary;
|
58 |
+
border: 1px solid $primary;
|
59 |
+
transition: background-color 300ms, color 150ms;
|
60 |
+
|
61 |
+
&:hover {
|
62 |
+
background: $primary;
|
63 |
+
color: white;
|
64 |
+
}
|
65 |
+
|
66 |
+
&.is-busy {
|
67 |
+
animation: components-button__busy-animation 2500ms infinite linear;
|
68 |
+
background-size: 100px 100%;
|
69 |
+
background-image: repeating-linear-gradient(-45deg, $alternate, #fff 11px, #fff 10px, $alternate 20px);
|
70 |
+
opacity: 1;
|
71 |
+
|
72 |
+
&:hover {
|
73 |
+
color: $primary;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
}
|
core/packages/utils/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/utils/src/error-response.js
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* WordPress dependencies
|
3 |
+
*/
|
4 |
+
import { __ } from '@wordpress/i18n';
|
5 |
+
|
6 |
+
export default class ErrorResponse extends Error {
|
7 |
+
constructor( response, ...args ) {
|
8 |
+
super( response.message || __( 'An unknown error occurred.', 'better-wp-security' ), ...args );
|
9 |
+
|
10 |
+
if ( Error.captureStackTrace ) {
|
11 |
+
Error.captureStackTrace( this, ErrorResponse );
|
12 |
+
}
|
13 |
+
|
14 |
+
this.__response = response;
|
15 |
+
|
16 |
+
for ( const prop in response ) {
|
17 |
+
if ( response.hasOwnProperty( prop ) ) {
|
18 |
+
Object.defineProperty( this, prop, {
|
19 |
+
value: response[ prop ],
|
20 |
+
configurable: true,
|
21 |
+
enumerable: true,
|
22 |
+
writable: true,
|
23 |
+
} );
|
24 |
+
}
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
toString() {
|
29 |
+
return this.__response.toString();
|
30 |
+
}
|
31 |
+
|
32 |
+
getResponse() {
|
33 |
+
return this.__response;
|
34 |
+
}
|
35 |
+
}
|
core/packages/utils/src/index.js
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
import { isPlainObject } from 'lodash';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Internal dependencies
|
8 |
+
*/
|
9 |
+
import WPError from './wp-error';
|
10 |
+
import ErrorResponse from './error-response';
|
11 |
+
|
12 |
+
export function makeUrlRelative( baseUrl, target ) {
|
13 |
+
let rel = target.replace( baseUrl, '' );
|
14 |
+
|
15 |
+
if ( rel.charAt( 0 ) !== '/' ) {
|
16 |
+
rel = '/' + rel;
|
17 |
+
}
|
18 |
+
|
19 |
+
return rel;
|
20 |
+
}
|
21 |
+
|
22 |
+
export function shortenNumber( number ) {
|
23 |
+
if ( number <= 999 ) {
|
24 |
+
return number.toString();
|
25 |
+
}
|
26 |
+
|
27 |
+
if ( number <= 9999 ) {
|
28 |
+
const dec = ( number / 1000 ),
|
29 |
+
fixed = dec.toFixed( 1 );
|
30 |
+
|
31 |
+
if ( fixed.charAt( fixed.length - 1 ) === '0' ) {
|
32 |
+
return fixed.replace( '.0', 'k' );
|
33 |
+
}
|
34 |
+
|
35 |
+
return `${ fixed }k`;
|
36 |
+
}
|
37 |
+
|
38 |
+
if ( number <= 99999 ) {
|
39 |
+
return number.toString().substring( 0, 2 ) + 'k';
|
40 |
+
}
|
41 |
+
|
42 |
+
if ( number <= 999999 ) {
|
43 |
+
return number.toString().substring( 0, 3 ) + 'k';
|
44 |
+
}
|
45 |
+
|
46 |
+
if ( number <= 9999999 ) {
|
47 |
+
const dec = ( number / 1000000 ),
|
48 |
+
fixed = dec.toFixed( 1 );
|
49 |
+
|
50 |
+
if ( fixed.charAt( fixed.length - 1 ) === '0' ) {
|
51 |
+
return fixed.replace( '.0', 'm' );
|
52 |
+
}
|
53 |
+
|
54 |
+
return `${ fixed }m`;
|
55 |
+
}
|
56 |
+
|
57 |
+
if ( number <= 99999999 ) {
|
58 |
+
return number.toString().substring( 0, 2 ) + 'm';
|
59 |
+
}
|
60 |
+
|
61 |
+
if ( number <= 999999999 ) {
|
62 |
+
return number.toString().substring( 0, 3 ) + 'm';
|
63 |
+
}
|
64 |
+
|
65 |
+
if ( number <= 9999999999 ) {
|
66 |
+
const dec = ( number / 1000000000 ),
|
67 |
+
fixed = dec.toFixed( 1 );
|
68 |
+
|
69 |
+
if ( fixed.charAt( fixed.length - 1 ) === '0' ) {
|
70 |
+
return fixed.replace( '.0', 'b' );
|
71 |
+
}
|
72 |
+
|
73 |
+
return `${ fixed }b`;
|
74 |
+
}
|
75 |
+
|
76 |
+
return number;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Is the given value likely a WP Error object.
|
81 |
+
*
|
82 |
+
* @param {*} object
|
83 |
+
* @return {boolean} Whether it was an error.
|
84 |
+
*/
|
85 |
+
export function isWPError( object ) {
|
86 |
+
if ( ! isPlainObject( object ) ) {
|
87 |
+
return false;
|
88 |
+
}
|
89 |
+
|
90 |
+
const keys = Object.keys( object );
|
91 |
+
|
92 |
+
if ( keys.length !== 2 ) {
|
93 |
+
return false;
|
94 |
+
}
|
95 |
+
|
96 |
+
return keys.includes( 'errors' ) && keys.includes( 'error_data' );
|
97 |
+
}
|
98 |
+
|
99 |
+
export function isApiError( object ) {
|
100 |
+
if ( ! isPlainObject( object ) ) {
|
101 |
+
return false;
|
102 |
+
}
|
103 |
+
|
104 |
+
const keys = Object.keys( object );
|
105 |
+
|
106 |
+
if ( keys.length !== 3 && keys.length !== 4 ) {
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
|
110 |
+
if ( keys.length === 4 && ! keys.includes( 'additional_errors' ) ) {
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
|
114 |
+
return keys.includes( 'code' ) && keys.includes( 'message' ) && keys.includes( 'data' );
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Cast to a WPError instance.
|
119 |
+
*
|
120 |
+
* @param {*} object
|
121 |
+
* @return {WPError} WPError instance.
|
122 |
+
*/
|
123 |
+
export function castWPError( object ) {
|
124 |
+
if ( isWPError( object ) ) {
|
125 |
+
return WPError.fromPHPObject( object );
|
126 |
+
}
|
127 |
+
|
128 |
+
if ( isApiError( object ) ) {
|
129 |
+
return WPError.fromApiError( object );
|
130 |
+
}
|
131 |
+
|
132 |
+
return new WPError();
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Convert an entries iterator to an object.
|
137 |
+
*
|
138 |
+
* @param {iterator} entries
|
139 |
+
*
|
140 |
+
* @return {Object} Object with entry[0] as the key and entry[1] as the value.
|
141 |
+
*/
|
142 |
+
export function entriesToObject( entries ) {
|
143 |
+
const obj = {};
|
144 |
+
|
145 |
+
for ( const [ key, val ] of entries ) {
|
146 |
+
obj[ key ] = val;
|
147 |
+
}
|
148 |
+
|
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 |
+
}
|
161 |
+
|
162 |
+
throw new ErrorResponse( response );
|
163 |
+
}
|
164 |
+
|
165 |
+
export const MYSTERY_MAN_AVATAR = 'https://secure.gravatar.com/avatar/d7a973c7dab26985da5f961be7b74480?s=96&d=mm&f=y&r=g';
|
core/packages/utils/src/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/utils/src/test/index.js
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { makeUrlRelative, shortenNumber } from '@ithemes/security-utils';
|
2 |
+
|
3 |
+
describe( 'makeUrlRelative', () => {
|
4 |
+
it( 'should return the relative url', () => {
|
5 |
+
const relative = makeUrlRelative( 'https://security.test/', 'https://security.test/wp-json/' );
|
6 |
+
|
7 |
+
expect( relative ).toEqual( '/wp-json/' );
|
8 |
+
} );
|
9 |
+
|
10 |
+
it( 'should return the relative url when base has a path', () => {
|
11 |
+
const relative = makeUrlRelative( 'https://security.test/wp-json/', 'https://security.test/wp-json/wp/v2/' );
|
12 |
+
expect( relative ).toEqual( '/wp/v2/' );
|
13 |
+
} );
|
14 |
+
} );
|
15 |
+
|
16 |
+
describe( 'shortenNumber', () => {
|
17 |
+
const cases = [
|
18 |
+
[ 5, '5' ],
|
19 |
+
[ 50, '50' ],
|
20 |
+
[ 53, '53' ],
|
21 |
+
[ 100, '100' ],
|
22 |
+
[ 152, '152' ],
|
23 |
+
[ 1000, '1k' ],
|
24 |
+
[ 1005, '1k' ],
|
25 |
+
[ 1025, '1k' ],
|
26 |
+
[ 1125, '1.1k' ],
|
27 |
+
[ 5232, '5.2k' ],
|
28 |
+
[ 12000, '12k' ],
|
29 |
+
[ 12345, '12k' ],
|
30 |
+
[ 123456, '123k' ],
|
31 |
+
[ 1000000, '1m' ],
|
32 |
+
[ 1234567, '1.2m' ],
|
33 |
+
[ 12345678, '12m' ],
|
34 |
+
[ 123456789, '123m' ],
|
35 |
+
[ 1234567890, '1.2b' ],
|
36 |
+
];
|
37 |
+
|
38 |
+
it.each( cases )( 'should convert %d to %s', ( number, shortened ) => {
|
39 |
+
expect( shortenNumber( number ) ).toBe( shortened );
|
40 |
+
} );
|
41 |
+
} );
|
core/packages/utils/src/test/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/utils/src/wp-error.js
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default class WPError {
|
2 |
+
#errors = {};
|
3 |
+
#errorData = {};
|
4 |
+
|
5 |
+
/**
|
6 |
+
* WP Error object.
|
7 |
+
*
|
8 |
+
* Close to the WordPress PHP WP Error object. Really only meant to be used for interfacing
|
9 |
+
* with server generated PHP errors, not for JS programming.
|
10 |
+
*
|
11 |
+
* @param {string} [code]
|
12 |
+
* @param {string} [message]
|
13 |
+
* @param {*} [data]
|
14 |
+
*/
|
15 |
+
constructor( code = undefined, message = undefined, data = undefined ) {
|
16 |
+
if ( ! code ) {
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
if ( message ) {
|
21 |
+
this.#errors[ code ] = [ message ];
|
22 |
+
}
|
23 |
+
|
24 |
+
if ( data ) {
|
25 |
+
this.#errorData[ code ] = data;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Create a WPError from a PHP object.
|
31 |
+
*
|
32 |
+
* @param {Object} object WPError like object. {@see isWPError}
|
33 |
+
* @return {WPError} WPError instance.
|
34 |
+
*/
|
35 |
+
static fromPHPObject( object ) {
|
36 |
+
const error = new WPError();
|
37 |
+
error.#errors = object.errors;
|
38 |
+
error.#errorData = object.error_data;
|
39 |
+
|
40 |
+
return error;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Create a WPError from a REST API error.
|
45 |
+
*
|
46 |
+
* @param {Object} object Api WP Error like object. {@see isApiError}
|
47 |
+
* @return {WPError} WPError instance.
|
48 |
+
*/
|
49 |
+
static fromApiError( object ) {
|
50 |
+
const error = new WPError();
|
51 |
+
error.#errors[ object.code ] = [ object.message ];
|
52 |
+
error.#errorData[ object.code ] = object.data;
|
53 |
+
|
54 |
+
if ( object.additional_errors ) {
|
55 |
+
for ( const additional of object.additional_errors ) {
|
56 |
+
error.#errors[ additional.code ] = [ additional.message ];
|
57 |
+
error.#errorData[ additional.code ] = additional.data;
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
return error;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get all the codes.
|
66 |
+
*
|
67 |
+
* @return {string[]} Array of error codes.
|
68 |
+
*/
|
69 |
+
getErrorCodes = () => {
|
70 |
+
return Object.keys( this.#errors );
|
71 |
+
};
|
72 |
+
|
73 |
+
/**
|
74 |
+
*Get the main error code.
|
75 |
+
*
|
76 |
+
* @return {string|undefined} Primary error code or undefined if no errors.
|
77 |
+
*/
|
78 |
+
getErrorCode = () => {
|
79 |
+
return this.getErrorCodes()[ 0 ];
|
80 |
+
};
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Get all the error messages.
|
84 |
+
*
|
85 |
+
* @param {string} [code] Optionally limit to errors from a specific code.
|
86 |
+
* @return {Array<string>} Array of error messages.
|
87 |
+
*/
|
88 |
+
getErrorMessages = ( code = undefined ) => {
|
89 |
+
if ( code ) {
|
90 |
+
return this.#errors[ code ];
|
91 |
+
}
|
92 |
+
|
93 |
+
const messages = [];
|
94 |
+
|
95 |
+
for ( const errorCode in this.#errors ) {
|
96 |
+
if ( this.#errors.hasOwnProperty( errorCode ) ) {
|
97 |
+
messages.concat( this.#errors[ errorCode ] );
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
return messages;
|
102 |
+
};
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Get the error message.
|
106 |
+
*
|
107 |
+
* @param {string} [code] Optionally specify the code.
|
108 |
+
* @return {*|undefined} Primary error message.
|
109 |
+
*/
|
110 |
+
getErrorMessage = ( code = undefined ) => {
|
111 |
+
code = code || this.getErrorCode();
|
112 |
+
|
113 |
+
return this.getErrorMessages( code )[ 0 ];
|
114 |
+
};
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Get error data.
|
118 |
+
*
|
119 |
+
* @param {string} [code]
|
120 |
+
* @return {*|undefined} Error data for this code, or undefined.
|
121 |
+
*/
|
122 |
+
getErrorData = ( code = undefined ) => {
|
123 |
+
code = code || this.getErrorCode();
|
124 |
+
|
125 |
+
return this.#errorData[ code ];
|
126 |
+
};
|
127 |
+
}
|
core/packages/webpack/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/babel.js
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
presets: [ '@wordpress/default' ],
|
3 |
+
plugins: [
|
4 |
+
[ '@wordpress/babel-plugin-import-jsx-pragma', {
|
5 |
+
scopeVariable: 'createElement',
|
6 |
+
source: '@wordpress/element',
|
7 |
+
isDefault: false,
|
8 |
+
} ],
|
9 |
+
[ '@babel/plugin-transform-react-jsx', {
|
10 |
+
pragma: 'createElement',
|
11 |
+
} ],
|
12 |
+
'@babel/plugin-proposal-class-properties',
|
13 |
+
'@babel/plugin-syntax-dynamic-import',
|
14 |
+
],
|
15 |
+
};
|
core/packages/webpack/src/config/index.js
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );
|
2 |
+
const ManifestPlugin = require( 'webpack-manifest-plugin' );
|
3 |
+
const FilterWarningsPlugin = require( 'webpack-filter-warnings-plugin' );
|
4 |
+
const CustomTemplatedPathPlugin = require( '../custom-templated-path-webpack-plugin' );
|
5 |
+
const DynamicPublicPath = require( '../dynamic-public-path' );
|
6 |
+
const StyleOnlyEntryPlugin = require( '../style-only-entry-plugin' );
|
7 |
+
const SplitChunkName = require( '../split-chunk-name' );
|
8 |
+
const { generate: generateManifest, serialize: serializeManifest } = require( '../manifest' );
|
9 |
+
const wpExternals = require( '../wp-externals' );
|
10 |
+
const debug = process.env.NODE_ENV !== 'production';
|
11 |
+
const glob = require( 'glob' );
|
12 |
+
const path = require( 'path' );
|
13 |
+
const autoprefixer = require( 'autoprefixer' );
|
14 |
+
const webpack = require( 'webpack' );
|
15 |
+
|
16 |
+
module.exports = function makeConfig( directory, pro ) {
|
17 |
+
/*
|
18 |
+
Convert the wildcard entry points into an entry object suitable for Webpack consumption.
|
19 |
+
|
20 |
+
This requires an object where the key is the path to the destination file without a file extension
|
21 |
+
and the value is the path to the source file.
|
22 |
+
|
23 |
+
For Example:
|
24 |
+
[ 'pro/dashboard/entries/dashboard.js' ]
|
25 |
+
{ 'dashboard/dashboard': './pro/dashboard/entries/dashboard.js' }
|
26 |
+
*/
|
27 |
+
const entries = glob.sync( 'core/modules/**/entries/*.js' ).reduce( function( acc, entry ) {
|
28 |
+
const baseName = path.basename( entry, '.js' );
|
29 |
+
let out = path.join( entry, '..', '..', baseName );
|
30 |
+
out = out.replace( /^core\/modules\//, '' );
|
31 |
+
|
32 |
+
// The entry needs to be marked as relative to the current directory.
|
33 |
+
acc[ out ] = './' + entry;
|
34 |
+
|
35 |
+
return acc;
|
36 |
+
}, {} );
|
37 |
+
|
38 |
+
if ( pro ) {
|
39 |
+
Object.assign( entries, glob.sync( 'pro/**/entries/*.js' ).reduce( function( acc, entry ) {
|
40 |
+
const baseName = path.basename( entry, '.js' );
|
41 |
+
let out = path.join( entry, '..', '..', baseName );
|
42 |
+
out = out.replace( /^pro\//, '' );
|
43 |
+
|
44 |
+
acc[ out ] = './' + entry;
|
45 |
+
|
46 |
+
return acc;
|
47 |
+
}, {} ) );
|
48 |
+
}
|
49 |
+
|
50 |
+
const config = {
|
51 |
+
context: directory,
|
52 |
+
devtool: debug ? 'inline-sourcemap' : false,
|
53 |
+
mode: debug ? 'development' : 'production',
|
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' ),
|
60 |
+
filename: debug ? '[name].js' : '[name].min.js',
|
61 |
+
jsonpFunction: 'itsecWebpackJsonP',
|
62 |
+
library: [ 'itsec', '[module]', '[entry]' ],
|
63 |
+
libraryTarget: 'this',
|
64 |
+
},
|
65 |
+
externals: [
|
66 |
+
...wpExternals,
|
67 |
+
function( context, request, callback ) {
|
68 |
+
if ( /^@ithemes\/security\./.test( request ) ) {
|
69 |
+
const parts = request.split( '.' );
|
70 |
+
const external = {
|
71 |
+
this: [ 'itsec', parts[ 1 ], parts[ 2 ] ],
|
72 |
+
};
|
73 |
+
|
74 |
+
callback( null, external, 'this' );
|
75 |
+
} else {
|
76 |
+
callback();
|
77 |
+
}
|
78 |
+
},
|
79 |
+
],
|
80 |
+
module: {
|
81 |
+
rules: [
|
82 |
+
{ parser: { amd: false } },
|
83 |
+
{
|
84 |
+
test: /\.js$/,
|
85 |
+
exclude: /node_modules/,
|
86 |
+
use: [
|
87 |
+
DynamicPublicPath.loader,
|
88 |
+
{
|
89 |
+
loader: 'babel-loader',
|
90 |
+
options: {
|
91 |
+
configFile: path.resolve( directory, './core/packages/webpack/src/babel.js' ),
|
92 |
+
},
|
93 |
+
},
|
94 |
+
],
|
95 |
+
},
|
96 |
+
{
|
97 |
+
test: /\.s?css$/,
|
98 |
+
use: [
|
99 |
+
MiniCssExtractPlugin.loader,
|
100 |
+
{
|
101 |
+
loader: 'css-loader',
|
102 |
+
options: {
|
103 |
+
url: false,
|
104 |
+
},
|
105 |
+
},
|
106 |
+
{
|
107 |
+
loader: 'postcss-loader',
|
108 |
+
options: {
|
109 |
+
plugins: [
|
110 |
+
autoprefixer,
|
111 |
+
],
|
112 |
+
},
|
113 |
+
},
|
114 |
+
{
|
115 |
+
loader: 'sass-loader',
|
116 |
+
options: {
|
117 |
+
outputStyle: debug ? 'nested' : 'compressed',
|
118 |
+
sourceMap: debug ? 'inline' : false,
|
119 |
+
includePaths: [
|
120 |
+
path.resolve( directory, './core/packages/style-guide/src' ),
|
121 |
+
],
|
122 |
+
},
|
123 |
+
},
|
124 |
+
],
|
125 |
+
},
|
126 |
+
{
|
127 |
+
test: /\.svg$/,
|
128 |
+
exclude: /node_modules/,
|
129 |
+
use: [
|
130 |
+
{
|
131 |
+
loader: 'svg-react-loader',
|
132 |
+
query: {
|
133 |
+
classIdPrefix: 'itsec-icon-[name]-[hash:5]__',
|
134 |
+
},
|
135 |
+
},
|
136 |
+
],
|
137 |
+
},
|
138 |
+
],
|
139 |
+
},
|
140 |
+
plugins: [
|
141 |
+
new MiniCssExtractPlugin( {
|
142 |
+
filename: debug ? '[name].css' : '[name].min.css',
|
143 |
+
} ),
|
144 |
+
new FilterWarningsPlugin( {
|
145 |
+
exclude: /mini-css-extract-plugin[^]*Conflicting order between:/,
|
146 |
+
} ),
|
147 |
+
new StyleOnlyEntryPlugin(),
|
148 |
+
new DynamicPublicPath( 'itsecWebpackPublicPath' ),
|
149 |
+
new CustomTemplatedPathPlugin( {
|
150 |
+
entry( p, data ) {
|
151 |
+
const parts = data.chunk.name.split( '/' );
|
152 |
+
|
153 |
+
return parts[ 1 ];
|
154 |
+
},
|
155 |
+
module( p, data ) {
|
156 |
+
const parts = data.chunk.name.split( '/' );
|
157 |
+
|
158 |
+
return parts[ 0 ];
|
159 |
+
},
|
160 |
+
} ),
|
161 |
+
new ManifestPlugin( {
|
162 |
+
fileName: debug ? 'manifest-dev.php' : 'manifest.php',
|
163 |
+
generate: generateManifest,
|
164 |
+
serialize: serializeManifest,
|
165 |
+
} ),
|
166 |
+
],
|
167 |
+
resolve: {
|
168 |
+
modules: [
|
169 |
+
path.resolve( directory, './' ),
|
170 |
+
path.resolve( directory, './node_modules' ),
|
171 |
+
],
|
172 |
+
alias: {
|
173 |
+
'@ithemes/security-utils': path.resolve( directory, './core/packages/utils/src/index.js' ),
|
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 ] }`;
|
180 |
+
|
181 |
+
acc[ alias ] = path.resolve( directory, entries[ entry ] );
|
182 |
+
|
183 |
+
return acc;
|
184 |
+
}, {} ),
|
185 |
+
},
|
186 |
+
},
|
187 |
+
optimization: {},
|
188 |
+
};
|
189 |
+
|
190 |
+
if ( ! debug ) {
|
191 |
+
const splitChunkName = new SplitChunkName();
|
192 |
+
|
193 |
+
config.optimization.splitChunks = {
|
194 |
+
chunks: 'all',
|
195 |
+
maxInitialRequests: 10,
|
196 |
+
hidePathInfo: true,
|
197 |
+
cacheGroups: {
|
198 |
+
recharts: {
|
199 |
+
test: /[\\/]node_modules[\\/](recharts)[\\/]/,
|
200 |
+
name: 'vendors/recharts',
|
201 |
+
enforce: true,
|
202 |
+
},
|
203 |
+
},
|
204 |
+
name: splitChunkName.name.bind( splitChunkName ),
|
205 |
+
};
|
206 |
+
|
207 |
+
config.plugins.push( new webpack.HashedModuleIdsPlugin() );
|
208 |
+
}
|
209 |
+
|
210 |
+
return config;
|
211 |
+
};
|
core/packages/webpack/src/config/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/custom-templated-path-webpack-plugin/index.js
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* External dependencies
|
3 |
+
*/
|
4 |
+
const escapeStringRegexp = require( 'escape-string-regexp' );
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Webpack plugin for handling specific template tags in Webpack configuration
|
8 |
+
* values like those supported in the base Webpack functionality (e.g. `name`).
|
9 |
+
*
|
10 |
+
* @see webpack.TemplatedPathPlugin
|
11 |
+
*/
|
12 |
+
class CustomTemplatedPathPlugin {
|
13 |
+
/**
|
14 |
+
* CustomTemplatedPathPlugin constructor. Initializes handlers as a tuple
|
15 |
+
* set of RegExp, handler, where the regular expression is used in matching
|
16 |
+
* a Webpack asset path.
|
17 |
+
*
|
18 |
+
* @param {Object.<string,Function>} handlers Object keyed by tag to match,
|
19 |
+
* with function value returning
|
20 |
+
* replacement string.
|
21 |
+
*/
|
22 |
+
constructor( handlers ) {
|
23 |
+
this.handlers = [];
|
24 |
+
|
25 |
+
for ( const [ key, handler ] of Object.entries( handlers ) ) {
|
26 |
+
const regexp = new RegExp( `\\[${ escapeStringRegexp( key ) }\\]`, 'gi' );
|
27 |
+
this.handlers.push( [ regexp, handler ] );
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Webpack plugin application logic.
|
33 |
+
*
|
34 |
+
* @param {Object} compiler Webpack compiler
|
35 |
+
*/
|
36 |
+
apply( compiler ) {
|
37 |
+
compiler.hooks.compilation.tap( 'CustomTemplatedPathPlugin', ( compilation ) => {
|
38 |
+
compilation.mainTemplate.hooks.assetPath.tap( 'CustomTemplatedPathPlugin', ( path, data ) => {
|
39 |
+
for ( let i = 0; i < this.handlers.length; i++ ) {
|
40 |
+
const [ regexp, handler ] = this.handlers[ i ];
|
41 |
+
if ( regexp.test( path ) ) {
|
42 |
+
path = path.replace( regexp, handler( path, data ) );
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
return path;
|
47 |
+
} );
|
48 |
+
} );
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
module.exports = CustomTemplatedPathPlugin;
|
core/packages/webpack/src/custom-templated-path-webpack-plugin/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/dynamic-public-path/index.js
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const path = require( 'path' );
|
2 |
+
|
3 |
+
function DynamicPublicPathPlugin( propertyName ) {
|
4 |
+
this.propertyName = propertyName;
|
5 |
+
}
|
6 |
+
|
7 |
+
DynamicPublicPathPlugin.prototype.apply = function( compiler ) {
|
8 |
+
compiler.hooks.thisCompilation.tap( 'dynamic-public-path', ( compilation ) => {
|
9 |
+
compilation.hooks.normalModuleLoader.tap( 'dynamic-public-path', ( loaderContext ) => {
|
10 |
+
const entryFiles = [];
|
11 |
+
Object.values( compiler.options.entry ).forEach( function( entry ) {
|
12 |
+
entryFiles.push( path.join( compiler.options.context, entry ) );
|
13 |
+
} );
|
14 |
+
|
15 |
+
loaderContext[ 'dynamic-public-path' ] = {
|
16 |
+
propertyName: this.propertyName,
|
17 |
+
entryFiles,
|
18 |
+
};
|
19 |
+
} );
|
20 |
+
} );
|
21 |
+
};
|
22 |
+
|
23 |
+
DynamicPublicPathPlugin.loader = require.resolve( './loader.js' );
|
24 |
+
|
25 |
+
module.exports = DynamicPublicPathPlugin;
|
core/packages/webpack/src/dynamic-public-path/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/dynamic-public-path/loader.js
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = function( source ) {
|
2 |
+
const config = this[ 'dynamic-public-path' ];
|
3 |
+
const resource = this.resource;
|
4 |
+
|
5 |
+
if ( config.entryFiles.includes( resource ) && resource.match( /\.js$/ ) ) {
|
6 |
+
source = `__webpack_public_path__ = window.${ config.propertyName };\n${ source }`;
|
7 |
+
}
|
8 |
+
|
9 |
+
return source;
|
10 |
+
};
|
core/packages/webpack/src/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/manifest/index.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const crypto = require( 'crypto' );
|
2 |
+
const path = require( 'path' );
|
3 |
+
const spawn = require( 'child_process' ).spawnSync;
|
4 |
+
|
5 |
+
function generate( seed, files ) {
|
6 |
+
const manifest = {};
|
7 |
+
const splitChunks = [];
|
8 |
+
|
9 |
+
for ( const file of files ) {
|
10 |
+
if ( ! file.chunk || ! file.chunk.name ) {
|
11 |
+
continue;
|
12 |
+
}
|
13 |
+
|
14 |
+
if ( ! manifest[ file.chunk.name ] ) {
|
15 |
+
manifest[ file.chunk.name ] = generateChunk( file.chunk );
|
16 |
+
}
|
17 |
+
|
18 |
+
manifest[ file.chunk.name ].files.push( file.name );
|
19 |
+
|
20 |
+
if ( ! file.chunk.hasRuntime() ) {
|
21 |
+
splitChunks.push( file );
|
22 |
+
}
|
23 |
+
}
|
24 |
+
|
25 |
+
for ( const file of splitChunks ) {
|
26 |
+
file.chunk.groupsIterable.forEach( ( group ) => {
|
27 |
+
if ( manifest[ group.name ] && ! manifest[ group.name ].vendors.includes( file.chunk.name ) ) {
|
28 |
+
manifest[ group.name ].vendors.push( file.chunk.name );
|
29 |
+
}
|
30 |
+
} );
|
31 |
+
}
|
32 |
+
|
33 |
+
return manifest;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Generate a chunk manifest entry.
|
38 |
+
*
|
39 |
+
* @param {Chunk} chunk
|
40 |
+
* @return {{runtime: boolean, vendors: Array, hash: string, dependencies: Array}} Manifest object.
|
41 |
+
*/
|
42 |
+
function generateChunk( chunk ) {
|
43 |
+
const chunkManifest = {
|
44 |
+
runtime: chunk.hasRuntime(),
|
45 |
+
files: [],
|
46 |
+
hash: crypto.createHash( 'md4' ).update( JSON.stringify( chunk.contentHash ) ).digest( 'hex' ),
|
47 |
+
contentHash: chunk.contentHash,
|
48 |
+
vendors: [],
|
49 |
+
dependencies: [],
|
50 |
+
};
|
51 |
+
|
52 |
+
chunk.getModules().forEach( ( module ) => {
|
53 |
+
if ( module.external && module.userRequest ) {
|
54 |
+
if ( module.userRequest.includes( '@wordpress/' ) ) {
|
55 |
+
chunkManifest.dependencies.push( `wp-${ module.userRequest.replace( '@wordpress/', '' ) }` );
|
56 |
+
} else {
|
57 |
+
chunkManifest.dependencies.push( module.userRequest );
|
58 |
+
}
|
59 |
+
}
|
60 |
+
} );
|
61 |
+
|
62 |
+
chunkManifest.dependencies.sort();
|
63 |
+
|
64 |
+
return chunkManifest;
|
65 |
+
}
|
66 |
+
|
67 |
+
function serialize( data ) {
|
68 |
+
const out = spawn( 'php', [
|
69 |
+
path.resolve( __dirname, 'json-to-php.php' ),
|
70 |
+
JSON.stringify( data ),
|
71 |
+
] );
|
72 |
+
|
73 |
+
if ( out.status !== 0 ) {
|
74 |
+
throw Error( 'Failed to generate PHP manifest.' );
|
75 |
+
}
|
76 |
+
|
77 |
+
return `<?php return ${ out.stdout };`;
|
78 |
+
}
|
79 |
+
|
80 |
+
module.exports = { generate, serialize };
|
core/packages/webpack/src/manifest/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/manifest/json-to-php.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( 'cli' !== php_sapi_name() ) {
|
4 |
+
die( 1 );
|
5 |
+
}
|
6 |
+
|
7 |
+
if ( ! isset( $argv[1] ) ) {
|
8 |
+
die( 1 );
|
9 |
+
}
|
10 |
+
|
11 |
+
$json = $argv[1];
|
12 |
+
|
13 |
+
if ( ! $decoded = json_decode( $json, true ) ) {
|
14 |
+
die( 1 );
|
15 |
+
}
|
16 |
+
|
17 |
+
ksort( $decoded );
|
18 |
+
|
19 |
+
fwrite( STDOUT, var_export( $decoded, true ) );
|
20 |
+
die( 0 );
|
core/packages/webpack/src/split-chunk-name/index.js
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const crypto = require( 'crypto' );
|
2 |
+
|
3 |
+
class SplitChunksName {
|
4 |
+
constructor() {
|
5 |
+
this.cache = new WeakMap();
|
6 |
+
}
|
7 |
+
|
8 |
+
name( module, chunks, cacheGroup ) {
|
9 |
+
const automaticNamePrefix = cacheGroup === 'vendors' ? 'vendors' : '',
|
10 |
+
automaticNameDelimiter = '~';
|
11 |
+
|
12 |
+
let cacheEntry = this.cache.get( chunks );
|
13 |
+
if ( cacheEntry === undefined ) {
|
14 |
+
cacheEntry = {};
|
15 |
+
this.cache.set( chunks, cacheEntry );
|
16 |
+
} else if ( cacheGroup in cacheEntry ) {
|
17 |
+
return cacheEntry[ cacheGroup ];
|
18 |
+
}
|
19 |
+
const names = chunks.filter( ( c ) => !! c.name ).map( ( c ) => c.name.split( '/' ) );
|
20 |
+
if ( ! names.length || ! names.every( Boolean ) ) {
|
21 |
+
cacheEntry[ cacheGroup ] = undefined;
|
22 |
+
return;
|
23 |
+
}
|
24 |
+
names.sort( ( a, b ) => b.length - a.length );
|
25 |
+
const prefix = typeof automaticNamePrefix === 'string' ? automaticNamePrefix : cacheGroup;
|
26 |
+
const namePrefix = prefix ? prefix + '/' : '';
|
27 |
+
|
28 |
+
/*
|
29 |
+
[
|
30 |
+
[ 'dashboard', 'dashboard' ]
|
31 |
+
[ 'dashboard', 'widget' ],
|
32 |
+
]
|
33 |
+
-> 'dashboard/dashboard~widget'
|
34 |
+
|
35 |
+
[
|
36 |
+
[ 'dashboard', 'dashboard' ]
|
37 |
+
[ 'fingerprinting', 'manager' ],
|
38 |
+
]
|
39 |
+
-> 'dist/dashboard-dist-dashboard~fingerprinting-dist-manager'
|
40 |
+
*/
|
41 |
+
|
42 |
+
let name = '';
|
43 |
+
const max = names[ 0 ].length;
|
44 |
+
|
45 |
+
for ( let i = 0; i < max; i++ ) {
|
46 |
+
if ( this.allAtPosSame( names, i ) ) {
|
47 |
+
name += names[ 0 ][ i ] + '/';
|
48 |
+
} else {
|
49 |
+
name = '';
|
50 |
+
name += names[ 0 ].slice( 0, i ).join( '/' ) + '/';
|
51 |
+
name += names.map( ( parts ) => parts[ i ] ).filter( ( part ) => typeof part === 'string' ).join( automaticNameDelimiter ) + '/';
|
52 |
+
}
|
53 |
+
}
|
54 |
+
|
55 |
+
name = namePrefix + name;
|
56 |
+
|
57 |
+
if ( '/' === name.charAt( name.length - 1 ) ) {
|
58 |
+
name = name.substring( 0, name.length - 1 );
|
59 |
+
}
|
60 |
+
|
61 |
+
if ( name.length > 100 ) {
|
62 |
+
name = name.slice( 0, 100 ) + automaticNameDelimiter + this.hashFilename( name );
|
63 |
+
}
|
64 |
+
|
65 |
+
cacheEntry[ cacheGroup ] = name;
|
66 |
+
return name;
|
67 |
+
}
|
68 |
+
|
69 |
+
allAtPosSame( list, pos ) {
|
70 |
+
let prev = null;
|
71 |
+
|
72 |
+
for ( const item of list ) {
|
73 |
+
if ( prev === null ) {
|
74 |
+
prev = item[ pos ];
|
75 |
+
} else if ( prev !== item[ pos ] ) {
|
76 |
+
return false;
|
77 |
+
}
|
78 |
+
}
|
79 |
+
|
80 |
+
return true;
|
81 |
+
}
|
82 |
+
|
83 |
+
hashFilename( name ) {
|
84 |
+
return crypto
|
85 |
+
.createHash( 'md4' )
|
86 |
+
.update( name )
|
87 |
+
.digest( 'hex' )
|
88 |
+
.slice( 0, 8 );
|
89 |
+
}
|
90 |
+
}
|
91 |
+
|
92 |
+
module.exports = SplitChunksName;
|
core/packages/webpack/src/split-chunk-name/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/style-only-entry-plugin/index.js
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function StyleOnlyEntryPlugin( styleTests ) {
|
2 |
+
let list = [];
|
3 |
+
|
4 |
+
if ( styleTests instanceof RegExp ) {
|
5 |
+
list.push( styleTests );
|
6 |
+
} else if ( Array.isArray( styleTests ) && styleTests.length ) {
|
7 |
+
list = styleTests;
|
8 |
+
} else {
|
9 |
+
list = [ /\.s?css$/ ];
|
10 |
+
}
|
11 |
+
|
12 |
+
Object.assign( this, {
|
13 |
+
styleTests: list,
|
14 |
+
} );
|
15 |
+
}
|
16 |
+
|
17 |
+
StyleOnlyEntryPlugin.prototype.isFileStyle = function( file ) {
|
18 |
+
for ( const test of this.styleTests ) {
|
19 |
+
if ( test.test( file ) ) {
|
20 |
+
return true;
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
return false;
|
25 |
+
};
|
26 |
+
|
27 |
+
StyleOnlyEntryPlugin.prototype.apply = function( compiler ) {
|
28 |
+
compiler.hooks.emit.tap( 'style-only-entry-plugin', ( compilation ) => {
|
29 |
+
for ( const chunk of compilation.chunks ) {
|
30 |
+
if ( chunk.entryModule && this.isFileStyle( chunk.entryModule.userRequest ) ) {
|
31 |
+
for ( const file of chunk.files ) {
|
32 |
+
if ( ! this.isFileStyle( file ) ) {
|
33 |
+
delete compilation.assets[ file ];
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
37 |
+
}
|
38 |
+
} );
|
39 |
+
};
|
40 |
+
|
41 |
+
module.exports = StyleOnlyEntryPlugin;
|
core/packages/webpack/src/style-only-entry-plugin/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/packages/webpack/src/wp-externals/index.js
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function camelCaseDash( string ) {
|
2 |
+
return string.replace(
|
3 |
+
/-([a-z])/g,
|
4 |
+
( match, letter ) => letter.toUpperCase(),
|
5 |
+
);
|
6 |
+
}
|
7 |
+
|
8 |
+
const formatRequest = ( request ) => {
|
9 |
+
// '@wordpress/api-fetch' -> [ '@wordpress', 'api-fetch' ]
|
10 |
+
const [ , name ] = request.split( '/' );
|
11 |
+
|
12 |
+
// { this: [ 'wp', 'apiFetch' ] }
|
13 |
+
return {
|
14 |
+
this: [ 'wp', camelCaseDash( name ) ],
|
15 |
+
};
|
16 |
+
};
|
17 |
+
|
18 |
+
const wpPackages = ( context, request, callback ) => {
|
19 |
+
if ( /^@wordpress\//.test( request ) ) {
|
20 |
+
callback( null, formatRequest( request ), 'this' );
|
21 |
+
} else {
|
22 |
+
callback();
|
23 |
+
}
|
24 |
+
};
|
25 |
+
|
26 |
+
const externals = Object.freeze( [
|
27 |
+
{
|
28 |
+
react: 'React',
|
29 |
+
'react-dom': 'ReactDOM',
|
30 |
+
tinymce: 'tinymce',
|
31 |
+
moment: 'moment',
|
32 |
+
jquery: 'jQuery',
|
33 |
+
lodash: 'lodash',
|
34 |
+
'lodash-es': 'lodash',
|
35 |
+
},
|
36 |
+
wpPackages,
|
37 |
+
] );
|
38 |
+
|
39 |
+
module.exports = externals;
|
core/packages/webpack/src/wp-externals/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/setup.php
CHANGED
@@ -107,8 +107,21 @@ final class ITSEC_Setup {
|
|
107 |
self::upgrade_data_to_4031();
|
108 |
}
|
109 |
|
|
|
|
|
|
|
|
|
110 |
if ( $build < 4069 ) {
|
111 |
self::upgrade_data_to_4069();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
}
|
113 |
|
114 |
// Run upgrade routines for modules to ensure that they are up-to-date.
|
107 |
self::upgrade_data_to_4031();
|
108 |
}
|
109 |
|
110 |
+
if ( $build < 4067 ) {
|
111 |
+
delete_site_option( 'itsec_pro_just_activated' );
|
112 |
+
}
|
113 |
+
|
114 |
if ( $build < 4069 ) {
|
115 |
self::upgrade_data_to_4069();
|
116 |
+
delete_site_option( 'itsec_free_just_activated' );
|
117 |
+
}
|
118 |
+
|
119 |
+
if ( $build < 4076 ) {
|
120 |
+
$digest = wp_next_scheduled( 'itsec_digest_email' );
|
121 |
+
|
122 |
+
if ( $digest ) {
|
123 |
+
wp_unschedule_event( $digest, 'itsec_digest_email' );
|
124 |
+
}
|
125 |
}
|
126 |
|
127 |
// Run upgrade routines for modules to ensure that they are up-to-date.
|
dist/core/admin-notices-api.min.js
ADDED
@@ -0,0 +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}()}});
|
dist/core/admin-notices-dashboard-admin-bar.min.css
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-admin-notice{background:white;border-radius:3px;box-shadow:0 0 5px rgba(211,211,211,0.35)}.itsec-admin-notice__header{background:#eeecec;padding:10px;border-top-left-radius:3px;border-top-right-radius:3px}.itsec-admin-notice__header .components-button{margin-top:1em;padding:.5em 1em;background:#E1f2fc;color:#0081E3;border:1px solid #0081E3;transition:background-color 300ms, color 150ms}.itsec-admin-notice__header .components-button:hover{background:#0081E3;color:white}.itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #E1f2fc, #fff 11px, #fff 10px, #E1f2fc 20px);opacity:1}.itsec-admin-notice__header .components-button.is-busy:hover{color:#0081E3}.itsec-admin-notice--severity-error .itsec-admin-notice__header{background:#d94f4f}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#fcefef;color:#d94f4f;border:1px solid #d94f4f;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button:hover{background:#d94f4f;color:white}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #fcefef, #fff 11px, #fff 10px, #fcefef 20px);opacity:1}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button.is-busy:hover{color:#d94f4f}.itsec-admin-notice--severity-warning .itsec-admin-notice__header{background:#f78b53}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#fef1ea;color:#f78b53;border:1px solid #f78b53;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button:hover{background:#f78b53;color:white}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #fef1ea, #fff 11px, #fff 10px, #fef1ea 20px);opacity:1}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button.is-busy:hover{color:#f78b53}.itsec-admin-notice--severity-info .itsec-admin-notice__header{background:#00a0d2}.itsec-admin-notice--severity-success .itsec-admin-notice__header{background:#4ab866}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#e4f4e8;color:#4ab866;border:1px solid #4ab866;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button:hover{background:#4ab866;color:white}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #e4f4e8, #fff 11px, #fff 10px, #e4f4e8 20px);opacity:1}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button.is-busy:hover{color:#4ab866}.itsec-admin-notice .itsec-admin-notice__header:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.itsec-admin-notice__header .itsec-admin-notice__header-inset{background:white;border-radius:5px;padding:10px}.itsec-admin-notice__header h4{font-size:14px;margin:0}.itsec-admin-notice__message{padding:10px 20px}.itsec-admin-notice__message p:first-child{margin-top:0}.itsec-admin-notice__message p:last-child{margin-bottom:0}.itsec-admin-notice__meta{background:#eeecec;padding:5px 20px;margin:0;display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:5px 20px}.itsec-admin-notice__meta dt,.itsec-admin-notice__meta dd{margin:0}.itsec-admin-notice__meta dt{text-transform:uppercase;color:#040404}.itsec-admin-notice__footer{padding:5px 20px;font-size:11px}
|
2 |
+
|
3 |
+
.itsec-admin-notice-list-actions .components-icon-button{padding:2px !important}.itsec-admin-notice-list-actions .components-icon-button:hover{background-color:#c6d1da !important;box-shadow:none !important}.itsec-admin-notice-list-actions__more-menu-items .components-popover__content>div{overflow:hidden;padding:7px 0}.itsec-admin-notice-list-actions__more-menu-items .components-button{width:100%;min-height:38px;box-sizing:border-box;padding:10px;color:#0073aa;justify-content:space-between;align-items:center}.itsec-admin-notice-list-actions__more-menu-items .components-button:focus{outline-offset:-2px;outline:1px dotted #555d66;box-shadow:none}.itsec-admin-notice-list-actions__more-menu-items .components-button:hover{color:#191e23;background:#f3f4f5}.itsec-admin-notice-list-actions__more-menu-items .components-button .components-spinner{margin:0}
|
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 |
+
|
dist/core/admin-notices-dashboard-admin-bar.min.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
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}()},TvNi:function(e,t){!function(){e.exports=this.wp.plugins}()},UuzZ:function(e,t){!function(){e.exports=this.wp.autop}()},VwaH:function(e,t,n){},YLtl:function(e,t){!function(){e.exports=this.lodash}()},cruf:function(e,t){!function(){e.exports=this.itsec.dashboard.api}()},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||f(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)(f(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 f(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 b=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 g=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)(b,{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)(g,{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})}});
|
dist/core/admin-notices.min.css
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.itsec-admin-notice{background:white;border-radius:3px;box-shadow:0 0 5px rgba(211,211,211,0.35)}.itsec-admin-notice__header{background:#eeecec;padding:10px;border-top-left-radius:3px;border-top-right-radius:3px}.itsec-admin-notice__header .components-button{margin-top:1em;padding:.5em 1em;background:#E1f2fc;color:#0081E3;border:1px solid #0081E3;transition:background-color 300ms, color 150ms}.itsec-admin-notice__header .components-button:hover{background:#0081E3;color:white}.itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #E1f2fc, #fff 11px, #fff 10px, #E1f2fc 20px);opacity:1}.itsec-admin-notice__header .components-button.is-busy:hover{color:#0081E3}.itsec-admin-notice--severity-error .itsec-admin-notice__header{background:#d94f4f}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#fcefef;color:#d94f4f;border:1px solid #d94f4f;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button:hover{background:#d94f4f;color:white}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #fcefef, #fff 11px, #fff 10px, #fcefef 20px);opacity:1}.itsec-admin-notice--severity-error .itsec-admin-notice__header .components-button.is-busy:hover{color:#d94f4f}.itsec-admin-notice--severity-warning .itsec-admin-notice__header{background:#f78b53}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#fef1ea;color:#f78b53;border:1px solid #f78b53;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button:hover{background:#f78b53;color:white}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #fef1ea, #fff 11px, #fff 10px, #fef1ea 20px);opacity:1}.itsec-admin-notice--severity-warning .itsec-admin-notice__header .components-button.is-busy:hover{color:#f78b53}.itsec-admin-notice--severity-info .itsec-admin-notice__header{background:#00a0d2}.itsec-admin-notice--severity-success .itsec-admin-notice__header{background:#4ab866}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button{padding:.5em 1em;background:#e4f4e8;color:#4ab866;border:1px solid #4ab866;transition:background-color 300ms, color 150ms}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button:hover{background:#4ab866;color:white}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button.is-busy{animation:components-button__busy-animation 2500ms infinite linear;background-size:100px 100%;background-image:repeating-linear-gradient(-45deg, #e4f4e8, #fff 11px, #fff 10px, #e4f4e8 20px);opacity:1}.itsec-admin-notice--severity-success .itsec-admin-notice__header .components-button.is-busy:hover{color:#4ab866}.itsec-admin-notice .itsec-admin-notice__header:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.itsec-admin-notice__header .itsec-admin-notice__header-inset{background:white;border-radius:5px;padding:10px}.itsec-admin-notice__header h4{font-size:14px;margin:0}.itsec-admin-notice__message{padding:10px 20px}.itsec-admin-notice__message p:first-child{margin-top:0}.itsec-admin-notice__message p:last-child{margin-bottom:0}.itsec-admin-notice__meta{background:#eeecec;padding:5px 20px;margin:0;display:grid;grid-template:auto / -webkit-min-content 1fr;grid-template:auto / min-content 1fr;grid-gap:5px 20px}.itsec-admin-notice__meta dt,.itsec-admin-notice__meta dd{margin:0}.itsec-admin-notice__meta dt{text-transform:uppercase;color:#040404}.itsec-admin-notice__footer{padding:5px 20px;font-size:11px}
|
2 |
+
|
3 |
+
.itsec-admin-notice-list-actions .components-icon-button{padding:2px !important}.itsec-admin-notice-list-actions .components-icon-button:hover{background-color:#c6d1da !important;box-shadow:none !important}.itsec-admin-notice-list-actions__more-menu-items .components-popover__content>div{overflow:hidden;padding:7px 0}.itsec-admin-notice-list-actions__more-menu-items .components-button{width:100%;min-height:38px;box-sizing:border-box;padding:10px;color:#0073aa;justify-content:space-between;align-items:center}.itsec-admin-notice-list-actions__more-menu-items .components-button:focus{outline-offset:-2px;outline:1px dotted #555d66;box-shadow:none}.itsec-admin-notice-list-actions__more-menu-items .components-button:hover{color:#191e23;background:#f3f4f5}.itsec-admin-notice-list-actions__more-menu-items .components-button .components-spinner{margin:0}
|
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 |
+
|
11 |
+
|
dist/core/admin-notices.min.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
this.itsec=this.itsec||{},this.itsec.core=this.itsec.core||{},this.itsec.core["admin-notices"]=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="dwGD")}({"1Zi1":function(e,t,n){},"1ZqX":function(e,t){!function(){e.exports=this.wp.data}()},"6IbE":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){},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=[],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})}});
|
dist/core/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
dist/core/packages/components/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
dist/core/packages/components/site-scan-results/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
dist/core/packages/components/site-scan-results/style.min.css
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
.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}
|
2 |
+
|
dist/core/packages/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
dist/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
dist/manifest.php
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php return array (
|
2 |
+
'core/admin-notices' =>
|
3 |
+
array (
|
4 |
+
'runtime' => true,
|
5 |
+
'files' =>
|
6 |
+
array (
|
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 (
|
18 |
+
),
|
19 |
+
'dependencies' =>
|
20 |
+
array (
|
21 |
+
0 => '@ithemes/security.core.admin-notices-api',
|
22 |
+
1 => 'lodash',
|
23 |
+
2 => 'wp-autop',
|
24 |
+
3 => 'wp-components',
|
25 |
+
4 => 'wp-compose',
|
26 |
+
5 => 'wp-data',
|
27 |
+
6 => 'wp-dom-ready',
|
28 |
+
7 => 'wp-element',
|
29 |
+
8 => 'wp-i18n',
|
30 |
+
),
|
31 |
+
),
|
32 |
+
'core/admin-notices-api' =>
|
33 |
+
array (
|
34 |
+
'runtime' => true,
|
35 |
+
'files' =>
|
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 (
|
47 |
+
),
|
48 |
+
'dependencies' =>
|
49 |
+
array (
|
50 |
+
0 => 'lodash',
|
51 |
+
1 => 'wp-api-fetch',
|
52 |
+
2 => 'wp-data',
|
53 |
+
3 => 'wp-i18n',
|
54 |
+
),
|
55 |
+
),
|
56 |
+
'core/admin-notices-dashboard-admin-bar' =>
|
57 |
+
array (
|
58 |
+
'runtime' => true,
|
59 |
+
'files' =>
|
60 |
+
array (
|
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 (
|
72 |
+
),
|
73 |
+
'dependencies' =>
|
74 |
+
array (
|
75 |
+
0 => '@ithemes/security.core.admin-notices-api',
|
76 |
+
1 => '@ithemes/security.dashboard.api',
|
77 |
+
2 => 'lodash',
|
78 |
+
3 => 'wp-autop',
|
79 |
+
4 => 'wp-components',
|
80 |
+
5 => 'wp-compose',
|
81 |
+
6 => 'wp-data',
|
82 |
+
7 => 'wp-element',
|
83 |
+
8 => 'wp-i18n',
|
84 |
+
9 => 'wp-plugins',
|
85 |
+
),
|
86 |
+
),
|
87 |
+
'core/packages/components/site-scan-results/style' =>
|
88 |
+
array (
|
89 |
+
'runtime' => true,
|
90 |
+
'files' =>
|
91 |
+
array (
|
92 |
+
0 => 'core/packages/components/site-scan-results/style.css',
|
93 |
+
1 => 'core/packages/components/site-scan-results/style.js',
|
94 |
+
),
|
95 |
+
'hash' => '17241399d68eeb900f5f2c289b1748f7',
|
96 |
+
'contentHash' =>
|
97 |
+
array (
|
98 |
+
'css/mini-extract' => '4938d7aee319aaa3968c',
|
99 |
+
'javascript' => '0ac75a114cf101452605',
|
100 |
+
),
|
101 |
+
'vendors' =>
|
102 |
+
array (
|
103 |
+
),
|
104 |
+
'dependencies' =>
|
105 |
+
array (
|
106 |
+
),
|
107 |
+
),
|
108 |
+
);
|
history.txt
CHANGED
@@ -836,3 +836,8 @@
|
|
836 |
Bug Fix: Resolve warning when a user is set to "No Role".
|
837 |
7.3.3 - 2019-03-25 - Chris Jean & Timothy Jacobs
|
838 |
Bug Fix: Hide backend bypass.
|
|
|
|
|
|
|
|
|
|
836 |
Bug Fix: Resolve warning when a user is set to "No Role".
|
837 |
7.3.3 - 2019-03-25 - Chris Jean & Timothy Jacobs
|
838 |
Bug Fix: Hide backend bypass.
|
839 |
+
7.4.0 - 2019-06-10 - Chris Jean & Timothy Jacobs
|
840 |
+
New: iThemes Security Admin Notices are now conveniently located in the new Security Messages Menu. Check your notices in the Security menu on the WordPress Admin Bar.
|
841 |
+
Enhancement: Add Security Message when a Notification Center email fails to send.
|
842 |
+
Enhancement: Replace Trace IP with IP Tracker Online.
|
843 |
+
Tweak: Remove 'DELETE' method from "System Tweaks -> Filter Request Methods"
|
package.json
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "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 |
+
"version": "5.6.0",
|
5 |
+
"author": "iThemes",
|
6 |
+
"browserslist": [
|
7 |
+
"extends @wordpress/browserslist-config"
|
8 |
+
],
|
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",
|
37 |
+
"react-grid-layout": "^0.16.6",
|
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",
|
49 |
+
"@wordpress/babel-plugin-import-jsx-pragma": "^1.1.3",
|
50 |
+
"@wordpress/babel-preset-default": "^3.0.2",
|
51 |
+
"@wordpress/browserslist-config": "^2.2.3",
|
52 |
+
"@wordpress/custom-templated-path-webpack-plugin": "^1.2.0",
|
53 |
+
"@wordpress/jest-preset-default": "^3.0.0",
|
54 |
+
"@wordpress/scripts": "^2.4.4",
|
55 |
+
"acorn": "^6.0.5",
|
56 |
+
"autoprefixer": "^9.4.7",
|
57 |
+
"babel-loader": "^8.0.5",
|
58 |
+
"clean-webpack-plugin": "^2.0.2",
|
59 |
+
"concurrently": "^4.1.0",
|
60 |
+
"css-loader": "^2.1.0",
|
61 |
+
"escape-string-regexp": "^2.0.0",
|
62 |
+
"eslint-config-wordpress": "2.0.0",
|
63 |
+
"eslint-plugin-jest": "^22.3.0",
|
64 |
+
"eslint-plugin-jsx-a11y": "^6.2.1",
|
65 |
+
"eslint-plugin-react": "^7.12.4",
|
66 |
+
"eslint-plugin-wordpress": "git://github.com/WordPress-Coding-Standards/eslint-plugin-wordpress.git#1774343f6226052a46b081e01db3fca8793cc9f1",
|
67 |
+
"glob": "^7.1.3",
|
68 |
+
"husky": "^1.3.1",
|
69 |
+
"jest": "^24.1.0",
|
70 |
+
"loader-utils": "^1.2.3",
|
71 |
+
"mini-css-extract-plugin": "^0.5.0",
|
72 |
+
"node-sass": "^4.12.0",
|
73 |
+
"postcss-loader": "^3.0.0",
|
74 |
+
"raw-loader": "^1.0.0",
|
75 |
+
"readdirp": "^2.2.1",
|
76 |
+
"rimraf": "^2.6.3",
|
77 |
+
"sass-loader": "^7.1.0",
|
78 |
+
"style-loader": "^0.23.1",
|
79 |
+
"svg-react-loader": "github:woutervanvliet/svg-react-loader",
|
80 |
+
"webpack": "^4.29.5",
|
81 |
+
"webpack-cli": "^3.2.3",
|
82 |
+
"webpack-filter-warnings-plugin": "^1.2.1",
|
83 |
+
"webpack-manifest-plugin": "^2.0.4",
|
84 |
+
"webpack-sources": "latest",
|
85 |
+
"webpack-webstorm-debugger-script": "^1.0.1"
|
86 |
+
},
|
87 |
+
"directories": {},
|
88 |
+
"homepage": "https://ithemes.com/security",
|
89 |
+
"husky": {
|
90 |
+
"hooks": {
|
91 |
+
"pre-commit": "node bin/pre-commit"
|
92 |
+
}
|
93 |
+
},
|
94 |
+
"license": "GPL-2.0-or-later",
|
95 |
+
"private": true,
|
96 |
+
"repository": {
|
97 |
+
"type": "git",
|
98 |
+
"url": "git+ssh://git@bitbucket.org/ithemes/ithemes-security-pro.git"
|
99 |
+
},
|
100 |
+
"scripts": {
|
101 |
+
"build": "NODE_ENV=production ./node_modules/.bin/webpack",
|
102 |
+
"clean": "node ./bin/clean.js ",
|
103 |
+
"dev": "./node_modules/.bin/webpack",
|
104 |
+
"lint": "concurrently \"npm run lint-js\"",
|
105 |
+
"lint-js": "wp-scripts lint-js .",
|
106 |
+
"lint-js:fix": "wp-scripts lint-js . --fix",
|
107 |
+
"test": "concurrently \"npm run lint-js && npm run test-unit\"",
|
108 |
+
"test-unit": "wp-scripts test-unit-js --config tests/js/unit/jest.config.json",
|
109 |
+
"test-unit:coverage": "npm run test-unit -- --coverage",
|
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 |
+
}
|
114 |
+
}
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
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.2.
|
6 |
-
Stable tag: 7.
|
7 |
Requires PHP: 5.2
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
@@ -189,6 +189,12 @@ Free support may be available with the help of the community in the <a href="htt
|
|
189 |
|
190 |
== Changelog ==
|
191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
= 7.3.3 =
|
193 |
* Bug Fix: Hide backend bypass.
|
194 |
|
@@ -534,5 +540,5 @@ Free support may be available with the help of the community in the <a href="htt
|
|
534 |
|
535 |
== Upgrade Notice ==
|
536 |
|
537 |
-
= 7.
|
538 |
-
Version 7.
|
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.2.2
|
6 |
+
Stable tag: 7.4.0
|
7 |
Requires PHP: 5.2
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
189 |
|
190 |
== Changelog ==
|
191 |
|
192 |
+
= 7.4.0 =
|
193 |
+
* New: iThemes Security Admin Notices are now conveniently located in the new Security Messages Menu. Check your notices in the Security menu on the WordPress Admin Bar.
|
194 |
+
* Enhancement: Add Security Message when a Notification Center email fails to send.
|
195 |
+
* Enhancement: Replace Trace IP with IP Tracker Online.
|
196 |
+
* Tweak: Remove 'DELETE' method from "System Tweaks -> Filter Request Methods"
|
197 |
+
|
198 |
= 7.3.3 =
|
199 |
* Bug Fix: Hide backend bypass.
|
200 |
|
540 |
|
541 |
== Upgrade Notice ==
|
542 |
|
543 |
+
= 7.4.0 =
|
544 |
+
Version 7.4.0 contains important improvements. It is recommended for all users.
|
webpack.config.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
const makeConfig = require( './core/packages/webpack/src/config' );
|
2 |
+
module.exports = makeConfig( __dirname, false );
|