update services and simple mode

* refactored services.js to handle simple mode (update hidden input for simple mode form, update form setup logic, update render data logic, ...)
* update simple mode template style and add new elements
* update input, select and checkbox block to handle simple mode (no global config matching cases, more fallback) + fix some miss
* update settings_simple template to fit data send
This commit is contained in:
Jordan Blasenhauer 2024-04-22 16:22:17 +02:00
parent dce5ec484a
commit deaf6644bb
9 changed files with 311 additions and 306 deletions

View file

@ -16,9 +16,6 @@ class ServiceModal {
this.modalTabsHeader = this.modal.querySelector([
"[data-services-tabs-select-header]",
]);
this.modalErrMsg = this.modal.querySelector(
"[data-services-modal-error-msg]",
);
this.modalCard = this.modal.querySelector("[data-services-modal-card]");
this.switchModeBtn = this.modal.querySelector(
"[data-toggle-settings-mode-btn]",
@ -31,6 +28,15 @@ class ServiceModal {
this.submitBtn = document.querySelector(
"button[data-services-modal-submit]",
);
// simple el
this.simpleForm = this.modal.querySelector("[data-services-simple-modal-form]");
this.nextBtn = this.modal.querySelector("button[data-simple-next]");
this.backBtn = this.modal.querySelector("button[data-simple-back]");
this.saveSimpleBtn = this.modal.querySelector("button[data-services-simple-modal-submit]");
// list els
this.serverNameInps = this.modal.querySelectorAll('input[name="SERVER_NAME"][data-setting-input]');
this.submitBtns = this.modal.querySelectorAll('button[data-services-modal-submit]');
//container
this.container = document.querySelector("main");
this.currAction = "";
@ -66,7 +72,17 @@ class ServiceModal {
}
init() {
this.disabledSaveCases();
// Get list of server name from services card
// When the server name input is matching existing server name that is not the current modal one
// Disable the submit button
window.addEventListener("DOMContentLoaded", () => {
this.serverNameInps.forEach((serverNameInput) => {
serverNameInput.addEventListener("input", () => {
// Case input is empty
this.checkServNameInput();
});
});
});
this.modal.addEventListener("click", (e) => {
// update draft mode
try {
@ -77,9 +93,10 @@ class ServiceModal {
.classList.contains("hidden")
? true
: false;
this.setIsDraft(currModeIsDraft, this.currMethod);
}
} catch (e) {}
} catch (err) { }
//close
try {
@ -115,6 +132,7 @@ class ServiceModal {
.closest("[data-services-service]")
.querySelector("[data-old-service-name]")
.getAttribute("data-value");
this.setForm(
action,
serviceName,
@ -130,10 +148,8 @@ class ServiceModal {
.querySelector("[data-services-settings]")
.getAttribute("data-value");
const obj = JSON.parse(servicesSettings);
this.updateModalData(obj);
this.updateModalData(obj, false, false, false);
//show modal
this.resetFilterInp();
this.changeSubmitBtn("SAVE", "valid-btn");
this.openModal();
}
} catch (err) {}
@ -163,17 +179,7 @@ class ServiceModal {
.querySelector("[data-services-settings]")
.getAttribute("data-value");
const obj = JSON.parse(servicesSettings);
this.updateModalData(obj, true, true);
// server name is unset
const inpServName = document.querySelector("input#SERVER_NAME");
inpServName.getAttribute("value", "");
inpServName.removeAttribute("disabled", "");
inpServName.value = "";
// clone is UI creation, so no setting should be disabled
//show modal
this.resetFilterInp();
this.changeSubmitBtn("CREATE", "valid-btn");
this.updateModalData(obj, true, true, true, true);
this.openModal(); //server name is unset
}
} catch (err) {}
@ -196,16 +202,7 @@ class ServiceModal {
method,
);
//set default value with method default
this.setSettingsDefault();
//server name is unset
const inpServName = document.querySelector("input#SERVER_NAME");
inpServName.getAttribute("value", "");
inpServName.removeAttribute("disabled", "");
inpServName.value = "";
//show modal
this.resetFilterInp();
this.changeSubmitBtn("CREATE", "valid-btn");
this.updateModalData({}, true, true, true, true);
this.openModal();
}
} catch (err) {}
@ -234,21 +231,57 @@ class ServiceModal {
});
}
resetSimpleForm() {
// reset button
this.backBtn.setAttribute("disabled", "");
this.nextBtn.removeAttribute("disabled");
// set current security level to input
}
resetFilterInp() {
const inpFilter = document.querySelector('input[name="settings-filter"]');
inpFilter.value = "";
inpFilter.dispatchEvent(new Event("input"));
}
changeSubmitBtn(text, btnType) {
this.submitBtn.textContent = text;
this.submitBtn.classList.remove(
"delete-btn",
"valid-btn",
"edit-btn",
"info-btn",
);
this.submitBtn.classList.add(btnType);
changeSubmitBtn(action) {
if(action === "delete") return;
const text = action === "edit" ? "SAVE" : "CREATE";
this.submitBtns.forEach((btn) => {
btn.textContent = text;
btn.classList.remove(
"delete-btn",
"valid-btn",
"edit-btn",
"info-btn",
);
btn.classList.add("valid-btn");
});
}
resetServerName() {
this.serverNameInps.forEach((inpServName) => {
inpServName.getAttribute("value", "");
inpServName.removeAttribute("disabled", "");
inpServName.value = "";
});
}
isAvoidInpList(inp, inpName) {
if (
inpName === "csrf_token" ||
inpName === "OLD_SERVER_NAME" ||
inpName === "mode" ||
inpName === "security-level" ||
inpName === "is_draft" ||
inpName === "operation" ||
inpName === "settings-filter" ||
inp.hasAttribute("data-combobox")
)
return true;
return false;
}
setSettingsDefault() {
@ -256,15 +289,8 @@ class ServiceModal {
inps.forEach((inp) => {
//form related values are excludes
const inpName = inp.getAttribute("name");
if (
inpName === "csrf_token" ||
inpName === "OLD_SERVER_NAME" ||
inpName === "is_draft" ||
inpName === "operation" ||
inpName === "settings-filter" ||
inp.hasAttribute("data-combobox")
)
return;
if (this.isAvoidInpList(inp, inpName)) return;
//for all other settings values
const defaultMethod = inp.getAttribute("data-default-method");
@ -334,17 +360,18 @@ class ServiceModal {
setIsDraft(isDraft, method) {
const draftVal = isDraft ? "yes" : "no";
document.querySelectorAll('input[name="is_draft"]').forEach((inp) => {
const draftInps = this.modal.querySelectorAll('input[name="is_draft"]');
draftInps.forEach((inp) => {
inp.setAttribute("value", draftVal);
inp.value = draftVal;
});
//Update draft button
const btn = document.querySelector("button[data-toggle-draft-btn]");
const btn = this.modal.querySelector("button[data-toggle-draft-btn]");
if (
(!["ui", "default"].includes(method) && this.currAction !== "clone") ||
this.currAction === "delete"
(!["ui", "default"].includes(method) && this.currAction !== "clone")
) {
return btn.classList.add("hidden");
}
@ -362,63 +389,48 @@ class ServiceModal {
setForm(action, serviceName, oldServName, formEl, isDraft, method) {
this.currAction = action;
this.setIsDraft(isDraft === "yes" ? true : false, method);
this.modalTitle.textContent = `${action} ${serviceName}`;
const operation = action === "clone" ? "new" : action;
// update operation and other hidden inputs for all mode in modal
const operationInps = this.modal.querySelectorAll('input[name="operation"]');
operationInps.forEach((inp) => {
inp.setAttribute("value", operation);
inp.value = operation;
});
// show / hide components
this.hideForms();
this.setCardViewportHeight(action === "delete" ? false : true);
this.SetSelectTabsVisible(action === "delete" ? false : true);
this.setHeaderActionsVisible(action === "delete" ? false : true);
this.changeSubmitBtn(action);
formEl.setAttribute("id", `form-${operation}-${serviceName}`);
const opeInp = formEl.querySelector(`input[name="operation"]`);
opeInp.setAttribute("value", operation);
opeInp.value = operation;
if (action === "edit" || action === "new" || action === "clone") {
this.formNewEdit.classList.remove("hidden");
this.simpleForm.classList.remove("hidden");
if (action === "edit" || action === "new") {
this.showNewEditForm();
const oldNameInp = formEl.querySelector(`input[name="OLD_SERVER_NAME"]`);
oldNameInp.setAttribute("value", oldServName);
oldNameInp.value = oldServName;
}
const oldNameValue = action === "edit" ? oldServName : "";
const oldNameInps = this.modal.querySelectorAll('input[name="OLD_SERVER_NAME"]');
oldNameInps.forEach((inp) => {
inp.setAttribute("value", oldNameValue);
inp.value = oldNameValue;
});
if (action === "clone") {
this.showNewEditForm();
const oldNameInp = formEl.querySelector(`input[name="OLD_SERVER_NAME"]`);
oldNameInp.setAttribute("value", "");
oldNameInp.value = "";
}
if (action === "delete") {
this.showDeleteForm();
this.formDelete.classList.remove("hidden");
formEl.querySelector(`[data-services-modal-text]`).textContent =
`Are you sure you want to delete ${serviceName} ?`;
const nameInp = formEl.querySelector(`input[name="SERVER_NAME"]`);
nameInp.setAttribute("value", serviceName);
nameInp.value = serviceName;
}
}
// Get list of server name from services card
// When the server name input is matching existing server name that is not the current modal one
// Disable the submit button
disabledSaveCases() {
window.addEventListener("DOMContentLoaded", () => {
const serverNameInput = document.querySelector(
'input[name="SERVER_NAME"]',
);
this.setIsDraft(isDraft === "yes" ? true : false, method);
window.addEventListener("click", (e) => {
if (e.target.hasAttribute("data-services-action")) {
// focus on input server name
serverNameInput.focus();
this.checkServNameInput();
}
});
serverNameInput.addEventListener("input", () => {
// Case input is empty
this.checkServNameInput();
});
});
}
// Switch settings mode and update button
@ -450,86 +462,94 @@ class ServiceModal {
}
checkServNameInput() {
const serverNameInput = document.querySelector('input[name="SERVER_NAME"]');
if (serverNameInput.value === "") {
this.modalErrMsg.textContent = "Server name cannot be empty";
this.modalErrMsg.classList.remove("hidden");
return this.submitBtn.setAttribute("disabled", "");
}
// Case conflict with another server name
const serverNames = document.querySelectorAll("[data-services-service]");
const serverNameValue = serverNameInput.getAttribute("value");
// Case inp name is current server name
if (serverNameInput.value === serverNameValue) {
return this.submitBtn.removeAttribute("disabled");
}
// case inp name is not current server name, check if it is same as another
for (let i = 0; i < serverNames.length; i++) {
const name = serverNames[i].getAttribute("data-services-service");
if (name === serverNameValue) continue;
if (name === serverNameInput.value) {
this.modalErrMsg.textContent = "Server name already exists";
this.modalErrMsg.classList.remove("hidden");
return this.submitBtn.setAttribute("disabled", "");
this.serverNameInps.forEach((serverNameInput) => {
const modalErrMsg = serverNameInput.closest("form").querySelector("[data-services-modal-error-msg]");
if (serverNameInput.value === "") {
modalErrMsg.textContent = "Server name cannot be empty";
modalErrMsg.classList.remove("hidden");
return serverNameInput.closest("form").querySelector('button[data-services-modal-submit]').setAttribute("disabled", "");
}
// Case conflict with another server name
const serverNames = document.querySelectorAll("[data-services-service]");
const serverNameValue = serverNameInput.getAttribute("value");
// case inp name is not current server name, check if it is same as another
for (let i = 0; i < serverNames.length; i++) {
const name = serverNames[i].getAttribute("data-services-service");
if (name === serverNameValue) continue;
if (name === serverNameInput.value) {
modalErrMsg.textContent = "Server name already exists";
modalErrMsg.classList.remove("hidden");
return serverNameInput.closest("form").querySelector('button[data-services-modal-submit]').setAttribute("disabled", "");
}
}
modalErrMsg.classList.add("hidden");
return serverNameInput.closest("form").querySelector('button[data-services-modal-submit]').removeAttribute("disabled");
})
}
setHeaderActionsVisible(setVisible) {
if (setVisible) {
this.modal.querySelector('[data-toggle-draft-btn]').classList.remove("hidden");
this.modal.querySelector('[data-toggle-settings-mode-btn]').classList.remove("hidden");
}
this.modalErrMsg.textContent = "";
this.modalErrMsg.classList.add("hidden");
return this.submitBtn.removeAttribute("disabled");
if (!setVisible) {
this.modal.querySelector('[data-toggle-draft-btn]').classList.add("hidden");
this.modal.querySelector('[data-toggle-settings-mode-btn]').classList.add("hidden");
}
}
showNewEditForm() {
this.cardViewport();
this.showSelectTabs();
this.hideForms();
this.formNewEdit.classList.remove("hidden");
}
setCardViewportHeight(setAsViewport) {
showDeleteForm() {
this.cardNoViewport();
this.hideSelectTabs();
this.hideForms();
if(setAsViewport) {
this.modalCard.classList.add("h-[90vh]");
this.modalCard.classList.add("w-full");
}
this.formDelete.classList.remove("hidden");
}
cardViewport() {
this.modalCard.classList.add("h-[90vh]");
this.modalCard.classList.add("w-full");
}
cardNoViewport() {
this.modalCard.classList.remove("h-[90vh]");
this.modalCard.classList.remove("w-full");
if(!setAsViewport) {
this.modalCard.classList.remove("h-[90vh]");
this.modalCard.classList.remove("w-full");
}
}
hideForms() {
this.formNewEdit.classList.add("hidden");
this.simpleForm.classList.add("hidden");
this.formDelete.classList.add("hidden");
}
hideSelectTabs() {
this.modalTabs.classList.remove("grid");
this.modalTabs.classList.add("hidden");
SetSelectTabsVisible(setVisible) {
if (setVisible) {
this.modalTabs.classList.add("grid");
this.modalTabs.classList.remove("hidden");
this.modalTabsHeader.classList.add("flex");
this.modalTabsHeader.classList.remove("hidden");
}
this.modalTabsHeader.classList.remove("flex");
this.modalTabsHeader.classList.add("hidden");
if (!setVisible) {
this.modalTabs.classList.remove("grid");
this.modalTabs.classList.add("hidden");
this.modalTabsHeader.classList.remove("flex");
this.modalTabsHeader.classList.add("hidden");
}
}
showSelectTabs() {
this.modalTabs.classList.add("grid");
this.modalTabs.classList.remove("hidden");
this.modalTabsHeader.classList.add("flex");
this.modalTabsHeader.classList.remove("hidden");
}
updateModalData(settings, forceEnabled = false, setMethodUI = false) {
//use this to select inputEl and change value
updateModalData(settings = {}, forceEnabled = false, setMethodUI = false, emptyServerName = false) {
// check if at least one key in settings
if (Object.keys(settings).length === 0) {
this.setSettingsDefault();
}
if (Object.keys(settings).length > 0) {
//use this to select inputEl and change value
for (const [key, data] of Object.entries(settings)) {
//change format to match id
const value = data["value"];
@ -541,14 +561,7 @@ class ServiceModal {
inps.forEach((inp) => {
//form related values are excludes
const inpName = inp.getAttribute("name");
if (
inpName === "csrf_token" ||
inpName === "OLD_SERVER_NAME" ||
inpName === "is_draft" ||
inpName === "operation" ||
inpName === "settings-filter"
)
return;
if (this.isAvoidInpList(inp, inpName)) return;
//SET DISABLED / ENABLED
//for regular input
@ -598,6 +611,12 @@ class ServiceModal {
});
} catch (err) {}
}
}
this.resetFilterInp();
if(emptyServerName) this.resetServerName();
this.checkServNameInput();
}
setDisabledState(inp, method, global) {
@ -1069,7 +1088,9 @@ class Multiple {
if (
inpName === "csrf_token" ||
inpName === "OLD_SERVER_NAME" ||
inpName === "is_draft" ||
inpName === "is_draft" ||
inpName === "mode" ||
inpName === "security-level" ||
inpName === "operation" ||
inpName === "settings-filter"
)
@ -1096,7 +1117,7 @@ class Multiple {
inp.checked = true;
}
if (inp.getAttribute("type") !== "checkbox") {
if (inp.getAttribute("type") !== "checkbox" ) {
inp.setAttribute("value", value);
inp.value = value;
inp.setAttribute("data-method", method);

View file

@ -99,6 +99,8 @@
<input type="hidden" id="operation" value="new" name="operation" />
<input type="hidden" value="new" name="OLD_SERVER_NAME" />
<input type="hidden" value="no" name="is_draft" />
<input type="hidden" value="mode" name="advanced" />
{% include "settings_plugins.html" %}
<!-- action button -->
<div class="w-full flex-col items-center justify-center flex mt-10">

View file

@ -1,9 +1,21 @@
{% set steps = [
{
"name" : "STEP 1 - SERVER NAME",
"description" : "We need this to...",
"name" : "STEP 1 - STARTING UP",
"description" : "we need some information to get started.",
"settings" : [
{"plugin_id" : "general", "setting_id": "SERVER_NAME", "label" : "What is your host ?", "levels" : {"standard" : "", "advanced" : "", "high" : ""} },
{"plugin_id" : "general", "setting_id": "SERVER_NAME", "title" : "DEFINE HOST", "subtitle" : "We need this to connect BunkerWeb to your application. You need to set your domain name.", "levels" : False },
{"plugin_id" : "security-level", "setting_id": "SECURITY_LEVEL", "title" : "DEFINE SECURITY LEVEL", "subtitle" : "This will determine default settings value for the next steps. You'll be allow to modify settings to match your case if needed.", "levels" : False, "setting" : {'context': 'global', 'default': 'standard', 'help': 'Determine the default settings value. You can override them.', 'id': 'security-level', 'label': 'Security level', 'regex': '^(standard|advanced|bunker)$', 'type': 'select', 'select': ['standard', 'advanced', 'bunker']} }
],
"configs": [
{"name": "my_config_1", "type": "modsec", "data": "..."},
{"name": "my_config_2", "type": "server-http", "data": "..."}
]
},
{
"name" : "STEP 2 - ANTIBOT",
"description" : "Avoid spammer and bot to access your website.",
"settings" : [
{"plugin_id" : "antibot", "setting_id": "USE_ANTIBOT", "title" : "Define the type of your antibot", "subtitle" : "Javascript, Captcha or Cookie don't need additionnal settings to be fill. Recaptcha, Hcaptcha and Turnstile need secret key and site key delivered from providers.", "levels" : {"standard" : "no", "advanced" : "no", "high" : "no"} },
],
"configs": [
{"name": "my_config_1", "type": "modsec", "data": "..."},
@ -31,22 +43,24 @@
id="form-simple-new"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" id="simple_operation" value="new" name="simple_operation" />
<input type="hidden" value="" name="simple_OLD_SERVER_NAME" />
<input type="hidden" value="no" name="simple_is_draft" />
<input type="hidden" value="yes" name="simple_is_simple_mode" />
<input type="hidden" value="new" name="operation" />
<input type="hidden" value="" name="OLD_SERVER_NAME" />
<input type="hidden" value="no" name="is_draft" />
<input type="hidden" value="mode" name="easy" />
{% for step in steps %}
<div data-step="{{loop.index}}" class="flex flex-col">
<div data-step="{{loop.index}}" class="flex flex-col {% if loop.index != 1 %} hidden {% endif %}">
<div class="flex flex-col w-full items-start mt-2">
<h2 class="ml-2 mr-1 text-xl transition duration-300 ease-in-out font-bold text-md uppercase dark:text-white/90 mb-0" data-simple-step>{{ step.get("name")}}</h2>
<p class="ml-2 mr-1 text-sm dark:text-gray-300 mb-1" data-simple-description>{{ step.get("description")}}</p>
<p class="ml-2 mr-1 text-base dark:text-gray-300 mb-1" data-simple-description>{{ step.get("description")}}</p>
</div>
<div class="w-full min-w-[300px] my-1 sm:my-0">
<hr class="separator" />
</div>
{% set plugin_simple = step.get('settings') %}
{% include "settings_simple.html" %}
</div>
</div>
{% endfor %}
<!-- action button -->
@ -55,10 +69,15 @@
<button data-services-simple-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button data-services-simple-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
<button data-simple-back
type="button"
disabled
class="dark:bg-slate-800 info-btn mb-4 mr-3 text-base">Back</button>
<button data-simple-next type="button" class="mb-4 valid-btn">Continue</button>
<button data-services-modal-submit type="submit" class="hidden mb-4 valid-btn">Save</button>
</div>
<!-- end action button-->
<p data-services-simple-modal-error-msg
<p data-services-modal-error-msg
class="hidden text-red-500 font-bold dark:opacity-80 mb-0 text-center"></p>
</div>
</form>

View file

@ -1,23 +1,28 @@
{% if setting_input["type"] == "check" %}
{% if setting_input and setting_input["type"] == "check" %}
{% set inp_name = setting_input['name'] %}
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
{% set inp_id = setting_input['id'] %}
{% set inp_regex = setting_input['regex'] %}
{% set inp_default = setting_input['default'] %}
{% set inp_value = setting_input['value'] %}
{% set global_config_method = global_config.get(inp_name, {'method' : inp_method }).get('method') %}
{% set global_config_value = global_config.get(inp_name, {'value' : inp_value }).get('value') %}
<div data-checkbox-handler="{{ inp_id }}"
class="relative mb-7 md:mb-0 z-10 ">
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
<input id="{{ inp_name_mult }}"
name="{{ inp_name_mult }}"
data-default-method="{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] %}mode{% else %}{{ global_config[setting]['method'] }}{% endif %}"
data-default-method="{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] %}mode{% else %}{{ global_config_method }}{% endif %}"
data-default-value="{{ global_config[inp_name]['value'] }}"
{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] or global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %} disabled {% endif %}
data-checked="{% if global_config[inp_name]['value'] == "yes" %}true{% else %}false{% endif %}"
{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] or global_config_method != 'ui' and global_config_method != 'default' %} disabled {% endif %}
data-checked="{% if global_config_value == "yes" %}true{% else %}false{% endif %}"
checked
id="checkbox-{{ inp_id }}"
class="checkbox"
type="checkbox"
data-pattern="{{ setting_input['regex']|safe }}"
value="{{ global_config[inp_name]['value'] }}" />
data-pattern="{{ inp_regex|safe }}"
value="{{ global_config_value }}" />
<svg data-checkbox-handler="{{ inp_id }}"
class="pointer-events-none absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
xmlns="http://www.w3.org/2000/svg"

View file

@ -2,7 +2,6 @@
{% set inp_name = setting_input['name'] %}
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
{% set inp_name_multisite = inp_name + "-multisite_SCHEMA" if setting_input["is_multiple"] else inp_name %}
{% set inp_label = setting_input['label'] %}
{% set inp_help = setting_input['help'] %}
{% set inp_context = setting_input['context'] %}

View file

@ -1,23 +1,27 @@
{% if value["type"] != "select" and value["type"] != "check" %}
{% if setting_input and setting_input["type"] != "select" and setting_input["type"] != "check" %}
{% set inp_name = setting_input['name'] %}
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
{% set inp_type = setting_input['type'] %}
{% set inp_default = setting_input['default'] %}
{% set inp_value = setting_input['value'] %}
{% set inp_regex = setting_input['regex'] %}
{% set global_config_method = global_config.get(inp_name, {'method' : inp_method }).get('method') %}
{% set global_config_value = global_config.get(inp_name, {'value' : inp_value }).get('value') %}
<div class="relative flex items-center">
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
<input {% if inp_name == "SERVER_NAME" %}required{% endif %}
data-default-value="{{ global_config[inp_name]['value'] }}"
data-default-method="{{ global_config[inp_name]['method'] }}"
data-default-value="{{ global_config_value }}"
data-default-method="{{ global_config_method }}"
data-setting-input
{% if global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %}disabled{% endif %}
{% if global_config_method != 'ui' and global_config_method != 'default' %}disabled{% endif %}
id="{{ inp_name_mult }}"
name="{{ inp_name_mult }}"
class="regular-input"
value="{% if global_config[inp_name]['value'] %} {{ global_config[inp_name]['value'] }} {% else %} {{ inp_default }} {% endif %}"
value="{% if global_config_value %} {{ global_config_value }} {% else %} {{ inp_default }} {% endif %}"
type="{{ inp_type }}"
pattern="{{ value['regex']|safe }}" />
pattern="{{ inp_regex|safe }}" />
{% if inp_type == "password" %}
<div data-setting-password-container class="absolute flex right-2 h-5 w-5">
<button type="button"

View file

@ -1,12 +1,17 @@
{% if value["type"] == "select" %}
{% if setting_input and setting_input["type"] == "select" %}
{% set inp_name = setting_input['name'] %}
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
{% set inp_default = setting_input['default'] %}
{% set inp_value = setting_input['value'] %}
{% set inp_method = setting_input['method'] %}
{% set inp_items = setting_input['select'] %}
{% set inp_id = setting_input['id'] %}
{% set inp_is_simple = setting_input['is_simple'] %}
{% set global_config_method = global_config.get(inp_name, {'method' : inp_method }).get('method') %}
{% set global_config_value = global_config.get(inp_name, {'value' : inp_value }).get('value') %}
<!-- default hidden-->
<select data-default-method="{{ global_config[inp_name]['method'] }}"
<select data-default-method="{{ global_config_method }}"
data-default-value="{{ inp_default }}"
id="{{ inp_name_mult }}"
name="{{ inp_name_mult }}"
@ -16,7 +21,7 @@
{% for item in inp_items %}
<option {% if not item %}label="empty"{% endif %}
value="{{ item }}"
{% if global_config[inp_name]['value'] and global_config[inp_name]['value'] == item or not global_config[inp_name]['value'] and inp_default == item %} selected{% endif %}>
{% if global_config_value and global_config_value == item or not global_config_value and inp_default == item %} selected{% endif %}>
{{ item }}
</option>
{% endfor %}
@ -24,19 +29,19 @@
<!-- end default hidden-->
<!--custom-->
<div data-select-container class="relative">
<button {% if global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %}disabled{% endif %}
<button {% if global_config_method != 'ui' and global_config_method != 'default' %}disabled{% endif %}
data-setting-select="{{ inp_id }}"
data-default-value="{{ global_config[inp_name]['value'] }}"
data-default-method="{{ global_config[inp_name]['method'] }}"
aria-controls="{{ value['id'] }}-dropdown"
data-default-value="{{ global_config_value }}"
data-default-method="{{ global_config_method }}"
aria-controls="{{ inp_id }}-dropdown"
type="button"
class="custom-select-btn">
{% for item in inp_items %}
{% if global_config[inp_name]['value'] and
global_config[inp_name]['value'] == item %}
{% if global_config_value and
global_config_value == item %}
<span data-setting-select-text="{{ inp_id }}"
data-value="{{ global_config[inp_name]['value'] }}">{{ global_config[inp_name]['value'] }}</span>
{% elif not global_config[inp_name]['value'] and inp_default == item %}
data-value="{{ global_config_value }}">{{ global_config_value }}</span>
{% elif not global_config_value and inp_default == item %}
<span aria-description="current value"
data-setting-select-text="{{ inp_id }}"
data-value="{{ inp_default }}">{{ inp_default }}</span>
@ -57,8 +62,8 @@
data-setting-select-dropdown="{{ inp_id }}"
class="hidden z-[20] fixed h-full flex-col mt-2 max-h-[200px] overflow-auto">
{% for item in inp_items %}
{% if global_config[inp_name]['value'] and
global_config[inp_name]['value'] == item or not global_config[setting_input['name']]['value']
{% if global_config_value and
global_config_value == item or not global_config_value
and inp_default == item %}
<button role="option"
value="{{ item }}"

View file

@ -52,7 +52,7 @@
<div data-plugin-settings class="w-full grid grid-cols-12">
<!-- plugin settings not multiple -->
{% for setting, value in plugin["settings"].items() %}
{% set setting_input = { "name" : setting, "context" : value.get("context"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : False} %}
{% set setting_input = { "name" : setting, "context" : value.get("context"), "method" : value.get("method"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "value" : value.get("value"), "is_multiple" : False} %}
{% if setting != "IS_DRAFT" and (current_endpoint == "global-config" and setting not in ["SERVER_NAME", "IS_LOADING"] or current_endpoint == "services" and value['context'] == "multisite") %}
{% if value['multiple'] and value['multiple'] not in multList %}
@ -96,7 +96,7 @@
<!-- multiple settings -->
<div data-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full my-4 grid-cols-12 border dark:border-gray-700 rounded">
{% for setting, value in plugin["settings"].items() %}
{% set setting_input = { "name" : setting, "context" : value.get("context"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : False} %}
{% set setting_input = { "name" : setting, "context" : value.get("context"), "method" : value.get("method"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "value" : value.get("value"), "is_multiple" : True} %}
{# render only setting that match the multiple id and context #}
{% if value['multiple'] == multiple and (

View file

@ -1,127 +1,77 @@
{% set current_endpoint = current_endpoint or url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- plugin item -->
{% for plugin in plugin_simple %}
<div data-simple id="{{ plugin['plugin_id'] }}-simple"
class="w-full px-1">
{% for setting_simple in plugin_simple %}
{% set setting_input = { "name" : setting_simple.get('setting_id'), "method" : setting_simple.get('setting').get("method", "default"), "help" : setting_simple.get('setting').get("help"), "label" : setting_simple.get('setting').get("label"), "id" : setting_simple.get('setting').get("id"), "type" : setting_simple.get('setting').get("type"), "default" : setting_simple.get('setting').get("default", "default"), "select" : setting_simple.get('setting').get("select"), "regex" : setting_simple.get('setting').get("regex"), "value" : setting_simple.get('setting').get("value"), "is_multiple" : True if setting_simple.get('setting').get("type") == "multiple" else False } %}
<div data-simple id="{{ setting_simple['plugin_id'] }}-simple"
class="w-full px-1 mb-2">
<!-- title and desc -->
<div class="col-span-12" data-setting-header>
<div class="flex justify-start items-center">
<h5 class="my-2 transition duration-300 ease-in-out ml-2 mr-1 font-bold text-base uppercase dark:text-white/90 mb-0">
{{ plugin['label'] }}
<div class="flex flex-col justify-start items-start">
<h5 class="ml-2 mr-1 mt-2 transition duration-300 ease-in-out font-bold text-base uppercase dark:text-white/90 mb-0">
{{ setting_simple['title'] }}
</h5>
<p class="ml-2 mr-1 text-sm dark:text-gray-300 mb-0">{{ setting_simple['subtitle'] }}</p>
</div>
</div>
{# get number of multiple groups for the plugin #}
{% set multList = [] %}
{% if not setting_input.get('is_multiple') %}
<!-- end title and desc -->
<div class="w-full grid grid-cols-12">
<!-- plugin settings not multiple -->
{% for setting, value in plugin["setting"].items() %}
{% set setting_input = { "name" : setting, "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : True} %}
{% if value['multiple'] and value['multiple'] not in multList %}
{% if multList.append(value['multiple']) %}{% endif %}
{% endif %}
{% if not value['multiple'] %}
<div data-setting-container data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-0 sm:mx-2 md:mx-3 lg:mx-4 my-2 col-span-12 md:my-3 md:col-span-6 2xl:my-3 2xl:col-span-4" id="form-edit-{{ current_endpoint }}-{{ value["id"] }}">
<!-- title and info -->
<div class="flex items-center my-1 relative z-10">
<h5 class="input-title">{{ value["label"] }}</h5>
<!-- popover -->
<button type="button" data-popover-btn="{{ setting }}">
<svg class="popover-settings-svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z" />
</svg>
</button>
<div role="alert"
aria-description="show detail"
class="popover-settings-container hidden"
data-popover-content="{{ setting }}">
<p class="popover-settings-text">{{ value['help'] }}</p>
</div>
<!-- end popover -->
</div>
<!-- end title and info -->
{% include "setting_input.html"%}
{% include "setting_select.html"%}
{% include "setting_checkbox.html"%}
{% include "setting_invalid.html"%}
</div>
{% endif %}
{% endfor %}
<div data-setting-container data--simple-{{ current_endpoint }}-type="{{ setting_input['type'] }}" data--simple-{{ current_endpoint }}-context="{{ setting_input['context'] }}" class="relative mx-0 sm:mx-2 md:mx-3 lg:mx-4 my-2 col-span-12 md:my-3 md:col-span-6 2xl:my-3 2xl:col-span-4" id="form-edit--simple-{{ current_endpoint }}-{{ setting_input["id"] }}">
{% include "setting_header.html" %}
{% include "setting_input.html" %}
{% include "setting_select.html" %}
{% include "setting_checkbox.html" %}
{% include "setting_invalid.html"%}
</div>
<!-- end plugin settings -->
</div>
<!-- end plugin settings not multiple -->
{% if multList|length > 0 %}
{% endif %}
{% if setting_input.get('is_multiple') %}
<h5 data-multiple-title
class="transition duration-300 ease-in-out ml-2 font-bold text-[1.1rem] uppercase dark:text-white/90 mt-2 mb-0">
multiple settings
</h5>
{% endif %}
{% for multiple in multList %}
<!-- plugin multiple handler -->
<div data-multiple-handler="{{ multiple }}"
class="flex items-center mx-2 mb-2 mt-5 col-span-12 ">
<h5 class="input-title max-w-[150px] sm:max-w-[350px]">{{ multiple.replace('-', ' ').replace('_', ' ')|upper }}</h5>
<button data-{{ current_endpoint }}-multiple-add="{{ multiple }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
Add
</button>
<button data-{{ current_endpoint }}-multiple-toggle="{{ multiple }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
SHOW / HIDE
</button>
</div>
<!-- end plugin multiple handler-->
<!-- multiple settings -->
<div data-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full my-4 grid-cols-12 border dark:border-gray-700 rounded">
{% for setting, value in plugin["setting"].items() %}
{% set setting_input = { "name" : setting, "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : True} %}
{# render only setting that match the multiple id and context #}
{% if value['multiple'] == multiple and (
current_endpoint == "global-config"
or current_endpoint == "services" and value['context'] == "multisite"
) %}
<div data-setting-container="{{ setting }}_SCHEMA" data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-2 md:mx-3 my-2 md:my-3 col-span-12 md:col-span-6 2xl:col-span-4" id="form-edit-{{ current_endpoint }}-{{ value["id"] }}_SCHEMA">
{% for multiple in multiple_settings %}
<!-- plugin multiple handler -->
<div data-multiple-handler="{{ multiple }}"
class="flex items-center mx-2 mb-2 mt-5 col-span-12 ">
<h5 class="input-title max-w-[150px] sm:max-w-[350px]">{{ multiple.replace('-', ' ').replace('_', ' ')|upper }}</h5>
<button data--simple-{{ current_endpoint }}-multiple-add="{{ multiple }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
Add
</button>
<button data--simple-{{ current_endpoint }}-multiple-toggle="{{ multiple }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
SHOW / HIDE
</button>
</div>
<!-- end plugin multiple handler-->
<!-- multiple settings -->
<div data--simple-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full my-4 grid-cols-12 border dark:border-gray-700 rounded">
{% for setting, value in plugin["settings"].items() %}
{% set setting_input = { "name" : setting, "context" : value.get("context"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default", "default"), "select" : value.get("select"), "regex" : value.get("regex"), "value" : value.get("value"), "is_multiple" : True} %}
<div data-setting-container="{{ setting }}_SCHEMA" data--simple-{{ current_endpoint }}-type="{{ plugin['type'] }}" data--simple-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-2 md:mx-3 my-2 md:my-3 col-span-12 md:col-span-6 2xl:col-span-4" id="form-edit--simple-{{ current_endpoint }}-{{ value["id"] }}_SCHEMA">
<!-- title and info -->
<div class="flex items-center my-1 relative z-10">
<h5 class="input-title">{{ value["label"] }}</h5>
<!-- popover -->
<button type="button" data-popover-btn="{{ setting }}_SCHEMA">
<svg class="popover-settings-svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z" />
</svg>
</button>
<div role="alert"
aria-description="show detail"
class="popover-settings-container hidden"
data-popover-content="{{ setting }}_SCHEMA">
<p class="popover-settings-text">{{ value['help'] }}</p>
</div>
<!-- end popover -->
</div>
{% include "setting_input.html"%}
{% include "setting_select.html"%}
{% include "setting_checkbox.html"%}
{% include "setting_header.html" %}
{% include "setting_input.html" %}
{% include "setting_select.html" %}
{% include "setting_checkbox.html" %}
{% include "setting_invalid.html"%}
</div>
{% endif %}
{% endfor %}
<div data-multiple-delete-container
class="col-span-12 flex justify-center my-4">
<button data-{{ current_endpoint }}-multiple-delete="{{ plugin['name'] }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-red-500 hover:bg-red-500/80 focus:bg-red-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
Remove
</button>
{% endfor %}
<div data-multiple-delete-container
class="col-span-12 flex justify-center my-4">
<button data--simple-{{ current_endpoint }}-multiple-delete="{{ plugin['name'] }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-red-500 hover:bg-red-500/80 focus:bg-red-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
Remove
</button>
</div>
<!-- end plugin settings -->
</div>
<!-- end plugin settings -->
</div>
{% endfor %}
<!-- end multiple settings -->
{% endif %}
</div>
{% endfor %}
<!-- end plugin item -->