iThemes Security (formerly Better WP Security) - Version 6.4.0

Version Description

  • Enhancement: Replaced file locking with database locking. This method of locking is compatible with all systems as it does not require the ability to write files. It also allows for locking to work on sites that have multiple front-end servers with a shared database. Since file locking is no longer used, the Global Settings > Disable File Locking setting was removed.
    • Enhancement: Add "Copy to Clipboard" functionality for server and wp-config rules.
    • Bug Fix: Prevent 404s when following links in email notifications on a site with Hide Backend enabled.
    • Bug Fix: Ensure uninstall process is not run when another version of iThemes Security is still active.
    • Bug Fix: Fixed method of working around Hide Backend.
    • Bug Fix: Warnings are no longer generated when saving a user profile with a role of "No role for this site" selected.
Download this release

Release Info

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

Code changes from version 6.3.0 to 6.4.0

better-wp-security.php CHANGED
@@ -6,7 +6,7 @@
6
  * Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
7
  * Author: iThemes
8
  * Author URI: https://ithemes.com
9
- * Version: 6.3.0
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.4.0
10
  * Text Domain: better-wp-security
11
  * Network: True
12
  * License: GPLv2
core/admin-pages/css/style.css CHANGED
@@ -181,12 +181,12 @@ ul {
181
  }
182
 
183
  .itsec-pro-upsell {
184
- display: block;
185
- height: 100%;
186
- left: 0;
187
- position: absolute;
188
- top: 0;
189
- width: 100%;
190
  text-decoration: none;
191
  }
192
 
@@ -488,7 +488,7 @@ body.itsec-modal-open {
488
 
489
  #itsec-settings-messages-container div,
490
  .itsec-module-messages-container div {
491
- background: #f1f1f1;
492
  }
493
 
494
  #itsec-settings-messages-container.no-module {
@@ -498,7 +498,7 @@ body.itsec-modal-open {
498
  right: inherit;
499
  opacity: 1;
500
  background: none;
501
- border-radius: none;
502
  box-shadow:none;
503
  -webkit-transition: none;
504
  transition: none;
@@ -507,7 +507,6 @@ body.itsec-modal-open {
507
  }
508
 
509
  #itsec-settings-messages-container.no-module div {
510
- box-shadow: none;
511
  background: #fff;
512
  }
513
 
@@ -699,3 +698,37 @@ body.itsec-modal-open {
699
  background-color: #c1e1b9;
700
  border-color: #83c373;
701
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
 
183
  .itsec-pro-upsell {
184
+ display: block;
185
+ height: 100%;
186
+ left: 0;
187
+ position: absolute;
188
+ top: 0;
189
+ width: 100%;
190
  text-decoration: none;
191
  }
192
 
488
 
489
  #itsec-settings-messages-container div,
490
  .itsec-module-messages-container div {
491
+
492
  }
493
 
494
  #itsec-settings-messages-container.no-module {
498
  right: inherit;
499
  opacity: 1;
500
  background: none;
501
+ border-radius: 0;
502
  box-shadow:none;
503
  -webkit-transition: none;
504
  transition: none;
507
  }
508
 
509
  #itsec-settings-messages-container.no-module div {
 
510
  background: #fff;
511
  }
512
 
698
  background-color: #c1e1b9;
699
  border-color: #83c373;
700
  }
701
+
702
+ /**
703
+ * Styling for output created by ITSEC_Settings_Page::show_details_toggle()
704
+ */
705
+ .itsec-details-toggle-container {
706
+ }
707
+ .itsec-details-toggle-container .itsec-details-toggle-details-hidde {
708
+ display: none;
709
+ }
710
+ .itsec-details-toggle-container {
711
+ font-size: 13px;
712
+ }
713
+ .itsec-details-toggle-container .itsec-details-toggle-details :last-child {
714
+ margin-bottom: 0;
715
+ }
716
+ .itsec-details-toggle-container .itsec-details-toggle-details ul li {
717
+ margin: 1em 0;
718
+ }
719
+
720
+ /**
721
+ * Styling for email contacts lists
722
+ */
723
+ ul.itsec-settings-contacts {
724
+ list-style: none;
725
+ margin: 1em 0;
726
+ padding-left: 0;
727
+ }
728
+ ul.itsec-settings-contacts + ul.itsec-settings-contacts {
729
+ padding-top: 1em;
730
+ border-top: 1px solid #ddd;
731
+ }
732
+ ul.itsec-settings-contacts li input[type="checkbox"] {
733
+ margin-top: 0;
734
+ }
core/admin-pages/js/script.js CHANGED
@@ -1,6 +1,9 @@
1
  "use strict";
2
 
3
  var itsecSettingsPage = {
 
 
 
4
  init: function() {
5
  jQuery( '.itsec-module-settings-container' ).hide();
6
 
@@ -9,23 +12,19 @@ var itsecSettingsPage = {
9
  jQuery( '.itsec-settings-view-toggle .itsec-selected' ).removeClass( 'itsec-selected' ).trigger( 'click' );
10
  jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
11
 
12
- jQuery(window).on("popstate", function(e, data) {
13
- if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module && '' !== e.originalEvent.state.module.replace( /[^\w-]/g, '' ) ) {
14
- jQuery( '#itsec-module-card-' + e.originalEvent.state.module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'itsec-popstate' );
15
- } else {
16
- itsecSettingsPage.closeGridSettingsModal( e );
17
- }
18
-
19
- if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module_type && '' !== e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) ) {
20
- jQuery( '#itsec-module-filter-' + e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'itsec-popstate' );
21
- }
22
- });
23
 
 
24
  var module_type = this.getUrlParameter( 'module_type' );
25
  if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
26
  module_type = 'recommended';
27
  }
28
  jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'click' );
 
 
 
29
 
30
  var module = this.getUrlParameter( 'module' );
