plugins: config.showIf

Massimo Melina committed Dec 13, 2024 at 23:36 UTC 36abf3ab3fb495b8d421cb978fbb426f1d94eda4
3 files changed +20 -7
admin/src/InstalledPlugins.ts
+11 -4
@@ -101,7 +101,7 @@ export default function InstalledPlugins({ updates }: { updates?: true }) {
101 title: `Options for ${id}`,
102 form: values => ({
103 before: h(Box, { mx: 2, mb: 3 }, row.description),
104 - fields: makeFields(row.config),
104 + fields: makeFields(row.config, values),
105 save: { children: "Save and close" },
106 barSx: { gap: 1 },
107 addToBar: [h(Btn, { variant: 'outlined', onClick: () => save(values) }, "Save")],
@@ -167,17 +167,24 @@ export function renderName({ row, value }: any) {
167 }
168 }
169
170 -function makeFields(config: any) {
170 +function makeFields(config: any, values: any) {
171 return Object.entries(config).map(([k,o]: [string,any]) => {
172 if (!_.isPlainObject(o))
173 return o
174 - let { type, defaultValue, fields, frontend, helperText, ...rest } = o
174 + let { type, defaultValue, fields, frontend, helperText, showIf, ...rest } = o
175 + try {
176 + if (typeof showIf === 'string') // compile once
177 + o.showIf = showIf = eval(showIf) // eval is normally considered a threat, but this code is coming from a plugin that's already running on your server, so you already decided to trust it. Here it will run in your browser, and inside the page that administrating the same server.
178 + if (showIf && !showIf(values))
179 + return
180 + }
181 + catch {}
182 if (helperText)
183 helperText = md(helperText, { html: false })
184 const comp = (type2comp as any)[type] as Field<any> | undefined
185 if (comp === ArrayField) {
186 rest.valuesForAdd = newObj(fields, x => x.defaultValue)
180 - fields = makeFields(fields)
187 + fields = makeFields(fields, values)
188 }
189 if (defaultValue !== undefined && type === 'boolean')
190 rest.placeholder = `Default value is ${JSON.stringify(defaultValue)}`
dev-plugins.md
+4 -2
@@ -136,11 +136,12 @@ used must be strictly JSON (thus, no single quotes, only double quotes for strin
136
137 ### FieldDescriptor
138
139 -Currently, these properties are supported:
139 +A FieldDescriptor is an object and can be empty. Currently, these optional properties are supported:
140 - `type: 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'real_path' | 'vfs_path' | 'array' | 'username' | 'color'` . Default is `string`.
141 - `label: string` what name to display next to the field. Default is based on `key`.
142 -- `defaultValue: any` value to be used when nothing is set.
142 +- `defaultValue: any` value to be used when nothing is set. Default is undefined.
143 - `helperText: string` extra text printed next to the field.
144 +- `showIf: (values: object) => boolean` only show this field if the function returns truthy. Must not reference variables of the outer scope.
145 - `frontend: boolean` expose this setting on the frontend, so that javascript can access it
146 using `HFS.getPluginConfig()[CONFIG_KEY]` but also css can access it as `var(--PLUGIN_NAME-CONFIG_KEY)`.
147 Hint: if you need to use a numeric config in CSS but you need to add a unit (like `em`),
@@ -699,6 +700,7 @@ If you want to override a text regardless of the language, use the special langu
700 - HFS.urlParams
701 - exports.beforePlugin + afterPlugin
702 - config.type: color
703 + - config.showIf
704 - init can now return directly the unload function
705 - api.i18n
706 - frontend event: newListEntries
src/api.plugins.ts
+5 -1
@@ -15,6 +15,7 @@ import {
15 } from './github'
16 import { HTTP_FAILED_DEPENDENCY, HTTP_NOT_FOUND, HTTP_SERVER_ERROR } from './const'
17 import { SendListReadable } from './SendList'
18 +import produce from 'immer'
19
20 const apis: ApiHandlers = {
21
@@ -174,10 +175,13 @@ const apis: ApiHandlers = {
175 export default apis
176
177 function serialize(p: Readonly<Plugin> | AvailablePlugin) {
177 - const o = 'getData' in p ? Object.assign(_.pick(p, ['id','started']), p.getData())
178 + let o = 'getData' in p ? Object.assign(_.pick(p, ['id','started']), p.getData())
179 : { ...p } // _.defaults mutates object, and we don't want that
180 if (typeof o.repo === 'object') // custom repo
181 o.repo = o.repo.web
182 + o = produce(o, (o: any) => {
183 + _.each(o.config, x => x.showIf &&= String(x.showIf))
184 + })
185 return _.defaults(o, { started: null, badApi: null }) // nulls should be used to be sure to overwrite previous values,
186 }
187