/** * Send an action over AJAX. A wrapper around jQuery.ajax. In future, all consumers can be reviewed to simplify some of the options, where there is historical cruft. * * @param {string} action - the action to send * @param {*} data - data to send * @param {Function} callback - will be called with the results * @param {object} options -further options. Relevant properties include: * - [json_parse=true] - whether to JSON parse the results * - [alert_on_error=true] - whether to show an alert box if there was a problem (otherwise, suppress it) * - [action='aios_ajax'] - what to send as the action parameter on the AJAX request (N.B. action parameter to this function goes as the 'subaction' parameter on the AJAX request) * - [nonce=aios_ajax_nonce] - the nonce value to send. * - [nonce_key='nonce'] - the key value for the nonce field * - [timeout=null] - set a timeout after this number of seconds (or if null, none is set) * - [async=true] - control whether the request is asynchronous (almost always wanted) or blocking (would need to have a specific reason) * - [type='POST'] - GET or POST */ function aios_send_command(action, data, callback, options) { default_options = { json_parse: true, alert_on_error: true, action: 'aios_ajax', nonce: aios_data.ajax_nonce, nonce_key: 'nonce', timeout: null, async: true, type: 'POST' }; if ('undefined' === typeof options) options = {}; for (var opt in default_options) { if (!options.hasOwnProperty(opt)) { options[opt] = default_options[opt]; } } var ajax_data = { action: options.action, subaction: action }; ajax_data[options.nonce_key] = options.nonce; ajax_data.data = data; var ajax_opts = { type: options.type, url: ajaxurl, data: ajax_data, success: function(response, status) { if (options.json_parse) { try { var resp = aios_parse_json(response); } catch (e) { if ('function' == typeof options.error_callback) { return options.error_callback(response, e, 502, resp); } else { console.log(e); console.log(response); if (options.alert_on_error) { alert(aios_trans.unexpected_response+' '+response); } return; } } if (resp.hasOwnProperty('fatal_error')) { if ('function' == typeof options.error_callback) { // 500 is internal server error code return options.error_callback(response, status, 500, resp); } else { console.error(resp.fatal_error_message); if (options.alert_on_error) { alert(resp.fatal_error_message); } return false; } } if ('function' == typeof callback) callback(resp, status, response); } else { if ('function' == typeof callback) callback(response, status); } }, error: function(response, status, error_code) { if ('function' == typeof options.error_callback) { options.error_callback(response, status, error_code); } else { console.log("aios_send_command: error: "+status+" ("+error_code+")"); console.log(response); } }, dataType: 'text', async: options.async }; if (null != options.timeout) { ajax_opts.timeout = options.timeout; } jQuery.ajax(ajax_opts); } /** * Parse JSON string, including automatically detecting unwanted extra input and skipping it * * @param {string} json_mix_str - JSON string which need to parse and convert to object * @param {boolean} analyse - if true, then the return format will contain information on the parsing, and parsing will skip attempting to JSON.parse() the entire string (will begin with trying to locate the actual JSON) * * @throws SyntaxError|String (including passing on what JSON.parse may throw) if a parsing error occurs. * * @returns Mixed parsed JSON object. Will only return if parsing is successful (otherwise, will throw). If analyse is true, then will rather return an object with properties (mixed)parsed, (integer)json_start_pos and (integer)json_end_pos */ function aios_parse_json(json_mix_str, analyse) { analyse = ('undefined' === typeof analyse) ? false : true; // Just try it - i.e. the 'default' case where things work (which can include extra whitespace/line-feeds, and simple strings, etc.). if (!analyse) { try { var result = JSON.parse(json_mix_str); return result; } catch (e) { console.log('AIOS: Exception when trying to parse JSON (1) - will attempt to fix/re-parse based upon first/last curly brackets'); console.log(json_mix_str); } } var json_start_pos = json_mix_str.indexOf('{'); var json_last_pos = json_mix_str.lastIndexOf('}'); // Case where some php notice may be added after or before json string if (json_start_pos > -1 && json_last_pos > -1) { var json_str = json_mix_str.slice(json_start_pos, json_last_pos + 1); try { var parsed = JSON.parse(json_str); if (!analyse) { console.log('AIOS: JSON re-parse successful'); } return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: json_last_pos + 1 } : parsed; } catch (e) { console.log('AIOS: Exception when trying to parse JSON (2) - will attempt to fix/re-parse based upon bracket counting'); var cursor = json_start_pos; var open_count = 0; var last_character = ''; var inside_string = false; // Don't mistake this for a real JSON parser. Its aim is to improve the odds in real-world cases seen, not to arrive at universal perfection. while ((open_count > 0 || cursor == json_start_pos) && cursor <= json_last_pos) { var current_character = json_mix_str.charAt(cursor); if (!inside_string && '{' == current_character) { open_count++; } else if (!inside_string && '}' == current_character) { open_count--; } else if ('"' == current_character && '\\' != last_character) { inside_string = inside_string ? false : true; } last_character = current_character; cursor++; } console.log("Started at cursor="+json_start_pos+", ended at cursor="+cursor+" with result following:"); console.log(json_mix_str.substring(json_start_pos, cursor)); try { var parsed = JSON.parse(json_mix_str.substring(json_start_pos, cursor)); console.log('AIOS: JSON re-parse successful'); return analyse ? { parsed: parsed, json_start_pos: json_start_pos, json_last_pos: cursor } : parsed; } catch (e) { // Throw it again, so that our function works just like JSON.parse() in its behaviour. throw e; } } } throw "AIOS: could not parse the JSON"; } /** * Updates the content of an HTML element identified by its ID with the provided badge text. * * @param {Array} badges - An array of objects representing badges to update. * @param {string} badges.id - The ID of the HTML element to update. * @param {string} badges.html - The HTML content to set for the element. * @returns {void} */ function aios_update_badge(badges) { badges.forEach(function(badge) { aios_update_content(badge.id, badge.html); }); } /** * Update the content of an element with the specified HTML. * * @param {string} id - The ID of the element to update. * @param {string} html - The HTML content to set for the element. * @returns {void} */ function aios_update_content(id, html) { jQuery(id).html(html); } /** * Function to block the UI and display a loading message. * Uses jQuery blockUI plugin. * * @param {string} message - A string to be shown when function is called * * @returns {void} */ function aios_block_ui(message = aios_trans.saving) { jQuery.blockUI({ css: { width: '500px', border: 'none', 'border-radius': '10px', left: 'calc(50% - 150px)', top: 'calc(50% - 150px)', padding: '20px' }, message: '

'+message+'
' }); } /** * Display a success modal with optional message and icon. * * @param {Object|string} args - Configuration object or message string. * * @returns {void} */ function aios_show_success_modal(args) { if (typeof args == 'string') { args = { message: args }; } var data = jQuery.extend( { icon: 'yes', close: aios_trans.close, message: '', classes: 'success' }, args ); jQuery.blockUI({ css: { width: '500px', border: 'none', 'border-radius': '10px', left: 'calc(50% - 150px)', top: 'calc(50% - 150px)' }, onOverlayClick: jQuery.unblockUI, message: '
'+data.message+'
' }); // close success popup jQuery('.blockUI .aios-close-overlay').on('click', function() { jQuery.unblockUI(); }) } /** * Submits a form using AJAX and handles the response. * * @param {jQuery} form - The jQuery object representing the form element. * @param {string} action - The action to perform when submitting the form. * @param {boolean|Object} [use_data=true] - Indicates whether to include form data in the AJAX request. * @param {string} [block_ui_message="Saving..."] - The message to display while blocking UI during AJAX request. * @param {Function} [pre_ajax_callback] - Optional callback function to execute before the AJAX request. * @param {Function} [post_ajax_callback] - Optional callback function to execute after the AJAX request. */ function aios_submit_form(form, action, use_data = true, block_ui_message = aios_trans.saving, pre_ajax_callback, post_ajax_callback ) { aios_block_ui(block_ui_message); var submitButton = form.find(':submit'); submitButton.prop('disabled', true); var data = {}; if (typeof use_data === 'object') { data = use_data; // Use custom data object } else if (use_data) { var dataArray = form.serializeArray(); var dataLength = dataArray.length; for (var i = 0; i < dataLength; i++) { data[dataArray[i].name] = dataArray[i].value; } } if (typeof pre_ajax_callback === 'function') { pre_ajax_callback(); } aios_send_command(action, data, function(response) { aios_handle_ajax_update(response, post_ajax_callback); submitButton.prop('disabled', false); }) } /** * Handle AJAX response and update UI elements accordingly. * If a callback function is provided, it will be executed. * * @param {Object} response - The AJAX response object. * @param {Function} [callback] - Optional callback function to execute. * * @returns {void} */ function aios_handle_ajax_update(response, callback) { var update_message = (response.hasOwnProperty('message') && response.message.length > 0) || (response.hasOwnProperty('info') && response.info.length > 0); if (update_message) { var messageContainer = jQuery('
'); // display single message if (response.hasOwnProperty('message')) { messageContainer.append(response.message); messageContainer.append('
'); } if (response.hasOwnProperty('info') && response.info.length > 0) { // info toggle let toggle = jQuery('' + aios_trans.show_notices + ' ('+aios_trans.show_info+')'); toggle.appendTo(messageContainer) let infoContainer = jQuery(''); response.info.forEach(function (info) { infoContainer.append(`${info}`, '
'); }); infoContainer.appendTo(messageContainer); } if ('error' === response.status) { aios_show_success_modal({ message: messageContainer.html(), icon: 'no-alt', classes: 'warning' }); } else { aios_show_success_modal(messageContainer.html()) } } else { aios_show_success_modal(aios_trans.completed) } // update contents on the page if (response.hasOwnProperty('content')) { jQuery.each(response.content, function(key, value) { aios_update_content('#' + key, value) }); } // update fields with new values if changed if (response.hasOwnProperty('values')) { jQuery.each(response.values, function(key, value) { jQuery('#' + key).val(value); }); } // update badges if (response.hasOwnProperty('badges')) { aios_update_badge(response.badges) } if (typeof callback === 'function') { callback(); } } jQuery(function($) { //Add Generic Admin Dashboard JS Code in this file //Media Uploader - start jQuery("#aiowps_restore_htaccess_form").submit(function(e) { e.preventDefault(); aios_read_restore_file(this, 'htaccess'); }); jQuery("#aiowps_restore_wp_config_form").submit(function(e) { e.preventDefault(); aios_read_restore_file(this, 'wp_config'); }); jQuery("#aiowps_restore_settings_form").submit(function(e) { e.preventDefault(); aios_read_restore_file(this, 'import_settings'); }); function aios_read_restore_file(form, file) { var aios_import_file_input = document.getElementById('aiowps_' + file + '_file'); if (aios_import_file_input.files.length == 0) { alert(aios_trans.no_import_file); return; } var aios_import_file_file = aios_import_file_input.files[0]; var aios_import_file_reader = new FileReader(); aios_import_file_reader.onload = function() { jQuery('#aiowps_' + file + '_file_contents').val(this.result); form.submit(); }; aios_import_file_reader.readAsText(aios_import_file_file); } //End of Media Uploader // Triggers the more info toggle link $(".aiowps_more_info_body").hide();//hide the more info on page load $('.aiowps_more_info_anchor').on('click', function() { $(this).next(".aiowps_more_info_body").animate({ "height": "toggle"}); var toggle_char_ref = $(this).find(".aiowps_more_info_toggle_char"); var toggle_char_value = toggle_char_ref.text(); if (toggle_char_value === "+") { toggle_char_ref.text("-"); } else { toggle_char_ref.text("+"); } }); //End of more info toggle //This function uses javascript to retrieve a query arg from the current page URL function getParameterByName(name) { var url = window.location.href; name = name.replace(/[\[\]]/g, "\\$&"); var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex.exec(url); if (!results) return null; if (!results[2]) return ''; return decodeURIComponent(results[2].replace(/\+/g, " ")); } // Start of brute force attack prevention toggle handling jQuery('input[name=aiowps_enable_brute_force_attack_prevention]').on('click', function() { jQuery('input[name=aiowps_brute_force_secret_word]').prop('disabled', !jQuery(this).prop('checked')); jQuery('input[name=aiowps_cookie_based_brute_force_redirect_url]').prop('disabled', !jQuery(this).prop('checked')); jQuery('input[name=aiowps_brute_force_attack_prevention_pw_protected_exception]').prop('disabled', !jQuery(this).prop('checked')); jQuery('input[name=aiowps_brute_force_attack_prevention_ajax_exception]').prop('disabled', !jQuery(this).prop('checked')); }); // End of brute force attack prevention toggle handling // Start of CAPTCHA handling jQuery('.wrap').on('change', '#aiowps_default_captcha', function () { var selected_captcha = $(this).val(); jQuery('.captcha_settings').hide(); jQuery('#aios-'+ selected_captcha).show(); if ('none' === selected_captcha) { jQuery('#aios-captcha-options').hide() } else { jQuery('#aios-captcha-options').show(); } }); // End of CAPTCHA handling /** * Take a backup with UpdraftPlus if possible. * * @param {String} file_entities * * @return void */ function take_a_backup_with_updraftplus(file_entities) { // Set default for file_entities to empty string if ('undefined' == typeof file_entities) file_entities = ''; var exclude_files = file_entities ? 0 : 1; if (typeof updraft_backupnow_inpage_go === 'function') { updraft_backupnow_inpage_go(function () { // Close the backup dialogue. $('#updraft-backupnow-inpage-modal').dialog('close'); }, file_entities, 'autobackup', 0, exclude_files, 0); } } if (jQuery('#aios-manual-db-backup-now').length) { jQuery('#aios-manual-db-backup-now').on('click', function (e) { e.preventDefault(); take_a_backup_with_updraftplus(); }); } // Hide 2FA premium section (advertisements) for free. if (jQuery('.tfa-premium').length && 0 == jQuery('#tfa_trusted_for').length) { jQuery('.tfa-premium').parent().find('hr').first().hide(); jQuery('.tfa-premium').hide(); } // Start of trash spam comments toggle handling jQuery('input[name=aiowps_enable_trash_spam_comments]').on('click', function() { jQuery('input[name=aiowps_trash_spam_comments_after_days]').prop('disabled', !jQuery(this).prop('checked')); }); // End of trash spam comments toggle handling // Copies text using the deprecated document.execCommand method function deprecated_copy(text) { var $temp = $(''); $('body').append($temp); $temp.val(text).select(); if (document.execCommand('copy')) { alert(aios_trans.copied); } $temp.remove() } // Start of copy-to-clipboard click handling jQuery('.copy-to-clipboard').on('click', function(event) { if (navigator.clipboard) { navigator.clipboard.writeText(event.target.value).then(function() { alert(aios_trans.copied); }, function() { deprecated_copy(event.target.value); }); } else { deprecated_copy(event.target.value); } }); // End of copy-to-clipboard click handling // Start of database table prefix handling jQuery('#aiowps_enable_random_prefix').on('click', function() { jQuery('#aiowps_new_manual_db_prefix').prop('disabled', jQuery(this).prop('checked')); }); jQuery('#aiowps_new_manual_db_prefix').on('input', function() { if (jQuery(this).prop('value')) { jQuery('#aiowps_enable_random_prefix').prop('disabled', true); } else { jQuery('#aiowps_enable_random_prefix').prop('disabled', false); } }); // End of database table prefix handling // Dashboard menu ajaxify jQuery("#locked-ip-list-table").on('click', '.aios-unlock-ip-button', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('unlock_ip', {ip: jQuery(this).data('ip')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#locked-ip-list-table').load(' #locked-ip-list-table > *'); }): false }); jQuery("#locked-ip-list-table").on('click', '.aios-delete-locked-ip-record-button', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('delete_locked_ip_record', {id: jQuery(this).data('id')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#locked-ip-list-table').load(' #locked-ip-list-table > *'); }): false }); jQuery("#permanent-ip-list-table").on('click', '.aios-unblock-permanent-ip', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('blocked_ip_list_unblock_ip', {id: jQuery(this).data('id')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message) if (response.status === 'success') $('#permanent-ip-list-table').load(' #permanent-ip-list-table > *'); }): false }); jQuery("#audit-log-list-table").on('click', '.aios-delete-audit-log', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('delete_audit_log', {id: jQuery(this).data('id')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#audit-log-list-table').load(' #audit-log-list-table > *'); }): false; }); jQuery("#audit-log-list-table .aios-unlock-ip-button").on('click', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('unlock_ip', {ip: jQuery(this).data('ip')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#audit-log-list-table').load(' #audit-log-list-table > *'); }) : false; }); jQuery("#audit-log-list-table .aios-unblacklist-ip-button").on('click', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('unblacklist_ip', {ip: jQuery(this).data('ip')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#audit-log-list-table').load(' #audit-log-list-table > *'); }) : false; }); jQuery("#audit-log-list-table .aios-lock-ip-button").on('click', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('lock_ip', {ip: jQuery(this).data('ip'), lock_reason: 'audit-log'}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#audit-log-list-table').load(' #audit-log-list-table > *'); }) : false; }); jQuery("#audit-log-list-table .aios-blacklist-ip-button").on('click', function(e) { e.preventDefault(); confirm(jQuery(this).data('message')) ? aios_send_command('blacklist_ip', {ip: jQuery(this).data('ip')}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if (response.status === 'success') $('#audit-log-list-table').load(' #audit-log-list-table > *'); }) : false; }); jQuery("#aios-clear-debug-logs").on('submit', function(e) { e.preventDefault(); aios_send_command('clear_debug_logs', {}, function(response) { jQuery('#aios_message').remove(); jQuery('#wpbody-content .wrap h2:first').after(response.message); if ("success" === response.status) $('#debug-list-table').load(' #debug-list-table > *'); }) }); // End of dashboard menu ajaxify // Start of file scan handling jQuery('.aiowps_next_scheduled_scan_wrapper').on('click', '.aiowps_view_last_fcd_results', view_scan_results_handler); jQuery('#aiowps_fcds_change_detected').on('click', '.aiowps_view_last_fcd_results', view_scan_results_handler); /** * This function handles the view last scan result event * * @param {*} e - the event */ function view_scan_results_handler(e) { e.preventDefault(); var reset_change_detected = $(this).data('reset_change_detected') ? 1 : 0; aios_send_command('get_last_scan_results', { reset_change_detected: reset_change_detected}, function(resp) { if (resp.hasOwnProperty('content')) { if (reset_change_detected) jQuery('#aiowps_fcds_change_detected').remove(); jQuery('#aiowps_previous_scan_wrapper').html(resp.content); } }); } jQuery('#aiowps_manual_fcd_scan').on('click', function(e) { e.preventDefault(); jQuery('#aiowps_activejobs_table').html('

'+ aios_trans.processing + '

'); jQuery('#aiowps_activejobs_table .aiowps_spinner').addClass('visible'); aios_send_command('perform_file_scan', {}, function (resp) { if (resp.hasOwnProperty('content')) { jQuery('#aiowps-previous-files-scan-inner').html(resp.content.last_scan); } if (resp.hasOwnProperty('messages')) { jQuery('#aiowps_activejobs_table').html(''); resp.messages.forEach(function(message) { jQuery('#aiowps_activejobs_table').append('

'+message+'

'); }); } }); }); // End of file scan handling // Start of login whitelist suggests both IPv4 and IPv6 if (jQuery('#aios_user_ip_maybe_also').length) { var selector = '#aios-ipify-ip-address'; var ipfield = '#aios_user_ip_maybe_also'; var getting_text = jQuery(ipfield).attr('getting_text'); var ip_maybe = jQuery(ipfield).attr('ip_maybe'); if ('v6' == ip_maybe) { var url = 'https://api64.ipify.org/?format=json'; } else { var url = 'https://api.ipify.org/?format=json'; } jQuery(selector).html(getting_text); jQuery.ajax({ type: 'GET', dataType: 'json', url: url, success: function (response, status) { if (response.hasOwnProperty('ip') && response.ip != jQuery('#aiowps_user_ip').val()) { jQuery(ipfield).val(response.ip); jQuery(ipfield).removeClass('aio_hidden'); } else { console.log(response); } jQuery(selector).html(''); }, error: function (response, status, error_code) { console.log(response); jQuery(selector).html(''); } }); } // End of login whitelist suggests both IPv4 and IPv6 // Click the 'show/hide advanced options' button jQuery('button.button-link.aios-toggle-advanced-options').on('click', function() { if (!jQuery(this).hasClass('advanced-options-disabled')) { jQuery(this).toggleClass('opened'); } }); // click the show more for AJAX info jQuery(document).on('click','#aios_ajax_showmoreoptions', function (e) { e.preventDefault() let moreOptions = jQuery('#aios_ajax_moreoptions'); moreOptions.toggle() // Toggle text between "Show more" and "Hide" let newText = moreOptions.is(':visible') ? aios_trans.hide_info : aios_trans.show_info; jQuery(this).text(newText); }); });