31
  if ( 'string' === typeof module ) {
@@ -34,6 +33,23 @@ var itsecSettingsPage = {
34
  },
35
 
36
  bindEvents: function() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  var $container = jQuery( '#wpcontent' );
38
 
39
  $container.on( 'click', '.itsec-module-filter a', this.filterView );
@@ -48,11 +64,27 @@ var itsecSettingsPage = {
48
  $container.on( 'click', '.itsec-toggle-activation', this.toggleModuleActivation );
49
  $container.on( 'click', '.itsec-module-settings-save', this.saveSettings );
50
  $container.on( 'click', '.itsec-reload-module', this.reloadModule );
 
51
 
52
  $container.on( 'change', '#itsec-filter', this.logPageChangeFilter );
53
 
54
  // For use by module content to show/hide settings sections based upon an input.
55
  $container.on( 'change', '.itsec-settings-toggle', this.toggleModuleContent );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  },
57
 
58
  logPageChangeFilter: function( e ) {
@@ -87,6 +119,82 @@ var itsecSettingsPage = {
87
  }
88
  },
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  saveSettings: function( e ) {
91
  e.preventDefault();
92
 
@@ -124,8 +232,9 @@ var itsecSettingsPage = {
124
 
125
  itsecSettingsPage.clearMessages();
126
 
127
- if ( results.errors.length > 0 || ! results.closeModal ) {
128
  itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
 
129
  itsecSettingsPage.showMessages( results.messages, results.module, 'open' );
130
 
131
  if ( 'grid' === view ) {
@@ -149,13 +258,16 @@ var itsecSettingsPage = {
149
  jQuery( '#itsec-settings-messages-container, .itsec-module-messages-container' ).empty();
150
  },
151
 
152
- showErrors: function( errors, module, containerStatus ) {
153
  jQuery.each( errors, function( index, error ) {
154
- itsecSettingsPage.showError( error, module, containerStatus );
155
  } );
156
  },
157
 
158
- showError: function( error, module, containerStatus ) {
 
 
 
159
  if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
160
  var view = 'grid';
161
  } else {
@@ -181,7 +293,14 @@ var itsecSettingsPage = {
181
  var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
182
  }
183
 
184
- container.append( '<div class="error"><p><strong>' + error + '</strong></p></div>' ).addClass( 'visible' );
 
 
 
 
 
 
 
185
  },
186
 
187
  showMessages: function( messages, module, containerStatus ) {
@@ -219,7 +338,13 @@ var itsecSettingsPage = {
219
  var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
220
  }
221
 
222
- container.append( '<div class="updated fade"><p><strong>' + message + '</strong></p></div>' ).addClass( 'visible' );
 
 
 
 
 
 
223
  },
224
 
225
  filterView: function( e ) {
@@ -269,7 +394,7 @@ var itsecSettingsPage = {
269
  jQuery( '.itsec-toggle-settings' ).each(function( index ) {
270
  var $button = jQuery( this );
271
 
272
- if ( $button.parents( '.itsec-module-card' ).hasClass( 'itsec-module-type-enabled' ) ) {
273
  $button.html( itsec_page.translations.show_settings );
274
  } else if ( $button.hasClass( 'information-only' ) ) {
275
  $button.html( itsec_page.translations.information_only );
@@ -295,7 +420,8 @@ var itsecSettingsPage = {
295
  openModuleFromLink: function( e ) {
296
 
297
  var $link = jQuery( this ), module = $link.data( 'module-link' ),
298
- $module = jQuery( '.itsec-module-card[data-module-id="' + module + '"]' );
 
299
 
300
  if ( ! $module.length ) {
301
  return; // safety check
@@ -308,7 +434,9 @@ var itsecSettingsPage = {
308
  var $listClassElement = $module.parents( '.itsec-module-cards-container' ),
309
  $toggleButton = $module.find( '.itsec-toggle-settings' );
310
 
311
- console.log( $toggleButton );
 
 
312
 
313
  if ( $listClassElement.hasClass( 'list' ) ) {
314
  itsecSettingsPage.toggleListSettingsCard.call( $toggleButton, e );
@@ -605,10 +733,56 @@ var itsecSettingsPage = {
605
  jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
606
  } else if ( results.errors && results.errors.length > 0 ) {
607
  itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
 
 
608
  }
609
  } );
610
  },
611
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
  reloadWidget: function( widget ) {
613
  var method = 'get_refreshed_widget_settings';
614
  var data = {};
@@ -618,6 +792,7 @@ var itsecSettingsPage = {
618
  jQuery( '#itsec-sidebar-widget-' + module + ' .inside' ).html( results.response );
619
  } else {
620
  itsecSettingsPage.showErrors( results.errors, results.module, 'closed' );
 
621
  }
622
  } );
623
  },
@@ -647,6 +822,7 @@ var itsecSettingsPage = {
647
  'success': false,
648
  'response': null,
649
  'errors': [],
 
650
  'messages': [],
651
  'functionCalls': [],
652
  'redirect': false,
@@ -660,6 +836,7 @@ var itsecSettingsPage = {
660
  results.success = a.success;
661
  results.response = a.response;
662
  results.errors = a.errors;
 
663
  results.messages = a.messages;
664
  results.functionCalls = a.functionCalls;
665
  results.redirect = a.redirect;
@@ -715,7 +892,7 @@ var itsecSettingsPage = {
715
  if ( results.functionCalls ) {
716
  for ( var i = 0; i < results.functionCalls.length; i++ ) {
717
  if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof itsecSettingsPage[results.functionCalls[i][0]] ) {
718
- itsecSettingsPage[results.functionCalls[i][0]]( results.functionCalls[i][1] );
719
  } else if ( 'string' === typeof results.functionCalls[i] && 'function' === typeof window[results.functionCalls[i]] ) {
720
  window[results.functionCalls[i]]();
721
  } else if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof window[results.functionCalls[i][0]] ) {
@@ -765,33 +942,27 @@ jQuery(document).ready(function( $ ) {
765
  }
766
 
767
 
768
-
769
  jQuery( '.dialog' ).click( function ( event ) {
770
-
771
  event.preventDefault();
772
 
773
  var target = jQuery( this ).attr( 'href' );
774
  var title = jQuery( this ).parents( '.inside' ).siblings( 'h3.hndle' ).children( 'span' ).text();
775
 
776
  jQuery( '#' + target ).dialog( {
777
- dialogClass : 'wp-dialog itsec-dialog itsec-dialog-logs',
778
- modal : true,
779
- closeOnEscape: true,
780
- title : title,
781
- height : ( jQuery( window ).height() * 0.8 ),
782
- width : ( jQuery( window ).width() * 0.8 ),
783
- open : function ( event, ui ) {
784
-
785
- jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
786
- jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
787
- } );
788
-
789
- }
790
-
791
- } );
792
 
793
  jQuery( '.ui-dialog :button' ).blur();
794
-
795
  } );
796
 
797
  var regex = /[^\w]/ig;
@@ -801,6 +972,11 @@ jQuery(document).ready(function( $ ) {
801
  $searchFilter = $( '#itsec-module-filter-search' ),
802
  $currentFilter = $( '.itsec-feature-tabs .current' ).parent();
803
 
 
 
 
 
 
804
  $search.on( 'input', _.debounce( function () {
805
  var query = $search.val().trim().replace( regex, ' ' );
806
 
@@ -854,13 +1030,10 @@ jQuery(document).ready(function( $ ) {
854
  if ( $matches.length === 1 ) {
855
  $( '.itsec-toggle-settings', $matches.first() ).click();
856
  }
857
-
858
  }, 250 ) );
859
 
860
  $.expr[":"].itsecContains = $.expr.createPseudo( function ( arg ) {
861
-
862
  return function ( elem ) {
863
-
864
  var candidate = $( elem ).text().toUpperCase().replace( regex, ' ' ), term = arg.toUpperCase();
865
  var index = candidate.indexOf( term );
866
 
1
  "use strict";
2
 
3
  var itsecSettingsPage = {
4
+
5
+ events: jQuery( {} ),
6
+
7
  init: function() {
8
  jQuery( '.itsec-module-settings-container' ).hide();
9
 
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
+ },
 
 
 
 
 
 
 
 
18
 
19
+ initFilters: function() {
20
  var module_type = this.getUrlParameter( 'module_type' );
21
  if ( false === module_type || 0 === jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) ).length ) {
22
  module_type = 'recommended';
23
  }
24
  jQuery( '#itsec-module-filter-' + module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'click' );
25
+ },
26
+
27
+ initCurrentModule: function() {
28
 
29
  var module = this.getUrlParameter( 'module' );
30
  if ( 'string' === typeof module ) {
33
  },
34
 
35
  bindEvents: function() {
36
+
37
+ if ( itsecSettingsPage.bindEvents.bound ) {
38
+ return;
39
+ }
40
+
41
+ jQuery(window).on("popstate", function(e, data) {
42
+ if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module && '' !== e.originalEvent.state.module.replace( /[^\w-]/g, '' ) ) {
43
+ jQuery( '#itsec-module-card-' + e.originalEvent.state.module.replace( /[^\w-]/g, '' ) + ' button.itsec-toggle-settings' ).trigger( 'itsec-popstate' );
44
+ } else {
45
+ itsecSettingsPage.closeGridSettingsModal( e );
46
+ }
47
+
48
+ if ( null !== e.originalEvent.state && 'string' == typeof e.originalEvent.state.module_type && '' !== e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) ) {
49
+ jQuery( '#itsec-module-filter-' + e.originalEvent.state.module_type.replace( /[^\w-]/g, '' ) + ' a' ).trigger( 'itsec-popstate' );
50
+ }
51
+ });
52
+
53
  var $container = jQuery( '#wpcontent' );
54
 
55
  $container.on( 'click', '.itsec-module-filter a', this.filterView );
64
  $container.on( 'click', '.itsec-toggle-activation', this.toggleModuleActivation );
65
  $container.on( 'click', '.itsec-module-settings-save', this.saveSettings );
66
  $container.on( 'click', '.itsec-reload-module', this.reloadModule );
67
+ $container.on( 'click', '.itsec-details-toggle-container a[href="#"]', this.toggleDetails );
68
 
69
  $container.on( 'change', '#itsec-filter', this.logPageChangeFilter );
70
 
71
  // For use by module content to show/hide settings sections based upon an input.
72
  $container.on( 'change', '.itsec-settings-toggle', this.toggleModuleContent );
73
+ $container.on( 'click', '.itsec-copy-trigger', this.handleCopy );
74
+
75
+ itsecSettingsPage.bindEvents.bound = true;
76
+ },
77
+
78
+ toggleDetails: function( e ) {
79
+ e.preventDefault();
80
+
81
+ var $details = jQuery(this).parent().find( '.itsec-details-toggle-details' ).toggleClass( 'hide-if-js' );
82
+
83
+ if ( $details.hasClass( 'hide-if-js' ) ) {
84
+ jQuery(this).html( itsec_page.translations.show_information );
85
+ } else {
86
+ jQuery(this).html( itsec_page.translations.hide_description );
87
+ }
88
  },
89
 
90
  logPageChangeFilter: function( e ) {
119
  }
120
  },
121
 
122
+ handleCopy: function( e ) {
123
+
124
+ e.preventDefault();
125
+
126
+ var $trigger = jQuery( e.currentTarget );
127
+ var fromId = $trigger.data( 'copy-from' );
128
+
129
+ if ( ! fromId.length ) {
130
+ return;
131
+ }
132
+
133
+ var el = document.getElementById( fromId );
134
+
135
+ var removeSelect = itsecSettingsPage.selectText( el );
136
+
137
+ try {
138
+
139
+ document.execCommand( 'copy' );
140
+ removeSelect();
141
+ $trigger.text( itsec_page.translations.copied );
142
+
143
+ } catch ( e ) {
144
+ var $p = jQuery( '<p></p>' ).text( itsec_page.translations.copy_instruction ),
145
+ $notice = jQuery( '<div class="notice notice-alt notice-info"></div>' ).append( $p ),
146
+ $el = jQuery( el );
147
+
148
+ $trigger.after( $notice );
149
+
150
+ var removeNotice = function () {
151
+ $notice.fadeOut( function () {
152
+ $notice.remove();
153
+ } );
154
+ };
155
+ var copy = function () {
156
+
157
+ setTimeout( function () {
158
+ removeNotice();
159
+ removeSelect();
160
+ }, 100 );
161
+
162
+ $el.off( 'copy', copy );
163
+
164
+ return true;
165
+ };
166
+
167
+ $el.on( 'copy', copy );
168
+
169
+ setTimeout( removeNotice, 5000 );
170
+ }
171
+ },
172
+
173
+ // https://stackoverflow.com/a/987376
174
+ selectText: function( element ) {
175
+ var doc = document, text = element, range, selection;
176
+
177
+ if ( doc.body.createTextRange ) { // ie
178
+ range = document.body.createTextRange();
179
+ range.moveToElementText( text );
180
+ range.select();
181
+ } else if ( window.getSelection ) {
182
+ selection = window.getSelection();
183
+ range = document.createRange();
184
+ range.selectNodeContents( text );
185
+ selection.removeAllRanges();
186
+ selection.addRange( range );
187
+ }
188
+
189
+ return function() {
190
+ if ( selection ) {
191
+ selection.removeAllRanges();
192
+ } else {
193
+ range.collapse();
194
+ }
195
+ };
196
+ },
197
+
198
  saveSettings: function( e ) {
199
  e.preventDefault();
200
 
232
 
233
  itsecSettingsPage.clearMessages();
234
 
235
+ if ( results.errors.length > 0 || results.warnings.length > 0 || ! results.closeModal ) {
236
  itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
237
+ itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
238
  itsecSettingsPage.showMessages( results.messages, results.module, 'open' );
239
 
240
  if ( 'grid' === view ) {
258
  jQuery( '#itsec-settings-messages-container, .itsec-module-messages-container' ).empty();
259
  },
260
 
261
+ showErrors: function( errors, module, containerStatus, type ) {
262
  jQuery.each( errors, function( index, error ) {
263
+ itsecSettingsPage.showError( error, module, containerStatus, type );
264
  } );
265
  },
266
 
267
+ showError: function( error, module, containerStatus, type ) {
268
+
269
+ type = type || 'error';
270
+
271
  if ( jQuery( '.itsec-module-cards-container' ).hasClass( 'grid' ) ) {
272
  var view = 'grid';
273
  } else {
293
  var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
294
  }
295
 
296
+ var $notice = jQuery( '<div class="notice"><p><strong>' + error + '</strong></p></div>' );
297
+ $notice.addClass( 'notice-' + type );
298
+
299
+ if ( containerStatus === 'open' || module.length ) {
300
+ $notice.addClass( 'notice-alt' );
301
+ }
302
+
303
+ container.append( $notice ).addClass( 'visible' );
304
  },
305
 
306
  showMessages: function( messages, module, containerStatus ) {
338
  var container = jQuery( '#itsec-module-card-' + module + ' .itsec-module-messages-container' );
339
  }
340
 
341
+ var $notice = jQuery( '<div class="notice notice-success fade"><p><strong>' + message + '</strong></p></div>' );
342
+
343
+ if ( containerStatus === 'open' || module.length ) {
344
+ $notice.addClass( 'notice-alt' );
345
+ }
346
+
347
+ container.append( $notice ).addClass( 'visible' );
348
  },
349
 
350
  filterView: function( e ) {
394
  jQuery( '.itsec-toggle-settings' ).each(function( index ) {
395
  var $button = jQuery( this );
396
 
397
+ if ( $button.parents( '.itsec-module-card' ).hasClass( 'itsec-module-type-enabled' ) && ! $button.hasClass( 'information-only' ) ) {
398
  $button.html( itsec_page.translations.show_settings );
399
  } else if ( $button.hasClass( 'information-only' ) ) {
400
  $button.html( itsec_page.translations.information_only );
420
  openModuleFromLink: function( e ) {
421
 
422
  var $link = jQuery( this ), module = $link.data( 'module-link' ),
423
+ $module = jQuery( '.itsec-module-card[data-module-id="' + module + '"]' ),
424
+ highlight = $link.data( 'highlight-setting-id' );
425
 
426
  if ( ! $module.length ) {
427
  return; // safety check
434
  var $listClassElement = $module.parents( '.itsec-module-cards-container' ),
435
  $toggleButton = $module.find( '.itsec-toggle-settings' );
436
 
437
+ if ( highlight.length ) {
438
+ jQuery( 'label[for="' + highlight + '"]', $module ).parents( 'tr' ).addClass( 'itsec-highlighted-setting' );
439
+ }
440
 
441
  if ( $listClassElement.hasClass( 'list' ) ) {
442
  itsecSettingsPage.toggleListSettingsCard.call( $toggleButton, e );
733
  jQuery( '.itsec-settings-toggle' ).trigger( 'change' );
734
  } else if ( results.errors && results.errors.length > 0 ) {
735
  itsecSettingsPage.showErrors( results.errors, results.module, 'open' );
736
+ } else if ( results.warnings && results.warnings.length > 0 ) {
737
+ itsecSettingsPage.showErrors( results.warnings, results.module, 'open', 'warning' );
738
  }
739
  } );
740
  },
741
 
742
+ reloadAllModules: function( _, initialResponse) {
743
+ itsecSettingsPage.sendAJAXRequest( '#', 'get_refreshed_module_form', null, function ( response ) {
744
+
745
+ if ( ! response.success || response.errors.length ) {
746
+ return;
747
+ }
748
+
749
+ var $open;
750
+
751
+ if ( jQuery( 'body' ).hasClass( 'itsec-modal-open' ) ) {
752
+ var $newModules = jQuery( response.response ), $cardsList = jQuery( '.itsec-module-cards' );
753
+ $open = jQuery( '.itsec-module-settings-container:visible' ).parents( '.itsec-module-card' );
754
+
755
+ jQuery( '.itsec-module-card', $newModules ).each( function () {
756
+ var $new = jQuery( this ), $current = jQuery( '#' + $new.attr( 'id' ), $cardsList );
757
+
758
+ if ( $new.attr( 'id' ).length && $new.attr( 'id' ) === $open.attr( 'id' ) ) {
759
+ jQuery( '.itsec-module-settings-content-main', $current ).html( jQuery( '.itsec-module-settings-content-main', $new ).html() );
760
+ } else {
761
+ jQuery( '.itsec-module-settings-container', $new ).hide();
762
+ $current.replaceWith( $new );
763
+ }
764
+ } );
765
+
766
+ } else {
767
+ jQuery( '.itsec-module-cards-container' ).html( response.response );
768
+ }
769
+
770
+ itsecSettingsPage.initFilters();
771
+
772
+ if ( ! $open ) {
773
+ jQuery( '.itsec-module-settings-container' ).hide();
774
+ }
775
+
776
+ if ( initialResponse ) {
777
+ itsecSettingsPage.showMessages( initialResponse.messages, initialResponse.module, $open ? 'open' : 'closed' );
778
+ itsecSettingsPage.showErrors( initialResponse.errors, initialResponse.module, $open ? 'open' : 'closed' );
779
+ itsecSettingsPage.showErrors( initialResponse.warnings, initialResponse.module, $open ? 'open' : 'closed', 'warning' );
780
+ }
781
+
782
+ itsecSettingsPage.events.trigger( 'modulesReloaded', initialResponse );
783
+ } );
784
+ },
785
+
786
  reloadWidget: function( widget ) {
787
  var method = 'get_refreshed_widget_settings';
788
  var data = {};
792
  jQuery( '#itsec-sidebar-widget-' + module + ' .inside' ).html( results.response );
793
  } else {
794
  itsecSettingsPage.showErrors( results.errors, results.module, 'closed' );
795
+ itsecSettingsPage.showErrors( results.warnings, results.module, 'closed', 'warning' );
796
  }
797
  } );
798
  },
822
  'success': false,
823
  'response': null,
824
  'errors': [],
825
+ 'warnings': [],
826
  'messages': [],
827
  'functionCalls': [],
828
  'redirect': false,
836
  results.success = a.success;
837
  results.response = a.response;
838
  results.errors = a.errors;
839
+ results.warnings = a.warnings;
840
  results.messages = a.messages;
841
  results.functionCalls = a.functionCalls;
842
  results.redirect = a.redirect;
892
  if ( results.functionCalls ) {
893
  for ( var i = 0; i < results.functionCalls.length; i++ ) {
894
  if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof itsecSettingsPage[results.functionCalls[i][0]] ) {
895
+ itsecSettingsPage[results.functionCalls[i][0]]( results.functionCalls[i][1], results );
896
  } else if ( 'string' === typeof results.functionCalls[i] && 'function' === typeof window[results.functionCalls[i]] ) {
897
  window[results.functionCalls[i]]();
898
  } else if ( 'object' === typeof results.functionCalls[i] && 'string' === typeof results.functionCalls[i][0] && 'function' === typeof window[results.functionCalls[i][0]] ) {
942
  }
943
 
944
 
 
945
  jQuery( '.dialog' ).click( function ( event ) {
 
946
  event.preventDefault();
947
 
948
  var target = jQuery( this ).attr( 'href' );
949
  var title = jQuery( this ).parents( '.inside' ).siblings( 'h3.hndle' ).children( 'span' ).text();
950
 
951
  jQuery( '#' + target ).dialog( {
952
+ dialogClass : 'wp-dialog itsec-dialog itsec-dialog-logs',
953
+ modal : true,
954
+ closeOnEscape: true,
955
+ title : title,
956
+ height : ( jQuery( window ).height() * 0.8 ),
957
+ width : ( jQuery( window ).width() * 0.8 ),
958
+ open : function ( event, ui ) {
959
+ jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
960
+ jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
961
+ } );
962
+ }
963
+ } );
 
 
 
964
 
965
  jQuery( '.ui-dialog :button' ).blur();
 
966
  } );
967
 
968
  var regex = /[^\w]/ig;
972
  $searchFilter = $( '#itsec-module-filter-search' ),
973
  $currentFilter = $( '.itsec-feature-tabs .current' ).parent();
974
 
975
+ itsecSettingsPage.events.on( 'modulesReloaded', function() {
976
+ $cardsContainer = $( '.itsec-module-cards' );
977
+ $cards = $( '.itsec-module-card', $cardsContainer );
978
+ } );
979
+
980
  $search.on( 'input', _.debounce( function () {
981
  var query = $search.val().trim().replace( regex, ' ' );
982
 
1030
  if ( $matches.length === 1 ) {
1031
  $( '.itsec-toggle-settings', $matches.first() ).click();
1032
  }
 
1033
  }, 250 ) );
1034
 
1035
  $.expr[":"].itsecContains = $.expr.createPseudo( function ( arg ) {
 
1036
  return function ( elem ) {
 
1037
  var candidate = $( elem ).text().toUpperCase().replace( regex, ' ' ), term = arg.toUpperCase();
1038
  var index = candidate.indexOf( term );
1039
 
core/admin-pages/page-settings.php CHANGED
@@ -2,7 +2,9 @@
2
 
3
 
4
  final class ITSEC_Settings_Page {
5
- private $version = 1.5;
 
 
6
 
7
  private $self_url = '';
8
  private $modules = array();
@@ -10,7 +12,7 @@ final class ITSEC_Settings_Page {
10
  private $translations = array();
11
 
12
 
13
- public function __construct() {
14
  add_action( 'itsec-settings-page-register-module', array( $this, 'register_module' ) );
15
  add_action( 'itsec-settings-page-register-widget', array( $this, 'register_widget' ) );
16
 
@@ -43,6 +45,14 @@ final class ITSEC_Settings_Page {
43
  }
44
  }
45
 
 
 
 
 
 
 
 
 
46
  public function add_settings_classes( $classes ) {
47
  if ( ITSEC_Modules::get_setting( 'global', 'show_error_codes' ) ) {
48
  $classes .= ' itsec-show-error-codes';
@@ -104,6 +114,8 @@ final class ITSEC_Settings_Page {
104
  'activate' => __( 'Enable', 'better-wp-security' ),
105
  'deactivate' => __( 'Disable', 'better-wp-security' ),
106
  'error' => __( 'Error', 'better-wp-security' ),
 
 
107
 
108
  /* translators: 1: module name */
109
  'successful_save' => __( 'Settings saved successfully for %1$s.', 'better-wp-security' ),
@@ -167,6 +179,12 @@ final class ITSEC_Settings_Page {
167
  ITSEC_Response::set_response( $this->get_module_settings( $module ) );
168
  } else if ( 'get_refreshed_widget_settings' === $method ) {
169
  ITSEC_Response::set_response( $this->get_widget_settings( $module ) );
 
 
 
 
 
 
170
  } else if ( 'handle_module_request' === $method ) {
171
  if ( isset( $this->modules[$module] ) ) {
172
  if ( isset( $_POST['data'] ) ) {
@@ -370,10 +388,7 @@ final class ITSEC_Settings_Page {
370
  }
371
  }
372
 
373
- private function show_settings_page() {
374
- $form = new ITSEC_Form();
375
-
376
-
377
  $module_filters = array(
378
  'all' => array(
379
  _x( 'All', 'List all modules', 'better-wp-security' ),
@@ -389,31 +404,27 @@ final class ITSEC_Settings_Page {
389
  ),
390
  );
391
 
392
-
393
- $current_type = isset( $_REQUEST['module_type'] ) ? $_REQUEST['module_type'] : 'recommended';
394
- $visible_modules = array();
395
-
396
  foreach ( $this->modules as $id => $module ) {
397
  $module_filters['all'][1]++;
398
 
399
- if ( 'all' === $current_type ) {
400
- $visible_modules[] = $id;
401
- }
402
-
403
-
404
  if ( isset( $module_filters[$module->type] ) ) {
405
  $module_filters[$module->type][1]++;
406
-
407
- if ( $module->type === $current_type ) {
408
- $visible_modules[] = $id;
409
- }
410
  }
411
 
412
-
413
  $module->enabled = ITSEC_Modules::is_active( $id );
414
  $module->always_active = ITSEC_Modules::is_always_active( $id );
415
  }
416
 
 
 
 
 
 
 
 
 
 
 
417
  $feature_tabs = array();
418
 
419
  foreach ( $module_filters as $type => $data ) {
@@ -426,10 +437,6 @@ final class ITSEC_Settings_Page {
426
  $feature_tabs[] = "<li class='itsec-module-filter' id='itsec-module-filter-$type'><a href='" . esc_url( add_query_arg( 'module_type', $type, $this->self_url ) ) . "' class='$class'>{$data[0]} <span class='count'>({$data[1]})</span></a>";
427
  }
428
 
429
-
430
- $whitelisted_ips = ITSEC_Lib::get_whitelisted_ips();
431
- $blacklisted_ips = ITSEC_Lib::get_blacklisted_ips();
432
-
433
  // Get user's view preference
434
  $view = get_user_meta( get_current_user_id(), 'itsec-settings-view', true );
435
 
@@ -475,99 +482,7 @@ final class ITSEC_Settings_Page {
475
  </ul>
476
  </div>
477
  <div class="itsec-module-cards-container <?php echo $view; ?> hide-if-js">
478
- <?php $form->start_form( 'itsec-module-settings-form' ); ?>
479
- <?php $form->add_nonce( 'itsec-settings-page' ); ?>
480
- <ul class="itsec-module-cards">
481
- <?php foreach ( $this->modules as $id => $module ) : ?>
482
- <?php
483
- if ( ! in_array( $id, $visible_modules ) ) {
484
- // continue;
485
- }
486
-
487
- $classes = array(
488
- 'itsec-module-type-' . $module->type,
489
- 'itsec-module-type-' . ( $module->enabled ? 'enabled' : 'disabled' ),
490
- );
491
-
492
- if ( $module->upsell ) {
493
- $classes[] = 'itsec-module-pro-upsell';
494
- }
495
-
496
- if ( $module->pro ) {
497
- $classes[] = 'itsec-module-type-pro';
498
- }
499
- ?>
500
- <li id="itsec-module-card-<?php echo $id; ?>" class="itsec-module-card <?php echo implode( ' ', $classes ); ?>" data-module-id="<?php echo $id; ?>">
501
- <div class="itsec-module-card-content">
502
- <?php if ( $module->upsell ) : ?>
503
- <a href="<?php echo esc_url( $module->upsell_url ); ?>" target="_blank" rel="noopener noreferrer" class="itsec-pro-upsell">&nbsp;</a>
504
- <?php endif; ?>
505
- <h2><?php echo esc_html( $module->title ); ?></h2>
506
- <?php if ( $module->pro ) : ?>
507
- <div class="itsec-pro-label"><?php _e( 'Pro', 'better-wp-security' ); ?></div>
508
- <?php endif; ?>
509
- <p class="module-description"><?php echo $module->description; ?></p>
510
- <?php if ( ! $module->upsell ) : ?>
511
- <div class="module-actions hide-if-no-js">
512
- <?php if ( $module->information_only ) : ?>
513
- <button class="button button-secondary itsec-toggle-settings information-only"><?php echo $this->translations['show_information']; ?></button>
514
- <?php elseif ( $module->enabled || $module->always_active ) : ?>
515
- <button class="button button-secondary itsec-toggle-settings"><?php echo $this->translations['show_settings']; ?></button>
516
- <?php if ( ! $module->always_active ) : ?>
517
- <button class="button button-secondary itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
518
- <?php endif; ?>
519
- <?php else : ?>
520
- <button class="button button-secondary itsec-toggle-settings"><?php echo $this->translations['show_description']; ?></button>
521
- <button class="button button-primary itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
522
- <?php endif; ?>
523
- </div>
524
- <?php endif; ?>
525
- </div>
526
- <?php if ( ! $module->upsell ) : ?>
527
- <div class="itsec-module-settings-container">
528
- <div class="itsec-modal-navigation">
529
- <button class="dashicons itsec-close-modal"></button>
530
- <button class="itsec-right dashicons hidden"><span class="screen-reader-text"><?php _e( 'Configure next iThemes Security setting', 'better-wp-security' ); ?></span></button>
531
- <button class="itsec-left dashicons hidden"><span class="screen-reader-text"><?php _e( 'Configure previous iThemes Security setting', 'better-wp-security' ); ?></span></button>
532
- </div>
533
- <div class="itsec-module-settings-content-container">
534
- <div class="itsec-module-settings-content">
535
- <h3 class="itsec-modal-header"><?php echo esc_html( $module->title ); ?></h3>
536
- <div class="itsec-module-messages-container"></div>
537
- <div class="itsec-module-settings-content-main">
538
- <?php $this->get_module_settings( $id, $form, true ); ?>
539
- </div>
540
- </div>
541
- </div>
542
- <div class="itsec-list-content-footer hide-if-no-js">
543
- <?php if ( $module->can_save ) : ?>
544
- <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
545
- <?php endif; ?>
546
- <button class="button button-secondary align-left itsec-module-settings-cancel"><?php _e( 'Cancel', 'better-wp-security' ); ?></button>
547
- </div>
548
- <div class="itsec-modal-content-footer">
549
- <?php if ( $module->enabled || $module->always_active || $module->information_only ) : ?>
550
- <?php if ( ! $module->always_active && ! $module->information_only ) : ?>
551
- <button class="button button-secondary align-right itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
552
- <?php endif; ?>
553
- <?php else : ?>
554
- <button class="button button-primary align-right itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
555
- <?php endif; ?>
556
-
557
- <?php if ( $module->can_save ) : ?>
558
- <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
559
- <?php else : ?>
560
- <button class="button button-primary align-left itsec-close-modal"><?php echo $this->translations['close_settings']; ?></button>
561
- <?php endif; ?>
562
- </div>
563
- </div>
564
- <?php endif; ?>
565
- </li>
566
- <?php endforeach; ?>
567
- <li class="itsec-module-card-filler"></li>
568
- </ul>
569
-
570
- <?php $form->end_form(); ?>
571
  </div>
572
  </div>
573
  <div class="itsec-modal-background"></div>
@@ -602,6 +517,120 @@ final class ITSEC_Settings_Page {
602
  <?php
603
 
604
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
  }
606
 
607
- new ITSEC_Settings_Page();
2
 
3
 
4
  final class ITSEC_Settings_Page {
5
+ private $version = 1.6;
6
+
7
+ private static $instance;
8
 
9
  private $self_url = '';
10
  private $modules = array();
12
  private $translations = array();
13
 
14
 
15
+ private function __construct() {
16
  add_action( 'itsec-settings-page-register-module', array( $this, 'register_module' ) );
17
  add_action( 'itsec-settings-page-register-widget', array( $this, 'register_widget' ) );
18
 
45
  }
46
  }
47
 
48
+ public static function get_instance() {
49
+ if ( ! self::$instance ) {
50
+ self::$instance = new self;
51
+ }
52
+
53
+ return self::$instance;
54
+ }
55
+
56
  public function add_settings_classes( $classes ) {
57
  if ( ITSEC_Modules::get_setting( 'global', 'show_error_codes' ) ) {
58
  $classes .= ' itsec-show-error-codes';
114
  'activate' => __( 'Enable', 'better-wp-security' ),
115
  'deactivate' => __( 'Disable', 'better-wp-security' ),
116
  'error' => __( 'Error', 'better-wp-security' ),
117
+ 'copied' => __( 'Copied!', 'better-wp-security' ),
118
+ 'copy_instruction' => __( 'Please press Ctrl/Cmd+C to copy.', 'better-wp-security' ),
119
 
120
  /* translators: 1: module name */
121
  'successful_save' => __( 'Settings saved successfully for %1$s.', 'better-wp-security' ),
179
  ITSEC_Response::set_response( $this->get_module_settings( $module ) );
180
  } else if ( 'get_refreshed_widget_settings' === $method ) {
181
  ITSEC_Response::set_response( $this->get_widget_settings( $module ) );
182
+ } else if ( 'get_refreshed_module_form' === $method ) {
183
+ $form = new ITSEC_Form();
184
+ $this->prepare_modules_and_calculate_filters();
185
+ ob_start();
186
+ $this->print_modules_form( $form );
187
+ ITSEC_Response::set_response( ob_get_clean() );
188
  } else if ( 'handle_module_request' === $method ) {
189
  if ( isset( $this->modules[$module] ) ) {
190
  if ( isset( $_POST['data'] ) ) {
388
  }
389
  }
390
 
391
+ private function prepare_modules_and_calculate_filters() {
 
 
 
392
  $module_filters = array(
393
  'all' => array(
394
  _x( 'All', 'List all modules', 'better-wp-security' ),
404
  ),
405
  );
406
 
 
 
 
 
407
  foreach ( $this->modules as $id => $module ) {
408
  $module_filters['all'][1]++;
409
 
 
 
 
 
 
410
  if ( isset( $module_filters[$module->type] ) ) {
411
  $module_filters[$module->type][1]++;
 
 
 
 
412
  }
413
 
 
414
  $module->enabled = ITSEC_Modules::is_active( $id );
415
  $module->always_active = ITSEC_Modules::is_always_active( $id );
416
  }
417
 
418
+ return $module_filters;
419
+ }
420
+
421
+ private function show_settings_page() {
422
+ $form = new ITSEC_Form();
423
+
424
+ $module_filters = $this->prepare_modules_and_calculate_filters();
425
+
426
+ $current_type = isset( $_REQUEST['module_type'] ) ? $_REQUEST['module_type'] : 'recommended';
427
+
428
  $feature_tabs = array();
429
 
430
  foreach ( $module_filters as $type => $data ) {
437
  $feature_tabs[] = "<li class='itsec-module-filter' id='itsec-module-filter-$type'><a href='" . esc_url( add_query_arg( 'module_type', $type, $this->self_url ) ) . "' class='$class'>{$data[0]} <span class='count'>({$data[1]})</span></a>";
438
  }
439
 
 
 
 
 
440
  // Get user's view preference
441
  $view = get_user_meta( get_current_user_id(), 'itsec-settings-view', true );
442
 
482
  </ul>
483
  </div>
484
  <div class="itsec-module-cards-container <?php echo $view; ?> hide-if-js">
485
+ <?php $this->print_modules_form( $form ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  </div>
487
  </div>
488
  <div class="itsec-modal-background"></div>
517
  <?php
518
 
519
  }
520
+
521
+ /**
522
+ * Print the modules form element.
523
+ *
524
+ * @param ITSEC_Form $form
525
+ */
526
+ private function print_modules_form( $form ) {
527
+ ?>
528
+ <?php $form->start_form( 'itsec-module-settings-form' ); ?>
529
+ <?php $form->add_nonce( 'itsec-settings-page' ); ?>
530
+ <ul class="itsec-module-cards">
531
+ <?php foreach ( $this->modules as $id => $module ) : ?>
532
+ <?php
533
+
534
+ $classes = array(
535
+ 'itsec-module-type-' . $module->type,
536
+ 'itsec-module-type-' . ( $module->enabled ? 'enabled' : 'disabled' ),
537
+ );
538
+
539
+ if ( $module->upsell ) {
540
+ $classes[] = 'itsec-module-pro-upsell';
541
+ }
542
+
543
+ if ( $module->pro ) {
544
+ $classes[] = 'itsec-module-type-pro';
545
+ }
546
+ ?>
547
+ <li id="itsec-module-card-<?php echo $id; ?>" class="itsec-module-card <?php echo implode( ' ', $classes ); ?>" data-module-id="<?php echo $id; ?>">
548
+ <div class="itsec-module-card-content">
549
+ <?php if ( $module->upsell ) : ?>
550
+ <a href="<?php echo esc_url( $module->upsell_url ); ?>" target="_blank" rel="noopener noreferrer" class="itsec-pro-upsell">&nbsp;</a>
551
+ <?php endif; ?>
552
+ <h2><?php echo esc_html( $module->title ); ?></h2>
553
+ <?php if ( $module->pro ) : ?>
554
+ <div class="itsec-pro-label"><?php _e( 'Pro', 'better-wp-security' ); ?></div>
555
+ <?php endif; ?>
556
+ <p class="module-description"><?php echo $module->description; ?></p>
557
+ <?php if ( ! $module->upsell ) : ?>
558
+ <div class="module-actions hide-if-no-js">
559
+ <?php if ( $module->information_only ) : ?>
560
+ <button class="button button-secondary itsec-toggle-settings information-only"><?php echo $this->translations['show_information']; ?></button>
561
+ <?php elseif ( $module->enabled || $module->always_active ) : ?>
562
+ <button class="button button-secondary itsec-toggle-settings"><?php echo $this->translations['show_settings']; ?></button>
563
+ <?php if ( ! $module->always_active ) : ?>
564
+ <button class="button button-secondary itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
565
+ <?php endif; ?>
566
+ <?php else : ?>
567
+ <button class="button button-secondary itsec-toggle-settings"><?php echo $this->translations['show_description']; ?></button>
568
+ <button class="button button-primary itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
569
+ <?php endif; ?>
570
+ </div>
571
+ <?php endif; ?>
572
+ </div>
573
+ <?php if ( ! $module->upsell ) : ?>
574
+ <div class="itsec-module-settings-container">
575
+ <div class="itsec-modal-navigation">
576
+ <button class="dashicons itsec-close-modal"></button>
577
+ <button class="itsec-right dashicons hidden"><span class="screen-reader-text"><?php _e( 'Configure next iThemes Security setting', 'better-wp-security' ); ?></span></button>
578
+ <button class="itsec-left dashicons hidden"><span class="screen-reader-text"><?php _e( 'Configure previous iThemes Security setting', 'better-wp-security' ); ?></span></button>
579
+ </div>
580
+ <div class="itsec-module-settings-content-container">
581
+ <div class="itsec-module-settings-content">
582
+ <h3 class="itsec-modal-header"><?php echo esc_html( $module->title ); ?></h3>
583
+ <div class="itsec-module-messages-container"></div>
584
+ <div class="itsec-module-settings-content-main">
585
+ <?php $this->get_module_settings( $id, $form, true ); ?>
586
+ </div>
587
+ </div>
588
+ </div>
589
+ <div class="itsec-list-content-footer hide-if-no-js">
590
+ <?php if ( $module->can_save ) : ?>
591
+ <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
592
+ <?php endif; ?>
593
+ <button class="button button-secondary align-left itsec-module-settings-cancel"><?php _e( 'Cancel', 'better-wp-security' ); ?></button>
594
+ </div>
595
+ <div class="itsec-modal-content-footer">
596
+ <?php if ( $module->enabled || $module->always_active || $module->information_only ) : ?>
597
+ <?php if ( ! $module->always_active && ! $module->information_only ) : ?>
598
+ <button class="button button-secondary align-right itsec-toggle-activation"><?php echo $this->translations['deactivate']; ?></button>
599
+ <?php endif; ?>
600
+ <?php else : ?>
601
+ <button class="button button-primary align-right itsec-toggle-activation"><?php echo $this->translations['activate']; ?></button>
602
+ <?php endif; ?>
603
+
604
+ <?php if ( $module->can_save ) : ?>
605
+ <button class="button button-primary align-left itsec-module-settings-save"><?php echo $this->translations['save_settings']; ?></button>
606
+ <?php else : ?>
607
+ <button class="button button-primary align-left itsec-close-modal"><?php echo $this->translations['close_settings']; ?></button>
608
+ <?php endif; ?>
609
+ </div>
610
+ </div>
611
+ <?php endif; ?>
612
+ </li>
613
+ <?php endforeach; ?>
614
+ <li class="itsec-module-card-filler"></li>
615
+ </ul>
616
+
617
+ <?php $form->end_form(); ?>
618
+
619
+ <?php
620
+
621
+ }
622
+
623
+ public static function show_details_toggle( $description, $details ) {
624
+ $self = self::get_instance();
625
+
626
+ echo "<div class='itsec-warning-message itsec-details-toggle-container'>\n";
627
+ echo "$description<br />\n";
628
+ echo '<a href="#" class="hide-if-no-js">' . esc_html( $self->translations['show_information'] ) . '</a>';
629
+ echo "<div class='itsec-details-toggle-details hide-if-js'>\n";
630
+ echo $details;
631
+ echo "</div>\n";
632
+ echo "</div>\n";
633
+ }
634
  }
635
 
636
+ ITSEC_Settings_Page::get_instance();
core/files.php CHANGED
@@ -158,84 +158,4 @@ final class ITSEC_Files {
158
 
159
  return true;
160
  }
161
-
162
- /**
163
- * Attempt to get a lock for atomic operations.
164
- *
165
- * Tries to get a more robust lock on the file in question. Useful in situations where automatic
166
- * file locking doesn't work.
167
- *
168
- * @since 4.0.0
169
- *
170
- * @param string $lock_file file name of lock
171
- * @param int $exp seconds until lock expires
172
- *
173
- * @return bool true if lock was achieved, else false
174
- */
175
- public function get_file_lock( $lock_file, $exp = 180 ) {
176
-
177
- if ( ITSEC_Modules::get_setting( 'global', 'lock_file' ) ) {
178
- return true;
179
- }
180
-
181
- clearstatcache();
182
-
183
- $lock_file = ITSEC_Core::get_storage_dir() . '/' . sanitize_text_field( $lock_file ) . '.lock';
184
- $dir_age = @filectime( $lock_file );
185
-
186
- if ( false === @mkdir( $lock_file ) ) {
187
-
188
- if ( false !== $dir_age ) {
189
-
190
- if ( ( time() - $dir_age ) > intval( $exp ) ) { //see if the lock has expired
191
-
192
- @rmdir( $lock_file );
193
- @mkdir( $lock_file );
194
-
195
- } else { //couldn't get the lock
196
-
197
- return false;
198
-
199
- }
200
-
201
- } else {
202
-
203
- return false;
204
-
205
- }
206
-
207
- }
208
-
209
- return true; //file lock was achieved
210
-
211
- }
212
-
213
- /**
214
- * Release the lock.
215
- *
216
- * Releases a file lock to allow others to use it.
217
- *
218
- * @since 4.0.0
219
- *
220
- * @param string $lock_file file name of lock
221
- *
222
- * @return bool true if released, false otherwise
223
- */
224
- public function release_file_lock( $lock_file ) {
225
- if ( ITSEC_Modules::get_setting( 'global', 'lock_file' ) ) {
226
- return true;
227
- }
228
-
229
- require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-directory.php' );
230
-
231
- $lock_file = ITSEC_Core::get_storage_dir() . '/' . sanitize_text_field( $lock_file ) . '.lock';
232
-
233
- $result = ITSEC_Lib_Directory::remove( $lock_file );
234
-
235
- if ( is_wp_error( $result ) ) {
236
- return false;
237
- }
238
-
239
- return true;
240
- }
241
  }
158
 
159
  return true;
160
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  }
core/history.txt CHANGED
@@ -548,3 +548,10 @@
548
  New Feature: Added support for the ITSEC_DISABLE_MODULES define.
549
  3.4.1 - 2017-07-05 - Chris Jean & Timothy Jacobs
550
  Bug Fix: Fixed password-protected posts not properly handling the password when Hide Backend is enabled.
 
 
 
 
 
 
 
548
  New Feature: Added support for the ITSEC_DISABLE_MODULES define.
549
  3.4.1 - 2017-07-05 - Chris Jean & Timothy Jacobs
550
  Bug Fix: Fixed password-protected posts not properly handling the password when Hide Backend is enabled.
551
+ 3.5.0 - 2017-07-24 - Chris Jean & Timothy Jacobs
552
+ Enhancement: Replaced file locking with database locking. This method of locking is compatible with all systems as it does not require the ability to write files. It also allows for locking to work on sites that have multiple front-end servers with a shared database. Since file locking is no longer used, the Global Settings > Disable File Locking setting was removed.
553
+ Enhancement: Add "Copy to Clipboard" functionality for server and wp-config rules.
554
+ Bug Fix: Prevent 404s when following links in email notifications on a site with Hide Backend enabled.
555
+ Bug Fix: Ensure uninstall process is not run when another version of iThemes Security is still active.
556
+ Bug Fix: Fixed method of working around Hide Backend.
557
+ Bug Fix: Warnings are no longer generated when saving a user profile with a role of "No role for this site" selected.
core/lib.php CHANGED
@@ -1002,6 +1002,7 @@ final class ITSEC_Lib {
1002
  */
1003
  public static function get_url_path( $url, $prefix = '' ) {
1004
  $path = (string) parse_url( $url, PHP_URL_PATH );
 
1005
  $path = untrailingslashit( $path );
1006
 
1007
  if ( ! empty( $prefix ) && 0 === strpos( $path, $prefix ) ) {
@@ -1025,4 +1026,62 @@ final class ITSEC_Lib {
1025
 
1026
  return $GLOBALS['__itsec_lib_get_request_path'];
1027
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028
  }
1002
  */
1003
  public static function get_url_path( $url, $prefix = '' ) {
1004
  $path = (string) parse_url( $url, PHP_URL_PATH );
1005
+ $path = preg_replace( '|//+|', '/', $path );
1006
  $path = untrailingslashit( $path );
1007
 
1008
  if ( ! empty( $prefix ) && 0 === strpos( $path, $prefix ) ) {
1026
 
1027
  return $GLOBALS['__itsec_lib_get_request_path'];
1028
  }
1029
+
1030
+ /**
1031
+ * Acquire a lock.
1032
+ *
1033
+ * @since 6.3.0
1034
+ *
1035
+ * @param string $name Lock name.
1036
+ * @param int $expires_in Number of seconds to hold the lock for.
1037
+ *
1038
+ * @return bool
1039
+ */
1040
+ public static function get_lock( $name, $expires_in = 30 ) {
1041
+
1042
+ /** @var \wpdb $wpdb */
1043
+ global $wpdb;
1044
+
1045
+ $lock = "itsec-lock-{$name}";
1046
+ $now = ITSEC_Core::get_current_time_gmt();
1047
+ $release_at = $now + $expires_in;
1048
+
1049
+ if ( empty( $wpdb->sitemeta ) ) {
1050
+ $result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, 'no') /* LOCK */", $lock, $release_at ) );
1051
+ } else {
1052
+ $result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->sitemeta` (`site_id`, `meta_key`, `meta_value`) VALUES (%d, %s, %s) /* LOCK */", $wpdb->siteid, $lock, $release_at ) );
1053
+ }
1054
+
1055
+ // The lock exists. See if it has expired.
1056
+ if ( ! $result ) {
1057
+
1058
+ $locked_until = get_site_option( $lock );
1059
+
1060
+ if ( ! $locked_until ) {
1061
+ // Can't write or read the lock. Bail due to an unknown and hopefully temporary error.
1062
+ return false;
1063
+ }
1064
+
1065
+ if ( $locked_until > $now ) {
1066
+ // The lock still exists and has not expired.
1067
+ return false;
1068
+ }
1069
+ }
1070
+
1071
+ // Ensure that the lock is set properly by triggering all the regular actions and filters.
1072
+ update_site_option( $lock, $release_at );
1073
+
1074
+ return true;
1075
+ }
1076
+
1077
+ /**
1078
+ * Release a lock.
1079
+ *
1080
+ * @since 6.3.0
1081
+ *
1082
+ * @param string $name The lock name.
1083
+ */
1084
+ public static function release_lock( $name ) {
1085
+ delete_site_option( "itsec-lock-{$name}" );
1086
+ }
1087
  }
core/lib/class-itsec-mail.php CHANGED
@@ -69,7 +69,7 @@ final class ITSEC_Mail {
69
  'support' => esc_html__( 'Support', 'better-wp-security' ),
70
  'pro' => esc_html__( 'Pro', 'better-wp-security' ),
71
  'support_content' => sprintf( wp_kses( __( 'Pro customers can contact <a href="%s">iThemes Helpdesk</a> for help. Our support team answers questions Monday – Friday, 8am – 5pm (CST).', 'better-wp-security' ), array( 'a' => array( 'href' => array() ) ) ), esc_url( 'https://members.ithemes.com/panel/helpdesk.php' ) ),
72
- 'security_settings_link' => esc_url( ITSEC_Core::get_settings_page_url() ),
73
  'unsubscribe_link_text' => esc_html__( 'This email was generated by the iThemes Security plugin.', 'better-wp-security' ) . '<br>' . esc_html__( 'To unsubscribe from these updates, visit the Settings page in the iThemes Security plugin menu.', 'better-wp-security' ),
74
  'security_guide' => esc_html__( 'Free WordPress Security Guide', 'better-wp-security' ),
75
  'security_guide_content' => sprintf( wp_kses( __( 'Learn simple WordPress security tips — including 3 kinds of security your site needs and 4 best security practices for keeping your WordPress site safe with our <a href="%s">free guide</a>.', 'better-wp-security' ), array( 'a' => array( 'href' => array() ) ) ), esc_url( 'https://ithemes.com/publishing/wordpress-security/' ) ),
@@ -235,4 +235,16 @@ final class ITSEC_Mail {
235
  private function get_template( $template ) {
236
  return file_get_contents( $this->template_path . $template );
237
  }
 
 
 
 
 
 
 
 
 
 
 
 
238
  }
69
  'support' => esc_html__( 'Support', 'better-wp-security' ),
70
  'pro' => esc_html__( 'Pro', 'better-wp-security' ),
71
  'support_content' => sprintf( wp_kses( __( 'Pro customers can contact <a href="%s">iThemes Helpdesk</a> for help. Our support team answers questions Monday – Friday, 8am – 5pm (CST).', 'better-wp-security' ), array( 'a' => array( 'href' => array() ) ) ), esc_url( 'https://members.ithemes.com/panel/helpdesk.php' ) ),
72
+ 'security_settings_link' => esc_url( self::filter_admin_page_url( ITSEC_Core::get_settings_page_url() ) ),
73
  'unsubscribe_link_text' => esc_html__( 'This email was generated by the iThemes Security plugin.', 'better-wp-security' ) . '<br>' . esc_html__( 'To unsubscribe from these updates, visit the Settings page in the iThemes Security plugin menu.', 'better-wp-security' ),
74
  'security_guide' => esc_html__( 'Free WordPress Security Guide', 'better-wp-security' ),
75
  'security_guide_content' => sprintf( wp_kses( __( 'Learn simple WordPress security tips — including 3 kinds of security your site needs and 4 best security practices for keeping your WordPress site safe with our <a href="%s">free guide</a>.', 'better-wp-security' ), array( 'a' => array( 'href' => array() ) ) ), esc_url( 'https://ithemes.com/publishing/wordpress-security/' ) ),
235
  private function get_template( $template ) {
236
  return file_get_contents( $this->template_path . $template );
237
  }
238
+
239
+ public static function filter_admin_page_url( $url ) {
240
+
241
+ /**
242
+ * Filter admin page URLs so modules can add any necessary security tokens.
243
+ *
244
+ * @since 6.4.0
245
+ *
246
+ * @param string $url
247
+ */
248
+ return apply_filters( 'itsec_notify_admin_page_url', $url );
249
+ }
250
  }
core/lockout.php CHANGED
@@ -634,14 +634,13 @@ final class ITSEC_Lockout {
634
 
635
  global $wpdb, $itsec_logger, $itsec_globals;
636
 
637
- $itsec_files = ITSEC_Core::get_itsec_files();
638
-
639
  $host_expiration = null;
640
  $user_expiration = null;
641
  $username = sanitize_text_field( trim( $username ) );
 
642
 
643
  // Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
644
- if ( $itsec_files->get_file_lock( 'lockout_' . $host . $user . $username ) ) {
645
 
646
  //Do we have a good host to lock out or not
647
  if ( ! is_null( $host ) && ITSEC_Lib::is_ip_whitelisted( sanitize_text_field( $host ) ) === false && ITSEC_Lib_IP_Tools::validate( $host ) ) {
@@ -802,12 +801,12 @@ final class ITSEC_Lockout {
802
 
803
  if ( $good_host !== false ) {
804
 
805
- $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
806
  $this->execute_lock();
807
 
808
  } else {
809
 
810
- $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
811
  $this->execute_lock( true );
812
 
813
  }
@@ -816,7 +815,7 @@ final class ITSEC_Lockout {
816
 
817
  }
818
 
819
- $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
820
 
821
  }
822
 
634
 
635
  global $wpdb, $itsec_logger, $itsec_globals;
636
 
 
 
637
  $host_expiration = null;
638
  $user_expiration = null;
639
  $username = sanitize_text_field( trim( $username ) );
640
+ $lock = 'lockout_' . $host . $user . $username;
641
 
642
  // Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
643
+ if ( ITSEC_Lib::get_lock( $lock, 180 ) ) {
644
 
645
  //Do we have a good host to lock out or not
646
  if ( ! is_null( $host ) && ITSEC_Lib::is_ip_whitelisted( sanitize_text_field( $host ) ) === false && ITSEC_Lib_IP_Tools::validate( $host ) ) {
801
 
802
  if ( $good_host !== false ) {
803
 
804
+ ITSEC_Lib::release_lock( $lock );
805
  $this->execute_lock();
806
 
807
  } else {
808
 
809
+ ITSEC_Lib::release_lock( $lock );
810
  $this->execute_lock( true );
811
 
812
  }
815
 
816
  }
817
 
818
+ ITSEC_Lib::release_lock( $lock );
819
 
820
  }
821
 
core/modules/admin-user/validator.php CHANGED
@@ -66,9 +66,7 @@ final class ITSEC_Admin_User_Validator extends ITSEC_Validator {
66
 
67
  global $wpdb;
68
 
69
- $itsec_files = ITSEC_Core::get_itsec_files();
70
-
71
- if ( $itsec_files->get_file_lock( 'admin_user' ) ) { //make sure it isn't already running
72
 
73
  //sanitize the username
74
  $new_user = sanitize_text_field( $username );
@@ -95,14 +93,14 @@ final class ITSEC_Admin_User_Validator extends ITSEC_Validator {
95
 
96
  }
97
 
98
- $itsec_files->release_file_lock( 'admin_user' );
99
 
100
  return true;
101
 
102
  }
103
 
104
  } elseif ( $username !== null ) { //username didn't validate
105
- $itsec_files->release_file_lock( 'admin_user' );
106
 
107
  return false;
108
 
@@ -148,7 +146,7 @@ final class ITSEC_Admin_User_Validator extends ITSEC_Validator {
148
  */
149
  do_action( 'itsec_change_admin_user_id', $new_user );
150
 
151
- $itsec_files->release_file_lock( 'admin_user' );
152
 
153
  return true;
154
 
66
 
67
  global $wpdb;
68
 
69
+ if ( ITSEC_Lib::get_lock( 'admin_user', 180 ) ) { //make sure it isn't already running
 
 
70
 
71
  //sanitize the username
72
  $new_user = sanitize_text_field( $username );
93
 
94
  }
95
 
96
+ ITSEC_Lib::release_lock( 'admin_user' );
97
 
98
  return true;
99
 
100
  }
101
 
102
  } elseif ( $username !== null ) { //username didn't validate
103
+ ITSEC_Lib::release_lock( 'admin_user' );
104
 
105
  return false;
106
 
146
  */
147
  do_action( 'itsec_change_admin_user_id', $new_user );
148
 
149
+ ITSEC_Lib::release_lock( 'admin_user' );
150
 
151
  return true;
152
 
core/modules/away-mode/js/settings-page.js CHANGED
@@ -19,5 +19,7 @@
19
  $(document).ready(function() {
20
  ithemesSecurityAwayModeSettingsPage.init();
21
  ithemesSecurityAwayModeSettingsPage.typeChanged();
 
 
22
  });
23
  })( jQuery );
19
  $(document).ready(function() {
20
  ithemesSecurityAwayModeSettingsPage.init();
21
  ithemesSecurityAwayModeSettingsPage.typeChanged();
22
+
23
+ itsecSettingsPage.events.on( 'modulesReloaded', ithemesSecurityAwayModeSettingsPage.init );
24
  });
25
  })( jQuery );
core/modules/backup/class-itsec-backup.php CHANGED
@@ -81,16 +81,15 @@ class ITSEC_Backup {
81
  * @return mixed false on error or nothing
82
  */
83
  public function do_backup( $one_time = false ) {
84
- $itsec_files = ITSEC_Core::get_itsec_files();
85
 
86
- if ( ! $itsec_files->get_file_lock( 'backup' ) ) {
87
  return new WP_Error( 'itsec-backup-do-backup-already-running', __( 'Unable to create a backup at this time since a backup is currently being created. If you wish to create an additional backup, please wait a few minutes before trying again.', 'better-wp-security' ) );
88
  }
89
 
90
 
91
  ITSEC_Lib::set_minimum_memory_limit( '256M' );
92
  $this->execute_backup( $one_time );
93
- $itsec_files->release_file_lock( 'backup' );
94
 
95
  switch ( $this->settings['method'] ) {
96
 
81
  * @return mixed false on error or nothing
82
  */
83
  public function do_backup( $one_time = false ) {
 
84
 
85
+ if ( ITSEC_Lib::get_lock( 'backup', 180 ) ) {
86
  return new WP_Error( 'itsec-backup-do-backup-already-running', __( 'Unable to create a backup at this time since a backup is currently being created. If you wish to create an additional backup, please wait a few minutes before trying again.', 'better-wp-security' ) );
87
  }
88
 
89
 
90
  ITSEC_Lib::set_minimum_memory_limit( '256M' );
91
  $this->execute_backup( $one_time );
92
+ ITSEC_Lib::release_lock( 'backup' );
93
 
94
  switch ( $this->settings['method'] ) {
95
 
core/modules/file-change/js/settings-page.js CHANGED
@@ -1,30 +1,38 @@
1
  jQuery( document ).ready( function ( $ ) {
2
- /**
3
- * Show the file tree in the settings.
4
- */
5
- $( '.jquery_file_tree' ).fileTree(
6
- {
7
- root : itsec_file_change_settings.ABSPATH,
8
- script : ajaxurl,
9
- expandSpeed : -1,
10
- collapseSpeed: -1,
11
- multiFolder : false
12
 
13
- }, function ( file ) {
14
 
15
- $( '#itsec-file-change-file_list' ).val( file.substring( itsec_file_change_settings.ABSPATH.length ) + "\n" + $( '#itsec-file-change-file_list' ).val() );
16
 
17
- }, function ( directory ) {
 
 
 
 
 
 
 
 
 
18
 
19
- $( '#itsec-file-change-file_list' ).val( directory.substring( itsec_file_change_settings.ABSPATH.length ) + "\n" + $( '#itsec-file-change-file_list' ).val() );
20
 
21
- }
22
- );
 
 
 
 
 
 
 
 
 
23
 
24
  /**
25
  * Performs a one-time file scan
26
  */
27
- $( '#itsec-file-change-one_time_check' ).click(function( e ) {
28
  e.preventDefault();
29
 
30
  //let user know we're working
1
  jQuery( document ).ready( function ( $ ) {
 
 
 
 
 
 
 
 
 
 
2
 
3
+ itsecSettingsPage.events.on( 'modulesReloaded', initializeFileTrees );
4
 
5
+ function initializeFileTrees() {
6
 
7
+ /**
8
+ * Show the file tree in the settings.
9
+ */
10
+ $( '.jquery_file_tree' ).fileTree(
11
+ {
12
+ root : itsec_file_change_settings.ABSPATH,
13
+ script : ajaxurl,
14
+ expandSpeed : -1,
15
+ collapseSpeed: -1,
16
+ multiFolder : false
17
 
18
+ }, function ( file ) {
19
 
20
+ $( '#itsec-file-change-file_list' ).val( file.substring( itsec_file_change_settings.ABSPATH.length ) + "\n" + $( '#itsec-file-change-file_list' ).val() );
21
+
22
+ }, function ( directory ) {
23
+
24
+ $( '#itsec-file-change-file_list' ).val( directory.substring( itsec_file_change_settings.ABSPATH.length ) + "\n" + $( '#itsec-file-change-file_list' ).val() );
25
+
26
+ }
27
+ );
28
+ }
29
+
30
+ initializeFileTrees();
31
 
32
  /**
33
  * Performs a one-time file scan
34
  */
35
+ $( document ).on( 'click', '#itsec-file-change-one_time_check', function( e ) {
36
  e.preventDefault();
37
 
38
  //let user know we're working
core/modules/file-change/scanner.php CHANGED
@@ -78,9 +78,7 @@ final class ITSEC_File_Change_Scanner {
78
 
79
  ITSEC_Lib::set_minimum_memory_limit( '256M' );
80
 
81
- $itsec_files = ITSEC_Core::get_itsec_files();
82
-
83
- if ( $itsec_files->get_file_lock( 'file_change', 300 ) ) { //make sure it isn't already running
84
 
85
  define( 'ITSEC_DOING_FILE_CHECK', true );
86
 
@@ -278,7 +276,7 @@ final class ITSEC_File_Change_Scanner {
278
  ITSEC_Modules::set_setting( 'file-change', 'show_warning', true );
279
  }
280
 
281
- $itsec_files->release_file_lock( 'file_change' );
282
 
283
  if ( $files_added_count > 0 || $files_changed_count > 0 || $files_deleted_count > 0 ) {
284
 
78
 
79
  ITSEC_Lib::set_minimum_memory_limit( '256M' );
80
 
81
+ if ( ITSEC_Lib::get_lock( 'file_change', 300 ) ) { //make sure it isn't already running
 
 
82
 
83
  define( 'ITSEC_DOING_FILE_CHECK', true );
84
 
276
  ITSEC_Modules::set_setting( 'file-change', 'show_warning', true );
277
  }
278
 
279
+ ITSEC_Lib::release_lock( 'file_change' );
280
 
281
  if ( $files_added_count > 0 || $files_changed_count > 0 || $files_deleted_count > 0 ) {
282
 
core/modules/file-change/settings-page.php CHANGED
@@ -155,7 +155,7 @@ final class ITSEC_File_Change_Settings_Page extends ITSEC_Module_Settings_Page {
155
  *
156
  * @since 4.0.0
157
  *
158
- * @return void
159
  */
160
  public function get_filetree_data( $data ) {
161
 
155
  *
156
  * @since 4.0.0
157
  *
158
+ * @return string
159
  */
160
  public function get_filetree_data( $data ) {
161
 
core/modules/file-writing/settings-page.php CHANGED
@@ -21,7 +21,8 @@ final class ITSEC_Core_Server_Config_Rules_Settings_Page extends ITSEC_Module_Se
21
  _e( 'There are no rules that need to be written.', 'better-wp-security' );
22
  } else {
23
  echo '<p>' . __( "The following rules need to be written to your server's config file. Please make sure to keep the comments in place." ) . '</p>';
24
- echo '<div class="itsec_server_config_rules"><pre>' . esc_html( $config ) . '</pre></div>';
 
25
  }
26
  }
27
  }
@@ -49,7 +50,8 @@ final class ITSEC_Core_WPConfig_File_Settings_Page extends ITSEC_Module_Settings
49
  _e( 'There is nothing that needs to be written to your <code>wp-config.php</code> file.', 'better-wp-security' );
50
  } else {
51
  echo '<p>' . __( "The following rules need to be written to your <code>wp-config.php</code> file. Please make sure to keep the comments in place." ) . '</p>';
52
- echo '<div class="itsec_rewrite_rules"><pre>' . esc_html( $config ) . '</pre></div>';
 
53
  }
54
  }
55
  }
21
  _e( 'There are no rules that need to be written.', 'better-wp-security' );
22
  } else {
23
  echo '<p>' . __( "The following rules need to be written to your server's config file. Please make sure to keep the comments in place." ) . '</p>';
24
+ echo '<div class="itsec_server_config_rules"><pre id="itsec-server-config-rules">' . esc_html( $config ) . '</pre></div>';
25
+ echo '<button class="button itsec-copy-trigger" data-copy-from="itsec-server-config-rules">' . esc_html__( 'Copy to Clipboard', 'better-wp-security' ) . '</button>';
26
  }
27
  }
28
  }
50
  _e( 'There is nothing that needs to be written to your <code>wp-config.php</code> file.', 'better-wp-security' );
51
  } else {
52
  echo '<p>' . __( "The following rules need to be written to your <code>wp-config.php</code> file. Please make sure to keep the comments in place." ) . '</p>';
53
+ echo '<div class="itsec_rewrite_rules"><pre id="itsec-rewrite-rules">' . esc_html( $config ) . '</pre></div>';
54
+ echo '<button class="button itsec-copy-trigger" data-copy-from="itsec-rewrite-rules">' . esc_html__( 'Copy to Clipboard', 'better-wp-security' ) . '</button>';
55
  }
56
  }
57
  }
core/modules/global/settings-page.php CHANGED
@@ -243,14 +243,6 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
243
  </td>
244
  </tr>
245
  <?php endif; ?>
246
- <tr>
247
- <th scope="row"><label for="itsec-global-lock_file"><?php _e( 'Disable File Locking', 'better-wp-security' ); ?></label></th>
248
- <td>
249
- <?php $form->add_checkbox( 'lock_file' ); ?>
250
- <label for="itsec-global-lock_file"><?php _e( 'Disable File Locking', 'better-wp-security' ); ?></label>
251
- <p class="description"><?php _e( 'iThemes Security uses file locking to prevent operations from being executed twice. We do not recommend disabling file locking unless your host prevents it from working correctly.', 'better-wp-security' ); ?></p>
252
- </td>
253
- </tr>
254
  <tr>
255
  <th scope="row"><label for="itsec-global-proxy_override"><?php _e( 'Override Proxy Detection', 'better-wp-security' ); ?></label></th>
256
  <td>
243
  </td>
244
  </tr>
245
  <?php endif; ?>
 
 
 
 
 
 
 
 
246
  <tr>
247
  <th scope="row"><label for="itsec-global-proxy_override"><?php _e( 'Override Proxy Detection', 'better-wp-security' ); ?></label></th>
248
  <td>
core/modules/global/validator.php CHANGED
@@ -19,7 +19,7 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
19
  }
20
 
21
 
22
- $this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_new_dashboard_notice', 'show_security_check', 'digest_last_sent', 'digest_messages', 'build', 'activation_timestamp' ) );
23
  $this->set_default_if_empty( array( 'log_location', 'nginx_file' ) );
24
 
25
 
@@ -28,7 +28,6 @@ class ITSEC_Global_Validator extends ITSEC_Validator {
28
  $this->sanitize_setting( 'bool', 'blacklist', __( 'Blacklist Repeat Offender', 'better-wp-security' ) );
29
  $this->sanitize_setting( 'bool', 'email_notifications', __( 'Email Lockout Notifications', 'better-wp-security' ) );
30
  $this->sanitize_setting( 'bool', 'allow_tracking', __( 'Allow Data Tracking', 'better-wp-security' ) );
31
- $this->sanitize_setting( 'bool', 'lock_file', __( 'Disable File Locking', 'better-wp-security' ) );
32
  $this->sanitize_setting( 'bool', 'proxy_override', __( 'Override Proxy Detection', 'better-wp-security' ) );
33
  $this->sanitize_setting( 'bool', 'hide_admin_bar', __( 'Hide Security Menu in Admin Bar', 'better-wp-security' ) );
34
  $this->sanitize_setting( 'bool', 'show_error_codes', __( 'Show Error Codes', 'better-wp-security' ) );
19
  }
20
 
21
 
22
+ $this->set_previous_if_empty( array( 'did_upgrade', 'log_info', 'show_new_dashboard_notice', 'show_security_check', 'digest_last_sent', 'digest_messages', 'build', 'activation_timestamp', 'lock_file' ) );
23
  $this->set_default_if_empty( array( 'log_location', 'nginx_file' ) );
24
 
25
 
28
  $this->sanitize_setting( 'bool', 'blacklist', __( 'Blacklist Repeat Offender', 'better-wp-security' ) );
29
  $this->sanitize_setting( 'bool', 'email_notifications', __( 'Email Lockout Notifications', 'better-wp-security' ) );
30
  $this->sanitize_setting( 'bool', 'allow_tracking', __( 'Allow Data Tracking', 'better-wp-security' ) );
 
31
  $this->sanitize_setting( 'bool', 'proxy_override', __( 'Override Proxy Detection', 'better-wp-security' ) );
32
  $this->sanitize_setting( 'bool', 'hide_admin_bar', __( 'Hide Security Menu in Admin Bar', 'better-wp-security' ) );
33
  $this->sanitize_setting( 'bool', 'show_error_codes', __( 'Show Error Codes', 'better-wp-security' ) );
core/modules/hide-backend/class-itsec-hide-backend.php CHANGED
@@ -26,6 +26,7 @@ class ITSEC_Hide_Backend {
26
  add_filter( 'network_site_url', array( $this, 'filter_generated_url' ), 100, 2 );
27
  add_filter( 'wp_redirect', array( $this, 'filter_redirect' ) );
28
  add_filter( 'comment_moderation_text', array( $this, 'filter_comment_moderation_text' ) );
 
29
 
30
  remove_action( 'template_redirect', 'wp_redirect_admin_locations', 1000 );
31
  }
@@ -270,6 +271,19 @@ class ITSEC_Hide_Backend {
270
  return $this->filter_generated_url( $location, $location );
271
  }
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  /**
274
  * Add the access token query arg to the URL.
275
  *
26
  add_filter( 'network_site_url', array( $this, 'filter_generated_url' ), 100, 2 );
27
  add_filter( 'wp_redirect', array( $this, 'filter_redirect' ) );
28
  add_filter( 'comment_moderation_text', array( $this, 'filter_comment_moderation_text' ) );
29
+ add_filter( 'itsec_notify_admin_page_url', array( $this, 'filter_notify_admin_page_urls' ) );
30
 
31
  remove_action( 'template_redirect', 'wp_redirect_admin_locations', 1000 );
32
  }
271
  return $this->filter_generated_url( $location, $location );
272
  }
273
 
274
+ /**
275
+ * Filter URLs to admin pages in emails to include the access token query arg.
276
+ *
277
+ * This ensures that users are redirected to the correct login page if they are logged-out.
278
+ *
279
+ * @param string $location
280
+ *
281
+ * @return string
282
+ */
283
+ public function filter_notify_admin_page_urls( $location ) {
284
+ return $this->add_token_to_url( $location, 'login' );
285
+ }
286
+
287
  /**
288
  * Add the access token query arg to the URL.
289
  *
core/modules/malware/js/settings-page.js CHANGED
@@ -7,8 +7,8 @@
7
  },
8
 
9
  bindEvents: function() {
10
- $( '#itsec-malware-scan-start' ).on( 'click', this.startScan );
11
- $( '.itsec-malware-scan-results-wrapper' ).on( 'click', '.itsec-malware-scan-toggle-details', this.toggleDetails );
12
  },
13
 
14
  toggleDetails: function( e ) {
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 ) {
core/modules/security-check/js/settings-page.js CHANGED
@@ -1,7 +1,6 @@
1
  jQuery( document ).ready( function ( $ ) {
2
- var $container = $( '#itsec-module-card-security-check' );
3
 
4
- $container.on( 'click', '#itsec-security-check-secure_site', function( e ) {
5
  e.preventDefault();
6
 
7
  $( '#itsec-security-check-secure_site' )
@@ -27,7 +26,7 @@ jQuery( document ).ready( function ( $ ) {
27
  } );
28
  } );
29
 
30
- $container.on( 'click', '.itsec-security-check-container-is-interactive :submit', function( e ) {
31
  e.preventDefault();
32
 
33
  var $button = $( this );
1
  jQuery( document ).ready( function ( $ ) {
 
2
 
3
+ $( document ).on( 'click', '#itsec-security-check-secure_site', function( e ) {
4
  e.preventDefault();
5
 
6
  $( '#itsec-security-check-secure_site' )
26
  } );
27
  } );
28
 
29
+ $( document ).on( 'click', '#itsec-module-card-security-check .itsec-security-check-container-is-interactive :submit', function( e ) {
30
  e.preventDefault();
31
 
32
  var $button = $( this );
core/modules/ssl/js/settings-page.js CHANGED
@@ -1,6 +1,6 @@
1
  (function( $ ) {
2
  $(document).ready(function() {
3
- $( '#itsec-ssl-admin' ).change(function( e ) {
4
  if ( this.checked && ! confirm( itsec_ssl.translations.ssl_warning ) ) {
5
  $(this).attr( 'checked', false );
6
  }
1
  (function( $ ) {
2
  $(document).ready(function() {
3
+ $( document ).on( 'change', '#itsec-ssl-admin', function( e ) {
4
  if ( this.checked && ! confirm( itsec_ssl.translations.ssl_warning ) ) {
5
  $(this).attr( 'checked', false );
6
  }
core/modules/strong-passwords/class-itsec-strong-passwords.php CHANGED
@@ -65,7 +65,7 @@ final class ITSEC_Strong_Passwords {
65
  }
66
 
67
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
68
-
69
  if ( isset( $user->role ) ) {
70
  $role = $this->get_canonical_role_from_role_and_user( $user->role, $user );
71
  } else {
@@ -140,7 +140,12 @@ final class ITSEC_Strong_Passwords {
140
  * @return string
141
  */
142
  private function get_canonical_role_from_role_and_user( $role, $user ) {
143
- $role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
 
 
 
 
 
144
  $user_caps = array();
145
 
146
  if ( isset( $user->caps ) ) {
65
  }
66
 
67
  require_once( ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-canonical-roles.php' );
68
+
69
  if ( isset( $user->role ) ) {
70
  $role = $this->get_canonical_role_from_role_and_user( $user->role, $user );
71
  } else {
140
  * @return string
141
  */
142
  private function get_canonical_role_from_role_and_user( $role, $user ) {
143
+ if ( empty( $role ) ) {
144
+ $role_caps = array();
145
+ } else {
146
+ $role_caps = array_keys( array_filter( wp_roles()->get_role( $role )->capabilities ) );
147
+ }
148
+
149
  $user_caps = array();
150
 
151
  if ( isset( $user->caps ) ) {
core/notify.php CHANGED
@@ -24,14 +24,7 @@ class ITSEC_Notify {
24
  }
25
 
26
  } else {
27
- $last_sent = ITSEC_Modules::get_setting( 'global', 'digest_last_sent' );
28
- $yesterday = ITSEC_Core::get_current_time_gmt() - DAY_IN_SECONDS;
29
-
30
- // Send digest if it has been 24 hours
31
- if ( $last_sent < $yesterday && false === get_site_transient( 'itsec_notification_running' ) ) {
32
- add_action( 'init', array( $this, 'init' ) );
33
- }
34
-
35
  }
36
 
37
  }
@@ -41,21 +34,43 @@ class ITSEC_Notify {
41
  *
42
  * @since 4.5
43
  *
44
- * @return void
45
  */
46
  public function init() {
47
- if ( is_404() || ( ( ! defined( 'ITSEC_NOTIFY_USE_CRON' ) || false === ITSEC_NOTIFY_USE_CRON ) && get_site_transient( 'itsec_notification_running' ) !== false ) ) {
48
- return;
 
 
 
 
 
 
 
 
49
  }
50
 
51
- if ( ( ! defined( 'ITSEC_NOTIFY_USE_CRON' ) || false === ITSEC_NOTIFY_USE_CRON ) ) {
52
- set_site_transient( 'itsec_notification_running', true, 3600 );
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  $result = $this->send_daily_digest();
57
 
58
- delete_site_transient( 'itsec_notification_running' );
59
 
60
  return $result;
61
  }
@@ -65,7 +80,7 @@ class ITSEC_Notify {
65
  *
66
  * @since 2.6.0
67
  *
68
- * @return
69
  */
70
  public function send_daily_digest() {
71
 
@@ -125,11 +140,11 @@ class ITSEC_Notify {
125
  }
126
 
127
 
128
- $mail->add_details_box( sprintf( wp_kses( __( 'For more details, <a href="%s"><b>visit your security logs</b></a>', 'better-wp-security' ), array( 'a' => array( 'href' => array() ), 'b' => array() ) ), ITSEC_Core::get_logs_page_url() ) );
129
  $mail->add_divider();
130
  $mail->add_large_text( esc_html__( 'Is your site as secure as it could be?', 'better-wp-security' ) );
131
  $mail->add_text( esc_html__( 'Ensure your site is using recommended settings and features with a security check.', 'better-wp-security' ) );
132
- $mail->add_button( esc_html__( 'Run a Security Check ✓', 'better-wp-security' ), ITSEC_Core::get_security_check_page_url() );
133
 
134
  if ( defined( 'ITSEC_DEBUG' ) && true === ITSEC_DEBUG ) {
135
  $mail->add_text( sprintf( esc_html__( 'Debug info (source page): %s', 'better-wp-security' ), esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) );
@@ -294,5 +309,4 @@ class ITSEC_Notify {
294
  return 'text/html';
295
 
296
  }
297
-
298
  }
24
  }
25
 
26
  } else {
27
+ add_action( 'init', array( $this, 'init' ) );
 
 
 
 
 
 
 
28
  }
29
 
30
  }
34
  *
35
  * @since 4.5
36
  *
37
+ * @return bool
38
  */
39
  public function init() {
40
+
41
+ if ( is_404() ) {
42
+ return false;
43
+ }
44
+
45
+ $use_cron = defined( 'ITSEC_NOTIFY_USE_CRON' ) && ITSEC_NOTIFY_USE_CRON;
46
+ $doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
47
+
48
+ if ( $doing_cron && ! $use_cron ) {
49
+ return false;
50
  }
51
 
52
+ if ( ! ITSEC_Lib::get_lock( 'daily-digest' ) ) {
53
+ return false;
54
  }
55
 
56
+ if ( ! $use_cron ) {
57
+
58
+ $global = ITSEC_Modules::get_settings_obj( 'global' );
59
+ ITSEC_Storage::reload();
60
+ $global->load();
61
+
62
+ $last_sent = $global->get('digest_last_sent' );
63
+ $yesterday = ITSEC_Core::get_current_time_gmt() - DAY_IN_SECONDS;
64
+
65
+ // Send digest if it has been 24 hours
66
+ if ( $last_sent > $yesterday ) {
67
+ return false;
68
+ }
69
+ }
70
 
71
  $result = $this->send_daily_digest();
72
 
73
+ ITSEC_Lib::release_lock( 'daily-digest' );
74
 
75
  return $result;
76
  }
80
  *
81
  * @since 2.6.0
82
  *
83
+ * @return bool
84
  */
85
  public function send_daily_digest() {
86
 
140
  }
141
 
142
 
143
+ $mail->add_details_box( sprintf( wp_kses( __( 'For more details, <a href="%s"><b>visit your security logs</b></a>', 'better-wp-security' ), array( 'a' => array( 'href' => array() ), 'b' => array() ) ), ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_logs_page_url() ) ) );
144
  $mail->add_divider();
145
  $mail->add_large_text( esc_html__( 'Is your site as secure as it could be?', 'better-wp-security' ) );
146
  $mail->add_text( esc_html__( 'Ensure your site is using recommended settings and features with a security check.', 'better-wp-security' ) );
147
+ $mail->add_button( esc_html__( 'Run a Security Check ✓', 'better-wp-security' ), ITSEC_Mail::filter_admin_page_url( ITSEC_Core::get_security_check_page_url() ) );
148
 
149
  if ( defined( 'ITSEC_DEBUG' ) && true === ITSEC_DEBUG ) {
150
  $mail->add_text( sprintf( esc_html__( 'Debug info (source page): %s', 'better-wp-security' ), esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) );
309
  return 'text/html';
310
 
311
  }
 
312
  }
core/response.php CHANGED
@@ -5,6 +5,7 @@ final class ITSEC_Response {
5
 
6
  private $response;
7
  private $errors;
 
8
  private $messages;
9
  private $success;
10
  private $js_function_calls;
@@ -69,6 +70,24 @@ final class ITSEC_Response {
69
 
70
  return $self->errors;
71
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  public static function get_error_count() {
74
  $self = self::get_instance();
@@ -156,6 +175,10 @@ final class ITSEC_Response {
156
  $self->add_js_function_call( 'reloadModule', $module );
157
  }
158
 
 
 
 
 
159
  public static function regenerate_wp_config() {
160
  $self = self::get_instance();
161
 
@@ -244,8 +267,9 @@ final class ITSEC_Response {
244
  'success' => $self->success,
245
  'response' => $self->response,
246
  'errors' => self::get_error_strings( $self->errors ),
 
247
  'messages' => $self->messages,
248
- 'functionCalls' => $self->js_function_calls,
249
  'redirect' => $self->redirect,
250
  'closeModal' => $self->close_modal,
251
  );
@@ -271,6 +295,7 @@ final class ITSEC_Response {
271
  public function reset_to_defaults() {
272
  $this->response = null;
273
  $this->errors = array();
 
274
  $this->messages = array();
275
  $this->success = true;
276
  $this->js_function_calls = array();
@@ -312,6 +337,32 @@ final class ITSEC_Response {
312
  return array( sprintf( __( 'Unknown error type received: %1$s.', 'better-wp-security' ), gettype( $error ) ) );
313
  }
314
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  public function shutdown() {
316
  self::maybe_regenerate_wp_config();
317
  self::maybe_regenerate_server_config();
5
 
6
  private $response;
7
  private $errors;
8
+ private $warnings;
9
  private $messages;
10
  private $success;
11
  private $js_function_calls;
70
 
71
  return $self->errors;
72
  }
73
+
74
+ public static function add_warnings( $warnings ) {
75
+ foreach ( $warnings as $warning ) {
76
+ self::add_warning( $warning );
77
+ }
78
+ }
79
+
80
+ public static function add_warning( $warning ) {
81
+ $self = self::get_instance();
82
+
83
+ $self->warnings[] = $warning;
84
+ }
85
+
86
+ public static function get_warnings() {
87
+ $self = self::get_instance();
88
+
89
+ return $self->warnings;
90
+ }
91
 
92
  public static function get_error_count() {
93
  $self = self::get_instance();
175
  $self->add_js_function_call( 'reloadModule', $module );
176
  }
177
 
178
+ public static function reload_all_modules() {
179
+ self::get_instance()->add_js_function_call( 'reloadAllModules' );
180
+ }
181
+
182
  public static function regenerate_wp_config() {
183
  $self = self::get_instance();
184
 
267
  'success' => $self->success,
268
  'response' => $self->response,
269
  'errors' => self::get_error_strings( $self->errors ),
270
+ 'warnings' => self::get_error_strings( $self->warnings ),
271
  'messages' => $self->messages,
272
+ 'functionCalls' => self::parse_js_function_calls_for_module_reloads(),
273
  'redirect' => $self->redirect,
274
  'closeModal' => $self->close_modal,
275
  );
295
  public function reset_to_defaults() {
296
  $this->response = null;
297
  $this->errors = array();
298
+ $this->warnings = array();
299
  $this->messages = array();
300
  $this->success = true;
301
  $this->js_function_calls = array();
337
  return array( sprintf( __( 'Unknown error type received: %1$s.', 'better-wp-security' ), gettype( $error ) ) );
338
  }
339
 
340
+ private static function parse_js_function_calls_for_module_reloads() {
341
+
342
+ $has_reload_all = false;
343
+
344
+ $function_calls = self::get_instance()->js_function_calls;
345
+
346
+ foreach ( $function_calls as $function_call ) {
347
+ if ( $function_call[0] === 'reloadAllModules' ) {
348
+ $has_reload_all = true;
349
+ break;
350
+ }
351
+ }
352
+
353
+ if ( ! $has_reload_all ) {
354
+ return $function_calls;
355
+ }
356
+
357
+ foreach ( $function_calls as $i => $function_call ) {
358
+ if ( $function_call[0] === 'reloadModule' ) {
359
+ unset( $function_calls[ $i ] );
360
+ }
361
+ }
362
+
363
+ return array_values( $function_calls );
364
+ }
365
+
366
  public function shutdown() {
367
  self::maybe_regenerate_wp_config();
368
  self::maybe_regenerate_server_config();
core/setup.php CHANGED
@@ -12,25 +12,33 @@ final class ITSEC_Setup {
12
  }
13
 
14
  public static function handle_deactivation() {
15
- if ( ! self::is_only_active_itsec_plugin() ) {
16
  return;
17
  }
18
 
19
  if ( defined( 'ITSEC_DEVELOPMENT' ) && ITSEC_DEVELOPMENT ) {
20
  // Set this in wp-config.php to run the uninstall routine on deactivate.
21
- self::handle_uninstall();
 
22
  } else {
23
  self::deactivate();
24
  }
25
  }
26
 
27
  public static function handle_uninstall() {
28
- if ( ! self::is_only_active_itsec_plugin() ) {
 
29
  return;
30
  }
31
 
32
  self::deactivate();
33
- self::uninstall();
 
 
 
 
 
 
34
  }
35
 
36
  public static function handle_upgrade( $build = false ) {
@@ -169,14 +177,38 @@ final class ITSEC_Setup {
169
 
170
  }
171
 
172
- private static function is_only_active_itsec_plugin() {
173
- $active_plugins = (array) get_option( 'active_plugins', array() );
174
 
175
- if ( is_multisite() ) {
176
- $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
177
- $active_plugins = array_merge( $active_plugins, array_keys( $network_plugins ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
 
 
 
 
 
 
 
 
 
 
 
 
180
  foreach ( $active_plugins as $active_plugin ) {
181
  $file = basename( $active_plugin );
182
 
@@ -188,6 +220,18 @@ final class ITSEC_Setup {
188
  return false;
189
  }
190
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  private static function upgrade_from_bwps() {
192
  global $itsec_bwps_options, $wpdb;
193
 
12
  }
13
 
14
  public static function handle_deactivation() {
15
+ if ( ! self::is_at_least_one_itsec_version_active() ) {
16
  return;
17
  }
18
 
19
  if ( defined( 'ITSEC_DEVELOPMENT' ) && ITSEC_DEVELOPMENT ) {
20
  // Set this in wp-config.php to run the uninstall routine on deactivate.
21
+ self::deactivate();
22
+ self::uninstall();
23
  } else {
24
  self::deactivate();
25
  }
26
  }
27
 
28
  public static function handle_uninstall() {
29
+
30
+ if ( self::is_at_least_one_itsec_version_active() ) {
31
  return;
32
  }
33
 
34
  self::deactivate();
35
+
36
+ $uninstalled = self::get_version_being_uninstalled();
37
+ $loaded = self::get_version_loaded();
38
+
39
+ if ( $uninstalled === $loaded ) {
40
+ self::uninstall();
41
+ }
42
  }
43
 
44
  public static function handle_upgrade( $build = false ) {
177
 
178
  }
179
 
180
+ private static function get_version_being_uninstalled() {
 
181
 
182
+ if ( doing_action( 'uninstall_better-wp-security/better-wp-security.php' ) ) {
183
+ return 'free';
184
+ }
185
+
186
+ if ( doing_action( 'uninstall_ithemes-security-pro/ithemes-security-pro.php' ) ) {
187
+ return 'pro';
188
+ }
189
+
190
+ return '';
191
+ }
192
+
193
+ private static function get_version_loaded() {
194
+
195
+ $plugin_dir = plugin_basename( dirname( dirname( __FILE__ ) ) );
196
+
197
+ if ( $plugin_dir === 'better-wp-security' ) {
198
+ return 'free';
199
  }
200
 
201
+ if ( $plugin_dir === 'ithemes-security-pro' ) {
202
+ return 'pro';
203
+ }
204
+
205
+ return '';
206
+ }
207
+
208
+ private static function is_at_least_one_itsec_version_active() {
209
+
210
+ $active_plugins = self::get_active_plugins();
211
+
212
  foreach ( $active_plugins as $active_plugin ) {
213
  $file = basename( $active_plugin );
214
 
220
  return false;
221
  }
222
 
223
+ private static function get_active_plugins() {
224
+
225
+ $active_plugins = (array) get_option( 'active_plugins', array() );
226
+
227
+ if ( is_multisite() ) {
228
+ $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
229
+ $active_plugins = array_merge( $active_plugins, array_keys( $network_plugins ) );
230
+ }
231
+
232
+ return $active_plugins;
233
+ }
234
+
235
  private static function upgrade_from_bwps() {
236
  global $itsec_bwps_options, $wpdb;
237
 
history.txt CHANGED
@@ -662,3 +662,10 @@
662
  Enhancement: Hide Backend functions purely in PHP code now rather than relying half on PHP code and half on .htaccess and nginx.conf modifications. This allows Hide Backend to function on web servers and server configurations that it was previously not compatible with.
663
  Misc: Updated or added phpDoc to many functions.
664
  Misc: Updated Disable File Locking description.
 
 
 
 
 
 
 
662
  Enhancement: Hide Backend functions purely in PHP code now rather than relying half on PHP code and half on .htaccess and nginx.conf modifications. This allows Hide Backend to function on web servers and server configurations that it was previously not compatible with.
663
  Misc: Updated or added phpDoc to many functions.
664
  Misc: Updated Disable File Locking description.
665
+ 6.4.0 - 2017-07-24 - Chris Jean & Timothy Jacobs
666
+ Enhancement: Replaced file locking with database locking. This method of locking is compatible with all systems as it does not require the ability to write files. It also allows for locking to work on sites that have multiple front-end servers with a shared database. Since file locking is no longer used, the Global Settings > Disable File Locking setting was removed.
667
+ Enhancement: Add "Copy to Clipboard" functionality for server and wp-config rules.
668
+ Bug Fix: Prevent 404s when following links in email notifications on a site with Hide Backend enabled.
669
+ Bug Fix: Ensure uninstall process is not run when another version of iThemes Security is still active.
670
+ Bug Fix: Fixed method of working around Hide Backend.
671
+ Bug Fix: Warnings are no longer generated when saving a user profile with a role of "No role for this site" selected.
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: ithemes, chrisjean, gerroald, mattdanner
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.6
5
  Tested up to: 4.8
6
- Stable tag: 6.3.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -188,6 +188,14 @@ Free support may be available with the help of the community in the <a href="htt
188
 
189
  == Changelog ==
190
 
 
 
 
 
 
 
 
 
191
  = 6.3.0 =
192
  * Important: The way that Hide Backend functions changes in this release. Previously, if your Hide Backend Login Slug was wplogin, going to example.com/wplogin would result in the URL remaining example.com/wplogin. The new implementation of this feature results in a redirect to a URL that looks as follows: example.com/wp-login.php?itsec-hb-token=wplogin. While this may not be desireable for some users, this change was necessary to fix longstanding compatibility issues with other plugins. Once you access the login page using the Login Slug page, a cookie is set with an expiration time of one hour. As long as the cookie remains, you can access example.com/wp-login.php without having to access the Hide Backend Login Slug first. If you wish to confirm that Hide Backend is working properly on your site, opening up a private browsing window is a quick way to test without having to log out and clear cookies.
193
  * New Feature: Added support for iThemes Sync to run the Security Check feature from inside the Sync service.
@@ -1701,5 +1709,5 @@ This release is a complete rewrite from the ground up. Special thanks to Cory Mi
1701
 
1702
  == Upgrade Notice ==
1703
 
1704
- = 6.3.0 =
1705
- Version 6.3.0 contains important bug and compatibility fixes. It is recommended for all users.
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.6
5
  Tested up to: 4.8
6
+ Stable tag: 6.4.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.4.0 =
192
+ * Enhancement: Replaced file locking with database locking. This method of locking is compatible with all systems as it does not require the ability to write files. It also allows for locking to work on sites that have multiple front-end servers with a shared database. Since file locking is no longer used, the Global Settings > Disable File Locking setting was removed.
193
+ * Enhancement: Add "Copy to Clipboard" functionality for server and wp-config rules.
194
+ * Bug Fix: Prevent 404s when following links in email notifications on a site with Hide Backend enabled.
195
+ * Bug Fix: Ensure uninstall process is not run when another version of iThemes Security is still active.
196
+ * Bug Fix: Fixed method of working around Hide Backend.
197
+ * Bug Fix: Warnings are no longer generated when saving a user profile with a role of "No role for this site" selected.
198
+
199
  = 6.3.0 =
200
  * Important: The way that Hide Backend functions changes in this release. Previously, if your Hide Backend Login Slug was wplogin, going to example.com/wplogin would result in the URL remaining example.com/wplogin. The new implementation of this feature results in a redirect to a URL that looks as follows: example.com/wp-login.php?itsec-hb-token=wplogin. While this may not be desireable for some users, this change was necessary to fix longstanding compatibility issues with other plugins. Once you access the login page using the Login Slug page, a cookie is set with an expiration time of one hour. As long as the cookie remains, you can access example.com/wp-login.php without having to access the Hide Backend Login Slug first. If you wish to confirm that Hide Backend is working properly on your site, opening up a private browsing window is a quick way to test without having to log out and clear cookies.
201
  * New Feature: Added support for iThemes Sync to run the Security Check feature from inside the Sync service.
1709
 
1710
  == Upgrade Notice ==
1711
 
1712
+ = 6.4.0 =
1713
+ Version 6.4.0 contains important bug fixes and enhancements. It is recommended for all users.