fix filter settings + loop rendering

* fix Filter component not using update template (keeping previosu one) in case user is updating value by watching the props template and rerun filter everytime the template is changing, and not only if a filter value is changed
* add unique id to multiples settings in order to them to be catch by useUpdateTemplate utils
* fix for-loop settings watch :key by changing id by name in order to avoid a bad behavior with filtering (setting value id was static but not others components)
* start useUpdateTempMultiples to update multiple settings
* update Filter component by now checking multiples and returning only matching settings keeping the format
This commit is contained in:
Jordan Blasenhauer 2024-07-12 11:06:00 +02:00
parent 011e8f1074
commit 79e6eefa2b
10 changed files with 719 additions and 151 deletions

View file

@ -7382,7 +7382,7 @@
"context": "multisite",
"default": "",
"help": "Full URL of the proxied resource (proxy_pass).",
"id": "reverse-proxy-host",
"id": "reverse-proxy-host_1",
"label": "Reverse proxy host",
"regex": "^.*$",
"type": "text",
@ -7418,7 +7418,7 @@
"context": "multisite",
"default": "/",
"help": "Location URL that will be proxied.",
"id": "reverse-proxy-url",
"id": "reverse-proxy-url_1",
"label": "Reverse proxy url",
"regex": "^.*$",
"type": "text",
@ -7449,7 +7449,7 @@
"context": "multisite",
"default": "no",
"help": "Enable websocket on the proxied resource.",
"id": "reverse-proxy-ws",
"id": "reverse-proxy-ws_1",
"label": "Reverse proxy WS",
"regex": "^(yes|no)$",
"type": "check",
@ -7480,7 +7480,7 @@
"context": "multisite",
"default": "",
"help": "List of HTTP headers to send to proxied resource separated with semicolons (values for proxy_set_header directive).",
"id": "reverse-proxy-headers",
"id": "reverse-proxy-headers_1",
"label": "Reverse proxy headers",
"regex": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
@ -7511,7 +7511,7 @@
"context": "multisite",
"default": "",
"help": "List of HTTP headers to send to client separated with semicolons (values for add_header directive).",
"id": "reverse-proxy-headers-client",
"id": "reverse-proxy-headers-client_1",
"label": "Reverse proxy headers-client",
"regex": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
@ -7542,7 +7542,7 @@
"context": "multisite",
"default": "yes",
"help": "Enable or disable buffering of responses from proxied resource.",
"id": "reverse-proxy-buffering",
"id": "reverse-proxy-buffering_1",
"label": "Reverse proxy buffering",
"regex": "^(yes|no)$",
"type": "check",
@ -7573,7 +7573,7 @@
"context": "multisite",
"default": "no",
"help": "Enable or disable keepalive connections with the proxied resource.",
"id": "reverse-proxy-keepalive",
"id": "reverse-proxy-keepalive_1",
"label": "Reverse proxy keepalive",
"regex": "^(yes|no)$",
"type": "check",
@ -7604,7 +7604,7 @@
"context": "multisite",
"default": "",
"help": "Enable authentication using an external provider (value of auth_request directive).",
"id": "reverse-proxy-auth-request",
"id": "reverse-proxy-auth-request_1",
"label": "Reverse proxy auth request",
"regex": "^(\\/[\\w\\].~:\\/?#\\[@!$\\&'\\(\\)*+,;=\\-]*|off)?$",
"type": "text",
@ -7635,7 +7635,7 @@
"context": "multisite",
"default": "",
"help": "Redirect clients to sign-in URL when using REVERSE_PROXY_AUTH_REQUEST (used when auth_request call returned 401).",
"id": "reverse-proxy-auth-request-signin-url",
"id": "reverse-proxy-auth-request-signin-url_1",
"label": "Auth request signin URL",
"regex": "^(https?:\\/\\/[\\-\\w@:%.+~#=]+[\\-\\w\\(\\)!@:%+.~#?&\\/=$]*)?$",
"type": "text",
@ -7666,7 +7666,7 @@
"context": "multisite",
"default": "",
"help": "List of variables to set from the authentication provider, separated with semicolons (values of auth_request_set directives).",
"id": "reverse-proxy-auth-request-set",
"id": "reverse-proxy-auth-request-set_1",
"label": "Reverse proxy auth request set",
"regex": "^(?! ;)(;? ?(\\$[a-z_\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
@ -7697,7 +7697,7 @@
"context": "multisite",
"default": "60s",
"help": "Timeout when connecting to the proxied resource.",
"id": "reverse-proxy-connect-timeout",
"id": "reverse-proxy-connect-timeout_1",
"label": "Reverse proxy connect timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
@ -7728,7 +7728,7 @@
"context": "multisite",
"default": "60s",
"help": "Timeout when reading from the proxied resource.",
"id": "reverse-proxy-read-timeout",
"id": "reverse-proxy-read-timeout_1",
"label": "Reverse proxy read timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
@ -7759,7 +7759,7 @@
"context": "multisite",
"default": "60s",
"help": "Timeout when sending to the proxied resource.",
"id": "reverse-proxy-send-timeout",
"id": "reverse-proxy-send-timeout_1",
"label": "Reverse proxy send timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
@ -7787,6 +7787,443 @@
"containerClass": "z-14"
},
"REVERSE_PROXY_INCLUDES_1": {
"context": "multisite",
"default": "",
"help": "Additional configuration to include in the location block, separated with spaces.",
"id": "reverse-proxy-includes_1",
"label": "Reverse proxy includes",
"regex": "^(?! )( ?(\\w+)(?!.*\\b\\2\\b))*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(?! )( ?(\\w+)(?!.*\\b\\2\\b))*$",
"inpType": "input",
"name": "Reverse proxy includes",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Additional configuration to include in the location block, separated with spaces."
}
],
"containerClass": "z-13"
}
},
"0": {
"REVERSE_PROXY_HOST": {
"context": "multisite",
"default": "",
"help": "Full URL of the proxied resource (proxy_pass).",
"id": "reverse-proxy-host",
"label": "Reverse proxy host",
"regex": "^.*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^.*$",
"inpType": "input",
"name": "Reverse proxy host",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "no",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Full URL of the proxied resource (proxy_pass)."
}
],
"containerClass": "z-26",
"method": "ui"
},
"REVERSE_PROXY_URL": {
"context": "multisite",
"default": "/",
"help": "Location URL that will be proxied.",
"id": "reverse-proxy-url",
"label": "Reverse proxy url",
"regex": "^.*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^.*$",
"inpType": "input",
"name": "Reverse proxy url",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "/",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Location URL that will be proxied."
}
],
"containerClass": "z-25"
},
"REVERSE_PROXY_WS": {
"context": "multisite",
"default": "no",
"help": "Enable websocket on the proxied resource.",
"id": "reverse-proxy-ws",
"label": "Reverse proxy WS",
"regex": "^(yes|no)$",
"type": "check",
"multiple": "reverse-proxy",
"pattern": "^(yes|no)$",
"inpType": "checkbox",
"name": "Reverse proxy WS",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "no",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Enable websocket on the proxied resource."
}
],
"containerClass": "z-24"
},
"REVERSE_PROXY_HEADERS": {
"context": "multisite",
"default": "",
"help": "List of HTTP headers to send to proxied resource separated with semicolons (values for proxy_set_header directive).",
"id": "reverse-proxy-headers",
"label": "Reverse proxy headers",
"regex": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"inpType": "input",
"name": "Reverse proxy headers",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "List of HTTP headers to send to proxied resource separated with semicolons (values for proxy_set_header directive)."
}
],
"containerClass": "z-23"
},
"REVERSE_PROXY_HEADERS_CLIENT": {
"context": "multisite",
"default": "",
"help": "List of HTTP headers to send to client separated with semicolons (values for add_header directive).",
"id": "reverse-proxy-headers-client",
"label": "Reverse proxy headers-client",
"regex": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(?![; ])(;? ?([\\w\\-]+)(?!.*\\2 ) [^;]+)*$",
"inpType": "input",
"name": "Reverse proxy headers-client",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "List of HTTP headers to send to client separated with semicolons (values for add_header directive)."
}
],
"containerClass": "z-22"
},
"REVERSE_PROXY_BUFFERING": {
"context": "multisite",
"default": "yes",
"help": "Enable or disable buffering of responses from proxied resource.",
"id": "reverse-proxy-buffering",
"label": "Reverse proxy buffering",
"regex": "^(yes|no)$",
"type": "check",
"multiple": "reverse-proxy",
"pattern": "^(yes|no)$",
"inpType": "checkbox",
"name": "Reverse proxy buffering",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "yes",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Enable or disable buffering of responses from proxied resource."
}
],
"containerClass": "z-21"
},
"REVERSE_PROXY_KEEPALIVE": {
"context": "multisite",
"default": "no",
"help": "Enable or disable keepalive connections with the proxied resource.",
"id": "reverse-proxy-keepalive",
"label": "Reverse proxy keepalive",
"regex": "^(yes|no)$",
"type": "check",
"multiple": "reverse-proxy",
"pattern": "^(yes|no)$",
"inpType": "checkbox",
"name": "Reverse proxy keepalive",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "no",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Enable or disable keepalive connections with the proxied resource."
}
],
"containerClass": "z-20"
},
"REVERSE_PROXY_AUTH_REQUEST": {
"context": "multisite",
"default": "",
"help": "Enable authentication using an external provider (value of auth_request directive).",
"id": "reverse-proxy-auth-request",
"label": "Reverse proxy auth request",
"regex": "^(\\/[\\w\\].~:\\/?#\\[@!$\\&'\\(\\)*+,;=\\-]*|off)?$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(\\/[\\w\\].~:\\/?#\\[@!$\\&'\\(\\)*+,;=\\-]*|off)?$",
"inpType": "input",
"name": "Reverse proxy auth request",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Enable authentication using an external provider (value of auth_request directive)."
}
],
"containerClass": "z-19"
},
"REVERSE_PROXY_AUTH_REQUEST_SIGNIN_URL": {
"context": "multisite",
"default": "",
"help": "Redirect clients to sign-in URL when using REVERSE_PROXY_AUTH_REQUEST (used when auth_request call returned 401).",
"id": "reverse-proxy-auth-request-signin-url",
"label": "Auth request signin URL",
"regex": "^(https?:\\/\\/[\\-\\w@:%.+~#=]+[\\-\\w\\(\\)!@:%+.~#?&\\/=$]*)?$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(https?:\\/\\/[\\-\\w@:%.+~#=]+[\\-\\w\\(\\)!@:%+.~#?&\\/=$]*)?$",
"inpType": "input",
"name": "Auth request signin URL",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Redirect clients to sign-in URL when using REVERSE_PROXY_AUTH_REQUEST (used when auth_request call returned 401)."
}
],
"containerClass": "z-18"
},
"REVERSE_PROXY_AUTH_REQUEST_SET": {
"context": "multisite",
"default": "",
"help": "List of variables to set from the authentication provider, separated with semicolons (values of auth_request_set directives).",
"id": "reverse-proxy-auth-request-set",
"label": "Reverse proxy auth request set",
"regex": "^(?! ;)(;? ?(\\$[a-z_\\-]+)(?!.*\\2 ) [^;]+)*$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^(?! ;)(;? ?(\\$[a-z_\\-]+)(?!.*\\2 ) [^;]+)*$",
"inpType": "input",
"name": "Reverse proxy auth request set",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "List of variables to set from the authentication provider, separated with semicolons (values of auth_request_set directives)."
}
],
"containerClass": "z-17"
},
"REVERSE_PROXY_CONNECT_TIMEOUT": {
"context": "multisite",
"default": "60s",
"help": "Timeout when connecting to the proxied resource.",
"id": "reverse-proxy-connect-timeout",
"label": "Reverse proxy connect timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^\\d+(ms?|[shdwMy])$",
"inpType": "input",
"name": "Reverse proxy connect timeout",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "60s",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Timeout when connecting to the proxied resource."
}
],
"containerClass": "z-16"
},
"REVERSE_PROXY_READ_TIMEOUT": {
"context": "multisite",
"default": "60s",
"help": "Timeout when reading from the proxied resource.",
"id": "reverse-proxy-read-timeout",
"label": "Reverse proxy read timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^\\d+(ms?|[shdwMy])$",
"inpType": "input",
"name": "Reverse proxy read timeout",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "60s",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Timeout when reading from the proxied resource."
}
],
"containerClass": "z-15"
},
"REVERSE_PROXY_SEND_TIMEOUT": {
"context": "multisite",
"default": "60s",
"help": "Timeout when sending to the proxied resource.",
"id": "reverse-proxy-send-timeout",
"label": "Reverse proxy send timeout",
"regex": "^\\d+(ms?|[shdwMy])$",
"type": "text",
"multiple": "reverse-proxy",
"pattern": "^\\d+(ms?|[shdwMy])$",
"inpType": "input",
"name": "Reverse proxy send timeout",
"columns": {
"pc": 4,
"tablet": 6,
"mobile": 12
},
"disabled": false,
"value": "60s",
"popovers": [
{
"iconName": "disk",
"text": "inp_popover_multisite"
},
{
"iconName": "info",
"text": "Timeout when sending to the proxied resource."
}
],
"containerClass": "z-14"
},
"REVERSE_PROXY_INCLUDES": {
"context": "multisite",
"default": "",
"help": "Additional configuration to include in the location block, separated with spaces.",
@ -8850,7 +9287,7 @@
"raw": {
"default": {
"USE_GZIP": "dsfrgrdgrdgrdhgd",
"REVERSE_PROXY_HOST": "template",
"REVERSE_PROXY_HOST": "no",
"USE_UI": "yes"
}
}

View file

@ -3016,6 +3016,7 @@ service_settings = {
"USE_UI": {"value": "yes", "global": True, "method": "ui"},
"USE_CORS": {"value": "yes", "global": True, "method": "scheduler"},
"REVERSE_PROXY_HOST_1": {"value": "yes", "global": True, "method": "scheduler"},
"REVERSE_PROXY_HOST": {"value": "no", "global": True, "method": "ui"},
}
@ -3207,7 +3208,13 @@ def get_multiple_from_template(template, multiples):
for multSett, multValue in mult_settings.items():
new_multiple_group[f"{multSett}{f'_{prefix}' if prefix != '0' else ''}"] = multValue
multiple_template[mult_name][prefix] = copy.deepcopy(new_multiple_group)
new_multiple_group = copy.deepcopy(new_multiple_group)
# Update id for each settings
for multSett, multValue in new_multiple_group.items():
multValue["id"] = f"{multValue['id']}{f'-{prefix}' if prefix != '0' else ''}"
multiple_template[mult_name][prefix] = new_multiple_group
# We can now add the template value to setting using the same setting name with prefix
multiple_template[mult_name][prefix][setting]["value"] = value
@ -3258,7 +3265,14 @@ def get_multiple_from_settings(settings, multiples):
for multSett, multValue in mult_settings.items():
new_multiple_group[f"{multSett}{f'_{prefix}' if prefix != '0' else ''}"] = multValue
multiple_settings[mult_name][prefix] = copy.deepcopy(new_multiple_group)
new_multiple_group = copy.deepcopy(new_multiple_group)
# Update id for each settings
for multSett, multValue in new_multiple_group.items():
multValue["id"] = f"{multValue['id']}{f'-{prefix}' if prefix != '0' else ''}"
multiple_settings[mult_name][prefix] = new_multiple_group
# We can now add the template value to setting using the same setting name with prefix
multiple_settings[mult_name][prefix][setting]["value"] = value.get("value", multiple_settings[mult_name][prefix][setting]["value"])
multiple_settings[mult_name][prefix][setting]["method"] = value.get("method", "ui")

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
<script setup>
import { defineProps, reactive, onMounted, onUnmounted, KeepAlive } from "vue";
import { defineProps, reactive, onMounted, onUnmounted } from "vue";
import MessageUnmatch from "@components/Message/Unmatch.vue";
import Container from "@components/Widget/Container.vue";
import Fields from "@components/Form/Fields.vue";
@ -14,7 +14,7 @@ import { plugin_types } from "@utils/variables";
import {
useCheckPluginsValidity,
useUpdateTempSettings,
useUpdateTemplate,
useListenTemp,
useUnlistenTemp,
} from "@utils/form.js";
@ -239,7 +239,7 @@ function getPluginNames(template) {
function updateTemplate(e) {
if (!e.target.closest("[data-advanced-form-plugin]")) return;
useUpdateTempSettings(e, data.base);
useUpdateTemplate(e, data.base);
}
onMounted(() => {
@ -284,30 +284,25 @@ onUnmounted(() => {
</Filter>
<MessageUnmatch v-if="!data.format.length" />
<template v-for="plugin in data.format">
<KeepAlive>
<Container
data-is="content"
data-advanced-form-plugin
v-if="plugin.name === data.currPlugin"
class="form-advanced-plugin-container"
>
<Title type="content" :title="plugin.name" />
<Subtitle type="content" :subtitle="plugin.description" />
<Container
data-is="content"
data-advanced-form-plugin
v-if="plugin.name === data.currPlugin"
class="form-advanced-plugin-container"
>
<Title type="content" :title="plugin.name" />
<Subtitle type="content" :subtitle="plugin.description" />
<Container class="layout-settings">
<template
v-for="(setting, name, index) in plugin.settings"
:key="index"
>
<Fields :setting="setting" />
</template>
</Container>
<GroupMultiple
v-if="plugin.multiples"
:multiples="plugin.multiples"
/>
<Container class="layout-settings">
<template
v-for="(setting, name, index) in plugin.settings"
:key="name"
>
<Fields :setting="setting" />
</template>
</Container>
</KeepAlive>
<GroupMultiple v-if="plugin.multiples" :multiples="plugin.multiples" />
</Container>
</template>
<Button
v-if="data.format.length"

View file

@ -9,7 +9,7 @@ import Text from "@components/Widget/Text.vue";
import { v4 as uuidv4 } from "uuid";
import {
useCheckPluginsValidity,
useUpdateTempSettings,
useUpdateTemplate,
useListenTemp,
useUnlistenTemp,
} from "@utils/form.js";
@ -94,7 +94,7 @@ function setValidity() {
function updateTemplate(e) {
if (!e.target.closest("[data-easy-form-step]")) return;
useUpdateTempSettings(e, data.base);
useUpdateTemplate(e, data.base);
}
const buttonSave = {
@ -164,10 +164,7 @@ onUnmounted(() => {
<Subtitle type="content" :subtitle="step.subtitle" />
<Container class="layout-settings">
<template
v-for="(setting, name, index) in step.settings"
:key="index"
>
<template v-for="(setting, name, index) in step.settings" :key="name">
<Fields :setting="setting" />
</template>
</Container>

View file

@ -155,9 +155,9 @@ import Container from "@components/Widget/Container.vue";
const props = defineProps({
// id && value && method
multiples: {
type: String,
type: Object,
required: false,
default: "",
default: {},
},
columns: {
type: [Object, Boolean],
@ -207,6 +207,22 @@ const buttonDelete = {
containerClass: "flex justify-center",
};
// Check if at least one input is disabled (this means a method different than ui, default or manual is used)
// If true, disable the delete button
function setDeleteState(group) {
// Loop on group keys and check if at least one input is disabled
let isDisabled = false;
for (const [key, value] of Object.entries(group)) {
if (value.disabled) {
isDisabled = true;
break;
}
}
const delBtn = JSON.parse(JSON.stringify(buttonDelete));
delBtn.disabled = isDisabled;
return delBtn;
}
function setInvisible(id) {
multiples.invisible.push(id);
}
@ -223,13 +239,13 @@ function toggleVisible(id) {
}
}
function delGroup(multName, groupName) {
function delGroup(group, multName, groupName) {
multiples.toDelete.push({ multName: multName, groupName: groupName });
}
</script>
<template>
<template v-for="(multObj, multName, id) in props.multiples">
<template :key="multName" v-for="(multObj, multName, id) in props.multiples">
<Container
data-is="multiple"
class="layout-settings-multiple"
@ -245,9 +261,11 @@ function delGroup(multName, groupName) {
</Container>
<template
:key="groupId"
v-for="(group, groupName, groupId) in props.multiples[multName]"
>
<Container
data-group="multiple"
class="layout-settings-multiple-group"
:aria-hidden="multiples.invisible.includes(`${multName}${id}`)"
v-show="
@ -257,12 +275,15 @@ function delGroup(multName, groupName) {
<Subtitle
:subtitle="`${multName.replaceAll('-', ' ')} #${+groupName + 1}`"
/>
<template v-for="(setting, settingName, settingId) in group">
<Fields :setting="setting" :tabId="props.tabId" />
<template
:key="settingName"
v-for="(setting, settingName, settingId) in group"
>
<Fields :setting="setting" />
</template>
<ButtonGroup
@click="delGroup(multName, groupName)"
:buttons="[buttonDelete]"
@click="delGroup(group, multName, groupName)"
:buttons="[setDeleteState(group)]"
/>
</Container>
</template>

View file

@ -1,5 +1,5 @@
<script setup>
import { defineProps, reactive } from "vue";
import { defineProps, reactive, watch } from "vue";
import Container from "@components/Widget/Container.vue";
import Input from "@components/Forms/Field/Input.vue";
@ -73,6 +73,12 @@ const filters = reactive({
base: JSON.parse(JSON.stringify(props.filters)),
});
watch(props.data, () => {
filters.base.forEach((filter) => {
filterData(filter, filter.value);
});
});
function filterData(filter, value) {
// Loop on filter.base and update the "value" key when matching filterName
filters.base.forEach((f) => {
@ -92,25 +98,14 @@ function filterData(filter, value) {
// Specific settings filtering from advanced template
const filterSettings = getFilters.filter((f) => f.filter === "settings");
if (filterSettings.length) {
template.forEach((plugin, id) => {
// loop on plugin settings dict
const settings = [];
for (const [key, value] of Object.entries(plugin.settings)) {
// add to value the key as setting_name
settings.push({ ...value, setting_name: key });
}
const filterSettingsData = useFilter(settings, filterSettings);
// Transform list of dict by a dict of dict with setting_name as key and add update plugin settings
const settingsData = {};
filterSettingsData.forEach((setting) => {
settingsData[setting.setting_name] = setting;
});
template[id].settings = settingsData;
});
// Case no settings found, remove plugin
filterRegularSettings(filterSettings, template);
filterMultiplesSettings(filterSettings, template);
// Case no settings or multiple found, remove plugin
template = template.filter((plugin) => {
return Object.keys(plugin.settings).length > 0;
return (
Object.keys(plugin?.settings || {}).length > 0 ||
Object.keys(plugin?.multiples || {}).length > 0
);
});
}
@ -138,6 +133,54 @@ function filterData(filter, value) {
emits("filter", template);
}
function filterRegularSettings(filterSettings, template) {
template.forEach((plugin, id) => {
// loop on plugin settings dict
const settings = [];
for (const [key, value] of Object.entries(plugin.settings)) {
// add to value the key as setting_name
settings.push({ ...value, setting_name: key });
}
const filterSettingsData = useFilter(settings, filterSettings);
// Transform list of dict by a dict of dict with setting_name as key and add update plugin settings
const settingsData = {};
filterSettingsData.forEach((setting) => {
settingsData[setting.setting_name] = setting;
});
template[id].settings = settingsData;
});
}
function filterMultiplesSettings(filterSettings, template) {
template.forEach((plugin, id) => {
// loop on plugin settings dict
const filterMultiple = {};
const multiples = plugin?.multiples;
if (!multiples || Object.keys(multiples).length <= 0) return;
for (const [multName, multGroups] of Object.entries(multiples)) {
for (const [groupId, groupSettings] of Object.entries(multGroups)) {
// Check if inpid is mathing a groupSettings key
const settings = [];
for (const [key, value] of Object.entries(groupSettings)) {
settings.push({ ...value, setting_name: key });
}
const filterSettingsData = useFilter(settings, filterSettings);
const settingsData = {};
filterSettingsData.forEach((setting) => {
settingsData[setting.setting_name] = setting;
delete settingsData[setting.setting_name]?.setting_name;
});
// Check if at least one setting is matching
if (Object.keys(settingsData).length > 0) {
if (!filterMultiple[multName]) filterMultiple[multName] = {};
filterMultiple[multName][groupId] = settingsData;
}
}
}
template[id].multiples = filterMultiple;
});
}
</script>
<template>

View file

@ -1,5 +1,5 @@
{
"dashboard_placeholder" : "{placeholder}",
"dashboard_placeholder": "{placeholder}",
"dashboard_details": "details",
"dashboard_logo_alt": "BunkerWeb logo image",
"dashboard_logo_link_label": "Redirect to home page",
@ -37,7 +37,7 @@
"dashboard_logs": "logs",
"dashboard_feedback_toggle_sidebar": "Toggle feedback sidebar.",
"dashboard_feedback_close_sidebar": "Close feedback sidebar.",
"dashboard_feedback_alert_close" : "Close feedback alert",
"dashboard_feedback_alert_close": "Close feedback alert",
"dashboard_feedback_title": "feedback",
"dashboard_feedback_subtitle": "BunkerWeb actions",
"dashboard_menu_toggle_sidebar": "Toggle menu sidebar.",
@ -98,8 +98,8 @@
"dashboard_easy_invalid": "{setting} in step {step} is invalid",
"dashboard_easy_required": "{setting} in step {step} is required",
"dashboard_table": "Table element",
"dashboard_no_match" : "No match found",
"dashboard_no_match_filter" : "No match found with filter",
"dashboard_no_match": "No match found",
"dashboard_no_match_filter": "No match found with filter",
"dashboard_something_wrong": "Something is wrong",
"inp_input_valid": "input valid",
"inp_input_error_no_match": "No match found",
@ -129,7 +129,7 @@
"inp_input_clipboard_desc": "Copy to clipboard on click.",
"inp_templates_desc": "Choose a template. Switching will reset none save settings update.",
"inp_list_enter_match": "This value already match existing list item",
"inp_list_invalid_entry" : "This value is invalid for list",
"inp_list_invalid_entry": "This value is invalid for list",
"icons_cross_desc": "Cross icon representing a close, delete, error or cancel state.",
"icons_check_desc": "Check icon representing a success, valid or active state.",
"icons_core_desc": "Core icon representing a core setting or plugin.",
@ -158,6 +158,7 @@
"action_stop": "stop {name}",
"action_ping": "ping {name}",
"action_reload": "reload {name}",
"action_remove": "remove {name}",
"action_restart": "restart {name}",
"action_upload": "upload {name}",
"action_delete_all": "delete all {name}",
@ -203,14 +204,14 @@
"jobs_success_desc": "Choose the job success state.",
"jobs_reload": "reload",
"jobs_reload_desc": "Choose the job reload state.",
"jobs_table_title" :"Jobs list with plugin id, name, last run, interval and success state.",
"jobs_table_name" : "Name",
"jobs_table_plugin_id" : "Plugin id",
"jobs_table_interval" : "Interval",
"jobs_table_reload" : "Reload",
"jobs_table_success" : "Success",
"jobs_table_last_run_date" : "Last run date",
"jobs_table_cache_downloadable" : "Cache (downloadable)",
"jobs_table_title": "Jobs list with plugin id, name, last run, interval and success state.",
"jobs_table_name": "Name",
"jobs_table_plugin_id": "Plugin id",
"jobs_table_interval": "Interval",
"jobs_table_reload": "Reload",
"jobs_table_success": "Success",
"jobs_table_last_run_date": "Last run date",
"jobs_table_cache_downloadable": "Cache (downloadable)",
"plugins_pro_plugin_desc": "Pro plugin",
"plugins_core_plugin_desc": "Core plugin",
"plugins_external_plugin_desc": "External plugin",
@ -233,33 +234,33 @@
"reports_status_desc": "Status are HTTP status codes.",
"reports_reason": "Reason",
"reports_reason_desc": "Reason is the plugin name that triggered the report.",
"reports_title" :"Reports",
"reports_table_title" :"Reports list with date, ip, country, method, url, status code, user agent, reason and data.",
"reports_table_date" : "Date",
"reports_title": "Reports",
"reports_table_title": "Reports list with date, ip, country, method, url, status code, user agent, reason and data.",
"reports_table_date": "Date",
"reports_table_ip": "IP",
"reports_table_country" : "Country",
"reports_table_method" : "Method",
"reports_table_url" : "URL",
"reports_table_status_code" : "Status code",
"reports_table_cache_user_agent" : "User agent",
"reports_table_reason" : "Reason",
"reports_table_country": "Country",
"reports_table_method": "Method",
"reports_table_url": "URL",
"reports_table_status_code": "Status code",
"reports_table_cache_user_agent": "User agent",
"reports_table_reason": "Reason",
"reports_table_data": "Data",
"reports_total": "Total reports",
"reports_top_status": "Top status code",
"reports_top_reason": "Top reason",
"bans_search" : "Search bans",
"bans_search_desc" : "Search within ban ip, ban start / end date",
"bans_search": "Search bans",
"bans_search_desc": "Search within ban ip, ban start / end date",
"bans_reason": "Reason",
"bans_reason_desc": "Reason is the method that triggered the ban.",
"bans_terms": "Term",
"bans_terms_desc": "Order of magnitude before unban.",
"bans_title" :"Bans",
"bans_table_title" :"Bans list with ip, start date, end date, reason and terms.",
"bans_table_ip" : "IP",
"bans_table_ban_start" : "Ban start",
"bans_table_ban_end" : "Ban end",
"bans_table_reason" : "Reason",
"bans_table_remain" : "Remain",
"bans_table_term" : "Term",
"bans_table_select" : "Select"
"bans_title": "Bans",
"bans_table_title": "Bans list with ip, start date, end date, reason and terms.",
"bans_table_ip": "IP",
"bans_table_ban_start": "Ban start",
"bans_table_ban_end": "Ban end",
"bans_table_reason": "Reason",
"bans_table_remain": "Remain",
"bans_table_term": "Term",
"bans_table_select": "Select"
}

File diff suppressed because one or more lines are too long

View file

@ -307,11 +307,11 @@ function useCheckPluginsValidity(template) {
@description This will add an handler to all needed event listeners to listen to input, select... fields in order to update the template settings.
@example
function hander(e) {
// some code before calling useUpdateTempSettings
// some code before calling useUpdateTemplate
if (!e.target.closest("[data-advanced-form-plugin]")) return;
useUpdateTempSettings(e, data.base);
useUpdateTemplate(e, data.base);
}
@param handler - Callback function to call when event is triggered. This is usually an intermediate function that will call the useUpdateTempSettings function.
@param handler - Callback function to call when event is triggered. This is usually an intermediate function that will call the useUpdateTemplate function.
*/
function useListenTemp(handler) {
window.addEventListener("input", handler);
@ -324,9 +324,9 @@ function useListenTemp(handler) {
@description This will stop listening to input, select... fields. Performance optimization and avoid duplicate calls conflicts.
@example
function hander(e) {
// some code before calling useUpdateTempSettings
// some code before calling useUpdateTemplate
if (!e.target.closest("[data-advanced-form-plugin]")) return;
useUpdateTempSettings(e, data.base);
useUpdateTemplate(e, data.base);
}
@param handler - Callback function to call when event is triggered. Need to be the same function as the one passed to useListenTemp.
*/
@ -337,7 +337,7 @@ function useUnlistenTemp(handler) {
}
/**
@name useUpdateTempSettings
@name useUpdateTemplate
@description This function will check if the target is a setting input-like field.
In case it is, it will get the id and value for each field case, this will allow to update the template settings.
@example
@ -356,42 +356,100 @@ function useUnlistenTemp(handler) {
@param e - Event object, get it by default in the event listener.
@param template - Template with plugins list and detail settings
*/
function useUpdateTempSettings(e, template) {
function useUpdateTemplate(e, template) {
// Wait some ms that previous update logic is done like datepicker
setTimeout(() => {
let inpId, inpValue;
let inpId, inpValue;
// Case target is input (a little different for datepicker)
if (e.target.tagName === "INPUT") {
inpId = e.target.id;
inpValue = e.target.hasAttribute("data-timestamp")
? e.target.getAttribute("data-timestamp")
: e.target.value;
}
// Case target is input (a little different for datepicker)
if (e.target.tagName === "INPUT") {
inpId = e.target.id;
inpValue = e.target.hasAttribute("data-timestamp")
? e.target.getAttribute("data-timestamp")
: e.target.value;
}
// Case target is select
if (
e.target.closest("[data-field-container]") &&
e.target.hasAttribute("data-setting-id") &&
e.target.hasAttribute("data-setting-value")
) {
inpId = e.target.getAttribute("data-setting-id");
inpValue = e.target.getAttribute("data-setting-value");
}
// Case target is select
if (
e.target.closest("[data-field-container]") &&
e.target.hasAttribute("data-setting-id") &&
e.target.hasAttribute("data-setting-value")
) {
inpId = e.target.getAttribute("data-setting-id");
inpValue = e.target.getAttribute("data-setting-value");
}
// Case target is not an input-like
if (!inpId) return;
// Case target is not an input-like
if (!inpId) return;
template.find((plugin) => {
const settings = plugin["settings"];
// loop on each settings from plugin
for (const [key, value] of Object.entries(settings)) {
if (value.id === inpId) {
value.value = inpValue;
}
// Check if setting is part multiple or regular settings
const isMultiple = e.target.closest('[data-group="multiple"]') ? true : false;
if (!isMultiple) useUpdateTempSettings(template, inpId, inpValue);
if (isMultiple) useUpdateTempMultiples(template, inpId, inpValue);
return template;
}
/**
@name useUpdateTempSettings
@description This function will loop on template settings in order to update the setting value.
This will check each plugin.settings (what I call regular) instead of other type of settings like multiples (in plugin.multiples).
This function needs to be call in useUpdateTemplate.
@param template - Template with plugins list and detail settings
@param inpId - Input id to update
@param inpValue - Input value to update
*/
function useUpdateTempSettings(template, inpId, inpValue) {
// Try to update settings
let isSettingUpdated = false;
for (let i = 0; i < template.length; i++) {
const plugin = template[i];
const settings = plugin?.settings;
if (!settings) continue;
for (const [key, value] of Object.entries(settings)) {
if (value.id === inpId) {
value.value = inpValue;
isSettingUpdated = true;
break;
}
});
}, 50);
}
if (isSettingUpdated) break;
}
}
/**
@name useUpdateTempMultiples
@description This function will loop on template multiples in order to update the setting value.
This will check each plugin.multiples that can be found in the template.
This function needs to be call in useUpdateTemplate.
@param template - Template with plugins list and detail settings
@param inpId - Input id to update
@param inpValue - Input value to update
*/
function useUpdateTempMultiples(template, inpId, inpValue) {
// Check at the same time the inpId without prefix group he is part of
// And try to update an existing inpId
// Case we found the inpId, we update the value
// Case we didn't find existing inpId, we create a new one
let isSettingUpdated = false;
for (let i = 0; i < template.length; i++) {
const plugin = template[i];
const multiples = plugin?.multiples;
if (!multiples || Object.keys(multiples).length <= 0) continue;
for (const [multName, multGroups] of Object.entries(multiples)) {
for (const [groupId, groupSettings] of Object.entries(multGroups)) {
// Check if inpid is mathing a groupSettings key
for (const [settingName, settings] of Object.entries(groupSettings)) {
if (!settings?.inpId) continue;
settings.value = inpValue;
isSettingUpdated = true;
if (isSettingUpdated) break;
}
if (isSettingUpdated) break;
}
}
if (isSettingUpdated) break;
}
}
export {
@ -400,7 +458,7 @@ export {
isItemKeyword,
isItemSelect,
useCheckPluginsValidity,
useUpdateTempSettings,
useUpdateTemplate,
useListenTemp,
useUnlistenTemp,
};