');
}
// Format secret in groups of 4 for readability
var formattedSecret = d.secret.replace(/(.{4})/g, '$1 ').trim();
$('.vigilante-totp-secret-display').text(formattedSecret);
$('.vigilante-totp-setup-qr').data('secret', d.secret).show();
$('#vigilante_totp_verify_code').focus();
} else {
$btn.prop('disabled', false).text('Start setup');
alert(response.data || 'Error');
}
});
},
confirmSetup: function() {
var self = this;
var c = this.cfg();
// Separators are presentation: a password manager shows the code
// as "123 456" and pastes it that way. The server drops everything
// that is not a digit too; this is only so the length check below
// measures the code and not the spaces.
var code = $('#vigilante_totp_verify_code').val().replace(/\D/g, '');
var secret = $('.vigilante-totp-setup-qr').data('secret');
var $status = $('.vigilante-totp-setup-status');
var $btn = $('.vigilante-totp-confirm-setup');
if (!code || code.length !== 6) {
$status.text('Enter a valid 6-digit code.').addClass('error');
return;
}
$btn.prop('disabled', true);
$status.text(c.strings.verifying || 'Verifying...').removeClass('error success');
$.post(c.ajaxUrl, {
action: 'vigilante_totp_verify_setup',
nonce: c.nonce,
user_id: self.getUserId(),
code: code,
secret: secret
}, function(response) {
if (response.success) {
$status.text('').removeClass('error');
$('.vigilante-totp-setup-qr').hide();
var $success = $('.vigilante-totp-setup-success');
if (response.data.backup_codes) {
self.renderCodes(response.data.backup_codes, $success.find('.vigilante-totp-backup-codes-display'));
}
$success.show();
} else {
$btn.prop('disabled', false);
$status.text(response.data || 'Invalid code.').addClass('error');
}
});
},
regenerateBackup: function($btn) {
var self = this;
var c = this.cfg();
if (!confirm(c.strings.confirmRegen || 'This will invalidate all existing backup codes. Continue?')) return;
$btn.prop('disabled', true);
$.post(c.ajaxUrl, {
action: 'vigilante_totp_regenerate_backup',
nonce: c.nonce,
user_id: $btn.data('user')
}, function(response) {
$btn.prop('disabled', false);
if (response.success && response.data.backup_codes) {
var $container = $btn.closest('td').find('.vigilante-totp-backup-codes-display');
self.renderCodes(response.data.backup_codes, $container);
}
});
},
reconfigure: function($btn) {
var c = this.cfg();
// The button carries its own question when it removes a setup nothing asks for (2.11.11).
if (!confirm($btn.data('confirm') || c.strings.confirmReconfig || 'This will reset your current TOTP setup. Continue?')) return;
$btn.prop('disabled', true).text('Resetting...');
$.post(c.ajaxUrl, {
action: 'vigilante_totp_reconfigure',
nonce: c.nonce,
user_id: $btn.data('user')
}, function(response) {
if (response.success) {
window.location.reload();
} else {
$btn.prop('disabled', false).text($btn.data('original-text') || 'Set up new authenticator');
alert(response.data || 'Error');
}
}).fail(function() {
$btn.prop('disabled', false);
});
},
renderCodes: function(codes, $container) {
var c = this.cfg();
var html = '
';
html += '
';
html += (c.strings.saveBackupCodes || 'Save these backup codes now. They will not be shown again.');
html += '
';
$.each(codes, function(i, code) {
html += '' + (i + 1) + '. ' + code + '';
});
html += '
';
$container.html(html).show();
}
};
// =========================================================================
// Password expiration: per-user exclusion search
// Same UX as the 2FA exclusion picker, reusing the existing CSS classes.
// =========================================================================
var VigilantePwExpExclusion = {
searchTimeout: null,
minSearchLength: 2,
init: function() {
if (!$('#vigilante_pwexp_user_search').length) return;
this.bindEvents();
},
bindEvents: function() {
var self = this;
$(document).on('input', '#vigilante_pwexp_user_search', function() {
self.handleSearch($(this));
});
$(document).on('focus', '#vigilante_pwexp_user_search', function() {
var $r = $(this).siblings('.vigilante-pwexp-search-results');
if ($r.children().length > 0) $r.addClass('active');
});
$(document).on('click', '.vigilante-pwexp-search-results .search-result-item', function() {
self.addExcludedUser($(this));
});
$(document).on('click', '.vigilante-pwexp-excluded-user .remove-user', function() {
$(this).closest('.vigilante-pwexp-excluded-user').remove();
});
$(document).on('click', function(e) {
if (!$(e.target).closest('#vigilante_pwexp_user_search').length &&
!$(e.target).closest('.vigilante-pwexp-search-results').length) {
$('.vigilante-pwexp-search-results').removeClass('active');
}
});
},
handleSearch: function($input) {
var self = this;
var query = $input.val().trim();
var $results = $input.siblings('.vigilante-pwexp-search-results');
if (this.searchTimeout) clearTimeout(this.searchTimeout);
if (query.length < this.minSearchLength) {
$results.removeClass('active').empty();
return;
}
$results.addClass('active').html('