PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 3.0.0
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v3.0.0
3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 2.11.12 2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 All 93 releases
← All changes | assets/js/admin.js +209 -37 2.9.9 → 3.0.0 View file →
@@ -1245,8 +1245,9 @@
1245 1245 results.extra = results.extra || [];
1246 1246 results.missing = results.missing || [];
1247 1247
1248 1248 var strings = vigilanteAdmin.strings;
1249 + var esc = Vigilante_Admin.escapeHtml;
1249 1250
1250 1251 var html = '<h2>' + strings.scanResults + '</h2>';
1251 1252 html += '<div class="vigilante-scan-summary">';
1252 1253 html += '<div class="vigilante-scan-stat vigilante-stat-ok">';
@@ -1295,13 +1296,13 @@
1295 1296 html += '<thead><tr>' + selectAllCell + '<th>' + strings.file + '</th><th style="width: 250px;">' + strings.reason + '</th><th style="width: 120px;">' + strings.type + '</th><th style="width: 80px;">' + (strings.actions || 'Actions') + '</th></tr></thead>';
1296 1297 html += '<tbody>';
1297 1298 results.suspicious.forEach(function(file) {
1298 1299 html += '<tr>';
1299 - html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + file.file + '"></th>';
1300 - html += '<td><code style="color: #d63638;">' + file.file + '</code></td>';
1301 - html += '<td>' + (file.reason || strings.unknown) + '</td>';
1302 - html += '<td>' + (file.type || strings.unknown) + '</td>';
1303 - html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + file.file + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1300 + html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + esc(file.file) + '"></th>';
1301 + html += '<td><code style="color: #d63638;">' + esc(file.file) + '</code></td>';
1302 + html += '<td>' + esc(file.reason || strings.unknown) + '</td>';
1303 + html += '<td>' + esc(file.type === 'vigilante_self' ? (strings.selfType || 'Vigilant (self)') : (file.type || strings.unknown)) + '</td>';
1304 + html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + esc(file.file) + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1304 1305 html += '</tr>';
1305 1306 });
1306 1307 html += '</tbody></table>';
1307 1308 html += '</div>';
@@ -1318,13 +1319,13 @@
1318 1319 html += '<thead><tr>' + selectAllCell + '<th>' + strings.file + '</th><th style="width: 250px;">' + strings.reason + '</th><th style="width: 120px;">' + strings.type + '</th><th style="width: 80px;">' + (strings.actions || 'Actions') + '</th></tr></thead>';
1319 1320 html += '<tbody>';
1320 1321 results.extra.forEach(function(file) {
1321 1322 html += '<tr>';
1322 - html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + file.file + '"></th>';
1323 - html += '<td><code style="color: #b32d2e;">' + file.file + '</code></td>';
1324 - html += '<td>' + (file.reason || strings.unknown) + '</td>';
1325 - html += '<td>' + (file.type || strings.unknown) + '</td>';
1326 - html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + file.file + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1323 + html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + esc(file.file) + '"></th>';
1324 + html += '<td><code style="color: #b32d2e;">' + esc(file.file) + '</code></td>';
1325 + html += '<td>' + esc(file.reason || strings.unknown) + '</td>';
1326 + html += '<td>' + esc(file.type === 'vigilante_self' ? (strings.selfType || 'Vigilant (self)') : (file.type || strings.unknown)) + '</td>';
1327 + html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + esc(file.file) + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1327 1328 html += '</tr>';
1328 1329 });
1329 1330 html += '</tbody></table>';
1330 1331 html += '</div>';
@@ -1339,8 +1340,16 @@
1339 1340 } else {
1340 1341 regularModified.push(file);
1341 1342 }
1342 1343 });
1344 + // A missing file of Vigilant is critical and the generic missing
1345 + // files have no table: list it with the modified files, or the
1346 + // screen said "All clear" with a module deleted.
1347 + results.missing.forEach(function(file) {
1348 + if (file && file.type === 'vigilante_self') {
1349 + regularModified.push(file);
1350 + }
1351 + });
1343 1352
1344 1353 // Critical config files section (same visual treatment as suspicious files)
1345 1354 if (criticalModified.length > 0) {
1346 1355 html += '<div class="vigilante-file-list vigilante-critical-config-files">';
@@ -1349,9 +1358,13 @@
1349 1358 html += '<table class="wp-list-table widefat fixed striped">';
1350 1359 html += '<thead><tr><th>' + strings.file + '</th><th style="width: 200px;">' + (strings.changes || 'Changes') + '</th><th style="width: 220px;">' + (strings.actions || 'Actions') + '</th></tr></thead>';
1351 1360 html += '<tbody>';
1352 1361
1353 - var escHtml = function(s) { return $('<span>').text(s).html(); };
1362 + // Delega en el escapador del objeto, que codifica comillas. La
1363 + // version anterior era $('<span>').text(s).html(), que no las toca:
1364 + // aqui solo se usa en posicion de texto, pero es el patron del
1365 + // CVE-2026-81754 esperando a que alguien lo mueva a un atributo.
1366 + var escHtml = Vigilante_Admin.escapeHtml;
1354 1367
1355 1368 criticalModified.forEach(function(file) {
1356 1369 var fileId = file.file.replace(/[^a-z0-9]/gi, '-');
1357 1370 var baselineSize = file.baseline_size ? Number(file.baseline_size).toLocaleString() : '?';
@@ -1362,9 +1375,9 @@
1362 1375 var diffUnavailable = !!diff.unavailable;
1363 1376
1364 1377 // Main row
1365 1378 html += '<tr>';
1366 - html += '<td><code style="color: #e36210;">' + file.file + '</code></td>';
1379 + html += '<td><code style="color: #e36210;">' + esc(file.file) + '</code></td>';
1367 1380 html += '<td>';
1368 1381 if (!diffUnavailable) {
1369 1382 html += '<span style="color: #007017;">+' + added.length + '</span> <span style="color: #b32d2e;">-' + removed.length + '</span> ' + (strings.diffLines || 'lines') + '<br>';
1370 1383 }
@@ -1371,9 +1384,24 @@
1371 1384 html += '<small style="color: #50575e;">' + baselineSize + ' &rarr; ' + currentSize + ' bytes</small>';
1372 1385 html += '</td>';
1373 1386 html += '<td>';
1374 1387 html += '<button type="button" class="button button-small vigilante-toggle-critical-content" data-target="vigilante-critical-content-' + fileId + '" data-label-show="' + (strings.reviewChanges || 'Review changes') + '" data-label-hide="' + (strings.hideChanges || 'Hide changes') + '">' + (strings.reviewChanges || 'Review changes') + '</button> ';
1375 - html += '<button type="button" class="button button-small button-primary vigilante-approve-critical-file" data-file="' + file.file + '">' + (strings.approve || 'Approve') + '</button>';
1388 +
1389 + // Same gate as the AJAX handler behind the button, and as the
1390 + // first render in PHP. Since 2.11.3 approving these two files
1391 + // takes a network administrator, so painting the button to
1392 + // anybody else buys them a Permission denied and no reason why.
1393 + //
1394 + // Truthy test on purpose: wp_localize_script() casts every
1395 + // scalar to a string, so the PHP boolean arrives here as "1"
1396 + // or "" and never as true. Measured on the network, admin of
1397 + // a subsite against network admin. A === true would gate
1398 + // nobody and would look right.
1399 + if (vigilanteAdmin.approvalLocked) {
1400 + html += '<span class="description" style="display:block;margin-top:4px;">' + escHtml(strings.approvalLockedNotice || '') + '</span>';
1401 + } else {
1402 + html += '<button type="button" class="button button-small button-primary vigilante-approve-critical-file" data-file="' + esc(file.file) + '">' + (strings.approve || 'Approve') + '</button>';
1403 + }
1376 1404 html += '</td>';
1377 1405 html += '</tr>';
1378 1406
1379 1407 // Expandable diff row
@@ -1379,9 +1407,15 @@
1379 1407 // Expandable diff row
1380 1408 html += '<tr id="vigilante-critical-content-' + fileId + '" class="vigilante-critical-content-row" style="display:none;">';
1381 1409 html += '<td colspan="3" style="padding: 0;">';
1382 1410 html += '<div class="vigilante-critical-content" style="max-height: 400px; overflow: auto; background: #fff; padding: 10px; font-size: 12px; line-height: 1.5; font-family: Consolas, Monaco, monospace; border-top: 1px solid #c3c4c7;">';
1383 - if (diffUnavailable) {
1411 + if (diff.network) {
1412 + html += '<p style="color: #50575e; font-style: italic; margin: 0;">' + escHtml(strings.diffNetwork || '') + '</p>';
1413 + } else if (diff.rescan) {
1414 + html += '<p style="color: #50575e; font-style: italic; margin: 0;">' + escHtml(strings.diffRescan || '') + '</p>';
1415 + } else if (diff.redaction) {
1416 + html += '<p style="color: #50575e; font-style: italic; margin: 0;">' + escHtml(strings.diffRedaction || '') + '</p>';
1417 + } else if (diffUnavailable) {
1384 1418 html += '<p style="color: #50575e; font-style: italic; margin: 0;">' + (strings.diffUnavailable || 'Diff not available.') + '</p>';
1385 1419 } else if (added.length === 0 && removed.length === 0) {
1386 1420 html += '<p style="color: #50575e; font-style: italic; margin: 0;">' + (strings.diffEmpty || 'No line changes detected.') + '</p>';
1387 1421 } else {
@@ -1412,12 +1446,12 @@
1412 1446 html += '<thead><tr>' + selectAllCell + '<th>' + strings.file + '</th><th style="width: 100px;">' + strings.type + '</th><th style="width: 80px;">' + (strings.actions || 'Actions') + '</th></tr></thead>';
1413 1447 html += '<tbody>';
1414 1448 regularModified.forEach(function(file) {
1415 1449 html += '<tr>';
1416 - html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + file.file + '"></th>';
1417 - html += '<td><code>' + file.file + '</code></td>';
1418 - html += '<td>' + (file.type || strings.unknown) + '</td>';
1419 - html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + file.file + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1450 + html += '<th scope="row" class="check-column"><input type="checkbox" class="vigilante-fi-cb" value="' + esc(file.file) + '"></th>';
1451 + html += '<td><code>' + esc(file.file) + '</code></td>';
1452 + html += '<td>' + esc(file.type === 'vigilante_self' ? (strings.selfType || 'Vigilant (self)') : (file.type || strings.unknown)) + '</td>';
1453 + html += '<td><button type="button" class="button button-small vigilante-ignore-file" data-file="' + esc(file.file) + '">' + (strings.ignore || 'Ignore') + '</button></td>';
1420 1454 html += '</tr>';
1421 1455 });
1422 1456 html += '</tbody></table>';
1423 1457 html += '</div>';
@@ -1422,9 +1456,9 @@
1422 1456 html += '</tbody></table>';
1423 1457 html += '</div>';
1424 1458 }
1425 1459
1426 - if (results.modified.length === 0 && results.suspicious.length === 0 && results.extra.length === 0) {
1460 + if (criticalModified.length === 0 && regularModified.length === 0 && results.suspicious.length === 0 && results.extra.length === 0) {
1427 1461 html += '<div class="vigilante-all-clear"><span class="dashicons dashicons-yes-alt"></span> ' + strings.allClear + '</div>';
1428 1462 }
1429 1463
1430 1464 $container.html(html).show();
@@ -1967,14 +2001,16 @@
1967 2001 user: log.user_login || '',
1968 2002 ip: log.ip_address || '',
1969 2003 user_agent: log.user_agent || '',
1970 2004 request_method: log.request_method || '',
2005 + request_uri: log.request_uri || '',
1971 2006 date: log.created_at || '',
1972 2007 severity: log.severity || 'info',
1973 2008 is_ip_whitelisted: !!log.is_ip_whitelisted,
1974 2009 is_ip_blacklisted: !!log.is_ip_blacklisted,
1975 2010 is_ua_whitelisted: !!log.is_ua_whitelisted,
1976 - is_ua_blacklisted: !!log.is_ua_blacklisted
2011 + is_ua_blacklisted: !!log.is_ua_blacklisted,
2012 + self: log.self_guidance || null
1977 2013 };
1978 2014
1979 2015 var displayType = typeLabels[log.event_type] || log.event_type;
1980 2016 var displaySeverity = severityLabels[log.severity] || log.severity;
@@ -2054,20 +2090,39 @@
2054 2090 },
2055 2091
2056 2092 /**
2057 2093 * Download logs as CSV
2094 + *
2095 + * Every cell goes through csvCell(): quotes are doubled in all columns,
2096 + * not only the message, and a value starting with = + - @ or a control
2097 + * character gets an apostrophe in front so a spreadsheet shows it
2098 + * instead of running it as a formula (S12). Action, address and
2099 + * User-Agent were added for B38: the columns that help diagnose an
2100 + * entry were exactly the ones the export left out.
2058 2101 */
2059 2102 downloadCSV: function(logs) {
2060 - var csv = 'Date,Type,Method,Severity,Message,User,IP\n';
2103 + var csvCell = function(value) {
2104 + var s = (value === null || value === undefined) ? '' : String(value);
2105 + if (/^[=+\-@\t\r]/.test(s)) {
2106 + s = "'" + s;
2107 + }
2108 + return '"' + s.replace(/"/g, '""') + '"';
2109 + };
2110 + var csv = 'Date,Type,Action,Method,Severity,Message,User,IP,Address,User Agent\n';
2061 2111
2062 2112 logs.forEach(function(log) {
2063 - csv += '"' + log.created_at + '",';
2064 - csv += '"' + log.event_type + '",';
2065 - csv += '"' + (log.request_method || '') + '",';
2066 - csv += '"' + log.severity + '",';
2067 - csv += '"' + (log.event_message || '').replace(/"/g, '""') + '",';
2068 - csv += '"' + (log.user_login || '') + '",';
2069 - csv += '"' + log.ip_address + '"\n';
2113 + csv += [
2114 + csvCell(log.created_at),
2115 + csvCell(log.event_type),
2116 + csvCell(log.event_action),
2117 + csvCell(log.request_method),
2118 + csvCell(log.severity),
2119 + csvCell(log.event_message),
2120 + csvCell(log.user_login),
2121 + csvCell(log.ip_address),
2122 + csvCell(log.request_uri),
2123 + csvCell(log.user_agent)
2124 + ].join(',') + '\n';
2070 2125 });
2071 2126
2072 2127 var blob = new Blob([csv], { type: 'text/csv' });
2073 2128 var url = URL.createObjectURL(blob);
@@ -2592,9 +2647,43 @@
2592 2647
2593 2648 var html = '';
2594 2649
2595 2650 var s = vigilanteAdmin.strings;
2651 + var esc = Vigilante_Admin.escapeHtml;
2596 2652
2653 + // -- Section: what happened (self-protection entries) --
2654 + // Text only: every value below is printed as text, never as an
2655 + // attribute, and the link is a fixed URL built with esc_url().
2656 + if (details.self && details.self.title) {
2657 + html += '<div class="vigilante-popup-section">';
2658 + html += '<h4 class="vigilante-popup-section-title">' + esc(s.logWhatHappened || 'What happened') + '</h4>';
2659 + html += '<p><strong>' + esc(details.self.title) + '</strong></p>';
2660 + if (details.self.files && details.self.files.length) {
2661 + html += '<ul class="vigilante-popup-files">';
2662 + details.self.files.slice(0, 20).forEach(function(file) {
2663 + html += '<li><code>' + esc(file) + '</code></li>';
2664 + });
2665 + if (details.self.files.length > 20) {
2666 + html += '<li>' + esc('+' + (details.self.files.length - 20)) + '</li>';
2667 + }
2668 + html += '</ul>';
2669 + }
2670 + if (details.self.meaning) {
2671 + html += '<p><strong>' + esc(s.logWhatItMeans || 'What it means') + ':</strong> ' + esc(details.self.meaning) + '</p>';
2672 + }
2673 + if (details.self.steps && details.self.steps.length) {
2674 + html += '<p><strong>' + esc(s.logWhatToDo || 'What to do') + ':</strong></p><ol>';
2675 + details.self.steps.forEach(function(step) {
2676 + html += '<li>' + esc(step) + '</li>';
2677 + });
2678 + html += '</ol>';
2679 + }
2680 + if (vigilanteAdmin.selfBoxUrl) {
2681 + html += '<p><a href="' + esc(vigilanteAdmin.selfBoxUrl) + '">' + esc(s.logSelfSeeDetails || 'Open File Integrity for the full detail') + '</a></p>';
2682 + }
2683 + html += '</div>';
2684 + }
2685 +
2597 2686 // -- Section: Request --
2598 2687 html += '<div class="vigilante-popup-section">';
2599 2688 html += '<h4 class="vigilante-popup-section-title">' + this.escapeHtml(s.logRequest || 'Request') + '</h4>';
2600 2689 html += '<table class="vigilante-details-table">';
@@ -2605,8 +2694,11 @@
2605 2694 html += '<tr><th>' + this.escapeHtml(s.logType || 'Type') + '</th><td>' + this.escapeHtml(details.type) + '</td></tr>';
2606 2695 html += '<tr><th>' + this.escapeHtml(s.logAction || 'Action') + '</th><td>' + this.escapeHtml(details.action || '-') + '</td></tr>';
2607 2696 html += '<tr><th>' + this.escapeHtml(s.logSeverity || 'Severity') + '</th><td><span class="vigilante-badge vigilante-badge-' + this.escapeHtml(details.severity || 'info') + '">' + this.escapeHtml(details.severity || 'info') + '</span></td></tr>';
2608 2697 html += '<tr><th>' + this.escapeHtml(s.logMessage || 'Message') + '</th><td>' + this.escapeHtml(details.message) + '</td></tr>';
2698 + if (details.request_uri) {
2699 + html += '<tr><th>' + this.escapeHtml(s.logRequestUri || 'Address') + '</th><td class="vigilante-ua-cell"><code>' + this.escapeHtml(details.request_uri) + '</code></td></tr>';
2700 + }
2609 2701 html += '</table>';
2610 2702 html += '</div>';
2611 2703
2612 2704 // -- Section: Client --
@@ -2680,9 +2772,11 @@
2680 2772 e.preventDefault();
2681 2773 var $btn = $(e.currentTarget);
2682 2774 var itemType = $btn.data('item-type');
2683 2775 var listType = $btn.data('list-type');
2684 - var value = $btn.data('value');
2776 + // attr(), not data(): jQuery runs JSON.parse on values that look like
2777 + // JSON, so a User-Agent such as {"a":1} would arrive as an object.
2778 + var value = $btn.attr('data-value');
2685 2779
2686 2780 if (!itemType || !listType || !value) {
2687 2781 return;
2688 2782 }
@@ -2775,11 +2869,16 @@
2775 2869 if (text === null || text === undefined) return '';
2776 2870 if (typeof text !== 'string') {
2777 2871 text = String(text);
2778 2872 }
2779 - var div = document.createElement('div');
2780 - div.textContent = text;
2781 - return div.innerHTML;
2873 + // Quotes must be encoded too: this helper is also used in attribute
2874 + // position, where an unescaped quote closes the attribute early.
2875 + return text
2876 + .replace(/&/g, '&amp;')
2877 + .replace(/</g, '&lt;')
2878 + .replace(/>/g, '&gt;')
2879 + .replace(/"/g, '&quot;')
2880 + .replace(/'/g, '&#039;');
2782 2881 },
2783 2882
2784 2883 /**
2785 2884 * Debounce function
@@ -3324,9 +3423,9 @@
3324 3423 if (response.success && response.data.length > 0) {
3325 3424 var html = '<ul class="vigilante-user-search-list">';
3326 3425 $.each(response.data, function(i, user) {
3327 3426 html += '<li data-user-id="' + user.ID + '">' +
3328 - '<img src="' + user.avatar + '" class="vigilante-user-avatar" alt="">' +
3427 + '<img src="' + self.escapeHtml(user.avatar) + '" class="vigilante-user-avatar" alt="">' +
3329 3428 '<div class="vigilante-user-info">' +
3330 3429 '<strong>' + self.escapeHtml(user.display_name) + '</strong><br>' +
3331 3430 '<small>' + self.escapeHtml(user.user_email) + '</small>' +
3332 3431 '</div>' +
@@ -4164,10 +4263,17 @@
4164 4263 + '</svg>';
4165 4264 },
4166 4265
4167 4266 /**
4168 - * When landing on a fix link (#field-* or #vigilante-section-*),
4169 - * scroll smoothly and flash-highlight the target.
4267 + * When landing on a fix link (#field-*, #vigilante-section-* or any
4268 + * other #vigilante-* target), scroll smoothly and flash-highlight it.
4269 + *
4270 + * The pattern used to name the two families literally, which left the
4271 + * one anchor that follows neither out in the cold: the weekly report
4272 + * email links to #vigilante-analyzer, so that link landed on the
4273 + * dashboard with no scroll and no flash. Widened in 2.11.1 to the
4274 + * whole vigilante- prefix, which is still narrow enough that a hash
4275 + * written by anything else on the page cannot reach the selector.
4170 4276 */
4171 4277 hashFocusFlash: function() {
4172 4278 var hash = window.location.hash;
4173 4279 if (!hash || hash.length < 2) {
@@ -4172,10 +4278,10 @@
4172 4278 var hash = window.location.hash;
4173 4279 if (!hash || hash.length < 2) {
4174 4280 return;
4175 4281 }
4176 - // Accept both section anchors and field anchors.
4177 - if (!/^#(?:field-|vigilante-section-)/.test(hash)) {
4282 + // Accept field anchors and every anchor the plugin owns.
4283 + if (!/^#(?:field-|vigilante-)/.test(hash)) {
4178 4284 return;
4179 4285 }
4180 4286
4181 4287 setTimeout(function() {
@@ -4197,8 +4303,18 @@
4197 4303 $target.addClass('vigilante-focus-flash');
4198 4304 setTimeout(function() {
4199 4305 $target.removeClass('vigilante-focus-flash');
4200 4306 }, 1800);
4307 +
4308 + // And once more when everything has loaded: anything that
4309 + // changes height above the target (images, notices, the admin
4310 + // bar on a narrow screen) moves it after the first scroll, and
4311 + // then the link looks like it only opened the tab.
4312 + $(window).one('load', function() {
4313 + if ($target[0] && 'scrollIntoView' in $target[0]) {
4314 + $target[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
4315 + }
4316 + });
4201 4317 }, 150);
4202 4318 },
4203 4319
4204 4320 /** Escape text for HTML. */
@@ -4253,8 +4369,64 @@
4253 4369 complete: function() {
4254 4370 $btn.prop('disabled', false).text(original);
4255 4371 }
4256 4372 });
4373 + });
4374 + });
4375 +
4376 + /**
4377 + * Security Headers settings recovery (2.10.0).
4378 + *
4379 + * Both buttons are disabled together while the request is in flight, so a
4380 + * second click cannot fire a second restore. The page reloads afterwards
4381 + * because the whole tab is rendered from the settings that just changed.
4382 + */
4383 + $(function() {
4384 + var SELECTOR = '#vigilante-recovery-restore, #vigilante-recovery-dismiss';
4385 +
4386 + function recoveryRun(action) {
4387 + $(SELECTOR).prop('disabled', true);
4388 +
4389 + $.ajax({
4390 + url: vigilanteAdmin.ajaxUrl,
4391 + type: 'POST',
4392 + data: {
4393 + action: action,
4394 + nonce: vigilanteAdmin.nonce
4395 + },
4396 + success: function(response) {
4397 + if (response.success) {
4398 + Vigilante_Admin.showNotice('success', response.data);
4399 + setTimeout(function() {
4400 + location.reload();
4401 + }, 1200);
4402 + return;
4403 + }
4404 +
4405 + Vigilante_Admin.showNotice('error', response.data);
4406 + $(SELECTOR).prop('disabled', false);
4407 + },
4408 + error: function() {
4409 + Vigilante_Admin.showNotice('error', vigilanteAdmin.strings.error);
4410 + $(SELECTOR).prop('disabled', false);
4411 + }
4412 + });
4413 + }
4414 +
4415 + $(document).on('click', '#vigilante-recovery-restore', function(e) {
4416 + e.preventDefault();
4417 + recoveryRun('vigilante_headers_recovery_restore');
4418 + });
4419 +
4420 + $(document).on('click', '#vigilante-recovery-dismiss', function(e) {
4421 + e.preventDefault();
4422 + recoveryRun('vigilante_headers_recovery_dismiss');
4423 + });
4424 +
4425 + $(document).on('click', '#vigilante-recovery-undo', function(e) {
4426 + e.preventDefault();
4427 + $(this).prop('disabled', true);
4428 + recoveryRun('vigilante_headers_recovery_undo');
4257 4429 });
4258 4430 });
4259 4431
4260 4432 })(jQuery);