'; modal_content += SecuPressi18nModules.firstScanTitle; modal_content += '
'; modal_content += ''; modal_content += SecuPressi18nModules.firstScanText; modal_content += '
'; modal_content += ''; modal_content += ''; modal_content += ''; modal_content += ''; modal_content += ''; modal_content += ''; modal_content += SecuPressi18nModules.firstScanButton; modal_content += ''; modal_content += ''; modal_content += '
'; swal2( jQuery.extend( {}, SecuPress.swal2Defaults, { title: modal_title, html: modal_content, type: null, width: 400, showConfirmButton: false, showCloseButton: true, showCancelButton: false, customClass: 'wpmedia-swal2 secupress-swal2 secupress-swal-dark-header secupress-text-center' } ) ); } // Tools =========================================================================================== /** * Disable a button that calls an ajax action. * - Add a "working" class, so that the spinner can be displayed. * - Add a "aria-disabled" attribute. * - If it's a link: add a "disabled" attribute. If it's a button or input: add a "disabled" attribute. * - Change the button text if a "data-loading-i18n" attribute is present. * - Use `wp.a11y.speak` if a text is provided. * - Set a `SecuPress.doingAjax` attribute to `true`. * * @since 1.0 * * @param (object) $button jQuery object of the button. * @param (string) speak Text for `wp.a11y.speak`. * @param (string) ajaxID An identifier used for `SecuPress.doingAjax`. Default: "global". */ function secupressDisableAjaxButton( $button, speak, ajaxID ) { var text = $button.attr( "data-loading-i18n" ), isButton = $button.get( 0 ).nodeName.toLowerCase(), value; ajaxID = undefined !== ajaxID ? ajaxID : "global"; SecuPress.doingAjax[ ajaxID ] = true; isButton = isButton === "button" || isButton === "input"; if ( undefined !== text && text ) { if ( isButton ) { value = $button.val(); if ( undefined !== value && value ) { $button.val( text ); } else { if ( $button.find('.text').length ) { $button.find('.text').text( text ); } else { $button.text( text ); } } } else { if ( $button.find('.text').length ) { $button.find('.text').text( text ); } else { $button.text( text ); } } if ( undefined === speak || ! speak ) { speak = text; } } if ( isButton ) { $button.addClass( "working" ).attr( { "disabled": "disabled", "aria-disabled": "true" } ); } else { $button.addClass( "disabled working" ).attr( "aria-disabled", "true" ); } if ( wp.a11y && wp.a11y.speak && undefined !== speak && speak ) { wp.a11y.speak( speak ); } } /** * Enable a button that calls an ajax action. * - Remove the "working" class, so that the spinner can be hidden again. * - Remove the "aria-disabled" attribute. * - If it's a link: remove the "disabled" attribute. If it's a button or input: remove the "disabled" attribute. * - Change the button text if a "data-original-i18n" attribute is present. * - Use `wp.a11y.speak` if a text is provided. * - Set a `SecuPress.doingAjax` attribute to `false`. * * @since 1.0 * * @param (object) $button jQuery object of the button. * @param (string) speak Text for `wp.a11y.speak`. * @param (string) ajaxID An identifier used for `SecuPress.doingAjax`. Default: "global". */ function secupressEnableAjaxButton( $button, speak, ajaxID ) { var text, isButton, value; if ( undefined !== $button && $button && $button.length ) { text = $button.attr( "data-original-i18n" ); isButton = $button.get( 0 ).nodeName.toLowerCase(); isButton = "button" === isButton || "input" === isButton; if ( undefined !== text && text ) { if ( isButton ) { value = $button.val(); if ( undefined !== value && value ) { $button.val( text ); } else { if ( $button.find('.text').length ) { $button.find('.text').text( text ); } else { $button.text( text ); } } } else { if ( $button.find('.text').length ) { $button.find('.text').text( text ); } else { $button.text( text ); } } } if ( isButton ) { $button.removeClass( "working" ).removeAttr( "disabled aria-disabled" ); } else { $button.removeClass( "disabled working" ).removeAttr( "aria-disabled" ); } } if ( wp.a11y && wp.a11y.speak && undefined !== speak && speak ) { wp.a11y.speak( speak ); } ajaxID = undefined !== ajaxID ? ajaxID : "global"; SecuPress.doingAjax[ ajaxID ] = false; } /** * Before doing an ajax call, do some tests: * - test if we have an URL. * - if the event is "keyup", test if the key is the Space bar or Enter. * - test another ajax call is not running. * Also prevent default event. * * @since 1.0 * * @param (string) href The URL. * @param (object) e The jQuery event object. * @param (string) ajaxID An identifier used for `SecuPress.doingAjax`. Default: "global". * * @return (bool|string) False on failure, the ajax URL on success. */ function secupressPreAjaxCall( href, e, ajaxID ) { e.preventDefault(); if ( undefined === href || ! href ) { return false; } if ( "keyup" === e.type && ! secupressIsSpaceOrEnterKey( e ) ) { return false; } ajaxID = undefined !== ajaxID ? ajaxID : "global"; if ( typeof SecuPress.doingAjax[ ajaxID ] === "undefined" ) { SecuPress.doingAjax[ ajaxID ] = false; } if ( SecuPress.doingAjax[ ajaxID ] ) { return false; } return href.replace( "admin-post.php", "admin-ajax.php" ); } /** * Display an error message via Sweet Alert and re-enable the button. * * @since 1.0 * * @param (object) $button jQuery object of the button. * @param (string) text Text for swal2 + `wp.a11y.speak`. * @param (string) ajaxID An identifier used for `SecuPress.doingAjax`. Default: "global". */ function secupressDisplayAjaxError( $button, text, ajaxID ) { if ( undefined === text ) { text = SecuPressi18nModules.unknownError; } swal2( jQuery.extend( {}, SecuPress.swal2Defaults, { title: SecuPressi18nModules.error, html: text, type: "error" } ) ); ajaxID = undefined !== ajaxID ? ajaxID : "global"; secupressEnableAjaxButton( $button, text, ajaxID ); } /** * Display a success message via Sweet Alert and re-enable the button. * * @since 1.0 * * @param (object) $button jQuery object of the button. * @param (string) text Text for swal2 + `wp.a11y.speak`. * @param (string) ajaxID An identifier used for `SecuPress.doingAjax`. Default: "global". */ function secupressDisplayAjaxSuccess( $button, text, ajaxID ) { if ( undefined === text ) { text = null; } swal2( jQuery.extend( {}, SecuPress.swal2Defaults, { title: SecuPressi18nModules.done, html: text, type: "success", timer: 4000 } ) ); ajaxID = undefined !== ajaxID ? ajaxID : "global"; secupressEnableAjaxButton( $button, text, ajaxID ); } // Roles: at least one role must be chosen. ======================================================== (function($, d, w, undefined) { if ( "function" === typeof document.createElement( "input" ).checkValidity ) { $( ".affected-role-row :checkbox" ).on( "click.secupress", function() { this.setCustomValidity( '' ); if ( 0 === $( '[name="' + this.name + '"]:checked' ).length ) { this.setCustomValidity( SecuPressi18nModules.selectOneRoleMinimum ); $( "#secupress-module-form-settings [type='submit']" ).first().trigger( "click.secupress" ); } } ); } else { $( ".affected-role-row p.warning" ).removeClass( "hide-if-js" ); } } )(jQuery, document, window); // Radioboxes: 1 checked at most. ================================================================== (function($, d, w, undefined) { $( ".radiobox" ).on( "click.secupress", function() { $( '[name="' + this.name + '"]:checked' ).not( this ).removeAttr( "checked" ).trigger( "change" ); } ); } )(jQuery, document, window); // Show/Hide panels, depending on some other field value. ========================================== (function($, d, w, undefined) { var $depends = $( "#wpbody-content" ).find( '[class*="depends-"]' ), // Rows that will open/close. dependsIds = {}, // IDs of the checkboxes, radios, etc that will trigger a panel open/close. dependsRadioNames = {}; // names of the radios. $( '.secupress-setting-row_move-login_slug-login' ) .on( 'secupressbeforeshow secupressinitshow', function() { $( '#move-login_slug-login' ).attr( { 'required': 'required', 'aria-required': 'true' } ); } ) .on( 'secupressafterhide secupressinithide', function() { $( '#move-login_slug-login' ).removeAttr( 'required aria-required' ); } ); $depends.each( function() { var classes = $( this ).attr( "class" ).replace( /^\s+|\s+$/g, "" ).replace( /\s+/, " " ).split( " " ); $.each( classes, function( i, id ) { var $input, // input element inputTagName, // input tag name inputTypeAttr, // input type inputNameAttr, // input name inputIsValid = false; // If the class is not a "depends-XXXXXX", bail out. if ( 0 !== id.indexOf( "depends-" ) ) { return true; } id = id.substr( 8 ); // If the ID was previously delt with, bail out. if ( "undefined" !== typeof dependsIds[ id ] ) { return true; } dependsIds[ id ] = 1; $input = $( "#" + id ); // Uh? The input doesn't exist? if ( ! $input.length ) { return true; } // We need to know which type of input we deal with, the way we deal with it is not the same. inputTagName = $input.get( 0 ).nodeName.toLowerCase(); if ( "input" === inputTagName ) { inputTypeAttr = $input.attr( "type" ).toLowerCase(); if ( "checkbox" === inputTypeAttr || "radio" === inputTypeAttr ) { inputIsValid = true; } } else if ( "button" === inputTagName ) { inputIsValid = true; } // Only checkboxes, radios groups and buttons so far. if ( ! inputIsValid ) { return true; } // Attach the events. // Buttons if ( "button" === inputTagName ) { $input.on( "click.secupress", function() { var id = $( this ).attr( "id" ); $( this ).toggleClass( 'open' ); $( ".depends-" + id ).toggle( 250 ); } ); } // Radios else if ( "radio" === inputTypeAttr ) { inputNameAttr = $input.attr( "name" ); // If the name was previously delt with, bail out. if ( "undefined" !== typeof dependsRadioNames[ inputNameAttr ] ) { return true; } dependsRadioNames[ inputNameAttr ] = 1; $( '[name="' + inputNameAttr + '"]' ).on( "change init.secupress", function( e ) { var $this = $( this ), $toShow = $( ".depends-" + $this.attr( "id" ) ), // Elements to show. toHide = [], // Elements to hide. tempo = "init" === e.type && "secupress" === e.namespace ? 0 : 250; // On page load, no animation. // The radio is checked: open the desired boxes if not visible. $toShow.not( ":visible" ).trigger( "secupressbeforeshow" ).show( tempo, function() { $( this ).trigger( "secupressaftershow" ); } ); // Find boxes to hide. $( '[name="' + $this.attr( "name" ) + '"]' ).not( $this ).each( function() { toHide.push( ".depends-" + $( this ).attr( "id" ).replace( /^\s+|\s+$/g, "" ) ); } ); $( toHide.join( "," ) ).not( $toShow ).filter( ":visible" ).trigger( "secupressbeforehide" ).hide( tempo, function() { $( this ).trigger( "secupressafterhide" ); } ); } ).filter( ":checked" ).trigger( "init.secupress" ); } // Checkboxes else if ( "checkbox" === inputTypeAttr ) { $input.on( "change init.secupress", function( e ) { var $this = $( this ), id = $this.attr( "id" ), $elems = $( ".depends-" + id ), // Elements to hide or show. tempo = "init" === e.type && "secupress" === e.namespace ? 0 : 250; // On page load, no animation. // Uh? No rows? if ( ! $elems.length ) { return true; } // The checkbox is checked: open if not visible. if ( $this.is( ":checked" ) ) { $elems.not( ":visible" ).trigger( "secupressbeforeshow" ).show( tempo, function() { $( this ).trigger( "secupressaftershow" ); } ); } // The checkbox is not checked: close if visible and no other checkboxes that want this row to be open is checked. else { $elems.filter( ":visible" ).each( function() { var $this = $( this ), classes = $this.attr( "class" ).replace( /^\s+|\s+$/g, "" ).replace( /\s+/, " " ).split( " " ), others = []; // Other checkboxes $.each( classes, function( i, v ) { if ( "depends-" + id !== v && 0 === v.indexOf( "depends-" ) ) { others.push( "#" + v.substr( 8 ) + ":checked" ); } } ); others = others.join( "," ); if ( ! $( others ).length ) { $this.trigger( "secupressbeforehide" ).hide( tempo, function() { $( this ).trigger( "secupressafterhide" ); } ); } } ); } } ).filter( ":checked" ).trigger( "init.secupress" ); if ( $input.is( ':checked' ) ) { $( '.depends-' + id ).filter( ':visible' ).trigger( 'secupressinitshow' ); } else { $( '.depends-' + id ).not( ':visible' ).trigger( 'secupressinithide' ); } } } ); } ); } )(jQuery, document, window); // Move Login ====================================================================================== (function($, d, w, undefined) { var cache = {}, timeout = {}; function secupressUpdateMoveLoginSlug( value, $input ) { $( $input ).closest( '.secupress-text-label' ).find( '.dynamic-login-url-slug' ).text( value ); } if ( SecuPressi18nModules.moveLoginNonce ) { $( '.dynamic-login-url-slug' ).closest( '.secupress-text-label' ).find( '[type="text"]' ).on( 'keyup', function( e ) { var elem, value, action, // Shift, Control, Alt, Meta, Escape. keys = [ 16, 17, 18, 224, 27 ]; if ( $.inArray( e.which, keys ) !== -1 ) { return false; } elem = this; value = elem.value.replace( /^\s+|\s+$/g, '' ); action = elem.id.replace( 'move-login_slug-', '' ); if ( typeof timeout[ elem.id ] !== undefined ) { w.clearTimeout( timeout[ elem.id ] ); } if ( 'login' !== action ) { value = '' === value ? action : value; } if ( typeof cache[ value ] === 'string' ) { secupressUpdateMoveLoginSlug( cache[ value ], elem ); return true; } timeout[ elem.id ] = w.setTimeout( function() { var $elem = $( elem ).addClass( 'ui-autocomplete-loading' ), params = { 'action': 'sanitize_move_login_slug', 'slug': value, 'default': action, '_wpnonce': SecuPressi18nModules.moveLoginNonce }; $.getJSON( ajaxurl, params ) .done( function( r ) { if ( $.isPlainObject( r ) && r.success ) { cache[ value ] = r.data; secupressUpdateMoveLoginSlug( r.data, $elem ); } else { secupressUpdateMoveLoginSlug( '--' + SecuPressi18nModules.error + '--', $elem ); } } ) .fail( function() { secupressUpdateMoveLoginSlug( '--' + SecuPressi18nModules.error + '--', $elem ); } ) .always( function() { $elem.removeClass( 'ui-autocomplete-loading' ); } ); }, 300 ); } ); } } )(jQuery, document, window); // Backups ========================================================================================= (function($, d, w, undefined) { function secupressUpdateAvailableBackupCounter( r ) { $( "#secupress-available-backups" ).text( r.data.countText ); } function secupressUpdateBackupVisibility() { if ( 0 === $( "#form-delete-backups" ).find( ".secupress-large-row" ).length ) { $( "#form-delete-backups" ).hide(); $( "#secupress-no-backups" ).show(); } else { $( "#secupress-no-backups" ).hide(); $( "#form-delete-backups" ).show(); } } // Delete all backups. function secupressDeleteAllBackups( $button, href ) { secupressDisableAjaxButton( $button, SecuPressi18nModules.deletingAllText, "backup" ); $.getJSON( href ) .done( function( r ) { var $fieldset, $legend; if ( $.isPlainObject( r ) && r.success ) { swal2.close(); $fieldset = $button.closest( "form" ).find( "fieldset" ); $legend = $fieldset.children( "legend" ); $fieldset.text( "" ).prepend( $legend ); secupressUpdateBackupVisibility(); secupressEnableAjaxButton( $button, SecuPressi18nModules.deletedAllText, "backup" ); } else { secupressDisplayAjaxError( $button, SecuPressi18nModules.deleteAllImpossible, "backup" ); } } ) .fail( function() { secupressDisplayAjaxError( $button, null, "backup" ); } ); } // Delete one backup. function secupressDeleteOneBackup( $button, href ) { secupressDisableAjaxButton( $button, SecuPressi18nModules.deletingOneText, "backup" ); $.getJSON( href ) .done( function( r ) { if ( $.isPlainObject( r ) && r.success ) { swal2.close(); $button.closest( ".secupress-large-row" ).css( "backgroundColor", SecuPress.deletedRowColor ).hide( "normal", function() { $( this ).remove(); secupressUpdateAvailableBackupCounter( r ); secupressUpdateBackupVisibility(); SecuPress.doingAjax.backup = false; } ); if ( wp.a11y && wp.a11y.speak ) { wp.a11y.speak( SecuPressi18nModules.deletedOneText ); } } else { secupressDisplayAjaxError( $button, SecuPressi18nModules.deleteOneImpossible, "backup" ); } } ) .fail( function() { secupressDisplayAjaxError( $button, null, "backup" ); } ); } // Do a DB backup. function secupressDoDbBackup( $button, href ) { secupressDisableAjaxButton( $button, SecuPressi18nModules.backupingText, 'backup' ); $.post( href, $button.closest( "form" ).serializeArray() ) .done( function( r ) { if ( $.isPlainObject( r ) ) { if ( r.success ) { $( r.data.elemRow ).addClass( "hidden" ).css( "backgroundColor", SecuPress.addedRowColor ).insertAfter( "#form-delete-backups legend" ).show( "normal", function() { $( this ).css( "backgroundColor", "" ); } ); secupressUpdateAvailableBackupCounter( r ); secupressUpdateBackupVisibility(); secupressEnableAjaxButton( $button, SecuPressi18nModules.backupedText, "backup" ); } else { r.data = r.data ? r.data : SecuPressi18nModules.backupImpossible; secupressDisplayAjaxError( $button, r.data, "backup" ); } } else { secupressDisplayAjaxError( $button, SecuPressi18nModules.backupImpossible, "backup" ); } } ) .fail( function() { secupressDisplayAjaxError( $button, null, "backup" ); } ); } // Do a files backup. function secupressDoFilesBackup( $button, href ) { secupressDisableAjaxButton( $button, SecuPressi18nModules.backupingText, 'backup' ); $.post( href, $button.closest( "form" ).serializeArray() ) .done( function( r ) { if ( $.isPlainObject( r ) ) { if ( r.success ) { $( r.data.elemRow ).addClass( "hidden" ).css( "backgroundColor", SecuPress.addedRowColor ).insertAfter( "#form-delete-backups legend" ).show( "normal", function() { $( this ).css( "backgroundColor", "" ); } ); secupressUpdateAvailableBackupCounter( r ); secupressUpdateBackupVisibility(); $( "#ignored_directories" ).val( r.data.ignoredFiles ); secupressEnableAjaxButton( $button, SecuPressi18nModules.backupedText, "backup" ); } else { r.data = r.data ? r.data : SecuPressi18nModules.backupImpossible; secupressDisplayAjaxError( $button, r.data, "backup" ); } } else { secupressDisplayAjaxError( $button, SecuPressi18nModules.backupImpossible, "backup" ); } } ) .fail( function() { secupressDisplayAjaxError( $button, null, "backup" ); } ); } // Ajax call that delete all backups. $( "#submit-delete-backups" ).on( "click.secupress", function( e ) { var $this = $( this ), href = secupressPreAjaxCall( $this.closest( "form" ).attr( "action" ), e ); if ( ! href ) { return; } if ( "function" === typeof w.swal2 ) { swal2( $.extend( {}, SecuPress.swal2Defaults, SecuPress.swal2ConfirmDefaults, { text: SecuPressi18nModules.confirmDeleteBackups, confirmButtonText: SecuPressi18nModules.yesDeleteAll, type: "question", reverseButtons: true } ) ).then( function ( isConfirm ) { if ( isConfirm ) { swal2.enableLoading(); secupressDeleteAllBackups( $this, href ); } } ); } else if ( w.confirm( SecuPressi18nModules.confirmTitle + "\n" + SecuPressi18nModules.confirmDeleteBackups ) ) { secupressDeleteAllBackups( $this, href ); } } ).removeAttr( "disabled aria-disabled" ); // Ajax call that delete one Backup. $( "body" ).on( "click.secupress keyup", ".a-delete-backup", function( e ) { var $this = $( this ), href = secupressPreAjaxCall( $this.attr( "href" ), e ); if ( ! href ) { return; } if ( "function" === typeof w.swal2 ) { swal2( $.extend( {}, SecuPress.swal2Defaults, SecuPress.swal2ConfirmDefaults, { text: SecuPressi18nModules.confirmDeleteBackup, confirmButtonText: SecuPressi18nModules.yesDeleteOne, type: "question", reverseButtons: true } ) ).then( function ( isConfirm ) { if ( isConfirm ) { swal2.enableLoading(); secupressDeleteOneBackup( $this, href ); } } ); } else if ( w.confirm( SecuPressi18nModules.confirmTitle + "\n" + SecuPressi18nModules.confirmDeleteBackup ) ) { secupressDeleteOneBackup( $this, href ); } } ); // Ajax call that does a DB Backup. $( "#submit-backup-db" ).on( "click.secupress", function( e ) { var $this = $( this ), href = secupressPreAjaxCall( $this.closest( "form" ).attr( "action" ), e ); if ( href ) { secupressDoDbBackup( $this, href ); } } ).removeAttr( 'disabled aria-disabled' ); // Ajax call that does a files Backup. $( "#submit-backup-files" ).on( "click.secupress", function( e ) { var $this = $( this ), href = secupressPreAjaxCall( $this.closest( "form" ).attr( "action" ), e ); if ( href ) { secupressDoFilesBackup( $this, href ); } } ).removeAttr( 'disabled aria-disabled' ); } )(jQuery, document, window); // Countries ======================================================================================= (function($, d, w, undefined) { function secupress_set_indeterminate_state( code ) { var all_boxes = $( "[data-code-country='" + code + "']" ).length, checked_boxes = $( "[data-code-country='" + code + "']:checked" ).length; if ( checked_boxes === all_boxes ) { $( "[value='continent-" + code + "']" ).prop( { "checked": true, "indeterminate": false } ).css( "-webkit-appearance", "none" ); } else if ( 0 === checked_boxes ) { $( "[value='continent-" + code + "']" ).prop( { "checked": false, "indeterminate": false } ).css( "-webkit-appearance", "none" ); } else { $( "[value='continent-" + code + "']" ).prop( { "checked": false, "indeterminate": true } ).css( "-webkit-appearance", "checkbox" ); } } $( ".continent input" ).on( "click.secupress", function( e ) { var $this = $( this ), val = $this.css( "-webkit-appearance", "none" ).val().replace( "continent-", "" ); $( ".depends-geoip-system_type_blacklist.depends-geoip-system_type_whitelist [data-code-country='" + val + "']" ).prop( "checked", $this.is( ":checked" ) ); } ); $( "[data-code-country]" ).on( "click.secupress", function( e ) { var code = $( this ).data( "code-country" ); secupress_set_indeterminate_state( code ); } ); $( ".continent input" ).each( function( i ) { var code = $( this ).val().replace( "continent-", "" ); secupress_set_indeterminate_state( code ); } ); $( ".expand_country" ).on( "click.secupress", function( e ) { $( this ).next( "fieldset" ).toggleClass( "hide-if-js" ); } ); } )(jQuery, document, window); // Banned IPs ====================================================================================== (function($, d, w, undefined) { var $row = $( "#banned-ips-row" ), $banForm, banUrl; if ( ! $row.length ) { return; } // Empty the list, display a message in a placeholder (a row in the list), and maybe hide the search form and the "Clear all IPs" button. function secupressBannedIPsEmptyList( message, resetSearch ) { var $form; if ( undefined === message || ! message ) { message = SecuPressi18nModules.noBannedIPs; } // Remove all rows from the list and display the placeholder. $row.find( "#secupress-banned-ips-list" ).html( '