Version Description
- Enhancement: Updated logging system to keep track of more information and have more options to filter and sort log entries.
- Enhancement: Improved efficiency of File Change Detection scanning.
- Bug Fix: Fixed issue that could register loading the logging page as a failed login attempt on some sites.
Download this release
Release Info
Developer | chrisjean |
Plugin | iThemes Security (formerly Better WP Security) |
Version | 6.9.0 |
Comparing to | |
See all releases |
Code changes from version 6.8.1 to 6.9.0
- better-wp-security.php +1 -1
- core/admin-pages/css/style.css +93 -0
- core/admin-pages/init.php +55 -1
- core/admin-pages/js/logs.js +279 -0
- core/admin-pages/js/settings.js +975 -0
- core/admin-pages/js/util.js +171 -0
- core/admin-pages/logs-list-table.php +427 -0
- core/admin-pages/page-logs.php +323 -153
- core/admin-pages/page-settings.php +2 -16
- core/core.php +4 -5
- core/files.php +1 -1
- core/history.txt +10 -2
- core/lib.php +21 -74
- core/lib/class-itsec-scheduler-cron.php +17 -0
- core/lib/class-itsec-scheduler-page-load.php +6 -3
- core/lib/class-itsec-scheduler.php +3 -2
- core/lib/class-itsec-wp-list-table.php +692 -423
- core/lib/debug.php +307 -0
- core/lib/form.php +23 -6
- core/lib/log-util.php +368 -0
- core/lib/log.php +238 -0
- core/lib/schema.php +89 -0
- core/lockout.php +208 -267
- core/logger-all-logs.php +0 -260
- core/logger.php +0 -554
- core/modules/404-detection/class-itsec-four-oh-four-log.php +0 -233
- core/modules/404-detection/class-itsec-four-oh-four.php +5 -77
- core/modules/404-detection/logs.php +28 -0
- core/modules/away-mode/class-itsec-away-mode.php +3 -38
- core/modules/away-mode/logs.php +18 -0
- core/modules/backup/class-itsec-backup.php +14 -35
- core/modules/backup/js/settings-page.js +17 -17
- core/modules/backup/logs.php +38 -0
- core/modules/brute-force/class-itsec-brute-force-log.php +0 -186
- core/modules/brute-force/class-itsec-brute-force.php +30 -151
- core/modules/brute-force/logs.php +47 -0
- core/modules/file-change/class-itsec-file-change-log.php +0 -255
- core/modules/file-change/class-itsec-file-change.php +0 -98
- core/modules/file-change/js/settings-page.js +7 -7
- core/modules/file-change/logs.php +56 -0
- core/modules/file-change/scanner.php +260 -284
- core/modules/global/js/settings-page.js +25 -6
- core/modules/global/settings.php +1 -1
- core/modules/ipcheck/class-itsec-ipcheck.php +63 -106
- core/modules/ipcheck/js/settings-page.js +1 -1
- core/modules/ipcheck/logs.php +57 -0
- core/modules/malware/class-itsec-malware-log.php +0 -340
- core/modules/malware/class-itsec-malware-scan-results-template.php +13 -0
- core/modules/malware/class-itsec-malware-scanner.php +161 -56
- core/modules/malware/class-itsec-malware.php +0 -74
- core/modules/malware/css/malware.css +3 -0
- core/modules/malware/css/settings.css +7 -0
- core/modules/malware/js/malware.js +1 -1
- core/modules/malware/js/settings-page.js +14 -14
- core/modules/malware/logs.php +64 -0
- core/modules/malware/sync-verbs/itsec-get-malware-scan-log.php +2 -17
- core/modules/notification-center/js/settings-page.js +2 -2
- core/modules/security-check/js/settings-page.js +3 -3
- core/setup.php +8 -12
- history.txt +4 -0
- readme.txt +9 -4
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: 6.
|
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: 6.9.0
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
core/admin-pages/css/style.css
CHANGED
@@ -755,3 +755,96 @@ ul.itsec-settings-contacts + ul.itsec-settings-contacts {
|
|
755 |
ul.itsec-settings-contacts li input[type="checkbox"] {
|
756 |
margin-top: 0;
|
757 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
755 |
ul.itsec-settings-contacts li input[type="checkbox"] {
|
756 |
margin-top: 0;
|
757 |
}
|
758 |
+
|
759 |
+
/**
|
760 |
+
* Styling for the logs page
|
761 |
+
*/
|
762 |
+
@media screen and (max-width: 1200px) {
|
763 |
+
body.security_page_itsec-logs div#postbox-container-1 {
|
764 |
+
display: none;
|
765 |
+
}
|
766 |
+
body.security_page_itsec-logs #postbox-container-2 {
|
767 |
+
border-right: none !important;
|
768 |
+
padding-right: 0 !important;
|
769 |
+
}
|
770 |
+
body.security_page_itsec-logs #post-body {
|
771 |
+
margin-right: 0 !important;
|
772 |
+
}
|
773 |
+
}
|
774 |
+
.itsec-log-entries .dashicons {
|
775 |
+
color: #555;
|
776 |
+
font-size: 16px;
|
777 |
+
padding: 2px 0 0 2px;
|
778 |
+
visibility: hidden;
|
779 |
+
}
|
780 |
+
.itsec-log-entries td:hover .dashicons {
|
781 |
+
visibility: visible;
|
782 |
+
}
|
783 |
+
#itsec-log-details-container {
|
784 |
+
display: none;
|
785 |
+
}
|
786 |
+
#itsec-log-details-container.grid .itsec-module-settings-content {
|
787 |
+
max-width: none;
|
788 |
+
}
|
789 |
+
#itsec-log-details-container.grid .itsec-module-settings-content pre {
|
790 |
+
overflow: auto;
|
791 |
+
}
|
792 |
+
.itsec-log-entries .hidden {
|
793 |
+
display: none !important;
|
794 |
+
}
|
795 |
+
.itsec-logs-color > tbody > .itsec-log-type-critical-issue td,
|
796 |
+
.itsec-logs-color > tbody > .itsec-log-type-critical-issue th,
|
797 |
+
.itsec-logs-color > tbody > .itsec-log-type-fatal td,
|
798 |
+
.itsec-logs-color > tbody > .itsec-log-type-fatal th {
|
799 |
+
color: #000;
|
800 |
+
}
|
801 |
+
.itsec-logs-color > tbody > .itsec-log-type-critical-issue a,
|
802 |
+
.itsec-logs-color > tbody > .itsec-log-type-fatal a {
|
803 |
+
color: #124B66;
|
804 |
+
}
|
805 |
+
.itsec-logs-color > tbody > .itsec-log-type-critical-issue,
|
806 |
+
.itsec-logs-color > tbody > .itsec-log-type-fatal {
|
807 |
+
background-color: #FF7575;
|
808 |
+
}
|
809 |
+
.itsec-logs-color > tbody > :nth-child(2n+1).itsec-log-type-critical-issue,
|
810 |
+
.itsec-logs-color > tbody > :nth-child(2n+1).itsec-log-type-fatal {
|
811 |
+
background-color: #FF4848;
|
812 |
+
}
|
813 |
+
.itsec-logs-color > tbody > .itsec-log-type-error {
|
814 |
+
background-color: #FFDFDF;
|
815 |
+
}
|
816 |
+
.itsec-logs-color > tbody > :nth-child(2n+1).itsec-log-type-error {
|
817 |
+
background-color: #FFCECE;
|
818 |
+
}
|
819 |
+
.itsec-logs-color > tbody > .itsec-log-type-warning {
|
820 |
+
background-color: #FFFBDF;
|
821 |
+
}
|
822 |
+
.itsec-logs-color > tbody > :nth-child(2n+1).itsec-log-type-warning {
|
823 |
+
background-color: #FFF9CE;
|
824 |
+
}
|
825 |
+
.itsec-log-entries .column-description {
|
826 |
+
max-width: 30%;
|
827 |
+
}
|
828 |
+
.tablenav .actions {
|
829 |
+
overflow: visible;
|
830 |
+
}
|
831 |
+
body.security_page_itsec-logs .itsec-module-settings-content-main .form-table td pre {
|
832 |
+
margin: 0;
|
833 |
+
}
|
834 |
+
body.security_page_itsec-logs .itsec-module-settings-content-main .form-table th {
|
835 |
+
padding: 10px 10px 10px 0;
|
836 |
+
width: 150px;
|
837 |
+
}
|
838 |
+
body.security_page_itsec-logs .itsec-module-settings-content-main .form-table td {
|
839 |
+
padding: 5px 10px;
|
840 |
+
}
|
841 |
+
body.security_page_itsec-logs .itsec-module-settings-content-main .form-table td .itsec-log-raw-details {
|
842 |
+
display: none;
|
843 |
+
margin-top: 1em;
|
844 |
+
}
|
845 |
+
body.security_page_itsec-logs #old-logs-migration-status img {
|
846 |
+
vertical-align: middle;
|
847 |
+
}
|
848 |
+
body.security_page_itsec-logs #old-logs-migration-status p {
|
849 |
+
line-height: 1.4em;
|
850 |
+
}
|
core/admin-pages/init.php
CHANGED
@@ -2,8 +2,11 @@
|
|
2 |
|
3 |
|
4 |
final class ITSEC_Admin_Page_Loader {
|
|
|
|
|
5 |
private $page_refs = array();
|
6 |
private $page_id;
|
|
|
7 |
|
8 |
|
9 |
public function __construct() {
|
@@ -22,6 +25,48 @@ final class ITSEC_Admin_Page_Loader {
|
|
22 |
add_filter( 'itsec-user-setting-valid-itsec-settings-view', array( $this, 'validate_view' ), null, 2 );
|
23 |
}
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
public function add_admin_pages() {
|
26 |
$capability = ITSEC_Core::get_required_cap();
|
27 |
$page_refs = array();
|
@@ -65,6 +110,9 @@ final class ITSEC_Admin_Page_Loader {
|
|
65 |
}
|
66 |
|
67 |
public function load() {
|
|
|
|
|
|
|
68 |
$this->load_file( 'page-%s.php' );
|
69 |
}
|
70 |
|
@@ -90,9 +138,15 @@ final class ITSEC_Admin_Page_Loader {
|
|
90 |
$id = $this->get_page_id();
|
91 |
|
92 |
if ( empty( $id ) ) {
|
93 |
-
|
|
|
|
|
|
|
|
|
94 |
}
|
95 |
|
|
|
|
|
96 |
$file = dirname( __FILE__ ) . '/' . sprintf( $file, $id );
|
97 |
|
98 |
if ( is_file( $file ) ) {
|
2 |
|
3 |
|
4 |
final class ITSEC_Admin_Page_Loader {
|
5 |
+
private $version = 2.0;
|
6 |
+
|
7 |
private $page_refs = array();
|
8 |
private $page_id;
|
9 |
+
private $translations = array();
|
10 |
|
11 |
|
12 |
public function __construct() {
|
25 |
add_filter( 'itsec-user-setting-valid-itsec-settings-view', array( $this, 'validate_view' ), null, 2 );
|
26 |
}
|
27 |
|
28 |
+
public function add_scripts() {
|
29 |
+
$this->set_translation_strings();
|
30 |
+
|
31 |
+
$vars = array(
|
32 |
+
'ajax_action' => 'itsec_settings_page',
|
33 |
+
'ajax_nonce' => wp_create_nonce( 'itsec-settings-nonce' ),
|
34 |
+
'translations' => $this->translations,
|
35 |
+
);
|
36 |
+
|
37 |
+
wp_enqueue_script( 'itsec-util-script', plugins_url( 'js/util.js', __FILE__ ), array(), $this->version, true );
|
38 |
+
wp_localize_script( 'itsec-util-script', 'itsec_util', $vars );
|
39 |
+
}
|
40 |
+
|
41 |
+
public function add_styles() {
|
42 |
+
wp_enqueue_style( 'itsec-settings-page-style', plugins_url( 'css/style.css', __FILE__ ), array(), $this->version );
|
43 |
+
}
|
44 |
+
|
45 |
+
private function set_translation_strings() {
|
46 |
+
$this->translations = array(
|
47 |
+
'ajax_invalid' => new WP_Error( 'itsec-settings-page-invalid-ajax-response', __( 'An "invalid format" error prevented the request from completing as expected. The format of data returned could not be recognized. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
48 |
+
|
49 |
+
'ajax_forbidden' => new WP_Error( 'itsec-settings-page-forbidden-ajax-response: %1$s "%2$s"', __( 'A "request forbidden" error prevented the request from completing as expected. The server returned a 403 status code, indicating that the server configuration is prohibiting this request. This could be due to a plugin/theme conflict or a server configuration issue. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings or server configuration that could account for this AJAX request being blocked.', 'better-wp-security' ) ),
|
50 |
+
|
51 |
+
'ajax_not_found' => new WP_Error( 'itsec-settings-page-not-found-ajax-response: %1$s "%2$s"', __( 'A "not found" error prevented the request from completing as expected. The server returned a 404 status code, indicating that the server was unable to find the requested admin-ajax.php file. This could be due to a plugin/theme conflict, a server configuration issue, or an incomplete WordPress installation. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings, alter server configurations, or reinstall WordPress.', 'better-wp-security' ) ),
|
52 |
+
|
53 |
+
'ajax_server_error' => new WP_Error( 'itsec-settings-page-server-error-ajax-response: %1$s "%2$s"', __( 'A "internal server" error prevented the request from completing as expected. The server returned a 500 status code, indicating that the server was unable to complete the request due to a fatal PHP error or a server problem. This could be due to a plugin/theme conflict, a server configuration issue, a temporary hosting issue, or invalid custom PHP modifications. Please check your server\'s error logs for details about the source of the error and contact your hosting company for assistance if required.', 'better-wp-security' ) ),
|
54 |
+
|
55 |
+
'ajax_unknown' => new WP_Error( 'itsec-settings-page-ajax-error-unknown: %1$s "%2$s"', __( 'An unknown error prevented the request from completing as expected. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
56 |
+
|
57 |
+
'ajax_timeout' => new WP_Error( 'itsec-settings-page-ajax-error-timeout: %1$s "%2$s"', __( 'A timeout error prevented the request from completing as expected. The site took too long to respond. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
58 |
+
|
59 |
+
'ajax_parsererror' => new WP_Error( 'itsec-settings-page-ajax-error-parsererror: %1$s "%2$s"', __( 'A parser error prevented the request from completing as expected. The site sent a response that jQuery could not process. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
60 |
+
);
|
61 |
+
|
62 |
+
foreach ( $this->translations as $key => $message ) {
|
63 |
+
if ( is_wp_error( $message ) ) {
|
64 |
+
$messages = ITSEC_Response::get_error_strings( $message );
|
65 |
+
$this->translations[$key] = $messages[0];
|
66 |
+
}
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
public function add_admin_pages() {
|
71 |
$capability = ITSEC_Core::get_required_cap();
|
72 |
$page_refs = array();
|
110 |
}
|
111 |
|
112 |
public function load() {
|
113 |
+
add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
|
114 |
+
add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
|
115 |
+
|
116 |
$this->load_file( 'page-%s.php' );
|
117 |
}
|
118 |
|
138 |
$id = $this->get_page_id();
|
139 |
|
140 |
if ( empty( $id ) ) {
|
141 |
+
if ( isset( $GLOBALS['pagenow'] ) && 'admin.php' === $GLOBALS['pagenow'] && isset( $_GET['page'] ) && 'itsec-' === substr( $_GET['page'], 0, 6 ) ) {
|
142 |
+
$id = substr( $_GET['page'], 6 );
|
143 |
+
} else {
|
144 |
+
return;
|
145 |
+
}
|
146 |
}
|
147 |
|
148 |
+
$id = str_replace( '_', '-', $id );
|
149 |
+
|
150 |
$file = dirname( __FILE__ ) . '/' . sprintf( $file, $id );
|
151 |
|
152 |
if ( is_file( $file ) ) {
|
core/admin-pages/js/logs.js
ADDED
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use strict";
|
2 |
+
|
3 |
+
var itsecLogsPage = {
|
4 |
+
init: function() {
|
5 |
+
this.bindEvents();
|
6 |
+
|
7 |
+
var id = itsecUtil.getUrlParameter( 'id' );
|
8 |
+
|
9 |
+
if ( false !== id ) {
|
10 |
+
itsecLogsPage.showLog( id );
|
11 |
+
}
|
12 |
+
|
13 |
+
itsecLogsPage.originalHREF = jQuery( '.itsec-module-cards-container .subsubsub a.current' ).attr( 'href' );
|
14 |
+
|
15 |
+
this.migrateOldLogs();
|
16 |
+
},
|
17 |
+
|
18 |
+
bindEvents: function() {
|
19 |
+
var $container = jQuery( '#wpcontent' );
|
20 |
+
|
21 |
+
$container.on( 'click', '.itsec-logs-view-details', this.showModal );
|
22 |
+
$container.on( 'click', '.itsec-close-modal, .itsec-modal-background', this.closeModal );
|
23 |
+
$container.on( 'click', '.itsec-log-raw-details-toggle', this.toggleRawDetails );
|
24 |
+
$container.on( 'keyup', this.closeModal );
|
25 |
+
},
|
26 |
+
|
27 |
+
toggleRawDetails: function( e ) {
|
28 |
+
e.preventDefault();
|
29 |
+
|
30 |
+
if ( jQuery( '.itsec-log-raw-details' ).is( ':visible' ) ) {
|
31 |
+
jQuery( this ).html( itsec_page.translations.show_raw_details );
|
32 |
+
jQuery( '.itsec-log-raw-details' ).hide();
|
33 |
+
} else {
|
34 |
+
jQuery( this ).html( itsec_page.translations.hide_raw_details );
|
35 |
+
jQuery( '.itsec-log-raw-details' ).show();
|
36 |
+
}
|
37 |
+
},
|
38 |
+
|
39 |
+
showModal: function( e ) {
|
40 |
+
e.preventDefault();
|
41 |
+
|
42 |
+
var id = jQuery( this ).parent().parent().find( 'td.column-id' ).html();
|
43 |
+
|
44 |
+
try {
|
45 |
+
if ( '' != itsecLogsPage.originalHREF ) {
|
46 |
+
window.history.replaceState( {}, '', itsecLogsPage.originalHREF + '&id=' + id );
|
47 |
+
}
|
48 |
+
} catch( err ) {}
|
49 |
+
|
50 |
+
itsecLogsPage.showLog( id );
|
51 |
+
},
|
52 |
+
|
53 |
+
showLog: function( id ) {
|
54 |
+
var $modalBackground = jQuery( '.itsec-modal-background' ),
|
55 |
+
$detailsContainer = jQuery( '#itsec-log-details-container' );
|
56 |
+
|
57 |
+
jQuery( '#itsec-log-details-container .itsec-module-messages-container' ).html( '' );
|
58 |
+
jQuery( '#itsec-log-details-container .itsec-module-settings-content-main' ).html( itsec_page.translations.loading );
|
59 |
+
|
60 |
+
$modalBackground.fadeIn();
|
61 |
+
$detailsContainer.fadeIn( 200 );
|
62 |
+
|
63 |
+
jQuery( 'body' ).addClass( 'itsec-modal-open' );
|
64 |
+
|
65 |
+
var $cached_data = jQuery( '#itsec-logs-cache-id-' + id );
|
66 |
+
|
67 |
+
if ( $cached_data.length ) {
|
68 |
+
jQuery( '#itsec-log-details-container' ).html( $cached_data.html() );
|
69 |
+
jQuery( '.itsec-log-raw-details' ).hide();
|
70 |
+
return;
|
71 |
+
}
|
72 |
+
|
73 |
+
var postData = {
|
74 |
+
'action': itsec_page.ajax_action,
|
75 |
+
'nonce': itsec_page.ajax_nonce,
|
76 |
+
'id': id,
|
77 |
+
};
|
78 |
+
|
79 |
+
jQuery.post( ajaxurl, postData )
|
80 |
+
.always(function( a, status, b ) {
|
81 |
+
itsecLogsPage.updateDetails( a, status, b, id );
|
82 |
+
});
|
83 |
+
},
|
84 |
+
|
85 |
+
updateDetails: function( a, status, b, id ) {
|
86 |
+
var results = {
|
87 |
+
'id': id,
|
88 |
+
'status': status,
|
89 |
+
'jqxhr': null,
|
90 |
+
'success': false,
|
91 |
+
'response': null,
|
92 |
+
'errors': [],
|
93 |
+
'warnings': [],
|
94 |
+
'messages': [],
|
95 |
+
'functionCalls': [],
|
96 |
+
'redirect': false,
|
97 |
+
'closeModal': true
|
98 |
+
};
|
99 |
+
|
100 |
+
|
101 |
+
if ( 'ITSEC_Response' === a.source && 'undefined' !== a.response ) {
|
102 |
+
// Successful response with a valid format.
|
103 |
+
results.jqxhr = b;
|
104 |
+
results.success = a.success;
|
105 |
+
results.response = a.response;
|
106 |
+
results.errors = a.errors;
|
107 |
+
results.warnings = a.warnings;
|
108 |
+
results.messages = a.messages;
|
109 |
+
results.functionCalls = a.functionCalls;
|
110 |
+
results.redirect = a.redirect;
|
111 |
+
results.closeModal = a.closeModal;
|
112 |
+
} else if ( a.responseText ) {
|
113 |
+
// Failed response.
|
114 |
+
results.jqxhr = a;
|
115 |
+
var errorThrown = b;
|
116 |
+
|
117 |
+
if ( 'undefined' === typeof results.jqxhr.status ) {
|
118 |
+
results.jqxhr.status = -1;
|
119 |
+
}
|
120 |
+
|
121 |
+
if ( 'timeout' === status ) {
|
122 |
+
var error = itsec_page.translations.ajax_timeout;
|
123 |
+
} else if ( 'parsererror' === status ) {
|
124 |
+
var error = itsec_page.translations.ajax_parsererror;
|
125 |
+
} else if ( 403 == results.jqxhr.status ) {
|
126 |
+
var error = itsec_page.translations.ajax_forbidden;
|
127 |
+
} else if ( 404 == results.jqxhr.status ) {
|
128 |
+
var error = itsec_page.translations.ajax_not_found;
|
129 |
+
} else if ( 500 == results.jqxhr.status ) {
|
130 |
+
var error = itsec_page.translations.ajax_server_error;
|
131 |
+
} else {
|
132 |
+
var error = itsec_page.translations.ajax_unknown;
|
133 |
+
}
|
134 |
+
|
135 |
+
error = error.replace( '%1$s', status );
|
136 |
+
error = error.replace( '%2$s', errorThrown );
|
137 |
+
|
138 |
+
results.errors = [ error ];
|
139 |
+
} else {
|
140 |
+
// Successful response with an invalid format.
|
141 |
+
results.jqxhr = b;
|
142 |
+
|
143 |
+
results.response = a;
|
144 |
+
results.errors = [ itsec_page.translations.ajax_invalid ];
|
145 |
+
}
|
146 |
+
|
147 |
+
|
148 |
+
if ( results.redirect ) {
|
149 |
+
window.location = results.redirect;
|
150 |
+
}
|
151 |
+
|
152 |
+
|
153 |
+
var $messages = jQuery( '#itsec-log-details-container .itsec-module-messages-container' ),
|
154 |
+
$content = jQuery( '#itsec-log-details-container .itsec-module-settings-content-main' );
|
155 |
+
|
156 |
+
$messages.html( '' );
|
157 |
+
|
158 |
+
for ( var i = 0; i < results.errors.length; i++ ) {
|
159 |
+
$messages.append( '<div class="error inline"><p><strong>' + results.errors[i] + '</strong></p></div>' );
|
160 |
+
}
|
161 |
+
for ( var i = 0; i < results.warnings.length; i++ ) {
|
162 |
+
$messages.append( '<div class="warning inline"><p><strong>' + results.warnings[i] + '</strong></p></div>' );
|
163 |
+
}
|
164 |
+
for ( var i = 0; i < results.messages.length; i++ ) {
|
165 |
+
$messages.append( '<div class="info inline"><p><strong>' + results.messages[i] + '</strong></p></div>' );
|
166 |
+
}
|
167 |
+
|
168 |
+
$content.html( results.response );
|
169 |
+
|
170 |
+
|
171 |
+
var $div = jQuery( '<div>', {id: 'itsec-logs-cache-id-' + id} );
|
172 |
+
$div.html( jQuery( '#itsec-log-details-container' ).html() );
|
173 |
+
|
174 |
+
jQuery( '#itsec-logs-cache' ).append( $div );
|
175 |
+
},
|
176 |
+
|
177 |
+
closeModal: function( e ) {
|
178 |
+
if ( 'undefined' !== typeof e ) {
|
179 |
+
e.preventDefault();
|
180 |
+
|
181 |
+
// For keyup events, only process esc
|
182 |
+
if ( 'keyup' === e.type && 27 !== e.which ) {
|
183 |
+
return;
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
|
188 |
+
try {
|
189 |
+
if ( '' != itsecLogsPage.originalHREF ) {
|
190 |
+
window.history.replaceState( {}, '', itsecLogsPage.originalHREF );
|
191 |
+
}
|
192 |
+
} catch( err ) {}
|
193 |
+
|
194 |
+
|
195 |
+
jQuery( '.itsec-modal-background' ).fadeOut();
|
196 |
+
jQuery( '#itsec-log-details-container' ).fadeOut( 200 );
|
197 |
+
jQuery( 'body' ).removeClass( 'itsec-modal-open' );
|
198 |
+
},
|
199 |
+
|
200 |
+
migrateOldLogs: function() {
|
201 |
+
var $status = jQuery( '#old-logs-migration-status' );
|
202 |
+
|
203 |
+
if ( $status.length < 1 ) {
|
204 |
+
return;
|
205 |
+
}
|
206 |
+
|
207 |
+
var message = itsec_page.translations.log_migration_started.replace( '%1$s', '<img src="' + itsec_page.translations.log_migration_loading_url + '" />' );
|
208 |
+
|
209 |
+
$status.append( '<div class="notice notice-info notice-alt"><p><strong>' + message + '</strong></p></div>' );
|
210 |
+
|
211 |
+
itsecLogsPage.sendMigrationRequest();
|
212 |
+
},
|
213 |
+
|
214 |
+
handleMigrationCallback: function( results ) {
|
215 |
+
var clearStatus = false;
|
216 |
+
|
217 |
+
if ( results.response && results.response.length ) {
|
218 |
+
if ( 'incomplete' === results.response ) {
|
219 |
+
if ( 'undefined' === typeof itsecLogsPage.callCount ) {
|
220 |
+
itsecLogsPage.callCount = 1;
|
221 |
+
}
|
222 |
+
|
223 |
+
itsecLogsPage.callCount++;
|
224 |
+
|
225 |
+
if ( 0 === itsecLogsPage.callCount % 10 ) {
|
226 |
+
// Every 10 requests, delay a bit to prevent from slamming the server.
|
227 |
+
setTimeout( itsecLogsPage.sendMigrationRequest, 5000 );
|
228 |
+
} else {
|
229 |
+
itsecLogsPage.sendMigrationRequest();
|
230 |
+
}
|
231 |
+
|
232 |
+
return;
|
233 |
+
}
|
234 |
+
|
235 |
+
jQuery('#old-logs-migration-status').html( results.response );
|
236 |
+
} else {
|
237 |
+
clearStatus = true;
|
238 |
+
}
|
239 |
+
|
240 |
+
if ( results.errors.length > 0 ) {
|
241 |
+
if ( 'undefined' === typeof itsecLogsPage.errorCount ) {
|
242 |
+
itsecLogsPage.errorCount = 0;
|
243 |
+
}
|
244 |
+
|
245 |
+
itsecLogsPage.errorCount++;
|
246 |
+
|
247 |
+
if ( itsecLogsPage.errorCount < 10 ) {
|
248 |
+
// Keep retrying until we reach 10 errors, but delay a bit before retrying.
|
249 |
+
setTimeout( itsecLogsPage.sendMigrationRequest, 5000 );
|
250 |
+
|
251 |
+
return;
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
if ( clearStatus ) {
|
256 |
+
jQuery('#old-logs-migration-status').html( '' );
|
257 |
+
}
|
258 |
+
|
259 |
+
if ( results.errors.length > 0 ) {
|
260 |
+
var message = '<div class="notice notice-error notice-alt"><p><strong>' + itsec_page.translations.log_migration_failed + '</strong></p></div>';
|
261 |
+
jQuery('#old-logs-migration-status').append( message );
|
262 |
+
}
|
263 |
+
|
264 |
+
if ( results.warnings.length > 0 ) {
|
265 |
+
jQuery.each( results.warnings, function( index, warning ) {
|
266 |
+
message = '<div class="notice notice-warning notice-alt"><p>' + warning + '</p></div>';
|
267 |
+
jQuery('#old-logs-migration-status').append( message );
|
268 |
+
} );
|
269 |
+
}
|
270 |
+
},
|
271 |
+
|
272 |
+
sendMigrationRequest: function() {
|
273 |
+
itsecUtil.sendAJAXRequest( 'logs', 'handle_logs_migration', {}, itsecLogsPage.handleMigrationCallback, itsec_page.ajax_action, itsec_page.ajax_nonce );
|
274 |
+
}
|
275 |
+
};
|
276 |
+
|
277 |
+
jQuery(document).ready(function( $ ) {
|
278 |
+
itsecLogsPage.init();
|
279 |
+
});
|
core/admin-pages/js/settings.js
ADDED
@@ -0,0 +1,975 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use strict";
|
2 |
+
|
3 |
+
var itsecSettingsPage = {
|
4 |
+
|
5 |
+
events: jQuery( {} ),
|
6 |
+
|
7 |
+
init: function() {
|
8 |
+
jQuery( '.itsec-module-settings-container' ).hide();
|
9 |
+
|
10 |
+
this.bindEvents();
|
11 |
+
|
12 |
+
jQuery( '.itsec-settings-view-toggle .itsec-selected' ).removeClass( 'itsec-selected' ).trigger( 'click' );
|
13 |
+
jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
|
14 |
+
|
15 |
+
this.initFilters();
|
16 |
+
this.initCurrentModule();
|
17 |
+
this.makeNoticesDismissible();
|
18 |
+
},
|
19 |
+
|
20 |
+
initFilters: function() {
|
21 |
+
var module_type = itsecUtil.getUrlParameter( 'module_type' );
|
22 |
+
if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
|
23 |
+
module_type = 'recommended';
|
24 |
+
}
|
25 |
+
jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'click' );
|
26 |
+
},
|
27 |
+
|
28 |
+
initCurrentModule: function() {
|
29 |
+
|
30 |
+
var module = itsecUtil.getUrlParameter( 'module' );
|
31 |
+
if ( 'string' === typeof module ) {
|
32 |
+
jQuery( '#itsec-module-card-' + module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'click' );
|
33 |
+
}
|
34 |
+
},
|
35 |
+
|
36 |
+
bindEvents: function() {
|
37 |
+
|
38 |
+
if ( itsecSettingsPage.bindEvents.bound ) {
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
jQuery(window).on("popstate", function(e, data) {
|
43 |
+
if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module && '' !== e.originalEvent.state.module.replace( /[^\w-]/g, '' ) ) {
|
44 |
+
jQuery( '#itsec-module-card-' + e.originalEvent.state.module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'itsec-popstate' );
|
45 |
+
} else {
|
46 |
+
itsecSettingsPage.closeGridSettingsModal( e );
|
47 |
+
}
|
48 |
+
|
49 |
+
if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module_type && '' !== e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) ) {
|
50 |
+
jQuery( '#itsec-module-filter-' + e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'itsec-popstate' );
|
51 |
+
}
|
52 |
+
});
|
53 |
+
|
54 |
+
var $container = jQuery( '#wpcontent' );
|
55 |
+
|
56 |
+
$container.on( 'click', '.itsec-module-filter a', this.filterView );
|
57 |
+
$container.on( 'itsec-popstate', '.itsec-module-filter a', this.filterView );
|
58 |
+
$container.on( 'click', '.itsec-settings-view-toggle a', this.toggleView );
|
59 |
+
// $container.on( 'click', '.itsec-toggle-settings, .itsec-module-card-content h2', this.toggleSettings );
|
60 |
+
$container.on( 'click', 'a[data-module-link]', this.openModuleFromLink );
|
61 |
+
$container.on( 'click', '.list .itsec-module-card:not(.itsec-module-pro-upsell) .itsec-module-card-content, .itsec-toggle-settings, .itsec-module-settings-cancel', this.toggleSettings );
|
62 |
+
$container.on( 'itsec-popstate', '.list .itsec-module-card-content, .itsec-toggle-settings', this.toggleSettings );
|
63 |
+
$container.on( 'click', '.itsec-close-modal, .itsec-modal-background', this.closeGridSettingsModal );
|
64 |
+
$container.on( 'keyup', this.closeGridSettingsModal );
|
65 |
+
$container.on( 'click', '.itsec-toggle-activation', this.toggleModuleActivation );
|
66 |
+
$container.on( 'click', '.itsec-module-settings-save', this.saveSettings );
|
67 |
+
$container.on( 'click', '.itsec-reload-module', this.reloadModule );
|
68 |
+
$container.on( 'click', '.itsec-details-toggle-container a[href="#"]', this.toggleDetails );
|
69 |
+
|
70 |
+
$container.on( 'change', '#itsec-filter', this.logPageChangeFilter );
|
71 |
+
|
72 |
+
// For use by module content to show/hide settings sections based upon an input.
|
73 |
+
$container.on( 'change', '.itsec-settings-toggle', this.toggleModuleContent );
|
74 |
+
$container.on( 'click', '.itsec-copy-trigger', this.handleCopy );
|
75 |
+
|
76 |
+
itsecSettingsPage.bindEvents.bound = true;
|
77 |
+
},
|
78 |
+
|
79 |
+
toggleDetails: function( e ) {
|
80 |
+
e.preventDefault();
|
81 |
+
|
82 |
+
var $details = jQuery(this).parent().find( '.itsec-details-toggle-details' ).toggleClass( 'hide-if-js' );
|
83 |
+
|
84 |
+
if ( $details.hasClass( 'hide-if-js' ) ) {
|
85 |
+
jQuery(this).html( itsec_page.translations.show_information );
|
86 |
+
} else {
|
87 |
+
jQuery(this).html( itsec_page.translations.hide_description );
|
88 |
+
}
|
89 |
+
},
|
90 |
+
|
91 |
+
logPageChangeFilter: function( e ) {
|
92 |
+
var filter = jQuery( this ).val();
|
93 |
+
var url = itsec_page.logs_page_url + '&filter=' + filter;
|
94 |
+
window.location.href = url;
|
95 |
+
},
|
96 |
+
|
97 |
+
toggleModuleContent: function( e ) {
|
98 |
+
if ( 'checkbox' === jQuery(this).attr( 'type' ) ) {
|
99 |
+
var show = jQuery(this).prop( 'checked' );
|
100 |
+
} else {
|
101 |
+
var show = ( jQuery(this).val() ) ? true : false;
|
102 |
+
}
|
103 |
+
|
104 |
+
var $content = jQuery( '.' + jQuery(this).attr( 'id' ) + '-content' );
|
105 |
+
|
106 |
+
if ( show ) {
|
107 |
+
$content.show();
|
108 |
+
|
109 |
+
|
110 |
+
var $container = jQuery( '.itsec-module-cards-container' );
|
111 |
+
|
112 |
+
if ( $container.hasClass( 'grid' ) ) {
|
113 |
+
var $modal = jQuery(this).parents( '.itsec-module-settings-content-container' );
|
114 |
+
var scrollOffset = $modal.scrollTop() + jQuery(this).parent().position().top;
|
115 |
+
|
116 |
+
$modal.animate( {'scrollTop': scrollOffset}, 'slow' );
|
117 |
+
}
|
118 |
+
} else {
|
119 |
+
$content.hide();
|
120 |
+
}
|
121 |
+
},
|
122 |
+
|
123 |
+
handleCopy: function( e ) {
|
124 |
+
|
125 |
+
e.preventDefault();
|
126 |
+
|
127 |
+
var $trigger = jQuery( e.currentTarget );
|
128 |
+
var fromId = $trigger.data( 'copy-from' );
|
129 |
+
|
130 |
+
if ( ! fromId.length ) {
|
131 |
+
return;
|
132 |
+
}
|
133 |
+
|
134 |
+
var el = document.getElementById( fromId );
|
135 |
+
|
136 |
+
var removeSelect = itsecSettingsPage.selectText( el );
|
137 |
+
|
138 |
+
try {
|
139 |
+
|
140 |
+
document.execCommand( 'copy' );
|
141 |
+
removeSelect();
|
142 |
+
$trigger.text( itsec_page.translations.copied );
|
143 |
+
|
144 |
+
} catch ( e ) {
|
145 |
+
var $p = jQuery( '<p></p>' ).text( itsec_page.translations.copy_instruction ),
|
146 |
+
$notice = jQuery( '<div class="notice notice-alt notice-info"></div>' ).append( $p ),
|
147 |
+
$el = jQuery( el );
|
148 |
+
|
149 |
+
$trigger.after( $notice );
|
150 |
+
|
151 |
+
var removeNotice = function () {
|
152 |
+
$notice.fadeOut( function () {
|
153 |
+
$notice.remove();
|
154 |
+
} );
|
155 |
+
};
|
156 |
+
var copy = function () {
|
157 |
+
|
158 |
+
setTimeout( function () {
|
159 |
+
removeNotice();
|
160 |
+
removeSelect();
|
161 |
+
}, 100 );
|
162 |
+
|
163 |
+
$el.off( 'copy', copy );
|
164 |
+
|
165 |
+
return true;
|
166 |
+
};
|
167 |
+
|
168 |
+
$el.on( 'copy', copy );
|
169 |
+
|
170 |
+
setTimeout( removeNotice, 5000 );
|
171 |
+
}
|
172 |
+
},
|
173 |
+
|
174 |
+
// https://stackoverflow.com/a/987376
|
175 |
+
selectText: function( element ) {
|
176 |
+
var doc = document, text = element, range, selection;
|
177 |
+
|
178 |
+
if ( doc.body.createTextRange ) { // ie
|
179 |
+
range = document.body.createTextRange();
|
180 |
+
range.moveToElementText( text );
|
181 |
+
range.select();
|
182 |
+
} else if ( window.getSelection ) {
|
183 |
+
selection = window.getSelection();
|
184 |
+
range = document.createRange();
|
185 |
+
range.selectNodeContents( text );
|
186 |
+
selection.removeAllRanges();
|
187 |
+
selection.addRange( range );
|
188 |
+
}
|
189 |
+
|
190 |
+
return function() {
|
191 |
+
if ( selection ) {
|
192 |
+
selection.removeAllRanges();
|
193 |
+
} else {
|
194 |
+
range.collapse();
|
195 |
+
}
|
196 |
+
};
|
197 |
+
},
|
198 |
+
|
199 |
+
saveSettings: function( e ) {
|
200 |
+
e.preventDefault();
|
201 |
+
|
202 |
+
var $button = jQuery(this);
|
203 |
+
|
204 |
+
if ( $button.hasClass( 'itsec-module-settings-save' ) ) {
|
205 |
+
var module = $button.parents( '.itsec-module-card' ).attr( 'id' ).replace( 'itsec-module-card-', '' );
|
206 |
+
} else {
|
207 |
+
var module = '';
|
208 |
+
}
|
209 |
+
|
210 |
+
$button.prop( 'disabled', true );
|
211 |
+
|
212 |
+
var data = {
|
213 |
+
'--itsec-form-serialized-data': jQuery( '#itsec-module-settings-form' ).serialize()
|
214 |
+
};
|
215 |
+
|
216 |
+
itsecUtil.sendAJAXRequest( module, 'save', data, itsecSettingsPage.saveSettingsCallback );
|
217 |
+
},
|
218 |
+
|
219 |
+
saveSettingsCallback: function( results ) {
|
220 |
+
if ( '' === results.module ) {
|
221 |
+
jQuery( '#itsec-save' ).prop( 'disabled', false );
|
222 |
+
} else {
|
223 |
+
jQuery( '#itsec-module-card-' + results.module + ' button.itsec-module-settings-save' ).prop( 'disabled', false );
|
224 |
+
}
|
225 |
+
|
226 |
+
var $container = jQuery( '.itsec-module-cards-container' );
|
227 |
+
|
228 |
+
if ( $container.hasClass( 'grid' ) ) {
|
229 |
+
var view = 'grid';
|
230 |
+
} else {
|
231 |
+
var view = 'list';
|
232 |
+
}
|
233 |
+
|
234 |
+
itsecSettingsPage.clearMessages();
|
235 |
+
|
236 |
+
if ( results.errors.length > 0 || results.warnings.length > 0 || ! results.closeModal ) {
|
237 |
+
itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
|
238 |
+
itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
|
239 |
+
itsecSettingsPage.showMessages( results.messages, results.module, 'open' );
|
240 |
+
itsecSettingsPage.showMessages( results.infos, results.module, 'open', 'info' );
|
241 |
+
|
242 |
+
if ( 'grid' === view ) {
|
243 |
+
$container.find( '.itsec-module-settings-content-container:visible' ).animate( {'scrollTop': 0}, 'fast' );
|
244 |
+
}
|
245 |
+
|
246 |
+
if ( 'list' === view ) {
|
247 |
+
jQuery(document).scrollTop( 0 );
|
248 |
+
}
|
249 |
+
} else {
|
250 |
+
itsecSettingsPage.showMessages( results.messages, results.module, 'closed' );
|
251 |
+
itsecSettingsPage.showMessages( results.infos, results.module, 'closed', 'info' );
|
252 |
+
|
253 |
+
if ( 'grid' === view ) {
|
254 |
+
$container.find( '.itsec-module-settings-content-container:visible' ).scrollTop( 0 );
|
255 |
+
itsecSettingsPage.closeGridSettingsModal();
|
256 |
+
}
|
257 |
+
}
|
258 |
+
},
|
259 |
+
|
260 |
+
clearMessages: function() {
|
261 |
+
jQuery( '#itsec-settings-messages-container, .itsec-module-messages-container' ).empty();
|
262 |
+
},
|
263 |
+
|
264 |
+
showErrors: function( errors, module, containerStatus, type ) {
|
265 |
+
jQuery.each( errors, function( index, error ) {
|
266 |
+
itsecSettingsPage.showError( error, module, containerStatus, type );
|
267 |
+
} );
|
268 |
+
},
|
269 |
+
|
270 |
+
showError: function( error, module, containerStatus, type ) {
|
271 |
+
|
272 |
+
type = type || 'error';
|
273 |
+
|
274 |
+
if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
|
275 |
+
var view = 'grid';
|
276 |
+
} else {
|
277 |
+
var view = 'list';
|
278 |
+
}
|
279 |
+
|
280 |
+
if ( 'closed' !== containerStatus && 'open' !== containerStatus ) {
|
281 |
+
containerStatus = 'closed';
|
282 |
+
}
|
283 |
+
|
284 |
+
if ( 'string' !== typeof module ) {
|
285 |
+
module = '';
|
286 |
+
}
|
287 |
+
|
288 |
+
|
289 |
+
if ( 'closed' === containerStatus || '' === module ) {
|
290 |
+
var container = jQuery( '#itsec-settings-messages-container' );
|
291 |
+
|
292 |
+
if ( '' === module ) {
|
293 |
+
container.addClass( 'no-module' );
|
294 |
+
}
|
295 |
+
} else {
|
296 |
+
var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
|
297 |
+
}
|
298 |
+
|
299 |
+
var $notice = jQuery( '<div class="notice"><p><strong>' + error + '</strong></p></div>' );
|
300 |
+
$notice.addClass( 'notice-' + type );
|
301 |
+
|
302 |
+
if ( containerStatus === 'open' || module.length ) {
|
303 |
+
$notice.addClass( 'notice-alt' );
|
304 |
+
}
|
305 |
+
|
306 |
+
container.append( $notice ).addClass( 'visible' );
|
307 |
+
},
|
308 |
+
|
309 |
+
showMessages: function( messages, module, containerStatus, type ) {
|
310 |
+
jQuery.each( messages, function( index, message ) {
|
311 |
+
itsecSettingsPage.showMessage( message, module, containerStatus, type );
|
312 |
+
} );
|
313 |
+
},
|
314 |
+
|
315 |
+
showMessage: function( message, module, containerStatus, type ) {
|
316 |
+
|
317 |
+
type = type || 'success';
|
318 |
+
|
319 |
+
if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
|
320 |
+
var view = 'grid';
|
321 |
+
} else {
|
322 |
+
var view = 'list';
|
323 |
+
}
|
324 |
+
|
325 |
+
if ( 'closed' !== containerStatus && 'open' !== containerStatus ) {
|
326 |
+
containerStatus = 'closed';
|
327 |
+
}
|
328 |
+
|
329 |
+
if ( 'string' !== typeof module ) {
|
330 |
+
module = '';
|
331 |
+
}
|
332 |
+
|
333 |
+
|
334 |
+
if ( 'closed' === containerStatus || '' === module ) {
|
335 |
+
var container = jQuery( '#itsec-settings-messages-container' );
|
336 |
+
|
337 |
+
var dismiss = function () {
|
338 |
+
|
339 |
+
if ( container.is( ':hover' ) ) {
|
340 |
+
return setTimeout( dismiss, 2000 );
|
341 |
+
}
|
342 |
+
|
343 |
+
container.removeClass( 'visible' );
|
344 |
+
setTimeout( function () {
|
345 |
+
container.find( 'div' ).remove();
|
346 |
+
}, 500 );
|
347 |
+
};
|
348 |
+
|
349 |
+
setTimeout( dismiss, 4000 );
|
350 |
+
} else {
|
351 |
+
var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
|
352 |
+
}
|
353 |
+
|
354 |
+
var $notice = jQuery( '<div class="notice fade"><p><strong>' + message + '</strong></p></div>' );
|
355 |
+
$notice.addClass( 'notice-' + type );
|
356 |
+
|
357 |
+
if ( containerStatus === 'open' || module.length ) {
|
358 |
+
$notice.addClass( 'notice-alt' );
|
359 |
+
}
|
360 |
+
|
361 |
+
container.append( $notice ).addClass( 'visible' );
|
362 |
+
},
|
363 |
+
|
364 |
+
filterView: function( e ) {
|
365 |
+
e.preventDefault();
|
366 |
+
|
367 |
+
var $activeLink = jQuery(this),
|
368 |
+
$oldLink = $activeLink.parents( '.itsec-feature-tabs' ).find( '.current' ),
|
369 |
+
type = $activeLink.parent().attr( 'id' ).substr( 20 );
|
370 |
+
|
371 |
+
$oldLink.removeClass( 'current' );
|
372 |
+
$activeLink.addClass( 'current' );
|
373 |
+
|
374 |
+
if ( 'all' === type ) {
|
375 |
+
jQuery( '.itsec-module-card' ).show();
|
376 |
+
} else {
|
377 |
+
jQuery( '.itsec-module-type-' + type ).show();
|
378 |
+
jQuery( '.itsec-module-card' ).not( '.itsec-module-type-' + type ).hide();
|
379 |
+
}
|
380 |
+
|
381 |
+
// We use this to avoid pushing a new state when we're trying to handle a popstate
|
382 |
+
if ( 'itsec-popstate' !== e.type ) {
|
383 |
+
var url = '?page=itsec&module_type=' + type;
|
384 |
+
var module = itsecUtil.getUrlParameter( 'module' );
|
385 |
+
if ( 'string' === typeof module ) {
|
386 |
+
url += '&module=' + module;
|
387 |
+
}
|
388 |
+
|
389 |
+
window.history.pushState( {'module_type':type}, type, url );
|
390 |
+
}
|
391 |
+
},
|
392 |
+
|
393 |
+
toggleView: function( e ) {
|
394 |
+
e.preventDefault();
|
395 |
+
|
396 |
+
var $self = jQuery(this);
|
397 |
+
|
398 |
+
if ( $self.hasClass( 'itsec-selected' ) ) {
|
399 |
+
// Do nothing if already selected.
|
400 |
+
return;
|
401 |
+
}
|
402 |
+
|
403 |
+
var $view = $self.attr( 'class' ).replace( 'itsec-', '' );
|
404 |
+
|
405 |
+
$self.addClass( 'itsec-selected' ).siblings().removeClass( 'itsec-selected' );
|
406 |
+
jQuery( '.itsec-module-settings-container' ).hide();
|
407 |
+
|
408 |
+
jQuery( '.itsec-toggle-settings' ).each(function( index ) {
|
409 |
+
var $button = jQuery( this );
|
410 |
+
|
411 |
+
if ( $button.parents( '.itsec-module-card' ).hasClass( 'itsec-module-type-enabled' ) && ! $button.hasClass( 'information-only' ) ) {
|
412 |
+
$button.html( itsec_page.translations.show_settings );
|
413 |
+
} else if ( $button.hasClass( 'information-only' ) ) {
|
414 |
+
$button.html( itsec_page.translations.information_only );
|
415 |
+
} else {
|
416 |
+
$button.html( itsec_page.translations.show_description );
|
417 |
+
}
|
418 |
+
});
|
419 |
+
|
420 |
+
var $cardContainer = jQuery( '.itsec-module-cards-container' );
|
421 |
+
jQuery.post( ajaxurl, {
|
422 |
+
'action': 'itsec-set-user-setting',
|
423 |
+
'itsec-user-setting-nonce': $self.parent().data( 'nonce' ),
|
424 |
+
'setting': 'itsec-settings-view',
|
425 |
+
'value': $view
|
426 |
+
} );
|
427 |
+
|
428 |
+
$cardContainer.fadeOut( 100, function() {
|
429 |
+
$cardContainer.removeClass( 'grid list' ).addClass( $view );
|
430 |
+
} );
|
431 |
+
$cardContainer.fadeIn( 100 );
|
432 |
+
},
|
433 |
+
|
434 |
+
openModuleFromLink: function( e ) {
|
435 |
+
|
436 |
+
var $link = jQuery( this ), module = $link.data( 'module-link' ),
|
437 |
+
$module = jQuery( '.itsec-module-card[data-module-id="' + module + '"]' ),
|
438 |
+
highlight = $link.data( 'highlight-setting-id' );
|
439 |
+
|
440 |
+
if ( ! $module.length ) {
|
441 |
+
return; // safety check
|
442 |
+
}
|
443 |
+
|
444 |
+
e.preventDefault();
|
445 |
+
|
446 |
+
jQuery( '.itsec-module-settings-container:visible' ).hide();
|
447 |
+
|
448 |
+
var $listClassElement = $module.parents( '.itsec-module-cards-container' ),
|
449 |
+
$toggleButton = $module.find( '.itsec-toggle-settings' );
|
450 |
+
|
451 |
+
if ( highlight && highlight.length ) {
|
452 |
+
jQuery( 'label[for="' + highlight + '"]', $module ).parents( 'tr' ).addClass( 'itsec-highlighted-setting' );
|
453 |
+
}
|
454 |
+
|
455 |
+
if ( $listClassElement.hasClass( 'list' ) ) {
|
456 |
+
itsecSettingsPage.toggleListSettingsCard.call( $toggleButton, e );
|
457 |
+
} else if ( $listClassElement.hasClass( 'grid' ) ) {
|
458 |
+
itsecSettingsPage.showGridSettingsModal.call( $toggleButton, e );
|
459 |
+
}
|
460 |
+
|
461 |
+
var type = $module.hasClass( 'itsec-module-type-advanced' ) ? 'advanced' : 'recommended';
|
462 |
+
|
463 |
+
window.history.pushState( {module: module}, module, '?page=itsec&module=' + module + '&module_type=' + type );
|
464 |
+
|
465 |
+
var href = $link.attr( 'href' );
|
466 |
+
|
467 |
+
if ( href && href.length > 1 && href.charAt( 0 ) === '#' ) {
|
468 |
+
setTimeout( function () {
|
469 |
+
jQuery( '.itsec-module-settings-content-container', '#itsec-module-card-notification-center' ).scrollTo( jQuery( href ), 'swing', { offset: -30 } );
|
470 |
+
}, 350 );
|
471 |
+
}
|
472 |
+
},
|
473 |
+
|
474 |
+
toggleSettings: function( e ) {
|
475 |
+
e.stopPropagation();
|
476 |
+
|
477 |
+
var $listClassElement = jQuery(e.currentTarget).parents( '.itsec-module-cards-container' );
|
478 |
+
|
479 |
+
if ( $listClassElement.hasClass( 'list') ) {
|
480 |
+
itsecSettingsPage.toggleListSettingsCard.call( this, e );
|
481 |
+
} else if ( $listClassElement.hasClass( 'grid' ) ) {
|
482 |
+
itsecSettingsPage.showGridSettingsModal.call( this, e );
|
483 |
+
}
|
484 |
+
|
485 |
+
// We use this to avoid pushing a new state when we're trying to handle a popstate
|
486 |
+
if ( 'itsec-popstate' !== e.type ) {
|
487 |
+
var module_id = jQuery(this).closest('.itsec-module-card').data( 'module-id' );
|
488 |
+
|
489 |
+
var module_type = itsecUtil.getUrlParameter( 'module_type' );
|
490 |
+
if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
|
491 |
+
module_type = 'recommended';
|
492 |
+
}
|
493 |
+
window.history.pushState( {'module':module_id}, module_id, '?page=itsec&module=' + module_id + '&module_type=' + module_type );
|
494 |
+
}
|
495 |
+
},
|
496 |
+
|
497 |
+
toggleListSettingsCard: function( e ) {
|
498 |
+
e.preventDefault();
|
499 |
+
|
500 |
+
var $container = jQuery(this);
|
501 |
+
|
502 |
+
if ( ! $container.hasClass( 'itsec-module-card-content' ) ) {
|
503 |
+
$container = $container.parents( '.itsec-module-card' ).find( '.itsec-module-card-content' );
|
504 |
+
}
|
505 |
+
|
506 |
+
var $settings = $container.siblings( '.itsec-module-settings-container' ),
|
507 |
+
isVisible = $settings.is( ':visible' );
|
508 |
+
$settings.stop().slideToggle( 300 );
|
509 |
+
|
510 |
+
if ( ! isVisible ) {
|
511 |
+
var $highlighted = jQuery( '.itsec-highlighted-setting', $settings );
|
512 |
+
|
513 |
+
if ( $highlighted.length ) {
|
514 |
+
setTimeout( function () {
|
515 |
+
jQuery.scrollTo( $highlighted.first(), 'swing', {
|
516 |
+
offset: { top: -30 },
|
517 |
+
onAfter: function() {
|
518 |
+
var $el = jQuery( 'input[type!="button"], textarea, select', $highlighted ).not( ':hidden' ).first();
|
519 |
+
itsecUtil.focus( $el, $highlighted );
|
520 |
+
}
|
521 |
+
} );
|
522 |
+
}, 50 );
|
523 |
+
} else {
|
524 |
+
var $el = jQuery( 'input[type!="button"], textarea, select', $settings ).not( ':hidden' ).first();
|
525 |
+
itsecUtil.focus( $el, $settings );
|
526 |
+
}
|
527 |
+
}
|
528 |
+
|
529 |
+
var $button = $container.find( '.itsec-toggle-settings' );
|
530 |
+
|
531 |
+
if ( $container.parent().hasClass( 'itsec-module-type-enabled' ) ) {
|
532 |
+
if ( $button.html() == itsec_page.translations.show_settings ) {
|
533 |
+
$button.html( itsec_page.translations.hide_settings );
|
534 |
+
} else {
|
535 |
+
$button.html( itsec_page.translations.show_settings );
|
536 |
+
}
|
537 |
+
} else {
|
538 |
+
if ( $button.hasClass( 'information-only' ) ) {
|
539 |
+
if ( $button.html() == itsec_page.translations.show_information ) {
|
540 |
+
$button.html( itsec_page.translations.hide_description );
|
541 |
+
} else {
|
542 |
+
$button.html( itsec_page.translations.show_information );
|
543 |
+
}
|
544 |
+
} else {
|
545 |
+
if ( $button.html() == itsec_page.translations.show_description ) {
|
546 |
+
$button.html( itsec_page.translations.hide_description );
|
547 |
+
} else {
|
548 |
+
$button.html( itsec_page.translations.show_description );
|
549 |
+
}
|
550 |
+
}
|
551 |
+
}
|
552 |
+
},
|
553 |
+
|
554 |
+
showGridSettingsModal: function( e ) {
|
555 |
+
e.preventDefault();
|
556 |
+
|
557 |
+
var $module = jQuery(this).parents( '.itsec-module-card' ),
|
558 |
+
$settingsContainer = $module.find( '.itsec-module-settings-container' ),
|
559 |
+
$modalBackground = jQuery( '.itsec-modal-background' );
|
560 |
+
|
561 |
+
$module.show();
|
562 |
+
|
563 |
+
$modalBackground.fadeIn();
|
564 |
+
$settingsContainer.fadeIn( 200 );
|
565 |
+
|
566 |
+
jQuery( 'body' ).addClass( 'itsec-modal-open' );
|
567 |
+
|
568 |
+
var $highlighted = jQuery( '.itsec-highlighted-setting', $module ).first();
|
569 |
+
|
570 |
+
if ( $highlighted.length ) {
|
571 |
+
jQuery( '.itsec-module-settings-content-container', $module ).scrollTo( $highlighted, 'swing', {
|
572 |
+
offset: { top: -20 },
|
573 |
+
onAfter: function() {
|
574 |
+
var $el = jQuery( 'input[type!="button"], textarea, select', $highlighted ).not( ':hidden' ).first();
|
575 |
+
itsecUtil.focus( $el, $highlighted );
|
576 |
+
}
|
577 |
+
} );
|
578 |
+
} else {
|
579 |
+
var $el = jQuery( 'input[type!="button"], textarea, select', $settingsContainer ).not( ':hidden' ).first();
|
580 |
+
itsecUtil.focus( $el, $settingsContainer );
|
581 |
+
}
|
582 |
+
},
|
583 |
+
|
584 |
+
closeGridSettingsModal: function( e ) {
|
585 |
+
if ( 'undefined' !== typeof e ) {
|
586 |
+
e.preventDefault();
|
587 |
+
|
588 |
+
// For keyup events, only process esc
|
589 |
+
if ( 'keyup' === e.type && 27 !== e.which ) {
|
590 |
+
return;
|
591 |
+
}
|
592 |
+
}
|
593 |
+
|
594 |
+
jQuery( '.itsec-modal-background' ).fadeOut();
|
595 |
+
jQuery( '.itsec-module-settings-container' ).fadeOut( 200 );
|
596 |
+
jQuery( 'body' ).removeClass( 'itsec-modal-open' );
|
597 |
+
|
598 |
+
if ( 'undefined' === typeof e || 'popstate' !== e.type ) {
|
599 |
+
var module_type = itsecUtil.getUrlParameter( 'module_type' );
|
600 |
+
if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
|
601 |
+
module_type = 'recommended';
|
602 |
+
}
|
603 |
+
window.history.pushState( {'module':'', 'module_type':module_type}, module_type, '?page=itsec&module_type=' + module_type );
|
604 |
+
}
|
605 |
+
|
606 |
+
if ( jQuery( '#search' ).val().length ) {
|
607 |
+
jQuery( '#search' ).focus();
|
608 |
+
}
|
609 |
+
},
|
610 |
+
|
611 |
+
toggleModuleActivation: function( e ) {
|
612 |
+
e.preventDefault();
|
613 |
+
e.stopPropagation();
|
614 |
+
|
615 |
+
var $button = jQuery(this),
|
616 |
+
$card = $button.parents( '.itsec-module-card' ),
|
617 |
+
$buttons = $card.find( '.itsec-toggle-activation' ),
|
618 |
+
module = $card.attr( 'id' ).replace( 'itsec-module-card-', '' );
|
619 |
+
|
620 |
+
$buttons.prop( 'disabled', true );
|
621 |
+
|
622 |
+
if ( $button.html() == itsec_page.translations.activate ) {
|
623 |
+
var method = 'activate';
|
624 |
+
} else {
|
625 |
+
var method = 'deactivate';
|
626 |
+
}
|
627 |
+
|
628 |
+
itsecUtil.sendAJAXRequest( module, method, {}, itsecSettingsPage.toggleModuleActivationCallback );
|
629 |
+
},
|
630 |
+
|
631 |
+
setModuleToActive: function( module ) {
|
632 |
+
var args = {
|
633 |
+
'module': module,
|
634 |
+
'method': 'activate',
|
635 |
+
'errors': []
|
636 |
+
};
|
637 |
+
|
638 |
+
itsecSettingsPage.toggleModuleActivationCallback( args );
|
639 |
+
},
|
640 |
+
|
641 |
+
setModuleToInactive: function( module ) {
|
642 |
+
var args = {
|
643 |
+
'module': module,
|
644 |
+
'method': 'deactivate',
|
645 |
+
'errors': []
|
646 |
+
};
|
647 |
+
|
648 |
+
itsecSettingsPage.toggleModuleActivationCallback( args );
|
649 |
+
},
|
650 |
+
|
651 |
+
toggleModuleActivationCallback: function( results ) {
|
652 |
+
var module = results.module;
|
653 |
+
var method = results.method;
|
654 |
+
|
655 |
+
var $card = jQuery( '#itsec-module-card-' + module ),
|
656 |
+
$buttons = $card.find( '.itsec-toggle-activation' )
|
657 |
+
|
658 |
+
if ( results.errors.length > 0 ) {
|
659 |
+
$buttons
|
660 |
+
.html( itsec_page.translations.error )
|
661 |
+
.addClass( 'button-secondary' )
|
662 |
+
.removeClass( 'button-primary' );
|
663 |
+
|
664 |
+
setTimeout( function() {
|
665 |
+
itsecSettingsPage.isModuleActive( module );
|
666 |
+
}, 1000 );
|
667 |
+
|
668 |
+
return;
|
669 |
+
}
|
670 |
+
|
671 |
+
if ( 'activate' === method ) {
|
672 |
+
$buttons
|
673 |
+
.html( itsec_page.translations.deactivate )
|
674 |
+
.addClass( 'button-secondary' )
|
675 |
+
.removeClass( 'button-primary' )
|
676 |
+
.prop( 'disabled', false );
|
677 |
+
|
678 |
+
$card
|
679 |
+
.addClass( 'itsec-module-type-enabled' )
|
680 |
+
.removeClass( 'itsec-module-type-disabled' );
|
681 |
+
|
682 |
+
var newToggleSettingsLabel = itsec_page.translations.show_settings;
|
683 |
+
} else {
|
684 |
+
$buttons
|
685 |
+
.html( itsec_page.translations.activate )
|
686 |
+
.addClass( 'button-primary' )
|
687 |
+
.removeClass( 'button-secondary' )
|
688 |
+
.prop( 'disabled', false );
|
689 |
+
|
690 |
+
$card
|
691 |
+
.addClass( 'itsec-module-type-disabled' )
|
692 |
+
.removeClass( 'itsec-module-type-enabled' );
|
693 |
+
|
694 |
+
var newToggleSettingsLabel = itsec_page.translations.show_description;
|
695 |
+
}
|
696 |
+
|
697 |
+
$card.find( '.itsec-toggle-settings' ).html( newToggleSettingsLabel );
|
698 |
+
|
699 |
+
var enabledCount = jQuery( '.itsec-module-type-enabled' ).length,
|
700 |
+
disabledCount = jQuery( '.itsec-module-type-disabled' ).length;
|
701 |
+
|
702 |
+
jQuery( '#itsec-module-filter-enabled .count' ).html( '(' + enabledCount + ')' );
|
703 |
+
jQuery( '#itsec-module-filter-disabled .count' ).html( '(' + disabledCount + ')' );
|
704 |
+
|
705 |
+
|
706 |
+
itsecSettingsPage.showErrors( results.warnings, results.module, 'closed', 'warning' );
|
707 |
+
itsecSettingsPage.showMessages( results.messages, results.module, 'closed' );
|
708 |
+
itsecSettingsPage.showMessages( results.infos, results.module, 'closed', 'info' );
|
709 |
+
},
|
710 |
+
|
711 |
+
isModuleActive: function( module ) {
|
712 |
+
var data = {
|
713 |
+
'module': module,
|
714 |
+
'method': 'is_active'
|
715 |
+
};
|
716 |
+
|
717 |
+
itsecUtil.sendAJAXRequest( module, 'is_active', {}, itsecSettingsPage.isModuleActiveCallback );
|
718 |
+
},
|
719 |
+
|
720 |
+
isModuleActiveCallback: function( results ) {
|
721 |
+
if ( true === results.response ) {
|
722 |
+
results.method = 'activate';
|
723 |
+
} else if ( false === results.response ) {
|
724 |
+
results.method = 'deactivate';
|
725 |
+
} else {
|
726 |
+
return;
|
727 |
+
}
|
728 |
+
|
729 |
+
itsecSettingsPage.toggleModuleActivationCallback( results );
|
730 |
+
},
|
731 |
+
|
732 |
+
reloadModule: function( module ) {
|
733 |
+
if ( module.preventDefault ) {
|
734 |
+
module.preventDefault();
|
735 |
+
|
736 |
+
module = jQuery(this).parents( '.itsec-module-card' ).attr( 'id' ).replace( 'itsec-module-card-', '' );
|
737 |
+
}
|
738 |
+
|
739 |
+
var method = 'get_refreshed_module_settings';
|
740 |
+
var data = {};
|
741 |
+
|
742 |
+
itsecUtil.sendAJAXRequest( module, method, data, function( results ) {
|
743 |
+
if ( results.success && results.response ) {
|
744 |
+
var $card = jQuery( '#itsec-module-card-' + module );
|
745 |
+
var isHidden = $card.is( ':hidden' );
|
746 |
+
|
747 |
+
jQuery( '.itsec-module-settings-content-main', $card ).html( results.response );
|
748 |
+
|
749 |
+
if ( isHidden ) {
|
750 |
+
$card.hide();
|
751 |
+
} else {
|
752 |
+
jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
|
753 |
+
}
|
754 |
+
} else if ( results.errors && results.errors.length > 0 ) {
|
755 |
+
itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
|
756 |
+
} else if ( results.warnings && results.warnings.length > 0 ) {
|
757 |
+
itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
|
758 |
+
}
|
759 |
+
|
760 |
+
itsecSettingsPage.events.trigger( 'moduleReloaded', module );
|
761 |
+
|
762 |
+
itsecSettingsPage.makeNoticesDismissible();
|
763 |
+
} );
|
764 |
+
},
|
765 |
+
|
766 |
+
reloadAllModules: function( _, initialResponse) {
|
767 |
+
itsecUtil.sendAJAXRequest( '#', 'get_refreshed_module_form', null, function ( response ) {
|
768 |
+
|
769 |
+
if ( ! response.success || response.errors.length ) {
|
770 |
+
return;
|
771 |
+
}
|
772 |
+
|
773 |
+
var $open;
|
774 |
+
|
775 |
+
if ( jQuery( 'body' ).hasClass( 'itsec-modal-open' ) ) {
|
776 |
+
var $newModules = jQuery( response.response ), $cardsList = jQuery( '.itsec-module-cards' );
|
777 |
+
$open = jQuery( '.itsec-module-settings-container:visible' ).parents( '.itsec-module-card' );
|
778 |
+
|
779 |
+
jQuery( '.itsec-module-card', $newModules ).each( function () {
|
780 |
+
var $new = jQuery( this ), $current = jQuery( '#' + $new.attr( 'id' ), $cardsList );
|
781 |
+
|
782 |
+
if ( $new.attr( 'id' ).length && $new.attr( 'id' ) === $open.attr( 'id' ) ) {
|
783 |
+
jQuery( '.itsec-module-settings-content-main', $current ).html( jQuery( '.itsec-module-settings-content-main', $new ).html() );
|
784 |
+
} else {
|
785 |
+
jQuery( '.itsec-module-settings-container', $new ).hide();
|
786 |
+
$current.replaceWith( $new );
|
787 |
+
}
|
788 |
+
} );
|
789 |
+
|
790 |
+
} else {
|
791 |
+
jQuery( '.itsec-module-cards-container' ).html( response.response );
|
792 |
+
}
|
793 |
+
|
794 |
+
itsecSettingsPage.initFilters();
|
795 |
+
|
796 |
+
if ( ! $open ) {
|
797 |
+
jQuery( '.itsec-module-settings-container' ).hide();
|
798 |
+
}
|
799 |
+
|
800 |
+
if ( initialResponse ) {
|
801 |
+
itsecSettingsPage.showMessages( initialResponse.messages, initialResponse.module, $open ? 'open' : 'closed' );
|
802 |
+
itsecSettingsPage.showMessages( initialResponse.infos, initialResponse.module, $open ? 'open' : 'closed', 'info' );
|
803 |
+
itsecSettingsPage.showErrors( initialResponse.errors, initialResponse.module, $open ? 'open' : 'closed' );
|
804 |
+
itsecSettingsPage.showErrors( initialResponse.warnings, initialResponse.module, $open ? 'open' : 'closed', 'warning' );
|
805 |
+
}
|
806 |
+
|
807 |
+
itsecSettingsPage.makeNoticesDismissible();
|
808 |
+
itsecSettingsPage.events.trigger( 'modulesReloaded', initialResponse );
|
809 |
+
} );
|
810 |
+
},
|
811 |
+
|
812 |
+
reloadWidget: function( widget ) {
|
813 |
+
var method = 'get_refreshed_widget_settings';
|
814 |
+
var data = {};
|
815 |
+
|
816 |
+
itsecUtil.sendAJAXRequest( module, method, data, function( results ) {
|
817 |
+
if ( results.success && results.response ) {
|
818 |
+
jQuery( '#itsec-sidebar-widget-' + module + ' .inside' ).html( results.response );
|
819 |
+
} else {
|
820 |
+
itsecSettingsPage.showErrors( results.errors, results.module, 'closed' );
|
821 |
+
itsecSettingsPage.showErrors( results.warnings, results.module, 'closed', 'warning' );
|
822 |
+
}
|
823 |
+
} );
|
824 |
+
},
|
825 |
+
|
826 |
+
// Make notices dismissible
|
827 |
+
makeNoticesDismissible: function(){
|
828 |
+
jQuery( '.notice.itsec-is-dismissible' ).each( function() {
|
829 |
+
var $el = jQuery( this ),
|
830 |
+
$button = jQuery( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
|
831 |
+
btnText = itsec_page.translations.dismiss || '';
|
832 |
+
|
833 |
+
// Don't rebind twice
|
834 |
+
if ( jQuery( '.notice-dismiss', $el ).length ) {
|
835 |
+
return;
|
836 |
+
}
|
837 |
+
|
838 |
+
// Ensure plain text
|
839 |
+
$button.find( '.screen-reader-text' ).text( btnText );
|
840 |
+
$button.on( 'click.wp-dismiss-notice', function( event ) {
|
841 |
+
event.preventDefault();
|
842 |
+
|
843 |
+
$el.trigger( 'itsec-dismiss-notice' );
|
844 |
+
|
845 |
+
$el.fadeTo( 100, 0, function() {
|
846 |
+
$el.slideUp( 100, function() {
|
847 |
+
$el.remove();
|
848 |
+
});
|
849 |
+
});
|
850 |
+
});
|
851 |
+
|
852 |
+
$el.append( $button );
|
853 |
+
});
|
854 |
+
}
|
855 |
+
};
|
856 |
+
|
857 |
+
jQuery(document).ready(function( $ ) {
|
858 |
+
itsecSettingsPage.init();
|
859 |
+
|
860 |
+
if ( itsec_page.show_security_check ) {
|
861 |
+
jQuery( '.itsec-settings-view-toggle a.itsec-grid' ).trigger( 'click' );
|
862 |
+
jQuery( '#itsec-module-card-security-check .itsec-toggle-settings' ).trigger( 'click' );
|
863 |
+
}
|
864 |
+
|
865 |
+
|
866 |
+
jQuery( '.dialog' ).click( function ( event ) {
|
867 |
+
event.preventDefault();
|
868 |
+
|
869 |
+
var target = jQuery( this ).attr( 'href' );
|
870 |
+
var title = jQuery( this ).parents( '.inside' ).siblings( 'h3.hndle' ).children( 'span' ).text();
|
871 |
+
|
872 |
+
jQuery( '#' + target ).dialog( {
|
873 |
+
dialogClass : 'wp-dialog itsec-dialog itsec-dialog-logs',
|
874 |
+
modal : true,
|
875 |
+
closeOnEscape: true,
|
876 |
+
title : title,
|
877 |
+
height : ( jQuery( window ).height() * 0.8 ),
|
878 |
+
width : ( jQuery( window ).width() * 0.8 ),
|
879 |
+
open : function ( event, ui ) {
|
880 |
+
jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
|
881 |
+
jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
|
882 |
+
} );
|
883 |
+
}
|
884 |
+
} );
|
885 |
+
|
886 |
+
jQuery( '.ui-dialog :button' ).blur();
|
887 |
+
} );
|
888 |
+
|
889 |
+
var regex = /[^\w]/ig;
|
890 |
+
|
891 |
+
var $search = $( '#search' ), $cardsContainer = $( '.itsec-module-cards' ),
|
892 |
+
$cards = $( '.itsec-module-card', $cardsContainer ),
|
893 |
+
$searchFilter = $( '#itsec-module-filter-search' ),
|
894 |
+
$currentFilter = $( '.itsec-feature-tabs .current' ).parent();
|
895 |
+
|
896 |
+
itsecSettingsPage.events.on( 'modulesReloaded', function() {
|
897 |
+
$cardsContainer = $( '.itsec-module-cards' );
|
898 |
+
$cards = $( '.itsec-module-card', $cardsContainer );
|
899 |
+
} );
|
900 |
+
|
901 |
+
$search.on( 'input', _.debounce( function () {
|
902 |
+
var query = $search.val().trim().replace( regex, ' ' );
|
903 |
+
|
904 |
+
var $maybeCurrent = $( '.itsec-feature-tabs .current' ).parent();
|
905 |
+
|
906 |
+
if ( $maybeCurrent && $maybeCurrent.prop( 'id' ) !== 'itsec-module-filter-search' ) {
|
907 |
+
$currentFilter = $maybeCurrent;
|
908 |
+
}
|
909 |
+
|
910 |
+
$( '.itsec-highlighted-setting', $cards ).removeClass( 'itsec-highlighted-setting' );
|
911 |
+
|
912 |
+
if ( !query.length ) {
|
913 |
+
$searchFilter.addClass( 'hide-if-js' );
|
914 |
+
$( 'a', $searchFilter ).removeClass( 'current' );
|
915 |
+
$( 'a', $currentFilter ).addClass( 'current' );
|
916 |
+
|
917 |
+
var type = $currentFilter.prop( 'id' ).substr( 20 );
|
918 |
+
|
919 |
+
if ( 'all' === type ) {
|
920 |
+
$cards.show();
|
921 |
+
} else {
|
922 |
+
$( '.itsec-module-type-' + type ).show();
|
923 |
+
$( '.itsec-module-card' ).not( '.itsec-module-type-' + type ).hide();
|
924 |
+
}
|
925 |
+
|
926 |
+
return;
|
927 |
+
}
|
928 |
+
|
929 |
+
var $titleMatches = $( ".itsec-module-card-content > h2:itsecContains('" + query + "')", $cards ),
|
930 |
+
$titleMatchesCards = $titleMatches.parents( '.itsec-module-card' );
|
931 |
+
|
932 |
+
var $descriptionMatches = $( ".itsec-module-card-content > p:itsecContains('" + query + "')", $cards ),
|
933 |
+
$descriptionMatchesCards = $descriptionMatches.parents( '.itsec-module-card' );
|
934 |
+
|
935 |
+
var $settingMatches = $( ".itsec-module-settings-container .form-table tr > th > label:itsecContains('" + query + "')", $cards ),
|
936 |
+
$settingMatchesCards = $settingMatches.parents( '.itsec-module-card' );
|
937 |
+
|
938 |
+
|
939 |
+
var $matches = $titleMatchesCards.add( $descriptionMatchesCards ).add( $settingMatchesCards );
|
940 |
+
|
941 |
+
$searchFilter.removeClass( 'hide-if-js' );
|
942 |
+
$( 'a', $currentFilter ).removeClass( 'current' );
|
943 |
+
$( 'a', $searchFilter ).addClass( 'current' );
|
944 |
+
$( '.count', $searchFilter ).text( '(' + $matches.length + ')' );
|
945 |
+
|
946 |
+
$cards.hide();
|
947 |
+
$matches.show();
|
948 |
+
|
949 |
+
$settingMatches.parents( 'tr' ).addClass( 'itsec-highlighted-setting' );
|
950 |
+
|
951 |
+
if ( $matches.length === 1 ) {
|
952 |
+
$( '.itsec-toggle-settings', $matches.first() ).click();
|
953 |
+
}
|
954 |
+
}, 250 ) );
|
955 |
+
|
956 |
+
$.expr[":"].itsecContains = $.expr.createPseudo( function ( arg ) {
|
957 |
+
return function ( elem ) {
|
958 |
+
var candidate = $( elem ).text().toUpperCase().replace( regex, ' ' ), term = arg.toUpperCase();
|
959 |
+
var index = candidate.indexOf( term );
|
960 |
+
|
961 |
+
if ( index === -1 ) {
|
962 |
+
return false;
|
963 |
+
}
|
964 |
+
|
965 |
+
if ( index === 0 ) {
|
966 |
+
return true;
|
967 |
+
}
|
968 |
+
|
969 |
+
var prior = candidate.charAt( index - 1 ), next = candidate.charAt( term.length + index );
|
970 |
+
|
971 |
+
// full word
|
972 |
+
return prior === ' ' && ( next === ' ' || next === '' );
|
973 |
+
};
|
974 |
+
} );
|
975 |
+
});
|
core/admin-pages/js/util.js
ADDED
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use strict";
|
2 |
+
|
3 |
+
var itsecUtil = {
|
4 |
+
|
5 |
+
focus: function( $el, $fallback ) {
|
6 |
+
if ( itsecUtil.isElementVisible( $el ) && jQuery( window ).height() > 800 ) {
|
7 |
+
$el.focus();
|
8 |
+
} else {
|
9 |
+
$fallback.prop( 'tabindex', -1 ).focus();
|
10 |
+
}
|
11 |
+
},
|
12 |
+
|
13 |
+
isElementVisible: function( $el ) {
|
14 |
+
|
15 |
+
var $window = jQuery( window ), height = $window.height(), width = $window.width(), offset = $el.offset();
|
16 |
+
|
17 |
+
if ( ! $el || ! offset ) {
|
18 |
+
return false;
|
19 |
+
}
|
20 |
+
|
21 |
+
return offset.top < height && offset.left < width;
|
22 |
+
},
|
23 |
+
|
24 |
+
sendModuleAJAXRequest: function( module, data, callback ) {
|
25 |
+
itsecUtil.sendAJAXRequest( module, 'handle_module_request', data, callback );
|
26 |
+
},
|
27 |
+
|
28 |
+
sendWidgetAJAXRequest: function( widget, data, callback ) {
|
29 |
+
itsecUtil.sendAJAXRequest( widget, 'handle_widget_request', data, callback );
|
30 |
+
},
|
31 |
+
|
32 |
+
sendAJAXRequest: function( module, method, data, callback, action, nonce ) {
|
33 |
+
var postData = {
|
34 |
+
'action': itsec_util.ajax_action,
|
35 |
+
'nonce': itsec_util.ajax_nonce,
|
36 |
+
'module': module,
|
37 |
+
'method': method,
|
38 |
+
'data': data,
|
39 |
+
};
|
40 |
+
|
41 |
+
if ( 'undefined' !== typeof action ) {
|
42 |
+
postData.action = action;
|
43 |
+
}
|
44 |
+
|
45 |
+
if ( 'undefined' !== typeof nonce ) {
|
46 |
+
postData.nonce = nonce;
|
47 |
+
}
|
48 |
+
|
49 |
+
jQuery.post( ajaxurl, postData )
|
50 |
+
.always(function( a, status, b ) {
|
51 |
+
itsecUtil.processAjaxResponse( a, status, b, module, method, data, callback );
|
52 |
+
});
|
53 |
+
},
|
54 |
+
|
55 |
+
processAjaxResponse: function( a, status, b, module, method, data, callback ) {
|
56 |
+
var results = {
|
57 |
+
'module': module,
|
58 |
+
'method': method,
|
59 |
+
'data': data,
|
60 |
+
'status': status,
|
61 |
+
'jqxhr': null,
|
62 |
+
'success': false,
|
63 |
+
'response': null,
|
64 |
+
'errors': [],
|
65 |
+
'warnings': [],
|
66 |
+
'messages': [],
|
67 |
+
'infos': [],
|
68 |
+
'functionCalls': [],
|
69 |
+
'redirect': false,
|
70 |
+
'closeModal': true
|
71 |
+
};
|
72 |
+
|
73 |
+
|
74 |
+
if ( 'ITSEC_Response' === a.source && 'undefined' !== a.response ) {
|
75 |
+
// Successful response with a valid format.
|
76 |
+
results.jqxhr = b;
|
77 |
+
results.success = a.success;
|
78 |
+
results.response = a.response;
|
79 |
+
results.errors = a.errors;
|
80 |
+
results.warnings = a.warnings;
|
81 |
+
results.messages = a.messages;
|
82 |
+
results.infos = a.infos;
|
83 |
+
results.functionCalls = a.functionCalls;
|
84 |
+
results.redirect = a.redirect;
|
85 |
+
results.closeModal = a.closeModal;
|
86 |
+
} else if ( a.responseText ) {
|
87 |
+
// Failed response.
|
88 |
+
results.jqxhr = a;
|
89 |
+
var errorThrown = b;
|
90 |
+
|
91 |
+
if ( 'undefined' === typeof results.jqxhr.status ) {
|
92 |
+
results.jqxhr.status = -1;
|
93 |
+
}
|
94 |
+
|
95 |
+
var error = '';
|
96 |
+
|
97 |
+
if ( 'timeout' === status ) {
|
98 |
+
error = itsec_util.translations.ajax_timeout;
|
99 |
+
} else if ( 'parsererror' === status ) {
|
100 |
+
error = itsec_util.translations.ajax_parsererror;
|
101 |
+
} else if ( 403 == results.jqxhr.status ) {
|
102 |
+
error = itsec_util.translations.ajax_forbidden;
|
103 |
+
} else if ( 404 == results.jqxhr.status ) {
|
104 |
+
error = itsec_util.translations.ajax_not_found;
|
105 |
+
} else if ( 500 == results.jqxhr.status ) {
|
106 |
+
error = itsec_util.translations.ajax_server_error;
|
107 |
+
} else {
|
108 |
+
error = itsec_util.translations.ajax_unknown;
|
109 |
+
}
|
110 |
+
|
111 |
+
error = error.replace( '%1$s', status );
|
112 |
+
error = error.replace( '%2$s', errorThrown );
|
113 |
+
|
114 |
+
results.errors = [ error ];
|
115 |
+
} else {
|
116 |
+
// Successful response with an invalid format.
|
117 |
+
results.jqxhr = b;
|
118 |
+
|
119 |
+
results.response = a;
|
120 |
+
results.errors = [ itsec_util.translations.ajax_invalid ];
|
121 |
+
}
|
122 |
+
|
123 |
+
|
124 |
+
if ( results.redirect ) {
|
125 |
+
window.location = results.redirect;
|
126 |
+
}
|
127 |
+
|
128 |
+
|
129 |
+
if ( 'function' === typeof callback ) {
|
130 |
+
callback( results );
|
131 |
+
} else if ( 'function' === typeof console.log ) {
|
132 |
+
console.log( 'ERROR: Unable to handle settings AJAX request due to an invalid callback:', callback, {'data': postData, 'results': results} );
|
133 |
+
}
|
134 |
+
|
135 |
+
|
136 |
+
if ( results.functionCalls ) {
|
137 |
+
for ( var i = 0; i < results.functionCalls.length; i++ ) {
|
138 |
+
if ( 'object' === typeof itsecSettingsPage && 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof itsecSettingsPage[results.functionCalls[i][0]] ) {
|
139 |
+
itsecSettingsPage[results.functionCalls[i][0]]( results.functionCalls[i][1], results );
|
140 |
+
} else if ( 'string' === typeof results.functionCalls[i] && 'function' === typeof window[results.functionCalls[i]] ) {
|
141 |
+
window[results.functionCalls[i]]();
|
142 |
+
} else if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof window[results.functionCalls[i][0]] ) {
|
143 |
+
window[results.functionCalls[i][0]]( results.functionCalls[i][1] );
|
144 |
+
} else if ( 'function' === typeof console.log ) {
|
145 |
+
console.log( 'ERROR: Unable to call missing function:', results.functionCalls[i] );
|
146 |
+
}
|
147 |
+
}
|
148 |
+
}
|
149 |
+
},
|
150 |
+
|
151 |
+
getUrlParameter: function( name ) {
|
152 |
+
var pageURL = decodeURIComponent( window.location.search.substring( 1 ) ),
|
153 |
+
URLParameters = pageURL.split( '&' ),
|
154 |
+
parameterName,
|
155 |
+
i;
|
156 |
+
|
157 |
+
// Loop through all parameters
|
158 |
+
for ( i = 0; i < URLParameters.length; i++ ) {
|
159 |
+
parameterName = URLParameters[i].split( '=' );
|
160 |
+
|
161 |
+
// If this is the parameter we're looking for
|
162 |
+
if ( parameterName[0] === name ) {
|
163 |
+
// Return the value or true if there is no value
|
164 |
+
return parameterName[1] === undefined ? true : parameterName[1];
|
165 |
+
}
|
166 |
+
}
|
167 |
+
// If the requested parameter doesn't exist, return false
|
168 |
+
return false;
|
169 |
+
}
|
170 |
+
|
171 |
+
};
|
core/admin-pages/logs-list-table.php
ADDED
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* List table for logs
|
5 |
+
*
|
6 |
+
* @package iThemes-Security
|
7 |
+
* @since 3.9
|
8 |
+
*/
|
9 |
+
final class ITSEC_Logs_List_Table extends ITSEC_WP_List_Table {
|
10 |
+
private $user_cache = array();
|
11 |
+
private $raw_filters = array();
|
12 |
+
private $current_filters = array();
|
13 |
+
private $types = array();
|
14 |
+
|
15 |
+
|
16 |
+
public function __construct() {
|
17 |
+
parent::__construct(
|
18 |
+
array(
|
19 |
+
'singular' => 'itsec-log-entry',
|
20 |
+
'plural' => 'itsec-log-entries',
|
21 |
+
'ajax' => true
|
22 |
+
)
|
23 |
+
);
|
24 |
+
|
25 |
+
$this->types = ITSEC_Log::get_types_for_display();
|
26 |
+
}
|
27 |
+
|
28 |
+
public function column_default( $entry, $field ) {
|
29 |
+
return esc_html( $entry[$field] );
|
30 |
+
}
|
31 |
+
|
32 |
+
public function column_description( $entry ) {
|
33 |
+
return $entry['description'];
|
34 |
+
}
|
35 |
+
|
36 |
+
public function column_type( $entry ) {
|
37 |
+
return $this->types[$entry['type']];
|
38 |
+
}
|
39 |
+
|
40 |
+
public function column_timestamp( $entry ) {
|
41 |
+
$timestamp = strtotime( $entry['timestamp'] );
|
42 |
+
$datetime = date( 'Y-m-d H:i:s', $timestamp + ITSEC_Core::get_time_offset() );
|
43 |
+
/* translators: 1: date and time, 2: time difference */
|
44 |
+
return sprintf( __( '%1$s - %2$s ago', 'better-wp-security' ), $datetime, human_time_diff( $timestamp, time() ) );
|
45 |
+
}
|
46 |
+
|
47 |
+
public function column_remote_ip( $entry ) {
|
48 |
+
if ( empty( $entry['remote_ip'] ) ) {
|
49 |
+
return '';
|
50 |
+
}
|
51 |
+
|
52 |
+
return '<code><a href="' . esc_url( ITSEC_Lib::get_trace_ip_link( $entry['remote_ip'] ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $entry['remote_ip'] ) . '</a></code>';
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Define username column
|
57 |
+
*
|
58 |
+
* @param array $item array of row data
|
59 |
+
*
|
60 |
+
* @return string formatted output
|
61 |
+
*
|
62 |
+
**/
|
63 |
+
public function column_user_id( $item ) {
|
64 |
+
if ( $item['user_id'] > 0 ) {
|
65 |
+
if ( ! isset( $this->user_cache[$item['user_id']] ) ) {
|
66 |
+
$this->user_cache[$item['user_id']] = get_userdata( $item['user_id'] );
|
67 |
+
}
|
68 |
+
|
69 |
+
if ( false === $this->user_cache[$item['user_id']] ) {
|
70 |
+
return '';
|
71 |
+
}
|
72 |
+
|
73 |
+
return '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $item['user_id'] ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $this->user_cache[$item['user_id']]->user_login ) . '</a>';
|
74 |
+
}
|
75 |
+
|
76 |
+
return '';
|
77 |
+
}
|
78 |
+
|
79 |
+
public function column_details( $entry ) {
|
80 |
+
echo '<a class="itsec-logs-view-details" href="' . self::get_self_link( array( 'id' => $entry['id'] ), array() ) . '">' . esc_html__( 'View Details', 'better-wp-security' ) . '</a>';
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Generates and display row actions links for the list table.
|
85 |
+
*
|
86 |
+
* @since 4.3.0
|
87 |
+
*
|
88 |
+
* @param object $item The item being acted upon.
|
89 |
+
* @param string $column_name Current column name.
|
90 |
+
* @param string $primary Primary column name.
|
91 |
+
* @return string The row actions HTML, or an empty string if the current column is the primary column.
|
92 |
+
*/
|
93 |
+
protected function handle_row_actions( $item, $column_name, $primary ) {
|
94 |
+
$columns = $this->get_columns();
|
95 |
+
$column_header = $columns[$column_name];
|
96 |
+
|
97 |
+
if ( 'module_display' === $column_name ) {
|
98 |
+
$column_name = 'module';
|
99 |
+
} else if ( 'description' === $column_name ) {
|
100 |
+
$column_name = 'code';
|
101 |
+
}
|
102 |
+
|
103 |
+
if ( 'details' === $column_name || 'id' === $column_name ) {
|
104 |
+
return;
|
105 |
+
} else if ( 'timestamp' === $column_name ) {
|
106 |
+
list( $date, $time ) = explode( ' ', $item[$column_name] );
|
107 |
+
$url = self::get_self_link( array( 'filters' => "$column_name|$date%" ) );
|
108 |
+
return ' <a class="dashicons dashicons-filter" href="' . esc_url( $url ) . '" title="' . esc_attr__( 'Show only entries for this day', 'better-wp-security' ) . '"> </a>';
|
109 |
+
} else if ( empty( $item[$column_name] ) ) {
|
110 |
+
return;
|
111 |
+
}
|
112 |
+
|
113 |
+
if ( 'four_oh_four' === $item['module'] && 'code' === $column_name ) {
|
114 |
+
$url = self::get_self_link( array( 'filters[10]' => "url|{$item['url']}", 'filters[11]' => 'module|four_oh_four' ) );
|
115 |
+
} else {
|
116 |
+
$url = self::get_self_link( array( 'filters' => "$column_name|{$item[$column_name]}" ) );
|
117 |
+
}
|
118 |
+
|
119 |
+
$out = ' <a class="dashicons dashicons-filter" href="' . esc_url( $url ) . '" title="' . sprintf( esc_attr__( 'Show only entries for this %s', 'better-wp-security' ), strtolower( $column_header ) ) . '"> </a>';
|
120 |
+
|
121 |
+
if ( 'module' === $column_name ) {
|
122 |
+
$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
|
123 |
+
}
|
124 |
+
|
125 |
+
return $out;
|
126 |
+
}
|
127 |
+
|
128 |
+
private function get_self_link( $vars = array(), $current_vars = false ) {
|
129 |
+
if ( ! is_array( $current_vars ) ) {
|
130 |
+
$current_vars = $_GET;
|
131 |
+
unset( $current_vars['id'] );
|
132 |
+
unset( $current_vars['paged'] );
|
133 |
+
}
|
134 |
+
|
135 |
+
$vars = array_merge_recursive( $current_vars, $vars );
|
136 |
+
|
137 |
+
if ( ! isset( $vars['page'] ) ) {
|
138 |
+
$vars = array_merge( array( 'page' => $_GET['page'] ), $vars );
|
139 |
+
}
|
140 |
+
|
141 |
+
return network_admin_url( 'admin.php?' . http_build_query( $vars, null, '&' ) );
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Define Columns
|
146 |
+
*
|
147 |
+
* @return array array of column titles
|
148 |
+
*/
|
149 |
+
public function get_columns() {
|
150 |
+
return array(
|
151 |
+
'id' => esc_html__( 'ID', 'better-wp-security' ),
|
152 |
+
'module_display' => esc_html__( 'Module', 'better-wp-security' ),
|
153 |
+
'type' => esc_html__( 'Type', 'better-wp-security' ),
|
154 |
+
'description' => esc_html__( 'Description', 'better-wp-security' ),
|
155 |
+
'timestamp' => esc_html__( 'Time', 'better-wp-security' ),
|
156 |
+
'remote_ip' => esc_html__( 'Host', 'better-wp-security' ),
|
157 |
+
'user_id' => esc_html__( 'User', 'better-wp-security' ),
|
158 |
+
'details' => esc_html__( 'Details', 'better-wp-security' ),
|
159 |
+
);
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
*
|
164 |
+
* @return array
|
165 |
+
*/
|
166 |
+
protected function get_sortable_columns() {
|
167 |
+
return array(
|
168 |
+
'timestamp' => array( 'timestamp', false ),
|
169 |
+
'remote_ip' => array( 'remote_ip', false ),
|
170 |
+
);
|
171 |
+
}
|
172 |
+
|
173 |
+
private function get_raw_filters() {
|
174 |
+
if ( ! empty( $this->raw_filters ) ) {
|
175 |
+
return $this->raw_filters;
|
176 |
+
}
|
177 |
+
|
178 |
+
if ( empty( $_GET['filters'] ) ) {
|
179 |
+
$raw_filters = array();
|
180 |
+
} else {
|
181 |
+
$raw_filters = $_GET['filters'];
|
182 |
+
}
|
183 |
+
|
184 |
+
$filters = array();
|
185 |
+
|
186 |
+
foreach ( (array) $raw_filters as $var ) {
|
187 |
+
list( $field, $value ) = explode( '|', $var, 2 );
|
188 |
+
|
189 |
+
$filters[$field] = $value;
|
190 |
+
}
|
191 |
+
|
192 |
+
if ( ! isset( $filters['type'] ) ) {
|
193 |
+
if ( ! isset( $filters['type_not'] ) ) {
|
194 |
+
$options = ITSEC_Log_Util::get_logs_page_screen_options();
|
195 |
+
|
196 |
+
$filters['type'] = $options['default_view'];
|
197 |
+
} else {
|
198 |
+
$filters['type'] = 'all';
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
$this->raw_filters = $filters;
|
203 |
+
|
204 |
+
return $this->raw_filters;
|
205 |
+
}
|
206 |
+
|
207 |
+
private function get_current_view() {
|
208 |
+
$raw_filters = $this->get_raw_filters();
|
209 |
+
|
210 |
+
return $raw_filters['type'];
|
211 |
+
}
|
212 |
+
|
213 |
+
private function get_current_filters( $options ) {
|
214 |
+
if ( ! empty( $this->current_filters ) ) {
|
215 |
+
return $this->current_filters;
|
216 |
+
}
|
217 |
+
|
218 |
+
$filters = $this->get_raw_filters();
|
219 |
+
|
220 |
+
if ( 'process' === $filters['type'] ) {
|
221 |
+
$filters['type'] = 'process-start';
|
222 |
+
}
|
223 |
+
|
224 |
+
if ( 'all' === $filters['type'] ) {
|
225 |
+
$type_not = array();
|
226 |
+
|
227 |
+
if ( ! $options['show_debug'] ) {
|
228 |
+
$type_not[] = 'debug';
|
229 |
+
}
|
230 |
+
if ( ! $options['show_process'] ) {
|
231 |
+
$type_not[] = 'process-start';
|
232 |
+
}
|
233 |
+
|
234 |
+
unset( $filters['type'] );
|
235 |
+
} else if ( 'important' === $filters['type'] ) {
|
236 |
+
$type_not = array( 'action', 'notice', 'debug', 'process-start' );
|
237 |
+
|
238 |
+
unset( $filters['type'] );
|
239 |
+
}
|
240 |
+
|
241 |
+
if ( ! empty( $type_not ) ) {
|
242 |
+
if ( isset( $filters['type_not'] ) && is_array( $filters['type_not'] ) ) {
|
243 |
+
$filters['type_not'] = array_merge( $filters['type_not'], $type_not );
|
244 |
+
$filters['type_not'] = array_unique( $filters['type_not'] );
|
245 |
+
} else {
|
246 |
+
$filters['type_not'] = $type_not;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
$this->current_filters = $filters;
|
251 |
+
|
252 |
+
return $this->current_filters;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Prepare data for table
|
257 |
+
*
|
258 |
+
* @return void
|
259 |
+
*/
|
260 |
+
public function prepare_items() {
|
261 |
+
global $wpdb;
|
262 |
+
|
263 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
264 |
+
$options = ITSEC_Log_Util::get_logs_page_screen_options();
|
265 |
+
|
266 |
+
$filters = $this->get_current_filters( $options );
|
267 |
+
$columns = $this->get_columns();
|
268 |
+
$hidden_fields = array( 'id' );
|
269 |
+
$sortable_columns = $this->get_sortable_columns();
|
270 |
+
|
271 |
+
if ( isset( $_GET['orderby'], $_GET['order'] ) ) {
|
272 |
+
$sort_by_column = $_GET['orderby'];
|
273 |
+
$sort_direction = $_GET['order'];
|
274 |
+
} else {
|
275 |
+
$sort_by_column = 'timestamp';
|
276 |
+
$sort_direction = 'DESC';
|
277 |
+
}
|
278 |
+
|
279 |
+
if ( $options['last_seen'] > 0 ) {
|
280 |
+
$filters['__min_timestamp'] = $options['last_seen'];
|
281 |
+
}
|
282 |
+
|
283 |
+
$total_items = ITSEC_Log::get_number_of_entries( $filters );
|
284 |
+
$items = ITSEC_Log::get_entries( $filters, $options['per_page'], $this->get_pagenum(), $sort_by_column, $sort_direction );
|
285 |
+
|
286 |
+
ITSEC_Modules::load_module_file( 'logs.php' );
|
287 |
+
|
288 |
+
foreach ( $items as $item ) {
|
289 |
+
if ( false === strpos( $item['code'], '::' ) ) {
|
290 |
+
$code = $item['code'];
|
291 |
+
$data = array();
|
292 |
+
} else {
|
293 |
+
list( $code, $data ) = explode( '::', $item['code'], 2 );
|
294 |
+
$data = explode( ',', $data );
|
295 |
+
}
|
296 |
+
|
297 |
+
$item['description'] = $item['code'];
|
298 |
+
$item['module_display'] = $item['module'];
|
299 |
+
$item = apply_filters( "itsec_logs_prepare_{$item['module']}_entry_for_list_display", $item, $code, $data );
|
300 |
+
|
301 |
+
$this->items[$item['id']] = $item;
|
302 |
+
}
|
303 |
+
|
304 |
+
$this->_column_headers = array( $columns, $hidden_fields, $sortable_columns, 'module_display' );
|
305 |
+
|
306 |
+
$this->set_pagination_args(
|
307 |
+
array(
|
308 |
+
'total_items' => $total_items,
|
309 |
+
'per_page' => $options['per_page'],
|
310 |
+
)
|
311 |
+
);
|
312 |
+
}
|
313 |
+
|
314 |
+
protected function get_views() {
|
315 |
+
$options = ITSEC_Log_Util::get_logs_page_screen_options();
|
316 |
+
$counts = ITSEC_Log::get_type_counts( $options['last_seen'] );
|
317 |
+
|
318 |
+
$views = array(
|
319 |
+
'important' => esc_html__( 'Important Events (%s)', 'better-wp-security' ),
|
320 |
+
'all' => esc_html__( 'All Events (%s)', 'better-wp-security' ),
|
321 |
+
'critical-issue' => esc_html__( 'Critical Issues (%s)', 'better-wp-security' ),
|
322 |
+
'fatal-error' => esc_html__( 'Fatal Errors (%s)', 'better-wp-security' ),
|
323 |
+
'error' => esc_html__( 'Errors (%s)', 'better-wp-security' ),
|
324 |
+
'warning' => esc_html__( 'Warnings (%s)', 'better-wp-security' ),
|
325 |
+
'action' => esc_html__( 'Actions (%s)', 'better-wp-security' ),
|
326 |
+
'notice' => esc_html__( 'Notices (%s)', 'better-wp-security' ),
|
327 |
+
'debug' => esc_html__( 'Debug (%s)', 'better-wp-security' ),
|
328 |
+
'process' => esc_html__( 'Process (%s)', 'better-wp-security' ),
|
329 |
+
);
|
330 |
+
|
331 |
+
if ( ! $options['show_debug'] ) {
|
332 |
+
unset( $views['debug'] );
|
333 |
+
}
|
334 |
+
if ( ! $options['show_process'] ) {
|
335 |
+
unset( $views['process'] );
|
336 |
+
}
|
337 |
+
|
338 |
+
$important_count = 0;
|
339 |
+
$all_count = 0;
|
340 |
+
|
341 |
+
foreach ( $views as $type => $description ) {
|
342 |
+
if ( in_array( $type, array( 'important', 'all' ) ) ) {
|
343 |
+
continue;
|
344 |
+
}
|
345 |
+
|
346 |
+
if ( empty( $counts[$type] ) ) {
|
347 |
+
unset( $views[$type] );
|
348 |
+
continue;
|
349 |
+
}
|
350 |
+
|
351 |
+
$views[$type] = sprintf( $description, $counts[$type] );
|
352 |
+
|
353 |
+
if ( in_array( $type, array( 'critical-issue', 'fatal-error', 'error', 'warning' ) ) ) {
|
354 |
+
$important_count += $counts[$type];
|
355 |
+
}
|
356 |
+
|
357 |
+
$all_count += $counts[$type];
|
358 |
+
}
|
359 |
+
|
360 |
+
$views['important'] = sprintf( $views['important'], $important_count );
|
361 |
+
$views['all'] = sprintf( $views['all'], $all_count );
|
362 |
+
$formatted_views = array();
|
363 |
+
$current = $this->get_current_view();
|
364 |
+
|
365 |
+
foreach ( $views as $type => $description ) {
|
366 |
+
$url = self::get_self_link( array( 'filters' => "type|$type" ), array() );
|
367 |
+
|
368 |
+
if ( $current === $type ) {
|
369 |
+
$description = '<a href="' . esc_url( $url ) . '" class="current" aria-current="page">' . $description . '</a>';
|
370 |
+
} else {
|
371 |
+
$description = '<a href="' . esc_url( $url ) . '">' . $description . '</a>';
|
372 |
+
}
|
373 |
+
|
374 |
+
$formatted_views["itsec-$type"] = $description;
|
375 |
+
}
|
376 |
+
|
377 |
+
return $formatted_views;
|
378 |
+
}
|
379 |
+
|
380 |
+
public function single_row( $item ) {
|
381 |
+
echo "<tr class='itsec-log-type-{$item['type']}'>";
|
382 |
+
$this->single_row_columns( $item );
|
383 |
+
echo '</tr>';
|
384 |
+
}
|
385 |
+
|
386 |
+
protected function get_table_classes() {
|
387 |
+
$options = ITSEC_Log_Util::get_logs_page_screen_options();
|
388 |
+
|
389 |
+
$classes = array(
|
390 |
+
'widefat',
|
391 |
+
'striped',
|
392 |
+
$this->_args['plural']
|
393 |
+
);
|
394 |
+
|
395 |
+
if ( $options['color'] ) {
|
396 |
+
$classes[] = 'itsec-logs-color';
|
397 |
+
}
|
398 |
+
|
399 |
+
return $classes;
|
400 |
+
}
|
401 |
+
|
402 |
+
protected function extra_tablenav( $which ) {
|
403 |
+
echo '<div class="alignleft actions">';
|
404 |
+
|
405 |
+
if ( 'top' === $which ) {
|
406 |
+
/*
|
407 |
+
ob_start();
|
408 |
+
|
409 |
+
$output = ob_get_clean();
|
410 |
+
|
411 |
+
if ( ! empty( $output ) ) {
|
412 |
+
echo $output;
|
413 |
+
submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
|
414 |
+
}*/
|
415 |
+
}
|
416 |
+
|
417 |
+
/* if ( $this->is_trash && current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_others_posts ) && $this->has_items() ) {
|
418 |
+
submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false );
|
419 |
+
}*/
|
420 |
+
|
421 |
+
echo '</div>';
|
422 |
+
}
|
423 |
+
|
424 |
+
public function no_items() {
|
425 |
+
esc_html_e( 'No events.', 'better-wp-security' );
|
426 |
+
}
|
427 |
+
}
|
core/admin-pages/page-logs.php
CHANGED
@@ -2,14 +2,12 @@
|
|
2 |
|
3 |
|
4 |
final class ITSEC_Logs_Page {
|
5 |
-
private $version = 1.
|
6 |
|
7 |
private $self_url = '';
|
8 |
private $modules = array();
|
9 |
private $widgets = array();
|
10 |
private $translations = array();
|
11 |
-
private $logger_modules = array();
|
12 |
-
private $logger_displays = array();
|
13 |
|
14 |
|
15 |
public function __construct() {
|
@@ -20,11 +18,9 @@ final class ITSEC_Logs_Page {
|
|
20 |
add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
|
21 |
add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
|
22 |
|
23 |
-
$this
|
24 |
-
|
25 |
|
26 |
-
$this->
|
27 |
-
$this->logger_displays = apply_filters( 'itsec_logger_displays', array() );
|
28 |
|
29 |
|
30 |
require( dirname( __FILE__ ) . '/module-settings.php' );
|
@@ -52,14 +48,14 @@ final class ITSEC_Logs_Page {
|
|
52 |
}
|
53 |
|
54 |
$vars = array(
|
55 |
-
'ajax_action' => '
|
56 |
-
'ajax_nonce' => wp_create_nonce( 'itsec-
|
57 |
'logs_page_url' => ITSEC_Core::get_logs_page_url(),
|
58 |
'translations' => $this->translations,
|
59 |
);
|
60 |
|
61 |
-
wp_enqueue_script( 'itsec-
|
62 |
-
wp_localize_script( 'itsec-
|
63 |
}
|
64 |
|
65 |
public function add_styles() {
|
@@ -69,21 +65,27 @@ final class ITSEC_Logs_Page {
|
|
69 |
|
70 |
private function set_translation_strings() {
|
71 |
$this->translations = array(
|
72 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
-
'ajax_invalid'
|
75 |
|
76 |
-
'ajax_forbidden'
|
77 |
|
78 |
-
'ajax_not_found'
|
79 |
|
80 |
-
'ajax_server_error'
|
81 |
|
82 |
-
'ajax_unknown'
|
83 |
|
84 |
-
'ajax_timeout'
|
85 |
|
86 |
-
'ajax_parsererror'
|
87 |
);
|
88 |
|
89 |
foreach ( $this->translations as $key => $message ) {
|
@@ -94,28 +96,37 @@ final class ITSEC_Logs_Page {
|
|
94 |
}
|
95 |
}
|
96 |
|
97 |
-
|
98 |
-
if (
|
99 |
-
trigger_error( 'An invalid widget was registered.', E_USER_ERROR );
|
100 |
return;
|
101 |
}
|
102 |
|
103 |
-
if (
|
104 |
-
|
105 |
-
return;
|
106 |
-
}
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
return;
|
111 |
-
}
|
112 |
|
|
|
|
|
113 |
|
114 |
-
|
115 |
-
|
|
|
116 |
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_clear_logs' ) ) {
|
120 |
die( __( 'Security error!', 'better-wp-security' ) );
|
121 |
}
|
@@ -183,69 +194,233 @@ final class ITSEC_Logs_Page {
|
|
183 |
}
|
184 |
}
|
185 |
|
186 |
-
public function
|
187 |
-
|
|
|
|
|
188 |
|
189 |
-
$
|
190 |
-
|
191 |
|
192 |
-
|
193 |
-
if ( ! isset( $this->widgets[$id] ) ) {
|
194 |
-
$error = new WP_Error( 'itsec-settings-page-get-widget-settings-invalid-id', sprintf( __( 'The requested widget (%s) does not exist. Logs for it cannot be rendered.', 'better-wp-security' ), $id ) );
|
195 |
|
196 |
-
if ( $
|
197 |
-
|
|
|
|
|
|
|
198 |
} else {
|
199 |
-
|
200 |
}
|
201 |
-
}
|
|
|
202 |
|
203 |
-
|
204 |
-
$form = new ITSEC_Form();
|
205 |
-
}
|
206 |
|
207 |
-
|
|
|
|
|
208 |
|
209 |
-
$form->add_input_group( $id );
|
210 |
-
$form->set_defaults( $widget->get_defaults() );
|
211 |
|
212 |
-
|
213 |
-
|
214 |
-
|
|
|
|
|
|
|
|
|
|
|
215 |
|
216 |
-
|
217 |
|
218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
|
220 |
-
|
221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
}
|
|
|
|
|
|
|
223 |
}
|
224 |
|
225 |
-
|
226 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/
|
|
|
227 |
|
|
|
228 |
|
229 |
-
|
230 |
-
$filter = $_GET['filter'];
|
231 |
-
} else {
|
232 |
-
$filter = 'all';
|
233 |
-
}
|
234 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
|
236 |
-
|
|
|
237 |
|
|
|
|
|
238 |
|
239 |
-
$
|
240 |
-
|
241 |
-
);
|
242 |
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
|
247 |
|
248 |
-
$form
|
249 |
|
250 |
?>
|
251 |
<div class="wrap">
|
@@ -270,26 +445,39 @@ final class ITSEC_Logs_Page {
|
|
270 |
<div id="poststuff">
|
271 |
<div id="post-body" class="metabox-holder columns-2 hide-if-no-js">
|
272 |
<div id="postbox-container-2" class="postbox-container">
|
|
|
|
|
273 |
<?php if ( 'file' === ITSEC_Modules::get_setting( 'global', 'log_type' ) ) : ?>
|
274 |
<p><?php _e( 'To view logs within the plugin you must enable database logging in the Global Settings. File logging is not available for access within the plugin itself.', 'better-wp-security' ); ?></p>
|
|
|
275 |
<?php else : ?>
|
276 |
<div class="itsec-module-cards-container list">
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
<?php $form->start_form( array( 'method' => 'GET' ) ); ?>
|
287 |
-
<?php $this->show_filtered_logs( $filter ); ?>
|
288 |
-
<?php $form->end_form(); ?>
|
289 |
</div>
|
290 |
<?php endif; ?>
|
291 |
</div>
|
292 |
<div class="itsec-modal-background"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
|
294 |
<div id="postbox-container-1" class="postbox-container">
|
295 |
<?php foreach ( $this->widgets as $id => $widget ) : ?>
|
@@ -310,92 +498,74 @@ final class ITSEC_Logs_Page {
|
|
310 |
<div class="hide-if-js">
|
311 |
<p class="itsec-warning-message"><?php _e( 'iThemes Security requires Javascript in order for the settings to be modified. Please enable Javascript to configure the settings.', 'better-wp-security' ); ?></p>
|
312 |
</div>
|
|
|
|
|
|
|
313 |
</div>
|
314 |
</div>
|
315 |
<?php
|
316 |
|
317 |
}
|
318 |
|
319 |
-
|
320 |
-
$
|
|
|
|
|
|
|
321 |
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
break;
|
326 |
-
}
|
327 |
}
|
328 |
|
329 |
-
|
330 |
-
|
|
|
|
|
331 |
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
}
|
337 |
|
338 |
-
|
|
|
|
|
339 |
|
340 |
-
|
341 |
-
|
|
|
|
|
|
|
|
|
|
|
342 |
}
|
343 |
-
}
|
344 |
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
*
|
352 |
-
* @return void
|
353 |
-
*/
|
354 |
-
public function all_logs_content( $include_clear_logs_form = true ) {
|
355 |
-
|
356 |
-
require_once( ITSEC_Core::get_core_dir() . '/logger-all-logs.php' );
|
357 |
-
|
358 |
-
$log_display = new ITSEC_Logger_All_Logs();
|
359 |
-
$log_display->prepare_items();
|
360 |
-
$log_display->display();
|
361 |
-
|
362 |
-
if ( $include_clear_logs_form ) {
|
363 |
-
$this->clear_logs_form();
|
364 |
}
|
365 |
}
|
366 |
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
$log_count = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log`;" );
|
374 |
-
|
375 |
-
?>
|
376 |
-
<form method="post" action="">
|
377 |
-
<?php wp_nonce_field( 'itsec_clear_logs', 'wp_nonce' ); ?>
|
378 |
-
<input type="hidden" name="itsec_clear_logs" value="clear_logs"/>
|
379 |
-
<table class="form-table">
|
380 |
-
<tr valign="top">
|
381 |
-
<th scope="row" class="settinglabel">
|
382 |
-
<?php _e( 'Log Summary', 'better-wp-security' ); ?>
|
383 |
-
</th>
|
384 |
-
<td class="settingfield">
|
385 |
-
|
386 |
-
<p><?php _e( 'Your database contains', 'better-wp-security' ); ?>
|
387 |
-
<strong><?php echo $log_count; ?></strong> <?php _e( 'log entries.', 'better-wp-security' ); ?>
|
388 |
-
</p>
|
389 |
-
|
390 |
-
<p><?php _e( 'Use the button below to purge the log table in your database. Please note this will purge all log entries in the database including 404s.', 'better-wp-security' ); ?></p>
|
391 |
-
|
392 |
-
<p class="submit"><input type="submit" class="button-primary"
|
393 |
-
value="<?php _e( 'Clear Logs', 'better-wp-security' ); ?>"/></p>
|
394 |
-
</td>
|
395 |
-
</tr>
|
396 |
-
</table>
|
397 |
-
</form>
|
398 |
-
<?php
|
399 |
}
|
400 |
}
|
401 |
|
2 |
|
3 |
|
4 |
final class ITSEC_Logs_Page {
|
5 |
+
private $version = 1.8;
|
6 |
|
7 |
private $self_url = '';
|
8 |
private $modules = array();
|
9 |
private $widgets = array();
|
10 |
private $translations = array();
|
|
|
|
|
11 |
|
12 |
|
13 |
public function __construct() {
|
18 |
add_action( 'admin_print_scripts', array( $this, 'add_scripts' ) );
|
19 |
add_action( 'admin_print_styles', array( $this, 'add_styles' ) );
|
20 |
|
21 |
+
add_filter( 'screen_settings', array( $this, 'filter_screen_settings' ) );
|
|
|
22 |
|
23 |
+
$this->set_translation_strings();
|
|
|
24 |
|
25 |
|
26 |
require( dirname( __FILE__ ) . '/module-settings.php' );
|
48 |
}
|
49 |
|
50 |
$vars = array(
|
51 |
+
'ajax_action' => 'itsec_logs_page',
|
52 |
+
'ajax_nonce' => wp_create_nonce( 'itsec-logs-nonce' ),
|
53 |
'logs_page_url' => ITSEC_Core::get_logs_page_url(),
|
54 |
'translations' => $this->translations,
|
55 |
);
|
56 |
|
57 |
+
wp_enqueue_script( 'itsec-logs-page-script', plugins_url( 'js/logs.js', __FILE__ ), array( 'jquery-ui-dialog' ), $this->version, true );
|
58 |
+
wp_localize_script( 'itsec-logs-page-script', 'itsec_page', $vars );
|
59 |
}
|
60 |
|
61 |
public function add_styles() {
|
65 |
|
66 |
private function set_translation_strings() {
|
67 |
$this->translations = array(
|
68 |
+
'show_raw_details' => esc_html__( 'Show Raw Details', 'better-wp-security' ),
|
69 |
+
'hide_raw_details' => esc_html__( 'Hide Raw Details', 'better-wp-security' ),
|
70 |
+
'loading' => esc_html__( 'Loading...', 'better-wp-security' ),
|
71 |
+
/* translators: 1: loading gif image */
|
72 |
+
'log_migration_started' => esc_html__( '%1$s Migrating log entries from an older format. This message will update when the migration is complete.', 'better-wp-security' ),
|
73 |
+
'log_migration_failed' => esc_html__( 'The log entry migration failed. Reload the page to try again.', 'better-wp-security' ),
|
74 |
+
'log_migration_loading_url' => admin_url( 'images/loading.gif' ),
|
75 |
|
76 |
+
'ajax_invalid' => new WP_Error( 'itsec-settings-page-invalid-ajax-response', __( 'An "invalid format" error prevented the request from completing as expected. The format of data returned could not be recognized. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
77 |
|
78 |
+
'ajax_forbidden' => new WP_Error( 'itsec-settings-page-forbidden-ajax-response: %1$s "%2$s"', __( 'A "request forbidden" error prevented the request from completing as expected. The server returned a 403 status code, indicating that the server configuration is prohibiting this request. This could be due to a plugin/theme conflict or a server configuration issue. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings or server configuration that could account for this AJAX request being blocked.', 'better-wp-security' ) ),
|
79 |
|
80 |
+
'ajax_not_found' => new WP_Error( 'itsec-settings-page-not-found-ajax-response: %1$s "%2$s"', __( 'A "not found" error prevented the request from completing as expected. The server returned a 404 status code, indicating that the server was unable to find the requested admin-ajax.php file. This could be due to a plugin/theme conflict, a server configuration issue, or an incomplete WordPress installation. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings, alter server configurations, or reinstall WordPress.', 'better-wp-security' ) ),
|
81 |
|
82 |
+
'ajax_server_error' => new WP_Error( 'itsec-settings-page-server-error-ajax-response: %1$s "%2$s"', __( 'A "internal server" error prevented the request from completing as expected. The server returned a 500 status code, indicating that the server was unable to complete the request due to a fatal PHP error or a server problem. This could be due to a plugin/theme conflict, a server configuration issue, a temporary hosting issue, or invalid custom PHP modifications. Please check your server\'s error logs for details about the source of the error and contact your hosting company for assistance if required.', 'better-wp-security' ) ),
|
83 |
|
84 |
+
'ajax_unknown' => new WP_Error( 'itsec-settings-page-ajax-error-unknown: %1$s "%2$s"', __( 'An unknown error prevented the request from completing as expected. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
85 |
|
86 |
+
'ajax_timeout' => new WP_Error( 'itsec-settings-page-ajax-error-timeout: %1$s "%2$s"', __( 'A timeout error prevented the request from completing as expected. The site took too long to respond. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
87 |
|
88 |
+
'ajax_parsererror' => new WP_Error( 'itsec-settings-page-ajax-error-parsererror: %1$s "%2$s"', __( 'A parser error prevented the request from completing as expected. The site sent a response that jQuery could not process. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
89 |
);
|
90 |
|
91 |
foreach ( $this->translations as $key => $message ) {
|
96 |
}
|
97 |
}
|
98 |
|
99 |
+
private function handle_post() {
|
100 |
+
if ( ITSEC_Core::is_ajax_request() ) {
|
|
|
101 |
return;
|
102 |
}
|
103 |
|
104 |
+
if ( ! empty( $_POST['screenoptionnonce'] ) ) {
|
105 |
+
check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
|
|
|
|
|
106 |
|
107 |
+
if ( isset( $_POST['apply'] ) ) {
|
108 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
|
|
|
|
109 |
|
110 |
+
$options = ITSEC_Form::get_post_data( array_keys( ITSEC_Log_Util::get_logs_page_screen_options() ) );
|
111 |
+
ITSEC_Log_Util::set_logs_page_screen_options( $options );
|
112 |
|
113 |
+
ITSEC_Response::add_message( __( 'Your screen options saved successfully.', 'better-wp-security' ) );
|
114 |
+
} else if ( isset( $_POST['mark_all_seen'] ) ) {
|
115 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
116 |
|
117 |
+
$options['last_seen'] = $_POST['current_time_gmt'];
|
118 |
+
ITSEC_Log_Util::set_logs_page_screen_options( $options );
|
119 |
+
|
120 |
+
ITSEC_Response::add_message( __( 'Log entries hidden.', 'better-wp-security' ) );
|
121 |
+
} else if ( isset( $_POST['mark_all_unseen'] ) ) {
|
122 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
123 |
+
|
124 |
+
$options['last_seen'] = 0;
|
125 |
+
ITSEC_Log_Util::set_logs_page_screen_options( $options );
|
126 |
+
|
127 |
+
ITSEC_Response::add_message( __( 'Log entries shown.', 'better-wp-security' ) );
|
128 |
+
}
|
129 |
+
} else if ( ! empty( $_POST['itsec_clear_logs'] ) && 'clear_logs' === $_POST['itsec_clear_logs'] ) {
|
130 |
if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_clear_logs' ) ) {
|
131 |
die( __( 'Security error!', 'better-wp-security' ) );
|
132 |
}
|
194 |
}
|
195 |
}
|
196 |
|
197 |
+
public function handle_ajax_request() {
|
198 |
+
if ( WP_DEBUG ) {
|
199 |
+
ini_set( 'display_errors', 1 );
|
200 |
+
}
|
201 |
|
202 |
+
if ( ! empty( $_POST['method'] ) && 'handle_logs_migration' === $_POST['method'] ) {
|
203 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
204 |
|
205 |
+
$complete = ITSEC_Log_Util::migrate_old_log_entries();
|
|
|
|
|
206 |
|
207 |
+
if ( $complete ) {
|
208 |
+
$message = '<p>' . esc_html__( 'Migration complete. Please refresh the page to see all log entries.', 'better-wp-security' ) . '</p>';
|
209 |
+
$message .= '<a href="' . esc_url( $this->self_url ) . '" class="button-secondary">' . esc_html__( 'Refresh Page', 'better-wp-security' ) . '</a>';
|
210 |
+
|
211 |
+
ITSEC_Response::set_response( $message );
|
212 |
} else {
|
213 |
+
ITSEC_Response::set_response( 'incomplete' );
|
214 |
}
|
215 |
+
} else {
|
216 |
+
ITSEC_Core::set_interactive( true );
|
217 |
|
218 |
+
$id = ( isset( $_POST['id'] ) && is_string( $_POST['id'] ) ) ? $_POST['id'] : '';
|
|
|
|
|
219 |
|
220 |
+
if ( empty( $GLOBALS['hook_suffix'] ) ) {
|
221 |
+
$GLOBALS['hook_suffix'] = 'toplevel_page_itsec';
|
222 |
+
}
|
223 |
|
|
|
|
|
224 |
|
225 |
+
if ( false === check_ajax_referer( 'itsec-logs-nonce', 'nonce', false ) ) {
|
226 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-logs-page-failed-nonce', __( 'A nonce security check failed, preventing the request from completing as expected. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
|
227 |
+
} else if ( ! ITSEC_Core::current_user_can_manage() ) {
|
228 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-logs-page-insufficient-privileges', __( 'A permissions security check failed, preventing the request from completing as expected. The currently logged in user does not have sufficient permissions to make this request. Please try reloading the page and trying again.', 'better-wp-security' ) ) );
|
229 |
+
} else if ( empty( $id ) ) {
|
230 |
+
ITSEC_Response::add_error( new WP_Error( 'itsec-logs-page-missing-method', __( 'The server did not receive a valid request. The required "id" argument is missing. Please try again.', 'better-wp-security' ) ) );
|
231 |
+
} else {
|
232 |
+
ITSEC_Modules::load_module_file( 'logs.php' );
|
233 |
|
234 |
+
$entry = ITSEC_Log::get_entry( $id );
|
235 |
|
236 |
+
if ( false === strpos( $entry['code'], '::' ) ) {
|
237 |
+
$code = $entry['code'];
|
238 |
+
$code_data = array();
|
239 |
+
} else {
|
240 |
+
list( $code, $code_data ) = explode( '::', $entry['code'], 2 );
|
241 |
+
$code_data = explode( ',', $code_data );
|
242 |
+
}
|
243 |
|
244 |
+
|
245 |
+
$timestamp = strtotime( $entry['timestamp'] );
|
246 |
+
$datetime = date( 'Y-m-d H:i:s', $timestamp + ITSEC_Core::get_time_offset() );
|
247 |
+
$types = ITSEC_Log::get_types_for_display();
|
248 |
+
|
249 |
+
if ( isset( $types[$entry['type']] ) ) {
|
250 |
+
$type = $types[$entry['type']];
|
251 |
+
} else {
|
252 |
+
$type = esc_html( $entry['type'] );
|
253 |
+
}
|
254 |
+
|
255 |
+
$user = get_user_by( 'id', $entry['user_id'] );
|
256 |
+
|
257 |
+
if ( $user ) {
|
258 |
+
$username = $user->user_login;
|
259 |
+
} else {
|
260 |
+
$username = '';
|
261 |
+
}
|
262 |
+
|
263 |
+
$details = array(
|
264 |
+
'module' => array(
|
265 |
+
'header' => esc_html__( 'Module', 'better-wp-security' ),
|
266 |
+
'content' => esc_html( $entry['module'] ),
|
267 |
+
),
|
268 |
+
'type' => array(
|
269 |
+
'header' => esc_html__( 'Type', 'better-wp-security' ),
|
270 |
+
'content' => $type,
|
271 |
+
),
|
272 |
+
'description' => array(
|
273 |
+
'header' => esc_html__( 'Description', 'better-wp-security' ),
|
274 |
+
'content' => esc_html( $code ),
|
275 |
+
),
|
276 |
+
'timestamp' => array(
|
277 |
+
'header' => esc_html__( 'Timestamp', 'better-wp-security' ),
|
278 |
+
'content' => esc_html( $datetime ),
|
279 |
+
),
|
280 |
+
'host' => array(
|
281 |
+
'header' => esc_html__( 'Host', 'better-wp-security' ),
|
282 |
+
'content' => '<code>' . esc_html( $entry['remote_ip'] ) . '</code>',
|
283 |
+
),
|
284 |
+
'user' => array(
|
285 |
+
'header' => esc_html__( 'User', 'better-wp-security' ),
|
286 |
+
'content' => esc_html( $username ),
|
287 |
+
),
|
288 |
+
'url' => array(
|
289 |
+
'header' => esc_html__( 'URL', 'better-wp-security' ),
|
290 |
+
'content' => '<code>' . esc_html( $entry['url'] ) . '</code>',
|
291 |
+
),
|
292 |
+
'raw-details' => array(
|
293 |
+
'header' => esc_html__( 'Raw Details', 'better-wp-security' ),
|
294 |
+
'content' => true,
|
295 |
+
),
|
296 |
+
);
|
297 |
+
|
298 |
+
|
299 |
+
$details = apply_filters( "itsec_logs_prepare_{$entry['module']}_entry_for_details_display", $details, $entry, $code, $code_data );
|
300 |
+
|
301 |
+
if ( isset( $details['raw-details'] ) ) {
|
302 |
+
if ( true === $details['raw-details']['content'] ) {
|
303 |
+
// Ensure that Raw Details is listed last.
|
304 |
+
$raw_details = $details['raw-details'];
|
305 |
+
unset( $details['raw-details'] );
|
306 |
+
|
307 |
+
if ( empty( $entry['parent_id'] ) ) {
|
308 |
+
unset( $entry['parent_id'] );
|
309 |
+
}
|
310 |
+
|
311 |
+
if ( strlen( serialize( $entry['data'] ) ) > 1048576 ) {
|
312 |
+
// Don't run the risk of crashing the process when trying to display a large data set.
|
313 |
+
$entry['data'] = '[' . esc_html__( 'Too large to display', 'better-wp-security' ) . ']';
|
314 |
+
}
|
315 |
+
|
316 |
+
$raw_details['content'] = '<pre>' . preg_replace( '/^ /m', '', substr( ITSEC_Lib::get_print_r( $entry ), 23 ) ) . '</pre>';
|
317 |
+
$details['raw-details'] = $raw_details;
|
318 |
+
}
|
319 |
+
|
320 |
+
$details['raw-details']['content'] = '<p><a class="itsec-log-raw-details-toggle" href="#">' . $this->translations['show_raw_details'] . '</a></p><div class="itsec-log-raw-details">' . $details['raw-details']['content'] . '</div>';
|
321 |
+
}
|
322 |
+
}
|
323 |
+
|
324 |
+
ob_start();
|
325 |
+
|
326 |
+
?>
|
327 |
+
<table class="form-table">
|
328 |
+
<?php foreach ( $details as $row ) : ?>
|
329 |
+
<tr>
|
330 |
+
<th scope="row"><?php echo $row['header']; ?></th>
|
331 |
+
<td><?php echo $row['content'] ?></td>
|
332 |
+
</tr>
|
333 |
+
<?php endforeach; ?>
|
334 |
+
</table>
|
335 |
+
<?php
|
336 |
+
|
337 |
+
ITSEC_Response::set_response( ob_get_clean() );
|
338 |
}
|
339 |
+
|
340 |
+
|
341 |
+
ITSEC_Response::send_json();
|
342 |
}
|
343 |
|
344 |
+
public function filter_screen_settings( $settings ) {
|
345 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
346 |
+
$options = ITSEC_Log_Util::get_logs_page_screen_options();
|
347 |
|
348 |
+
$form = new ITSEC_Form( $options );
|
349 |
|
350 |
+
ob_start();
|
|
|
|
|
|
|
|
|
351 |
|
352 |
+
?>
|
353 |
+
<fieldset class="screen-options">
|
354 |
+
<legend><?php esc_html_e( 'Pagination' ); ?></legend>
|
355 |
+
<label for="itsec_logs_page_entries_per_page"><?php esc_html_e( 'Number of items per page:' ); ?></label>
|
356 |
+
<?php $form->add_number( 'per_page', array( 'step' => 1, 'min' => 1, 'max' => 999, 'maxlength' => 3 ) ); ?>
|
357 |
+
</fieldset>
|
358 |
+
|
359 |
+
<fieldset>
|
360 |
+
<legend><?php esc_html_e( 'View Mode' ); ?></legend>
|
361 |
+
|
362 |
+
<label for="itsec-default_view-important">
|
363 |
+
<?php $form->add_radio( 'default_view', 'important' ); ?>
|
364 |
+
<?php esc_html_e( 'Important Events', 'better-wp-security' ); ?>
|
365 |
+
</label>
|
366 |
+
<label for="itsec-default_view-all">
|
367 |
+
<?php $form->add_radio( 'default_view', 'all' ); ?>
|
368 |
+
<?php esc_html_e( 'All Events', 'better-wp-security' ); ?>
|
369 |
+
</label>
|
370 |
+
<label for="itsec-default_view-critical-issue">
|
371 |
+
<?php $form->add_radio( 'default_view', 'critical-issue' ); ?>
|
372 |
+
<?php esc_html_e( 'Critical Issues', 'better-wp-security' ); ?>
|
373 |
+
</label>
|
374 |
+
</fieldset>
|
375 |
+
|
376 |
+
<fieldset>
|
377 |
+
<legend><?php esc_html_e( 'Colors', 'better-wp-security' ); ?></legend>
|
378 |
+
<label for="itsec-color">
|
379 |
+
<?php $form->add_checkbox( 'color' ); ?>
|
380 |
+
<?php esc_html_e( 'Use colors to indicate the severity of each entry.', 'better-wp-security' ); ?>
|
381 |
+
</label>
|
382 |
+
</fieldset>
|
383 |
+
|
384 |
+
<fieldset>
|
385 |
+
<legend><?php esc_html_e( 'Advanced Entries for Support and Developers', 'better-wp-security' ); ?></legend>
|
386 |
+
<label for="itsec-show_debug">
|
387 |
+
<?php $form->add_checkbox( 'show_debug' ); ?>
|
388 |
+
<?php esc_html_e( 'Show Debug entries.', 'better-wp-security' ); ?>
|
389 |
+
</label>
|
390 |
+
<br />
|
391 |
+
<label for="itsec-show_process">
|
392 |
+
<?php $form->add_checkbox( 'show_process' ); ?>
|
393 |
+
<?php esc_html_e( 'Show Process entries.', 'better-wp-security' ); ?>
|
394 |
+
</label>
|
395 |
+
</fieldset>
|
396 |
+
|
397 |
+
<p class="submit">
|
398 |
+
<?php $form->add_submit( 'apply', __( 'Apply' ) ); ?>
|
399 |
+
</p>
|
400 |
+
|
401 |
+
<p class="submit">
|
402 |
+
<?php $form->add_submit( 'mark_all_seen', array( 'class' => 'button-secondary', 'value' => esc_html__( 'Hide Current Entries', 'better-wp-security' ), 'title' => esc_html__( 'Hide existing entries from view without deleting them.', 'better-wp-security' ) ) ); ?>
|
403 |
+
|
404 |
+
<?php $form->add_submit( 'mark_all_unseen', array( 'class' => 'button-secondary', 'value' => esc_html__( 'Show All Entries', 'better-wp-security' ), 'title' => esc_html__( 'Show all entries, including ones that were previously hidden.', 'better-wp-security' ) ) ); ?>
|
405 |
+
<?php $form->add_hidden( 'current_time_gmt', ITSEC_Core::get_current_time_gmt() ); ?>
|
406 |
+
</p>
|
407 |
+
<?php
|
408 |
|
409 |
+
return ob_get_clean();
|
410 |
+
}
|
411 |
|
412 |
+
public function handle_page_load( $self_url ) {
|
413 |
+
$this->self_url = $self_url;
|
414 |
|
415 |
+
$this->show_settings_page();
|
416 |
+
}
|
|
|
417 |
|
418 |
+
private function show_settings_page() {
|
419 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-wp-list-table.php' );
|
420 |
+
require_once( ITSEC_Core::get_core_dir() . '/admin-pages/logs-list-table.php' );
|
421 |
|
422 |
|
423 |
+
$form = new ITSEC_Form();
|
424 |
|
425 |
?>
|
426 |
<div class="wrap">
|
445 |
<div id="poststuff">
|
446 |
<div id="post-body" class="metabox-holder columns-2 hide-if-no-js">
|
447 |
<div id="postbox-container-2" class="postbox-container">
|
448 |
+
<?php $this->show_old_logs_migration(); ?>
|
449 |
+
|
450 |
<?php if ( 'file' === ITSEC_Modules::get_setting( 'global', 'log_type' ) ) : ?>
|
451 |
<p><?php _e( 'To view logs within the plugin you must enable database logging in the Global Settings. File logging is not available for access within the plugin itself.', 'better-wp-security' ); ?></p>
|
452 |
+
<p><?php printf( wp_kses( __( 'The log file can be found at: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), ITSEC_Log::get_log_file_path() ); ?></p>
|
453 |
<?php else : ?>
|
454 |
<div class="itsec-module-cards-container list">
|
455 |
+
<?php
|
456 |
+
$list = new ITSEC_Logs_List_Table();
|
457 |
+
|
458 |
+
$list->prepare_items();
|
459 |
+
$list->views();
|
460 |
+
$form->start_form( array( 'method' => 'GET' ) );
|
461 |
+
$list->display();
|
462 |
+
$form->end_form();
|
463 |
+
?>
|
|
|
|
|
|
|
464 |
</div>
|
465 |
<?php endif; ?>
|
466 |
</div>
|
467 |
<div class="itsec-modal-background"></div>
|
468 |
+
<div id="itsec-log-details-container" class="grid">
|
469 |
+
<div class="itsec-module-settings-container">
|
470 |
+
<div class="itsec-modal-navigation">
|
471 |
+
<button class="dashicons itsec-close-modal"></button>
|
472 |
+
</div>
|
473 |
+
<div class="itsec-module-settings-content-container">
|
474 |
+
<div class="itsec-module-settings-content">
|
475 |
+
<div class="itsec-module-messages-container"></div>
|
476 |
+
<div class="itsec-module-settings-content-main"></div>
|
477 |
+
</div>
|
478 |
+
</div>
|
479 |
+
</div>
|
480 |
+
</div>
|
481 |
|
482 |
<div id="postbox-container-1" class="postbox-container">
|
483 |
<?php foreach ( $this->widgets as $id => $widget ) : ?>
|
498 |
<div class="hide-if-js">
|
499 |
<p class="itsec-warning-message"><?php _e( 'iThemes Security requires Javascript in order for the settings to be modified. Please enable Javascript to configure the settings.', 'better-wp-security' ); ?></p>
|
500 |
</div>
|
501 |
+
|
502 |
+
<div class="hidden" id="itsec-logs-cache">
|
503 |
+
</div>
|
504 |
</div>
|
505 |
</div>
|
506 |
<?php
|
507 |
|
508 |
}
|
509 |
|
510 |
+
public function register_widget( $widget ) {
|
511 |
+
if ( ! is_object( $widget ) || ! is_a( $widget, 'ITSEC_Settings_Page_Sidebar_Widget' ) ) {
|
512 |
+
trigger_error( 'An invalid widget was registered.', E_USER_ERROR );
|
513 |
+
return;
|
514 |
+
}
|
515 |
|
516 |
+
if ( isset( $this->modules[$widget->id] ) ) {
|
517 |
+
trigger_error( "A widget with the id of {$widget->id} is registered. Widget id's must be unique from any other module or widget." );
|
518 |
+
return;
|
|
|
|
|
519 |
}
|
520 |
|
521 |
+
if ( isset( $this->widgets[$widget->id] ) ) {
|
522 |
+
trigger_error( "A widget with the id of {$widget->id} is already registered. Widget id's must be unique from any other module or widget." );
|
523 |
+
return;
|
524 |
+
}
|
525 |
|
526 |
+
|
527 |
+
$this->widgets[$widget->id] = $widget;
|
528 |
+
}
|
529 |
+
|
530 |
+
private function get_widget_settings( $id, $form = false, $echo = false ) {
|
531 |
+
if ( ! isset( $this->widgets[$id] ) ) {
|
532 |
+
$error = new WP_Error( 'itsec-settings-page-get-widget-settings-invalid-id', sprintf( __( 'The requested widget (%s) does not exist. Logs for it cannot be rendered.', 'better-wp-security' ), $id ) );
|
533 |
+
|
534 |
+
if ( $echo ) {
|
535 |
+
ITSEC_Lib::show_error_message( $error );
|
536 |
+
} else {
|
537 |
+
return $error;
|
538 |
+
}
|
539 |
}
|
540 |
|
541 |
+
if ( false === $form ) {
|
542 |
+
$form = new ITSEC_Form();
|
543 |
+
}
|
544 |
|
545 |
+
$widget = $this->widgets[$id];
|
546 |
+
|
547 |
+
$form->add_input_group( $id );
|
548 |
+
$form->set_defaults( $widget->get_defaults() );
|
549 |
+
|
550 |
+
if ( ! $echo ) {
|
551 |
+
ob_start();
|
552 |
}
|
|
|
553 |
|
554 |
+
$widget->render( $form );
|
555 |
+
|
556 |
+
$form->remove_all_input_groups();
|
557 |
+
|
558 |
+
if ( ! $echo ) {
|
559 |
+
return ob_get_clean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
560 |
}
|
561 |
}
|
562 |
|
563 |
+
private function show_old_logs_migration() {
|
564 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/log-util.php' );
|
565 |
+
|
566 |
+
if ( ITSEC_Log_Util::has_old_log_entries() ) {
|
567 |
+
echo '<div id="old-logs-migration-status"></div>';
|
568 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
}
|
570 |
}
|
571 |
|
core/admin-pages/page-settings.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
|
4 |
final class ITSEC_Settings_Page {
|
5 |
-
private $version =
|
6 |
|
7 |
private static $instance;
|
8 |
|
@@ -94,7 +94,7 @@ final class ITSEC_Settings_Page {
|
|
94 |
}
|
95 |
|
96 |
wp_enqueue_script( 'itsec-scrollTo', plugins_url( 'js/scrollTo.js', dirname( __FILE__ ) ), array( 'jquery' ) );
|
97 |
-
wp_enqueue_script( 'itsec-settings-page-script', plugins_url( 'js/
|
98 |
wp_localize_script( 'itsec-settings-page-script', 'itsec_page', $vars );
|
99 |
}
|
100 |
|
@@ -120,20 +120,6 @@ final class ITSEC_Settings_Page {
|
|
120 |
|
121 |
/* translators: 1: module name */
|
122 |
'successful_save' => __( 'Settings saved successfully for %1$s.', 'better-wp-security' ),
|
123 |
-
|
124 |
-
'ajax_invalid' => new WP_Error( 'itsec-settings-page-invalid-ajax-response', __( 'An "invalid format" error prevented the request from completing as expected. The format of data returned could not be recognized. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
125 |
-
|
126 |
-
'ajax_forbidden' => new WP_Error( 'itsec-settings-page-forbidden-ajax-response: %1$s "%2$s"', __( 'A "request forbidden" error prevented the request from completing as expected. The server returned a 403 status code, indicating that the server configuration is prohibiting this request. This could be due to a plugin/theme conflict or a server configuration issue. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings or server configuration that could account for this AJAX request being blocked.', 'better-wp-security' ) ),
|
127 |
-
|
128 |
-
'ajax_not_found' => new WP_Error( 'itsec-settings-page-not-found-ajax-response: %1$s "%2$s"', __( 'A "not found" error prevented the request from completing as expected. The server returned a 404 status code, indicating that the server was unable to find the requested admin-ajax.php file. This could be due to a plugin/theme conflict, a server configuration issue, or an incomplete WordPress installation. Please try refreshing the page and trying again. If the request continues to fail, you may have to alter plugin settings, alter server configurations, or reinstall WordPress.', 'better-wp-security' ) ),
|
129 |
-
|
130 |
-
'ajax_server_error' => new WP_Error( 'itsec-settings-page-server-error-ajax-response: %1$s "%2$s"', __( 'A "internal server" error prevented the request from completing as expected. The server returned a 500 status code, indicating that the server was unable to complete the request due to a fatal PHP error or a server problem. This could be due to a plugin/theme conflict, a server configuration issue, a temporary hosting issue, or invalid custom PHP modifications. Please check your server\'s error logs for details about the source of the error and contact your hosting company for assistance if required.', 'better-wp-security' ) ),
|
131 |
-
|
132 |
-
'ajax_unknown' => new WP_Error( 'itsec-settings-page-ajax-error-unknown: %1$s "%2$s"', __( 'An unknown error prevented the request from completing as expected. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
133 |
-
|
134 |
-
'ajax_timeout' => new WP_Error( 'itsec-settings-page-ajax-error-timeout: %1$s "%2$s"', __( 'A timeout error prevented the request from completing as expected. The site took too long to respond. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
135 |
-
|
136 |
-
'ajax_parsererror' => new WP_Error( 'itsec-settings-page-ajax-error-parsererror: %1$s "%2$s"', __( 'A parser error prevented the request from completing as expected. The site sent a response that jQuery could not process. This could be due to a plugin/theme conflict or a server configuration issue.', 'better-wp-security' ) ),
|
137 |
);
|
138 |
|
139 |
foreach ( $this->translations as $key => $message ) {
|
2 |
|
3 |
|
4 |
final class ITSEC_Settings_Page {
|
5 |
+
private $version = 2.0;
|
6 |
|
7 |
private static $instance;
|
8 |
|
94 |
}
|
95 |
|
96 |
wp_enqueue_script( 'itsec-scrollTo', plugins_url( 'js/scrollTo.js', dirname( __FILE__ ) ), array( 'jquery' ) );
|
97 |
+
wp_enqueue_script( 'itsec-settings-page-script', plugins_url( 'js/settings.js', __FILE__ ), array( 'underscore' ), $this->version, true );
|
98 |
wp_localize_script( 'itsec-settings-page-script', 'itsec_page', $vars );
|
99 |
}
|
100 |
|
120 |
|
121 |
/* translators: 1: module name */
|
122 |
'successful_save' => __( 'Settings saved successfully for %1$s.', 'better-wp-security' ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
);
|
124 |
|
125 |
foreach ( $this->translations as $key => $message ) {
|
core/core.php
CHANGED
@@ -10,7 +10,6 @@
|
|
10 |
* @since 4.0
|
11 |
*
|
12 |
* @global array $itsec_globals Global variables for use throughout iThemes Security.
|
13 |
-
* @global object $itsec_logger iThemes Security logging class.
|
14 |
* @global object $itsec_lockout Class for handling lockouts.
|
15 |
*
|
16 |
*/
|
@@ -25,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
25 |
*
|
26 |
* @access private
|
27 |
*/
|
28 |
-
private $plugin_build =
|
29 |
|
30 |
/**
|
31 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
@@ -88,7 +87,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
88 |
*
|
89 |
*/
|
90 |
public function init( $plugin_file, $plugin_name ) {
|
91 |
-
global $itsec_globals, $
|
92 |
|
93 |
$this->plugin_file = $plugin_file;
|
94 |
$this->plugin_dir = dirname( $plugin_file ) . '/';
|
@@ -111,7 +110,6 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
111 |
ITSEC_Modules::init_modules();
|
112 |
|
113 |
require( $this->plugin_dir . 'core/lib.php' );
|
114 |
-
require( $this->plugin_dir . 'core/logger.php' );
|
115 |
require( $this->plugin_dir . 'core/lockout.php' );
|
116 |
require( $this->plugin_dir . 'core/files.php' );
|
117 |
require( $this->plugin_dir . 'core/notify.php' );
|
@@ -119,12 +117,13 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
119 |
require( $this->plugin_dir . 'core/lib/class-itsec-lib-user-activity.php' );
|
120 |
require( $this->plugin_dir . 'core/lib/class-itsec-lib-password-requirements.php' );
|
121 |
|
|
|
|
|
122 |
require( $this->plugin_dir . 'core/lib/class-itsec-scheduler.php' );
|
123 |
require( $this->plugin_dir . 'core/lib/class-itsec-job.php' );
|
124 |
|
125 |
$this->itsec_files = ITSEC_Files::get_instance();
|
126 |
$this->itsec_notify = new ITSEC_Notify();
|
127 |
-
$itsec_logger = new ITSEC_Logger();
|
128 |
$itsec_lockout = new ITSEC_Lockout();
|
129 |
$itsec_lockout->run();
|
130 |
|
10 |
* @since 4.0
|
11 |
*
|
12 |
* @global array $itsec_globals Global variables for use throughout iThemes Security.
|
|
|
13 |
* @global object $itsec_lockout Class for handling lockouts.
|
14 |
*
|
15 |
*/
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
+
private $plugin_build = 4084;
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
87 |
*
|
88 |
*/
|
89 |
public function init( $plugin_file, $plugin_name ) {
|
90 |
+
global $itsec_globals, $itsec_lockout;
|
91 |
|
92 |
$this->plugin_file = $plugin_file;
|
93 |
$this->plugin_dir = dirname( $plugin_file ) . '/';
|
110 |
ITSEC_Modules::init_modules();
|
111 |
|
112 |
require( $this->plugin_dir . 'core/lib.php' );
|
|
|
113 |
require( $this->plugin_dir . 'core/lockout.php' );
|
114 |
require( $this->plugin_dir . 'core/files.php' );
|
115 |
require( $this->plugin_dir . 'core/notify.php' );
|
117 |
require( $this->plugin_dir . 'core/lib/class-itsec-lib-user-activity.php' );
|
118 |
require( $this->plugin_dir . 'core/lib/class-itsec-lib-password-requirements.php' );
|
119 |
|
120 |
+
require( $this->plugin_dir . 'core/lib/log.php' );
|
121 |
+
|
122 |
require( $this->plugin_dir . 'core/lib/class-itsec-scheduler.php' );
|
123 |
require( $this->plugin_dir . 'core/lib/class-itsec-job.php' );
|
124 |
|
125 |
$this->itsec_files = ITSEC_Files::get_instance();
|
126 |
$this->itsec_notify = new ITSEC_Notify();
|
|
|
127 |
$itsec_lockout = new ITSEC_Lockout();
|
128 |
$itsec_lockout->run();
|
129 |
|
core/files.php
CHANGED
@@ -143,9 +143,9 @@ final class ITSEC_Files {
|
|
143 |
$host_rule .= "</IfModule>\n";
|
144 |
$host_rule .= "<IfModule !mod_authz_core.c>\n";
|
145 |
$host_rule .= "\tOrder allow,deny\n";
|
|
|
146 |
$host_rule .= "\tDeny from env=DenyAccess\n";
|
147 |
$host_rule .= "\tDeny from $host\n";
|
148 |
-
$host_rule .= "\tAllow from all\n";
|
149 |
$host_rule .= "</IfModule>\n";
|
150 |
}
|
151 |
|
143 |
$host_rule .= "</IfModule>\n";
|
144 |
$host_rule .= "<IfModule !mod_authz_core.c>\n";
|
145 |
$host_rule .= "\tOrder allow,deny\n";
|
146 |
+
$host_rule .= "\tAllow from all\n";
|
147 |
$host_rule .= "\tDeny from env=DenyAccess\n";
|
148 |
$host_rule .= "\tDeny from $host\n";
|
|
|
149 |
$host_rule .= "</IfModule>\n";
|
150 |
}
|
151 |
|
core/history.txt
CHANGED
@@ -618,11 +618,11 @@
|
|
618 |
Bug Fix: Prevent multiple cron tests from being scheduled at once.
|
619 |
Bug Fix: Cron test being stuck in a loop preventing a site from switching back to the cron scheduler.
|
620 |
Bug Fix: Prevent warnings when a single and recurring event were scheduled at the same time.
|
621 |
-
4.0.3 -
|
622 |
Bug Fix: Fix scheduling retries for Malware Scans on sites that don't fully support WordPress's cron system.
|
623 |
Bug Fix: Reactivating Away Mode now replaces the active file if you had previously removed it.
|
624 |
Bug Fix: Ensure lockouts take effect immediately, even on systems where changes to server configuration files do not take effect immediately.
|
625 |
-
4.0.4 -
|
626 |
Enhancement: Display user lockouts in Lockout Sidebar.
|
627 |
Bug Fix: Load translations on the plugins_loaded hook.
|
628 |
Bug Fix: Fixed method that could be used to discover hidden login slug on some sites.
|
@@ -630,3 +630,11 @@
|
|
630 |
Bug Fix: Update to the REST API "Restricted Access" feature to protect against methods to work around the restricted access.
|
631 |
Bug Fix: Prevent login page being hidden when following the "Confirm Email Address" notification URL.
|
632 |
Bug Fix: Hide Backend notifications not being properly sent when first enabled.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
618 |
Bug Fix: Prevent multiple cron tests from being scheduled at once.
|
619 |
Bug Fix: Cron test being stuck in a loop preventing a site from switching back to the cron scheduler.
|
620 |
Bug Fix: Prevent warnings when a single and recurring event were scheduled at the same time.
|
621 |
+
4.0.3 - 2018-01-04 - Chris Jean & Timothy Jacobs
|
622 |
Bug Fix: Fix scheduling retries for Malware Scans on sites that don't fully support WordPress's cron system.
|
623 |
Bug Fix: Reactivating Away Mode now replaces the active file if you had previously removed it.
|
624 |
Bug Fix: Ensure lockouts take effect immediately, even on systems where changes to server configuration files do not take effect immediately.
|
625 |
+
4.0.4 - 2018-01-29 - Chris Jean & Timothy Jacobs
|
626 |
Enhancement: Display user lockouts in Lockout Sidebar.
|
627 |
Bug Fix: Load translations on the plugins_loaded hook.
|
628 |
Bug Fix: Fixed method that could be used to discover hidden login slug on some sites.
|
630 |
Bug Fix: Update to the REST API "Restricted Access" feature to protect against methods to work around the restricted access.
|
631 |
Bug Fix: Prevent login page being hidden when following the "Confirm Email Address" notification URL.
|
632 |
Bug Fix: Hide Backend notifications not being properly sent when first enabled.
|
633 |
+
4.1.0 - 2018-02-08 - Chris Jean & Timothy Jacobs
|
634 |
+
Enhancement: Updated logging system to keep track of more information and have more options to filter and sort log entries.
|
635 |
+
Enhancement: Improved efficiency of File Change Detection scanning.
|
636 |
+
Bug Fix: Fixed issue that could register loading the logging page as a failed login attempt on some sites.
|
637 |
+
4.1.1 - 2018-02-08 - Chris Jean & Timothy Jacobs
|
638 |
+
Bug Fix: Fixed schema issue with new logs table.
|
639 |
+
4.1.2 - 2018-02-12 - Chris Jean & Timothy Jacobs
|
640 |
+
Bug Fix: Fixed "undefined offset" error when displaying specific migrated old log entries.
|
core/lib.php
CHANGED
@@ -52,84 +52,14 @@ final class ITSEC_Lib {
|
|
52 |
/**
|
53 |
* Creates appropriate database tables.
|
54 |
*
|
55 |
-
* Uses dbdelta to create database tables either on activation or in the event that one is missing.
|
56 |
-
*
|
57 |
* @since 4.0.0
|
58 |
*
|
59 |
* @return void
|
60 |
*/
|
61 |
public static function create_database_tables() {
|
|
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
$charset_collate = '';
|
66 |
-
|
67 |
-
if ( ! empty( $wpdb->charset ) ) {
|
68 |
-
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
69 |
-
}
|
70 |
-
|
71 |
-
if ( ! empty( $wpdb->collate ) ) {
|
72 |
-
$charset_collate .= " COLLATE $wpdb->collate";
|
73 |
-
}
|
74 |
-
|
75 |
-
//Set up log table
|
76 |
-
$tables = "CREATE TABLE " . $wpdb->base_prefix . "itsec_log (
|
77 |
-
log_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
78 |
-
log_type varchar(20) NOT NULL DEFAULT '',
|
79 |
-
log_function varchar(255) NOT NULL DEFAULT '',
|
80 |
-
log_priority int(2) NOT NULL DEFAULT 1,
|
81 |
-
log_date datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
|
82 |
-
log_date_gmt datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
|
83 |
-
log_host varchar(40),
|
84 |
-
log_username varchar(60),
|
85 |
-
log_user bigint(20) UNSIGNED,
|
86 |
-
log_url varchar(255),
|
87 |
-
log_referrer varchar(255),
|
88 |
-
log_data longtext NOT NULL,
|
89 |
-
PRIMARY KEY (log_id),
|
90 |
-
KEY log_type (log_type),
|
91 |
-
KEY log_date_gmt (log_date_gmt)
|
92 |
-
) " . $charset_collate . ";";
|
93 |
-
|
94 |
-
//set up lockout table
|
95 |
-
$tables .= "CREATE TABLE " . $wpdb->base_prefix . "itsec_lockouts (
|
96 |
-
lockout_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
97 |
-
lockout_type varchar(20) NOT NULL,
|
98 |
-
lockout_start datetime NOT NULL,
|
99 |
-
lockout_start_gmt datetime NOT NULL,
|
100 |
-
lockout_expire datetime NOT NULL,
|
101 |
-
lockout_expire_gmt datetime NOT NULL,
|
102 |
-
lockout_host varchar(40),
|
103 |
-
lockout_user bigint(20) UNSIGNED,
|
104 |
-
lockout_username varchar(60),
|
105 |
-
lockout_active int(1) NOT NULL DEFAULT 1,
|
106 |
-
PRIMARY KEY (lockout_id),
|
107 |
-
KEY lockout_expire_gmt (lockout_expire_gmt),
|
108 |
-
KEY lockout_host (lockout_host),
|
109 |
-
KEY lockout_user (lockout_user),
|
110 |
-
KEY lockout_username (lockout_username),
|
111 |
-
KEY lockout_active (lockout_active)
|
112 |
-
) " . $charset_collate . ";";
|
113 |
-
|
114 |
-
//set up temp table
|
115 |
-
$tables .= "CREATE TABLE " . $wpdb->base_prefix . "itsec_temp (
|
116 |
-
temp_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
117 |
-
temp_type varchar(20) NOT NULL,
|
118 |
-
temp_date datetime NOT NULL,
|
119 |
-
temp_date_gmt datetime NOT NULL,
|
120 |
-
temp_host varchar(40),
|
121 |
-
temp_user bigint(20) UNSIGNED,
|
122 |
-
temp_username varchar(60),
|
123 |
-
PRIMARY KEY (temp_id),
|
124 |
-
KEY temp_date_gmt (temp_date_gmt),
|
125 |
-
KEY temp_host (temp_host),
|
126 |
-
KEY temp_user (temp_user),
|
127 |
-
KEY temp_username (temp_username)
|
128 |
-
) " . $charset_collate . ";";
|
129 |
-
|
130 |
-
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
131 |
-
@dbDelta( $tables );
|
132 |
-
|
133 |
}
|
134 |
|
135 |
/**
|
@@ -703,6 +633,12 @@ final class ITSEC_Lib {
|
|
703 |
* @param string $username
|
704 |
*/
|
705 |
public static function handle_wp_login_failed( $username ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
706 |
$authentication_types = array();
|
707 |
|
708 |
if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
|
@@ -739,9 +675,8 @@ final class ITSEC_Lib {
|
|
739 |
}
|
740 |
|
741 |
$details = compact( 'source', 'authentication_types' );
|
742 |
-
$details = apply_filters( 'itsec-filter-failed-login-details', $details );
|
743 |
|
744 |
-
|
745 |
}
|
746 |
|
747 |
/**
|
@@ -1084,6 +1019,18 @@ final class ITSEC_Lib {
|
|
1084 |
return $array;
|
1085 |
}
|
1086 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1087 |
/**
|
1088 |
* Check if WP Cron appears to be running properly.
|
1089 |
*
|
52 |
/**
|
53 |
* Creates appropriate database tables.
|
54 |
*
|
|
|
|
|
55 |
* @since 4.0.0
|
56 |
*
|
57 |
* @return void
|
58 |
*/
|
59 |
public static function create_database_tables() {
|
60 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/schema.php' );
|
61 |
|
62 |
+
ITSEC_Schema::create_database_tables();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
}
|
64 |
|
65 |
/**
|
633 |
* @param string $username
|
634 |
*/
|
635 |
public static function handle_wp_login_failed( $username ) {
|
636 |
+
$details = self::get_login_details();
|
637 |
+
|
638 |
+
do_action( 'itsec-handle-failed-login', $username, $details );
|
639 |
+
}
|
640 |
+
|
641 |
+
public static function get_login_details() {
|
642 |
$authentication_types = array();
|
643 |
|
644 |
if ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
|
675 |
}
|
676 |
|
677 |
$details = compact( 'source', 'authentication_types' );
|
|
|
678 |
|
679 |
+
return apply_filters( 'itsec-filter-failed-login-details', $details );
|
680 |
}
|
681 |
|
682 |
/**
|
1019 |
return $array;
|
1020 |
}
|
1021 |
|
1022 |
+
public static function print_r( $data, $args = array() ) {
|
1023 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/debug.php' );
|
1024 |
+
|
1025 |
+
ITSEC_Debug::print_r( $data, $args );
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
public static function get_print_r( $data, $args = array() ) {
|
1029 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/debug.php' );
|
1030 |
+
|
1031 |
+
return ITSEC_Debug::get_print_r( $data, $args );
|
1032 |
+
}
|
1033 |
+
|
1034 |
/**
|
1035 |
* Check if WP Cron appears to be running properly.
|
1036 |
*
|
core/lib/class-itsec-scheduler-cron.php
CHANGED
@@ -86,6 +86,23 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
|
|
86 |
}
|
87 |
|
88 |
public function is_single_scheduled( $id, $data = array() ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
return (bool) wp_next_scheduled( self::HOOK, array( $id, $this->hash_data( $data ) ) );
|
90 |
}
|
91 |
|
86 |
}
|
87 |
|
88 |
public function is_single_scheduled( $id, $data = array() ) {
|
89 |
+
|
90 |
+
if ( null === $data ) {
|
91 |
+
$options = $this->get_options();
|
92 |
+
|
93 |
+
if ( ! isset( $options['single'][ $id ] ) ) {
|
94 |
+
return false;
|
95 |
+
}
|
96 |
+
|
97 |
+
foreach ( $options['single'][ $id ] as $hash => $event ) {
|
98 |
+
if ( wp_next_scheduled( self::HOOK, array( $id, $hash ) ) ) {
|
99 |
+
return true;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
return false;
|
104 |
+
}
|
105 |
+
|
106 |
return (bool) wp_next_scheduled( self::HOOK, array( $id, $this->hash_data( $data ) ) );
|
107 |
}
|
108 |
|
core/lib/class-itsec-scheduler-page-load.php
CHANGED
@@ -79,15 +79,18 @@ class ITSEC_Scheduler_Page_Load extends ITSEC_Scheduler {
|
|
79 |
|
80 |
public function is_single_scheduled( $id, $data = array() ) {
|
81 |
|
82 |
-
$hash = $this->hash_data( $data );
|
83 |
$options = $this->get_options();
|
84 |
|
85 |
if ( empty( $options['single'][ $id ] ) ) {
|
86 |
return false;
|
87 |
}
|
88 |
|
89 |
-
if (
|
90 |
-
|
|
|
|
|
|
|
|
|
91 |
}
|
92 |
|
93 |
return true;
|
79 |
|
80 |
public function is_single_scheduled( $id, $data = array() ) {
|
81 |
|
|
|
82 |
$options = $this->get_options();
|
83 |
|
84 |
if ( empty( $options['single'][ $id ] ) ) {
|
85 |
return false;
|
86 |
}
|
87 |
|
88 |
+
if ( null !== $data ) {
|
89 |
+
$hash = $this->hash_data( $data );
|
90 |
+
|
91 |
+
if ( empty( $options['single'][ $id ][ $hash ] ) ) {
|
92 |
+
return false;
|
93 |
+
}
|
94 |
}
|
95 |
|
96 |
return true;
|
core/lib/class-itsec-scheduler.php
CHANGED
@@ -68,8 +68,9 @@ abstract class ITSEC_Scheduler {
|
|
68 |
/**
|
69 |
* Is a single event scheduled with the given data.
|
70 |
*
|
71 |
-
* @param string
|
72 |
-
* @param array
|
|
|
73 |
*
|
74 |
* @return bool
|
75 |
*/
|
68 |
/**
|
69 |
* Is a single event scheduled with the given data.
|
70 |
*
|
71 |
+
* @param string $id The event ID to check.
|
72 |
+
* @param array|null $data The event data. Pass null to check if any event is scheduled with that ID,
|
73 |
+
* regardless of the data.
|
74 |
*
|
75 |
* @return bool
|
76 |
*/
|
core/lib/class-itsec-wp-list-table.php
CHANGED
@@ -1,100 +1,142 @@
|
|
1 |
<?php
|
2 |
/**
|
3 |
-
*
|
4 |
*
|
5 |
-
* @package
|
6 |
-
* @
|
|
|
7 |
*/
|
8 |
|
9 |
/**
|
10 |
* Base class for displaying a list of items in an ajaxified HTML table.
|
11 |
*
|
12 |
-
* @
|
13 |
-
* @
|
14 |
-
* @since 3.1.0
|
15 |
-
* @access private
|
16 |
*/
|
17 |
class ITSEC_WP_List_Table {
|
18 |
|
19 |
/**
|
20 |
-
* The current list of items
|
21 |
*
|
22 |
-
* @since
|
23 |
* @var array
|
24 |
-
* @access protected
|
25 |
*/
|
26 |
-
|
27 |
|
28 |
/**
|
29 |
-
* Various information about the current table
|
30 |
*
|
31 |
-
* @since
|
32 |
* @var array
|
33 |
-
* @access private
|
34 |
*/
|
35 |
-
|
36 |
|
37 |
/**
|
38 |
-
* Various information needed for displaying the pagination
|
39 |
*
|
40 |
-
* @since
|
41 |
* @var array
|
42 |
-
* @access private
|
43 |
*/
|
44 |
-
|
45 |
|
46 |
/**
|
47 |
-
* The current screen
|
48 |
*
|
49 |
-
* @since
|
50 |
* @var object
|
51 |
-
* @access protected
|
52 |
*/
|
53 |
-
|
54 |
|
55 |
/**
|
56 |
-
* Cached bulk actions
|
57 |
*
|
58 |
-
* @since
|
59 |
* @var array
|
60 |
-
* @access private
|
61 |
*/
|
62 |
-
|
63 |
|
64 |
/**
|
65 |
-
* Cached pagination output
|
66 |
*
|
67 |
-
* @since
|
68 |
* @var string
|
69 |
-
* @access private
|
70 |
*/
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
|
73 |
/**
|
74 |
-
*
|
75 |
*
|
76 |
-
* @
|
|
|
|
|
|
|
|
|
|
|
77 |
*
|
78 |
-
* @
|
79 |
*/
|
80 |
-
|
|
|
|
|
|
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
$args = wp_parse_args( $args, array(
|
83 |
-
'plural'
|
84 |
'singular' => '',
|
85 |
-
'ajax'
|
86 |
-
'screen'
|
87 |
) );
|
88 |
|
89 |
$this->screen = convert_to_screen( $args['screen'] );
|
90 |
|
91 |
add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
|
92 |
|
93 |
-
if (
|
94 |
$args['plural'] = $this->screen->base;
|
95 |
-
}
|
96 |
|
97 |
-
$args['plural']
|
98 |
$args['singular'] = sanitize_key( $args['singular'] );
|
99 |
|
100 |
$this->_args = $args;
|
@@ -103,57 +145,127 @@ class ITSEC_WP_List_Table {
|
|
103 |
// wp_enqueue_script( 'list-table' );
|
104 |
add_action( 'admin_footer', array( $this, '_js_vars' ) );
|
105 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
}
|
107 |
|
108 |
/**
|
109 |
-
*
|
110 |
*
|
111 |
-
* @
|
112 |
*
|
113 |
-
* @
|
114 |
-
* @
|
115 |
-
* @abstract
|
116 |
*/
|
117 |
-
function
|
|
|
|
|
|
|
|
|
118 |
|
119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
}
|
121 |
|
122 |
/**
|
123 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
*
|
125 |
-
* @
|
126 |
*
|
127 |
-
* @
|
128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
129 |
* @abstract
|
130 |
*/
|
131 |
-
function
|
|
|
|
|
132 |
|
133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
}
|
135 |
|
136 |
/**
|
137 |
* An internal method that sets all the necessary pagination arguments
|
138 |
*
|
139 |
-
* @
|
140 |
*
|
141 |
-
* @
|
142 |
*/
|
143 |
-
function set_pagination_args( $args ) {
|
144 |
-
|
145 |
$args = wp_parse_args( $args, array(
|
146 |
'total_items' => 0,
|
147 |
'total_pages' => 0,
|
148 |
-
'per_page'
|
149 |
) );
|
150 |
|
151 |
-
if (
|
152 |
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
|
153 |
-
}
|
154 |
|
155 |
-
//
|
156 |
-
if ( ! headers_sent() &&
|
157 |
wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
|
158 |
exit;
|
159 |
}
|
@@ -162,18 +274,16 @@ class ITSEC_WP_List_Table {
|
|
162 |
}
|
163 |
|
164 |
/**
|
165 |
-
* Access the pagination args
|
166 |
*
|
167 |
-
* @since
|
168 |
-
* @access public
|
169 |
*
|
170 |
-
* @param string $key
|
171 |
-
*
|
172 |
-
* @return
|
173 |
*/
|
174 |
-
function get_pagination_arg( $key ) {
|
175 |
-
|
176 |
-
if ( 'page' == $key ) {
|
177 |
return $this->get_pagenum();
|
178 |
}
|
179 |
|
@@ -185,92 +295,77 @@ class ITSEC_WP_List_Table {
|
|
185 |
/**
|
186 |
* Whether the table has items to display or not
|
187 |
*
|
188 |
-
* @since
|
189 |
-
* @access public
|
190 |
*
|
191 |
* @return bool
|
192 |
*/
|
193 |
-
function has_items() {
|
194 |
-
|
195 |
-
return ! empty( $this->items );
|
196 |
}
|
197 |
|
198 |
/**
|
199 |
* Message to be displayed when there are no items
|
200 |
*
|
201 |
-
* @since
|
202 |
-
* @access public
|
203 |
*/
|
204 |
-
function no_items() {
|
205 |
-
|
206 |
_e( 'No items found.' );
|
207 |
}
|
208 |
|
209 |
/**
|
210 |
-
*
|
211 |
*
|
212 |
-
* @since
|
213 |
-
* @access public
|
214 |
*
|
215 |
-
* @param string $text The
|
216 |
-
* @param string $input_id
|
217 |
*/
|
218 |
-
function search_box( $text, $input_id ) {
|
219 |
-
|
220 |
-
if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
|
221 |
return;
|
222 |
-
}
|
223 |
|
224 |
$input_id = $input_id . '-search-input';
|
225 |
|
226 |
-
if ( ! empty( $_REQUEST['orderby'] ) )
|
227 |
echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
|
228 |
-
|
229 |
-
if ( ! empty( $_REQUEST['order'] ) ) {
|
230 |
echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
|
231 |
-
|
232 |
-
if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
|
233 |
echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
|
234 |
-
|
235 |
-
if ( ! empty( $_REQUEST['detached'] ) ) {
|
236 |
echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
<?php
|
245 |
}
|
246 |
|
247 |
/**
|
248 |
* Get an associative array ( id => link ) with the list
|
249 |
* of views available on this table.
|
250 |
*
|
251 |
-
* @since
|
252 |
-
* @access protected
|
253 |
*
|
254 |
* @return array
|
255 |
*/
|
256 |
-
function get_views() {
|
257 |
-
|
258 |
return array();
|
259 |
}
|
260 |
|
261 |
/**
|
262 |
* Display the list of views available on this table.
|
263 |
*
|
264 |
-
* @since
|
265 |
-
* @access public
|
266 |
*/
|
267 |
-
function views() {
|
268 |
-
|
269 |
$views = $this->get_views();
|
270 |
/**
|
271 |
-
*
|
272 |
*
|
273 |
-
* The dynamic portion of the hook name,
|
274 |
* to the ID of the current screen, usually a string.
|
275 |
*
|
276 |
* @since 3.5.0
|
@@ -279,13 +374,14 @@ class ITSEC_WP_List_Table {
|
|
279 |
*/
|
280 |
$views = apply_filters( "views_{$this->screen->id}", $views );
|
281 |
|
282 |
-
if ( empty( $views ) )
|
283 |
return;
|
284 |
-
|
|
|
285 |
|
286 |
echo "<ul class='subsubsub'>\n";
|
287 |
foreach ( $views as $class => $view ) {
|
288 |
-
$views[$class] = "\t<li class='$class'>$view";
|
289 |
}
|
290 |
echo implode( " |</li>\n", $views ) . "</li>\n";
|
291 |
echo "</ul>";
|
@@ -295,30 +391,29 @@ class ITSEC_WP_List_Table {
|
|
295 |
* Get an associative array ( option_name => option_title ) with the list
|
296 |
* of bulk actions available on this table.
|
297 |
*
|
298 |
-
* @since
|
299 |
-
* @access protected
|
300 |
*
|
301 |
* @return array
|
302 |
*/
|
303 |
-
function get_bulk_actions() {
|
304 |
-
|
305 |
return array();
|
306 |
}
|
307 |
|
308 |
/**
|
309 |
* Display the bulk actions dropdown.
|
310 |
*
|
311 |
-
* @since
|
312 |
-
*
|
|
|
|
|
313 |
*/
|
314 |
-
function bulk_actions() {
|
315 |
-
|
316 |
if ( is_null( $this->_actions ) ) {
|
317 |
-
$
|
318 |
/**
|
319 |
-
*
|
320 |
*
|
321 |
-
* The dynamic portion of the hook name,
|
322 |
* to the ID of the current screen, usually a string.
|
323 |
*
|
324 |
* This filter can currently only be used to remove bulk actions.
|
@@ -328,48 +423,46 @@ class ITSEC_WP_List_Table {
|
|
328 |
* @param array $actions An array of the available bulk actions.
|
329 |
*/
|
330 |
$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
|
331 |
-
$
|
332 |
-
$two = '';
|
333 |
} else {
|
334 |
$two = '2';
|
335 |
}
|
336 |
|
337 |
-
if ( empty( $this->_actions ) )
|
338 |
return;
|
339 |
-
}
|
340 |
|
341 |
-
echo
|
342 |
-
echo
|
|
|
343 |
|
344 |
foreach ( $this->_actions as $name => $title ) {
|
345 |
-
$class = 'edit'
|
346 |
|
347 |
-
echo "\t<option value='$name'$class
|
348 |
}
|
349 |
|
350 |
echo "</select>\n";
|
351 |
|
352 |
-
submit_button( __( 'Apply' ), 'action',
|
353 |
echo "\n";
|
354 |
}
|
355 |
|
356 |
/**
|
357 |
* Get the current action selected from the bulk actions dropdown.
|
358 |
*
|
359 |
-
* @since
|
360 |
-
* @access public
|
361 |
*
|
362 |
-
* @return string|
|
363 |
*/
|
364 |
-
function current_action() {
|
|
|
|
|
365 |
|
366 |
-
if ( isset( $_REQUEST['action'] ) && -
|
367 |
return $_REQUEST['action'];
|
368 |
-
}
|
369 |
|
370 |
-
if ( isset( $_REQUEST['action2'] ) && -
|
371 |
return $_REQUEST['action2'];
|
372 |
-
}
|
373 |
|
374 |
return false;
|
375 |
}
|
@@ -377,22 +470,18 @@ class ITSEC_WP_List_Table {
|
|
377 |
/**
|
378 |
* Generate row actions div
|
379 |
*
|
380 |
-
* @since
|
381 |
-
* @access protected
|
382 |
-
*
|
383 |
-
* @param array $actions The list of actions
|
384 |
-
* @param bool $always_visible Whether the actions should be always visible
|
385 |
*
|
|
|
|
|
386 |
* @return string
|
387 |
*/
|
388 |
-
function row_actions( $actions, $always_visible = false ) {
|
389 |
-
|
390 |
$action_count = count( $actions );
|
391 |
-
$i
|
392 |
|
393 |
-
if (
|
394 |
return '';
|
395 |
-
}
|
396 |
|
397 |
$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
|
398 |
foreach ( $actions as $action => $link ) {
|
@@ -402,28 +491,53 @@ class ITSEC_WP_List_Table {
|
|
402 |
}
|
403 |
$out .= '</div>';
|
404 |
|
|
|
|
|
405 |
return $out;
|
406 |
}
|
407 |
|
408 |
/**
|
409 |
* Display a monthly dropdown for filtering items
|
410 |
*
|
411 |
-
* @since
|
412 |
-
*
|
|
|
|
|
|
|
|
|
413 |
*/
|
414 |
-
function months_dropdown( $post_type ) {
|
415 |
-
|
416 |
global $wpdb, $wp_locale;
|
417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
$months = $wpdb->get_results( $wpdb->prepare( "
|
419 |
SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
|
420 |
FROM $wpdb->posts
|
421 |
WHERE post_type = %s
|
|
|
422 |
ORDER BY post_date DESC
|
423 |
", $post_type ) );
|
424 |
|
425 |
/**
|
426 |
-
*
|
427 |
*
|
428 |
* @since 3.7.0
|
429 |
*
|
@@ -434,100 +548,125 @@ class ITSEC_WP_List_Table {
|
|
434 |
|
435 |
$month_count = count( $months );
|
436 |
|
437 |
-
if (
|
438 |
return;
|
439 |
-
}
|
440 |
|
441 |
$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
|
442 |
-
|
443 |
-
<
|
444 |
-
|
445 |
-
<?php
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
|
451 |
-
|
452 |
-
|
453 |
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
</select>
|
463 |
-
|
464 |
}
|
465 |
|
466 |
/**
|
467 |
* Display a view switcher
|
468 |
*
|
469 |
-
* @since
|
470 |
-
*
|
|
|
471 |
*/
|
472 |
-
function view_switcher( $current_mode ) {
|
473 |
-
|
474 |
-
|
475 |
-
'list' => __( 'List View' ),
|
476 |
-
'excerpt' => __( 'Excerpt View' )
|
477 |
-
);
|
478 |
-
|
479 |
-
?>
|
480 |
-
<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>"/>
|
481 |
<div class="view-switch">
|
482 |
-
|
483 |
-
foreach ( $modes as $mode => $title ) {
|
484 |
-
$
|
485 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
486 |
}
|
487 |
-
|
488 |
</div>
|
489 |
-
|
490 |
}
|
491 |
|
492 |
/**
|
493 |
* Display a comment count bubble
|
494 |
*
|
495 |
-
* @since
|
496 |
-
* @access protected
|
497 |
*
|
498 |
-
* @param int $post_id
|
499 |
-
* @param int $pending_comments
|
500 |
*/
|
501 |
-
function comments_bubble( $post_id, $pending_comments ) {
|
|
|
502 |
|
503 |
-
$
|
|
|
504 |
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
|
509 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
510 |
|
511 |
if ( $pending_comments ) {
|
512 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
}
|
514 |
}
|
515 |
|
516 |
/**
|
517 |
* Get the current page number
|
518 |
*
|
519 |
-
* @since
|
520 |
-
* @access protected
|
521 |
*
|
522 |
* @return int
|
523 |
*/
|
524 |
-
function get_pagenum() {
|
525 |
-
|
526 |
$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
|
527 |
|
528 |
-
if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
|
529 |
$pagenum = $this->_pagination_args['total_pages'];
|
530 |
-
}
|
531 |
|
532 |
return max( 1, $pagenum );
|
533 |
}
|
@@ -535,110 +674,144 @@ class ITSEC_WP_List_Table {
|
|
535 |
/**
|
536 |
* Get number of items to display on a single page
|
537 |
*
|
538 |
-
* @since
|
539 |
-
* @access protected
|
540 |
*
|
|
|
|
|
541 |
* @return int
|
542 |
*/
|
543 |
-
function get_items_per_page( $option, $default = 20 ) {
|
544 |
-
|
545 |
$per_page = (int) get_user_option( $option );
|
546 |
-
if ( empty( $per_page ) || $per_page < 1 )
|
547 |
$per_page = $default;
|
548 |
-
}
|
549 |
|
550 |
/**
|
551 |
-
*
|
552 |
*
|
553 |
-
* The dynamic hook name, $option, refers to the
|
554 |
-
* on the type of list table in use. Possible values
|
555 |
-
* '
|
556 |
-
* '
|
|
|
557 |
*
|
558 |
* @since 2.9.0
|
559 |
*
|
560 |
* @param int $per_page Number of items to be displayed. Default 20.
|
561 |
*/
|
562 |
-
|
563 |
-
return (int) apply_filters( $option, $per_page );
|
564 |
}
|
565 |
|
566 |
/**
|
567 |
* Display the pagination.
|
568 |
*
|
569 |
-
* @since
|
570 |
-
*
|
|
|
571 |
*/
|
572 |
-
function pagination( $which ) {
|
573 |
-
|
574 |
if ( empty( $this->_pagination_args ) ) {
|
575 |
return;
|
576 |
}
|
577 |
|
578 |
-
|
|
|
|
|
|
|
|
|
|
|
579 |
|
580 |
-
|
|
|
|
|
|
|
|
|
581 |
|
582 |
$current = $this->get_pagenum();
|
|
|
|
|
583 |
|
584 |
$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
|
585 |
|
586 |
-
$current_url = remove_query_arg(
|
587 |
|
588 |
$page_links = array();
|
589 |
|
590 |
-
$
|
591 |
-
|
592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
593 |
}
|
594 |
-
|
595 |
-
$disable_last =
|
|
|
|
|
|
|
|
|
596 |
}
|
597 |
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
esc_attr__( 'Go to the previous page' ),
|
608 |
-
esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
|
609 |
-
'‹'
|
610 |
-
);
|
611 |
|
612 |
-
if (
|
613 |
-
$
|
614 |
} else {
|
615 |
-
$
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
);
|
620 |
}
|
621 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
622 |
$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
|
623 |
-
$page_links[]
|
624 |
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
|
|
|
|
|
|
631 |
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
|
|
|
|
|
|
638 |
|
639 |
$pagination_links_class = 'pagination-links';
|
640 |
if ( ! empty( $infinite_scroll ) ) {
|
641 |
-
$pagination_links_class
|
642 |
}
|
643 |
$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
|
644 |
|
@@ -647,7 +820,6 @@ class ITSEC_WP_List_Table {
|
|
647 |
} else {
|
648 |
$page_class = ' no-pages';
|
649 |
}
|
650 |
-
|
651 |
$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
|
652 |
|
653 |
echo $this->_pagination;
|
@@ -657,15 +829,13 @@ class ITSEC_WP_List_Table {
|
|
657 |
* Get a list of columns. The format is:
|
658 |
* 'internal-name' => 'Title'
|
659 |
*
|
660 |
-
* @since
|
661 |
-
* @access protected
|
662 |
* @abstract
|
663 |
*
|
664 |
* @return array
|
665 |
*/
|
666 |
-
function get_columns() {
|
667 |
-
|
668 |
-
die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
|
669 |
}
|
670 |
|
671 |
/**
|
@@ -676,38 +846,116 @@ class ITSEC_WP_List_Table {
|
|
676 |
*
|
677 |
* The second format will make the initial sorting order be descending
|
678 |
*
|
679 |
-
* @since
|
680 |
-
* @access protected
|
681 |
*
|
682 |
* @return array
|
683 |
*/
|
684 |
-
function get_sortable_columns() {
|
685 |
-
|
686 |
return array();
|
687 |
}
|
688 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
689 |
/**
|
690 |
* Get a list of all, hidden and sortable columns, with filter applied
|
691 |
*
|
692 |
-
* @since
|
693 |
-
* @access protected
|
694 |
*
|
695 |
* @return array
|
696 |
*/
|
697 |
-
function get_column_info() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
698 |
|
699 |
-
|
700 |
-
return $this->_column_headers;
|
701 |
}
|
702 |
|
703 |
$columns = get_column_headers( $this->screen );
|
704 |
-
$hidden
|
705 |
|
706 |
$sortable_columns = $this->get_sortable_columns();
|
707 |
/**
|
708 |
-
*
|
709 |
*
|
710 |
-
* The dynamic portion of the hook name,
|
711 |
* to the ID of the current screen, usually a string.
|
712 |
*
|
713 |
* @since 3.5.0
|
@@ -718,19 +966,18 @@ class ITSEC_WP_List_Table {
|
|
718 |
|
719 |
$sortable = array();
|
720 |
foreach ( $_sortable as $id => $data ) {
|
721 |
-
if ( empty( $data ) )
|
722 |
continue;
|
723 |
-
}
|
724 |
|
725 |
$data = (array) $data;
|
726 |
-
if ( !
|
727 |
$data[1] = false;
|
728 |
-
}
|
729 |
|
730 |
$sortable[$id] = $data;
|
731 |
}
|
732 |
|
733 |
-
$
|
|
|
734 |
|
735 |
return $this->_column_headers;
|
736 |
}
|
@@ -738,30 +985,27 @@ class ITSEC_WP_List_Table {
|
|
738 |
/**
|
739 |
* Return number of visible columns
|
740 |
*
|
741 |
-
* @since
|
742 |
-
* @access public
|
743 |
*
|
744 |
* @return int
|
745 |
*/
|
746 |
-
function get_column_count() {
|
747 |
-
|
748 |
list ( $columns, $hidden ) = $this->get_column_info();
|
749 |
$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
|
750 |
-
|
751 |
return count( $columns ) - count( $hidden );
|
752 |
}
|
753 |
|
754 |
/**
|
755 |
* Print column headers, accounting for hidden and sortable columns.
|
756 |
*
|
757 |
-
* @since
|
758 |
-
*
|
|
|
759 |
*
|
760 |
* @param bool $with_id Whether to set the id attribute or not
|
761 |
*/
|
762 |
-
function print_column_headers( $with_id = true ) {
|
763 |
-
|
764 |
-
list( $columns, $hidden, $sortable ) = $this->get_column_info();
|
765 |
|
766 |
$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
|
767 |
$current_url = remove_query_arg( 'paged', $current_url );
|
@@ -772,7 +1016,7 @@ class ITSEC_WP_List_Table {
|
|
772 |
$current_orderby = '';
|
773 |
}
|
774 |
|
775 |
-
if ( isset( $_GET['order'] ) && 'desc'
|
776 |
$current_order = 'desc';
|
777 |
} else {
|
778 |
$current_order = 'asc';
|
@@ -781,35 +1025,35 @@ class ITSEC_WP_List_Table {
|
|
781 |
if ( ! empty( $columns['cb'] ) ) {
|
782 |
static $cb_counter = 1;
|
783 |
$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
|
784 |
-
|
785 |
-
$cb_counter
|
786 |
}
|
787 |
|
788 |
foreach ( $columns as $column_key => $column_display_name ) {
|
789 |
$class = array( 'manage-column', "column-$column_key" );
|
790 |
|
791 |
-
$style = '';
|
792 |
if ( in_array( $column_key, $hidden ) ) {
|
793 |
-
$
|
794 |
}
|
795 |
|
796 |
-
|
797 |
-
|
798 |
-
if ( 'cb' == $column_key ) {
|
799 |
$class[] = 'check-column';
|
800 |
-
|
801 |
$class[] = 'num';
|
|
|
|
|
|
|
802 |
}
|
803 |
|
804 |
if ( isset( $sortable[$column_key] ) ) {
|
805 |
list( $orderby, $desc_first ) = $sortable[$column_key];
|
806 |
|
807 |
-
if ( $current_orderby
|
808 |
-
$order
|
809 |
$class[] = 'sorted';
|
810 |
$class[] = $current_order;
|
811 |
} else {
|
812 |
-
$order
|
813 |
$class[] = 'sortable';
|
814 |
$class[] = $desc_first ? 'asc' : 'desc';
|
815 |
}
|
@@ -817,113 +1061,110 @@ class ITSEC_WP_List_Table {
|
|
817 |
$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
|
818 |
}
|
819 |
|
|
|
|
|
820 |
$id = $with_id ? "id='$column_key'" : '';
|
821 |
|
822 |
-
if ( !
|
823 |
$class = "class='" . join( ' ', $class ) . "'";
|
824 |
-
}
|
825 |
|
826 |
-
echo "
|
827 |
}
|
828 |
}
|
829 |
|
830 |
/**
|
831 |
* Display the table
|
832 |
*
|
833 |
-
* @since
|
834 |
-
* @access public
|
835 |
*/
|
836 |
-
function display() {
|
837 |
-
|
838 |
-
extract( $this->_args );
|
839 |
|
840 |
$this->display_tablenav( 'top' );
|
841 |
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
|
|
|
|
|
|
863 |
$this->display_tablenav( 'bottom' );
|
864 |
}
|
865 |
|
866 |
/**
|
867 |
-
* Get a list of CSS classes for the
|
868 |
*
|
869 |
-
* @since
|
870 |
-
* @access protected
|
871 |
*
|
872 |
-
* @return array
|
873 |
*/
|
874 |
-
function get_table_classes() {
|
875 |
-
|
876 |
-
return array( 'widefat', 'fixed', $this->_args['plural'] );
|
877 |
}
|
878 |
|
879 |
/**
|
880 |
* Generate the table navigation above or below the table
|
881 |
*
|
882 |
-
* @since
|
883 |
-
* @
|
884 |
*/
|
885 |
-
function display_tablenav( $which ) {
|
886 |
-
|
887 |
-
if ( 'top' == $which ) {
|
888 |
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
|
889 |
}
|
890 |
?>
|
891 |
-
|
892 |
-
|
893 |
-
<div class="alignleft actions bulkactions">
|
894 |
-
<?php $this->bulk_actions(); ?>
|
895 |
-
</div>
|
896 |
-
<?php
|
897 |
-
$this->extra_tablenav( $which );
|
898 |
-
$this->pagination( $which );
|
899 |
-
?>
|
900 |
|
901 |
-
|
|
|
|
|
902 |
</div>
|
903 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
904 |
}
|
905 |
|
906 |
/**
|
907 |
* Extra controls to be displayed between bulk actions and pagination
|
908 |
*
|
909 |
-
* @since
|
910 |
-
*
|
|
|
911 |
*/
|
912 |
-
function extra_tablenav( $which ) {
|
913 |
-
}
|
914 |
|
915 |
/**
|
916 |
-
* Generate the
|
917 |
*
|
918 |
-
* @since
|
919 |
-
* @access protected
|
920 |
*/
|
921 |
-
function display_rows_or_placeholder() {
|
922 |
-
|
923 |
if ( $this->has_items() ) {
|
924 |
$this->display_rows();
|
925 |
} else {
|
926 |
-
list( $columns, $hidden ) = $this->get_column_info();
|
927 |
echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
|
928 |
$this->no_items();
|
929 |
echo '</td></tr>';
|
@@ -933,85 +1174,113 @@ class ITSEC_WP_List_Table {
|
|
933 |
/**
|
934 |
* Generate the table rows
|
935 |
*
|
936 |
-
* @since
|
937 |
-
* @access protected
|
938 |
*/
|
939 |
-
function display_rows() {
|
940 |
-
|
941 |
-
foreach ( $this->items as $item ) {
|
942 |
$this->single_row( $item );
|
943 |
-
}
|
944 |
}
|
945 |
|
946 |
/**
|
947 |
* Generates content for a single row of the table
|
948 |
*
|
949 |
-
* @since
|
950 |
-
* @access protected
|
951 |
*
|
952 |
* @param object $item The current item
|
953 |
*/
|
954 |
-
function single_row( $item ) {
|
955 |
-
|
956 |
-
static $row_class = '';
|
957 |
-
$row_class = ( $row_class == '' ? ' class="alternate"' : '' );
|
958 |
-
|
959 |
-
echo '<tr' . $row_class . '>';
|
960 |
$this->single_row_columns( $item );
|
961 |
echo '</tr>';
|
962 |
}
|
963 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
964 |
/**
|
965 |
* Generates the columns for a single row of the table
|
966 |
*
|
967 |
-
* @since
|
968 |
-
* @access protected
|
969 |
*
|
970 |
* @param object $item The current item
|
971 |
*/
|
972 |
-
function single_row_columns( $item ) {
|
973 |
-
|
974 |
-
list( $columns, $hidden ) = $this->get_column_info();
|
975 |
|
976 |
foreach ( $columns as $column_name => $column_display_name ) {
|
977 |
-
$
|
|
|
|
|
|
|
978 |
|
979 |
-
$style = '';
|
980 |
if ( in_array( $column_name, $hidden ) ) {
|
981 |
-
$
|
982 |
}
|
983 |
|
984 |
-
|
|
|
|
|
|
|
|
|
985 |
|
986 |
-
if ( 'cb'
|
987 |
echo '<th scope="row" class="check-column">';
|
988 |
echo $this->column_cb( $item );
|
989 |
echo '</th>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
990 |
} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
|
991 |
echo "<td $attributes>";
|
992 |
echo call_user_func( array( $this, 'column_' . $column_name ), $item );
|
|
|
993 |
echo "</td>";
|
994 |
} else {
|
995 |
echo "<td $attributes>";
|
996 |
echo $this->column_default( $item, $column_name );
|
|
|
997 |
echo "</td>";
|
998 |
}
|
999 |
}
|
1000 |
}
|
1001 |
|
1002 |
/**
|
1003 |
-
*
|
1004 |
*
|
1005 |
-
* @since
|
1006 |
-
*
|
|
|
|
|
|
|
|
|
1007 |
*/
|
1008 |
-
function
|
|
|
|
|
1009 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1010 |
$this->prepare_items();
|
1011 |
|
1012 |
-
extract( $this->_args );
|
1013 |
-
extract( $this->_pagination_args, EXTR_SKIP );
|
1014 |
-
|
1015 |
ob_start();
|
1016 |
if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
|
1017 |
$this->display_rows();
|
@@ -1023,25 +1292,25 @@ class ITSEC_WP_List_Table {
|
|
1023 |
|
1024 |
$response = array( 'rows' => $rows );
|
1025 |
|
1026 |
-
if ( isset( $total_items ) ) {
|
1027 |
-
$response['total_items_i18n'] = sprintf(
|
|
|
|
|
|
|
1028 |
}
|
1029 |
-
|
1030 |
-
|
1031 |
-
$response['
|
1032 |
-
$response['total_pages_i18n'] = number_format_i18n( $total_pages );
|
1033 |
}
|
1034 |
|
1035 |
-
die(
|
1036 |
}
|
1037 |
|
1038 |
/**
|
1039 |
* Send required variables to JavaScript land
|
1040 |
*
|
1041 |
-
* @access private
|
1042 |
*/
|
1043 |
-
function _js_vars() {
|
1044 |
-
|
1045 |
$args = array(
|
1046 |
'class' => get_class( $this ),
|
1047 |
'screen' => array(
|
@@ -1050,6 +1319,6 @@ class ITSEC_WP_List_Table {
|
|
1050 |
)
|
1051 |
);
|
1052 |
|
1053 |
-
printf( "<script type='text/javascript'>list_args = %s;</script>\n",
|
1054 |
}
|
1055 |
}
|
1 |
<?php
|
2 |
/**
|
3 |
+
* Administration API: ITSEC_WP_List_Table class
|
4 |
*
|
5 |
+
* @package WordPress
|
6 |
+
* @subpackage List_Table
|
7 |
+
* @since 3.1.0
|
8 |
*/
|
9 |
|
10 |
/**
|
11 |
* Base class for displaying a list of items in an ajaxified HTML table.
|
12 |
*
|
13 |
+
* @since 3.1.0
|
14 |
+
* @access private
|
|
|
|
|
15 |
*/
|
16 |
class ITSEC_WP_List_Table {
|
17 |
|
18 |
/**
|
19 |
+
* The current list of items.
|
20 |
*
|
21 |
+
* @since 3.1.0
|
22 |
* @var array
|
|
|
23 |
*/
|
24 |
+
public $items;
|
25 |
|
26 |
/**
|
27 |
+
* Various information about the current table.
|
28 |
*
|
29 |
+
* @since 3.1.0
|
30 |
* @var array
|
|
|
31 |
*/
|
32 |
+
protected $_args;
|
33 |
|
34 |
/**
|
35 |
+
* Various information needed for displaying the pagination.
|
36 |
*
|
37 |
+
* @since 3.1.0
|
38 |
* @var array
|
|
|
39 |
*/
|
40 |
+
protected $_pagination_args = array();
|
41 |
|
42 |
/**
|
43 |
+
* The current screen.
|
44 |
*
|
45 |
+
* @since 3.1.0
|
46 |
* @var object
|
|
|
47 |
*/
|
48 |
+
protected $screen;
|
49 |
|
50 |
/**
|
51 |
+
* Cached bulk actions.
|
52 |
*
|
53 |
+
* @since 3.1.0
|
54 |
* @var array
|
|
|
55 |
*/
|
56 |
+
private $_actions;
|
57 |
|
58 |
/**
|
59 |
+
* Cached pagination output.
|
60 |
*
|
61 |
+
* @since 3.1.0
|
62 |
* @var string
|
|
|
63 |
*/
|
64 |
+
private $_pagination;
|
65 |
+
|
66 |
+
/**
|
67 |
+
* The view switcher modes.
|
68 |
+
*
|
69 |
+
* @since 4.1.0
|
70 |
+
* @var array
|
71 |
+
*/
|
72 |
+
protected $modes = array();
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Stores the value returned by ->get_column_info().
|
76 |
+
*
|
77 |
+
* @since 4.1.0
|
78 |
+
* @var array
|
79 |
+
*/
|
80 |
+
protected $_column_headers;
|
81 |
|
82 |
/**
|
83 |
+
* {@internal Missing Summary}
|
84 |
*
|
85 |
+
* @var array
|
86 |
+
*/
|
87 |
+
protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
|
88 |
+
|
89 |
+
/**
|
90 |
+
* {@internal Missing Summary}
|
91 |
*
|
92 |
+
* @var array
|
93 |
*/
|
94 |
+
protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
|
95 |
+
'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
|
96 |
+
'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
|
97 |
+
'single_row_columns' );
|
98 |
|
99 |
+
/**
|
100 |
+
* Constructor.
|
101 |
+
*
|
102 |
+
* The child class should call this constructor from its own constructor to override
|
103 |
+
* the default $args.
|
104 |
+
*
|
105 |
+
* @since 3.1.0
|
106 |
+
*
|
107 |
+
* @param array|string $args {
|
108 |
+
* Array or string of arguments.
|
109 |
+
*
|
110 |
+
* @type string $plural Plural value used for labels and the objects being listed.
|
111 |
+
* This affects things such as CSS class-names and nonces used
|
112 |
+
* in the list table, e.g. 'posts'. Default empty.
|
113 |
+
* @type string $singular Singular label for an object being listed, e.g. 'post'.
|
114 |
+
* Default empty
|
115 |
+
* @type bool $ajax Whether the list table supports Ajax. This includes loading
|
116 |
+
* and sorting data, for example. If true, the class will call
|
117 |
+
* the _js_vars() method in the footer to provide variables
|
118 |
+
* to any scripts handling Ajax events. Default false.
|
119 |
+
* @type string $screen String containing the hook name used to determine the current
|
120 |
+
* screen. If left null, the current screen will be automatically set.
|
121 |
+
* Default null.
|
122 |
+
* }
|
123 |
+
*/
|
124 |
+
public function __construct( $args = array() ) {
|
125 |
$args = wp_parse_args( $args, array(
|
126 |
+
'plural' => '',
|
127 |
'singular' => '',
|
128 |
+
'ajax' => false,
|
129 |
+
'screen' => null,
|
130 |
) );
|
131 |
|
132 |
$this->screen = convert_to_screen( $args['screen'] );
|
133 |
|
134 |
add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
|
135 |
|
136 |
+
if ( !$args['plural'] )
|
137 |
$args['plural'] = $this->screen->base;
|
|
|
138 |
|
139 |
+
$args['plural'] = sanitize_key( $args['plural'] );
|
140 |
$args['singular'] = sanitize_key( $args['singular'] );
|
141 |
|
142 |
$this->_args = $args;
|
145 |
// wp_enqueue_script( 'list-table' );
|
146 |
add_action( 'admin_footer', array( $this, '_js_vars' ) );
|
147 |
}
|
148 |
+
|
149 |
+
if ( empty( $this->modes ) ) {
|
150 |
+
$this->modes = array(
|
151 |
+
'list' => __( 'List View' ),
|
152 |
+
'excerpt' => __( 'Excerpt View' )
|
153 |
+
);
|
154 |
+
}
|
155 |
}
|
156 |
|
157 |
/**
|
158 |
+
* Make private properties readable for backward compatibility.
|
159 |
*
|
160 |
+
* @since 4.0.0
|
161 |
*
|
162 |
+
* @param string $name Property to get.
|
163 |
+
* @return mixed Property.
|
|
|
164 |
*/
|
165 |
+
public function __get( $name ) {
|
166 |
+
if ( in_array( $name, $this->compat_fields ) ) {
|
167 |
+
return $this->$name;
|
168 |
+
}
|
169 |
+
}
|
170 |
|
171 |
+
/**
|
172 |
+
* Make private properties settable for backward compatibility.
|
173 |
+
*
|
174 |
+
* @since 4.0.0
|
175 |
+
*
|
176 |
+
* @param string $name Property to check if set.
|
177 |
+
* @param mixed $value Property value.
|
178 |
+
* @return mixed Newly-set property.
|
179 |
+
*/
|
180 |
+
public function __set( $name, $value ) {
|
181 |
+
if ( in_array( $name, $this->compat_fields ) ) {
|
182 |
+
return $this->$name = $value;
|
183 |
+
}
|
184 |
}
|
185 |
|
186 |
/**
|
187 |
+
* Make private properties checkable for backward compatibility.
|
188 |
+
*
|
189 |
+
* @since 4.0.0
|
190 |
+
*
|
191 |
+
* @param string $name Property to check if set.
|
192 |
+
* @return bool Whether the property is set.
|
193 |
+
*/
|
194 |
+
public function __isset( $name ) {
|
195 |
+
if ( in_array( $name, $this->compat_fields ) ) {
|
196 |
+
return isset( $this->$name );
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Make private properties un-settable for backward compatibility.
|
202 |
*
|
203 |
+
* @since 4.0.0
|
204 |
*
|
205 |
+
* @param string $name Property to unset.
|
206 |
+
*/
|
207 |
+
public function __unset( $name ) {
|
208 |
+
if ( in_array( $name, $this->compat_fields ) ) {
|
209 |
+
unset( $this->$name );
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Make private/protected methods readable for backward compatibility.
|
215 |
+
*
|
216 |
+
* @since 4.0.0
|
217 |
+
*
|
218 |
+
* @param callable $name Method to call.
|
219 |
+
* @param array $arguments Arguments to pass when calling.
|
220 |
+
* @return mixed|bool Return value of the callback, false otherwise.
|
221 |
+
*/
|
222 |
+
public function __call( $name, $arguments ) {
|
223 |
+
if ( in_array( $name, $this->compat_methods ) ) {
|
224 |
+
return call_user_func_array( array( $this, $name ), $arguments );
|
225 |
+
}
|
226 |
+
return false;
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* Checks the current user's permissions
|
231 |
+
*
|
232 |
+
* @since 3.1.0
|
233 |
* @abstract
|
234 |
*/
|
235 |
+
public function ajax_user_can() {
|
236 |
+
die( 'function ITSEC_WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
|
237 |
+
}
|
238 |
|
239 |
+
/**
|
240 |
+
* Prepares the list of items for displaying.
|
241 |
+
* @uses ITSEC_WP_List_Table::set_pagination_args()
|
242 |
+
*
|
243 |
+
* @since 3.1.0
|
244 |
+
* @abstract
|
245 |
+
*/
|
246 |
+
public function prepare_items() {
|
247 |
+
die( 'function ITSEC_WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
|
248 |
}
|
249 |
|
250 |
/**
|
251 |
* An internal method that sets all the necessary pagination arguments
|
252 |
*
|
253 |
+
* @since 3.1.0
|
254 |
*
|
255 |
+
* @param array|string $args Array or string of arguments with information about the pagination.
|
256 |
*/
|
257 |
+
protected function set_pagination_args( $args ) {
|
|
|
258 |
$args = wp_parse_args( $args, array(
|
259 |
'total_items' => 0,
|
260 |
'total_pages' => 0,
|
261 |
+
'per_page' => 0,
|
262 |
) );
|
263 |
|
264 |
+
if ( !$args['total_pages'] && $args['per_page'] > 0 )
|
265 |
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
|
|
|
266 |
|
267 |
+
// Redirect if page number is invalid and headers are not already sent.
|
268 |
+
if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
|
269 |
wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
|
270 |
exit;
|
271 |
}
|
274 |
}
|
275 |
|
276 |
/**
|
277 |
+
* Access the pagination args.
|
278 |
*
|
279 |
+
* @since 3.1.0
|
|
|
280 |
*
|
281 |
+
* @param string $key Pagination argument to retrieve. Common values include 'total_items',
|
282 |
+
* 'total_pages', 'per_page', or 'infinite_scroll'.
|
283 |
+
* @return int Number of items that correspond to the given pagination argument.
|
284 |
*/
|
285 |
+
public function get_pagination_arg( $key ) {
|
286 |
+
if ( 'page' === $key ) {
|
|
|
287 |
return $this->get_pagenum();
|
288 |
}
|
289 |
|
295 |
/**
|
296 |
* Whether the table has items to display or not
|
297 |
*
|
298 |
+
* @since 3.1.0
|
|
|
299 |
*
|
300 |
* @return bool
|
301 |
*/
|
302 |
+
public function has_items() {
|
303 |
+
return !empty( $this->items );
|
|
|
304 |
}
|
305 |
|
306 |
/**
|
307 |
* Message to be displayed when there are no items
|
308 |
*
|
309 |
+
* @since 3.1.0
|
|
|
310 |
*/
|
311 |
+
public function no_items() {
|
|
|
312 |
_e( 'No items found.' );
|
313 |
}
|
314 |
|
315 |
/**
|
316 |
+
* Displays the search box.
|
317 |
*
|
318 |
+
* @since 3.1.0
|
|
|
319 |
*
|
320 |
+
* @param string $text The 'submit' button label.
|
321 |
+
* @param string $input_id ID attribute value for the search input field.
|
322 |
*/
|
323 |
+
public function search_box( $text, $input_id ) {
|
324 |
+
if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
|
|
|
325 |
return;
|
|
|
326 |
|
327 |
$input_id = $input_id . '-search-input';
|
328 |
|
329 |
+
if ( ! empty( $_REQUEST['orderby'] ) )
|
330 |
echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
|
331 |
+
if ( ! empty( $_REQUEST['order'] ) )
|
|
|
332 |
echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
|
333 |
+
if ( ! empty( $_REQUEST['post_mime_type'] ) )
|
|
|
334 |
echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
|
335 |
+
if ( ! empty( $_REQUEST['detached'] ) )
|
|
|
336 |
echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
|
337 |
+
?>
|
338 |
+
<p class="search-box">
|
339 |
+
<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
|
340 |
+
<input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
|
341 |
+
<?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
|
342 |
+
</p>
|
343 |
+
<?php
|
|
|
344 |
}
|
345 |
|
346 |
/**
|
347 |
* Get an associative array ( id => link ) with the list
|
348 |
* of views available on this table.
|
349 |
*
|
350 |
+
* @since 3.1.0
|
|
|
351 |
*
|
352 |
* @return array
|
353 |
*/
|
354 |
+
protected function get_views() {
|
|
|
355 |
return array();
|
356 |
}
|
357 |
|
358 |
/**
|
359 |
* Display the list of views available on this table.
|
360 |
*
|
361 |
+
* @since 3.1.0
|
|
|
362 |
*/
|
363 |
+
public function views() {
|
|
|
364 |
$views = $this->get_views();
|
365 |
/**
|
366 |
+
* Filters the list of available list table views.
|
367 |
*
|
368 |
+
* The dynamic portion of the hook name, `$this->screen->id`, refers
|
369 |
* to the ID of the current screen, usually a string.
|
370 |
*
|
371 |
* @since 3.5.0
|
374 |
*/
|
375 |
$views = apply_filters( "views_{$this->screen->id}", $views );
|
376 |
|
377 |
+
if ( empty( $views ) )
|
378 |
return;
|
379 |
+
|
380 |
+
$this->screen->render_screen_reader_content( 'heading_views' );
|
381 |
|
382 |
echo "<ul class='subsubsub'>\n";
|
383 |
foreach ( $views as $class => $view ) {
|
384 |
+
$views[ $class ] = "\t<li class='$class'>$view";
|
385 |
}
|
386 |
echo implode( " |</li>\n", $views ) . "</li>\n";
|
387 |
echo "</ul>";
|
391 |
* Get an associative array ( option_name => option_title ) with the list
|
392 |
* of bulk actions available on this table.
|
393 |
*
|
394 |
+
* @since 3.1.0
|
|
|
395 |
*
|
396 |
* @return array
|
397 |
*/
|
398 |
+
protected function get_bulk_actions() {
|
|
|
399 |
return array();
|
400 |
}
|
401 |
|
402 |
/**
|
403 |
* Display the bulk actions dropdown.
|
404 |
*
|
405 |
+
* @since 3.1.0
|
406 |
+
*
|
407 |
+
* @param string $which The location of the bulk actions: 'top' or 'bottom'.
|
408 |
+
* This is designated as optional for backward compatibility.
|
409 |
*/
|
410 |
+
protected function bulk_actions( $which = '' ) {
|
|
|
411 |
if ( is_null( $this->_actions ) ) {
|
412 |
+
$this->_actions = $this->get_bulk_actions();
|
413 |
/**
|
414 |
+
* Filters the list table Bulk Actions drop-down.
|
415 |
*
|
416 |
+
* The dynamic portion of the hook name, `$this->screen->id`, refers
|
417 |
* to the ID of the current screen, usually a string.
|
418 |
*
|
419 |
* This filter can currently only be used to remove bulk actions.
|
423 |
* @param array $actions An array of the available bulk actions.
|
424 |
*/
|
425 |
$this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
|
426 |
+
$two = '';
|
|
|
427 |
} else {
|
428 |
$two = '2';
|
429 |
}
|
430 |
|
431 |
+
if ( empty( $this->_actions ) )
|
432 |
return;
|
|
|
433 |
|
434 |
+
echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
|
435 |
+
echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
|
436 |
+
echo '<option value="-1">' . __( 'Bulk Actions' ) . "</option>\n";
|
437 |
|
438 |
foreach ( $this->_actions as $name => $title ) {
|
439 |
+
$class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
|
440 |
|
441 |
+
echo "\t" . '<option value="' . $name . '"' . $class . '>' . $title . "</option>\n";
|
442 |
}
|
443 |
|
444 |
echo "</select>\n";
|
445 |
|
446 |
+
submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
|
447 |
echo "\n";
|
448 |
}
|
449 |
|
450 |
/**
|
451 |
* Get the current action selected from the bulk actions dropdown.
|
452 |
*
|
453 |
+
* @since 3.1.0
|
|
|
454 |
*
|
455 |
+
* @return string|false The action name or False if no action was selected
|
456 |
*/
|
457 |
+
public function current_action() {
|
458 |
+
if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) )
|
459 |
+
return false;
|
460 |
|
461 |
+
if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
|
462 |
return $_REQUEST['action'];
|
|
|
463 |
|
464 |
+
if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
|
465 |
return $_REQUEST['action2'];
|
|
|
466 |
|
467 |
return false;
|
468 |
}
|
470 |
/**
|
471 |
* Generate row actions div
|
472 |
*
|
473 |
+
* @since 3.1.0
|
|
|
|
|
|
|
|
|
474 |
*
|
475 |
+
* @param array $actions The list of actions
|
476 |
+
* @param bool $always_visible Whether the actions should be always visible
|
477 |
* @return string
|
478 |
*/
|
479 |
+
protected function row_actions( $actions, $always_visible = false ) {
|
|
|
480 |
$action_count = count( $actions );
|
481 |
+
$i = 0;
|
482 |
|
483 |
+
if ( !$action_count )
|
484 |
return '';
|
|
|
485 |
|
486 |
$out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
|
487 |
foreach ( $actions as $action => $link ) {
|
491 |
}
|
492 |
$out .= '</div>';
|
493 |
|
494 |
+
$out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
|
495 |
+
|
496 |
return $out;
|
497 |
}
|
498 |
|
499 |
/**
|
500 |
* Display a monthly dropdown for filtering items
|
501 |
*
|
502 |
+
* @since 3.1.0
|
503 |
+
*
|
504 |
+
* @global wpdb $wpdb
|
505 |
+
* @global WP_Locale $wp_locale
|
506 |
+
*
|
507 |
+
* @param string $post_type
|
508 |
*/
|
509 |
+
protected function months_dropdown( $post_type ) {
|
|
|
510 |
global $wpdb, $wp_locale;
|
511 |
|
512 |
+
/**
|
513 |
+
* Filters whether to remove the 'Months' drop-down from the post list table.
|
514 |
+
*
|
515 |
+
* @since 4.2.0
|
516 |
+
*
|
517 |
+
* @param bool $disable Whether to disable the drop-down. Default false.
|
518 |
+
* @param string $post_type The post type.
|
519 |
+
*/
|
520 |
+
if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
|
521 |
+
return;
|
522 |
+
}
|
523 |
+
|
524 |
+
$extra_checks = "AND post_status != 'auto-draft'";
|
525 |
+
if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
|
526 |
+
$extra_checks .= " AND post_status != 'trash'";
|
527 |
+
} elseif ( isset( $_GET['post_status'] ) ) {
|
528 |
+
$extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
|
529 |
+
}
|
530 |
+
|
531 |
$months = $wpdb->get_results( $wpdb->prepare( "
|
532 |
SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
|
533 |
FROM $wpdb->posts
|
534 |
WHERE post_type = %s
|
535 |
+
$extra_checks
|
536 |
ORDER BY post_date DESC
|
537 |
", $post_type ) );
|
538 |
|
539 |
/**
|
540 |
+
* Filters the 'Months' drop-down results.
|
541 |
*
|
542 |
* @since 3.7.0
|
543 |
*
|
548 |
|
549 |
$month_count = count( $months );
|
550 |
|
551 |
+
if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
|
552 |
return;
|
|
|
553 |
|
554 |
$m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
|
555 |
+
?>
|
556 |
+
<label for="filter-by-date" class="screen-reader-text"><?php _e( 'Filter by date' ); ?></label>
|
557 |
+
<select name="m" id="filter-by-date">
|
558 |
+
<option<?php selected( $m, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
|
559 |
+
<?php
|
560 |
+
foreach ( $months as $arc_row ) {
|
561 |
+
if ( 0 == $arc_row->year )
|
562 |
+
continue;
|
563 |
|
564 |
+
$month = zeroise( $arc_row->month, 2 );
|
565 |
+
$year = $arc_row->year;
|
566 |
|
567 |
+
printf( "<option %s value='%s'>%s</option>\n",
|
568 |
+
selected( $m, $year . $month, false ),
|
569 |
+
esc_attr( $arc_row->year . $month ),
|
570 |
+
/* translators: 1: month name, 2: 4-digit year */
|
571 |
+
sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
|
572 |
+
);
|
573 |
+
}
|
574 |
+
?>
|
575 |
</select>
|
576 |
+
<?php
|
577 |
}
|
578 |
|
579 |
/**
|
580 |
* Display a view switcher
|
581 |
*
|
582 |
+
* @since 3.1.0
|
583 |
+
*
|
584 |
+
* @param string $current_mode
|
585 |
*/
|
586 |
+
protected function view_switcher( $current_mode ) {
|
587 |
+
?>
|
588 |
+
<input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
|
|
|
|
|
|
|
|
|
|
|
|
|
589 |
<div class="view-switch">
|
590 |
+
<?php
|
591 |
+
foreach ( $this->modes as $mode => $title ) {
|
592 |
+
$classes = array( 'view-' . $mode );
|
593 |
+
if ( $current_mode === $mode )
|
594 |
+
$classes[] = 'current';
|
595 |
+
printf(
|
596 |
+
"<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
|
597 |
+
esc_url( add_query_arg( 'mode', $mode ) ),
|
598 |
+
implode( ' ', $classes ),
|
599 |
+
$title
|
600 |
+
);
|
601 |
}
|
602 |
+
?>
|
603 |
</div>
|
604 |
+
<?php
|
605 |
}
|
606 |
|
607 |
/**
|
608 |
* Display a comment count bubble
|
609 |
*
|
610 |
+
* @since 3.1.0
|
|
|
611 |
*
|
612 |
+
* @param int $post_id The post ID.
|
613 |
+
* @param int $pending_comments Number of pending comments.
|
614 |
*/
|
615 |
+
protected function comments_bubble( $post_id, $pending_comments ) {
|
616 |
+
$approved_comments = get_comments_number();
|
617 |
|
618 |
+
$approved_comments_number = number_format_i18n( $approved_comments );
|
619 |
+
$pending_comments_number = number_format_i18n( $pending_comments );
|
620 |
|
621 |
+
$approved_only_phrase = sprintf( _n( '%s comment', '%s comments', $approved_comments ), $approved_comments_number );
|
622 |
+
$approved_phrase = sprintf( _n( '%s approved comment', '%s approved comments', $approved_comments ), $approved_comments_number );
|
623 |
+
$pending_phrase = sprintf( _n( '%s pending comment', '%s pending comments', $pending_comments ), $pending_comments_number );
|
624 |
|
625 |
+
// No comments at all.
|
626 |
+
if ( ! $approved_comments && ! $pending_comments ) {
|
627 |
+
printf( '<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>',
|
628 |
+
__( 'No comments' )
|
629 |
+
);
|
630 |
+
// Approved comments have different display depending on some conditions.
|
631 |
+
} elseif ( $approved_comments ) {
|
632 |
+
printf( '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
|
633 |
+
esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'approved' ), admin_url( 'edit-comments.php' ) ) ),
|
634 |
+
$approved_comments_number,
|
635 |
+
$pending_comments ? $approved_phrase : $approved_only_phrase
|
636 |
+
);
|
637 |
+
} else {
|
638 |
+
printf( '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
|
639 |
+
$approved_comments_number,
|
640 |
+
$pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
|
641 |
+
);
|
642 |
+
}
|
643 |
|
644 |
if ( $pending_comments ) {
|
645 |
+
printf( '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
|
646 |
+
esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'moderated' ), admin_url( 'edit-comments.php' ) ) ),
|
647 |
+
$pending_comments_number,
|
648 |
+
$pending_phrase
|
649 |
+
);
|
650 |
+
} else {
|
651 |
+
printf( '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
|
652 |
+
$pending_comments_number,
|
653 |
+
$approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
|
654 |
+
);
|
655 |
}
|
656 |
}
|
657 |
|
658 |
/**
|
659 |
* Get the current page number
|
660 |
*
|
661 |
+
* @since 3.1.0
|
|
|
662 |
*
|
663 |
* @return int
|
664 |
*/
|
665 |
+
public function get_pagenum() {
|
|
|
666 |
$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
|
667 |
|
668 |
+
if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
|
669 |
$pagenum = $this->_pagination_args['total_pages'];
|
|
|
670 |
|
671 |
return max( 1, $pagenum );
|
672 |
}
|
674 |
/**
|
675 |
* Get number of items to display on a single page
|
676 |
*
|
677 |
+
* @since 3.1.0
|
|
|
678 |
*
|
679 |
+
* @param string $option
|
680 |
+
* @param int $default
|
681 |
* @return int
|
682 |
*/
|
683 |
+
protected function get_items_per_page( $option, $default = 20 ) {
|
|
|
684 |
$per_page = (int) get_user_option( $option );
|
685 |
+
if ( empty( $per_page ) || $per_page < 1 )
|
686 |
$per_page = $default;
|
|
|
687 |
|
688 |
/**
|
689 |
+
* Filters the number of items to be displayed on each page of the list table.
|
690 |
*
|
691 |
+
* The dynamic hook name, $option, refers to the `per_page` option depending
|
692 |
+
* on the type of list table in use. Possible values include: 'edit_comments_per_page',
|
693 |
+
* 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
|
694 |
+
* 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
|
695 |
+
* 'edit_{$post_type}_per_page', etc.
|
696 |
*
|
697 |
* @since 2.9.0
|
698 |
*
|
699 |
* @param int $per_page Number of items to be displayed. Default 20.
|
700 |
*/
|
701 |
+
return (int) apply_filters( "{$option}", $per_page );
|
|
|
702 |
}
|
703 |
|
704 |
/**
|
705 |
* Display the pagination.
|
706 |
*
|
707 |
+
* @since 3.1.0
|
708 |
+
*
|
709 |
+
* @param string $which
|
710 |
*/
|
711 |
+
protected function pagination( $which ) {
|
|
|
712 |
if ( empty( $this->_pagination_args ) ) {
|
713 |
return;
|
714 |
}
|
715 |
|
716 |
+
$total_items = $this->_pagination_args['total_items'];
|
717 |
+
$total_pages = $this->_pagination_args['total_pages'];
|
718 |
+
$infinite_scroll = false;
|
719 |
+
if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
|
720 |
+
$infinite_scroll = $this->_pagination_args['infinite_scroll'];
|
721 |
+
}
|
722 |
|
723 |
+
if ( 'top' === $which && $total_pages > 1 ) {
|
724 |
+
$this->screen->render_screen_reader_content( 'heading_pagination' );
|
725 |
+
}
|
726 |
+
|
727 |
+
$output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
|
728 |
|
729 |
$current = $this->get_pagenum();
|
730 |
+
$removable_query_args = wp_removable_query_args();
|
731 |
+
$removable_query_args[] = 'id';
|
732 |
|
733 |
$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
|
734 |
|
735 |
+
$current_url = remove_query_arg( $removable_query_args, $current_url );
|
736 |
|
737 |
$page_links = array();
|
738 |
|
739 |
+
$total_pages_before = '<span class="paging-input">';
|
740 |
+
$total_pages_after = '</span></span>';
|
741 |
+
|
742 |
+
$disable_first = $disable_last = $disable_prev = $disable_next = false;
|
743 |
+
|
744 |
+
if ( $current == 1 ) {
|
745 |
+
$disable_first = true;
|
746 |
+
$disable_prev = true;
|
747 |
+
}
|
748 |
+
if ( $current == 2 ) {
|
749 |
+
$disable_first = true;
|
750 |
}
|
751 |
+
if ( $current == $total_pages ) {
|
752 |
+
$disable_last = true;
|
753 |
+
$disable_next = true;
|
754 |
+
}
|
755 |
+
if ( $current == $total_pages - 1 ) {
|
756 |
+
$disable_last = true;
|
757 |
}
|
758 |
|
759 |
+
if ( $disable_first ) {
|
760 |
+
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">«</span>';
|
761 |
+
} else {
|
762 |
+
$page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
|
763 |
+
esc_url( remove_query_arg( 'paged', $current_url ) ),
|
764 |
+
__( 'First page' ),
|
765 |
+
'«'
|
766 |
+
);
|
767 |
+
}
|
|
|
|
|
|
|
|
|
768 |
|
769 |
+
if ( $disable_prev ) {
|
770 |
+
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">‹</span>';
|
771 |
} else {
|
772 |
+
$page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
|
773 |
+
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
|
774 |
+
__( 'Previous page' ),
|
775 |
+
'‹'
|
776 |
);
|
777 |
}
|
778 |
|
779 |
+
if ( 'bottom' === $which ) {
|
780 |
+
$html_current_page = $current;
|
781 |
+
$total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
|
782 |
+
} else {
|
783 |
+
$html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
|
784 |
+
'<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
|
785 |
+
$current,
|
786 |
+
strlen( $total_pages )
|
787 |
+
);
|
788 |
+
}
|
789 |
$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
|
790 |
+
$page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
|
791 |
|
792 |
+
if ( $disable_next ) {
|
793 |
+
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">›</span>';
|
794 |
+
} else {
|
795 |
+
$page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
|
796 |
+
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
|
797 |
+
__( 'Next page' ),
|
798 |
+
'›'
|
799 |
+
);
|
800 |
+
}
|
801 |
|
802 |
+
if ( $disable_last ) {
|
803 |
+
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">»</span>';
|
804 |
+
} else {
|
805 |
+
$page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
|
806 |
+
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
|
807 |
+
__( 'Last page' ),
|
808 |
+
'»'
|
809 |
+
);
|
810 |
+
}
|
811 |
|
812 |
$pagination_links_class = 'pagination-links';
|
813 |
if ( ! empty( $infinite_scroll ) ) {
|
814 |
+
$pagination_links_class .= ' hide-if-js';
|
815 |
}
|
816 |
$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
|
817 |
|
820 |
} else {
|
821 |
$page_class = ' no-pages';
|
822 |
}
|
|
|
823 |
$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
|
824 |
|
825 |
echo $this->_pagination;
|
829 |
* Get a list of columns. The format is:
|
830 |
* 'internal-name' => 'Title'
|
831 |
*
|
832 |
+
* @since 3.1.0
|
|
|
833 |
* @abstract
|
834 |
*
|
835 |
* @return array
|
836 |
*/
|
837 |
+
public function get_columns() {
|
838 |
+
die( 'function ITSEC_WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
|
|
|
839 |
}
|
840 |
|
841 |
/**
|
846 |
*
|
847 |
* The second format will make the initial sorting order be descending
|
848 |
*
|
849 |
+
* @since 3.1.0
|
|
|
850 |
*
|
851 |
* @return array
|
852 |
*/
|
853 |
+
protected function get_sortable_columns() {
|
|
|
854 |
return array();
|
855 |
}
|
856 |
|
857 |
+
/**
|
858 |
+
* Gets the name of the default primary column.
|
859 |
+
*
|
860 |
+
* @since 4.3.0
|
861 |
+
*
|
862 |
+
* @return string Name of the default primary column, in this case, an empty string.
|
863 |
+
*/
|
864 |
+
protected function get_default_primary_column_name() {
|
865 |
+
$columns = $this->get_columns();
|
866 |
+
$column = '';
|
867 |
+
|
868 |
+
if ( empty( $columns ) ) {
|
869 |
+
return $column;
|
870 |
+
}
|
871 |
+
|
872 |
+
// We need a primary defined so responsive views show something,
|
873 |
+
// so let's fall back to the first non-checkbox column.
|
874 |
+
foreach ( $columns as $col => $column_name ) {
|
875 |
+
if ( 'cb' === $col ) {
|
876 |
+
continue;
|
877 |
+
}
|
878 |
+
|
879 |
+
$column = $col;
|
880 |
+
break;
|
881 |
+
}
|
882 |
+
|
883 |
+
return $column;
|
884 |
+
}
|
885 |
+
|
886 |
+
/**
|
887 |
+
* Public wrapper for ITSEC_WP_List_Table::get_default_primary_column_name().
|
888 |
+
*
|
889 |
+
* @since 4.4.0
|
890 |
+
*
|
891 |
+
* @return string Name of the default primary column.
|
892 |
+
*/
|
893 |
+
public function get_primary_column() {
|
894 |
+
return $this->get_primary_column_name();
|
895 |
+
}
|
896 |
+
|
897 |
+
/**
|
898 |
+
* Gets the name of the primary column.
|
899 |
+
*
|
900 |
+
* @since 4.3.0
|
901 |
+
*
|
902 |
+
* @return string The name of the primary column.
|
903 |
+
*/
|
904 |
+
protected function get_primary_column_name() {
|
905 |
+
$columns = get_column_headers( $this->screen );
|
906 |
+
$default = $this->get_default_primary_column_name();
|
907 |
+
|
908 |
+
// If the primary column doesn't exist fall back to the
|
909 |
+
// first non-checkbox column.
|
910 |
+
if ( ! isset( $columns[ $default ] ) ) {
|
911 |
+
$default = ITSEC_WP_List_Table::get_default_primary_column_name();
|
912 |
+
}
|
913 |
+
|
914 |
+
/**
|
915 |
+
* Filters the name of the primary column for the current list table.
|
916 |
+
*
|
917 |
+
* @since 4.3.0
|
918 |
+
*
|
919 |
+
* @param string $default Column name default for the specific list table, e.g. 'name'.
|
920 |
+
* @param string $context Screen ID for specific list table, e.g. 'plugins'.
|
921 |
+
*/
|
922 |
+
$column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
|
923 |
+
|
924 |
+
if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
|
925 |
+
$column = $default;
|
926 |
+
}
|
927 |
+
|
928 |
+
return $column;
|
929 |
+
}
|
930 |
+
|
931 |
/**
|
932 |
* Get a list of all, hidden and sortable columns, with filter applied
|
933 |
*
|
934 |
+
* @since 3.1.0
|
|
|
935 |
*
|
936 |
* @return array
|
937 |
*/
|
938 |
+
protected function get_column_info() {
|
939 |
+
// $_column_headers is already set / cached
|
940 |
+
if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
|
941 |
+
// Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
|
942 |
+
// In 4.3, we added a fourth argument for primary column.
|
943 |
+
$column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
|
944 |
+
foreach ( $this->_column_headers as $key => $value ) {
|
945 |
+
$column_headers[ $key ] = $value;
|
946 |
+
}
|
947 |
|
948 |
+
return $column_headers;
|
|
|
949 |
}
|
950 |
|
951 |
$columns = get_column_headers( $this->screen );
|
952 |
+
$hidden = get_hidden_columns( $this->screen );
|
953 |
|
954 |
$sortable_columns = $this->get_sortable_columns();
|
955 |
/**
|
956 |
+
* Filters the list table sortable columns for a specific screen.
|
957 |
*
|
958 |
+
* The dynamic portion of the hook name, `$this->screen->id`, refers
|
959 |
* to the ID of the current screen, usually a string.
|
960 |
*
|
961 |
* @since 3.5.0
|
966 |
|
967 |
$sortable = array();
|
968 |
foreach ( $_sortable as $id => $data ) {
|
969 |
+
if ( empty( $data ) )
|
970 |
continue;
|
|
|
971 |
|
972 |
$data = (array) $data;
|
973 |
+
if ( !isset( $data[1] ) )
|
974 |
$data[1] = false;
|
|
|
975 |
|
976 |
$sortable[$id] = $data;
|
977 |
}
|
978 |
|
979 |
+
$primary = $this->get_primary_column_name();
|
980 |
+
$this->_column_headers = array( $columns, $hidden, $sortable, $primary );
|
981 |
|
982 |
return $this->_column_headers;
|
983 |
}
|
985 |
/**
|
986 |
* Return number of visible columns
|
987 |
*
|
988 |
+
* @since 3.1.0
|
|
|
989 |
*
|
990 |
* @return int
|
991 |
*/
|
992 |
+
public function get_column_count() {
|
|
|
993 |
list ( $columns, $hidden ) = $this->get_column_info();
|
994 |
$hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
|
|
|
995 |
return count( $columns ) - count( $hidden );
|
996 |
}
|
997 |
|
998 |
/**
|
999 |
* Print column headers, accounting for hidden and sortable columns.
|
1000 |
*
|
1001 |
+
* @since 3.1.0
|
1002 |
+
*
|
1003 |
+
* @staticvar int $cb_counter
|
1004 |
*
|
1005 |
* @param bool $with_id Whether to set the id attribute or not
|
1006 |
*/
|
1007 |
+
public function print_column_headers( $with_id = true ) {
|
1008 |
+
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
|
|
|
1009 |
|
1010 |
$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
|
1011 |
$current_url = remove_query_arg( 'paged', $current_url );
|
1016 |
$current_orderby = '';
|
1017 |
}
|
1018 |
|
1019 |
+
if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
|
1020 |
$current_order = 'desc';
|
1021 |
} else {
|
1022 |
$current_order = 'asc';
|
1025 |
if ( ! empty( $columns['cb'] ) ) {
|
1026 |
static $cb_counter = 1;
|
1027 |
$columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
|
1028 |
+
. '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
|
1029 |
+
$cb_counter++;
|
1030 |
}
|
1031 |
|
1032 |
foreach ( $columns as $column_key => $column_display_name ) {
|
1033 |
$class = array( 'manage-column', "column-$column_key" );
|
1034 |
|
|
|
1035 |
if ( in_array( $column_key, $hidden ) ) {
|
1036 |
+
$class[] = 'hidden';
|
1037 |
}
|
1038 |
|
1039 |
+
if ( 'cb' === $column_key )
|
|
|
|
|
1040 |
$class[] = 'check-column';
|
1041 |
+
elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
|
1042 |
$class[] = 'num';
|
1043 |
+
|
1044 |
+
if ( $column_key === $primary ) {
|
1045 |
+
$class[] = 'column-primary';
|
1046 |
}
|
1047 |
|
1048 |
if ( isset( $sortable[$column_key] ) ) {
|
1049 |
list( $orderby, $desc_first ) = $sortable[$column_key];
|
1050 |
|
1051 |
+
if ( $current_orderby === $orderby ) {
|
1052 |
+
$order = 'asc' === $current_order ? 'desc' : 'asc';
|
1053 |
$class[] = 'sorted';
|
1054 |
$class[] = $current_order;
|
1055 |
} else {
|
1056 |
+
$order = $desc_first ? 'desc' : 'asc';
|
1057 |
$class[] = 'sortable';
|
1058 |
$class[] = $desc_first ? 'asc' : 'desc';
|
1059 |
}
|
1061 |
$column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
|
1062 |
}
|
1063 |
|
1064 |
+
$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
|
1065 |
+
$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
|
1066 |
$id = $with_id ? "id='$column_key'" : '';
|
1067 |
|
1068 |
+
if ( !empty( $class ) )
|
1069 |
$class = "class='" . join( ' ', $class ) . "'";
|
|
|
1070 |
|
1071 |
+
echo "<$tag $scope $id $class>$column_display_name</$tag>";
|
1072 |
}
|
1073 |
}
|
1074 |
|
1075 |
/**
|
1076 |
* Display the table
|
1077 |
*
|
1078 |
+
* @since 3.1.0
|
|
|
1079 |
*/
|
1080 |
+
public function display() {
|
1081 |
+
$singular = $this->_args['singular'];
|
|
|
1082 |
|
1083 |
$this->display_tablenav( 'top' );
|
1084 |
|
1085 |
+
$this->screen->render_screen_reader_content( 'heading_list' );
|
1086 |
+
?>
|
1087 |
+
<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
|
1088 |
+
<thead>
|
1089 |
+
<tr>
|
1090 |
+
<?php $this->print_column_headers(); ?>
|
1091 |
+
</tr>
|
1092 |
+
</thead>
|
1093 |
+
|
1094 |
+
<tbody id="the-list"<?php
|
1095 |
+
if ( $singular ) {
|
1096 |
+
echo " data-wp-lists='list:$singular'";
|
1097 |
+
} ?>>
|
1098 |
+
<?php $this->display_rows_or_placeholder(); ?>
|
1099 |
+
</tbody>
|
1100 |
+
|
1101 |
+
<tfoot>
|
1102 |
+
<tr>
|
1103 |
+
<?php $this->print_column_headers( false ); ?>
|
1104 |
+
</tr>
|
1105 |
+
</tfoot>
|
1106 |
+
|
1107 |
+
</table>
|
1108 |
+
<?php
|
1109 |
$this->display_tablenav( 'bottom' );
|
1110 |
}
|
1111 |
|
1112 |
/**
|
1113 |
+
* Get a list of CSS classes for the ITSEC_WP_List_Table table tag.
|
1114 |
*
|
1115 |
+
* @since 3.1.0
|
|
|
1116 |
*
|
1117 |
+
* @return array List of CSS classes for the table tag.
|
1118 |
*/
|
1119 |
+
protected function get_table_classes() {
|
1120 |
+
return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
|
|
|
1121 |
}
|
1122 |
|
1123 |
/**
|
1124 |
* Generate the table navigation above or below the table
|
1125 |
*
|
1126 |
+
* @since 3.1.0
|
1127 |
+
* @param string $which
|
1128 |
*/
|
1129 |
+
protected function display_tablenav( $which ) {
|
1130 |
+
if ( 'top' === $which ) {
|
|
|
1131 |
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
|
1132 |
}
|
1133 |
?>
|
1134 |
+
<div class="tablenav <?php echo esc_attr( $which ); ?>">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1135 |
|
1136 |
+
<?php if ( $this->has_items() ): ?>
|
1137 |
+
<div class="alignleft actions bulkactions">
|
1138 |
+
<?php $this->bulk_actions( $which ); ?>
|
1139 |
</div>
|
1140 |
+
<?php endif;
|
1141 |
+
$this->extra_tablenav( $which );
|
1142 |
+
$this->pagination( $which );
|
1143 |
+
?>
|
1144 |
+
|
1145 |
+
<br class="clear" />
|
1146 |
+
</div>
|
1147 |
+
<?php
|
1148 |
}
|
1149 |
|
1150 |
/**
|
1151 |
* Extra controls to be displayed between bulk actions and pagination
|
1152 |
*
|
1153 |
+
* @since 3.1.0
|
1154 |
+
*
|
1155 |
+
* @param string $which
|
1156 |
*/
|
1157 |
+
protected function extra_tablenav( $which ) {}
|
|
|
1158 |
|
1159 |
/**
|
1160 |
+
* Generate the tbody element for the list table.
|
1161 |
*
|
1162 |
+
* @since 3.1.0
|
|
|
1163 |
*/
|
1164 |
+
public function display_rows_or_placeholder() {
|
|
|
1165 |
if ( $this->has_items() ) {
|
1166 |
$this->display_rows();
|
1167 |
} else {
|
|
|
1168 |
echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
|
1169 |
$this->no_items();
|
1170 |
echo '</td></tr>';
|
1174 |
/**
|
1175 |
* Generate the table rows
|
1176 |
*
|
1177 |
+
* @since 3.1.0
|
|
|
1178 |
*/
|
1179 |
+
public function display_rows() {
|
1180 |
+
foreach ( $this->items as $item )
|
|
|
1181 |
$this->single_row( $item );
|
|
|
1182 |
}
|
1183 |
|
1184 |
/**
|
1185 |
* Generates content for a single row of the table
|
1186 |
*
|
1187 |
+
* @since 3.1.0
|
|
|
1188 |
*
|
1189 |
* @param object $item The current item
|
1190 |
*/
|
1191 |
+
public function single_row( $item ) {
|
1192 |
+
echo '<tr>';
|
|
|
|
|
|
|
|
|
1193 |
$this->single_row_columns( $item );
|
1194 |
echo '</tr>';
|
1195 |
}
|
1196 |
|
1197 |
+
/**
|
1198 |
+
*
|
1199 |
+
* @param object $item
|
1200 |
+
* @param string $column_name
|
1201 |
+
*/
|
1202 |
+
protected function column_default( $item, $column_name ) {}
|
1203 |
+
|
1204 |
+
/**
|
1205 |
+
*
|
1206 |
+
* @param object $item
|
1207 |
+
*/
|
1208 |
+
protected function column_cb( $item ) {}
|
1209 |
+
|
1210 |
/**
|
1211 |
* Generates the columns for a single row of the table
|
1212 |
*
|
1213 |
+
* @since 3.1.0
|
|
|
1214 |
*
|
1215 |
* @param object $item The current item
|
1216 |
*/
|
1217 |
+
protected function single_row_columns( $item ) {
|
1218 |
+
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
|
|
|
1219 |
|
1220 |
foreach ( $columns as $column_name => $column_display_name ) {
|
1221 |
+
$classes = "$column_name column-$column_name";
|
1222 |
+
if ( $primary === $column_name ) {
|
1223 |
+
$classes .= ' has-row-actions column-primary';
|
1224 |
+
}
|
1225 |
|
|
|
1226 |
if ( in_array( $column_name, $hidden ) ) {
|
1227 |
+
$classes .= ' hidden';
|
1228 |
}
|
1229 |
|
1230 |
+
// Comments column uses HTML in the display name with screen reader text.
|
1231 |
+
// Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
|
1232 |
+
$data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
|
1233 |
+
|
1234 |
+
$attributes = "class='$classes' $data";
|
1235 |
|
1236 |
+
if ( 'cb' === $column_name ) {
|
1237 |
echo '<th scope="row" class="check-column">';
|
1238 |
echo $this->column_cb( $item );
|
1239 |
echo '</th>';
|
1240 |
+
} elseif ( method_exists( $this, '_column_' . $column_name ) ) {
|
1241 |
+
echo call_user_func(
|
1242 |
+
array( $this, '_column_' . $column_name ),
|
1243 |
+
$item,
|
1244 |
+
$classes,
|
1245 |
+
$data,
|
1246 |
+
$primary
|
1247 |
+
);
|
1248 |
} elseif ( method_exists( $this, 'column_' . $column_name ) ) {
|
1249 |
echo "<td $attributes>";
|
1250 |
echo call_user_func( array( $this, 'column_' . $column_name ), $item );
|
1251 |
+
echo $this->handle_row_actions( $item, $column_name, $primary );
|
1252 |
echo "</td>";
|
1253 |
} else {
|
1254 |
echo "<td $attributes>";
|
1255 |
echo $this->column_default( $item, $column_name );
|
1256 |
+
echo $this->handle_row_actions( $item, $column_name, $primary );
|
1257 |
echo "</td>";
|
1258 |
}
|
1259 |
}
|
1260 |
}
|
1261 |
|
1262 |
/**
|
1263 |
+
* Generates and display row actions links for the list table.
|
1264 |
*
|
1265 |
+
* @since 4.3.0
|
1266 |
+
*
|
1267 |
+
* @param object $item The item being acted upon.
|
1268 |
+
* @param string $column_name Current column name.
|
1269 |
+
* @param string $primary Primary column name.
|
1270 |
+
* @return string The row actions HTML, or an empty string if the current column is the primary column.
|
1271 |
*/
|
1272 |
+
protected function handle_row_actions( $item, $column_name, $primary ) {
|
1273 |
+
return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
|
1274 |
+
}
|
1275 |
|
1276 |
+
/**
|
1277 |
+
* Handle an incoming ajax request (called from admin-ajax.php)
|
1278 |
+
*
|
1279 |
+
* @since 3.1.0
|
1280 |
+
*/
|
1281 |
+
public function ajax_response() {
|
1282 |
$this->prepare_items();
|
1283 |
|
|
|
|
|
|
|
1284 |
ob_start();
|
1285 |
if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
|
1286 |
$this->display_rows();
|
1292 |
|
1293 |
$response = array( 'rows' => $rows );
|
1294 |
|
1295 |
+
if ( isset( $this->_pagination_args['total_items'] ) ) {
|
1296 |
+
$response['total_items_i18n'] = sprintf(
|
1297 |
+
_n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
|
1298 |
+
number_format_i18n( $this->_pagination_args['total_items'] )
|
1299 |
+
);
|
1300 |
}
|
1301 |
+
if ( isset( $this->_pagination_args['total_pages'] ) ) {
|
1302 |
+
$response['total_pages'] = $this->_pagination_args['total_pages'];
|
1303 |
+
$response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
|
|
|
1304 |
}
|
1305 |
|
1306 |
+
die( wp_json_encode( $response ) );
|
1307 |
}
|
1308 |
|
1309 |
/**
|
1310 |
* Send required variables to JavaScript land
|
1311 |
*
|
|
|
1312 |
*/
|
1313 |
+
public function _js_vars() {
|
|
|
1314 |
$args = array(
|
1315 |
'class' => get_class( $this ),
|
1316 |
'screen' => array(
|
1319 |
)
|
1320 |
);
|
1321 |
|
1322 |
+
printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
|
1323 |
}
|
1324 |
}
|
core/lib/debug.php
ADDED
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Debug {
|
4 |
+
public static function print_r( $data, $args = array() ) {
|
5 |
+
echo "<style>.wp-admin .it-debug-print-r { margin-left: 170px; } .wp-admin #wpcontent .it-debug-print-r { margin-left: 0; }</style>\n";
|
6 |
+
echo "<pre style='color:black;background:white;padding:15px;font-family:\"Courier New\",Courier,monospace;font-size:12px;white-space:pre-wrap;text-align:left;max-width:100%;' class='it-debug-print-r'>";
|
7 |
+
echo self::get_print_r( $data, $args );
|
8 |
+
echo "</pre>\n";
|
9 |
+
}
|
10 |
+
|
11 |
+
public static function get_print_r( $data, $args = array() ) {
|
12 |
+
if ( is_bool( $args ) ) {
|
13 |
+
$args = array( 'expand_objects' => $args );
|
14 |
+
} else if ( is_numeric( $args ) ) {
|
15 |
+
$args = array( 'max_depth' => $args );
|
16 |
+
} else if ( ! is_array( $args ) ) {
|
17 |
+
$args = array();
|
18 |
+
}
|
19 |
+
|
20 |
+
// Create a deep copy so that variables aren't needlessly manipulated.
|
21 |
+
$data = unserialize( serialize( $data ) );
|
22 |
+
|
23 |
+
|
24 |
+
$default_args = array(
|
25 |
+
'expand_objects' => true,
|
26 |
+
'max_depth' => 10,
|
27 |
+
);
|
28 |
+
$args = array_merge( $default_args, $args );
|
29 |
+
|
30 |
+
if ( $args['max_depth'] < 1 ) {
|
31 |
+
$args['max_depth'] = 100;
|
32 |
+
}
|
33 |
+
|
34 |
+
|
35 |
+
return self::inspect_dive( $data, $args['expand_objects'], $args['max_depth'] );
|
36 |
+
}
|
37 |
+
|
38 |
+
public static function backtrace( $args = array() ) {
|
39 |
+
if ( is_string( $args ) ) {
|
40 |
+
$args = array( 'description' => $args );
|
41 |
+
} else if ( is_bool( $args ) ) {
|
42 |
+
$args = array( 'expand_objects' => $args );
|
43 |
+
} else if ( is_numeric( $args ) ) {
|
44 |
+
$args = array( 'max_depth' => $args );
|
45 |
+
} else if ( ! is_array( $args ) ) {
|
46 |
+
$args = array();
|
47 |
+
}
|
48 |
+
|
49 |
+
$default_args = array(
|
50 |
+
'description' => '',
|
51 |
+
'expand_objects' => false,
|
52 |
+
'max_depth' => 3,
|
53 |
+
'type' => '',
|
54 |
+
);
|
55 |
+
$args = array_merge( $default_args, $args );
|
56 |
+
|
57 |
+
|
58 |
+
if ( isset( $args['offset'] ) ) {
|
59 |
+
$args['offset']++;
|
60 |
+
} else {
|
61 |
+
$args['offset'] = 1;
|
62 |
+
}
|
63 |
+
|
64 |
+
$backtrace = self::get_backtrace( $args );
|
65 |
+
|
66 |
+
if ( 'string' == $args['type'] ) {
|
67 |
+
echo $backtrace;
|
68 |
+
} else {
|
69 |
+
$args['max_depth']++;
|
70 |
+
self::print_r( $backtrace, $args );
|
71 |
+
}
|
72 |
+
}
|
73 |
+
|
74 |
+
public static function get_backtrace( $args = array() ) {
|
75 |
+
if ( is_bool( $args ) ) {
|
76 |
+
$args = array( 'expand_objects' => $args );
|
77 |
+
} else if ( ! is_array( $args ) ) {
|
78 |
+
$args = array();
|
79 |
+
}
|
80 |
+
|
81 |
+
$default_args = array(
|
82 |
+
'expand_objects' => false,
|
83 |
+
'limit' => 0,
|
84 |
+
'offset' => 0,
|
85 |
+
'type' => 'array', // 'array' or 'string'
|
86 |
+
);
|
87 |
+
$args = array_merge( $default_args, $args );
|
88 |
+
|
89 |
+
|
90 |
+
$backtrace = debug_backtrace();
|
91 |
+
unset( $backtrace[0] );
|
92 |
+
|
93 |
+
if ( $args['offset'] > 0 ) {
|
94 |
+
$backtrace = array_slice( $backtrace, $args['offset'] );
|
95 |
+
}
|
96 |
+
if ( $args['limit'] > 0 ) {
|
97 |
+
$backtrace = array_slice( $backtrace, 0, $args['limit'] );
|
98 |
+
}
|
99 |
+
|
100 |
+
$backtrace = array_values( $backtrace );
|
101 |
+
|
102 |
+
|
103 |
+
if ( 'string' == $args['type'] ) {
|
104 |
+
$string_backtrace = '';
|
105 |
+
|
106 |
+
foreach ( $backtrace as $trace ) {
|
107 |
+
$string_backtrace .= self::get_backtrace_description( $trace, $args ) . "\n";
|
108 |
+
}
|
109 |
+
|
110 |
+
$backtrace = $string_backtrace;
|
111 |
+
}
|
112 |
+
|
113 |
+
|
114 |
+
return $backtrace;
|
115 |
+
}
|
116 |
+
|
117 |
+
private static function get_backtrace_description( $backtrace, $backtrace_args = array() ) {
|
118 |
+
$default_backtrace_args = array(
|
119 |
+
'remove_abspath' => true,
|
120 |
+
);
|
121 |
+
$backtrace_args = array_merge( $default_backtrace_args, $backtrace_args );
|
122 |
+
|
123 |
+
|
124 |
+
extract( $backtrace );
|
125 |
+
|
126 |
+
|
127 |
+
$args = self::flatten_backtrace_description_args( $args );
|
128 |
+
|
129 |
+
if ( $backtrace_args['remove_abspath'] && isset( $file ) ) {
|
130 |
+
$file = preg_replace( '/^' . preg_quote( ABSPATH, '/' ) . '/', '', $file );
|
131 |
+
}
|
132 |
+
|
133 |
+
|
134 |
+
if ( isset( $class ) && isset( $type ) && isset( $function ) && isset( $args ) ) {
|
135 |
+
return "<strong>$class$type$function(</strong>$args<strong>)</strong>";
|
136 |
+
} else if ( isset( $function ) && isset( $args ) ) {
|
137 |
+
if ( isset( $file ) && isset( $line ) ) {
|
138 |
+
return "<strong>$function(</strong>$args<strong>)</strong> on line $line of $file";
|
139 |
+
}
|
140 |
+
|
141 |
+
return "<strong>$function(</strong>$args<strong>)</strong>";
|
142 |
+
}
|
143 |
+
|
144 |
+
|
145 |
+
return 'String!';
|
146 |
+
}
|
147 |
+
|
148 |
+
private static function flatten_backtrace_description_args( $args, $max_depth = 2, $depth = 0 ) {
|
149 |
+
if ( is_string( $args ) ) {
|
150 |
+
return "'$args'";
|
151 |
+
} else if ( is_int( $args ) ) {
|
152 |
+
return "(int) $args";
|
153 |
+
} else if ( is_float( $args ) ) {
|
154 |
+
return "(float) $args";
|
155 |
+
} else if ( is_bool( $args ) ) {
|
156 |
+
return '(bool) ' . ( $args ? 'true' : 'false' );
|
157 |
+
} else if ( is_object( $args ) ) {
|
158 |
+
return '(object) ' . get_class( $args );
|
159 |
+
} else if ( ! is_array( $args ) ) {
|
160 |
+
return '[unknown]';
|
161 |
+
}
|
162 |
+
|
163 |
+
if ( $depth === $max_depth ) {
|
164 |
+
if ( empty( $args ) ) {
|
165 |
+
return 'array()';
|
166 |
+
} else {
|
167 |
+
return 'array( ' . count( $args ) . ' )';
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
|
172 |
+
$flat_args = array();
|
173 |
+
|
174 |
+
foreach ( $args as $arg ) {
|
175 |
+
$flat_args[] = self::flatten_backtrace_description_args( $arg, $max_depth, $depth + 1 );
|
176 |
+
}
|
177 |
+
|
178 |
+
$args = implode( ', ', $flat_args );
|
179 |
+
|
180 |
+
if ( ! empty( $args ) ) {
|
181 |
+
$args = " $args ";
|
182 |
+
}
|
183 |
+
|
184 |
+
if ( 0 === $depth ) {
|
185 |
+
return $args;
|
186 |
+
}
|
187 |
+
|
188 |
+
return "array($args)";
|
189 |
+
}
|
190 |
+
|
191 |
+
private static function pad( $depth, $pad = ' ' ) {
|
192 |
+
$retval = '';
|
193 |
+
|
194 |
+
for ( $x = 0; $x <= $depth; $x++ ) {
|
195 |
+
$retval .= $pad;
|
196 |
+
}
|
197 |
+
|
198 |
+
return $retval;
|
199 |
+
}
|
200 |
+
|
201 |
+
private static function is_callable_function( $function ) {
|
202 |
+
if ( ! is_callable( $function ) ) {
|
203 |
+
return false;
|
204 |
+
}
|
205 |
+
|
206 |
+
if ( ! isset( $GLOBALS['itsec_debug_cached_values'] ) ) {
|
207 |
+
$GLOBALS['itsec_debug_cached_values'] = array();
|
208 |
+
}
|
209 |
+
|
210 |
+
if ( ! isset( $GLOBALS['itsec_debug_cached_values']['ini_get:disable_functions'] ) ) {
|
211 |
+
$GLOBALS['itsec_debug_cached_values']['var:disable_functions'] = preg_split( '/\s*,\s*/', (string) ini_get( 'disable_functions' ) );
|
212 |
+
}
|
213 |
+
|
214 |
+
if ( in_array( $function, $GLOBALS['itsec_debug_cached_values']['var:disable_functions'] ) ) {
|
215 |
+
return false;
|
216 |
+
}
|
217 |
+
|
218 |
+
if ( ! isset( $GLOBALS['itsec_debug_cached_values']['ini_get:suhosin.executor.func.blacklist'] ) ) {
|
219 |
+
$GLOBALS['itsec_debug_cached_values']['ini_get:suhosin.executor.func.blacklist'] = preg_split( '/\s*,\s*/', (string) ini_get( 'suhosin.executor.func.blacklist' ) );
|
220 |
+
}
|
221 |
+
|
222 |
+
if ( in_array( $function, $GLOBALS['itsec_debug_cached_values']['ini_get:suhosin.executor.func.blacklist'] ) ) {
|
223 |
+
return false;
|
224 |
+
}
|
225 |
+
|
226 |
+
return true;
|
227 |
+
}
|
228 |
+
|
229 |
+
private static function inspect_dive( $data, $expand_objects, $max_depth, $depth = 0, $show_array_header = true ) {
|
230 |
+
$pad = self::pad( $depth, ' ' );
|
231 |
+
|
232 |
+
if ( is_string( $data ) ) {
|
233 |
+
if ( empty( $data ) ) {
|
234 |
+
return "<strong>[empty string]</strong>";
|
235 |
+
} else {
|
236 |
+
if ( self::is_callable_function( 'mb_detect_encoding' ) && ( 'UTF-8' !== mb_detect_encoding( $data, 'UTF-8', true ) ) && self::is_callable_function( 'utf8_encode' ) ) {
|
237 |
+
$data = utf8_encode( $data );
|
238 |
+
}
|
239 |
+
|
240 |
+
$flags = ENT_COMPAT;
|
241 |
+
|
242 |
+
if ( defined( 'ENT_HTML401' ) ) {
|
243 |
+
$flags |= ENT_HTML401;
|
244 |
+
}
|
245 |
+
|
246 |
+
return htmlspecialchars( $data, $flags, 'UTF-8', false );
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
if ( is_bool( $data ) ) {
|
251 |
+
return ( $data ) ? '<strong>[boolean] true</strong>' : '<strong>[boolean] false</strong>';
|
252 |
+
}
|
253 |
+
|
254 |
+
if ( is_null( $data ) ) {
|
255 |
+
return '<strong>null</strong>';
|
256 |
+
}
|
257 |
+
|
258 |
+
if ( is_object( $data ) ) {
|
259 |
+
$class_name = get_class( $data );
|
260 |
+
$retval = "<strong>Object</strong> $class_name";
|
261 |
+
|
262 |
+
if ( ! $expand_objects || ( $depth == $max_depth ) ) {
|
263 |
+
return $retval;
|
264 |
+
}
|
265 |
+
|
266 |
+
$vars = get_object_vars( $data );
|
267 |
+
|
268 |
+
if ( empty( $vars ) ) {
|
269 |
+
$vars = '';
|
270 |
+
} else {
|
271 |
+
$vars = self::inspect_dive( $vars, $expand_objects, $max_depth, $depth, false );
|
272 |
+
}
|
273 |
+
|
274 |
+
$retval .= "$vars";
|
275 |
+
|
276 |
+
return $retval;
|
277 |
+
}
|
278 |
+
|
279 |
+
if ( is_array( $data ) ) {
|
280 |
+
$retval = ( $show_array_header ) ? '<strong>Array</strong>' : '';
|
281 |
+
|
282 |
+
if ( empty( $data ) ) {
|
283 |
+
return "$retval()";
|
284 |
+
}
|
285 |
+
if ( $depth == $max_depth ) {
|
286 |
+
return "$retval( " . count( $data ) . " )";
|
287 |
+
}
|
288 |
+
|
289 |
+
$max = 0;
|
290 |
+
|
291 |
+
foreach ( array_keys( $data ) as $index ) {
|
292 |
+
if ( strlen( $index ) > $max ) {
|
293 |
+
$max = strlen( $index );
|
294 |
+
}
|
295 |
+
}
|
296 |
+
|
297 |
+
foreach ( $data as $index => $val ) {
|
298 |
+
$spaces = self::pad( $max - strlen( $index ), ' ' );
|
299 |
+
$retval .= "\n$pad" . htmlspecialchars( $index ) . "$spaces <strong>=></strong> " . self::inspect_dive( $val, $expand_objects, $max_depth, $depth + 1 );
|
300 |
+
}
|
301 |
+
|
302 |
+
return $retval;
|
303 |
+
}
|
304 |
+
|
305 |
+
return '<strong>[' . gettype( $data ) . ']</strong> ' . $data;
|
306 |
+
}
|
307 |
+
}
|
core/lib/form.php
CHANGED
@@ -13,7 +13,7 @@ final class ITSEC_Form {
|
|
13 |
$this->options =& $options;
|
14 |
}
|
15 |
|
16 |
-
public static function get_post_data() {
|
17 |
$remove_vars = array( 'itsec-nonce', '_wp_http_referer' );
|
18 |
$data = $_POST;
|
19 |
|
@@ -49,10 +49,8 @@ final class ITSEC_Form {
|
|
49 |
if ( is_null( $value ) ) {
|
50 |
ITSEC_Form::add_array_value( $data, $index, $default );
|
51 |
}
|
52 |
-
} else {
|
53 |
-
|
54 |
-
ITSEC_Form::add_array_value( $data, $index, $default );
|
55 |
-
}
|
56 |
}
|
57 |
}
|
58 |
|
@@ -87,6 +85,14 @@ final class ITSEC_Form {
|
|
87 |
unset( $data['--itsec-form-convert-to-array'] );
|
88 |
}
|
89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
return $data;
|
91 |
}
|
92 |
|
@@ -359,6 +365,7 @@ final class ITSEC_Form {
|
|
359 |
}
|
360 |
|
361 |
$options['type'] = 'password';
|
|
|
362 |
$this->add_custom_input( $var, $options );
|
363 |
}
|
364 |
|
@@ -435,6 +442,16 @@ final class ITSEC_Form {
|
|
435 |
$this->add_custom_input( $var, $options );
|
436 |
}
|
437 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
public function add_hidden( $var, $options = array() ) {
|
439 |
if ( ! is_array( $options ) ) {
|
440 |
$options = array( 'value' => $options );
|
@@ -592,7 +609,7 @@ final class ITSEC_Form {
|
|
592 |
}
|
593 |
} else if ( 'radio' === $options['type'] ) {
|
594 |
if ( ! isset( $this->tracked_strings[$options['name']] ) ) {
|
595 |
-
echo '<input type="hidden" name="--itsec-form-tracked-
|
596 |
$this->tracked_strings[$options['name']] = true;
|
597 |
}
|
598 |
}
|
13 |
$this->options =& $options;
|
14 |
}
|
15 |
|
16 |
+
public static function get_post_data( $desired_inputs = false ) {
|
17 |
$remove_vars = array( 'itsec-nonce', '_wp_http_referer' );
|
18 |
$data = $_POST;
|
19 |
|
49 |
if ( is_null( $value ) ) {
|
50 |
ITSEC_Form::add_array_value( $data, $index, $default );
|
51 |
}
|
52 |
+
} else if ( ! is_array( $value ) ) {
|
53 |
+
ITSEC_Form::add_array_value( $data, $index, $default );
|
|
|
|
|
54 |
}
|
55 |
}
|
56 |
|
85 |
unset( $data['--itsec-form-convert-to-array'] );
|
86 |
}
|
87 |
|
88 |
+
if ( is_array( $desired_inputs ) ) {
|
89 |
+
foreach ( array_keys( $data ) as $key ) {
|
90 |
+
if ( ! in_array( $key, $desired_inputs ) ) {
|
91 |
+
unset( $data[$key] );
|
92 |
+
}
|
93 |
+
}
|
94 |
+
}
|
95 |
+
|
96 |
return $data;
|
97 |
}
|
98 |
|
365 |
}
|
366 |
|
367 |
$options['type'] = 'password';
|
368 |
+
|
369 |
$this->add_custom_input( $var, $options );
|
370 |
}
|
371 |
|
442 |
$this->add_custom_input( $var, $options );
|
443 |
}
|
444 |
|
445 |
+
public function add_number( $var, $options = array() ) {
|
446 |
+
if ( ! is_array( $options ) ) {
|
447 |
+
$options = array( 'value' => $options );
|
448 |
+
}
|
449 |
+
|
450 |
+
$options['type'] = 'number';
|
451 |
+
|
452 |
+
$this->add_custom_input( $var, $options );
|
453 |
+
}
|
454 |
+
|
455 |
public function add_hidden( $var, $options = array() ) {
|
456 |
if ( ! is_array( $options ) ) {
|
457 |
$options = array( 'value' => $options );
|
609 |
}
|
610 |
} else if ( 'radio' === $options['type'] ) {
|
611 |
if ( ! isset( $this->tracked_strings[$options['name']] ) ) {
|
612 |
+
echo '<input type="hidden" name="--itsec-form-tracked-strings[]" value="' . esc_attr( $options['name'] ) . '" />' . "\n";
|
613 |
$this->tracked_strings[$options['name']] = true;
|
614 |
}
|
615 |
}
|
core/lib/log-util.php
ADDED
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Log_Util {
|
4 |
+
public static function get_type_counts( $min_timestamp = 0 ) {
|
5 |
+
global $wpdb;
|
6 |
+
|
7 |
+
|
8 |
+
$where = 'parent_id=0';
|
9 |
+
$prepare_args = array();
|
10 |
+
|
11 |
+
if ( $min_timestamp > 0 ) {
|
12 |
+
$where .= ' AND init_timestamp>%s';
|
13 |
+
$prepare_args[] = date( 'Y-m-d H:i:s', $min_timestamp );
|
14 |
+
}
|
15 |
+
|
16 |
+
$query = "SELECT type, COUNT(*) AS count FROM `{$wpdb->base_prefix}itsec_logs` WHERE $where GROUP BY type";
|
17 |
+
|
18 |
+
if ( ! empty( $prepare_args ) ) {
|
19 |
+
$query = $wpdb->prepare( $query, $prepare_args );
|
20 |
+
}
|
21 |
+
|
22 |
+
$results = $wpdb->get_results( $query, ARRAY_A );
|
23 |
+
|
24 |
+
$counts = array();
|
25 |
+
|
26 |
+
foreach ( $results as $result ) {
|
27 |
+
if ( 'process-start' === $result['type'] ) {
|
28 |
+
$result['type'] = 'process';
|
29 |
+
}
|
30 |
+
|
31 |
+
if ( isset( $counts[$result['type']] ) ) {
|
32 |
+
$counts[$result['type']] += $result['count'];
|
33 |
+
} else {
|
34 |
+
$counts[$result['type']] = $result['count'];
|
35 |
+
}
|
36 |
+
}
|
37 |
+
|
38 |
+
return $counts;
|
39 |
+
}
|
40 |
+
|
41 |
+
public static function get_entries( $filters = array(), $limit = 100, $page = 1, $sort_by_column = 'timestamp', $sort_direction = 'DESC', $columns = false ) {
|
42 |
+
global $wpdb;
|
43 |
+
|
44 |
+
|
45 |
+
$get_count = false;
|
46 |
+
$min_timestamp = false;
|
47 |
+
|
48 |
+
if ( isset( $filters['__get_count'] ) ) {
|
49 |
+
if ( $filters['__get_count'] ) {
|
50 |
+
$get_count = true;
|
51 |
+
}
|
52 |
+
|
53 |
+
unset( $filters['__get_count'] );
|
54 |
+
}
|
55 |
+
|
56 |
+
if ( isset( $filters['__min_timestamp'] ) ) {
|
57 |
+
$min_timestamp = $filters['__min_timestamp'];
|
58 |
+
unset( $filters['__min_timestamp'] );
|
59 |
+
}
|
60 |
+
|
61 |
+
$limit = max( 0, min( 100, intval( $limit ) ) );
|
62 |
+
$page = max( 1, intval( $page ) );
|
63 |
+
|
64 |
+
$sort_direction = strtoupper( $sort_direction );
|
65 |
+
if ( ! in_array( $sort_direction, array( 'DESC', 'ASC' ) ) ) {
|
66 |
+
$sort_direction = 'DESC';
|
67 |
+
}
|
68 |
+
|
69 |
+
|
70 |
+
$valid_columns = array(
|
71 |
+
'id',
|
72 |
+
'parent_id',
|
73 |
+
'module',
|
74 |
+
'type',
|
75 |
+
'code',
|
76 |
+
'timestamp',
|
77 |
+
'init_timestamp',
|
78 |
+
'remote_ip',
|
79 |
+
'user_id',
|
80 |
+
'url',
|
81 |
+
'memory_current',
|
82 |
+
'memory_peak',
|
83 |
+
);
|
84 |
+
|
85 |
+
if ( false === $columns ) {
|
86 |
+
$columns = $valid_columns;
|
87 |
+
} else if ( 'all' === $columns ) {
|
88 |
+
$columns = array_merge( $valid_columns, array( 'data' ) );
|
89 |
+
}
|
90 |
+
|
91 |
+
|
92 |
+
if ( $get_count ) {
|
93 |
+
$query = "SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_logs`";
|
94 |
+
} else {
|
95 |
+
$query = "SELECT " . implode( ', ', $columns ) . " FROM `{$wpdb->base_prefix}itsec_logs`";
|
96 |
+
}
|
97 |
+
|
98 |
+
$prepare_args = array();
|
99 |
+
|
100 |
+
|
101 |
+
$where_entries = array();
|
102 |
+
|
103 |
+
if ( ! isset( $filters['parent_id'] ) ) {
|
104 |
+
$filters['parent_id'] = 0;
|
105 |
+
}
|
106 |
+
|
107 |
+
foreach ( (array) $filters as $column => $value ) {
|
108 |
+
if ( preg_match( '/^(.+)_not$/', $column, $match ) ) {
|
109 |
+
$not = true;
|
110 |
+
$column = $match[1];
|
111 |
+
} else {
|
112 |
+
$not = false;
|
113 |
+
}
|
114 |
+
|
115 |
+
if ( preg_match( '/^(.+)_(min|max)$/', $column, $match ) ) {
|
116 |
+
if ( ! in_array( $match[1], $valid_columns ) ) {
|
117 |
+
continue;
|
118 |
+
}
|
119 |
+
|
120 |
+
if ( 'min' === $match[2] ) {
|
121 |
+
$where_entries[] = "'$column'>=%s";
|
122 |
+
$prepare_args[] = $value;
|
123 |
+
} else {
|
124 |
+
$where_entries[] = "'column'<=%s";
|
125 |
+
$prepare_args[] = $value;
|
126 |
+
}
|
127 |
+
} else if ( ! in_array( $column, $valid_columns ) ) {
|
128 |
+
continue;
|
129 |
+
} else if ( is_array( $value ) ) {
|
130 |
+
if ( ! empty( $value ) ) {
|
131 |
+
if ( $not ) {
|
132 |
+
$where_entries[] = "$column NOT IN (" . implode( ', ', array_fill( 0, count( $value ), '%s' ) ) . ")";
|
133 |
+
} else {
|
134 |
+
$where_entries[] = "$column IN (" . implode( ', ', array_fill( 0, count( $value ), '%s' ) ) . ")";
|
135 |
+
}
|
136 |
+
$prepare_args = array_merge( $prepare_args, $value );
|
137 |
+
}
|
138 |
+
} else if ( false !== strpos( $value, '%' ) ) {
|
139 |
+
if ( $not ) {
|
140 |
+
$where_entries[] = "$column NOT LIKE %s";
|
141 |
+
} else {
|
142 |
+
$where_entries[] = "$column LIKE %s";
|
143 |
+
}
|
144 |
+
$prepare_args[] = $value;
|
145 |
+
} else {
|
146 |
+
if ( $not ) {
|
147 |
+
$where_entries[] = "$column<>%s";
|
148 |
+
} else {
|
149 |
+
$where_entries[] = "$column=%s";
|
150 |
+
}
|
151 |
+
$prepare_args[] = $value;
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( false !== $min_timestamp ) {
|
156 |
+
$where_entries[] = 'init_timestamp>%s';
|
157 |
+
$prepare_args[] = date( 'Y-m-d H:i:s', $min_timestamp );
|
158 |
+
}
|
159 |
+
|
160 |
+
$query .= ' WHERE ' . implode( ' AND ', $where_entries );
|
161 |
+
|
162 |
+
|
163 |
+
if ( ! $get_count ) {
|
164 |
+
if ( ! is_array( $sort_by_column ) ) {
|
165 |
+
$sort_by_column = array( "$sort_by_column $sort_direction" );
|
166 |
+
}
|
167 |
+
|
168 |
+
$query .= ' ORDER BY ' . implode( ', ', $sort_by_column );
|
169 |
+
|
170 |
+
|
171 |
+
if ( $limit > 0 ) {
|
172 |
+
$offset = ( $page - 1 ) * $limit;
|
173 |
+
$query .= " LIMIT $offset,$limit";
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
$query = $wpdb->prepare( $query, $prepare_args );
|
178 |
+
|
179 |
+
|
180 |
+
if ( $get_count ) {
|
181 |
+
return intval( $wpdb->get_var( $query ) );
|
182 |
+
}
|
183 |
+
|
184 |
+
$rows = $wpdb->get_results( $query, ARRAY_A );
|
185 |
+
|
186 |
+
if ( is_null( $rows ) ) {
|
187 |
+
return new WP_Error( 'itsec-log-util-failed-query', sprintf( esc_html__( 'A query failure prevented the log data from being accessed: %s', 'better-wp-security' ), $wpdb->last_error ) );
|
188 |
+
}
|
189 |
+
|
190 |
+
foreach ( $rows as $index => $row ) {
|
191 |
+
if ( ! isset( $row['data'] ) ) {
|
192 |
+
break;
|
193 |
+
}
|
194 |
+
|
195 |
+
$data = unserialize( $row['data'] );
|
196 |
+
|
197 |
+
if ( false !== $data || 'b:0;' === $row['data'] ) {
|
198 |
+
$rows[$index]['data'] = $data;
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
return $rows;
|
203 |
+
}
|
204 |
+
|
205 |
+
public static function get_logs_page_screen_options() {
|
206 |
+
$defaults = array(
|
207 |
+
'per_page' => 20,
|
208 |
+
'default_view' => 'important',
|
209 |
+
'color' => true,
|
210 |
+
'show_debug' => false,
|
211 |
+
'show_process' => false,
|
212 |
+
'last_seen' => 0,
|
213 |
+
);
|
214 |
+
|
215 |
+
$options = get_user_option( 'itsec_logs_page_screen_options' );
|
216 |
+
|
217 |
+
if ( is_array( $options ) ) {
|
218 |
+
$options = array_merge( $defaults, $options );
|
219 |
+
} else {
|
220 |
+
$options = $defaults;
|
221 |
+
|
222 |
+
if ( $user = wp_get_current_user() ) {
|
223 |
+
update_user_option( $user->ID, 'itsec_logs_page_screen_options', $options, true );
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
return $options;
|
228 |
+
}
|
229 |
+
|
230 |
+
public static function set_logs_page_screen_options( $options ) {
|
231 |
+
if ( ! $user = wp_get_current_user() ) {
|
232 |
+
return;
|
233 |
+
}
|
234 |
+
|
235 |
+
if ( isset( $options['per_page'] ) && ( $options['per_page'] < 1 || $options['per_page'] > 999 ) ) {
|
236 |
+
unset( $options['per_page'] );
|
237 |
+
}
|
238 |
+
if ( isset( $options['default_view'] ) && ! in_array( $options['default_view'], array( 'important', 'all', 'critical-issue' ) ) ) {
|
239 |
+
unset( $options['default_view'] );
|
240 |
+
}
|
241 |
+
if ( isset( $options['last_seen'] ) ) {
|
242 |
+
$options['last_seen'] = intval( $options['last_seen'] );
|
243 |
+
|
244 |
+
if ( $options['last_seen'] < 0 || $options['last_seen'] > ITSEC_Core::get_current_time_gmt() ) {
|
245 |
+
unset( $options['last_seen'] );
|
246 |
+
}
|
247 |
+
}
|
248 |
+
|
249 |
+
$options = array_merge( self::get_logs_page_screen_options(), $options );
|
250 |
+
|
251 |
+
update_user_option( $user->ID, 'itsec_logs_page_screen_options', $options, true );
|
252 |
+
}
|
253 |
+
|
254 |
+
public static function has_old_log_entries() {
|
255 |
+
global $wpdb;
|
256 |
+
|
257 |
+
if ( ! $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}itsec_log'" ) ) {
|
258 |
+
return false;
|
259 |
+
}
|
260 |
+
|
261 |
+
$num_entries = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}itsec_log" );
|
262 |
+
|
263 |
+
if ( empty( $num_entries ) ) {
|
264 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}itsec_log" );
|
265 |
+
}
|
266 |
+
|
267 |
+
return true;
|
268 |
+
}
|
269 |
+
|
270 |
+
public static function migrate_old_log_entries() {
|
271 |
+
global $wpdb;
|
272 |
+
|
273 |
+
$max = 50;
|
274 |
+
$num_entries = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}itsec_log" );
|
275 |
+
$num_loops = min( $max, $num_entries );
|
276 |
+
|
277 |
+
for ( $count = 1; $count <= $num_loops; $count++ ) {
|
278 |
+
$old_entry = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}itsec_log ORDER BY log_date_gmt LIMIT 1", ARRAY_A );
|
279 |
+
$entry = self::get_new_log_entry_from_old( $old_entry );
|
280 |
+
|
281 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_logs", $entry );
|
282 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}itsec_log WHERE log_id=%d", $old_entry['log_id'] ) );
|
283 |
+
}
|
284 |
+
|
285 |
+
if ( $num_entries > $max ) {
|
286 |
+
return false;
|
287 |
+
}
|
288 |
+
|
289 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}itsec_log" );
|
290 |
+
|
291 |
+
return true;
|
292 |
+
}
|
293 |
+
|
294 |
+
public static function get_new_log_entry_from_old( $old_entry ) {
|
295 |
+
$old_entry['log_data'] = unserialize( $old_entry['log_data'] );
|
296 |
+
|
297 |
+
$entry = array(
|
298 |
+
'module' => $old_entry['log_type'],
|
299 |
+
'code' => $old_entry['log_function'],
|
300 |
+
'type' => 'notice',
|
301 |
+
'user_id' => $old_entry['log_user'],
|
302 |
+
'timestamp' => $old_entry['log_date_gmt'],
|
303 |
+
'init_timestamp' => $old_entry['log_date_gmt'],
|
304 |
+
'remote_ip' => $old_entry['log_host'],
|
305 |
+
'url' => $old_entry['log_url'],
|
306 |
+
);
|
307 |
+
|
308 |
+
if ( 'lockout' === $old_entry['log_type'] ) {
|
309 |
+
if ( isset( $old_entry['log_data']['expires'] ) ) {
|
310 |
+
$entry['type'] = 'action';
|
311 |
+
|
312 |
+
if ( empty( $old_entry['log_host'] ) ) {
|
313 |
+
$entry['code'] = 'user-lockout';
|
314 |
+
} else {
|
315 |
+
$entry['code'] = 'host-lockout';
|
316 |
+
}
|
317 |
+
} else {
|
318 |
+
if ( empty( $old_entry['log_host'] ) ) {
|
319 |
+
$entry['code'] = 'whitelisted-host-triggered-user-lockout';
|
320 |
+
} else {
|
321 |
+
$entry['code'] = 'whitelisted-host-triggered-host-lockout';
|
322 |
+
}
|
323 |
+
}
|
324 |
+
} else if ( 'file_change' === $old_entry['log_type'] ) {
|
325 |
+
$entry['type'] = 'warning';
|
326 |
+
$entry['code'] = 'changes-found';
|
327 |
+
$entry['data'] = $old_entry['log_data'];
|
328 |
+
} else if ( 'malware' === $old_entry['log_type'] ) {
|
329 |
+
$entry['code'] = 'scan';
|
330 |
+
$entry['data'] = array( 'results' => $old_entry['log_data'] );
|
331 |
+
} else if ( 'backup' === $old_entry['log_type'] ) {
|
332 |
+
$entry['code'] = 'details';
|
333 |
+
} else if ( 'four_oh_four' === $old_entry['log_type'] ) {
|
334 |
+
$entry['code'] = 'found_404';
|
335 |
+
} else if ( 'ipcheck' === $old_entry['log_type'] ) {
|
336 |
+
if ( empty( $old_entry['log_data'] ) ) {
|
337 |
+
$entry['code'] = 'failed-login-by-blocked-ip';
|
338 |
+
} else {
|
339 |
+
$entry['type'] = 'action';
|
340 |
+
$entry['code'] = 'ip-blocked';
|
341 |
+
}
|
342 |
+
} else if ( 'brute_force' === $old_entry['log_type'] ) {
|
343 |
+
if ( 'admin' === $old_entry['log_username'] ) {
|
344 |
+
$entry['code'] = 'auto-ban-admin-username';
|
345 |
+
} else {
|
346 |
+
$entry['code'] = 'invalid-login';
|
347 |
+
}
|
348 |
+
} else if ( 'away_mode' === $old_entry['log_type'] ) {
|
349 |
+
$entry['code'] = 'away-mode-active';
|
350 |
+
} else if ( 'recaptcha' === $old_entry['log_type'] ) {
|
351 |
+
$entry['code'] = 'failed-validation';
|
352 |
+
} else if ( 'user_logging' === $old_entry['log_type'] ) {
|
353 |
+
if ( isset( $old_entry['log_data']['post'] ) ) {
|
354 |
+
$entry['code'] = 'post-status-changed';
|
355 |
+
} else if ( empty( $old_entry['log_username'] ) ) {
|
356 |
+
$entry['code'] = 'user-logged-out';
|
357 |
+
} else {
|
358 |
+
$entry['code'] = 'user-logged-in';
|
359 |
+
}
|
360 |
+
}
|
361 |
+
|
362 |
+
if ( isset( $entry['data'] ) ) {
|
363 |
+
$entry['data'] = serialize( $entry['data'] );
|
364 |
+
}
|
365 |
+
|
366 |
+
return $entry;
|
367 |
+
}
|
368 |
+
}
|
core/lib/log.php
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Log {
|
4 |
+
/* Critical issues are very important events that administrators should be notified about, such as finding malware
|
5 |
+
* on the site or detecting a security breach.
|
6 |
+
*/
|
7 |
+
public static function add_critical_issue( $module, $code, $data = false ) {
|
8 |
+
return self::add( $module, $code, $data, 'critical-issue' );
|
9 |
+
}
|
10 |
+
|
11 |
+
/* Actions are noteworthy automated events that change the functionality of the site based upon certain criteria,
|
12 |
+
* such as locking out an IP address due to bruteforce attempts.
|
13 |
+
*/
|
14 |
+
public static function add_action( $module, $code, $data = false ) {
|
15 |
+
return self::add( $module, $code, $data, 'action' );
|
16 |
+
}
|
17 |
+
|
18 |
+
/* Fatal errors are critical problems detected in the code that could and should be reserved for very rare but
|
19 |
+
* highly problematic situations, such as a catch handler in a try/catch block or a shutdown handler running before
|
20 |
+
* a process finishes.
|
21 |
+
*/
|
22 |
+
public static function add_fatal_error( $module, $code, $data = false ) {
|
23 |
+
return self::add( $module, $code, $data, 'fatal' );
|
24 |
+
}
|
25 |
+
|
26 |
+
/* Errors are events that indicate a failure of some sort, such as failure to write to a file or an inability to
|
27 |
+
* request a remote URL.
|
28 |
+
*/
|
29 |
+
public static function add_error( $module, $code, $data = false ) {
|
30 |
+
return self::add( $module, $code, $data, 'error' );
|
31 |
+
}
|
32 |
+
|
33 |
+
/* Warnings are noteworthy events that might indicate an issue, such as finding changed files.
|
34 |
+
*/
|
35 |
+
public static function add_warning( $module, $code, $data = false ) {
|
36 |
+
return self::add( $module, $code, $data, 'warning' );
|
37 |
+
}
|
38 |
+
|
39 |
+
/* Notices keep track of events that should be tracked but do not necessarily indicate an issue, such as requests
|
40 |
+
* for files that do not exist and completed scans that did not find any issues.
|
41 |
+
*/
|
42 |
+
public static function add_notice( $module, $code, $data = false ) {
|
43 |
+
return self::add( $module, $code, $data, 'notice' );
|
44 |
+
}
|
45 |
+
|
46 |
+
/* Debug events are to be used in situations where extra information about a specific process could be helpful to
|
47 |
+
* have when investigating an issue but the information would typically be uninteresting to the user, such as
|
48 |
+
* noting the use of a compatibility function.
|
49 |
+
*/
|
50 |
+
public static function add_debug( $module, $code, $data = false ) {
|
51 |
+
return self::add( $module, $code, $data, 'debug' );
|
52 |
+
}
|
53 |
+
|
54 |
+
/* Process events allow for creating single entries that have a start, zero or more updates, and a stopping point.
|
55 |
+
* This allows for benchmarking performance of long-running code in addition to finding issues such as terminated
|
56 |
+
* execution due to the missing process-stop entry.
|
57 |
+
*/
|
58 |
+
public static function add_process_start( $module, $code, $data = false ) {
|
59 |
+
$id = self::add( $module, $code, $data, 'process-start' );
|
60 |
+
|
61 |
+
return compact( 'module', 'code', 'id' );
|
62 |
+
}
|
63 |
+
|
64 |
+
public static function add_process_update( $reference, $data = false ) {
|
65 |
+
self::add( $reference['module'], $reference['code'], $data, 'process-update', $reference['id'] );
|
66 |
+
}
|
67 |
+
|
68 |
+
public static function add_process_stop( $reference, $data = false ) {
|
69 |
+
self::add( $reference['module'], $reference['code'], $data, 'process-stop', $reference['id'] );
|
70 |
+
}
|
71 |
+
|
72 |
+
private static function add( $module, $code, $data, $type, $parent_id = 0 ) {
|
73 |
+
$data = array(
|
74 |
+
'parent_id' => $parent_id,
|
75 |
+
'module' => $module,
|
76 |
+
'code' => $code,
|
77 |
+
'data' => $data,
|
78 |
+
'type' => $type,
|
79 |
+
'timestamp' => gmdate( 'Y-m-d H:i:s' ),
|
80 |
+
'init_timestamp' => gmdate( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
81 |
+
'memory_current' => memory_get_usage(),
|
82 |
+
'memory_peak' => memory_get_peak_usage(),
|
83 |
+
'url' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
|
84 |
+
'blog_id' => get_current_blog_id(),
|
85 |
+
'user_id' => get_current_user_id(),
|
86 |
+
'remote_ip' => ITSEC_Lib::get_ip(),
|
87 |
+
);
|
88 |
+
|
89 |
+
$log_type = ITSEC_Modules::get_setting( 'global', 'log_type' );
|
90 |
+
|
91 |
+
if ( 'database' === $log_type ) {
|
92 |
+
$id = self::add_to_db( $data );
|
93 |
+
} else if ( 'file' === $log_type ) {
|
94 |
+
$id = self::add_to_file( $data );
|
95 |
+
} else {
|
96 |
+
$id = self::add_to_db( $data );
|
97 |
+
self::add_to_file( $data, $id );
|
98 |
+
}
|
99 |
+
|
100 |
+
return $id;
|
101 |
+
}
|
102 |
+
|
103 |
+
private static function add_to_db( $data ) {
|
104 |
+
global $wpdb;
|
105 |
+
|
106 |
+
$format = array();
|
107 |
+
|
108 |
+
foreach ( $data as $key => $value ) {
|
109 |
+
if ( is_int( $value ) ) {
|
110 |
+
$format[] = '%d';
|
111 |
+
} else {
|
112 |
+
$format[] = '%s';
|
113 |
+
|
114 |
+
if ( ! is_string( $value ) ) {
|
115 |
+
$data[$key] = serialize( $value );
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
$result = $wpdb->insert( "{$wpdb->base_prefix}itsec_logs", $data, $format );
|
121 |
+
|
122 |
+
if ( false === $result ) {
|
123 |
+
error_log( "Failed to insert log entry: {$wpdb->last_error}" );
|
124 |
+
return new WP_Error( 'itsec-log-failed-db-insert', sprintf( esc_html__( 'Failed to insert log entry: %s', 'better-wp-security' ), $wpdb->last_error ) );
|
125 |
+
}
|
126 |
+
|
127 |
+
return $wpdb->insert_id;
|
128 |
+
}
|
129 |
+
|
130 |
+
private static function add_to_file( $data, $id = false ) {
|
131 |
+
if ( false === $id ) {
|
132 |
+
$id = microtime( true );
|
133 |
+
}
|
134 |
+
|
135 |
+
|
136 |
+
$file = self::get_log_file_path();
|
137 |
+
|
138 |
+
if ( is_wp_error( $file ) ) {
|
139 |
+
return $file;
|
140 |
+
}
|
141 |
+
|
142 |
+
|
143 |
+
$entries = array();
|
144 |
+
|
145 |
+
foreach ( $data as $value ) {
|
146 |
+
if ( is_object( $value ) || is_array( $value ) ) {
|
147 |
+
$value = serialize( $value );
|
148 |
+
} else {
|
149 |
+
$value = (string) $value;
|
150 |
+
}
|
151 |
+
|
152 |
+
$value = str_replace( '"', '""', $value );
|
153 |
+
|
154 |
+
if ( preg_match( '/[", ]/', $value ) ) {
|
155 |
+
$value = "\"$value\"";
|
156 |
+
}
|
157 |
+
|
158 |
+
$entries[] = $value;
|
159 |
+
}
|
160 |
+
|
161 |
+
$entry = implode( ',', $entries ) . "\n";
|
162 |
+
|
163 |
+
|
164 |
+
$result = file_put_contents( $file, $entry, FILE_APPEND );
|
165 |
+
|
166 |
+
if ( false === $result ) {
|
167 |
+
return new WP_Error( 'itsec-log-failed-to-write-to-file', __( 'Unable to write to the log file. This could indicate that there is no space available, that there is a permissions issue, or that the server is not configured properly.', 'better-wp-security' ) );
|
168 |
+
}
|
169 |
+
|
170 |
+
|
171 |
+
return $id;
|
172 |
+
}
|
173 |
+
|
174 |
+
public static function get_log_file_path() {
|
175 |
+
static $log_file = false;
|
176 |
+
|
177 |
+
if ( false !== $log_file ) {
|
178 |
+
return $log_file;
|
179 |
+
}
|
180 |
+
|
181 |
+
$log_location = ITSEC_Modules::get_setting( 'global', 'log_location' );
|
182 |
+
$log_info = ITSEC_Modules::get_setting( 'global', 'log_info' );
|
183 |
+
|
184 |
+
if ( empty( $log_info ) ) {
|
185 |
+
$log_info = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
|
186 |
+
|
187 |
+
ITSEC_Modules::set_setting( 'global', 'log_info', $log_info );
|
188 |
+
}
|
189 |
+
|
190 |
+
$log_file = "$log_location/event-log-$log_info.log";
|
191 |
+
|
192 |
+
if ( ! file_exists( $log_file ) ) {
|
193 |
+
$header = "parent_id,module,code,data,type,timestamp,init_timestamp,memory_current,memory_peak,user_id,remote_ip\n";
|
194 |
+
|
195 |
+
file_put_contents( $log_file, $header );
|
196 |
+
}
|
197 |
+
|
198 |
+
return $log_file;
|
199 |
+
}
|
200 |
+
|
201 |
+
public static function get_entries( $filters = array(), $limit = 0, $page = 1, $sort_by_column = 'id', $sort_direction = 'DESC', $columns = false ) {
|
202 |
+
require_once( dirname( __FILE__ ) . '/log-util.php' );
|
203 |
+
|
204 |
+
return ITSEC_Log_Util::get_entries( $filters, $limit, $page, $sort_by_column, $sort_direction, $columns );
|
205 |
+
}
|
206 |
+
|
207 |
+
public static function get_entry( $id ) {
|
208 |
+
require_once( dirname( __FILE__ ) . '/log-util.php' );
|
209 |
+
|
210 |
+
$entries = ITSEC_Log_Util::get_entries( array( 'id' => $id ), 0, 1, 'id', 'DESC', 'all' );
|
211 |
+
|
212 |
+
return $entries[0];
|
213 |
+
}
|
214 |
+
|
215 |
+
public static function get_number_of_entries( $filters = array() ) {
|
216 |
+
$filters['__get_count'] = true;
|
217 |
+
return self::get_entries( $filters );
|
218 |
+
}
|
219 |
+
|
220 |
+
public static function get_type_counts( $min_timestamp = 0 ) {
|
221 |
+
require_once( dirname( __FILE__ ) . '/log-util.php' );
|
222 |
+
|
223 |
+
return ITSEC_Log_Util::get_type_counts( $min_timestamp );
|
224 |
+
}
|
225 |
+
|
226 |
+
public static function get_types_for_display() {
|
227 |
+
return array(
|
228 |
+
'critical-issue' => esc_html__( 'Critical Issue', 'better-wp-security' ),
|
229 |
+
'action' => esc_html__( 'Action', 'better-wp-security' ),
|
230 |
+
'fatal-error' => esc_html__( 'Fatal Error', 'better-wp-security' ),
|
231 |
+
'error' => esc_html__( 'Error', 'better-wp-security' ),
|
232 |
+
'warning' => esc_html__( 'Warning', 'better-wp-security' ),
|
233 |
+
'notice' => esc_html__( 'Notice', 'better-wp-security' ),
|
234 |
+
'debug' => esc_html__( 'Debug', 'better-wp-security' ),
|
235 |
+
'process-start' => esc_html__( 'Process', 'better-wp-security' ),
|
236 |
+
);
|
237 |
+
}
|
238 |
+
}
|
core/lib/schema.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Schema {
|
4 |
+
/**
|
5 |
+
* Creates appropriate database tables.
|
6 |
+
*
|
7 |
+
* Uses dbdelta to create database tables either on activation or in the event that one is missing.
|
8 |
+
*
|
9 |
+
* @since 3.9.0
|
10 |
+
*
|
11 |
+
* @return void
|
12 |
+
*/
|
13 |
+
public static function create_database_tables() {
|
14 |
+
global $wpdb;
|
15 |
+
|
16 |
+
$charset_collate = $wpdb->get_charset_collate();
|
17 |
+
|
18 |
+
$tables = "
|
19 |
+
CREATE TABLE {$wpdb->base_prefix}itsec_logs (
|
20 |
+
id bigint(20) unsigned NOT NULL auto_increment,
|
21 |
+
parent_id bigint(20) unsigned NOT NULL default '0',
|
22 |
+
module varchar(50) NOT NULL default '',
|
23 |
+
code varchar(100) NOT NULL default '',
|
24 |
+
data longtext NOT NULL default '',
|
25 |
+
type varchar(20) NOT NULL default 'notice',
|
26 |
+
timestamp datetime NOT NULL default '0000-00-00 00:00:00',
|
27 |
+
init_timestamp datetime NOT NULL default '0000-00-00 00:00:00',
|
28 |
+
memory_current bigint(20) unsigned NOT NULL default '0',
|
29 |
+
memory_peak bigint(20) unsigned NOT NULL default '0',
|
30 |
+
url varchar(500) NOT NULL default '',
|
31 |
+
blog_id bigint(20) NOT NULL default '0',
|
32 |
+
user_id bigint(20) unsigned NOT NULL default '0',
|
33 |
+
remote_ip varchar(50) NOT NULL default '',
|
34 |
+
PRIMARY KEY (id),
|
35 |
+
KEY module (module),
|
36 |
+
KEY code (code),
|
37 |
+
KEY type (type),
|
38 |
+
KEY timestamp (timestamp),
|
39 |
+
KEY user_id (user_id),
|
40 |
+
KEY blog_id (blog_id)
|
41 |
+
) $charset_collate;
|
42 |
+
|
43 |
+
CREATE TABLE {$wpdb->base_prefix}itsec_lockouts (
|
44 |
+
lockout_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
45 |
+
lockout_type varchar(20) NOT NULL,
|
46 |
+
lockout_start datetime NOT NULL,
|
47 |
+
lockout_start_gmt datetime NOT NULL,
|
48 |
+
lockout_expire datetime NOT NULL,
|
49 |
+
lockout_expire_gmt datetime NOT NULL,
|
50 |
+
lockout_host varchar(40),
|
51 |
+
lockout_user bigint(20) UNSIGNED,
|
52 |
+
lockout_username varchar(60),
|
53 |
+
lockout_active int(1) NOT NULL DEFAULT 1,
|
54 |
+
PRIMARY KEY (lockout_id),
|
55 |
+
KEY lockout_expire_gmt (lockout_expire_gmt),
|
56 |
+
KEY lockout_host (lockout_host),
|
57 |
+
KEY lockout_user (lockout_user),
|
58 |
+
KEY lockout_username (lockout_username),
|
59 |
+
KEY lockout_active (lockout_active)
|
60 |
+
) $charset_collate;
|
61 |
+
|
62 |
+
CREATE TABLE {$wpdb->base_prefix}itsec_temp (
|
63 |
+
temp_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
64 |
+
temp_type varchar(20) NOT NULL,
|
65 |
+
temp_date datetime NOT NULL,
|
66 |
+
temp_date_gmt datetime NOT NULL,
|
67 |
+
temp_host varchar(40),
|
68 |
+
temp_user bigint(20) UNSIGNED,
|
69 |
+
temp_username varchar(60),
|
70 |
+
PRIMARY KEY (temp_id),
|
71 |
+
KEY temp_date_gmt (temp_date_gmt),
|
72 |
+
KEY temp_host (temp_host),
|
73 |
+
KEY temp_user (temp_user),
|
74 |
+
KEY temp_username (temp_username)
|
75 |
+
) $charset_collate;";
|
76 |
+
|
77 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
78 |
+
dbDelta( $tables );
|
79 |
+
}
|
80 |
+
|
81 |
+
public static function remove_database_tables() {
|
82 |
+
global $wpdb;
|
83 |
+
|
84 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_logs;" );
|
85 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_log;" );
|
86 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_lockouts;" );
|
87 |
+
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}itsec_temp;" );
|
88 |
+
}
|
89 |
+
}
|
core/lockout.php
CHANGED
@@ -87,9 +87,6 @@ final class ITSEC_Lockout {
|
|
87 |
//Process clear lockout form
|
88 |
add_action( 'itsec_admin_init', array( $this, 'release_lockout' ) );
|
89 |
|
90 |
-
//Register Logger
|
91 |
-
add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
|
92 |
-
|
93 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
94 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
95 |
|
@@ -98,6 +95,8 @@ final class ITSEC_Lockout {
|
|
98 |
|
99 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
100 |
add_filter( 'itsec_lockout_notification_strings', array( $this, 'notification_strings' ) );
|
|
|
|
|
101 |
}
|
102 |
|
103 |
public function init_settings_page() {
|
@@ -160,7 +159,11 @@ final class ITSEC_Lockout {
|
|
160 |
$user_check = false;
|
161 |
$host_check = false;
|
162 |
|
163 |
-
if ( $user
|
|
|
|
|
|
|
|
|
164 |
|
165 |
$user = get_userdata( intval( $user ) );
|
166 |
$user_id = $user->ID;
|
@@ -280,7 +283,9 @@ final class ITSEC_Lockout {
|
|
280 |
/**
|
281 |
* This persists a lockout to storage or performs a permanent ban if appropriate.
|
282 |
*
|
283 |
-
*
|
|
|
|
|
284 |
*
|
285 |
* @since 4.0
|
286 |
*
|
@@ -289,133 +294,106 @@ final class ITSEC_Lockout {
|
|
289 |
*
|
290 |
* @return void
|
291 |
*/
|
292 |
-
public function do_lockout( $module, $username =
|
293 |
-
|
294 |
-
global $wpdb, $itsec_globals;
|
295 |
|
296 |
if ( ! isset( $this->lockout_modules[$module] ) ) {
|
297 |
return;
|
298 |
}
|
299 |
|
|
|
|
|
300 |
$wpdb->hide_errors(); //Hide database errors in case the tables aren't there
|
301 |
|
302 |
-
|
303 |
-
$
|
304 |
-
$
|
|
|
305 |
$options = $this->lockout_modules[$module];
|
306 |
|
307 |
-
$
|
|
|
|
|
|
|
|
|
|
|
308 |
|
309 |
if ( isset( $options['host'] ) && $options['host'] > 0 ) {
|
|
|
310 |
|
311 |
-
$
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
316 |
-
'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
317 |
-
'temp_host' => $host,
|
318 |
-
)
|
319 |
-
);
|
320 |
|
321 |
$host_count = $wpdb->get_var(
|
322 |
$wpdb->prepare(
|
323 |
-
"SELECT COUNT(*) FROM `
|
324 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] *
|
325 |
$host
|
326 |
)
|
327 |
);
|
328 |
|
329 |
if ( $host_count >= $options['host'] ) {
|
330 |
-
|
331 |
$lock_host = $host;
|
332 |
-
|
333 |
}
|
334 |
-
|
335 |
}
|
336 |
|
337 |
-
if (
|
|
|
|
|
338 |
|
339 |
-
|
|
|
|
|
|
|
340 |
|
341 |
-
|
342 |
-
|
343 |
-
$wpdb->insert(
|
344 |
-
$wpdb->base_prefix . 'itsec_temp',
|
345 |
-
array(
|
346 |
-
'temp_type' => $options['type'],
|
347 |
-
'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
348 |
-
'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
349 |
-
'temp_user' => intval( $user_id ),
|
350 |
-
'temp_username' => sanitize_text_field( $username ),
|
351 |
-
)
|
352 |
-
);
|
353 |
|
354 |
$user_count = $wpdb->get_var(
|
355 |
$wpdb->prepare(
|
356 |
-
"SELECT COUNT(*) FROM `
|
357 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] *
|
358 |
-
|
359 |
-
|
360 |
)
|
361 |
);
|
362 |
|
363 |
if ( $user_count >= $options['user'] ) {
|
364 |
-
|
365 |
-
$lock_user = $user_id;
|
366 |
-
|
367 |
}
|
368 |
-
|
369 |
} else {
|
|
|
|
|
370 |
|
371 |
-
$
|
372 |
-
|
373 |
-
$wpdb->insert(
|
374 |
-
$wpdb->base_prefix . 'itsec_temp',
|
375 |
-
array(
|
376 |
-
'temp_type' => $options['type'],
|
377 |
-
'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
378 |
-
'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
379 |
-
'temp_username' => $username,
|
380 |
-
)
|
381 |
-
);
|
382 |
|
383 |
$user_count = $wpdb->get_var(
|
384 |
$wpdb->prepare(
|
385 |
-
"SELECT COUNT(*) FROM `
|
386 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] *
|
387 |
$username
|
388 |
)
|
389 |
);
|
390 |
|
391 |
if ( $user_count >= $options['user'] ) {
|
392 |
-
|
393 |
$lock_username = $username;
|
394 |
-
|
395 |
}
|
396 |
-
|
397 |
}
|
398 |
-
|
399 |
}
|
400 |
|
|
|
401 |
$error = $wpdb->last_error;
|
402 |
|
|
|
403 |
if ( strlen( trim( $error ) ) > 0 ) {
|
404 |
ITSEC_Lib::create_database_tables();
|
405 |
}
|
406 |
|
407 |
-
if ( ! ITSEC_Lib::is_ip_whitelisted( $host ) && ( $lock_host !== null || $lock_user !== null || $lock_username !== null ) ) {
|
408 |
-
|
409 |
-
$this->lockout( $options['type'], $options['reason'], $lock_host, $lock_user, $lock_username );
|
410 |
-
|
411 |
-
} elseif ( $lock_host !== null || $lock_user !== null ) {
|
412 |
-
|
413 |
-
global $itsec_logger;
|
414 |
-
|
415 |
-
$itsec_logger->log_event( 'lockout', 10, array( __( 'A whitelisted host has triggered a lockout condition but was not locked out.', 'better-wp-security' ) ), sanitize_text_field( $host ) );
|
416 |
|
|
|
|
|
417 |
}
|
418 |
-
|
419 |
}
|
420 |
|
421 |
/**
|
@@ -737,12 +715,10 @@ final class ITSEC_Lockout {
|
|
737 |
* @return bool
|
738 |
*/
|
739 |
public function is_visitor_temp_whitelisted() {
|
740 |
-
global $itsec_globals;
|
741 |
-
|
742 |
$whitelist = $this->get_temp_whitelist();
|
743 |
$ip = ITSEC_Lib::get_ip();
|
744 |
|
745 |
-
if ( isset( $whitelist[$ip] ) && $whitelist[$ip] >
|
746 |
return true;
|
747 |
}
|
748 |
|
@@ -759,213 +735,158 @@ final class ITSEC_Lockout {
|
|
759 |
*
|
760 |
* @since 4.0
|
761 |
*
|
762 |
-
* @param string
|
763 |
-
* @param string $
|
764 |
-
* @param
|
765 |
-
* @param
|
766 |
-
* @param string $username username to lockout
|
767 |
*
|
768 |
* @return void
|
769 |
*/
|
770 |
-
private function lockout( $
|
|
|
771 |
|
772 |
-
global $wpdb, $itsec_logger, $itsec_globals;
|
773 |
|
774 |
-
$
|
775 |
-
$user_expiration = null;
|
776 |
-
$username = sanitize_text_field( trim( $username ) );
|
777 |
-
$lock = 'lockout_' . $host . $user . $username;
|
778 |
|
779 |
// Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
|
780 |
-
if ( ITSEC_Lib::get_lock( $lock, 180 ) ) {
|
781 |
-
|
782 |
-
|
783 |
-
if ( ! is_null( $host ) && ITSEC_Lib::is_ip_whitelisted( sanitize_text_field( $host ) ) === false && ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
784 |
-
$good_host = sanitize_text_field( $host );
|
785 |
-
} else {
|
786 |
-
$good_host = false;
|
787 |
-
}
|
788 |
-
|
789 |
-
//Do we have a valid user to lockout or not
|
790 |
-
if ( $user !== null && ITSEC_Lib::user_id_exists( intval( $user ) ) === true ) {
|
791 |
-
$good_user = intval( $user );
|
792 |
-
} else {
|
793 |
-
$good_user = false;
|
794 |
-
}
|
795 |
-
|
796 |
-
//Do we have a valid username to lockout or not
|
797 |
-
if ( $username !== null && $username != '' ) {
|
798 |
-
$good_username = $username;
|
799 |
-
} else {
|
800 |
-
$good_username = false;
|
801 |
-
}
|
802 |
-
|
803 |
-
$blacklist_host = false; //assume we're not permanently blcking the host
|
804 |
-
|
805 |
-
//Sanitize the data for later
|
806 |
-
$type = sanitize_text_field( $type );
|
807 |
-
$reason = sanitize_text_field( $reason );
|
808 |
-
|
809 |
-
//handle a permanent host ban (if needed)
|
810 |
-
if ( ITSEC_Modules::get_setting( 'global', 'blacklist' ) && $good_host !== false ) { //permanent blacklist
|
811 |
-
|
812 |
-
$blacklist_period = ITSEC_Modules::get_setting( 'global', 'blacklist_period', 7 );
|
813 |
-
$blacklist_seconds = $blacklist_period * DAY_IN_SECONDS;
|
814 |
-
|
815 |
-
$host_count = 1 + $wpdb->get_var(
|
816 |
-
$wpdb->prepare(
|
817 |
-
"SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_expire_gmt` > %s AND `lockout_host`= %s;",
|
818 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - $blacklist_seconds ),
|
819 |
-
$host
|
820 |
-
)
|
821 |
-
);
|
822 |
-
|
823 |
-
if ( $host_count >= ITSEC_Modules::get_setting( 'global', 'blacklist_count' ) && ITSEC_Files::can_write_to_files() ) {
|
824 |
|
825 |
-
$host_expiration = false;
|
826 |
|
827 |
-
|
828 |
|
829 |
-
|
|
|
830 |
|
831 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
832 |
|
833 |
-
}
|
834 |
|
835 |
-
|
836 |
-
|
|
|
|
|
|
|
837 |
|
838 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
839 |
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
|
|
|
|
|
844 |
} else {
|
845 |
-
|
846 |
-
$
|
847 |
-
$exp_seconds = ITSEC_Modules::get_setting( 'global', 'lockout_period' ) * MINUTE_IN_SECONDS;
|
848 |
-
$expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $exp_seconds );
|
849 |
-
$expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $exp_seconds );
|
850 |
-
|
851 |
}
|
|
|
|
|
852 |
|
853 |
-
if ( $good_host !== false && $blacklist_host === false ) { //temp lockout host
|
854 |
-
|
855 |
-
$host_expiration = $expiration;
|
856 |
-
|
857 |
-
$wpdb->insert(
|
858 |
-
$wpdb->base_prefix . 'itsec_lockouts',
|
859 |
-
array(
|
860 |
-
'lockout_type' => $type,
|
861 |
-
'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
862 |
-
'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
863 |
-
'lockout_expire' => $expiration,
|
864 |
-
'lockout_expire_gmt' => $expiration_gmt,
|
865 |
-
'lockout_host' => sanitize_text_field( $host ),
|
866 |
-
)
|
867 |
-
);
|
868 |
|
869 |
-
|
870 |
-
|
871 |
-
), sanitize_text_field( $host ) );
|
872 |
|
873 |
-
|
|
|
|
|
|
|
|
|
874 |
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
$wpdb->base_prefix . 'itsec_lockouts',
|
881 |
-
array(
|
882 |
-
'lockout_type' => $type,
|
883 |
-
'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
884 |
-
'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
885 |
-
'lockout_expire' => $expiration,
|
886 |
-
'lockout_expire_gmt' => $expiration_gmt,
|
887 |
-
'lockout_host' => '',
|
888 |
-
'lockout_user' => intval( $user ),
|
889 |
-
)
|
890 |
-
);
|
891 |
-
|
892 |
-
if ( $whitelisted === false ) {
|
893 |
-
$itsec_logger->log_event( 'lockout', 10, array(
|
894 |
-
'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
|
895 |
-
), '', '', intval( $user ) );
|
896 |
-
} else {
|
897 |
-
$itsec_logger->log_event( 'lockout', 10, array(
|
898 |
-
__( 'White Listed', 'better-wp-security' ), 'type' => $type
|
899 |
-
), '', '', intval( $user ) );
|
900 |
-
}
|
901 |
|
902 |
-
|
|
|
|
|
903 |
|
904 |
-
|
905 |
-
|
906 |
-
|
907 |
-
|
908 |
-
$wpdb->insert(
|
909 |
-
$wpdb->base_prefix . 'itsec_lockouts',
|
910 |
-
array(
|
911 |
-
'lockout_type' => $type,
|
912 |
-
'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
913 |
-
'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
914 |
-
'lockout_expire' => $expiration,
|
915 |
-
'lockout_expire_gmt' => $expiration_gmt,
|
916 |
-
'lockout_host' => '',
|
917 |
-
'lockout_username' => $username,
|
918 |
-
)
|
919 |
-
);
|
920 |
-
|
921 |
-
if ( $whitelisted === false ) {
|
922 |
-
$itsec_logger->log_event( 'lockout', 10, array(
|
923 |
-
'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
|
924 |
-
), '', '', $username );
|
925 |
-
} else {
|
926 |
-
$itsec_logger->log_event( 'lockout', 10, array(
|
927 |
-
__( 'White Listed', 'better-wp-security' ), 'type' => $type
|
928 |
-
), '', '', $username );
|
929 |
-
}
|
930 |
|
931 |
-
|
|
|
|
|
|
|
932 |
|
933 |
-
|
|
|
|
|
|
|
934 |
|
935 |
-
$this->send_lockout_email( $good_host, $good_user, $good_username, $host_expiration, $user_expiration, $reason );
|
936 |
|
937 |
-
|
938 |
-
|
939 |
-
|
|
|
940 |
|
941 |
-
if ( $user ) {
|
942 |
-
$lock_context['user'] = get_userdata( $user );
|
943 |
-
} elseif ( $username ) {
|
944 |
-
$lock_context['username'] = $username;
|
945 |
-
}
|
946 |
|
947 |
-
|
948 |
|
949 |
-
|
950 |
-
|
|
|
951 |
|
952 |
-
|
|
|
|
|
|
|
|
|
953 |
|
954 |
-
|
|
|
|
|
955 |
|
956 |
-
|
957 |
-
|
|
|
958 |
|
959 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
960 |
|
961 |
-
|
|
|
962 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
963 |
}
|
964 |
|
965 |
-
|
966 |
-
|
967 |
}
|
968 |
-
|
969 |
}
|
970 |
|
971 |
/**
|
@@ -1012,30 +933,10 @@ final class ITSEC_Lockout {
|
|
1012 |
* @return void
|
1013 |
*/
|
1014 |
public function purge_lockouts() {
|
1015 |
-
|
1016 |
global $wpdb;
|
1017 |
|
1018 |
-
$wpdb->query( "DELETE FROM `
|
1019 |
-
$wpdb->query( "DELETE FROM `
|
1020 |
-
|
1021 |
-
}
|
1022 |
-
|
1023 |
-
/**
|
1024 |
-
* Register 404 and file change detection for logger
|
1025 |
-
*
|
1026 |
-
* @param array $logger_modules array of logger modules
|
1027 |
-
*
|
1028 |
-
* @return array
|
1029 |
-
*/
|
1030 |
-
public function register_logger( $logger_modules ) {
|
1031 |
-
|
1032 |
-
$logger_modules['lockout'] = array(
|
1033 |
-
'type' => 'lockout',
|
1034 |
-
'function' => __( 'Host or User Lockout', 'better-wp-security' ),
|
1035 |
-
);
|
1036 |
-
|
1037 |
-
return $logger_modules;
|
1038 |
-
|
1039 |
}
|
1040 |
|
1041 |
/**
|
@@ -1255,4 +1156,44 @@ final class ITSEC_Lockout {
|
|
1255 |
|
1256 |
}
|
1257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1258 |
}
|
87 |
//Process clear lockout form
|
88 |
add_action( 'itsec_admin_init', array( $this, 'release_lockout' ) );
|
89 |
|
|
|
|
|
|
|
90 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
91 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
92 |
|
95 |
|
96 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
97 |
add_filter( 'itsec_lockout_notification_strings', array( $this, 'notification_strings' ) );
|
98 |
+
|
99 |
+
add_filter( 'itsec_logs_prepare_lockout_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ), 10, 3 );
|
100 |
}
|
101 |
|
102 |
public function init_settings_page() {
|
159 |
$user_check = false;
|
160 |
$host_check = false;
|
161 |
|
162 |
+
if ( is_object( $user ) && is_a( $user, 'WP_User' ) ) {
|
163 |
+
|
164 |
+
$user_id = $user->ID;
|
165 |
+
|
166 |
+
} else if ( ! empty( $user ) ) {
|
167 |
|
168 |
$user = get_userdata( intval( $user ) );
|
169 |
$user_id = $user->ID;
|
283 |
/**
|
284 |
* This persists a lockout to storage or performs a permanent ban if appropriate.
|
285 |
*
|
286 |
+
* Each module registers lockout settings that determine if a lockout event applies to the hostname, user, or
|
287 |
+
* username. In addition, these settings determine how many lockout events of a specific kind trigger an actual
|
288 |
+
* lockout by calling $this->lockout().
|
289 |
*
|
290 |
* @since 4.0
|
291 |
*
|
294 |
*
|
295 |
* @return void
|
296 |
*/
|
297 |
+
public function do_lockout( $module, $username = false ) {
|
298 |
+
global $wpdb;
|
|
|
299 |
|
300 |
if ( ! isset( $this->lockout_modules[$module] ) ) {
|
301 |
return;
|
302 |
}
|
303 |
|
304 |
+
|
305 |
+
// TODO: Ensure that this is not needed and remove it.
|
306 |
$wpdb->hide_errors(); //Hide database errors in case the tables aren't there
|
307 |
|
308 |
+
|
309 |
+
$lock_host = false;
|
310 |
+
$lock_user_id = false;
|
311 |
+
$lock_username = false;
|
312 |
$options = $this->lockout_modules[$module];
|
313 |
|
314 |
+
$lockout_event_data = array(
|
315 |
+
'temp_type' => $options['type'],
|
316 |
+
'temp_date' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
317 |
+
'temp_date_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
318 |
+
);
|
319 |
+
|
320 |
|
321 |
if ( isset( $options['host'] ) && $options['host'] > 0 ) {
|
322 |
+
$host = ITSEC_Lib::get_ip();
|
323 |
|
324 |
+
$host_lockout_event_data = $lockout_event_data;
|
325 |
+
$host_lockout_event_data['temp_host'] = $host;
|
326 |
+
|
327 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $host_lockout_event_data );
|
|
|
|
|
|
|
|
|
|
|
328 |
|
329 |
$host_count = $wpdb->get_var(
|
330 |
$wpdb->prepare(
|
331 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND `temp_host` = %s",
|
332 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
333 |
$host
|
334 |
)
|
335 |
);
|
336 |
|
337 |
if ( $host_count >= $options['host'] ) {
|
|
|
338 |
$lock_host = $host;
|
|
|
339 |
}
|
|
|
340 |
}
|
341 |
|
342 |
+
if ( false !== $username && isset( $options['user'] ) && $options['user'] > 0 ) {
|
343 |
+
$username = sanitize_text_field( $username );
|
344 |
+
$user_id = username_exists( $username );
|
345 |
|
346 |
+
if ( false !== $user_id ) {
|
347 |
+
$user_lockout_event_data = $lockout_event_data;
|
348 |
+
$user_lockout_event_data['temp_user'] = $user_id;
|
349 |
+
$user_lockout_event_data['temp_username'] = $username;
|
350 |
|
351 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $user_lockout_event_data );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
352 |
|
353 |
$user_count = $wpdb->get_var(
|
354 |
$wpdb->prepare(
|
355 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND (`temp_username` = %s OR `temp_user` = %d)",
|
356 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
357 |
+
$username,
|
358 |
+
$user_id
|
359 |
)
|
360 |
);
|
361 |
|
362 |
if ( $user_count >= $options['user'] ) {
|
363 |
+
$lock_user_id = $user_id;
|
|
|
|
|
364 |
}
|
|
|
365 |
} else {
|
366 |
+
$user_lockout_event_data = $lockout_event_data;
|
367 |
+
$user_lockout_event_data['temp_username'] = $username;
|
368 |
|
369 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $user_lockout_event_data );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
370 |
|
371 |
$user_count = $wpdb->get_var(
|
372 |
$wpdb->prepare(
|
373 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND `temp_username` = %s",
|
374 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
375 |
$username
|
376 |
)
|
377 |
);
|
378 |
|
379 |
if ( $user_count >= $options['user'] ) {
|
|
|
380 |
$lock_username = $username;
|
|
|
381 |
}
|
|
|
382 |
}
|
|
|
383 |
}
|
384 |
|
385 |
+
|
386 |
$error = $wpdb->last_error;
|
387 |
|
388 |
+
// TODO: Confirm that this scenario can never happen and remove this
|
389 |
if ( strlen( trim( $error ) ) > 0 ) {
|
390 |
ITSEC_Lib::create_database_tables();
|
391 |
}
|
392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
|
394 |
+
if ( false !== $lock_host || false !== $lock_user_id || false !== $lock_username ) {
|
395 |
+
$this->lockout( $module, $lock_host, $lock_user_id, $lock_username );
|
396 |
}
|
|
|
397 |
}
|
398 |
|
399 |
/**
|
715 |
* @return bool
|
716 |
*/
|
717 |
public function is_visitor_temp_whitelisted() {
|
|
|
|
|
718 |
$whitelist = $this->get_temp_whitelist();
|
719 |
$ip = ITSEC_Lib::get_ip();
|
720 |
|
721 |
+
if ( isset( $whitelist[$ip] ) && $whitelist[$ip] > ITSEC_Core::get_current_time() ) {
|
722 |
return true;
|
723 |
}
|
724 |
|
735 |
*
|
736 |
* @since 4.0
|
737 |
*
|
738 |
+
* @param string $module The module triggering the lockout.
|
739 |
+
* @param string|bool $host Host to lock out or false if the host should not be locked out.
|
740 |
+
* @param int|bool $user_id User ID to lockout or false if the host should not be locked out.
|
741 |
+
* @param string|bool $username Username to lockout or false if the host should not be locked out.
|
|
|
742 |
*
|
743 |
* @return void
|
744 |
*/
|
745 |
+
private function lockout( $module, $host, $user_id, $username ) {
|
746 |
+
global $wpdb;
|
747 |
|
|
|
748 |
|
749 |
+
$lock = "lockout_$host$user_id$username";
|
|
|
|
|
|
|
750 |
|
751 |
// Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
|
752 |
+
if ( ! ITSEC_Lib::get_lock( $lock, 180 ) ) {
|
753 |
+
return;
|
754 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
755 |
|
|
|
756 |
|
757 |
+
$module_details = $this->lockout_modules[$module];
|
758 |
|
759 |
+
$whitelisted = ITSEC_Lib::is_ip_whitelisted( $host );
|
760 |
+
$blacklisted = false;
|
761 |
|
762 |
+
$log_data = array(
|
763 |
+
'module' => $module,
|
764 |
+
'host' => $host,
|
765 |
+
'user_id' => $user_id,
|
766 |
+
'username' => $username,
|
767 |
+
'module_details' => $module_details,
|
768 |
+
'whitelisted' => $whitelisted,
|
769 |
+
'blacklisted' => false,
|
770 |
+
);
|
771 |
|
|
|
772 |
|
773 |
+
// Do a permanent ban if enabled and settings criteria are met.
|
774 |
+
if ( ITSEC_Modules::get_setting( 'global', 'blacklist' ) && false !== $host ) {
|
775 |
+
$blacklist_count = ITSEC_Modules::get_setting( 'global', 'blacklist_count' );
|
776 |
+
$blacklist_period = ITSEC_Modules::get_setting( 'global', 'blacklist_period', 7 );
|
777 |
+
$blacklist_seconds = $blacklist_period * DAY_IN_SECONDS;
|
778 |
|
779 |
+
$host_count = 1 + $wpdb->get_var(
|
780 |
+
$wpdb->prepare(
|
781 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_expire_gmt` > %s AND `lockout_host`= %s",
|
782 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - $blacklist_seconds ),
|
783 |
+
$host
|
784 |
+
)
|
785 |
+
);
|
786 |
|
787 |
+
if ( $host_count >= $blacklist_count ) {
|
788 |
+
$blacklisted = true;
|
789 |
+
$log_data['blacklisted'] = true;
|
790 |
|
791 |
+
if ( $whitelisted ) {
|
792 |
+
ITSEC_Log::add_notice( 'lockout', 'whitelisted-host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
793 |
} else {
|
794 |
+
$this->blacklist_ip( $host );
|
795 |
+
ITSEC_Log::add_action( 'lockout', 'host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
|
|
|
|
|
|
|
|
796 |
}
|
797 |
+
}
|
798 |
+
}
|
799 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
800 |
|
801 |
+
$host_expiration = false;
|
802 |
+
$user_expiration = false;
|
|
|
803 |
|
804 |
+
$lockouts_data = array(
|
805 |
+
'lockout_type' => $module_details['type'],
|
806 |
+
'lockout_start' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
807 |
+
'lockout_start_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
808 |
+
);
|
809 |
|
810 |
+
if ( $whitelisted ) {
|
811 |
+
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', 1 );
|
812 |
+
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', 1 );
|
813 |
+
} else {
|
814 |
+
$exp_seconds = ITSEC_Modules::get_setting( 'global', 'lockout_period' ) * MINUTE_IN_SECONDS;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
815 |
|
816 |
+
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() + $exp_seconds );
|
817 |
+
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() + $exp_seconds );
|
818 |
+
}
|
819 |
|
820 |
+
if ( false !== $host && ! $blacklisted ) {
|
821 |
+
$host_expiration = $lockouts_data['lockout_expire'];
|
822 |
+
$this->add_lockout_to_db( 'host', $host, $whitelisted, $lockouts_data, $log_data );
|
823 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
824 |
|
825 |
+
if ( false !== $user_id ) {
|
826 |
+
$user_expiration = $lockouts_data['lockout_expire'];
|
827 |
+
$this->add_lockout_to_db( 'user', $user_id, $whitelisted, $lockouts_data, $log_data );
|
828 |
+
}
|
829 |
|
830 |
+
if ( false !== $username ) {
|
831 |
+
$user_expiration = $lockouts_data['lockout_expire'];
|
832 |
+
$this->add_lockout_to_db( 'username', $username, $whitelisted, $lockouts_data, $log_data );
|
833 |
+
}
|
834 |
|
|
|
835 |
|
836 |
+
if ( $whitelisted ) {
|
837 |
+
// No need to send an email notice when the host is whitelisted.
|
838 |
+
ITSEC_Lib::release_lock( $lock );
|
839 |
+
}
|
840 |
|
|
|
|
|
|
|
|
|
|
|
841 |
|
842 |
+
$this->send_lockout_email( $host, $user_id, $username, $host_expiration, $user_expiration, $module_details['reason'] );
|
843 |
|
844 |
+
$lock_context = array(
|
845 |
+
'type' => $module_details['type'],
|
846 |
+
);
|
847 |
|
848 |
+
if ( false !== $user_id ) {
|
849 |
+
$lock_context['user'] = get_userdata( $user_id );
|
850 |
+
} else if ( false !== $username ) {
|
851 |
+
$lock_context['username'] = $username;
|
852 |
+
}
|
853 |
|
854 |
+
if ( false === $host ) {
|
855 |
+
$lock_context['user_lock'] = true;
|
856 |
+
}
|
857 |
|
858 |
+
ITSEC_Lib::release_lock( $lock );
|
859 |
+
$this->execute_lock( $lock_context );
|
860 |
+
}
|
861 |
|
862 |
+
/**
|
863 |
+
* Adds a record of a lockout event to the database and log the event.
|
864 |
+
*
|
865 |
+
* @param string $type The type of lockout: "host", "user", "username".
|
866 |
+
* @param string|int $id The value for the type: host's IP, user's ID, username.
|
867 |
+
* @param bool $whitelisted Whether or not the host triggering the event is whitelisted.
|
868 |
+
* @param array $lockout_data Array of base data to be inserted.
|
869 |
+
* @param array $log_data Array of data to be logged for the event.
|
870 |
+
*/
|
871 |
+
private function add_lockout_to_db( $type, $id, $whitelisted, $lockout_data, $log_data ) {
|
872 |
+
global $wpdb;
|
873 |
|
874 |
+
$lockout_data["lockout_$type"] = $id;
|
875 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_lockouts", $lockout_data );
|
876 |
|
877 |
+
if ( $whitelisted ) {
|
878 |
+
ITSEC_Log::add_notice( 'lockout', "whitelisted-host-triggered-$type-lockout", array_merge( $log_data, $lockout_data ) );
|
879 |
+
} else {
|
880 |
+
if ( 'host' === $type ) {
|
881 |
+
$code = "host-lockout::{$log_data['host']}";
|
882 |
+
} else if ( 'user' === $type ) {
|
883 |
+
$code = "user-lockout::{$log_data['user_id']}";
|
884 |
+
} else if ( 'username' === $type ) {
|
885 |
+
$code = "username-lockout::{$log_data['username']}";
|
886 |
}
|
887 |
|
888 |
+
ITSEC_Log::add_action( 'lockout', $code, array_merge( $log_data, $lockout_data ) );
|
|
|
889 |
}
|
|
|
890 |
}
|
891 |
|
892 |
/**
|
933 |
* @return void
|
934 |
*/
|
935 |
public function purge_lockouts() {
|
|
|
936 |
global $wpdb;
|
937 |
|
938 |
+
$wpdb->query( "DELETE FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_expire_gmt` < '" . date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( ( ITSEC_Modules::get_setting( 'global', 'blacklist_period' ) + 1 ) * DAY_IN_SECONDS ) ) . "';" );
|
939 |
+
$wpdb->query( "DELETE FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` < '" . date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - DAY_IN_SECONDS ) . "';" );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
940 |
}
|
941 |
|
942 |
/**
|
1156 |
|
1157 |
}
|
1158 |
|
1159 |
+
public function filter_entry_for_list_display( $entry, $code, $data ) {
|
1160 |
+
$entry['module_display'] = esc_html__( 'Lockout', 'better-wp-security' );
|
1161 |
+
|
1162 |
+
if ( 'whitelisted-host-triggered-blacklist' === $code ) {
|
1163 |
+
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Blacklist', 'better-wp-security' );
|
1164 |
+
} else if ( 'host-triggered-blacklist' === $code ) {
|
1165 |
+
$entry['description'] = esc_html__( 'Host Triggered Blacklist', 'better-wp-security' );
|
1166 |
+
} else if ( 'whitelisted-host-triggered-host-lockout' === $code ) {
|
1167 |
+
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Host Lockout', 'better-wp-security' );
|
1168 |
+
} else if ( 'host-lockout' === $code ) {
|
1169 |
+
if ( isset( $data[0] ) ) {
|
1170 |
+
$entry['description'] = sprintf( wp_kses( __( 'Host Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1171 |
+
} else {
|
1172 |
+
$entry['description'] = esc_html__( 'Host Lockout', 'better-wp-security' );
|
1173 |
+
}
|
1174 |
+
} else if ( 'whitelisted-host-triggered-user-lockout' === $code ) {
|
1175 |
+
$entry['description'] = esc_html__( 'Whitelisted Host Triggered User Lockout', 'better-wp-security' );
|
1176 |
+
} else if ( 'user-lockout' === $code ) {
|
1177 |
+
if ( isset( $data[0] ) ) {
|
1178 |
+
$user = get_user_by( 'id', $data[0] );
|
1179 |
+
}
|
1180 |
+
|
1181 |
+
if ( isset( $user ) && false !== $user ) {
|
1182 |
+
$entry['description'] = sprintf( wp_kses( __( 'User Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $user->user_login );
|
1183 |
+
} else {
|
1184 |
+
$entry['description'] = esc_html__( 'User Lockout', 'better-wp-security' );
|
1185 |
+
}
|
1186 |
+
} else if ( 'whitelisted-host-triggered-username-lockout' === $code ) {
|
1187 |
+
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Username Lockout', 'better-wp-security' );
|
1188 |
+
} else if ( 'username-lockout' === $code ) {
|
1189 |
+
if ( isset( $data[0] ) ) {
|
1190 |
+
$entry['description'] = sprintf( wp_kses( __( 'Username Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1191 |
+
} else {
|
1192 |
+
$entry['description'] = esc_html__( 'Username Lockout', 'better-wp-security' );
|
1193 |
+
}
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
return $entry;
|
1197 |
+
}
|
1198 |
+
|
1199 |
}
|
core/logger-all-logs.php
DELETED
@@ -1,260 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Log tables for Authentication Module
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @subpackage Authentication
|
8 |
-
* @since 4.0
|
9 |
-
*/
|
10 |
-
final class ITSEC_Logger_All_Logs extends ITSEC_WP_List_Table {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
|
14 |
-
parent::__construct(
|
15 |
-
array(
|
16 |
-
'singular' => 'itsec_raw_log_item',
|
17 |
-
'plural' => 'itsec_raw_log_items',
|
18 |
-
'ajax' => true
|
19 |
-
)
|
20 |
-
);
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Define type column
|
26 |
-
*
|
27 |
-
* @param array $item array of row data
|
28 |
-
*
|
29 |
-
* @return string formatted output
|
30 |
-
*
|
31 |
-
**/
|
32 |
-
function column_time( $item ) {
|
33 |
-
|
34 |
-
return $item['time'];
|
35 |
-
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Define function column
|
40 |
-
*
|
41 |
-
* @param array $item array of row data
|
42 |
-
*
|
43 |
-
* @return string formatted output
|
44 |
-
*
|
45 |
-
**/
|
46 |
-
function column_function( $item ) {
|
47 |
-
|
48 |
-
return $item['function'];
|
49 |
-
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Define priority column
|
54 |
-
*
|
55 |
-
* @param array $item array of row data
|
56 |
-
*
|
57 |
-
* @return string formatted output
|
58 |
-
*
|
59 |
-
**/
|
60 |
-
function column_priority( $item ) {
|
61 |
-
|
62 |
-
return $item['priority'];
|
63 |
-
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Define host column
|
68 |
-
*
|
69 |
-
* @param array $item array of row data
|
70 |
-
*
|
71 |
-
* @return string formatted output
|
72 |
-
*
|
73 |
-
**/
|
74 |
-
function column_host( $item ) {
|
75 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
76 |
-
|
77 |
-
$r = array();
|
78 |
-
if ( ! is_array( $item['host'] ) ) {
|
79 |
-
$item['host'] = array( $item['host'] );
|
80 |
-
}
|
81 |
-
foreach ( $item['host'] as $host ) {
|
82 |
-
if ( ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
83 |
-
$r[] = '<a href="' . esc_url( ITSEC_Lib::get_trace_ip_link( $host ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $host ) . '</a>';
|
84 |
-
}
|
85 |
-
}
|
86 |
-
$return = implode( '<br />', $r );
|
87 |
-
|
88 |
-
return $return;
|
89 |
-
|
90 |
-
}
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Define username column
|
94 |
-
*
|
95 |
-
* @param array $item array of row data
|
96 |
-
*
|
97 |
-
* @return string formatted output
|
98 |
-
*
|
99 |
-
**/
|
100 |
-
function column_user( $item ) {
|
101 |
-
|
102 |
-
if ( 0 != $item['user_id'] ) {
|
103 |
-
return '<a href="' . esc_url( admin_url( 'user-edit.php?user_id=' . $item['user_id'] ) ) . '" target="_blank" rel="noopener noreferrer">' . $item['user'] . '</a>';
|
104 |
-
} else {
|
105 |
-
return $item['user'];
|
106 |
-
}
|
107 |
-
|
108 |
-
}
|
109 |
-
|
110 |
-
/**
|
111 |
-
* Define url column
|
112 |
-
*
|
113 |
-
* @param array $item array of row data
|
114 |
-
*
|
115 |
-
* @return string formatted output
|
116 |
-
*
|
117 |
-
**/
|
118 |
-
function column_url( $item ) {
|
119 |
-
|
120 |
-
return $item['url'];
|
121 |
-
|
122 |
-
}
|
123 |
-
|
124 |
-
/**
|
125 |
-
* Define referrer column
|
126 |
-
*
|
127 |
-
* @param array $item array of row data
|
128 |
-
*
|
129 |
-
* @return string formatted output
|
130 |
-
*
|
131 |
-
**/
|
132 |
-
function column_referrer( $item ) {
|
133 |
-
|
134 |
-
return $item['referrer'];
|
135 |
-
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Define data column
|
140 |
-
*
|
141 |
-
* @param array $item array of row data
|
142 |
-
*
|
143 |
-
* @return string formatted output
|
144 |
-
*
|
145 |
-
**/
|
146 |
-
function column_data( $item ) {
|
147 |
-
|
148 |
-
global $itsec_logger;
|
149 |
-
|
150 |
-
$raw_data = maybe_unserialize( $item['data'] );
|
151 |
-
|
152 |
-
$data = apply_filters( "itsec_logger_filter_{$item['type']}_data_column_details", '', $raw_data );
|
153 |
-
|
154 |
-
if ( empty( $data ) ) {
|
155 |
-
if ( is_array( $raw_data ) && sizeof( $raw_data ) > 0 ) {
|
156 |
-
|
157 |
-
$data = $itsec_logger->print_array( $raw_data, true );
|
158 |
-
|
159 |
-
} elseif ( ! is_array( $raw_data ) ) {
|
160 |
-
|
161 |
-
$data = sanitize_text_field( $raw_data );
|
162 |
-
|
163 |
-
} else {
|
164 |
-
|
165 |
-
$data = '';
|
166 |
-
|
167 |
-
}
|
168 |
-
}
|
169 |
-
|
170 |
-
if ( strlen( $data ) > 1 ) {
|
171 |
-
|
172 |
-
$content = '<div class="itsec-all-log-dialog" id="itsec-log-all-row-' . $item['id'] . '" style="display:none;">';
|
173 |
-
$content .= $data;
|
174 |
-
$content .= '</div>';
|
175 |
-
|
176 |
-
$content .= '<a href="itsec-log-all-row-' . $item['id'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
|
177 |
-
|
178 |
-
return $content;
|
179 |
-
|
180 |
-
} else {
|
181 |
-
|
182 |
-
return '';
|
183 |
-
|
184 |
-
}
|
185 |
-
|
186 |
-
}
|
187 |
-
|
188 |
-
/**
|
189 |
-
* Define Columns
|
190 |
-
*
|
191 |
-
* @return array array of column titles
|
192 |
-
*/
|
193 |
-
public function get_columns() {
|
194 |
-
|
195 |
-
return array(
|
196 |
-
'function' => __( 'Function', 'better-wp-security' ),
|
197 |
-
'priority' => __( 'Priority', 'better-wp-security' ),
|
198 |
-
'time' => __( 'Time', 'better-wp-security' ),
|
199 |
-
'host' => __( 'Host', 'better-wp-security' ),
|
200 |
-
'user' => __( 'User', 'better-wp-security' ),
|
201 |
-
'url' => __( 'URL', 'better-wp-security' ),
|
202 |
-
'referrer' => __( 'Referrer', 'better-wp-security' ),
|
203 |
-
'data' => __( 'Data', 'better-wp-security' ),
|
204 |
-
);
|
205 |
-
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
* Prepare data for table
|
210 |
-
*
|
211 |
-
* @return void
|
212 |
-
*/
|
213 |
-
public function prepare_items() {
|
214 |
-
|
215 |
-
global $itsec_logger, $wpdb;
|
216 |
-
|
217 |
-
$columns = $this->get_columns();
|
218 |
-
$hidden = array();
|
219 |
-
$this->_column_headers = array( $columns, $hidden, false );
|
220 |
-
$per_page = 20; //20 items per page
|
221 |
-
$current_page = $this->get_pagenum();
|
222 |
-
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log`;" );
|
223 |
-
|
224 |
-
$items = $itsec_logger->get_events( 'all', array(), $per_page, ( ( $current_page - 1 ) * $per_page ), 'log_date' );
|
225 |
-
|
226 |
-
$table_data = array();
|
227 |
-
|
228 |
-
$count = 0;
|
229 |
-
|
230 |
-
foreach ( $items as $item ) { //loop through and group 404s
|
231 |
-
|
232 |
-
$table_data[ $count ]['id'] = $count;
|
233 |
-
$table_data[ $count ]['type'] = sanitize_text_field( $item['log_type'] );
|
234 |
-
$table_data[ $count ]['function'] = sanitize_text_field( $item['log_function'] );
|
235 |
-
$table_data[ $count ]['priority'] = sanitize_text_field( $item['log_priority'] );
|
236 |
-
$table_data[ $count ]['time'] = sanitize_text_field( $item['log_date'] );
|
237 |
-
$table_data[ $count ]['host'] = sanitize_text_field( $item['log_host'] );
|
238 |
-
$table_data[ $count ]['user'] = sanitize_text_field( $item['log_username'] );
|
239 |
-
$table_data[ $count ]['user_id'] = sanitize_text_field( $item['log_user'] );
|
240 |
-
$table_data[ $count ]['url'] = sanitize_text_field( $item['log_url'] );
|
241 |
-
$table_data[ $count ]['referrer'] = sanitize_text_field( $item['log_referrer'] );
|
242 |
-
$table_data[ $count ]['data'] = $item['log_data'];
|
243 |
-
|
244 |
-
$count ++;
|
245 |
-
|
246 |
-
}
|
247 |
-
|
248 |
-
$this->items = $table_data;
|
249 |
-
|
250 |
-
$this->set_pagination_args(
|
251 |
-
array(
|
252 |
-
'total_items' => $total_items,
|
253 |
-
'per_page' => $per_page,
|
254 |
-
'total_pages' => ceil( $total_items / $per_page )
|
255 |
-
)
|
256 |
-
);
|
257 |
-
|
258 |
-
}
|
259 |
-
|
260 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/logger.php
DELETED
@@ -1,554 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Handles the writing, maintenance and display of log files
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @since 4.0
|
8 |
-
*/
|
9 |
-
final class ITSEC_Logger {
|
10 |
-
|
11 |
-
private
|
12 |
-
$log_file,
|
13 |
-
$logger_displays,
|
14 |
-
$logger_modules;
|
15 |
-
|
16 |
-
/**
|
17 |
-
* @access private
|
18 |
-
*
|
19 |
-
* @var array Events that need to be logged to a file but couldn't
|
20 |
-
*/
|
21 |
-
private $events_to_log_to_file = array();
|
22 |
-
|
23 |
-
function __construct() {
|
24 |
-
|
25 |
-
$this->logger_modules = array(); //array to hold information on modules using this feature
|
26 |
-
$this->logger_displays = array(); //array to hold metabox information
|
27 |
-
|
28 |
-
add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
|
29 |
-
add_action( 'plugins_loaded', array( $this, 'write_pending_events_to_file' ) );
|
30 |
-
|
31 |
-
//Run database cleanup daily with cron
|
32 |
-
if ( ! wp_next_scheduled( 'itsec_purge_logs' ) ) {
|
33 |
-
wp_schedule_event( time(), 'daily', 'itsec_purge_logs' );
|
34 |
-
}
|
35 |
-
|
36 |
-
add_action( 'itsec_purge_logs', array( $this, 'purge_logs' ) );
|
37 |
-
|
38 |
-
}
|
39 |
-
|
40 |
-
/**
|
41 |
-
* Gets events from the logs for a specified module
|
42 |
-
*
|
43 |
-
* @param string $module module or type of events to fetch
|
44 |
-
* @param array $params array of extra query parameters
|
45 |
-
* @param int $limit the maximum number of rows to retrieve
|
46 |
-
* @param int $offset the offset of the data
|
47 |
-
* @param string $order order by column
|
48 |
-
* @param bool $direction false for descending or true for ascending
|
49 |
-
*
|
50 |
-
* @return bool|mixed false on error, null if no events or array of events
|
51 |
-
*/
|
52 |
-
public function get_events( $module, $params = array(), $limit = null, $offset = null, $order = null, $direction = false ) {
|
53 |
-
|
54 |
-
global $wpdb;
|
55 |
-
|
56 |
-
if ( isset( $module ) !== true || strlen( $module ) < 1 ) {
|
57 |
-
return false;
|
58 |
-
}
|
59 |
-
|
60 |
-
if ( sizeof( $params ) > 0 || $module != 'all' ) {
|
61 |
-
$where = " WHERE ";
|
62 |
-
} else {
|
63 |
-
$where = '';
|
64 |
-
}
|
65 |
-
|
66 |
-
$param_search = '';
|
67 |
-
|
68 |
-
if ( $module == 'all' ) {
|
69 |
-
|
70 |
-
$module_sql = '';
|
71 |
-
$and = '';
|
72 |
-
|
73 |
-
} else {
|
74 |
-
|
75 |
-
$module_sql = "`log_type` = '" . esc_sql( $module ) . "'";
|
76 |
-
$and = ' AND ';
|
77 |
-
|
78 |
-
}
|
79 |
-
|
80 |
-
if ( $direction === false ) {
|
81 |
-
|
82 |
-
$order_direction = ' DESC';
|
83 |
-
|
84 |
-
} else {
|
85 |
-
|
86 |
-
$order_direction = ' ASC';
|
87 |
-
|
88 |
-
}
|
89 |
-
|
90 |
-
if ( $order !== null ) {
|
91 |
-
|
92 |
-
$order_statement = ' ORDER BY `' . esc_sql( $order ) . '`';
|
93 |
-
|
94 |
-
} else {
|
95 |
-
|
96 |
-
$order_statement = ' ORDER BY `log_id`';
|
97 |
-
|
98 |
-
}
|
99 |
-
|
100 |
-
if ( $limit !== null ) {
|
101 |
-
|
102 |
-
if ( $offset !== null ) {
|
103 |
-
|
104 |
-
$result_limit = ' LIMIT ' . absint( $offset ) . ', ' . absint( $limit );
|
105 |
-
|
106 |
-
} else {
|
107 |
-
|
108 |
-
$result_limit = ' LIMIT ' . absint( $limit );
|
109 |
-
|
110 |
-
}
|
111 |
-
|
112 |
-
} else {
|
113 |
-
|
114 |
-
$result_limit = '';
|
115 |
-
|
116 |
-
}
|
117 |
-
|
118 |
-
if ( sizeof( $params ) > 0 ) {
|
119 |
-
|
120 |
-
foreach ( $params as $field => $value ) {
|
121 |
-
|
122 |
-
if ( gettype( $value ) != 'integer' ) {
|
123 |
-
$param_search .= $and . "`" . esc_sql( $field ) . "`='" . esc_sql( $value ) . "'";
|
124 |
-
} else {
|
125 |
-
$param_search .= $and . "`" . esc_sql( $field ) . "`=" . esc_sql( $value ) . "";
|
126 |
-
}
|
127 |
-
|
128 |
-
}
|
129 |
-
|
130 |
-
}
|
131 |
-
|
132 |
-
$items = $wpdb->get_results( "SELECT * FROM `" . $wpdb->base_prefix . "itsec_log`" . $where . $module_sql . $param_search . $order_statement . $order_direction . $result_limit . ";", ARRAY_A );
|
133 |
-
|
134 |
-
return $items;
|
135 |
-
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Logs events sent by other modules or systems
|
140 |
-
*
|
141 |
-
* @param string $module the module requesting the log entry
|
142 |
-
* @param int $priority the priority of the log entry (1-10)
|
143 |
-
* @param array $data extra data to log (non-indexed data would be good here)
|
144 |
-
* @param string $host the remote host triggering the event
|
145 |
-
* @param string $username the username triggering the event
|
146 |
-
* @param string $user the user id triggering the event
|
147 |
-
* @param string $url the url triggering the event
|
148 |
-
* @param string $referrer the referrer to the url (if applicable)
|
149 |
-
*
|
150 |
-
* @return void
|
151 |
-
*/
|
152 |
-
public function log_event( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
153 |
-
|
154 |
-
if ( isset( $this->logger_modules[ $module ] ) ) {
|
155 |
-
$type = ITSEC_Modules::get_setting( 'global', 'log_type' );
|
156 |
-
|
157 |
-
if ( 'database' === $type || 'both' === $type ) {
|
158 |
-
$this->log_event_to_db( $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
159 |
-
}
|
160 |
-
|
161 |
-
if ( 'file' === $type || 'both' === $type ) {
|
162 |
-
$this->log_event_to_file( $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
163 |
-
}
|
164 |
-
|
165 |
-
}
|
166 |
-
|
167 |
-
do_action( 'itsec_log_event', $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
168 |
-
|
169 |
-
}
|
170 |
-
|
171 |
-
private function log_event_to_db( $module, $priority, $data, $host, $username, $user, $url, $referrer ) {
|
172 |
-
global $wpdb, $itsec_globals;
|
173 |
-
|
174 |
-
$options = $this->logger_modules[ $module ];
|
175 |
-
|
176 |
-
$values = array(
|
177 |
-
'log_type' => $options['type'],
|
178 |
-
'log_priority' => intval( $priority ),
|
179 |
-
'log_function' => $options['function'],
|
180 |
-
'log_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
181 |
-
'log_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
182 |
-
'log_host' => sanitize_text_field( $host ),
|
183 |
-
'log_username' => sanitize_text_field( $username ),
|
184 |
-
'log_user' => intval( $user ),
|
185 |
-
'log_url' => $url,
|
186 |
-
'log_referrer' => $referrer,
|
187 |
-
'log_data' => serialize( $data ),
|
188 |
-
);
|
189 |
-
|
190 |
-
$columns = '`' . implode( '`, `', array_keys( $values ) ) . '`';
|
191 |
-
$placeholders = '%s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s';
|
192 |
-
|
193 |
-
$query_format = "INSERT INTO `{$wpdb->base_prefix}itsec_log` ($columns) VALUES ($placeholders)";
|
194 |
-
|
195 |
-
$cached_show_errors_setting = $wpdb->hide_errors();
|
196 |
-
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
197 |
-
|
198 |
-
if ( ! $result ) {
|
199 |
-
$wpdb->show_errors();
|
200 |
-
|
201 |
-
ITSEC_Lib::create_database_tables();
|
202 |
-
|
203 |
-
// Attempt the query again. Since errors will now be shown, a remaining issue will be display an error.
|
204 |
-
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
205 |
-
}
|
206 |
-
|
207 |
-
// Set $wpdb->show_errors back to its original setting.
|
208 |
-
$wpdb->show_errors( $cached_show_errors_setting );
|
209 |
-
}
|
210 |
-
|
211 |
-
private function log_event_to_file( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
212 |
-
global $itsec_globals;
|
213 |
-
|
214 |
-
// If the file can't be prepared, store the events up to write later (at plugins_loaded)
|
215 |
-
if ( false === $this->prepare_log_file() ) {
|
216 |
-
$this->events_to_log_to_file[] = compact( 'module', 'priority', 'data', 'host', 'username', 'user', 'url', 'referrer' );
|
217 |
-
return;
|
218 |
-
}
|
219 |
-
|
220 |
-
$options = $this->logger_modules[ $module ];
|
221 |
-
|
222 |
-
$file_data = $this->sanitize_array( $data, true );
|
223 |
-
|
224 |
-
$message =
|
225 |
-
$options['type'] . ',' .
|
226 |
-
intval( $priority ) . ',' .
|
227 |
-
$options['function'] . ',' .
|
228 |
-
date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ) . ',' .
|
229 |
-
date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . ',' .
|
230 |
-
sanitize_text_field( $host ) . ',' .
|
231 |
-
sanitize_text_field( $username ) . ',' .
|
232 |
-
( intval( $user ) === 0 ? '' : intval( $user ) ) . ',' .
|
233 |
-
esc_sql( $url ) . ',' .
|
234 |
-
esc_sql( $referrer ) . ',' .
|
235 |
-
maybe_serialize( $file_data );
|
236 |
-
|
237 |
-
$this->add_to_log_file( $message );
|
238 |
-
|
239 |
-
}
|
240 |
-
|
241 |
-
public function write_pending_events_to_file() {
|
242 |
-
foreach ( $this->events_to_log_to_file as $event ) {
|
243 |
-
call_user_func_array( array( $this, 'log_event_to_file' ), $event );
|
244 |
-
}
|
245 |
-
}
|
246 |
-
|
247 |
-
/**
|
248 |
-
* Displays into box for logs page
|
249 |
-
*
|
250 |
-
* @since 4.0
|
251 |
-
*
|
252 |
-
* @return void
|
253 |
-
*/
|
254 |
-
public function metabox_all_logs() {
|
255 |
-
|
256 |
-
$log_filter = isset( $_GET['itsec_log_filter'] ) ? sanitize_text_field( $_GET['itsec_log_filter'] ) : 'all-log-data';
|
257 |
-
$callback = null;
|
258 |
-
|
259 |
-
echo '<p>' . __( 'To adjust logging options visit the global settings page.', 'better-wp-security' ) . '</p>';
|
260 |
-
|
261 |
-
echo '<label for="itsec_log_filter"><strong>' . __( 'Select Filter: ', 'better-wp-security' ) . '</strong></label>';
|
262 |
-
echo '<select id="itsec_log_filter" name="itsec_log_filter">';
|
263 |
-
echo '<option value="all-log-data" ' . selected( $log_filter, 'all-log-data' ) . '>' . __( 'All Log Data', 'better-wp-security' ) . '</option>';
|
264 |
-
|
265 |
-
if ( sizeof( $this->logger_displays ) > 0 ) {
|
266 |
-
|
267 |
-
foreach ( $this->logger_displays as $display ) {
|
268 |
-
|
269 |
-
if ( $display['module'] === $log_filter ) {
|
270 |
-
$callback = $display['callback'];
|
271 |
-
}
|
272 |
-
|
273 |
-
echo '<option value="' . $display['module'] . '" ' . selected( $log_filter, $display['module'] ) . '>' . $display['title'] . '</option>';
|
274 |
-
|
275 |
-
}
|
276 |
-
|
277 |
-
}
|
278 |
-
|
279 |
-
echo '</select>';
|
280 |
-
|
281 |
-
if ( $log_filter === 'all-log-data' || $callback === null ) {
|
282 |
-
|
283 |
-
$this->all_logs_content();
|
284 |
-
|
285 |
-
} else {
|
286 |
-
|
287 |
-
call_user_func_array( $callback, array() );
|
288 |
-
|
289 |
-
}
|
290 |
-
|
291 |
-
}
|
292 |
-
|
293 |
-
/**
|
294 |
-
* A better print array function to display array data in the logs
|
295 |
-
*
|
296 |
-
* @since 4.2
|
297 |
-
*
|
298 |
-
* @param array $array_items array to print or return
|
299 |
-
* @param bool $return true to return the data false to echo it
|
300 |
-
*/
|
301 |
-
public function print_array( $array_items, $return = true ) {
|
302 |
-
|
303 |
-
$items = '';
|
304 |
-
|
305 |
-
//make sure we're working with an array
|
306 |
-
if ( ! is_array( $array_items ) ) {
|
307 |
-
return false;
|
308 |
-
}
|
309 |
-
|
310 |
-
if ( sizeof( $array_items ) > 0 ) {
|
311 |
-
|
312 |
-
$items .= '<ul>';
|
313 |
-
|
314 |
-
foreach ( $array_items as $key => $item ) {
|
315 |
-
|
316 |
-
if ( is_array( $item ) ) {
|
317 |
-
|
318 |
-
$items .= '<li>';
|
319 |
-
|
320 |
-
if ( ! is_numeric( $key ) ) {
|
321 |
-
$items .= '<h3>' . esc_html( $key ) . '</h3>';
|
322 |
-
}
|
323 |
-
|
324 |
-
$items .= $this->print_array( $item, true ) . PHP_EOL;
|
325 |
-
|
326 |
-
$items .= '</li>';
|
327 |
-
|
328 |
-
} else {
|
329 |
-
|
330 |
-
if ( strlen( trim( $item ) ) > 0 ) {
|
331 |
-
$items .= '<li><h3>' . esc_html( $key ) . ' = ' . esc_html( $item ) . '</h3></li>' . PHP_EOL;
|
332 |
-
}
|
333 |
-
|
334 |
-
}
|
335 |
-
|
336 |
-
}
|
337 |
-
|
338 |
-
$items .= '</ul>';
|
339 |
-
|
340 |
-
}
|
341 |
-
|
342 |
-
return $items;
|
343 |
-
|
344 |
-
}
|
345 |
-
|
346 |
-
/**
|
347 |
-
* Purges database logs and rotates file logs (when needed)
|
348 |
-
*
|
349 |
-
* @return void
|
350 |
-
*/
|
351 |
-
public function purge_logs( $purge_all = false ) {
|
352 |
-
|
353 |
-
global $wpdb, $itsec_globals, $itsec_clear_all_logs;
|
354 |
-
|
355 |
-
if ( true === $purge_all ) {
|
356 |
-
|
357 |
-
if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_clear_logs' ) ) {
|
358 |
-
return;
|
359 |
-
}
|
360 |
-
|
361 |
-
$wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log`;" );
|
362 |
-
|
363 |
-
} else {
|
364 |
-
|
365 |
-
//Clean up the database log first
|
366 |
-
$type = ITSEC_Modules::get_setting( 'global', 'log_type' );
|
367 |
-
|
368 |
-
if ( 'database' === $type || 'both' === $type ) {
|
369 |
-
|
370 |
-
$wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log` WHERE `log_date_gmt` < '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( ITSEC_Modules::get_setting( 'global', 'log_rotation' ) * DAY_IN_SECONDS ) ) . "';" );
|
371 |
-
|
372 |
-
} else {
|
373 |
-
|
374 |
-
$wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log`;" );
|
375 |
-
|
376 |
-
}
|
377 |
-
|
378 |
-
$this->rotate_log();
|
379 |
-
|
380 |
-
}
|
381 |
-
|
382 |
-
}
|
383 |
-
|
384 |
-
/**
|
385 |
-
* Register modules that will use the logger service
|
386 |
-
*
|
387 |
-
* @return void
|
388 |
-
*/
|
389 |
-
public function register_modules() {
|
390 |
-
|
391 |
-
$this->logger_modules = apply_filters( 'itsec_logger_modules', $this->logger_modules );
|
392 |
-
$this->logger_displays = apply_filters( 'itsec_logger_displays', $this->logger_displays );
|
393 |
-
|
394 |
-
}
|
395 |
-
|
396 |
-
/**
|
397 |
-
* Rotates the event-log.log file when called
|
398 |
-
*
|
399 |
-
* Adapted from http://www.phpclasses.org/browse/file/49471.html
|
400 |
-
*
|
401 |
-
* @return void
|
402 |
-
*/
|
403 |
-
private function rotate_log() {
|
404 |
-
$log_file = $this->get_log_file();
|
405 |
-
$max_size = 1024 * 1024 * 10; // 10MiB
|
406 |
-
|
407 |
-
if ( ! @file_exists( $log_file ) || @filesize( $log_file ) < $max_size ) {
|
408 |
-
return;
|
409 |
-
}
|
410 |
-
|
411 |
-
// rotate
|
412 |
-
$path_info = pathinfo( $log_file );
|
413 |
-
$base_directory = $path_info['dirname'];
|
414 |
-
$base_name = $path_info['basename'];
|
415 |
-
$num_map = array();
|
416 |
-
|
417 |
-
foreach ( new DirectoryIterator( $base_directory ) as $fInfo ) {
|
418 |
-
|
419 |
-
if ( $fInfo->isDot() || ! $fInfo->isFile() ) {
|
420 |
-
continue;
|
421 |
-
}
|
422 |
-
|
423 |
-
if ( preg_match( '/^' . $base_name . '\.?([0-9]*)$/', $fInfo->getFilename(), $matches ) ) {
|
424 |
-
|
425 |
-
$num = $matches[1];
|
426 |
-
$old_file = $fInfo->getFilename();
|
427 |
-
|
428 |
-
if ( $num == '' ) {
|
429 |
-
$num = - 1;
|
430 |
-
}
|
431 |
-
|
432 |
-
$num_map[ $num ] = $old_file;
|
433 |
-
|
434 |
-
}
|
435 |
-
|
436 |
-
}
|
437 |
-
|
438 |
-
krsort( $num_map );
|
439 |
-
|
440 |
-
foreach ( $num_map as $num => $old_file ) {
|
441 |
-
|
442 |
-
$new_file = $num + 1;
|
443 |
-
@rename( $base_directory . DIRECTORY_SEPARATOR . $old_file, $log_file . '.' . $new_file );
|
444 |
-
|
445 |
-
}
|
446 |
-
|
447 |
-
$this->prepare_log_file();
|
448 |
-
|
449 |
-
}
|
450 |
-
|
451 |
-
/**
|
452 |
-
* Sanitizes strings in a given array recursively
|
453 |
-
*
|
454 |
-
* @param array $array array to sanitize
|
455 |
-
* @param bool $to_string true if output should be string or false for array output
|
456 |
-
*
|
457 |
-
* @return mixed sanitized array or string
|
458 |
-
*/
|
459 |
-
private function sanitize_array( $array, $to_string = false ) {
|
460 |
-
|
461 |
-
$sanitized_array = array();
|
462 |
-
$string = '';
|
463 |
-
|
464 |
-
//Loop to sanitize each piece of data
|
465 |
-
foreach ( $array as $key => $value ) {
|
466 |
-
|
467 |
-
if ( is_array( $value ) ) {
|
468 |
-
|
469 |
-
if ( $to_string === false ) {
|
470 |
-
$sanitized_array[ esc_sql( $key ) ] = $this->sanitize_array( $value );
|
471 |
-
} else {
|
472 |
-
$string .= esc_sql( $key ) . '=' . $this->sanitize_array( $value, true );
|
473 |
-
}
|
474 |
-
|
475 |
-
} else {
|
476 |
-
|
477 |
-
$sanitized_array[ esc_sql( $key ) ] = esc_sql( $value );
|
478 |
-
|
479 |
-
$string .= esc_sql( $key ) . '=' . esc_sql( $value );
|
480 |
-
|
481 |
-
}
|
482 |
-
|
483 |
-
}
|
484 |
-
|
485 |
-
if ( $to_string === false ) {
|
486 |
-
return $sanitized_array;
|
487 |
-
} else {
|
488 |
-
return $string;
|
489 |
-
}
|
490 |
-
|
491 |
-
}
|
492 |
-
|
493 |
-
private function get_log_file() {
|
494 |
-
if ( isset( $this->log_file ) ) {
|
495 |
-
return $this->log_file;
|
496 |
-
}
|
497 |
-
|
498 |
-
$log_location = ITSEC_Modules::get_setting( 'global', 'log_location' );
|
499 |
-
$log_info = ITSEC_Modules::get_setting( 'global', 'log_info' );
|
500 |
-
|
501 |
-
if ( empty( $log_info ) ) {
|
502 |
-
// We need wp_generate_password() to create a cryptographically secure file name
|
503 |
-
if ( ! function_exists( 'wp_generate_password' ) ) {
|
504 |
-
$this->log_file = false;
|
505 |
-
return false;
|
506 |
-
}
|
507 |
-
|
508 |
-
$log_info = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
|
509 |
-
|
510 |
-
ITSEC_Modules::set_setting( 'global', 'log_info', $log_info );
|
511 |
-
}
|
512 |
-
|
513 |
-
$this->log_file = "$log_location/event-log-$log_info.log";
|
514 |
-
|
515 |
-
return $this->log_file;
|
516 |
-
}
|
517 |
-
|
518 |
-
/**
|
519 |
-
* Creates a new log file and adds header information (if needed)
|
520 |
-
*
|
521 |
-
* @return void
|
522 |
-
*/
|
523 |
-
private function prepare_log_file() {
|
524 |
-
$log_file = $this->get_log_file();
|
525 |
-
|
526 |
-
// We can't prepare a file if we can't get the file name
|
527 |
-
if ( false === $log_file ) {
|
528 |
-
return false;
|
529 |
-
}
|
530 |
-
|
531 |
-
if ( ! file_exists( $log_file ) ) { //only if current log file doesn't exist
|
532 |
-
|
533 |
-
$header = 'log_type,log_priority,log_function,log_date,log_date_gmt,log_host,log_username,log_user,log_url,log_referrer,log_data';
|
534 |
-
|
535 |
-
$this->add_to_log_file( $header );
|
536 |
-
|
537 |
-
}
|
538 |
-
|
539 |
-
return true;
|
540 |
-
|
541 |
-
}
|
542 |
-
|
543 |
-
private function add_to_log_file( $details ) {
|
544 |
-
$log_file = $this->get_log_file();
|
545 |
-
|
546 |
-
if ( false === $log_file ) {
|
547 |
-
return false;
|
548 |
-
}
|
549 |
-
|
550 |
-
@error_log( $details . PHP_EOL, 3, $log_file );
|
551 |
-
|
552 |
-
return true;
|
553 |
-
}
|
554 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/404-detection/class-itsec-four-oh-four-log.php
DELETED
@@ -1,233 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Log 404 errors for Intrusion Detection Module
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @subpackage Intrusion-Detection
|
8 |
-
* @since 4.0
|
9 |
-
*/
|
10 |
-
final class ITSEC_Four_Oh_Four_Log extends ITSEC_WP_List_Table {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
|
14 |
-
parent::__construct(
|
15 |
-
array(
|
16 |
-
'singular' => 'itsec_four_oh_four_log_item',
|
17 |
-
'plural' => 'itsec_four_oh_four_log_items',
|
18 |
-
'ajax' => true
|
19 |
-
)
|
20 |
-
);
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Define first time column
|
26 |
-
*
|
27 |
-
* @param array $item array of row data
|
28 |
-
*
|
29 |
-
* @return string formatted output
|
30 |
-
*
|
31 |
-
**/
|
32 |
-
function column_first_time( $item ) {
|
33 |
-
|
34 |
-
return $item['first_time'];
|
35 |
-
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Define time column
|
40 |
-
*
|
41 |
-
* @param array $item array of row data
|
42 |
-
*
|
43 |
-
* @return string formatted output
|
44 |
-
*
|
45 |
-
**/
|
46 |
-
function column_last_time( $item ) {
|
47 |
-
|
48 |
-
return $item['last_time'];
|
49 |
-
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Define count column
|
54 |
-
*
|
55 |
-
* @param array $item array of row data
|
56 |
-
*
|
57 |
-
* @return string formatted output
|
58 |
-
*
|
59 |
-
**/
|
60 |
-
function column_count( $item ) {
|
61 |
-
|
62 |
-
return $item['count'];
|
63 |
-
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Define uri column
|
68 |
-
*
|
69 |
-
* @param array $item array of row data
|
70 |
-
*
|
71 |
-
* @return string formatted output
|
72 |
-
*
|
73 |
-
**/
|
74 |
-
function column_uri( $item ) {
|
75 |
-
|
76 |
-
global $itsec_logger;
|
77 |
-
|
78 |
-
$items = $itsec_logger->get_events( 'four_oh_four', array( 'log_url' => $item['uri'] ) );
|
79 |
-
|
80 |
-
echo '<a href="itsec_404_details_' . $item['id'] . '" class="dialog">' . $item['uri'] . '</a>';
|
81 |
-
|
82 |
-
echo '<div id="itsec_404_details_' . $item['id'] . '" style="display:none;">';
|
83 |
-
|
84 |
-
echo '<h3>' . __( 'Details for ' . $item['uri'], 'better-wp-security' ) . '</h3>';
|
85 |
-
|
86 |
-
echo '<ol class="file_change_detail_list">';
|
87 |
-
|
88 |
-
foreach ( $items as $item => $details ) {
|
89 |
-
$data = maybe_unserialize( $details['log_data'] );
|
90 |
-
echo '<li class="404_detail"><strong>' . __( 'Time', 'better-wp-security' ) . '</strong>: ' . $details['log_date'] . '<br /><strong>' . __( 'Host', 'better-wp-security' ) . '</strong>: ' . $details['log_host'] . '<br /><strong>' . __( 'Referrer', 'better-wp-security' ) . '</strong>: ' . esc_html( $details['log_referrer'] ) . '<br /><strong>' . __( 'Query', 'better-wp-security' ) . '</strong>: ' . esc_html( $data['query_string'] ) . '</li>';
|
91 |
-
}
|
92 |
-
|
93 |
-
echo '</ol>';
|
94 |
-
|
95 |
-
echo '</div>';
|
96 |
-
|
97 |
-
}
|
98 |
-
|
99 |
-
/**
|
100 |
-
* Define Columns
|
101 |
-
*
|
102 |
-
* @return array array of column titles
|
103 |
-
*/
|
104 |
-
public function get_columns() {
|
105 |
-
|
106 |
-
return array(
|
107 |
-
'uri' => __( 'Location', 'better-wp-security' ),
|
108 |
-
'count' => __( 'Count', 'better-wp-security' ),
|
109 |
-
'first_time' => __( 'First Recorded', 'better-wp-security' ),
|
110 |
-
'last_time' => __( 'Last Recorded', 'better-wp-security' ),
|
111 |
-
);
|
112 |
-
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Define Sortable Columns
|
117 |
-
*
|
118 |
-
* @return array of column titles that can be sorted
|
119 |
-
*/
|
120 |
-
public function get_sortable_columns() {
|
121 |
-
|
122 |
-
$order = ( empty( $_GET['order'] ) ) ? false : true;
|
123 |
-
|
124 |
-
$sortable_columns = array(
|
125 |
-
'uri' => array( 'uri', $order ),
|
126 |
-
'count' => array( 'count', $order ),
|
127 |
-
'first_time' => array( 'first_time', $order ),
|
128 |
-
'last_time' => array( 'last_time', $order ),
|
129 |
-
);
|
130 |
-
|
131 |
-
return $sortable_columns;
|
132 |
-
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Prepare data for table
|
137 |
-
*
|
138 |
-
* @return void
|
139 |
-
*/
|
140 |
-
public function prepare_items() {
|
141 |
-
|
142 |
-
global $itsec_logger;
|
143 |
-
|
144 |
-
$columns = $this->get_columns();
|
145 |
-
$hidden = array();
|
146 |
-
$sortable = $this->get_sortable_columns();
|
147 |
-
$this->_column_headers = array( $columns, $hidden, $sortable );
|
148 |
-
|
149 |
-
$items = $itsec_logger->get_events( 'four_oh_four' );
|
150 |
-
|
151 |
-
$table_data = array();
|
152 |
-
|
153 |
-
foreach ( $items as $item ) { //loop through and group 404s
|
154 |
-
|
155 |
-
if ( isset( $table_data[ $item['log_url'] ] ) ) {
|
156 |
-
|
157 |
-
$table_data[ $item['log_url'] ]['id'] = $item['log_id'];
|
158 |
-
$table_data[ $item['log_url'] ]['count'] = $table_data[ $item['log_url'] ]['count'] + 1;
|
159 |
-
$table_data[ $item['log_url'] ]['last_time'] = strtotime( $table_data[ $item['log_url'] ]['last_time'] ) > strtotime( $item['log_date'] ) ? $table_data[ $item['log_url'] ]['last_time'] : sanitize_text_field( $item['log_date'] );
|
160 |
-
$table_data[ $item['log_url'] ]['first_time'] = strtotime( $table_data[ $item['log_url'] ]['first_time'] ) < strtotime( $item['log_date'] ) ? $table_data[ $item['log_url'] ]['first_time'] : sanitize_text_field( $item['log_date'] );
|
161 |
-
$table_data[ $item['log_url'] ]['uri'] = sanitize_text_field( $item['log_url'] );
|
162 |
-
|
163 |
-
} else {
|
164 |
-
|
165 |
-
$table_data[ $item['log_url'] ]['id'] = $item['log_id'];
|
166 |
-
$table_data[ $item['log_url'] ]['count'] = 1;
|
167 |
-
$table_data[ $item['log_url'] ]['last_time'] = sanitize_text_field( $item['log_date'] );
|
168 |
-
$table_data[ $item['log_url'] ]['first_time'] = sanitize_text_field( $item['log_date'] );
|
169 |
-
$table_data[ $item['log_url'] ]['uri'] = sanitize_text_field( $item['log_url'] );
|
170 |
-
|
171 |
-
}
|
172 |
-
|
173 |
-
}
|
174 |
-
|
175 |
-
usort( $table_data, array( $this, 'sortrows' ) );
|
176 |
-
|
177 |
-
$per_page = 20; //20 items per page
|
178 |
-
$current_page = $this->get_pagenum();
|
179 |
-
$total_items = count( $table_data );
|
180 |
-
|
181 |
-
$table_data = array_slice( $table_data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
182 |
-
|
183 |
-
$this->items = $table_data;
|
184 |
-
|
185 |
-
$this->set_pagination_args(
|
186 |
-
array(
|
187 |
-
'total_items' => $total_items,
|
188 |
-
'per_page' => $per_page,
|
189 |
-
'total_pages' => ceil( $total_items / $per_page )
|
190 |
-
)
|
191 |
-
);
|
192 |
-
|
193 |
-
}
|
194 |
-
|
195 |
-
/**
|
196 |
-
* Sorts rows by count in descending order
|
197 |
-
*
|
198 |
-
* @param array $a first array to compare
|
199 |
-
* @param array $b second array to compare
|
200 |
-
*
|
201 |
-
* @return int comparison result
|
202 |
-
*/
|
203 |
-
function sortrows( $a, $b ) {
|
204 |
-
|
205 |
-
// If no sort, default to count
|
206 |
-
$orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'last_time';
|
207 |
-
|
208 |
-
// If no order, default to desc
|
209 |
-
$order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
|
210 |
-
|
211 |
-
if ( $orderby == 'count' ) {
|
212 |
-
|
213 |
-
if ( intval( $a[ $orderby ] ) < intval( $b[ $orderby ] ) ) {
|
214 |
-
$result = - 1;
|
215 |
-
} elseif ( intval( $a[ $orderby ] ) === intval( $b[ $orderby ] ) ) {
|
216 |
-
$result = 0;
|
217 |
-
} else {
|
218 |
-
$result = 1;
|
219 |
-
}
|
220 |
-
|
221 |
-
} else {
|
222 |
-
|
223 |
-
// Determine sort order
|
224 |
-
$result = strcmp( $a[ $orderby ], $b[ $orderby ] );
|
225 |
-
|
226 |
-
}
|
227 |
-
|
228 |
-
// Send final sort direction to usort
|
229 |
-
return ( $order === 'asc' ) ? $result : - $result;
|
230 |
-
|
231 |
-
}
|
232 |
-
|
233 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/404-detection/class-itsec-four-oh-four.php
CHANGED
@@ -9,8 +9,6 @@ class ITSEC_Four_Oh_Four {
|
|
9 |
$this->settings = ITSEC_Modules::get_settings( '404-detection' );
|
10 |
|
11 |
add_filter( 'itsec_lockout_modules', array( $this, 'register_lockout' ) );
|
12 |
-
add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
|
13 |
-
add_filter( 'itsec_logger_displays', array( $this, 'register_logger_displays' ) );
|
14 |
|
15 |
add_action( 'wp', array( $this, 'check_404' ), 9999 );
|
16 |
}
|
@@ -23,7 +21,7 @@ class ITSEC_Four_Oh_Four {
|
|
23 |
public function check_404() {
|
24 |
|
25 |
/** @var ITSEC_Lockout $itsec_lockout */
|
26 |
-
global $
|
27 |
|
28 |
if ( ! is_404() ) {
|
29 |
return;
|
@@ -31,27 +29,14 @@ class ITSEC_Four_Oh_Four {
|
|
31 |
|
32 |
$uri = explode( '?', $_SERVER['REQUEST_URI'] );
|
33 |
|
34 |
-
if (
|
35 |
-
//
|
36 |
return;
|
37 |
}
|
38 |
|
39 |
-
$
|
40 |
-
'four_oh_four',
|
41 |
-
3,
|
42 |
-
array(
|
43 |
-
'query_string' => isset( $uri[1] ) ? esc_sql( $uri[1] ) : '',
|
44 |
-
),
|
45 |
-
ITSEC_Lib::get_ip(),
|
46 |
-
'',
|
47 |
-
'',
|
48 |
-
esc_sql( $uri[0] ),
|
49 |
-
isset( $_SERVER['HTTP_REFERER'] ) ? esc_sql( $_SERVER['HTTP_REFERER'] ) : ''
|
50 |
-
);
|
51 |
-
|
52 |
-
$path_info = pathinfo( $uri[0] );
|
53 |
|
54 |
-
if ( !
|
55 |
|
56 |
$itsec_lockout->do_lockout( 'four_oh_four' );
|
57 |
|
@@ -79,61 +64,4 @@ class ITSEC_Four_Oh_Four {
|
|
79 |
|
80 |
}
|
81 |
|
82 |
-
/**
|
83 |
-
* Register 404 and file change detection for logger
|
84 |
-
*
|
85 |
-
* @param array $logger_modules array of logger modules
|
86 |
-
*
|
87 |
-
* @return array array of logger modules
|
88 |
-
*/
|
89 |
-
public function register_logger( $logger_modules ) {
|
90 |
-
|
91 |
-
$logger_modules['four_oh_four'] = array(
|
92 |
-
'type' => 'four_oh_four',
|
93 |
-
'function' => __( '404 Error', 'better-wp-security' ),
|
94 |
-
);
|
95 |
-
|
96 |
-
return $logger_modules;
|
97 |
-
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* Array of displays for the logs screen
|
102 |
-
*
|
103 |
-
* @since 4.0
|
104 |
-
*
|
105 |
-
* @param array $logger_displays metabox array
|
106 |
-
*
|
107 |
-
* @return array metabox array
|
108 |
-
*/
|
109 |
-
public function register_logger_displays( $logger_displays ) {
|
110 |
-
|
111 |
-
$logger_displays[] = array(
|
112 |
-
'module' => 'four_oh_four',
|
113 |
-
'title' => __( '404 Errors Found', 'better-wp-security' ),
|
114 |
-
'callback' => array( $this, 'logs_metabox_content' )
|
115 |
-
);
|
116 |
-
|
117 |
-
return $logger_displays;
|
118 |
-
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Render the settings metabox
|
123 |
-
*
|
124 |
-
* @return void
|
125 |
-
*/
|
126 |
-
public function logs_metabox_content() {
|
127 |
-
|
128 |
-
if ( ! class_exists( 'ITSEC_Four_Oh_Four_Log' ) ) {
|
129 |
-
require( dirname( __FILE__ ) . '/class-itsec-four-oh-four-log.php' );
|
130 |
-
}
|
131 |
-
|
132 |
-
$log_display = new ITSEC_Four_Oh_Four_Log();
|
133 |
-
|
134 |
-
$log_display->prepare_items();
|
135 |
-
$log_display->display();
|
136 |
-
|
137 |
-
}
|
138 |
-
|
139 |
}
|
9 |
$this->settings = ITSEC_Modules::get_settings( '404-detection' );
|
10 |
|
11 |
add_filter( 'itsec_lockout_modules', array( $this, 'register_lockout' ) );
|
|
|
|
|
12 |
|
13 |
add_action( 'wp', array( $this, 'check_404' ), 9999 );
|
14 |
}
|
21 |
public function check_404() {
|
22 |
|
23 |
/** @var ITSEC_Lockout $itsec_lockout */
|
24 |
+
global $itsec_lockout;
|
25 |
|
26 |
if ( ! is_404() ) {
|
27 |
return;
|
29 |
|
30 |
$uri = explode( '?', $_SERVER['REQUEST_URI'] );
|
31 |
|
32 |
+
if ( in_array( '/' . ITSEC_Lib::get_request_path(), $this->settings['white_list'] ) ) {
|
33 |
+
// white listed page.
|
34 |
return;
|
35 |
}
|
36 |
|
37 |
+
ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
|
39 |
+
if ( ! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'] ) ) {
|
40 |
|
41 |
$itsec_lockout->do_lockout( 'four_oh_four' );
|
42 |
|
64 |
|
65 |
}
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
}
|
core/modules/404-detection/logs.php
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Four_Oh_Four_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
add_filter( 'itsec_logs_prepare_four_oh_four_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
}
|
8 |
+
|
9 |
+
public function filter_entry_for_list_display( $entry ) {
|
10 |
+
$entry['module_display'] = esc_html__( '404 Detection', 'better-wp-security' );
|
11 |
+
|
12 |
+
if ( 'found_404' === $entry['code'] ) {
|
13 |
+
$entry['description'] = $entry['url'];
|
14 |
+
}
|
15 |
+
|
16 |
+
return $entry;
|
17 |
+
}
|
18 |
+
|
19 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
20 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
21 |
+
|
22 |
+
$details['module']['content'] = $entry['module_display'];
|
23 |
+
$details['description']['content'] = $entry['description'];
|
24 |
+
|
25 |
+
return $details;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
new ITSEC_Four_Oh_Four_Logs();
|
core/modules/away-mode/class-itsec-away-mode.php
CHANGED
@@ -5,7 +5,6 @@ final class ITSEC_Away_Mode {
|
|
5 |
public function run() {
|
6 |
|
7 |
//Execute away mode functions on admin init
|
8 |
-
add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
|
9 |
add_action( 'itsec_admin_init', array( $this, 'run_active_check' ) );
|
10 |
add_action( 'login_init', array( $this, 'run_active_check' ) );
|
11 |
|
@@ -78,49 +77,15 @@ final class ITSEC_Away_Mode {
|
|
78 |
* @return void
|
79 |
*/
|
80 |
public function run_active_check() {
|
|
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
//execute lockout if applicable
|
85 |
-
if ( self::is_active() ) {
|
86 |
-
|
87 |
-
$itsec_logger->log_event(
|
88 |
-
'away_mode',
|
89 |
-
5,
|
90 |
-
array(
|
91 |
-
__( 'A host was prevented from accessing the dashboard due to away-mode restrictions being in effect', 'better-wp-security' ),
|
92 |
-
),
|
93 |
-
ITSEC_Lib::get_ip(),
|
94 |
-
'',
|
95 |
-
'',
|
96 |
-
'',
|
97 |
-
''
|
98 |
-
);
|
99 |
|
100 |
wp_redirect( get_option( 'siteurl' ) );
|
101 |
wp_clear_auth_cookie();
|
102 |
die();
|
103 |
-
|
104 |
}
|
105 |
-
|
106 |
-
}
|
107 |
-
|
108 |
-
/**
|
109 |
-
* Register 404 and file change detection for logger
|
110 |
-
*
|
111 |
-
* @param array $logger_modules array of logger modules
|
112 |
-
*
|
113 |
-
* @return array array of logger modules
|
114 |
-
*/
|
115 |
-
public function register_logger( $logger_modules ) {
|
116 |
-
|
117 |
-
$logger_modules['away_mode'] = array(
|
118 |
-
'type' => 'away_mode',
|
119 |
-
'function' => __( 'Away Mode Triggered', 'better-wp-security' ),
|
120 |
-
);
|
121 |
-
|
122 |
-
return $logger_modules;
|
123 |
-
|
124 |
}
|
125 |
|
126 |
/**
|
5 |
public function run() {
|
6 |
|
7 |
//Execute away mode functions on admin init
|
|
|
8 |
add_action( 'itsec_admin_init', array( $this, 'run_active_check' ) );
|
9 |
add_action( 'login_init', array( $this, 'run_active_check' ) );
|
10 |
|
77 |
* @return void
|
78 |
*/
|
79 |
public function run_active_check() {
|
80 |
+
$away_mode_details = self::is_active( true );
|
81 |
|
82 |
+
if ( $away_mode_details['active'] ) {
|
83 |
+
ITSEC_Log::add_notice( 'away_mode', 'away-mode-active', array( 'login_details' => ITSEC_Lib::get_login_details(), 'away_mode_details' => $away_mode_details ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
wp_redirect( get_option( 'siteurl' ) );
|
86 |
wp_clear_auth_cookie();
|
87 |
die();
|
|
|
88 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
|
91 |
/**
|
core/modules/away-mode/logs.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Away_Mode_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_away_mode_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
}
|
7 |
+
|
8 |
+
public function filter_entry_for_list_display( $entry ) {
|
9 |
+
$entry['module_display'] = esc_html__( 'Away Mode', 'better-wp-security' );
|
10 |
+
|
11 |
+
if ( 'away-mode-active' === $entry['code'] ) {
|
12 |
+
$entry['description'] = esc_html__( 'Access Blocked', 'better-wp-security' );
|
13 |
+
}
|
14 |
+
|
15 |
+
return $entry;
|
16 |
+
}
|
17 |
+
}
|
18 |
+
new ITSEC_Away_Mode_Logs();
|
core/modules/backup/class-itsec-backup.php
CHANGED
@@ -35,7 +35,6 @@ class ITSEC_Backup {
|
|
35 |
$this->settings = ITSEC_Modules::get_settings( 'backup' );
|
36 |
|
37 |
add_action( 'itsec_execute_backup_cron', array( $this, 'do_backup' ) );
|
38 |
-
add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
|
39 |
|
40 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
41 |
add_filter( 'itsec_backup_notification_strings', array( $this, 'notification_strings' ) );
|
@@ -109,7 +108,7 @@ class ITSEC_Backup {
|
|
109 |
* @return void
|
110 |
*/
|
111 |
private function execute_backup( $one_time = false ) {
|
112 |
-
global $wpdb
|
113 |
|
114 |
|
115 |
|
@@ -205,6 +204,8 @@ class ITSEC_Backup {
|
|
205 |
@fwrite( $fh, PHP_EOL . PHP_EOL );
|
206 |
@fclose( $fh );
|
207 |
|
|
|
|
|
208 |
if ( $this->settings['zip'] ) {
|
209 |
if ( ! class_exists( 'PclZip' ) ) {
|
210 |
require( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
@@ -246,27 +247,27 @@ class ITSEC_Backup {
|
|
246 |
}
|
247 |
|
248 |
|
249 |
-
$
|
250 |
-
|
|
|
|
|
|
|
251 |
|
252 |
if ( 0 === $this->settings['method'] ) {
|
253 |
if ( false === $mail_success ) {
|
254 |
-
|
255 |
-
$details = __( 'saved locally but email to backup recipients could not be sent.', 'better-wp-security' );
|
256 |
} else {
|
257 |
-
|
258 |
}
|
259 |
} else if ( 1 === $this->settings['method'] ) {
|
260 |
if ( false === $mail_success ) {
|
261 |
-
|
262 |
-
$details = __( 'email to backup recipients could not be sent.', 'better-wp-security' );
|
263 |
} else {
|
264 |
-
|
265 |
}
|
|
|
|
|
266 |
}
|
267 |
-
|
268 |
-
$data = compact( 'status', 'details' );
|
269 |
-
$itsec_logger->log_event( 'backup', 3, array( $data ) );
|
270 |
}
|
271 |
|
272 |
private function send_mail( $file ) {
|
@@ -298,28 +299,6 @@ class ITSEC_Backup {
|
|
298 |
return $nc->send( 'backup', $mail );
|
299 |
}
|
300 |
|
301 |
-
/**
|
302 |
-
* Register backups for logger.
|
303 |
-
*
|
304 |
-
* Adds the backup module to ITSEC_Logger.
|
305 |
-
*
|
306 |
-
* @since 4.0.0
|
307 |
-
*
|
308 |
-
* @param array $logger_modules array of logger modules
|
309 |
-
*
|
310 |
-
* @return array array of logger modules
|
311 |
-
*/
|
312 |
-
public function register_logger( $logger_modules ) {
|
313 |
-
|
314 |
-
$logger_modules['backup'] = array(
|
315 |
-
'type' => 'backup',
|
316 |
-
'function' => __( 'Database Backup Executed', 'better-wp-security' ),
|
317 |
-
);
|
318 |
-
|
319 |
-
return $logger_modules;
|
320 |
-
|
321 |
-
}
|
322 |
-
|
323 |
/**
|
324 |
* Register the events.
|
325 |
*
|
35 |
$this->settings = ITSEC_Modules::get_settings( 'backup' );
|
36 |
|
37 |
add_action( 'itsec_execute_backup_cron', array( $this, 'do_backup' ) );
|
|
|
38 |
|
39 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
40 |
add_filter( 'itsec_backup_notification_strings', array( $this, 'notification_strings' ) );
|
108 |
* @return void
|
109 |
*/
|
110 |
private function execute_backup( $one_time = false ) {
|
111 |
+
global $wpdb;
|
112 |
|
113 |
|
114 |
|
204 |
@fwrite( $fh, PHP_EOL . PHP_EOL );
|
205 |
@fclose( $fh );
|
206 |
|
207 |
+
$backup_file = $file;
|
208 |
+
|
209 |
if ( $this->settings['zip'] ) {
|
210 |
if ( ! class_exists( 'PclZip' ) ) {
|
211 |
require( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
|
247 |
}
|
248 |
|
249 |
|
250 |
+
$log_data = array(
|
251 |
+
'settings' => $this->settings,
|
252 |
+
'mail_success' => $mail_success,
|
253 |
+
'file' => $backup_file,
|
254 |
+
);
|
255 |
|
256 |
if ( 0 === $this->settings['method'] ) {
|
257 |
if ( false === $mail_success ) {
|
258 |
+
ITSEC_Log::add_warning( 'backup', 'email-failed-file-stored', $log_data );
|
|
|
259 |
} else {
|
260 |
+
ITSEC_Log::add_notice( 'backup', 'email-succeeded-file-stored', $log_data );
|
261 |
}
|
262 |
} else if ( 1 === $this->settings['method'] ) {
|
263 |
if ( false === $mail_success ) {
|
264 |
+
ITSEC_Log::add_error( 'backup', 'email-failed', $log_data );
|
|
|
265 |
} else {
|
266 |
+
ITSEC_Log::add_notice( 'backup', 'email-succeeded', $log_data );
|
267 |
}
|
268 |
+
} else {
|
269 |
+
ITSEC_Log::add_notice( 'backup', 'file-stored', $log_data );
|
270 |
}
|
|
|
|
|
|
|
271 |
}
|
272 |
|
273 |
private function send_mail( $file ) {
|
299 |
return $nc->send( 'backup', $mail );
|
300 |
}
|
301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
/**
|
303 |
* Register the events.
|
304 |
*
|
core/modules/backup/js/settings-page.js
CHANGED
@@ -1,53 +1,53 @@
|
|
1 |
jQuery( document ).ready( function () {
|
2 |
var $container = jQuery( '#wpcontent' );
|
3 |
-
|
4 |
-
|
5 |
$container.on( 'click', '#itsec-backup-reset_backup_location', function( e ) {
|
6 |
e.preventDefault();
|
7 |
-
|
8 |
jQuery( '#itsec-backup-location' ).val( itsec_backup.default_backup_location );
|
9 |
} );
|
10 |
-
|
11 |
$container.on( 'change', '#itsec-backup-method', function( e ) {
|
12 |
var method = jQuery(this).val();
|
13 |
-
|
14 |
if ( 1 == method ) {
|
15 |
jQuery( '.itsec-backup-method-file-content' ).hide();
|
16 |
} else {
|
17 |
jQuery( '.itsec-backup-method-file-content' ).show();
|
18 |
}
|
19 |
} );
|
20 |
-
|
21 |
jQuery( '#itsec-backup-method' ).trigger( 'change' );
|
22 |
-
|
23 |
-
|
24 |
jQuery( '#itsec-backup-exclude' ).multiSelect( {
|
25 |
selectableHeader: '<div class="custom-header">' + itsec_backup.available_tables_label + '</div>',
|
26 |
selectionHeader: '<div class="custom-header">' + itsec_backup.excluded_tables_label + '</div>',
|
27 |
keepOrder: true
|
28 |
} );
|
29 |
-
|
30 |
-
|
31 |
jQuery( '#itsec-backup-create_backup' ).click(function( e ) {
|
32 |
e.preventDefault();
|
33 |
-
|
34 |
var originalButtonLabel = jQuery( '#itsec-backup-create_backup' ).attr( 'value' );
|
35 |
-
|
36 |
jQuery( '#itsec-backup-create_backup' )
|
37 |
.removeClass( 'button-primary' )
|
38 |
.addClass( 'button-secondary' )
|
39 |
.attr( 'value', itsec_backup.creating_backup_text )
|
40 |
.prop( 'disabled', true );
|
41 |
-
|
42 |
jQuery( '#itsec_backup_status' ).html( '' );
|
43 |
-
|
44 |
var data = {
|
45 |
'method': 'create-backup'
|
46 |
};
|
47 |
-
|
48 |
-
|
49 |
jQuery( '#itsec_backup_status' ).html( results.response );
|
50 |
-
|
51 |
jQuery( '#itsec-backup-create_backup' )
|
52 |
.removeClass( 'button-secondary' )
|
53 |
.addClass( 'button-primary' )
|
1 |
jQuery( document ).ready( function () {
|
2 |
var $container = jQuery( '#wpcontent' );
|
3 |
+
|
4 |
+
|
5 |
$container.on( 'click', '#itsec-backup-reset_backup_location', function( e ) {
|
6 |
e.preventDefault();
|
7 |
+
|
8 |
jQuery( '#itsec-backup-location' ).val( itsec_backup.default_backup_location );
|
9 |
} );
|
10 |
+
|
11 |
$container.on( 'change', '#itsec-backup-method', function( e ) {
|
12 |
var method = jQuery(this).val();
|
13 |
+
|
14 |
if ( 1 == method ) {
|
15 |
jQuery( '.itsec-backup-method-file-content' ).hide();
|
16 |
} else {
|
17 |
jQuery( '.itsec-backup-method-file-content' ).show();
|
18 |
}
|
19 |
} );
|
20 |
+
|
21 |
jQuery( '#itsec-backup-method' ).trigger( 'change' );
|
22 |
+
|
23 |
+
|
24 |
jQuery( '#itsec-backup-exclude' ).multiSelect( {
|
25 |
selectableHeader: '<div class="custom-header">' + itsec_backup.available_tables_label + '</div>',
|
26 |
selectionHeader: '<div class="custom-header">' + itsec_backup.excluded_tables_label + '</div>',
|
27 |
keepOrder: true
|
28 |
} );
|
29 |
+
|
30 |
+
|
31 |
jQuery( '#itsec-backup-create_backup' ).click(function( e ) {
|
32 |
e.preventDefault();
|
33 |
+
|
34 |
var originalButtonLabel = jQuery( '#itsec-backup-create_backup' ).attr( 'value' );
|
35 |
+
|
36 |
jQuery( '#itsec-backup-create_backup' )
|
37 |
.removeClass( 'button-primary' )
|
38 |
.addClass( 'button-secondary' )
|
39 |
.attr( 'value', itsec_backup.creating_backup_text )
|
40 |
.prop( 'disabled', true );
|
41 |
+
|
42 |
jQuery( '#itsec_backup_status' ).html( '' );
|
43 |
+
|
44 |
var data = {
|
45 |
'method': 'create-backup'
|
46 |
};
|
47 |
+
|
48 |
+
itsecUtil.sendModuleAJAXRequest( 'backup', data, function( results ) {
|
49 |
jQuery( '#itsec_backup_status' ).html( results.response );
|
50 |
+
|
51 |
jQuery( '#itsec-backup-create_backup' )
|
52 |
.removeClass( 'button-secondary' )
|
53 |
.addClass( 'button-primary' )
|
core/modules/backup/logs.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Backup_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_backup_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
add_filter( 'itsec_logs_prepare_backup_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
}
|
8 |
+
|
9 |
+
public function filter_entry_for_list_display( $entry ) {
|
10 |
+
$entry['module_display'] = esc_html__( 'Database Backups', 'better-wp-security' );
|
11 |
+
|
12 |
+
if ( 'email-failed-file-stored' === $entry['code'] ) {
|
13 |
+
$entry['description'] = esc_html__( 'File Created but Email Send Failed', 'better-wp-security' );
|
14 |
+
} else if ( 'email-succeeded-file-stored' === $entry['code'] ) {
|
15 |
+
$entry['description'] = esc_html__( 'File Created and Emails Sent', 'better-wp-security' );
|
16 |
+
} else if ( 'email-failed' === $entry['code'] ) {
|
17 |
+
$entry['description'] = esc_html__( 'Email Send Failed', 'better-wp-security' );
|
18 |
+
} else if ( 'email-succeeded' === $entry['code'] ) {
|
19 |
+
$entry['description'] = esc_html__( 'Email Send Succeeded', 'better-wp-security' );
|
20 |
+
} else if ( 'file-stored' === $entry['code'] ) {
|
21 |
+
$entry['description'] = esc_html__( 'File Created', 'better-wp-security' );
|
22 |
+
} else if ( 'details' === $entry['code'] ) {
|
23 |
+
$entry['description'] = esc_html__( 'Details', 'better-wp-security' );
|
24 |
+
}
|
25 |
+
|
26 |
+
return $entry;
|
27 |
+
}
|
28 |
+
|
29 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
30 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
31 |
+
|
32 |
+
$details['module']['content'] = $entry['module_display'];
|
33 |
+
$details['description']['content'] = $entry['description'];
|
34 |
+
|
35 |
+
return $details;
|
36 |
+
}
|
37 |
+
}
|
38 |
+
new ITSEC_Backup_Logs();
|
core/modules/brute-force/class-itsec-brute-force-log.php
DELETED
@@ -1,186 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Log tables for Authentication Module
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @subpackage Authentication
|
8 |
-
* @since 4.0
|
9 |
-
*/
|
10 |
-
final class ITSEC_Brute_Force_Log extends ITSEC_WP_List_Table {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
|
14 |
-
parent::__construct(
|
15 |
-
array(
|
16 |
-
'singular' => 'itsec_brute_force_log_item',
|
17 |
-
'plural' => 'itsec_brute_force_log_items',
|
18 |
-
'ajax' => true
|
19 |
-
)
|
20 |
-
);
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Define time column
|
26 |
-
*
|
27 |
-
* @param array $item array of row data
|
28 |
-
*
|
29 |
-
* @return string formatted output
|
30 |
-
*
|
31 |
-
**/
|
32 |
-
function column_time( $item ) {
|
33 |
-
|
34 |
-
return $item['time'];
|
35 |
-
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Define host column
|
40 |
-
*
|
41 |
-
* @param array $item array of row data
|
42 |
-
*
|
43 |
-
* @return string formatted output
|
44 |
-
*
|
45 |
-
**/
|
46 |
-
function column_host( $item ) {
|
47 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
48 |
-
|
49 |
-
$r = array();
|
50 |
-
if ( ! is_array( $item['host'] ) ) {
|
51 |
-
$item['host'] = array( $item['host'] );
|
52 |
-
}
|
53 |
-
foreach ( $item['host'] as $host ) {
|
54 |
-
if ( ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
55 |
-
$r[] = '<a href="' . esc_url( ITSEC_Lib::get_trace_ip_link( $host ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $host ) . '</a>';
|
56 |
-
}
|
57 |
-
}
|
58 |
-
$return = implode( '<br />', $r );
|
59 |
-
|
60 |
-
return $return;
|
61 |
-
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Define added column
|
66 |
-
*
|
67 |
-
* @param array $item array of row data
|
68 |
-
*
|
69 |
-
* @return string formatted output
|
70 |
-
*
|
71 |
-
**/
|
72 |
-
function column_user( $item ) {
|
73 |
-
|
74 |
-
return $item['user'];
|
75 |
-
|
76 |
-
}
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Define Columns
|
80 |
-
*
|
81 |
-
* @return array array of column titles
|
82 |
-
*/
|
83 |
-
public function get_columns() {
|
84 |
-
|
85 |
-
return array(
|
86 |
-
'time' => __( 'Time', 'better-wp-security' ),
|
87 |
-
'host' => __( 'Host', 'better-wp-security' ),
|
88 |
-
'user' => __( 'Username', 'better-wp-security' ),
|
89 |
-
);
|
90 |
-
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Define Sortable Columns
|
95 |
-
*
|
96 |
-
* @return array of column titles that can be sorted
|
97 |
-
*/
|
98 |
-
public function get_sortable_columns() {
|
99 |
-
|
100 |
-
$order = ( empty( $_GET['order'] ) ) ? false : true;
|
101 |
-
|
102 |
-
$sortable_columns = array(
|
103 |
-
'time' => array( 'time', $order ),
|
104 |
-
'host' => array( 'host', $order ),
|
105 |
-
'user' => array( 'user', $order ),
|
106 |
-
);
|
107 |
-
|
108 |
-
return $sortable_columns;
|
109 |
-
|
110 |
-
}
|
111 |
-
|
112 |
-
/**
|
113 |
-
* Prepare data for table
|
114 |
-
*
|
115 |
-
* @return void
|
116 |
-
*/
|
117 |
-
public function prepare_items() {
|
118 |
-
|
119 |
-
global $itsec_logger;
|
120 |
-
|
121 |
-
$columns = $this->get_columns();
|
122 |
-
$hidden = array();
|
123 |
-
$sortable = $this->get_sortable_columns();
|
124 |
-
$this->_column_headers = array( $columns, $hidden, $sortable );
|
125 |
-
|
126 |
-
$items = $itsec_logger->get_events( 'brute_force' );
|
127 |
-
|
128 |
-
$table_data = array();
|
129 |
-
|
130 |
-
$count = 0;
|
131 |
-
|
132 |
-
foreach ( $items as $item ) { //loop through and group 404s
|
133 |
-
|
134 |
-
$table_data[$count]['time'] = sanitize_text_field( $item['log_date'] );
|
135 |
-
$table_data[$count]['host'] = sanitize_text_field( $item['log_host'] );
|
136 |
-
$table_data[$count]['user'] = sanitize_text_field( $item['log_username'] );
|
137 |
-
|
138 |
-
$count ++;
|
139 |
-
|
140 |
-
}
|
141 |
-
|
142 |
-
usort( $table_data, array( $this, 'sortrows' ) );
|
143 |
-
|
144 |
-
$per_page = 20; //20 items per page
|
145 |
-
$current_page = $this->get_pagenum();
|
146 |
-
$total_items = count( $table_data );
|
147 |
-
|
148 |
-
$table_data = array_slice( $table_data, ( ( $current_page - 1 ) * $per_page ), $per_page );
|
149 |
-
|
150 |
-
$this->items = $table_data;
|
151 |
-
|
152 |
-
$this->set_pagination_args(
|
153 |
-
array(
|
154 |
-
'total_items' => $total_items,
|
155 |
-
'per_page' => $per_page,
|
156 |
-
'total_pages' => ceil( $total_items / $per_page )
|
157 |
-
)
|
158 |
-
);
|
159 |
-
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* Sorts rows by count in descending order
|
164 |
-
*
|
165 |
-
* @param array $a first array to compare
|
166 |
-
* @param array $b second array to compare
|
167 |
-
*
|
168 |
-
* @return int comparison result
|
169 |
-
*/
|
170 |
-
function sortrows( $a, $b ) {
|
171 |
-
|
172 |
-
// If no sort, default to count
|
173 |
-
$orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'time';
|
174 |
-
|
175 |
-
// If no order, default to desc
|
176 |
-
$order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
|
177 |
-
|
178 |
-
// Determine sort order
|
179 |
-
$result = strcmp( $a[$orderby], $b[$orderby] );
|
180 |
-
|
181 |
-
// Send final sort direction to usort
|
182 |
-
return ( $order === 'asc' ) ? $result : - $result;
|
183 |
-
|
184 |
-
}
|
185 |
-
|
186 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/brute-force/class-itsec-brute-force.php
CHANGED
@@ -10,20 +10,14 @@ class ITSEC_Brute_Force {
|
|
10 |
|
11 |
$this->settings = ITSEC_Modules::get_settings( 'brute-force' );
|
12 |
|
13 |
-
|
14 |
-
add_action( 'itsec-handle-failed-login', array( $this, 'handle_failed_login' ), 10, 2 );
|
15 |
-
|
16 |
-
add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) ); //adds logs metaboxes
|
17 |
-
|
18 |
-
add_filter( 'authenticate', array( $this, 'authenticate' ), 10, 3 );
|
19 |
add_filter( 'itsec_lockout_modules', array( $this, 'itsec_lockout_modules' ) );
|
20 |
-
add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
|
21 |
add_filter( 'jetpack_get_default_modules', array( $this, 'jetpack_get_default_modules' ) ); //disable jetpack protect via Geoge Stephanis
|
22 |
|
23 |
}
|
24 |
|
25 |
/**
|
26 |
-
*
|
27 |
*
|
28 |
* @since 4.0
|
29 |
*
|
@@ -34,39 +28,46 @@ class ITSEC_Brute_Force {
|
|
34 |
* @return user object or WordPress error
|
35 |
*/
|
36 |
public function authenticate( $user, $username = '', $password = '' ) {
|
37 |
-
|
38 |
/** @var ITSEC_Lockout $itsec_lockout */
|
39 |
-
|
40 |
-
global $itsec_lockout, $itsec_logger;
|
41 |
-
|
42 |
-
//Look for the "admin" user name and ban it if it is set to auto-ban
|
43 |
-
if ( isset( $this->settings['auto_ban_admin'] ) && $this->settings['auto_ban_admin'] === true && 'admin' === $username ) {
|
44 |
-
|
45 |
-
$itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), $username );
|
46 |
-
|
47 |
-
$itsec_lockout->do_lockout( 'brute_force_admin_user', $username );
|
48 |
|
|
|
|
|
|
|
49 |
}
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
$user_id = username_exists( $username );
|
55 |
|
56 |
-
|
|
|
57 |
|
58 |
-
|
|
|
59 |
|
|
|
60 |
} else {
|
|
|
61 |
|
62 |
-
|
|
|
|
|
|
|
63 |
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
65 |
|
66 |
-
|
67 |
-
|
68 |
-
$itsec_lockout->do_lockout( 'brute_force', $username );
|
69 |
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
71 |
|
72 |
return $user;
|
@@ -104,26 +105,6 @@ class ITSEC_Brute_Force {
|
|
104 |
|
105 |
}
|
106 |
|
107 |
-
/**
|
108 |
-
* Register Brute Force for logger
|
109 |
-
*
|
110 |
-
* @since 4.0
|
111 |
-
*
|
112 |
-
* @param array $logger_modules array of logger modules
|
113 |
-
*
|
114 |
-
* @return array array of logger modules
|
115 |
-
*/
|
116 |
-
public function itsec_logger_modules( $logger_modules ) {
|
117 |
-
|
118 |
-
$logger_modules['brute_force'] = array(
|
119 |
-
'type' => 'brute_force',
|
120 |
-
'function' => __( 'Invalid Login Attempt', 'better-wp-security' ),
|
121 |
-
);
|
122 |
-
|
123 |
-
return $logger_modules;
|
124 |
-
|
125 |
-
}
|
126 |
-
|
127 |
/**
|
128 |
* Disables the jetpack protect module
|
129 |
*
|
@@ -141,106 +122,4 @@ class ITSEC_Brute_Force {
|
|
141 |
|
142 |
}
|
143 |
|
144 |
-
/**
|
145 |
-
* Make sure user isn't already locked out even on successful form submission
|
146 |
-
*
|
147 |
-
* @since 4.0
|
148 |
-
*
|
149 |
-
* @param string $username the username attempted
|
150 |
-
* @param object wp_user the user
|
151 |
-
*
|
152 |
-
* @return void
|
153 |
-
*/
|
154 |
-
public function wp_login( $username, $user = null ) {
|
155 |
-
|
156 |
-
/** @var ITSEC_Lockout $itsec_lockout */
|
157 |
-
global $itsec_lockout;
|
158 |
-
|
159 |
-
if ( ! $user === null ) {
|
160 |
-
|
161 |
-
$itsec_lockout->check_lockout( $user );
|
162 |
-
|
163 |
-
} elseif ( is_user_logged_in() ) {
|
164 |
-
|
165 |
-
$current_user = wp_get_current_user();
|
166 |
-
|
167 |
-
$itsec_lockout->check_lockout( $current_user->ID );
|
168 |
-
|
169 |
-
}
|
170 |
-
|
171 |
-
}
|
172 |
-
|
173 |
-
/**
|
174 |
-
* Sends to lockout class when username and password are filled out and wrong
|
175 |
-
*
|
176 |
-
* @since 4.0
|
177 |
-
*
|
178 |
-
* @param string $username the username attempted
|
179 |
-
*
|
180 |
-
* @return void
|
181 |
-
*/
|
182 |
-
public function handle_failed_login( $username, $details ) {
|
183 |
-
|
184 |
-
/** @var ITSEC_Lockout $itsec_lockout */
|
185 |
-
global $itsec_lockout, $itsec_logger;
|
186 |
-
|
187 |
-
$user_id = username_exists( $username );
|
188 |
-
|
189 |
-
if ( 'admin' === $username && $this->settings['auto_ban_admin'] && empty( $user_id ) ) {
|
190 |
-
$itsec_logger->log_event( 'brute_force', 5, $details, ITSEC_Lib::get_ip(), $username );
|
191 |
-
$itsec_lockout->do_lockout( 'brute_force_admin_user', $username );
|
192 |
-
|
193 |
-
return;
|
194 |
-
}
|
195 |
-
|
196 |
-
if ( empty( $user_id ) ) {
|
197 |
-
$itsec_lockout->check_lockout( false, $username, 'brute_force' );
|
198 |
-
} else {
|
199 |
-
$itsec_lockout->check_lockout( $user_id, false, 'brute_force' );
|
200 |
-
}
|
201 |
-
|
202 |
-
$itsec_logger->log_event( 'brute_force', 5, $details, ITSEC_Lib::get_ip(), $username, intval( $user_id ) );
|
203 |
-
$itsec_lockout->do_lockout( 'brute_force', $username );
|
204 |
-
}
|
205 |
-
|
206 |
-
/**
|
207 |
-
* Array of metaboxes for the logs screen
|
208 |
-
*
|
209 |
-
* @since 4.0
|
210 |
-
*
|
211 |
-
* @param object $displays metabox array
|
212 |
-
*
|
213 |
-
* @return array metabox array
|
214 |
-
*/
|
215 |
-
public function itsec_logger_displays( $displays ) {
|
216 |
-
|
217 |
-
$displays[] = array(
|
218 |
-
'module' => 'brute_force',
|
219 |
-
'title' => __( 'Invalid Login Attempts', 'better-wp-security' ),
|
220 |
-
'callback' => array( $this, 'logs_metabox_content' ),
|
221 |
-
);
|
222 |
-
|
223 |
-
return $displays;
|
224 |
-
|
225 |
-
}
|
226 |
-
|
227 |
-
/**
|
228 |
-
* Render the settings metabox
|
229 |
-
*
|
230 |
-
* @since 4.0
|
231 |
-
*
|
232 |
-
* @return void
|
233 |
-
*/
|
234 |
-
public function logs_metabox_content() {
|
235 |
-
|
236 |
-
if ( ! class_exists( 'ITSEC_Brute_Force_Log' ) ) {
|
237 |
-
require( dirname( __FILE__ ) . '/class-itsec-brute-force-log.php' );
|
238 |
-
}
|
239 |
-
|
240 |
-
$log_display = new ITSEC_Brute_Force_Log();
|
241 |
-
$log_display->prepare_items();
|
242 |
-
$log_display->display();
|
243 |
-
|
244 |
-
}
|
245 |
-
|
246 |
}
|
10 |
|
11 |
$this->settings = ITSEC_Modules::get_settings( 'brute-force' );
|
12 |
|
13 |
+
add_filter( 'authenticate', array( $this, 'authenticate' ), 10000, 3 ); // Set a very late priority so that we run after actual authentication takes place.
|
|
|
|
|
|
|
|
|
|
|
14 |
add_filter( 'itsec_lockout_modules', array( $this, 'itsec_lockout_modules' ) );
|
|
|
15 |
add_filter( 'jetpack_get_default_modules', array( $this, 'jetpack_get_default_modules' ) ); //disable jetpack protect via Geoge Stephanis
|
16 |
|
17 |
}
|
18 |
|
19 |
/**
|
20 |
+
* Handle brute force lockout conditions when the site is handling authentication.
|
21 |
*
|
22 |
* @since 4.0
|
23 |
*
|
28 |
* @return user object or WordPress error
|
29 |
*/
|
30 |
public function authenticate( $user, $username = '', $password = '' ) {
|
|
|
31 |
/** @var ITSEC_Lockout $itsec_lockout */
|
32 |
+
global $itsec_lockout;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
+
if ( is_wp_error( $user ) && $user->get_error_codes() == array( 'empty_username', 'empty_password' ) ) {
|
35 |
+
// This is not an authentication attempt. It is simply the login page loading.
|
36 |
+
return $user;
|
37 |
}
|
38 |
|
39 |
+
if ( is_wp_error( $user ) || null == $user ) {
|
40 |
+
// Failed authentication.
|
|
|
|
|
41 |
|
42 |
+
$details = ITSEC_Lib::get_login_details();
|
43 |
+
$SERVER = $_SERVER;
|
44 |
|
45 |
+
if ( 'admin' === $username && $this->settings['auto_ban_admin'] ) {
|
46 |
+
ITSEC_Log::add_notice( 'brute_force', 'auto-ban-admin-username', compact( 'details', 'user', 'username', 'SERVER' ) );
|
47 |
|
48 |
+
$itsec_lockout->do_lockout( 'brute_force_admin_user', $username );
|
49 |
} else {
|
50 |
+
$user_id = false;
|
51 |
|
52 |
+
if ( empty( $username ) ) {
|
53 |
+
$itsec_lockout->check_lockout( false, false, 'brute_force_empty_username' );
|
54 |
+
} else {
|
55 |
+
$user_id = username_exists( $username );
|
56 |
|
57 |
+
if ( empty( $user_id ) ) {
|
58 |
+
$itsec_lockout->check_lockout( false, $username, 'brute_force_invalid_username' );
|
59 |
+
} else {
|
60 |
+
$itsec_lockout->check_lockout( $user_id, false, 'brute_force_invalid_password' );
|
61 |
+
}
|
62 |
+
}
|
63 |
|
64 |
+
ITSEC_Log::add_notice( 'brute_force', 'invalid-login', compact( 'details', 'user', 'username', 'user_id', 'SERVER' ) );
|
|
|
|
|
65 |
|
66 |
+
$itsec_lockout->do_lockout( 'brute_force', $username );
|
67 |
+
}
|
68 |
+
} else {
|
69 |
+
// Successful authentication. Check to ensure that they are not locked out.
|
70 |
+
$itsec_lockout->check_lockout( $user, false, 'brute_force_host_lockout' );
|
71 |
}
|
72 |
|
73 |
return $user;
|
105 |
|
106 |
}
|
107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
/**
|
109 |
* Disables the jetpack protect module
|
110 |
*
|
122 |
|
123 |
}
|
124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
}
|
core/modules/brute-force/logs.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Brute_Force_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_brute_force_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
add_filter( 'itsec_logs_prepare_brute_force_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
}
|
8 |
+
|
9 |
+
public function filter_entry_for_list_display( $entry ) {
|
10 |
+
$entry['module_display'] = esc_html__( 'Brute Force', 'better-wp-security' );
|
11 |
+
|
12 |
+
if ( 'invalid-login' === $entry['code'] ) {
|
13 |
+
$entry['description'] = esc_html__( 'Invalid Login', 'better-wp-security' );
|
14 |
+
} else if ( 'auto-ban-admin-username' === $entry['code'] ) {
|
15 |
+
$entry['description'] = esc_html__( 'Banned Use of "admin" Username', 'better-wp-security' );
|
16 |
+
}
|
17 |
+
|
18 |
+
return $entry;
|
19 |
+
}
|
20 |
+
|
21 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
22 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
23 |
+
|
24 |
+
$details['module']['content'] = $entry['module_display'];
|
25 |
+
$details['description']['content'] = $entry['description'];
|
26 |
+
|
27 |
+
if ( isset( $entry['data']['details'] ) ) {
|
28 |
+
if ( 'xmlrpc' === $entry['data']['details']['source'] ) {
|
29 |
+
$source = esc_html__( 'XMLRPC Authentication', 'better-wp-security' );
|
30 |
+
} else if ( 'rest_api' === $entry['data']['details']['source'] ) {
|
31 |
+
$source = esc_html__( 'REST API Authentication', 'better-wp-security' );
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
if ( ! isset( $source ) ) {
|
36 |
+
$source = esc_html__( 'Login Page', 'better-wp-security' );
|
37 |
+
}
|
38 |
+
|
39 |
+
$details['source'] = array(
|
40 |
+
'header' => esc_html__( 'Login Source' ),
|
41 |
+
'content' => $source,
|
42 |
+
);
|
43 |
+
|
44 |
+
return $details;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
new ITSEC_Brute_Force_Logs();
|
core/modules/file-change/class-itsec-file-change-log.php
DELETED
@@ -1,255 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Display File Change Log for Intrusion Detection Module
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @subpackage Intrusion-Detection
|
8 |
-
* @since 4.0
|
9 |
-
*/
|
10 |
-
final class ITSEC_File_Change_Log extends ITSEC_WP_List_Table {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
|
14 |
-
parent::__construct(
|
15 |
-
array(
|
16 |
-
'singular' => 'itsec_file_change_log_item',
|
17 |
-
'plural' => 'itsec_file_change_log_items',
|
18 |
-
'ajax' => true
|
19 |
-
)
|
20 |
-
);
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Define time column
|
26 |
-
*
|
27 |
-
* @param array $item array of row data
|
28 |
-
*
|
29 |
-
* @return string formatted output
|
30 |
-
*
|
31 |
-
**/
|
32 |
-
function column_time( $item ) {
|
33 |
-
|
34 |
-
return $item['time'];
|
35 |
-
|
36 |
-
}
|
37 |
-
|
38 |
-
/**
|
39 |
-
* Define added column
|
40 |
-
*
|
41 |
-
* @param array $item array of row data
|
42 |
-
*
|
43 |
-
* @return string formatted output
|
44 |
-
*
|
45 |
-
**/
|
46 |
-
function column_added( $item ) {
|
47 |
-
|
48 |
-
return $item['added'];
|
49 |
-
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Define removed column
|
54 |
-
*
|
55 |
-
* @param array $item array of row data
|
56 |
-
*
|
57 |
-
* @return string formatted output
|
58 |
-
*
|
59 |
-
**/
|
60 |
-
function column_removed( $item ) {
|
61 |
-
|
62 |
-
return $item['removed'];
|
63 |
-
|
64 |
-
}
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Define changed column
|
68 |
-
*
|
69 |
-
* @param array $item array of row data
|
70 |
-
*
|
71 |
-
* @return string formatted output
|
72 |
-
*
|
73 |
-
**/
|
74 |
-
function column_changed( $item ) {
|
75 |
-
|
76 |
-
return $item['changed'];
|
77 |
-
|
78 |
-
}
|
79 |
-
|
80 |
-
/**
|
81 |
-
* Define memory used column
|
82 |
-
*
|
83 |
-
* @param array $item array of row data
|
84 |
-
*
|
85 |
-
* @return string formatted output
|
86 |
-
*
|
87 |
-
**/
|
88 |
-
function column_memory( $item ) {
|
89 |
-
|
90 |
-
return $item['memory'] . __( 'MB', 'better-wp-security' );
|
91 |
-
|
92 |
-
}
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Define detail column
|
96 |
-
*
|
97 |
-
* @param array $item array of row data
|
98 |
-
*
|
99 |
-
* @return string formatted output
|
100 |
-
*
|
101 |
-
**/
|
102 |
-
function column_detail( $item ) {
|
103 |
-
|
104 |
-
if ( $item['added'] > 0 || $item['removed'] > 0 || $item['changed'] > 0 ) {
|
105 |
-
|
106 |
-
echo '<a href="itsec-log-file-change-row-' . $item['detail'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
|
107 |
-
|
108 |
-
echo '<div id="itsec-log-file-change-row-' . $item['detail'] . '" style="display:none;">';
|
109 |
-
|
110 |
-
echo '<h3>' . __( 'Files Added', 'better-wp-security' ) . '</h3>';
|
111 |
-
|
112 |
-
echo '<ol class="file_change_detail_list">';
|
113 |
-
|
114 |
-
if ( sizeof( $item['added_detail'] ) > 0 ) {
|
115 |
-
|
116 |
-
foreach ( $item['added_detail'] as $file => $details ) {
|
117 |
-
echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>: ' . esc_html( $file ) . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
|
118 |
-
}
|
119 |
-
|
120 |
-
} else {
|
121 |
-
|
122 |
-
echo '<li class="file_change_detail">' . __( 'There are no added files to report', 'better-wp-security' ) . '</li>';
|
123 |
-
|
124 |
-
}
|
125 |
-
|
126 |
-
echo '</ol>';
|
127 |
-
|
128 |
-
echo '<h3>' . __( 'Files Removed', 'better-wp-security' ) . '</h3>';
|
129 |
-
|
130 |
-
echo '<ol class="file_change_detail_list">';
|
131 |
-
|
132 |
-
if ( sizeof( $item['removed_detail'] ) > 0 ) {
|
133 |
-
|
134 |
-
foreach ( $item['removed_detail'] as $file => $details ) {
|
135 |
-
echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>:' . esc_html( $file ) . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
|
136 |
-
}
|
137 |
-
|
138 |
-
} else {
|
139 |
-
|
140 |
-
echo '<li class="file_change_detail">' . __( 'There are no deleted files to report', 'better-wp-security' ) . '</li>';
|
141 |
-
|
142 |
-
}
|
143 |
-
|
144 |
-
echo '</ol>';
|
145 |
-
|
146 |
-
echo '<h3>' . __( 'Files Changed', 'better-wp-security' ) . '</h3>';
|
147 |
-
|
148 |
-
echo '<ol class="file_change_detail_list">';
|
149 |
-
|
150 |
-
if ( sizeof( $item['changed_detail'] ) > 0 ) {
|
151 |
-
|
152 |
-
foreach ( $item['changed_detail'] as $file => $details ) {
|
153 |
-
echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>: ' . esc_html( $file ) . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
|
154 |
-
}
|
155 |
-
|
156 |
-
} else {
|
157 |
-
|
158 |
-
echo '<li class="file_change_detail">' . __( 'There are no changed files to report', 'better-wp-security' ) . '</li>';
|
159 |
-
|
160 |
-
}
|
161 |
-
|
162 |
-
echo '</ol>';
|
163 |
-
echo '</div>';
|
164 |
-
|
165 |
-
}
|
166 |
-
|
167 |
-
}
|
168 |
-
|
169 |
-
/**
|
170 |
-
* Define Columns
|
171 |
-
*
|
172 |
-
* @return array array of column titles
|
173 |
-
*/
|
174 |
-
public function get_columns() {
|
175 |
-
|
176 |
-
return array(
|
177 |
-
'time' => __( 'Check Time', 'better-wp-security' ),
|
178 |
-
'added' => __( 'Files Added', 'better-wp-security' ),
|
179 |
-
'removed' => __( 'Files Deleted', 'better-wp-security' ),
|
180 |
-
'changed' => __( 'Files Changed', 'better-wp-security' ),
|
181 |
-
'memory' => __( 'Memory Used', 'better-wp-security' ),
|
182 |
-
'detail' => __( 'Details', 'better-wp-security' ),
|
183 |
-
);
|
184 |
-
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Prepare data for table
|
189 |
-
*
|
190 |
-
* @return void
|
191 |
-
*/
|
192 |
-
public function prepare_items() {
|
193 |
-
|
194 |
-
global $itsec_logger;
|
195 |
-
|
196 |
-
$columns = $this->get_columns();
|
197 |
-
$hidden = array();
|
198 |
-
$this->_column_headers = array( $columns, $hidden, false );
|
199 |
-
|
200 |
-
$items = $itsec_logger->get_events( 'file_change' );
|
201 |
-
|
202 |
-
$table_data = array();
|
203 |
-
|
204 |
-
$count = 0;
|
205 |
-
|
206 |
-
//Loop through results and take data we need
|
207 |
-
foreach ( $items as $item ) {
|
208 |
-
|
209 |
-
$data = maybe_unserialize( $item['log_data'] );
|
210 |
-
|
211 |
-
$table_data[$count]['time'] = $item['log_date'];
|
212 |
-
$table_data[$count]['detail'] = $item['log_id'];
|
213 |
-
$table_data[$count]['added'] = isset( $data['added'] ) ? sizeof( $data['added'] ) : 0;
|
214 |
-
$table_data[$count]['removed'] = isset( $data['removed'] ) ? sizeof( $data['removed'] ) : 0;
|
215 |
-
$table_data[$count]['changed'] = isset( $data['changed'] ) ? sizeof( $data['changed'] ) : 0;
|
216 |
-
$table_data[$count]['memory'] = isset( $data['memory'] ) ? $data['memory'] : 0;
|
217 |
-
$table_data[$count]['added_detail'] = $data['added'];
|
218 |
-
$table_data[$count]['removed_detail'] = $data['removed'];
|
219 |
-
$table_data[$count]['changed_detail'] = $data['changed'];
|
220 |
-
|
221 |
-
$count ++;
|
222 |
-
|
223 |
-
}
|
224 |
-
|
225 |
-
usort( $table_data, array( $this, 'sortrows' ) );
|
226 |
-
|
227 |
-
$this->items = $table_data;
|
228 |
-
|
229 |
-
}
|
230 |
-
|
231 |
-
/**
|
232 |
-
* Sorts rows by count in descending order
|
233 |
-
*
|
234 |
-
* @param array $a first array to compare
|
235 |
-
* @param array $b second array to compare
|
236 |
-
*
|
237 |
-
* @return int comparison result
|
238 |
-
*/
|
239 |
-
function sortrows( $a, $b ) {
|
240 |
-
|
241 |
-
// If no sort, default to count
|
242 |
-
$orderby = 'time';
|
243 |
-
|
244 |
-
// If no order, default to desc
|
245 |
-
$order = 'desc';
|
246 |
-
|
247 |
-
// Determine sort order
|
248 |
-
$result = strcmp( $a[$orderby], $b[$orderby] );
|
249 |
-
|
250 |
-
// Send final sort direction to usort
|
251 |
-
return ( $order === 'asc' ) ? $result : - $result;
|
252 |
-
|
253 |
-
}
|
254 |
-
|
255 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/file-change/class-itsec-file-change.php
CHANGED
@@ -26,8 +26,6 @@ class ITSEC_File_Change {
|
|
26 |
|
27 |
add_action( 'itsec_execute_file_check_cron', array( $this, 'run_scan' ) ); //Action to execute during a cron run.
|
28 |
|
29 |
-
add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) ); //adds logs metaboxes
|
30 |
-
add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
|
31 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
32 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
33 |
add_filter( 'itsec_file-change_notification_strings', array( $this, 'register_notification_strings' ) );
|
@@ -56,102 +54,6 @@ class ITSEC_File_Change {
|
|
56 |
$scheduler->schedule( $interval, 'file-change' );
|
57 |
}
|
58 |
|
59 |
-
/**
|
60 |
-
* Register file change detection for logger
|
61 |
-
*
|
62 |
-
* Registers the file change detection module with the core logger functionality.
|
63 |
-
*
|
64 |
-
* @since 4.0.0
|
65 |
-
*
|
66 |
-
* @param array $logger_modules array of logger modules
|
67 |
-
*
|
68 |
-
* @return array array of logger modules
|
69 |
-
*/
|
70 |
-
public function itsec_logger_modules( $logger_modules ) {
|
71 |
-
|
72 |
-
$logger_modules['file_change'] = array(
|
73 |
-
'type' => 'file_change',
|
74 |
-
'function' => __( 'File Changes Detected', 'better-wp-security' ),
|
75 |
-
);
|
76 |
-
|
77 |
-
return $logger_modules;
|
78 |
-
|
79 |
-
}
|
80 |
-
|
81 |
-
/**
|
82 |
-
* Array of displays for the logs screen
|
83 |
-
*
|
84 |
-
* Registers the custom log page with the core plugin to allow for access from the log page's
|
85 |
-
* dropdown menu.
|
86 |
-
*
|
87 |
-
* @since 4.0.0
|
88 |
-
*
|
89 |
-
* @param array $displays metabox array
|
90 |
-
*
|
91 |
-
* @return array metabox array
|
92 |
-
*/
|
93 |
-
public function itsec_logger_displays( $displays ) {
|
94 |
-
|
95 |
-
$displays[] = array(
|
96 |
-
'module' => 'file_change',
|
97 |
-
'title' => __( 'File Change History', 'better-wp-security' ),
|
98 |
-
'callback' => array( $this, 'logs_metabox_content' )
|
99 |
-
);
|
100 |
-
|
101 |
-
return $displays;
|
102 |
-
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Render the file change log metabox
|
107 |
-
*
|
108 |
-
* Displays a metabox on the logs page, when filtered, showing all file change items.
|
109 |
-
*
|
110 |
-
* @since 4.0.0
|
111 |
-
*
|
112 |
-
* @return void
|
113 |
-
*/
|
114 |
-
public function logs_metabox_content() {
|
115 |
-
|
116 |
-
if ( ! class_exists( 'ITSEC_File_Change_Log' ) ) {
|
117 |
-
require( dirname( __FILE__ ) . '/class-itsec-file-change-log.php' );
|
118 |
-
}
|
119 |
-
|
120 |
-
|
121 |
-
$settings = ITSEC_Modules::get_settings( 'file-change' );
|
122 |
-
|
123 |
-
|
124 |
-
// If we're splitting the file check run it every 6 hours. Else daily.
|
125 |
-
if ( isset( $settings['split'] ) && true === $settings['split'] ) {
|
126 |
-
|
127 |
-
$interval = 12342;
|
128 |
-
|
129 |
-
} else {
|
130 |
-
|
131 |
-
$interval = 86400;
|
132 |
-
|
133 |
-
}
|
134 |
-
|
135 |
-
$next_run_raw = $settings['last_run'] + $interval;
|
136 |
-
|
137 |
-
if ( date( 'j', $next_run_raw ) == date( 'j', ITSEC_Core::get_current_time() ) ) {
|
138 |
-
$next_run_day = __( 'Today', 'better-wp-security' );
|
139 |
-
} else {
|
140 |
-
$next_run_day = __( 'Tomorrow', 'better-wp-security' );
|
141 |
-
}
|
142 |
-
|
143 |
-
$next_run = $next_run_day . ' at ' . date( 'g:i a', $next_run_raw );
|
144 |
-
|
145 |
-
echo '<p>' . __( 'Next automatic scan at: ', 'better-wp-security' ) . '<strong>' . $next_run . '*</strong></p>';
|
146 |
-
echo '<p><em>*' . __( 'Automatic file change scanning is triggered by a user visiting your page and may not happen exactly at the time listed.', 'better-wp-security' ) . '</em>';
|
147 |
-
|
148 |
-
$log_display = new ITSEC_File_Change_Log();
|
149 |
-
|
150 |
-
$log_display->prepare_items();
|
151 |
-
$log_display->display();
|
152 |
-
|
153 |
-
}
|
154 |
-
|
155 |
/**
|
156 |
* Register verbs for Sync.
|
157 |
*
|
26 |
|
27 |
add_action( 'itsec_execute_file_check_cron', array( $this, 'run_scan' ) ); //Action to execute during a cron run.
|
28 |
|
|
|
|
|
29 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
30 |
add_filter( 'itsec_notifications', array( $this, 'register_notification' ) );
|
31 |
add_filter( 'itsec_file-change_notification_strings', array( $this, 'register_notification_strings' ) );
|
54 |
$scheduler->schedule( $interval, 'file-change' );
|
55 |
}
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
/**
|
58 |
* Register verbs for Sync.
|
59 |
*
|
core/modules/file-change/js/settings-page.js
CHANGED
@@ -34,23 +34,23 @@ jQuery( document ).ready( function ( $ ) {
|
|
34 |
*/
|
35 |
$( document ).on( 'click', '#itsec-file-change-one_time_check', function( e ) {
|
36 |
e.preventDefault();
|
37 |
-
|
38 |
//let user know we're working
|
39 |
$( '#itsec-file-change-one_time_check' )
|
40 |
.removeClass( 'button-primary' )
|
41 |
.addClass( 'button-secondary' )
|
42 |
.attr( 'value', itsec_file_change_settings.scanning_button_text )
|
43 |
.prop( 'disabled', true );
|
44 |
-
|
45 |
var data = {
|
46 |
'method': 'one-time-scan'
|
47 |
};
|
48 |
-
|
49 |
$( '#itsec_file_change_status' ).html( '' );
|
50 |
-
|
51 |
-
|
52 |
$( '#itsec_file_change_status' ).html( '' );
|
53 |
-
|
54 |
if ( false === results.response ) {
|
55 |
$( '#itsec_file_change_status' ).append( '<div class="updated fade inline"><p><strong>' + itsec_file_change_settings.no_changes + '</strong></p></div>' );
|
56 |
} else if ( true === results.response ) {
|
@@ -64,7 +64,7 @@ jQuery( document ).ready( function ( $ ) {
|
|
64 |
} else {
|
65 |
$( '#itsec_file_change_status' ).append( '<div class="error inline"><p><strong>' + itsec_file_change_settings.unknown_error + '</strong></p></div>' );
|
66 |
}
|
67 |
-
|
68 |
$( '#itsec-file-change-one_time_check' )
|
69 |
.removeClass( 'button-secondary' )
|
70 |
.addClass( 'button-primary' )
|
34 |
*/
|
35 |
$( document ).on( 'click', '#itsec-file-change-one_time_check', function( e ) {
|
36 |
e.preventDefault();
|
37 |
+
|
38 |
//let user know we're working
|
39 |
$( '#itsec-file-change-one_time_check' )
|
40 |
.removeClass( 'button-primary' )
|
41 |
.addClass( 'button-secondary' )
|
42 |
.attr( 'value', itsec_file_change_settings.scanning_button_text )
|
43 |
.prop( 'disabled', true );
|
44 |
+
|
45 |
var data = {
|
46 |
'method': 'one-time-scan'
|
47 |
};
|
48 |
+
|
49 |
$( '#itsec_file_change_status' ).html( '' );
|
50 |
+
|
51 |
+
itsecUtil.sendModuleAJAXRequest( 'file-change', data, function( results ) {
|
52 |
$( '#itsec_file_change_status' ).html( '' );
|
53 |
+
|
54 |
if ( false === results.response ) {
|
55 |
$( '#itsec_file_change_status' ).append( '<div class="updated fade inline"><p><strong>' + itsec_file_change_settings.no_changes + '</strong></p></div>' );
|
56 |
} else if ( true === results.response ) {
|
64 |
} else {
|
65 |
$( '#itsec_file_change_status' ).append( '<div class="error inline"><p><strong>' + itsec_file_change_settings.unknown_error + '</strong></p></div>' );
|
66 |
}
|
67 |
+
|
68 |
$( '#itsec-file-change-one_time_check' )
|
69 |
.removeClass( 'button-secondary' )
|
70 |
.addClass( 'button-primary' )
|
core/modules/file-change/logs.php
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
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 ) {
|
10 |
+
$entry['module_display'] = esc_html__( 'File Change', 'better-wp-security' );
|
11 |
+
|
12 |
+
if ( 'scan' === $code && 'process-start' === $entry['type'] ) {
|
13 |
+
$entry['description'] = esc_html__( 'Scan Performance', 'better-wp-security' );
|
14 |
+
} else if ( 'no-changes-found' === $code ) {
|
15 |
+
$entry['description'] = esc_html__( 'No Changes Found', 'better-wp-security' );
|
16 |
+
} else if ( 'changes-found' === $code ) {
|
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 {
|
20 |
+
$entry['description'] = esc_html__( 'Changes Found', 'better-wp-security' );
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
return $entry;
|
25 |
+
}
|
26 |
+
|
27 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
28 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
29 |
+
|
30 |
+
$details['module']['content'] = $entry['module_display'];
|
31 |
+
$details['description']['content'] = $entry['description'];
|
32 |
+
|
33 |
+
if ( 'process-start' !== $entry['type'] ) {
|
34 |
+
$details['memory'] = array(
|
35 |
+
'header' => esc_html__( 'Memory Used', 'better-wp-security' ),
|
36 |
+
'content' => sprintf( esc_html_x( '%s MB', 'Megabytes of memory used', 'better-wp-security' ), $entry['data']['memory'] ),
|
37 |
+
);
|
38 |
+
|
39 |
+
$types = array(
|
40 |
+
'added' => esc_html__( 'Added', 'better-wp-security' ),
|
41 |
+
'removed' => esc_html__( 'Removed', 'better-wp-security' ),
|
42 |
+
'changed' => esc_html__( 'Changed', 'better-wp-security' ),
|
43 |
+
);
|
44 |
+
|
45 |
+
foreach ( $types as $type => $header ) {
|
46 |
+
$details[$type] = array(
|
47 |
+
'header' => $header,
|
48 |
+
'content' => '<pre>' . implode( "\n", array_keys( $entry['data'][$type] ) ) . '</pre>',
|
49 |
+
);
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
return $details;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
new ITSEC_File_Change_Logs();
|
core/modules/file-change/scanner.php
CHANGED
@@ -11,15 +11,6 @@ final class ITSEC_File_Change_Scanner {
|
|
11 |
*/
|
12 |
private $excludes;
|
13 |
|
14 |
-
/**
|
15 |
-
* Flag to indicate if a file change scan is in process
|
16 |
-
*
|
17 |
-
* @since 4.0.0
|
18 |
-
* @access private
|
19 |
-
* @var bool
|
20 |
-
*/
|
21 |
-
private $running;
|
22 |
-
|
23 |
/**
|
24 |
* The module's saved options
|
25 |
*
|
@@ -29,21 +20,12 @@ final class ITSEC_File_Change_Scanner {
|
|
29 |
*/
|
30 |
private $settings;
|
31 |
|
32 |
-
private
|
33 |
-
|
34 |
|
35 |
-
private
|
36 |
|
37 |
-
$this->settings = ITSEC_Modules::get_settings( 'file-change' );
|
38 |
-
$this->running = false;
|
39 |
-
$this->excludes = array(
|
40 |
-
'file_change.lock',
|
41 |
-
ITSEC_Modules::get_setting( 'backup', 'location' ),
|
42 |
-
ITSEC_Modules::get_setting( 'global', 'log_location' ),
|
43 |
-
'.lock',
|
44 |
-
);
|
45 |
|
46 |
-
}
|
47 |
|
48 |
/**
|
49 |
* Executes file checking
|
@@ -67,247 +49,287 @@ final class ITSEC_File_Change_Scanner {
|
|
67 |
return self::$instance->execute_file_check( $scheduled_call, $return_data );
|
68 |
}
|
69 |
|
70 |
-
|
71 |
|
72 |
-
|
|
|
|
|
73 |
|
74 |
-
if ( false === $this->running ) {
|
75 |
|
76 |
-
|
77 |
-
$send_email = true;
|
78 |
|
79 |
-
ITSEC_Lib::set_minimum_memory_limit( '256M' );
|
80 |
|
81 |
-
|
82 |
|
83 |
-
|
84 |
|
85 |
-
|
86 |
-
|
|
|
87 |
|
88 |
-
|
|
|
|
|
89 |
|
90 |
-
|
|
|
|
|
|
|
91 |
|
92 |
-
} else {
|
93 |
|
94 |
-
|
95 |
|
96 |
-
|
97 |
|
98 |
-
|
99 |
|
100 |
-
|
|
|
101 |
|
102 |
-
|
|
|
|
|
|
|
|
|
103 |
|
104 |
-
|
105 |
|
106 |
-
$db_field = 'itsec_local_file_list_' . $chunk;
|
107 |
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
|
110 |
-
|
111 |
|
112 |
-
|
113 |
|
114 |
-
|
115 |
-
$memory_used = @memory_get_peak_usage();
|
116 |
|
117 |
-
|
|
|
|
|
118 |
|
119 |
-
|
120 |
-
|
121 |
|
122 |
-
|
123 |
|
124 |
-
|
|
|
|
|
125 |
|
126 |
-
|
127 |
|
128 |
-
add_site_option( $db_field, $logged_files );
|
129 |
|
130 |
-
|
131 |
|
132 |
-
|
133 |
|
134 |
-
|
|
|
135 |
|
136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
|
138 |
-
|
139 |
-
$current_files = $this->scan_files( '', $scheduled_call, $chunk ); //scan current files
|
140 |
-
do_action( 'itsec-file-change-end-scan' );
|
141 |
|
142 |
-
|
143 |
-
$files_removed = @array_diff_assoc( $logged_files, $current_files ); //files deleted
|
144 |
-
$current_minus_added = @array_diff_key( $current_files, $files_added ); //remove all added files from current filelist
|
145 |
-
$logged_minus_deleted = @array_diff_key( $logged_files, $files_removed ); //remove all deleted files from old file list
|
146 |
-
$files_changed = array(); //array of changed files
|
147 |
|
148 |
-
|
149 |
|
150 |
-
|
151 |
-
foreach ( $current_minus_added as $current_file => $current_attr ) {
|
152 |
|
153 |
-
|
|
|
|
|
154 |
|
155 |
-
|
156 |
-
if (
|
157 |
-
(
|
158 |
-
(
|
159 |
-
isset( $current_attr['mod_date'] ) &&
|
160 |
-
0 != strcmp( $current_attr['mod_date'], $logged_minus_deleted[ $current_file ]['mod_date'] )
|
161 |
-
) ||
|
162 |
-
0 != strcmp( $current_attr['d'], $logged_minus_deleted[ $current_file ]['d'] )
|
163 |
-
) ||
|
164 |
-
(
|
165 |
-
(
|
166 |
-
isset( $current_attr['hash'] ) &&
|
167 |
-
0 != strcmp( $current_attr['hash'], $logged_minus_deleted[ $current_file ]['hash'] ) ) ||
|
168 |
-
0 != strcmp( $current_attr['h'], $logged_minus_deleted[ $current_file ]['h'] )
|
169 |
-
)
|
170 |
-
) {
|
171 |
|
172 |
-
$remote_check = apply_filters( 'itsec_process_changed_file', true, $current_file, $current_attr['h'] ); //hook to run actions on a changed file at time of discovery
|
173 |
|
174 |
-
|
|
|
|
|
|
|
|
|
175 |
|
176 |
-
|
177 |
-
$files_changed[ $current_file ]['d'] = isset( $current_attr['mod_date'] ) ? $current_attr['mod_date'] : $current_attr['d'];
|
178 |
|
179 |
-
|
|
|
180 |
|
181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
183 |
}
|
184 |
|
185 |
}
|
186 |
|
187 |
-
|
188 |
-
$files_added_count = sizeof( $files_added );
|
189 |
-
$files_deleted_count = sizeof( $files_removed );
|
190 |
-
$files_changed_count = sizeof( $files_changed );
|
191 |
|
192 |
-
|
193 |
|
194 |
-
$files_added = apply_filters( 'itsec_process_added_files', $files_added ); //hook to run actions on all files added
|
195 |
-
$files_added_count = sizeof( $files_added );
|
196 |
|
197 |
-
|
|
|
|
|
|
|
198 |
|
199 |
-
|
200 |
-
do_action( 'itsec_process_removed_files', $files_removed ); //hook to run actions on all files removed
|
201 |
-
}
|
202 |
|
203 |
-
|
|
|
204 |
|
205 |
-
|
206 |
-
$full_change_list = array(
|
207 |
-
'added' => $files_added,
|
208 |
-
'removed' => $files_removed,
|
209 |
-
'changed' => $files_changed,
|
210 |
-
);
|
211 |
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
'changed' => count( $files_changed ),
|
216 |
-
);
|
217 |
|
218 |
-
|
219 |
|
220 |
-
|
221 |
-
unset( $files_added );
|
222 |
-
unset( $files_removed );
|
223 |
-
unset( $files_changed );
|
224 |
-
unset( $current_files );
|
225 |
|
226 |
-
$this->settings['last_run'] = ITSEC_Core::get_current_time();
|
227 |
-
$this->settings['last_chunk'] = $chunk;
|
228 |
|
229 |
-
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
|
237 |
-
|
238 |
|
239 |
-
$itsec_logger->log_event(
|
240 |
-
'file_change',
|
241 |
-
8,
|
242 |
-
$full_change_list
|
243 |
-
);
|
244 |
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
0 < $files_changed_count ||
|
251 |
-
0 < $files_deleted_count
|
252 |
-
)
|
253 |
-
) {
|
254 |
|
255 |
-
|
256 |
-
|
257 |
-
$files_deleted_count,
|
258 |
-
$files_changed_count,
|
259 |
-
$full_change_list
|
260 |
-
);
|
261 |
|
262 |
-
|
263 |
-
}
|
264 |
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
) {
|
273 |
-
ITSEC_Modules::set_setting( 'file-change', 'show_warning', true );
|
274 |
-
}
|
275 |
|
276 |
-
|
|
|
|
|
|
|
|
|
277 |
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
|
280 |
-
|
|
|
281 |
|
282 |
-
|
283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
284 |
|
285 |
-
|
|
|
|
|
|
|
|
|
286 |
|
287 |
-
|
288 |
|
289 |
-
return true;
|
290 |
|
291 |
-
|
292 |
|
293 |
-
|
294 |
|
295 |
-
|
|
|
296 |
|
297 |
-
|
298 |
|
299 |
-
|
|
|
|
|
300 |
|
301 |
}
|
302 |
|
303 |
-
|
304 |
|
305 |
-
return
|
306 |
|
307 |
}
|
308 |
|
309 |
-
return -1;
|
310 |
-
|
311 |
}
|
312 |
|
313 |
/**
|
@@ -328,73 +350,53 @@ final class ITSEC_File_Change_Scanner {
|
|
328 |
}
|
329 |
|
330 |
/**
|
331 |
-
*
|
332 |
*
|
333 |
-
*
|
|
|
334 |
*
|
335 |
-
* @since 4.
|
336 |
*
|
337 |
* @access private
|
338 |
*
|
339 |
-
* @param string $
|
|
|
340 |
*
|
341 |
-
* @return
|
342 |
*/
|
343 |
-
private function
|
344 |
|
345 |
-
|
346 |
-
$
|
347 |
-
$
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
}
|
353 |
|
354 |
-
|
355 |
-
$abs_file = ITSEC_Lib::get_home_path() . $file;
|
356 |
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
if ( is_array( $this->excludes ) && ( in_array( $file, $this->excludes ) || in_array( $abs_file, $this->excludes ) ) ) {
|
361 |
-
return false;
|
362 |
-
}
|
363 |
-
|
364 |
-
if ( in_array( $file, $file_list ) ) {
|
365 |
-
$flag = true;
|
366 |
-
}
|
367 |
|
368 |
-
|
369 |
|
370 |
-
$
|
371 |
|
372 |
-
|
|
|
|
|
|
|
|
|
373 |
|
374 |
-
return false;
|
375 |
-
|
376 |
-
}
|
377 |
-
|
378 |
-
if ( isset( $path_info['extension'] ) && in_array( '.' . $path_info['extension'], $type_list ) ) {
|
379 |
-
$flag = true;
|
380 |
}
|
381 |
|
382 |
}
|
383 |
|
384 |
-
|
385 |
-
|
386 |
-
if ( true === $flag ) { //if exclude reverse
|
387 |
-
return false;
|
388 |
-
} else {
|
389 |
-
return true;
|
390 |
-
}
|
391 |
-
|
392 |
-
} else { //return flag
|
393 |
-
|
394 |
-
return $flag;
|
395 |
-
|
396 |
-
}
|
397 |
|
|
|
398 |
}
|
399 |
|
400 |
/**
|
@@ -407,89 +409,63 @@ final class ITSEC_File_Change_Scanner {
|
|
407 |
*
|
408 |
* @access private
|
409 |
*
|
410 |
-
* @param string $path
|
411 |
-
* @param bool $scheduled_call is this a scheduled call
|
412 |
-
* @param mixed $chunk the current chunk or false
|
413 |
*
|
414 |
* @return array array of files found and their information
|
415 |
*
|
416 |
*/
|
417 |
-
private function scan_files( $path
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
$content_dir = explode( '/', WP_CONTENT_DIR );
|
422 |
-
$plugin_dir = explode( '/', WP_PLUGIN_DIR );
|
423 |
-
|
424 |
-
$dirs = array(
|
425 |
-
'wp-admin/',
|
426 |
-
WPINC . '/',
|
427 |
-
$content_dir[ sizeof( $content_dir ) - 1 ] . '/',
|
428 |
-
$content_dir[ sizeof( $content_dir ) - 1 ] . '/uploads/',
|
429 |
-
$content_dir[ sizeof( $content_dir ) - 1 ] . '/themes/',
|
430 |
-
$content_dir[ sizeof( $content_dir ) - 1 ] . '/' . $plugin_dir[ sizeof( $plugin_dir ) - 1 ] . '/',
|
431 |
-
''
|
432 |
-
);
|
433 |
-
|
434 |
-
$path = $dirs[ $chunk ];
|
435 |
-
|
436 |
-
unset( $dirs[ $chunk ] );
|
437 |
-
|
438 |
-
$this->excludes = $dirs;
|
439 |
-
|
440 |
}
|
441 |
|
442 |
-
$data = array();
|
443 |
-
|
444 |
-
$clean_path = sanitize_text_field( $path );
|
445 |
-
|
446 |
-
if ( $directory_handle = @opendir( ITSEC_Lib::get_home_path() . $clean_path ) ) { //get the directory
|
447 |
-
|
448 |
-
while ( false !== ( $item = @readdir( $directory_handle ) ) ) { // loop through dirs
|
449 |
-
|
450 |
-
if ( '.' != $item && '..' != $item ) { //don't scan parents
|
451 |
|
452 |
-
|
453 |
-
|
454 |
-
$absname = ITSEC_Lib::get_home_path() . $relname;
|
455 |
-
|
456 |
-
if ( is_dir( $absname ) && 'dir' == filetype( $absname ) ) {
|
457 |
|
458 |
-
|
459 |
-
|
|
|
460 |
|
461 |
-
} else {
|
462 |
|
463 |
-
|
464 |
-
$check_name = $relname;
|
465 |
|
466 |
-
|
|
|
|
|
467 |
|
468 |
-
if ( true === $this->is_checkable_file( $check_name ) ) { //make sure the user wants this file scanned
|
469 |
|
470 |
-
|
|
|
471 |
|
472 |
-
$data = array_merge( $data, $this->scan_files( $relname . '/', $scheduled_call, false ) );
|
473 |
|
474 |
-
|
|
|
|
|
|
|
|
|
475 |
|
476 |
-
$data[ $relname ] = array();
|
477 |
-
$data[ $relname ]['d'] = @filemtime( $absname );
|
478 |
-
$data[ $relname ]['h'] = @md5_file( $absname );
|
479 |
|
480 |
-
|
481 |
|
482 |
-
|
483 |
|
|
|
|
|
|
|
484 |
}
|
485 |
|
|
|
|
|
|
|
|
|
486 |
}
|
487 |
|
488 |
-
@closedir( $directory_handle ); //close the directory we're working with
|
489 |
-
|
490 |
}
|
491 |
|
492 |
-
|
|
|
|
|
493 |
|
494 |
}
|
495 |
|
11 |
*/
|
12 |
private $excludes;
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
/**
|
15 |
* The module's saved options
|
16 |
*
|
20 |
*/
|
21 |
private $settings;
|
22 |
|
23 |
+
private $home_path;
|
|
|
24 |
|
25 |
+
private static $instance = false;
|
26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
+
private function __construct() {}
|
29 |
|
30 |
/**
|
31 |
* Executes file checking
|
49 |
return self::$instance->execute_file_check( $scheduled_call, $return_data );
|
50 |
}
|
51 |
|
52 |
+
private function execute_file_check( $scheduled_call, $return_data ) {
|
53 |
|
54 |
+
if ( ! ITSEC_Lib::get_lock( 'file_change', 300 ) ) {
|
55 |
+
return -1;
|
56 |
+
}
|
57 |
|
|
|
58 |
|
59 |
+
$process_id = ITSEC_Log::add_process_start( 'file_change', 'scan' );
|
|
|
60 |
|
|
|
61 |
|
62 |
+
$this->home_path = untrailingslashit( ITSEC_Lib::get_home_path() );
|
63 |
|
64 |
+
$this->settings = ITSEC_Modules::get_settings( 'file-change' );
|
65 |
|
66 |
+
if ( ! in_array( '.lock', $this->settings['types'] ) ) {
|
67 |
+
$this->settings['types'][] = '.lock';
|
68 |
+
}
|
69 |
|
70 |
+
foreach ( $this->settings['file_list'] as $index => $path ) {
|
71 |
+
$this->settings['file_list'][$index] = untrailingslashit( $path );
|
72 |
+
}
|
73 |
|
74 |
+
$this->excludes = array(
|
75 |
+
ITSEC_Modules::get_setting( 'backup', 'location' ),
|
76 |
+
ITSEC_Modules::get_setting( 'global', 'log_location' ),
|
77 |
+
);
|
78 |
|
|
|
79 |
|
80 |
+
$send_email = true;
|
81 |
|
82 |
+
ITSEC_Lib::set_minimum_memory_limit( '512M' );
|
83 |
|
84 |
+
define( 'ITSEC_DOING_FILE_CHECK', true );
|
85 |
|
86 |
+
//figure out what chunk we're on
|
87 |
+
if ( $this->settings['split'] ) {
|
88 |
|
89 |
+
if ( false === $this->settings['last_chunk'] || $this->settings['last_chunk'] > 5 ) {
|
90 |
+
$chunk = 0;
|
91 |
+
} else {
|
92 |
+
$chunk = $this->settings['last_chunk'] + 1;
|
93 |
+
}
|
94 |
|
95 |
+
$db_field = 'itsec_local_file_list_' . $chunk;
|
96 |
|
|
|
97 |
|
98 |
+
$content_dir = explode( '/', WP_CONTENT_DIR );
|
99 |
+
$plugin_dir = explode( '/', WP_PLUGIN_DIR );
|
100 |
+
$wp_upload_dir = ITSEC_Core::get_wp_upload_dir();
|
101 |
+
|
102 |
+
$dirs = array(
|
103 |
+
'wp-admin',
|
104 |
+
WPINC,
|
105 |
+
WP_CONTENT_DIR,
|
106 |
+
ITSEC_Core::get_wp_upload_dir(),
|
107 |
+
WP_CONTENT_DIR . '/themes',
|
108 |
+
WP_PLUGIN_DIR,
|
109 |
+
''
|
110 |
+
);
|
111 |
|
112 |
+
$path = $dirs[ $chunk ];
|
113 |
|
114 |
+
unset( $dirs[ $chunk ] );
|
115 |
|
116 |
+
$this->excludes = array_merge( $this->excludes, $dirs );
|
|
|
117 |
|
118 |
+
foreach ( $this->excludes as $index => $path ) {
|
119 |
+
$path = untrailingslashit( $path );
|
120 |
+
$path = preg_replace( '/^' . preg_quote( ABSPATH, '/' ) . '/', '', $path );
|
121 |
|
122 |
+
$this->excludes[$index] = $path;
|
123 |
+
}
|
124 |
|
125 |
+
} else {
|
126 |
|
127 |
+
$chunk = false;
|
128 |
+
$db_field = 'itsec_local_file_list';
|
129 |
+
$path = '';
|
130 |
|
131 |
+
}
|
132 |
|
|
|
133 |
|
134 |
+
$memory_used = @memory_get_peak_usage();
|
135 |
|
136 |
+
$logged_files = get_site_option( $db_field );
|
137 |
|
138 |
+
//if there are no old files old file list is an empty array
|
139 |
+
if ( false === $logged_files ) {
|
140 |
|
141 |
+
$send_email = false;
|
142 |
+
|
143 |
+
$logged_files = array();
|
144 |
+
|
145 |
+
if ( is_multisite() ) {
|
146 |
+
|
147 |
+
add_site_option( $db_field, $logged_files );
|
148 |
+
|
149 |
+
} else {
|
150 |
|
151 |
+
add_option( $db_field, $logged_files, '', 'no' );
|
|
|
|
|
152 |
|
153 |
+
}
|
|
|
|
|
|
|
|
|
154 |
|
155 |
+
}
|
156 |
|
157 |
+
ITSEC_Log::add_process_update( $process_id, array( 'status' => 'init_complete', 'settings' => $this->settings, 'excludes' => $this->excludes, 'path' => $path, 'scheduled_call' => $scheduled_call, 'chunk' => $chunk ) );
|
|
|
158 |
|
159 |
+
do_action( 'itsec-file-change-start-scan' );
|
160 |
+
$current_files = $this->scan_files( $path );
|
161 |
+
do_action( 'itsec-file-change-end-scan' );
|
162 |
|
163 |
+
ITSEC_Log::add_process_update( $process_id, array( 'status' => 'file_scan_complete' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
|
|
165 |
|
166 |
+
$files_added = @array_diff_assoc( $current_files, $logged_files ); //files added
|
167 |
+
$files_removed = @array_diff_assoc( $logged_files, $current_files ); //files deleted
|
168 |
+
$current_minus_added = @array_diff_key( $current_files, $files_added ); //remove all added files from current filelist
|
169 |
+
$logged_minus_deleted = @array_diff_key( $logged_files, $files_removed ); //remove all deleted files from old file list
|
170 |
+
$files_changed = array(); //array of changed files
|
171 |
|
172 |
+
do_action( 'itsec-file-change-start-hash-comparisons' );
|
|
|
173 |
|
174 |
+
//compare file hashes and mod dates
|
175 |
+
foreach ( $current_minus_added as $current_file => $current_attr ) {
|
176 |
|
177 |
+
if ( array_key_exists( $current_file, $logged_minus_deleted ) ) {
|
178 |
+
|
179 |
+
//if attributes differ added to changed files array
|
180 |
+
if (
|
181 |
+
(
|
182 |
+
(
|
183 |
+
isset( $current_attr['mod_date'] ) &&
|
184 |
+
0 != strcmp( $current_attr['mod_date'], $logged_minus_deleted[ $current_file ]['mod_date'] )
|
185 |
+
) ||
|
186 |
+
0 != strcmp( $current_attr['d'], $logged_minus_deleted[ $current_file ]['d'] )
|
187 |
+
) ||
|
188 |
+
(
|
189 |
+
(
|
190 |
+
isset( $current_attr['hash'] ) &&
|
191 |
+
0 != strcmp( $current_attr['hash'], $logged_minus_deleted[ $current_file ]['hash'] ) ) ||
|
192 |
+
0 != strcmp( $current_attr['h'], $logged_minus_deleted[ $current_file ]['h'] )
|
193 |
+
)
|
194 |
+
) {
|
195 |
+
|
196 |
+
$remote_check = apply_filters( 'itsec_process_changed_file', true, $current_file, $current_attr['h'] ); //hook to run actions on a changed file at time of discovery
|
197 |
+
|
198 |
+
if ( true === $remote_check ) { //don't list the file if it matches the WordPress.org hash
|
199 |
+
|
200 |
+
$files_changed[ $current_file ]['h'] = isset( $current_attr['hash'] ) ? $current_attr['hash'] : $current_attr['h'];
|
201 |
+
$files_changed[ $current_file ]['d'] = isset( $current_attr['mod_date'] ) ? $current_attr['mod_date'] : $current_attr['d'];
|
202 |
|
203 |
}
|
204 |
|
205 |
}
|
206 |
|
207 |
+
}
|
|
|
|
|
|
|
208 |
|
209 |
+
}
|
210 |
|
|
|
|
|
211 |
|
212 |
+
//get count of changes
|
213 |
+
$files_added_count = count( $files_added );
|
214 |
+
$files_deleted_count = count( $files_removed );
|
215 |
+
$files_changed_count = count( $files_changed );
|
216 |
|
217 |
+
if ( $files_added_count > 0 ) {
|
|
|
|
|
218 |
|
219 |
+
$files_added = apply_filters( 'itsec_process_added_files', $files_added ); //hook to run actions on all files added
|
220 |
+
$files_added_count = count( $files_added );
|
221 |
|
222 |
+
}
|
|
|
|
|
|
|
|
|
|
|
223 |
|
224 |
+
if ( $files_deleted_count > 0 ) {
|
225 |
+
do_action( 'itsec_process_removed_files', $files_removed ); //hook to run actions on all files removed
|
226 |
+
}
|
|
|
|
|
227 |
|
228 |
+
do_action( 'itsec-file-change-end-hash-comparisons' );
|
229 |
|
230 |
+
ITSEC_Log::add_process_update( $process_id, array( 'status' => 'hash_comparisons_complete' ) );
|
|
|
|
|
|
|
|
|
231 |
|
|
|
|
|
232 |
|
233 |
+
//create single array of all changes
|
234 |
+
$full_change_list = array(
|
235 |
+
'added' => $files_added,
|
236 |
+
'removed' => $files_removed,
|
237 |
+
'changed' => $files_changed,
|
238 |
+
);
|
239 |
|
240 |
+
$this->settings['latest_changes'] = array(
|
241 |
+
'added' => count( $files_added ),
|
242 |
+
'removed' => count( $files_removed ),
|
243 |
+
'changed' => count( $files_changed ),
|
244 |
+
);
|
245 |
|
246 |
+
update_site_option( $db_field, $current_files );
|
247 |
|
|
|
|
|
|
|
|
|
|
|
248 |
|
249 |
+
//Cleanup variables when we're done with them
|
250 |
+
unset( $files_added );
|
251 |
+
unset( $files_removed );
|
252 |
+
unset( $files_changed );
|
253 |
+
unset( $current_files );
|
|
|
|
|
|
|
|
|
254 |
|
255 |
+
$this->settings['last_run'] = ITSEC_Core::get_current_time();
|
256 |
+
$this->settings['last_chunk'] = $chunk;
|
|
|
|
|
|
|
|
|
257 |
|
258 |
+
ITSEC_Modules::set_settings( 'file-change', $this->settings );
|
|
|
259 |
|
260 |
+
//get new max memory
|
261 |
+
$check_memory = @memory_get_peak_usage();
|
262 |
+
if ( $check_memory > $memory_used ) {
|
263 |
+
$memory_used = $check_memory - $memory_used;
|
264 |
+
}
|
265 |
+
|
266 |
+
$full_change_list['memory'] = round( ( $memory_used / 1000000 ), 2 );
|
|
|
|
|
|
|
267 |
|
268 |
+
if ( $files_added_count > 0 || $files_changed_count > 0 || $files_deleted_count > 0 ) {
|
269 |
+
$found_changes = true;
|
270 |
+
} else {
|
271 |
+
$found_changes = false;
|
272 |
+
}
|
273 |
|
274 |
+
if (
|
275 |
+
$found_changes &&
|
276 |
+
$send_email &&
|
277 |
+
! $scheduled_call &&
|
278 |
+
$this->settings['email']
|
279 |
+
) {
|
280 |
+
|
281 |
+
$email_details = array(
|
282 |
+
$files_added_count,
|
283 |
+
$files_deleted_count,
|
284 |
+
$files_changed_count,
|
285 |
+
$full_change_list
|
286 |
+
);
|
287 |
|
288 |
+
$this->send_notification_email( $email_details );
|
289 |
+
}
|
290 |
|
291 |
+
if (
|
292 |
+
$found_changes &&
|
293 |
+
$this->settings['notify_admin'] &&
|
294 |
+
function_exists( 'get_current_screen' ) &&
|
295 |
+
(
|
296 |
+
! isset( get_current_screen()->id ) ||
|
297 |
+
false === strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_logs' )
|
298 |
+
)
|
299 |
+
) {
|
300 |
+
ITSEC_Modules::set_setting( 'file-change', 'show_warning', true );
|
301 |
+
}
|
302 |
|
303 |
+
if ( $found_changes ) {
|
304 |
+
ITSEC_Log::add_warning( 'file_change', "changes-found::$files_added_count,$files_deleted_count,$files_changed_count", $full_change_list );
|
305 |
+
} else {
|
306 |
+
ITSEC_Log::add_notice( 'file_change', 'no-changes-found', $full_change_list );
|
307 |
+
}
|
308 |
|
309 |
+
ITSEC_Lib::release_lock( 'file_change' );
|
310 |
|
|
|
311 |
|
312 |
+
ITSEC_Log::add_process_stop( $process_id );
|
313 |
|
314 |
+
if ( $files_added_count > 0 || $files_changed_count > 0 || $files_deleted_count > 0 ) {
|
315 |
|
316 |
+
//There were changes found
|
317 |
+
if ( $return_data ) {
|
318 |
|
319 |
+
return $full_change_list;
|
320 |
|
321 |
+
} else {
|
322 |
+
|
323 |
+
return true;
|
324 |
|
325 |
}
|
326 |
|
327 |
+
} else {
|
328 |
|
329 |
+
return false; //No changes were found
|
330 |
|
331 |
}
|
332 |
|
|
|
|
|
333 |
}
|
334 |
|
335 |
/**
|
350 |
}
|
351 |
|
352 |
/**
|
353 |
+
* Builds table section for file report
|
354 |
*
|
355 |
+
* Builds the individual table areas for files added, changed and deleted that goes in the file
|
356 |
+
* change notification emails.
|
357 |
*
|
358 |
+
* @since 4.6.0
|
359 |
*
|
360 |
* @access private
|
361 |
*
|
362 |
+
* @param string $title User readable title to display
|
363 |
+
* @param array $files array of files to build the report on
|
364 |
*
|
365 |
+
* @return string the markup with the given files to be added to the report
|
366 |
*/
|
367 |
+
private function build_table_section( $title, $files ) {
|
368 |
|
369 |
+
$section = '<h4>' . __( 'Files', 'better-wp-security' ) . ' ' . $title . '</h4>';
|
370 |
+
$section .= '<table border="1" style="width: 100%; text-align: center;">' . PHP_EOL;
|
371 |
+
$section .= '<tr>' . PHP_EOL;
|
372 |
+
$section .= '<th>' . __( 'File', 'better-wp-security' ) . '</th>' . PHP_EOL;
|
373 |
+
$section .= '<th>' . __( 'Modified', 'better-wp-security' ) . '</th>' . PHP_EOL;
|
374 |
+
$section .= '<th>' . __( 'File Hash', 'better-wp-security' ) . '</th>' . PHP_EOL;
|
375 |
+
$section .= '</tr>' . PHP_EOL;
|
|
|
376 |
|
377 |
+
if ( empty( $files ) ) {
|
|
|
378 |
|
379 |
+
$section .= '<tr>' . PHP_EOL;
|
380 |
+
$section .= '<td colspan="3">' . __( 'No files were changed.', 'better-wp-security' ) . '</td>' . PHP_EOL;
|
381 |
+
$section .= '</tr>' . PHP_EOL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
|
383 |
+
} else {
|
384 |
|
385 |
+
foreach ( $files as $item => $attr ) {
|
386 |
|
387 |
+
$section .= '<tr>' . PHP_EOL;
|
388 |
+
$section .= '<td>' . $item . '</td>' . PHP_EOL;
|
389 |
+
$section .= '<td>' . date( 'l F jS, Y \a\t g:i a e', ( isset( $attr['mod_date'] ) ? $attr['mod_date'] : $attr['d'] ) ) . '</td>' . PHP_EOL;
|
390 |
+
$section .= '<td>' . ( isset( $attr['hash'] ) ? $attr['hash'] : $attr['h'] ) . '</td>' . PHP_EOL;
|
391 |
+
$section .= '</tr>' . PHP_EOL;
|
392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
}
|
394 |
|
395 |
}
|
396 |
|
397 |
+
$section .= '</table>' . PHP_EOL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
398 |
|
399 |
+
return $section;
|
400 |
}
|
401 |
|
402 |
/**
|
409 |
*
|
410 |
* @access private
|
411 |
*
|
412 |
+
* @param string $path Path to scan. Defaults to WordPress root
|
|
|
|
|
413 |
*
|
414 |
* @return array array of files found and their information
|
415 |
*
|
416 |
*/
|
417 |
+
private function scan_files( $path ) {
|
418 |
+
if ( in_array( $path, $this->excludes ) ) {
|
419 |
+
return array();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
420 |
}
|
421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
422 |
|
423 |
+
$abspath = "{$this->home_path}/$path";
|
424 |
+
$data = array();
|
|
|
|
|
|
|
425 |
|
426 |
+
if ( false === ( $dh = @opendir( $abspath ) ) ) {
|
427 |
+
return $data;
|
428 |
+
}
|
429 |
|
|
|
430 |
|
431 |
+
while ( false !== ( $item = @readdir( $dh ) ) ) {
|
|
|
432 |
|
433 |
+
if ( '.' === $item || '..' === $item ) {
|
434 |
+
continue;
|
435 |
+
}
|
436 |
|
|
|
437 |
|
438 |
+
$relname = "$path/$item";
|
439 |
+
$absname = "$abspath/$item";
|
440 |
|
|
|
441 |
|
442 |
+
// Efficient but difficult to grock way to skip an item if it is in the file_list and the method is
|
443 |
+
// exclude or if it is not in the file_list and the method is include.
|
444 |
+
if ( in_array( $relname, $this->settings['file_list'] ) xor 'include' === $this->settings['method'] ) {
|
445 |
+
continue;
|
446 |
+
}
|
447 |
|
|
|
|
|
|
|
448 |
|
449 |
+
if ( is_dir( $absname ) && 'dir' === filetype( $absname ) ) {
|
450 |
|
451 |
+
$data = array_merge( $data, $this->scan_files( $relname ) );
|
452 |
|
453 |
+
} else {
|
454 |
+
if ( in_array( '.' . pathinfo( $item, PATHINFO_EXTENSION ), $this->settings['types'] ) ) {
|
455 |
+
continue;
|
456 |
}
|
457 |
|
458 |
+
$data[ substr( $relname, 1 ) ] = array(
|
459 |
+
'd' => @filemtime( $absname ),
|
460 |
+
'h' => @md5_file( $absname ),
|
461 |
+
);
|
462 |
}
|
463 |
|
|
|
|
|
464 |
}
|
465 |
|
466 |
+
@closedir( $dh );
|
467 |
+
|
468 |
+
return $data;
|
469 |
|
470 |
}
|
471 |
|
core/modules/global/js/settings-page.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
function itsec_change_show_error_codes( args ) {
|
2 |
var show = args[0];
|
3 |
-
|
4 |
if ( show ) {
|
5 |
jQuery( 'body' ).addClass( 'itsec-show-error-codes' );
|
6 |
} else {
|
@@ -10,7 +10,7 @@ function itsec_change_show_error_codes( args ) {
|
|
10 |
|
11 |
function itsec_change_write_files( args ) {
|
12 |
var enabled = args[0];
|
13 |
-
|
14 |
if ( enabled ) {
|
15 |
jQuery( 'body' ).removeClass( 'itsec-write-files-disabled' ).addClass( 'itsec-write-files-enabled' );
|
16 |
} else {
|
@@ -18,21 +18,40 @@ function itsec_change_write_files( args ) {
|
|
18 |
}
|
19 |
}
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
jQuery( document ).ready(function() {
|
22 |
var $container = jQuery( '#wpcontent' );
|
23 |
-
|
24 |
$container.on( 'click', '#itsec-global-add-to-whitelist', function( e ) {
|
25 |
e.preventDefault();
|
26 |
-
|
27 |
var whitelist = jQuery( '#itsec-global-lockout_white_list' ).val();
|
28 |
whitelist = whitelist.trim();
|
29 |
whitelist += "\n" + itsec_global_settings_page.ip;
|
30 |
jQuery( '#itsec-global-lockout_white_list' ).val( whitelist );
|
31 |
} );
|
32 |
-
|
33 |
$container.on( 'click', '#itsec-global-reset-log-location', function( e ) {
|
34 |
e.preventDefault();
|
35 |
-
|
36 |
jQuery( '#itsec-global-log_location' ).val( itsec_global_settings_page.log_location );
|
37 |
} );
|
|
|
|
|
|
|
|
|
38 |
});
|
1 |
function itsec_change_show_error_codes( args ) {
|
2 |
var show = args[0];
|
3 |
+
|
4 |
if ( show ) {
|
5 |
jQuery( 'body' ).addClass( 'itsec-show-error-codes' );
|
6 |
} else {
|
10 |
|
11 |
function itsec_change_write_files( args ) {
|
12 |
var enabled = args[0];
|
13 |
+
|
14 |
if ( enabled ) {
|
15 |
jQuery( 'body' ).removeClass( 'itsec-write-files-disabled' ).addClass( 'itsec-write-files-enabled' );
|
16 |
} else {
|
18 |
}
|
19 |
}
|
20 |
|
21 |
+
var itsec_log_type_changed = function() {
|
22 |
+
var type = jQuery( '#itsec-global-log_type' ).val();
|
23 |
+
|
24 |
+
if ( 'both' === type ) {
|
25 |
+
jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
|
26 |
+
jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
|
27 |
+
} else if ( 'file' === type ) {
|
28 |
+
jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).hide();
|
29 |
+
jQuery( '#itsec-global-log_location' ).parents( 'tr' ).show();
|
30 |
+
} else {
|
31 |
+
jQuery( '#itsec-global-log_rotation' ).parents( 'tr' ).show();
|
32 |
+
jQuery( '#itsec-global-log_location' ).parents( 'tr' ).hide();
|
33 |
+
}
|
34 |
+
};
|
35 |
+
|
36 |
jQuery( document ).ready(function() {
|
37 |
var $container = jQuery( '#wpcontent' );
|
38 |
+
|
39 |
$container.on( 'click', '#itsec-global-add-to-whitelist', function( e ) {
|
40 |
e.preventDefault();
|
41 |
+
|
42 |
var whitelist = jQuery( '#itsec-global-lockout_white_list' ).val();
|
43 |
whitelist = whitelist.trim();
|
44 |
whitelist += "\n" + itsec_global_settings_page.ip;
|
45 |
jQuery( '#itsec-global-lockout_white_list' ).val( whitelist );
|
46 |
} );
|
47 |
+
|
48 |
$container.on( 'click', '#itsec-global-reset-log-location', function( e ) {
|
49 |
e.preventDefault();
|
50 |
+
|
51 |
jQuery( '#itsec-global-log_location' ).val( itsec_global_settings_page.log_location );
|
52 |
} );
|
53 |
+
|
54 |
+
$container.on( 'change', '#itsec-global-log_type', itsec_log_type_changed );
|
55 |
+
|
56 |
+
itsec_log_type_changed();
|
57 |
});
|
core/modules/global/settings.php
CHANGED
@@ -15,7 +15,7 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
|
|
15 |
'blacklist_period' => 7,
|
16 |
'lockout_period' => 15,
|
17 |
'lockout_white_list' => array(),
|
18 |
-
'log_rotation' =>
|
19 |
'log_type' => 'database',
|
20 |
'log_location' => ITSEC_Core::get_storage_dir( 'logs' ),
|
21 |
'log_info' => '',
|
15 |
'blacklist_period' => 7,
|
16 |
'lockout_period' => 15,
|
17 |
'lockout_white_list' => array(),
|
18 |
+
'log_rotation' => 60,
|
19 |
'log_type' => 'database',
|
20 |
'log_location' => ITSEC_Core::get_storage_dir( 'logs' ),
|
21 |
'log_info' => '',
|
core/modules/ipcheck/class-itsec-ipcheck.php
CHANGED
@@ -15,10 +15,7 @@ class ITSEC_IPCheck {
|
|
15 |
private $settings;
|
16 |
|
17 |
public function run() {
|
18 |
-
|
19 |
-
add_action( 'itsec-handle-failed-login', array( $this, 'handle_failed_login' ), 10, 2 );
|
20 |
-
|
21 |
-
add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
|
22 |
}
|
23 |
|
24 |
private function load_settings() {
|
@@ -27,44 +24,27 @@ class ITSEC_IPCheck {
|
|
27 |
}
|
28 |
}
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
$time = ITSEC_Core::get_current_time_gmt();
|
33 |
-
|
34 |
-
if ( isset( $response['block'] ) ) {
|
35 |
-
$cache['block'] = (boolean) $response['block'];
|
36 |
-
}
|
37 |
-
|
38 |
-
if ( isset( $response['cache_ttl'] ) ) {
|
39 |
-
$cache['cache_ttl'] = intval( $response['cache_ttl'] ) + $time;
|
40 |
-
} else if ( 0 === $cache['cache_ttl'] ) {
|
41 |
-
$cache['cache_ttl'] = $time + HOUR_IN_SECONDS;
|
42 |
-
}
|
43 |
|
44 |
-
if (
|
45 |
-
|
|
|
46 |
}
|
47 |
|
48 |
-
$
|
49 |
-
|
50 |
-
|
51 |
-
set_site_transient( "itsec_ipcheck_$ip", $cache, $transient_time );
|
52 |
-
}
|
53 |
-
|
54 |
-
private function get_cache( $ip ) {
|
55 |
-
$cache = get_site_transient( "itsec_ipcheck_$ip" );
|
56 |
-
|
57 |
-
$defaults = array(
|
58 |
-
'block' => false,
|
59 |
-
'cache_ttl' => 0,
|
60 |
-
'report_ttl' => 0,
|
61 |
-
);
|
62 |
|
63 |
-
if (
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
}
|
66 |
|
67 |
-
return
|
68 |
}
|
69 |
|
70 |
/**
|
@@ -74,8 +54,8 @@ class ITSEC_IPCheck {
|
|
74 |
*
|
75 |
* @return bool true if banned, false otherwise.
|
76 |
*/
|
77 |
-
private function is_ip_banned(
|
78 |
-
return $this->get_server_response( 'check-ip'
|
79 |
}
|
80 |
|
81 |
/**
|
@@ -85,14 +65,11 @@ class ITSEC_IPCheck {
|
|
85 |
*
|
86 |
* @return bool true if banned, false otherwise.
|
87 |
*/
|
88 |
-
private function report_ip(
|
89 |
-
return $this->get_server_response( 'report-ip'
|
90 |
}
|
91 |
|
92 |
-
private function get_server_response( $action
|
93 |
-
global $itsec_logger;
|
94 |
-
|
95 |
-
|
96 |
$this->load_settings();
|
97 |
|
98 |
if ( empty( $this->settings['api_key'] ) || empty( $this->settings['api_secret'] ) ) {
|
@@ -100,9 +77,7 @@ class ITSEC_IPCheck {
|
|
100 |
}
|
101 |
|
102 |
|
103 |
-
|
104 |
-
$ip = ITSEC_Lib::get_ip();
|
105 |
-
}
|
106 |
|
107 |
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
108 |
|
@@ -167,7 +142,7 @@ class ITSEC_IPCheck {
|
|
167 |
'type' => 'host',
|
168 |
);
|
169 |
|
170 |
-
|
171 |
|
172 |
return true;
|
173 |
}
|
@@ -175,6 +150,46 @@ class ITSEC_IPCheck {
|
|
175 |
return false;
|
176 |
}
|
177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
/**
|
179 |
* Calculates the HMAC of a string using SHA1.
|
180 |
*
|
@@ -200,62 +215,4 @@ class ITSEC_IPCheck {
|
|
200 |
return base64_encode( $hmac );
|
201 |
}
|
202 |
|
203 |
-
/**
|
204 |
-
* Register IPCheck for logger
|
205 |
-
*
|
206 |
-
* @since 4.5
|
207 |
-
*
|
208 |
-
* @param array $logger_modules array of logger modules
|
209 |
-
*
|
210 |
-
* @return array array of logger modules
|
211 |
-
*/
|
212 |
-
public function itsec_logger_modules( $logger_modules ) {
|
213 |
-
$logger_modules['ipcheck'] = array(
|
214 |
-
'type' => 'ipcheck',
|
215 |
-
'function' => __( 'IP Flagged by Network Brute Force Protection', 'better-wp-security' ),
|
216 |
-
);
|
217 |
-
|
218 |
-
return $logger_modules;
|
219 |
-
}
|
220 |
-
|
221 |
-
/**
|
222 |
-
* Make sure user isn't already locked out even on successful form submission
|
223 |
-
*
|
224 |
-
* @since 4.5
|
225 |
-
*
|
226 |
-
* @return void
|
227 |
-
*/
|
228 |
-
public function wp_login() {
|
229 |
-
|
230 |
-
/** @var ITSEC_Lockout $itsec_lockout */
|
231 |
-
global $itsec_logger, $itsec_lockout;
|
232 |
-
|
233 |
-
$this->load_settings();
|
234 |
-
|
235 |
-
if ( $this->settings['enable_ban'] && $this->is_ip_banned() ) {
|
236 |
-
$itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
|
237 |
-
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
238 |
-
}
|
239 |
-
}
|
240 |
-
|
241 |
-
/**
|
242 |
-
* Sends to lockout class when username and password are filled out and wrong
|
243 |
-
*
|
244 |
-
* @since 4.5
|
245 |
-
*
|
246 |
-
* @return void
|
247 |
-
*/
|
248 |
-
public function handle_failed_login( $username, $details ) {
|
249 |
-
|
250 |
-
/** @var ITSEC_Lockout $itsec_lockout */
|
251 |
-
global $itsec_logger, $itsec_lockout;
|
252 |
-
|
253 |
-
$this->load_settings();
|
254 |
-
|
255 |
-
if ( $this->settings['enable_ban'] && $this->report_ip() ) {
|
256 |
-
$itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
|
257 |
-
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
258 |
-
}
|
259 |
-
}
|
260 |
-
|
261 |
}
|
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() {
|
24 |
}
|
25 |
}
|
26 |
|
27 |
+
public function filter_authenticate( $user, $username, $password ) {
|
28 |
+
global $itsec_lockout;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
if ( is_wp_error( $user ) && $user->get_error_codes() == array( 'empty_username', 'empty_password' ) ) {
|
31 |
+
// This is not an authentication attempt. It is simply the login page loading.
|
32 |
+
return $user;
|
33 |
}
|
34 |
|
35 |
+
$this->load_settings();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
+
if ( is_wp_error( $user ) || null == $user ) {
|
38 |
+
if ( $this->report_ip() && $this->settings['enable_ban'] ) {
|
39 |
+
ITSEC_Log::add_notice( 'ipcheck', 'failed-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
40 |
+
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
41 |
+
}
|
42 |
+
} else if ( $this->settings['enable_ban'] && $this->is_ip_banned() ) {
|
43 |
+
ITSEC_Log::add_critical_issue( 'ipcheck', 'successful-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
44 |
+
$itsec_lockout->execute_lock( array( 'network_lock' => true ) );
|
45 |
}
|
46 |
|
47 |
+
return $user;
|
48 |
}
|
49 |
|
50 |
/**
|
54 |
*
|
55 |
* @return bool true if banned, false otherwise.
|
56 |
*/
|
57 |
+
private function is_ip_banned() {
|
58 |
+
return $this->get_server_response( 'check-ip' );
|
59 |
}
|
60 |
|
61 |
/**
|
65 |
*
|
66 |
* @return bool true if banned, false otherwise.
|
67 |
*/
|
68 |
+
private function report_ip() {
|
69 |
+
return $this->get_server_response( 'report-ip' );
|
70 |
}
|
71 |
|
72 |
+
private function get_server_response( $action ) {
|
|
|
|
|
|
|
73 |
$this->load_settings();
|
74 |
|
75 |
if ( empty( $this->settings['api_key'] ) || empty( $this->settings['api_secret'] ) ) {
|
77 |
}
|
78 |
|
79 |
|
80 |
+
$ip = ITSEC_Lib::get_ip();
|
|
|
|
|
81 |
|
82 |
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php' );
|
83 |
|
142 |
'type' => 'host',
|
143 |
);
|
144 |
|
145 |
+
ITSEC_Log::add_action( 'ipcheck', 'ip-blocked', $data );
|
146 |
|
147 |
return true;
|
148 |
}
|
150 |
return false;
|
151 |
}
|
152 |
|
153 |
+
private function set_cache( $ip, $response ) {
|
154 |
+
$cache = $this->get_cache( $ip );
|
155 |
+
$time = ITSEC_Core::get_current_time_gmt();
|
156 |
+
|
157 |
+
if ( isset( $response['block'] ) ) {
|
158 |
+
$cache['block'] = (boolean) $response['block'];
|
159 |
+
}
|
160 |
+
|
161 |
+
if ( isset( $response['cache_ttl'] ) ) {
|
162 |
+
$cache['cache_ttl'] = intval( $response['cache_ttl'] ) + $time;
|
163 |
+
} else if ( 0 === $cache['cache_ttl'] ) {
|
164 |
+
$cache['cache_ttl'] = $time + HOUR_IN_SECONDS;
|
165 |
+
}
|
166 |
+
|
167 |
+
if ( isset( $response['report_ttl'] ) ) {
|
168 |
+
$cache['report_ttl'] = intval( $response['report_ttl'] ) + $time;
|
169 |
+
}
|
170 |
+
|
171 |
+
$transient_time = max( $cache['cache_ttl'], $cache['report_ttl'] ) - $time;
|
172 |
+
|
173 |
+
|
174 |
+
set_site_transient( "itsec_ipcheck_$ip", $cache, $transient_time );
|
175 |
+
}
|
176 |
+
|
177 |
+
private function get_cache( $ip ) {
|
178 |
+
$cache = get_site_transient( "itsec_ipcheck_$ip" );
|
179 |
+
|
180 |
+
$defaults = array(
|
181 |
+
'block' => false,
|
182 |
+
'cache_ttl' => 0,
|
183 |
+
'report_ttl' => 0,
|
184 |
+
);
|
185 |
+
|
186 |
+
if ( ! is_array( $cache ) ) {
|
187 |
+
return $defaults;
|
188 |
+
}
|
189 |
+
|
190 |
+
return array_merge( $defaults, $cache );
|
191 |
+
}
|
192 |
+
|
193 |
/**
|
194 |
* Calculates the HMAC of a string using SHA1.
|
195 |
*
|
215 |
return base64_encode( $hmac );
|
216 |
}
|
217 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
218 |
}
|
core/modules/ipcheck/js/settings-page.js
CHANGED
@@ -20,7 +20,7 @@ jQuery( document ).ready(function ( $ ) {
|
|
20 |
|
21 |
$( '#itsec-network-brute-force-reset-status' ).html( '' );
|
22 |
|
23 |
-
|
24 |
$( '#itsec-network-brute-force-reset-status' ).html( '' );
|
25 |
|
26 |
if ( true !== results.response ) {
|
20 |
|
21 |
$( '#itsec-network-brute-force-reset-status' ).html( '' );
|
22 |
|
23 |
+
itsecUtil.sendModuleAJAXRequest( 'network-brute-force', data, function( results ) {
|
24 |
$( '#itsec-network-brute-force-reset-status' ).html( '' );
|
25 |
|
26 |
if ( true !== results.response ) {
|
core/modules/ipcheck/logs.php
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_IPCheck_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_ipcheck_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
add_filter( 'itsec_logs_prepare_ipcheck_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
}
|
8 |
+
|
9 |
+
public function filter_entry_for_list_display( $entry ) {
|
10 |
+
$entry['module_display'] = esc_html__( 'Network Brute Force', 'better-wp-security' );
|
11 |
+
|
12 |
+
if ( 'ip-blocked' === $entry['code'] ) {
|
13 |
+
$entry['description'] = esc_html__( 'IP Blocked', 'better-wp-security' );
|
14 |
+
} else if ( 'successful-login-by-blocked-ip' === $entry['code'] ) {
|
15 |
+
$entry['description'] = esc_html__( 'Blocked Host Attempted Login With Good Credentials', 'better-wp-security' );
|
16 |
+
} else if ( 'failed-login-by-blocked-ip' === $entry['code'] ) {
|
17 |
+
$entry['description'] = esc_html__( 'Blocked Host Attempted Login', 'better-wp-security' );
|
18 |
+
}
|
19 |
+
|
20 |
+
return $entry;
|
21 |
+
}
|
22 |
+
|
23 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
24 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
25 |
+
|
26 |
+
$details['module']['content'] = $entry['module_display'];
|
27 |
+
$details['description']['content'] = $entry['description'];
|
28 |
+
|
29 |
+
if ( isset( $entry['data']['expires_gmt'] ) ) {
|
30 |
+
$timestamp = strtotime( $entry['data']['expires_gmt'] );
|
31 |
+
$datetime = date( 'Y-m-d H:i:s', $timestamp + ITSEC_Core::get_time_offset() );
|
32 |
+
|
33 |
+
$details['expiration'] = array(
|
34 |
+
'header' => esc_html__( 'Block Expiration', 'better-wp-security' ),
|
35 |
+
'content' => $datetime,
|
36 |
+
);
|
37 |
+
}
|
38 |
+
|
39 |
+
if ( isset( $entry['data']['details'] ) && isset( $entry['data']['details']['source'] ) ) {
|
40 |
+
if ( 'xmlrpc' === $entry['data']['details']['source'] ) {
|
41 |
+
$source = esc_html__( 'XMLRPC Authentication', 'better-wp-security' );
|
42 |
+
} else if ( 'rest_api' === $entry['data']['details']['source'] ) {
|
43 |
+
$source = esc_html__( 'REST API Authentication', 'better-wp-security' );
|
44 |
+
} else {
|
45 |
+
$source = esc_html__( 'Login Page', 'better-wp-security' );
|
46 |
+
}
|
47 |
+
|
48 |
+
$details['source'] = array(
|
49 |
+
'header' => esc_html__( 'Login Source' ),
|
50 |
+
'content' => $source,
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
return $details;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
new ITSEC_IPCheck_Logs();
|
core/modules/malware/class-itsec-malware-log.php
DELETED
@@ -1,340 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Log malware scan reports
|
5 |
-
*
|
6 |
-
* @package iThemes-Security
|
7 |
-
* @subpackage Intrusion-Detection
|
8 |
-
* @since 4.4
|
9 |
-
*/
|
10 |
-
final class ITSEC_Malware_Log extends ITSEC_WP_List_Table {
|
11 |
-
|
12 |
-
function __construct() {
|
13 |
-
|
14 |
-
parent::__construct(
|
15 |
-
array(
|
16 |
-
'singular' => 'itsec_malware_log_item',
|
17 |
-
'plural' => 'itsec_malware_log_items',
|
18 |
-
'ajax' => true
|
19 |
-
)
|
20 |
-
);
|
21 |
-
|
22 |
-
}
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Define first time column
|
26 |
-
*
|
27 |
-
* @param array $item array of row data
|
28 |
-
*
|
29 |
-
* @return string formatted output
|
30 |
-
*
|
31 |
-
**/
|
32 |
-
function column_details( $item ) {
|
33 |
-
|
34 |
-
$content = '';
|
35 |
-
|
36 |
-
if ( ! empty( $item['results'] ) && in_array( $item['results'], array( 'clean', 'warn', 'error' ) ) ) {
|
37 |
-
// Results for Sucuri scans.
|
38 |
-
|
39 |
-
require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
|
40 |
-
|
41 |
-
$content .= '<a href="itsec-log-malware-row-' . $item['count'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
|
42 |
-
$content .= '<div id="itsec-log-malware-row-' . $item['count'] . '" style="display:none;">';
|
43 |
-
$content .= "<div class='itsec-malware-scan-results-wrapper'>\n";
|
44 |
-
|
45 |
-
$content .= ITSEC_Malware_Scan_Results_Template::get_html( $item['data'], true );
|
46 |
-
|
47 |
-
$content .= "</div>\n";
|
48 |
-
$content .= "</div>\n";
|
49 |
-
} else if ( isset( $item['report'] ) ) {
|
50 |
-
// Results for legacy VirusTotal scans.
|
51 |
-
|
52 |
-
$content .= '<a href="itsec-log-malware-row-' . $item['count'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
|
53 |
-
|
54 |
-
$content .= '<div id="itsec-log-malware-row-' . $item['count'] . '" style="display:none;">';
|
55 |
-
|
56 |
-
if ( isset( $item['report']['resource'] ) ) {
|
57 |
-
$content .= '<strong>' . __( 'Resource Scanned', 'better-wp-security' ) . ':</strong> ' . $item['report']['resource'] . '<br />';
|
58 |
-
}
|
59 |
-
|
60 |
-
if ( isset( $item['report']['results'] ) ) {
|
61 |
-
|
62 |
-
if ( isset( $item['report']['results']['total'] ) ) {
|
63 |
-
$content .= '<strong>' . __( 'Total Scans', 'better-wp-security' ) . ':</strong> ' . $item['report']['results']['total'] . '<br />';
|
64 |
-
}
|
65 |
-
|
66 |
-
if ( isset( $item['report']['results']['positives'] ) ) {
|
67 |
-
$content .= '<strong>' . __( 'Problems Found', 'better-wp-security' ) . ':</strong> ' . $item['report']['results']['positives'] . '<br />';
|
68 |
-
}
|
69 |
-
|
70 |
-
if ( isset( $item['report']['results']['scans'] ) ) {
|
71 |
-
|
72 |
-
$content .= '<h3>' . __( 'Scan Details', 'better-wp-security' ) . '</h3>';
|
73 |
-
|
74 |
-
$content .= '<ol class="malware_detail_list">';
|
75 |
-
|
76 |
-
foreach ( $item['report']['results']['scans'] as $service => $results ) {
|
77 |
-
$content .= '<li class="malware_detail"><strong>' . $service . '</strong>: ' . $results['result'] . ' ' . $results['detected'] . '<br /></li>';
|
78 |
-
}
|
79 |
-
|
80 |
-
$content .= '</ol>';
|
81 |
-
|
82 |
-
}
|
83 |
-
|
84 |
-
}
|
85 |
-
|
86 |
-
$content .= '</div>';
|
87 |
-
|
88 |
-
}
|
89 |
-
|
90 |
-
return $content;
|
91 |
-
|
92 |
-
}
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Define results column
|
96 |
-
*
|
97 |
-
* @param array $item array of row data
|
98 |
-
*
|
99 |
-
* @return string formatted output
|
100 |
-
*
|
101 |
-
**/
|
102 |
-
function column_results( $item ) {
|
103 |
-
|
104 |
-
$content = '';
|
105 |
-
|
106 |
-
if ( ! empty( $item['results'] ) && in_array( $item['results'], array( 'clean', 'warn', 'error' ) ) ) {
|
107 |
-
// Results for Sucuri scans.
|
108 |
-
if ( 'error' === $item['results'] ) {
|
109 |
-
$content = '<span style="color: red">' . __( 'Error', 'better-wp-security' ) . '</span>';
|
110 |
-
} else if ( 'warn' === $item['results'] ) {
|
111 |
-
$content = '<span style="color: red">' . __( 'Warning', 'better-wp-security' ) . '</span>';
|
112 |
-
} else {
|
113 |
-
$content = '<span style="color: green">' . __( 'Clean', 'better-wp-security' ) . '</span>';
|
114 |
-
}
|
115 |
-
} else if ( isset( $item['report']['results']['positives'] ) ) {
|
116 |
-
// Results for legacy VirusTotal scans.
|
117 |
-
|
118 |
-
if ( $item['report']['results']['positives'] == 0 ) {
|
119 |
-
$content = '<span style="color: green">' . __( 'Clean', 'better-wp-security' ) . '</span>';
|
120 |
-
} else {
|
121 |
-
$content = '<span style="color: red">' . __( 'Issues Detected', 'better-wp-security' ) . '</span>';
|
122 |
-
}
|
123 |
-
|
124 |
-
}
|
125 |
-
|
126 |
-
return $content;
|
127 |
-
|
128 |
-
}
|
129 |
-
|
130 |
-
/**
|
131 |
-
* Define time column
|
132 |
-
*
|
133 |
-
* @param array $item array of row data
|
134 |
-
*
|
135 |
-
* @return string formatted output
|
136 |
-
*
|
137 |
-
**/
|
138 |
-
function column_time( $item ) {
|
139 |
-
|
140 |
-
return $item['time'];
|
141 |
-
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Define count column
|
146 |
-
*
|
147 |
-
* @param array $item array of row data
|
148 |
-
*
|
149 |
-
* @return string formatted output
|
150 |
-
*
|
151 |
-
**/
|
152 |
-
function column_user( $item ) {
|
153 |
-
|
154 |
-
$user = get_user_by( 'login', $item['user'] );
|
155 |
-
|
156 |
-
if ( $user !== false ) {
|
157 |
-
|
158 |
-
return '<a href="/wp-admin/user-edit.php?user_id=' . $user->ID . '">' . $item['user'] . '</a>';
|
159 |
-
|
160 |
-
} else {
|
161 |
-
|
162 |
-
return $item['user'];
|
163 |
-
|
164 |
-
}
|
165 |
-
|
166 |
-
}
|
167 |
-
|
168 |
-
/**
|
169 |
-
* Define uri column
|
170 |
-
*
|
171 |
-
* @param array $item array of row data
|
172 |
-
*
|
173 |
-
* @return string formatted output
|
174 |
-
*
|
175 |
-
**/
|
176 |
-
function column_action( $item ) {
|
177 |
-
|
178 |
-
return $item['action'];
|
179 |
-
|
180 |
-
}
|
181 |
-
|
182 |
-
/**
|
183 |
-
* Define Columns
|
184 |
-
*
|
185 |
-
* @return array array of column titles
|
186 |
-
*/
|
187 |
-
public function get_columns() {
|
188 |
-
|
189 |
-
return array(
|
190 |
-
'time' => __( 'Time', 'better-wp-security' ),
|
191 |
-
'action' => __( 'Action', 'better-wp-security' ),
|
192 |
-
'results' => __( 'Results', 'better-wp-security' ),
|
193 |
-
'user' => __( 'User', 'better-wp-security' ),
|
194 |
-
'details' => __( 'Details', 'better-wp-security' ),
|
195 |
-
);
|
196 |
-
|
197 |
-
}
|
198 |
-
|
199 |
-
/**
|
200 |
-
* Prepare data for table
|
201 |
-
*
|
202 |
-
* @return void
|
203 |
-
*/
|
204 |
-
public function prepare_items() {
|
205 |
-
global $itsec_logger, $wpdb;
|
206 |
-
|
207 |
-
$columns = $this->get_columns();
|
208 |
-
$hidden = array();
|
209 |
-
$this->_column_headers = array( $columns, $hidden, false );
|
210 |
-
$per_page = 20;
|
211 |
-
$current_page = $this->get_pagenum();
|
212 |
-
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log` WHERE `log_type`='malware'" );
|
213 |
-
|
214 |
-
$items = $itsec_logger->get_events( 'malware', array(), $per_page, ( ( $current_page - 1 ) * $per_page ), 'log_date' );
|
215 |
-
|
216 |
-
$table_data = array();
|
217 |
-
|
218 |
-
$count = 0;
|
219 |
-
|
220 |
-
foreach ( $items as $item ) { //loop through and group 404s
|
221 |
-
|
222 |
-
$log_data = maybe_unserialize( $item['log_data'] );
|
223 |
-
|
224 |
-
$table_data[$count]['time'] = sanitize_text_field( $item['log_date'] );
|
225 |
-
$table_data[$count]['host'] = sanitize_text_field( $item['log_host'] );
|
226 |
-
|
227 |
-
if ( strlen( trim( sanitize_text_field( $item['log_username'] ) ) ) > 0 ) {
|
228 |
-
|
229 |
-
$table_data[$count]['user'] = sanitize_text_field( $item['log_username'] );
|
230 |
-
|
231 |
-
} elseif ( intval( $item['log_user'] ) > 0 && ITSEC_Lib::user_id_exists( $item['log_user'] ) ) {
|
232 |
-
|
233 |
-
$user = get_user_by( 'id', $item['log_user'] );
|
234 |
-
|
235 |
-
$table_data[$count]['user'] = $user->data->user_login;
|
236 |
-
|
237 |
-
} else {
|
238 |
-
|
239 |
-
$table_data[$count]['user'] = '';
|
240 |
-
|
241 |
-
}
|
242 |
-
|
243 |
-
$table_data[$count]['action'] = ( is_array( $log_data ) && isset( $log_data['type'] ) ) ? sanitize_text_field( $log_data['type'] ) : __( 'Malware Scan Report', 'better-wp-security' );
|
244 |
-
|
245 |
-
if ( is_wp_error( $log_data ) ) {
|
246 |
-
$table_data[$count]['results'] = 'error';
|
247 |
-
$table_data[$count]['data'] = $log_data;
|
248 |
-
} else if ( isset( $log_data['SCAN']['SITE'] ) ) {
|
249 |
-
// New log data from Sucuri scan.
|
250 |
-
|
251 |
-
if (
|
252 |
-
empty( $log_data['SYSTEM']['WARN'] ) &&
|
253 |
-
empty( $log_data['MALWARE']['WARN'] ) &&
|
254 |
-
empty( $log_data['BLACKLIST']['WARN'] )
|
255 |
-
) {
|
256 |
-
$table_data[$count]['results'] = 'clean';
|
257 |
-
} else {
|
258 |
-
$table_data[$count]['results'] = 'warn';
|
259 |
-
}
|
260 |
-
|
261 |
-
$table_data[$count]['data'] = $log_data;
|
262 |
-
} else {
|
263 |
-
// Legacy log data from VirusTotal scan.
|
264 |
-
|
265 |
-
if ( isset( $log_data['resource'] ) ) {
|
266 |
-
|
267 |
-
$table_data[$count]['report'] = array(
|
268 |
-
'resource' => $log_data['resource'],
|
269 |
-
);
|
270 |
-
|
271 |
-
} else {
|
272 |
-
|
273 |
-
$table_data[$count]['report'] = array();
|
274 |
-
|
275 |
-
}
|
276 |
-
|
277 |
-
if ( isset( $log_data['report'] ) ) {
|
278 |
-
$table_data[$count]['report']['results'] = $log_data['report'];
|
279 |
-
}
|
280 |
-
|
281 |
-
}
|
282 |
-
|
283 |
-
$table_data[$count]['count'] = $count;
|
284 |
-
|
285 |
-
$count ++;
|
286 |
-
|
287 |
-
}
|
288 |
-
|
289 |
-
// usort( $table_data, array( $this, 'sortrows' ) );
|
290 |
-
|
291 |
-
$this->items = $table_data;
|
292 |
-
|
293 |
-
$this->set_pagination_args(
|
294 |
-
array(
|
295 |
-
'total_items' => $total_items,
|
296 |
-
'per_page' => $per_page,
|
297 |
-
'total_pages' => ceil( $total_items / $per_page )
|
298 |
-
)
|
299 |
-
);
|
300 |
-
}
|
301 |
-
|
302 |
-
/**
|
303 |
-
* Sorts rows by count in descending order
|
304 |
-
*
|
305 |
-
* @param array $a first array to compare
|
306 |
-
* @param array $b second array to compare
|
307 |
-
*
|
308 |
-
* @return int comparison result
|
309 |
-
*/
|
310 |
-
function sortrows( $a, $b ) {
|
311 |
-
|
312 |
-
// If no sort, default to count
|
313 |
-
$orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'time';
|
314 |
-
|
315 |
-
// If no order, default to desc
|
316 |
-
$order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
|
317 |
-
|
318 |
-
if ( $orderby == 'count' ) {
|
319 |
-
|
320 |
-
if ( intval( $a[$orderby] ) < intval( $b[$orderby] ) ) {
|
321 |
-
$result = - 1;
|
322 |
-
} elseif ( intval( $a[$orderby] ) === intval( $b[$orderby] ) ) {
|
323 |
-
$result = 0;
|
324 |
-
} else {
|
325 |
-
$result = 1;
|
326 |
-
}
|
327 |
-
|
328 |
-
} else {
|
329 |
-
|
330 |
-
// Determine sort order
|
331 |
-
$result = strcmp( $a[$orderby], $b[$orderby] );
|
332 |
-
|
333 |
-
}
|
334 |
-
|
335 |
-
// Send final sort direction to usort
|
336 |
-
return ( $order === 'asc' ) ? $result : - $result;
|
337 |
-
|
338 |
-
}
|
339 |
-
|
340 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/modules/malware/class-itsec-malware-scan-results-template.php
CHANGED
@@ -2,8 +2,21 @@
|
|
2 |
|
3 |
class ITSEC_Malware_Scan_Results_Template {
|
4 |
public static function get_html( $results, $show_error_details = false ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
$html = "<div class='itsec-malware-scan-results'>\n";
|
6 |
|
|
|
|
|
|
|
|
|
7 |
if ( is_wp_error( $results ) ) {
|
8 |
$html .= self::get_wp_error_details( $results, $show_error_details );
|
9 |
} else {
|
2 |
|
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'] ) ) {
|
9 |
+
$site_url = $results['itsec_site']['url'];
|
10 |
+
} else {
|
11 |
+
$site_url = '';
|
12 |
+
}
|
13 |
+
|
14 |
$html = "<div class='itsec-malware-scan-results'>\n";
|
15 |
|
16 |
+
if ( $site_url ) {
|
17 |
+
$html .= '<h4>' . sprintf( esc_html__( 'Site: %s', 'better-wp-security' ), $site_url ) . '</h4>';
|
18 |
+
}
|
19 |
+
|
20 |
if ( is_wp_error( $results ) ) {
|
21 |
$html .= self::get_wp_error_details( $results, $show_error_details );
|
22 |
} else {
|
core/modules/malware/class-itsec-malware-scanner.php
CHANGED
@@ -3,21 +3,40 @@
|
|
3 |
final class ITSEC_Malware_Scanner {
|
4 |
protected static $transient_name = 'itsec_cached_sucuri_scan';
|
5 |
|
6 |
-
public static function scan() {
|
7 |
-
|
8 |
-
/** @var ITSEC_Logger $itsec_logger */
|
9 |
-
global $itsec_logger;
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
$results
|
13 |
|
14 |
-
if ( is_array( $results ) &&
|
15 |
return $results;
|
16 |
}
|
17 |
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
return $results;
|
23 |
}
|
@@ -36,6 +55,10 @@ final class ITSEC_Malware_Scanner {
|
|
36 |
|
37 |
$code = $results->get_error_code();
|
38 |
|
|
|
|
|
|
|
|
|
39 |
// Networking error probably due to a server issue.
|
40 |
if ( strpos( $code, 'itsec' ) === false ) {
|
41 |
return false;
|
@@ -55,74 +78,165 @@ final class ITSEC_Malware_Scanner {
|
|
55 |
return true;
|
56 |
}
|
57 |
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
$response = get_site_transient( self::$transient_name );
|
61 |
$cached = true;
|
62 |
|
|
|
|
|
63 |
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE' ) && ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE ) {
|
|
|
64 |
$cached = false;
|
65 |
$response = false;
|
66 |
}
|
67 |
|
68 |
-
|
69 |
if ( false === $response ) {
|
70 |
$cached = false;
|
71 |
|
72 |
-
$scanner_url = 'https://sitecheck.sucuri.net/';
|
73 |
$site_url = apply_filters( 'itsec_test_malware_scan_site_url', get_site_url() );
|
74 |
|
75 |
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SITE_URL' ) ) {
|
|
|
76 |
$site_url = ITSEC_TEST_MALWARE_SCAN_SITE_URL;
|
77 |
}
|
78 |
|
79 |
-
$
|
80 |
-
|
81 |
-
$query_args = array(
|
82 |
-
'scan' => $site_url,
|
83 |
-
'p' => 'ithemes',
|
84 |
-
'clear' => 1,
|
85 |
-
'json' => 1,
|
86 |
-
'time' => time(),
|
87 |
-
);
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
if ( ! empty( $key ) ) {
|
96 |
-
$query_args['k'] = $key;
|
97 |
}
|
|
|
|
|
|
|
98 |
|
99 |
-
|
100 |
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
);
|
105 |
|
106 |
-
|
107 |
-
|
108 |
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
-
|
|
|
|
|
|
|
|
|
115 |
|
116 |
-
|
117 |
-
|
118 |
-
remove_filter( 'https_ssl_verify', '__return_false', 999999 );
|
119 |
-
}
|
120 |
|
121 |
if ( is_wp_error( $response ) ) {
|
122 |
return $response;
|
123 |
}
|
|
|
|
|
|
|
|
|
124 |
}
|
125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
if ( isset( $response['body'] ) ) {
|
128 |
$body = $response['body'];
|
@@ -136,14 +250,14 @@ final class ITSEC_Malware_Scanner {
|
|
136 |
|
137 |
$body = @json_decode( $body, true );
|
138 |
|
139 |
-
if ( is_null( $body ) && isset( $response['headers']
|
140 |
if ( 'application/json' === $response['headers']['content-type'] ) {
|
141 |
return new WP_Error( 'itsec-malware-scanner-invalid-json-data-in-scan-response', __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The response indicates that the encoding is JSON, but the data could not be decoded. This problem could be due to a temporary Sucuri server issue or a compatibility issue on your server. If the problem continues, please contact iThemes Security support.', 'better-wp-security' ), $response );
|
142 |
} else {
|
143 |
return new WP_Error( 'itsec-malware-scanner-invalid-content-type-in-scan-response', sprintf( __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The data received from the Sucuri server could not be decoded. In addition, a content type of <code>%s</code> was received when a content type of <code>application/json</code> was expected. This could indicate a temporary issue with the Sucuri servers.', 'better-wp-security' ), esc_html( $response['headers']['content-type'] ) ), $response );
|
144 |
}
|
145 |
} else if ( ! is_array( $body ) ) {
|
146 |
-
if (
|
147 |
return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'better-wp-security' ), '<code>' . $response['body'] . '</code>' ), $response );
|
148 |
}
|
149 |
|
@@ -154,15 +268,6 @@ final class ITSEC_Malware_Scanner {
|
|
154 |
return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-malformed', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function is missing some critical information that is needed in order to properly process the response from Sucuri\'s servers. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'better-wp-security' ), $response );
|
155 |
}
|
156 |
|
157 |
-
|
158 |
-
if ( ! $cached ) {
|
159 |
-
set_site_transient( self::$transient_name, $response, 10 * MINUTE_IN_SECONDS );
|
160 |
-
}
|
161 |
-
|
162 |
-
if ( is_array( $body ) ) {
|
163 |
-
$body['cached'] = $cached;
|
164 |
-
}
|
165 |
-
|
166 |
return $body;
|
167 |
}
|
168 |
}
|
3 |
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 ) ) {
|
10 |
+
$results = self::scan_sub_site( $site_id, $process_id );
|
11 |
+
} else {
|
12 |
+
$results = self::scan_main_site( $process_id );
|
13 |
+
}
|
14 |
|
15 |
+
ITSEC_Log::add_process_stop( $process_id, compact( 'results' ) );
|
16 |
|
17 |
+
if ( is_array( $results ) && ! empty( $results['cached'] ) ) {
|
18 |
return $results;
|
19 |
}
|
20 |
|
21 |
+
if ( is_wp_error( $results ) ) {
|
22 |
+
if ( self::is_sucuri_error( $results ) ) {
|
23 |
+
ITSEC_Log::add_warning( 'malware', 'scan-failure-server-error', compact( 'results' ) );
|
24 |
+
} else {
|
25 |
+
ITSEC_Log::add_warning( 'malware', 'scan-failure-client-error', compact( 'results' ) );
|
26 |
+
}
|
27 |
+
} else if ( ! empty( $results['SYSTEM']['ERROR'] ) ) {
|
28 |
+
ITSEC_Log::add_warning( 'malware', 'sucuri-system-error', compact( 'results' ) );
|
29 |
+
} else if ( ! empty( $results['MALWARE']['WARN'] ) ) {
|
30 |
+
if ( ! empty( $results['BLACKLIST']['WARN'] ) ) {
|
31 |
+
ITSEC_Log::add_critical_issue( 'malware', 'found-malware-and-on-blacklist', compact( 'results' ) );
|
32 |
+
} else {
|
33 |
+
ITSEC_Log::add_critical_issue( 'malware', 'found-malware', compact( 'results' ) );
|
34 |
+
}
|
35 |
+
} else if ( ! empty( $results['BLACKLIST']['WARN'] ) ) {
|
36 |
+
ITSEC_Log::add_critical_issue( 'malware', 'on-blacklist', compact( 'results' ) );
|
37 |
+
} else {
|
38 |
+
ITSEC_Log::add_notice( 'malware', 'clean', compact( 'results' ) );
|
39 |
+
}
|
40 |
|
41 |
return $results;
|
42 |
}
|
55 |
|
56 |
$code = $results->get_error_code();
|
57 |
|
58 |
+
if ( 'http_request_failed' === $code && strpos( $results->get_error_message(), 'cURL error 52:' ) !== false ) {
|
59 |
+
return true;
|
60 |
+
}
|
61 |
+
|
62 |
// Networking error probably due to a server issue.
|
63 |
if ( strpos( $code, 'itsec' ) === false ) {
|
64 |
return false;
|
78 |
return true;
|
79 |
}
|
80 |
|
81 |
+
/**
|
82 |
+
* Scan the main site of the network.
|
83 |
+
*
|
84 |
+
* This handles caching and filter overrides for URLs.
|
85 |
+
*
|
86 |
+
* @param array $process_id
|
87 |
+
*
|
88 |
+
* @return array|WP_Error
|
89 |
+
*/
|
90 |
+
protected static function scan_main_site( $process_id ) {
|
91 |
|
92 |
$response = get_site_transient( self::$transient_name );
|
93 |
$cached = true;
|
94 |
|
95 |
+
ITSEC_Log::add_process_update( $process_id, array( 'cached-response' => $response ) );
|
96 |
+
|
97 |
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE' ) && ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE ) {
|
98 |
+
ITSEC_Log::add_process_update( $process_id, array( 'action' => 'define-skip-cache', 'define-name' => 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE', 'define-value' => ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE ) );
|
99 |
$cached = false;
|
100 |
$response = false;
|
101 |
}
|
102 |
|
|
|
103 |
if ( false === $response ) {
|
104 |
$cached = false;
|
105 |
|
|
|
106 |
$site_url = apply_filters( 'itsec_test_malware_scan_site_url', get_site_url() );
|
107 |
|
108 |
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SITE_URL' ) ) {
|
109 |
+
ITSEC_Log::add_process_update( $process_id, array( 'action' => 'define-force-site-url', 'original-site-url' => $site_url, 'define-name' => 'ITSEC_TEST_MALWARE_SCAN_SITE_URL', 'define-value' => ITSEC_TEST_MALWARE_SCAN_SITE_URL ) );
|
110 |
$site_url = ITSEC_TEST_MALWARE_SCAN_SITE_URL;
|
111 |
}
|
112 |
|
113 |
+
$response = self::scan_url( $site_url, $process_id );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
+
if ( ! is_wp_error( $response ) ) {
|
116 |
+
set_site_transient( self::$transient_name, array(
|
117 |
+
'body' => $response['body'],
|
118 |
+
'headers' => $response['headers'],
|
119 |
+
'response' => $response['response'],
|
120 |
+
), MINUTE_IN_SECONDS * 10 );
|
|
|
|
|
121 |
}
|
122 |
+
} else {
|
123 |
+
ITSEC_Log::add_process_update( $process_id, array( 'action' => 'using-cached-response' ) );
|
124 |
+
}
|
125 |
|
126 |
+
$results = self::parse_response( $response );
|
127 |
|
128 |
+
if ( ! is_wp_error( $results ) ) {
|
129 |
+
$results['cached'] = $cached;
|
130 |
+
}
|
|
|
131 |
|
132 |
+
return $results;
|
133 |
+
}
|
134 |
|
135 |
+
/**
|
136 |
+
* Scan a sub site.
|
137 |
+
*
|
138 |
+
* These requests are not cached.
|
139 |
+
*
|
140 |
+
* @param int $site_id
|
141 |
+
* @param array $process_id
|
142 |
+
*
|
143 |
+
* @return array|WP_Error
|
144 |
+
*/
|
145 |
+
protected static function scan_sub_site( $site_id, $process_id ) {
|
146 |
|
147 |
+
$url = get_site_url( $site_id );
|
148 |
+
$record = array(
|
149 |
+
'url' => $url,
|
150 |
+
'id' => $site_id,
|
151 |
+
);
|
152 |
|
153 |
+
if ( $url ) {
|
154 |
+
$response = self::scan_url( $url, $process_id );
|
|
|
|
|
155 |
|
156 |
if ( is_wp_error( $response ) ) {
|
157 |
return $response;
|
158 |
}
|
159 |
+
|
160 |
+
$results = self::parse_response( $response );
|
161 |
+
} else {
|
162 |
+
$results = new WP_Error( 'itsec-malware-scanner-invalid-site', sprintf( __( 'Failed to scan site #%d because it does not exist.', 'better-wp-security' ), $site_id ) );
|
163 |
}
|
164 |
|
165 |
+
if ( is_wp_error( $results ) ) {
|
166 |
+
$results->add_data( array_merge( $results->get_error_data(), array( 'itsec_site' => $record ) ) );
|
167 |
+
} else {
|
168 |
+
$results['itsec_site'] = $record;
|
169 |
+
}
|
170 |
+
|
171 |
+
return $results;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Request a Sucuri Malware Scan for a URL.
|
176 |
+
*
|
177 |
+
* @param string $site_url
|
178 |
+
* @param array $process_id
|
179 |
+
*
|
180 |
+
* @return array|WP_Error
|
181 |
+
*/
|
182 |
+
protected static function scan_url( $site_url, $process_id ) {
|
183 |
+
|
184 |
+
$site_url = preg_replace( '|^https?://|i', '', $site_url );
|
185 |
+
|
186 |
+
$query_args = array(
|
187 |
+
'scan' => $site_url,
|
188 |
+
'p' => 'ithemes',
|
189 |
+
'clear' => 1,
|
190 |
+
'json' => 1,
|
191 |
+
'time' => time(),
|
192 |
+
);
|
193 |
+
|
194 |
+
$key = apply_filters( 'itsec_sucuri_key', '' );
|
195 |
+
|
196 |
+
if ( defined( 'ITSEC_SUCURI_KEY' ) ) {
|
197 |
+
$key = ITSEC_SUCURI_KEY;
|
198 |
+
}
|
199 |
+
|
200 |
+
if ( ! empty( $key ) ) {
|
201 |
+
$query_args['k'] = $key;
|
202 |
+
}
|
203 |
+
|
204 |
+
$scanner_url = 'https://sitecheck.sucuri.net/';
|
205 |
+
$scan_url = "$scanner_url?" . http_build_query( $query_args, '', '&' );
|
206 |
+
|
207 |
+
$req_args = array(
|
208 |
+
'connect_timeout' => 30, // Placeholder for when WordPress implements support.
|
209 |
+
'timeout' => 300,
|
210 |
+
);
|
211 |
+
|
212 |
+
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY' ) && ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY ) {
|
213 |
+
$req_args['sslverify'] = false;
|
214 |
+
|
215 |
+
// Ensure that another plugin isn't preventing the disabling of sslverify from working.
|
216 |
+
add_filter( 'https_local_ssl_verify', '__return_false', 999999 );
|
217 |
+
add_filter( 'https_ssl_verify', '__return_false', 999999 );
|
218 |
+
}
|
219 |
+
|
220 |
+
$response = wp_remote_get( $scan_url, $req_args );
|
221 |
+
|
222 |
+
if ( defined( 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY' ) && ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY ) {
|
223 |
+
remove_filter( 'https_local_ssl_verify', '__return_false', 999999 );
|
224 |
+
remove_filter( 'https_ssl_verify', '__return_false', 999999 );
|
225 |
+
}
|
226 |
+
|
227 |
+
ITSEC_Log::add_process_update( $process_id, compact( 'scan_url', 'req_args', 'response' ) );
|
228 |
+
|
229 |
+
return $response;
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Parse the response from Sucuri to get either a WP_Error instance or the scan results.
|
234 |
+
*
|
235 |
+
* @param array $response
|
236 |
+
*
|
237 |
+
* @return array|WP_Error
|
238 |
+
*/
|
239 |
+
protected static function parse_response( $response ) {
|
240 |
|
241 |
if ( isset( $response['body'] ) ) {
|
242 |
$body = $response['body'];
|
250 |
|
251 |
$body = @json_decode( $body, true );
|
252 |
|
253 |
+
if ( is_null( $body ) && isset( $response['headers']['content-type'] ) ) {
|
254 |
if ( 'application/json' === $response['headers']['content-type'] ) {
|
255 |
return new WP_Error( 'itsec-malware-scanner-invalid-json-data-in-scan-response', __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The response indicates that the encoding is JSON, but the data could not be decoded. This problem could be due to a temporary Sucuri server issue or a compatibility issue on your server. If the problem continues, please contact iThemes Security support.', 'better-wp-security' ), $response );
|
256 |
} else {
|
257 |
return new WP_Error( 'itsec-malware-scanner-invalid-content-type-in-scan-response', sprintf( __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The data received from the Sucuri server could not be decoded. In addition, a content type of <code>%s</code> was received when a content type of <code>application/json</code> was expected. This could indicate a temporary issue with the Sucuri servers.', 'better-wp-security' ), esc_html( $response['headers']['content-type'] ) ), $response );
|
258 |
}
|
259 |
} else if ( ! is_array( $body ) ) {
|
260 |
+
if ( 0 === strpos( $response['body'], 'ERROR' ) ) {
|
261 |
return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'better-wp-security' ), '<code>' . $response['body'] . '</code>' ), $response );
|
262 |
}
|
263 |
|
268 |
return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-malformed', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function is missing some critical information that is needed in order to properly process the response from Sucuri\'s servers. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'better-wp-security' ), $response );
|
269 |
}
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
return $body;
|
272 |
}
|
273 |
}
|
core/modules/malware/class-itsec-malware.php
CHANGED
@@ -5,10 +5,6 @@ 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 |
-
add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) );
|
10 |
-
add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
|
11 |
-
add_filter( 'itsec_logger_filter_malware_data_column_details', array( $this, 'filter_logger_data_column_details' ), 10, 2 );
|
12 |
}
|
13 |
|
14 |
/**
|
@@ -38,74 +34,4 @@ class ITSEC_Malware {
|
|
38 |
return $verbs;
|
39 |
}
|
40 |
|
41 |
-
/**
|
42 |
-
* Register malware for logger
|
43 |
-
*
|
44 |
-
* @since 4.4
|
45 |
-
*
|
46 |
-
* @param array $logger_modules array of logger modules
|
47 |
-
*
|
48 |
-
* @return array array of logger modules
|
49 |
-
*/
|
50 |
-
public function itsec_logger_modules( $logger_modules ) {
|
51 |
-
$logger_modules['malware'] = array(
|
52 |
-
'type' => 'malware',
|
53 |
-
'function' => __( 'Malware Scan', 'better-wp-security' ),
|
54 |
-
);
|
55 |
-
|
56 |
-
return $logger_modules;
|
57 |
-
|
58 |
-
}
|
59 |
-
|
60 |
-
/**
|
61 |
-
* Array of metaboxes for the logs screen
|
62 |
-
*
|
63 |
-
* @since 4.4
|
64 |
-
*
|
65 |
-
* @param array $displays metabox array
|
66 |
-
*
|
67 |
-
* @return array metabox array
|
68 |
-
*/
|
69 |
-
public function itsec_logger_displays( $displays ) {
|
70 |
-
$displays[] = array(
|
71 |
-
'module' => 'malware',
|
72 |
-
'title' => __( 'Malware Scan', 'better-wp-security' ),
|
73 |
-
'callback' => array( $this, 'logs_metabox_content' )
|
74 |
-
);
|
75 |
-
|
76 |
-
return $displays;
|
77 |
-
}
|
78 |
-
|
79 |
-
/**
|
80 |
-
* Render the settings metabox
|
81 |
-
*
|
82 |
-
* @return void
|
83 |
-
*/
|
84 |
-
public function logs_metabox_content() {
|
85 |
-
|
86 |
-
if ( ! class_exists( 'ITSEC_Malware_Log' ) ) {
|
87 |
-
require( dirname( __FILE__ ) . '/class-itsec-malware-log.php' );
|
88 |
-
}
|
89 |
-
|
90 |
-
$log_display = new ITSEC_Malware_Log();
|
91 |
-
|
92 |
-
$log_display->prepare_items();
|
93 |
-
$log_display->display();
|
94 |
-
|
95 |
-
}
|
96 |
-
|
97 |
-
public function filter_logger_data_column_details( $details, $data ) {
|
98 |
-
if ( is_wp_error( $data ) || ( ! empty( $data ) && ! empty( $data['SCAN']['SITE'] ) ) ) {
|
99 |
-
// Results for Sucuri scans.
|
100 |
-
|
101 |
-
require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
|
102 |
-
|
103 |
-
$details = "<div class='itsec-malware-scan-results-wrapper'>\n";
|
104 |
-
$details .= ITSEC_Malware_Scan_Results_Template::get_html( $data, true );
|
105 |
-
$details .= "</div>\n";
|
106 |
-
}
|
107 |
-
|
108 |
-
return $details;
|
109 |
-
}
|
110 |
-
|
111 |
}
|
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 |
/**
|
34 |
return $verbs;
|
35 |
}
|
36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
core/modules/malware/css/malware.css
CHANGED
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
1 |
.itsec-malware-scan-results ul {
|
2 |
margin-left: 20px;
|
3 |
}
|
1 |
+
.itsec-malware-scan-results h4 {
|
2 |
+
margin-top: 5px;
|
3 |
+
}
|
4 |
.itsec-malware-scan-results ul {
|
5 |
margin-left: 20px;
|
6 |
}
|
core/modules/malware/css/settings.css
CHANGED
@@ -1,3 +1,7 @@
|
|
|
|
|
|
|
|
|
|
1 |
.itsec-malware-scan-results ul {
|
2 |
margin-left: 20px;
|
3 |
}
|
@@ -21,6 +25,9 @@
|
|
21 |
.itsec-malware-scan-results-section:last-child {
|
22 |
border-bottom-color: #ddd;
|
23 |
}
|
|
|
|
|
|
|
24 |
.itsec-malware-scan-clean,
|
25 |
.itsec-malware-scan-warn,
|
26 |
.itsec-malware-scan-error {
|
1 |
+
.itsec-malware-scan-results h4 {
|
2 |
+
margin-top: 5px;
|
3 |
+
}
|
4 |
+
|
5 |
.itsec-malware-scan-results ul {
|
6 |
margin-left: 20px;
|
7 |
}
|
25 |
.itsec-malware-scan-results-section:last-child {
|
26 |
border-bottom-color: #ddd;
|
27 |
}
|
28 |
+
.form-table td .itsec-malware-scan-results-section p {
|
29 |
+
margin: 1em 0;
|
30 |
+
}
|
31 |
.itsec-malware-scan-clean,
|
32 |
.itsec-malware-scan-warn,
|
33 |
.itsec-malware-scan-error {
|
core/modules/malware/js/malware.js
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
|
9 |
bindEvents: function() {
|
10 |
$('#itsec-malware-scan').on('click', this.startScan);
|
11 |
-
$(
|
12 |
},
|
13 |
|
14 |
toggleDetails: function( event ) {
|
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 ) {
|
core/modules/malware/js/settings-page.js
CHANGED
@@ -5,18 +5,18 @@
|
|
5 |
init: function() {
|
6 |
this.bindEvents();
|
7 |
},
|
8 |
-
|
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 ) {
|
15 |
e.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( itsecMalwareScanData.showDetailsText );
|
22 |
$details.hide();
|
@@ -25,33 +25,33 @@
|
|
25 |
$details.show();
|
26 |
}
|
27 |
},
|
28 |
-
|
29 |
startScan: function( e ) {
|
30 |
e.preventDefault();
|
31 |
-
|
32 |
itsecMalwareScanData.originalSubmitButtonText = $(this).val();
|
33 |
-
|
34 |
$(this)
|
35 |
.prop( 'disabled', true )
|
36 |
.val( itsecMalwareScanData.clickedButtonText );
|
37 |
-
|
38 |
var data = {
|
39 |
'action': 'run-scan'
|
40 |
};
|
41 |
-
|
42 |
-
|
43 |
},
|
44 |
-
|
45 |
handleResponse: function( results ) {
|
46 |
$('#itsec-malware-scan-start').hide();
|
47 |
|
48 |
if ( results.response && results.response.length ) {
|
49 |
$('.itsec-malware-scan-results-wrapper').html( results.response );
|
50 |
}
|
51 |
-
|
52 |
if ( results.errors.length > 0 ) {
|
53 |
var message;
|
54 |
-
|
55 |
$.each( results.errors, function( index, error ) {
|
56 |
message = '<div class="notice notice-error notice-alt"><p><strong>' + error + '</strong></p></div>';
|
57 |
$('.itsec-malware-scan-results-wrapper').append( message );
|
@@ -66,7 +66,7 @@
|
|
66 |
}
|
67 |
},
|
68 |
};
|
69 |
-
|
70 |
$(document).ready(function() {
|
71 |
itsecMalwareScan.init();
|
72 |
});
|
5 |
init: function() {
|
6 |
this.bindEvents();
|
7 |
},
|
8 |
+
|
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 ) {
|
15 |
e.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( itsecMalwareScanData.showDetailsText );
|
22 |
$details.hide();
|
25 |
$details.show();
|
26 |
}
|
27 |
},
|
28 |
+
|
29 |
startScan: function( e ) {
|
30 |
e.preventDefault();
|
31 |
+
|
32 |
itsecMalwareScanData.originalSubmitButtonText = $(this).val();
|
33 |
+
|
34 |
$(this)
|
35 |
.prop( 'disabled', true )
|
36 |
.val( itsecMalwareScanData.clickedButtonText );
|
37 |
+
|
38 |
var data = {
|
39 |
'action': 'run-scan'
|
40 |
};
|
41 |
+
|
42 |
+
itsecUtil.sendWidgetAJAXRequest( 'malware-scan', data, itsecMalwareScan.handleResponse );
|
43 |
},
|
44 |
+
|
45 |
handleResponse: function( results ) {
|
46 |
$('#itsec-malware-scan-start').hide();
|
47 |
|
48 |
if ( results.response && results.response.length ) {
|
49 |
$('.itsec-malware-scan-results-wrapper').html( results.response );
|
50 |
}
|
51 |
+
|
52 |
if ( results.errors.length > 0 ) {
|
53 |
var message;
|
54 |
+
|
55 |
$.each( results.errors, function( index, error ) {
|
56 |
message = '<div class="notice notice-error notice-alt"><p><strong>' + error + '</strong></p></div>';
|
57 |
$('.itsec-malware-scan-results-wrapper').append( message );
|
66 |
}
|
67 |
},
|
68 |
};
|
69 |
+
|
70 |
$(document).ready(function() {
|
71 |
itsecMalwareScan.init();
|
72 |
});
|
core/modules/malware/logs.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
final class ITSEC_Malware_Logs {
|
4 |
+
public function __construct() {
|
5 |
+
add_filter( 'itsec_logs_prepare_malware_entry_for_list_display', array( $this, 'filter_entry_for_list_display' ) );
|
6 |
+
add_filter( 'itsec_logs_prepare_malware_entry_for_details_display', array( $this, 'filter_entry_for_details_display' ), 10, 4 );
|
7 |
+
|
8 |
+
if ( did_action( 'admin_enqueue_scripts' ) ) {
|
9 |
+
$this->enqueue();
|
10 |
+
} else {
|
11 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
|
12 |
+
}
|
13 |
+
}
|
14 |
+
|
15 |
+
public function filter_entry_for_list_display( $entry ) {
|
16 |
+
$entry['module_display'] = esc_html__( 'Malware Scan', 'better-wp-security' );
|
17 |
+
|
18 |
+
if ( 'scan' === $entry['code'] && 'process-start' === $entry['type'] ) {
|
19 |
+
$entry['description'] = esc_html__( 'Scan Performance', 'better-wp-security' );
|
20 |
+
} else if ( 'clean' === $entry['code'] ) {
|
21 |
+
$entry['description'] = esc_html__( 'Clean', 'better-wp-security' );
|
22 |
+
} else if ( 'scan-failure-server-error' === $entry['code'] ) {
|
23 |
+
$entry['description'] = esc_html__( 'Scan Failed: Sucuri Error', 'better-wp-security' );
|
24 |
+
} else if ( 'scan-failure-client-error' === $entry['code'] ) {
|
25 |
+
$entry['description'] = esc_html__( 'Scan Failed: Site Error', 'better-wp-security' );
|
26 |
+
} else if ( 'sucuri-system-error' === $entry['code'] ) {
|
27 |
+
$entry['description'] = esc_html__( 'Scan Failed: Sucuri Error', 'better-wp-security' );
|
28 |
+
} else if ( 'found-malware-and-on-blacklist' === $entry['code'] ) {
|
29 |
+
$entry['description'] = esc_html__( 'Malware Found, Site on Blacklist', 'better-wp-security' );
|
30 |
+
} else if ( 'found-malware' === $entry['code'] ) {
|
31 |
+
$entry['description'] = esc_html__( 'Malware Found', 'better-wp-security' );
|
32 |
+
} else if ( 'on-blacklist' === $entry['code'] ) {
|
33 |
+
$entry['description'] = esc_html__( 'Site on Blacklist', 'better-wp-security' );
|
34 |
+
} else if ( 'scan' === $entry['code'] ) {
|
35 |
+
$entry['description'] = esc_html__( 'Scan', 'better-wp-security' );
|
36 |
+
}
|
37 |
+
|
38 |
+
return $entry;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function filter_entry_for_details_display( $details, $entry, $code, $code_data ) {
|
42 |
+
require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
|
43 |
+
|
44 |
+
|
45 |
+
$entry = $this->filter_entry_for_list_display( $entry, $code, $code_data );
|
46 |
+
|
47 |
+
$details['module']['content'] = $entry['module_display'];
|
48 |
+
$details['description']['content'] = $entry['description'];
|
49 |
+
|
50 |
+
if ( 'process-start' !== $entry['type'] ) {
|
51 |
+
$details['results'] = array(
|
52 |
+
'header' => esc_html__( 'Results', 'better-wp-security' ),
|
53 |
+
'content' => ITSEC_Malware_Scan_Results_Template::get_html( $entry['data']['results'], true ),
|
54 |
+
);
|
55 |
+
}
|
56 |
+
|
57 |
+
return $details;
|
58 |
+
}
|
59 |
+
|
60 |
+
public function enqueue() {
|
61 |
+
wp_enqueue_script( 'itsec-malware-scan-logs', plugin_dir_url( __FILE__ ) . 'js/malware.js', array( 'jquery' ), 3 );
|
62 |
+
}
|
63 |
+
}
|
64 |
+
new ITSEC_Malware_Logs();
|
core/modules/malware/sync-verbs/itsec-get-malware-scan-log.php
CHANGED
@@ -6,27 +6,12 @@ class Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Log extends Ithemes_Sync_Verb {
|
|
6 |
|
7 |
public $default_arguments = array(
|
8 |
'count' => 10,
|
|
|
9 |
);
|
10 |
|
11 |
public function run( $arguments ) {
|
12 |
$arguments = Ithemes_Sync_Functions::merge_defaults( $arguments, $this->default_arguments );
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
$items = $itsec_logger->get_events( 'malware', array(), $arguments['count'] );
|
17 |
-
$response = array();
|
18 |
-
|
19 |
-
foreach ( $items as $item ) {
|
20 |
-
$item['log_data'] = maybe_unserialize( $item['log_data'] );
|
21 |
-
|
22 |
-
if ( ! is_array( $item['log_data'] ) || ! isset( $item['log_data']['SCAN']['SITE'] ) ) {
|
23 |
-
// Don't return old scan data.
|
24 |
-
continue;
|
25 |
-
}
|
26 |
-
|
27 |
-
$response[] = $item;
|
28 |
-
}
|
29 |
-
|
30 |
-
return $response;
|
31 |
}
|
32 |
}
|
6 |
|
7 |
public $default_arguments = array(
|
8 |
'count' => 10,
|
9 |
+
'page' => 1,
|
10 |
);
|
11 |
|
12 |
public function run( $arguments ) {
|
13 |
$arguments = Ithemes_Sync_Functions::merge_defaults( $arguments, $this->default_arguments );
|
14 |
|
15 |
+
return ITSEC_Log::get_entries( array( 'module' => 'malware' ), $arguments['count'], $arguments['page'], 'timestamp', 'DESC', 'all' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
}
|
17 |
}
|
core/modules/notification-center/js/settings-page.js
CHANGED
@@ -7,7 +7,7 @@ jQuery( function ( $ ) {
|
|
7 |
$( document ).on( 'itsec-dismiss-notice', '.itsec-notification-center-mail-errors-container .notice.itsec-is-dismissible', function () {
|
8 |
var errorId = $( this ).data( 'id' );
|
9 |
|
10 |
-
|
11 |
if ( r.response && r.response.status === 'all-cleared' ) {
|
12 |
jQuery( '#itsec-module-card-notification-center' ).removeClass( 'itsec-module-status--warning' );
|
13 |
}
|
@@ -41,4 +41,4 @@ jQuery( function ( $ ) {
|
|
41 |
initializeHiding();
|
42 |
}
|
43 |
} );
|
44 |
-
} );
|
7 |
$( document ).on( 'itsec-dismiss-notice', '.itsec-notification-center-mail-errors-container .notice.itsec-is-dismissible', function () {
|
8 |
var errorId = $( this ).data( 'id' );
|
9 |
|
10 |
+
itsecUtil.sendModuleAJAXRequest( 'notification-center', { method: 'dismiss-mail-error', mail_error: errorId }, function ( r ) {
|
11 |
if ( r.response && r.response.status === 'all-cleared' ) {
|
12 |
jQuery( '#itsec-module-card-notification-center' ).removeClass( 'itsec-module-status--warning' );
|
13 |
}
|
41 |
initializeHiding();
|
42 |
}
|
43 |
} );
|
44 |
+
} );
|
core/modules/security-check/js/settings-page.js
CHANGED
@@ -15,7 +15,7 @@ jQuery( document ).ready( function ( $ ) {
|
|
15 |
'method': 'secure-site'
|
16 |
};
|
17 |
|
18 |
-
|
19 |
$( '#itsec-security-check-secure_site' )
|
20 |
.addClass( 'button-primary' )
|
21 |
.removeClass( 'button-secondary' )
|
@@ -62,7 +62,7 @@ jQuery( document ).ready( function ( $ ) {
|
|
62 |
.attr( 'value', $( this ).data( 'clicked-value' ) )
|
63 |
}
|
64 |
|
65 |
-
var ajaxFunction =
|
66 |
|
67 |
if ( 'undefined' !== typeof itsecSecurityCheckAJAXRequest ) {
|
68 |
ajaxFunction = itsecSecurityCheckAJAXRequest;
|
@@ -108,6 +108,6 @@ jQuery( document ).ready( function ( $ ) {
|
|
108 |
/*
|
109 |
function itsecSecurityCheckAJAXRequest( type, data, callback ) {
|
110 |
console.log( 'Override called' );
|
111 |
-
|
112 |
}
|
113 |
*/
|
15 |
'method': 'secure-site'
|
16 |
};
|
17 |
|
18 |
+
itsecUtil.sendModuleAJAXRequest( 'security-check', data, function( results ) {
|
19 |
$( '#itsec-security-check-secure_site' )
|
20 |
.addClass( 'button-primary' )
|
21 |
.removeClass( 'button-secondary' )
|
62 |
.attr( 'value', $( this ).data( 'clicked-value' ) )
|
63 |
}
|
64 |
|
65 |
+
var ajaxFunction = itsecUtil.sendModuleAJAXRequest;
|
66 |
|
67 |
if ( 'undefined' !== typeof itsecSecurityCheckAJAXRequest ) {
|
68 |
ajaxFunction = itsecSecurityCheckAJAXRequest;
|
108 |
/*
|
109 |
function itsecSecurityCheckAJAXRequest( type, data, callback ) {
|
110 |
console.log( 'Override called' );
|
111 |
+
itsecUtil.sendModuleAJAXRequest( type, data, callback );
|
112 |
}
|
113 |
*/
|
core/setup.php
CHANGED
@@ -143,6 +143,10 @@ final class ITSEC_Setup {
|
|
143 |
ITSEC_Lib::schedule_cron_test();
|
144 |
}
|
145 |
|
|
|
|
|
|
|
|
|
146 |
// Update stored build number.
|
147 |
ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
|
148 |
}
|
@@ -184,8 +188,8 @@ final class ITSEC_Setup {
|
|
184 |
}
|
185 |
|
186 |
private static function uninstall() {
|
187 |
-
|
188 |
-
|
189 |
|
190 |
ITSEC_Modules::run_uninstall();
|
191 |
|
@@ -195,16 +199,8 @@ final class ITSEC_Setup {
|
|
195 |
delete_site_option( 'itsec-storage' );
|
196 |
delete_site_option( 'itsec_active_modules' );
|
197 |
|
198 |
-
|
199 |
-
|
200 |
-
$wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->base_prefix . "itsec_temp;" );
|
201 |
-
|
202 |
-
if ( is_dir( ITSEC_Core::get_storage_dir() ) ) {
|
203 |
-
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-directory.php' );
|
204 |
-
|
205 |
-
ITSEC_Lib_Directory::remove( ITSEC_Core::get_storage_dir() );
|
206 |
-
}
|
207 |
-
|
208 |
ITSEC_Lib::clear_caches();
|
209 |
|
210 |
}
|
143 |
ITSEC_Lib::schedule_cron_test();
|
144 |
}
|
145 |
|
146 |
+
if ( $build < 4081 ) {
|
147 |
+
ITSEC_Core::get_scheduler()->register_events();
|
148 |
+
}
|
149 |
+
|
150 |
// Update stored build number.
|
151 |
ITSEC_Modules::set_setting( 'global', 'build', ITSEC_Core::get_plugin_build() );
|
152 |
}
|
188 |
}
|
189 |
|
190 |
private static function uninstall() {
|
191 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/schema.php' );
|
192 |
+
require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-directory.php' );
|
193 |
|
194 |
ITSEC_Modules::run_uninstall();
|
195 |
|
199 |
delete_site_option( 'itsec-storage' );
|
200 |
delete_site_option( 'itsec_active_modules' );
|
201 |
|
202 |
+
ITSEC_Schema::remove_database_tables();
|
203 |
+
ITSEC_Lib_Directory::remove( ITSEC_Core::get_storage_dir() );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
ITSEC_Lib::clear_caches();
|
205 |
|
206 |
}
|
history.txt
CHANGED
@@ -717,3 +717,7 @@
|
|
717 |
Bug Fix: Update to the REST API "Restricted Access" feature to protect against methods to work around the restricted access.
|
718 |
Bug Fix: Prevent login page being hidden when following the "Confirm Email Address" notification URL.
|
719 |
Bug Fix: Hide Backend notifications not being properly sent when first enabled.
|
|
|
|
|
|
|
|
717 |
Bug Fix: Update to the REST API "Restricted Access" feature to protect against methods to work around the restricted access.
|
718 |
Bug Fix: Prevent login page being hidden when following the "Confirm Email Address" notification URL.
|
719 |
Bug Fix: Hide Backend notifications not being properly sent when first enabled.
|
720 |
+
6.9.0 - 2018-02-12 - Chris Jean & Timothy Jacobs
|
721 |
+
Enhancement: Updated logging system to keep track of more information and have more options to filter and sort log entries.
|
722 |
+
Enhancement: Improved efficiency of File Change Detection scanning.
|
723 |
+
Bug Fix: Fixed issue that could register loading the logging page as a failed login attempt on some sites.
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
Contributors: ithemes, chrisjean, gerroald, 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: 4.9.
|
6 |
-
Stable tag: 6.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -188,6 +188,11 @@ Free support may be available with the help of the community in the <a href="htt
|
|
188 |
|
189 |
== Changelog ==
|
190 |
|
|
|
|
|
|
|
|
|
|
|
191 |
= 6.8.1 =
|
192 |
* Enhancement: Display user lockouts in Lockout Sidebar.
|
193 |
* Bug Fix: Load translations on the plugins_loaded hook.
|
@@ -400,5 +405,5 @@ Free support may be available with the help of the community in the <a href="htt
|
|
400 |
|
401 |
== Upgrade Notice ==
|
402 |
|
403 |
-
= 6.
|
404 |
-
Version 6.
|
2 |
Contributors: ithemes, chrisjean, gerroald, 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: 4.9.4
|
6 |
+
Stable tag: 6.9.0
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
188 |
|
189 |
== Changelog ==
|
190 |
|
191 |
+
= 6.9.0 =
|
192 |
+
* Enhancement: Updated logging system to keep track of more information and have more options to filter and sort log entries.
|
193 |
+
* Enhancement: Improved efficiency of File Change Detection scanning.
|
194 |
+
* Bug Fix: Fixed issue that could register loading the logging page as a failed login attempt on some sites.
|
195 |
+
|
196 |
= 6.8.1 =
|
197 |
* Enhancement: Display user lockouts in Lockout Sidebar.
|
198 |
* Bug Fix: Load translations on the plugins_loaded hook.
|
405 |
|
406 |
== Upgrade Notice ==
|
407 |
|
408 |
+
= 6.9.0 =
|
409 |
+
Version 6.9.0 contains an improved logging system and bug fixes. It is recommended for all users.
|