www/caddy: Create custom displayValidationErrors function in general.volt (#4096)

* www/caddy: Create custom displayValidationErrors function in general.volt that appends validation errors to form keys outside the scope of the main form.

* www/caddy: Add missing key to validationExceptions array.

* www/caddy: Make the custom validation the same style as core.
This commit is contained in:
Monviech
2024-07-16 13:02:07 +02:00
committed by GitHub
parent 46165c9ad7
commit 160c6515c2
@@ -26,11 +26,22 @@
<script type="text/javascript">
$(document).ready(function() {
let data_get_map = {'frm_GeneralSettings':"/api/caddy/General/get"};
const data_get_map = {'frm_GeneralSettings':"/api/caddy/General/get"};
mapDataToFormUI(data_get_map).done(function(data){
// Refresh selectpicker for these dropdowns
$('.selectpicker').selectpicker('refresh');
// Function to initialize form elements within a tab dynamically
function initializeFormElements(tabContent) {
$(tabContent).find('.selectpicker').selectpicker('refresh');
}
// Initialize the first tab
initializeFormElements('#generalTab');
// Handle tab changes
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
let targetTab = $(e.target).attr('href'); // Activated tab
initializeFormElements(targetTab);
});
// Function to show alerts in the HTML message area
function showAlert(message, type = "error") {
@@ -55,6 +66,55 @@
$("#messageArea").hide();
});
// These fields do not need the validation workaround, they get their validation messages from core.
let validationExceptions = [
"caddy.general.enabled",
"caddy.general.DisableSuperuser",
"caddy.general.HttpPort",
"caddy.general.HttpsPort",
"caddy.general.TlsEmail",
"caddy.general.TlsAutoHttps",
"caddy.general.accesslist",
"caddy.general.abort",
"caddy.general.GracePeriod"
];
// For all other fields that are in different tabs than the main form, append the validation message.
// Note: This is a workaround and generally not needed. Do not copy this.
function displayValidationErrors(errors) {
$(".error-message").remove(); // Clear previous error messages
$(".error-input").removeClass("error-input");
$(".error-label").removeClass("error-label");
for (let key in errors) {
if (errors.hasOwnProperty(key) && !validationExceptions.includes(key)) {
let jquerySafeKey = key.replace(/\./g, '\\.'); // Escape dots for jQuery ID selector
let field = $('#' + jquerySafeKey);
let label = $('#control_label_' + jquerySafeKey);
let helpBlock = $('#help_block_' + jquerySafeKey);
if (field.length !== 0) {
field.addClass('error-input');
label.addClass('error-label');
let errorMessage = $('<div class="error-message">' + errors[key] + '</div>');
helpBlock.html(errorMessage); // append error message into help block
}
}
}
}
$('input, select, textarea').on('change', function() {
// Remove error styles when the user corrects the input
if($(this).hasClass('error-input')) {
$(this).removeClass('error-input');
let id = this.id.replace(/\./g, '\\.');
let label = $('#control_label_' + id);
label.removeClass('error-label');
let helpBlock = $('#help_block_' + id);
helpBlock.empty();
}
});
// Reconfigure the Caddy service, additional form save and validation with a validation API is made beforehand
$("#reconfigureAct").SimpleActionButton({
onPreAction: function() {
@@ -82,10 +142,13 @@
}
});
},
false, // disable_dialog: Show the dialog with the validation error
true, // disable_dialog: This has to be set explicitely so there actually is a callback_fail to catch the validation error.
function(errorData) { // callback_fail: What to do when save fails
// Handle failure due to validation errors or other issues
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
if (errorData.validations) {
displayValidationErrors(errorData.validations);
} else {
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
}
dfObj.reject(); // Reject the deferred object to stop the reconfigure action
}
);
@@ -114,9 +177,13 @@
showAlert("{{ lang._('Configuration saved successfully. Please do not forget to apply the configuration.') }}", "{{ lang._('Save Success') }}");
dfObj.resolve();
},
false, // disable_dialog: Show the dialog with the validation error
true, // disable_dialog
function(errorData) { // callback_fail: What to do when save fails
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
if (errorData.validations) {
displayValidationErrors(errorData.validations);
} else {
showAlert("{{ lang._('Configuration save failed: ') }}" + (errorData.message || "{{ lang._('Validation Error') }}"), "{{ lang._('Error') }}");
}
dfObj.reject();
}
);
@@ -132,6 +199,23 @@
});
</script>
<style>
.error-message {
color: #E55451;
margin-left: 10px; /* Adjust spacing to the right of the input field */
}
.form-control.error-input {
border: 1px solid #E55451;
padding: 2px 8px;
box-sizing: border-box;
}
.error-label {
color: #E55451;
}
</style>
<!-- Tab Navigation -->
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#generalTab">{{ lang._('General') }}</a></li>
@@ -141,7 +225,9 @@
<li><a data-toggle="tab" href="#logSettingsTab">{{ lang._('Log Settings') }}</a></li>
</ul>
<!-- Tab Content -->
<!-- Tab Content
Note: Do not copy this tab layout, it uses the same form id "frm_GeneralSettings". It works, but has problems with validation messages.
To fix this issue a custom displayValidationErrors function is used, that appends the messages to all keys. -->
<div class="tab-content content-box">
<!-- General Tab -->
<div id="generalTab" class="tab-pane fade in active">