mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
doc:options: add documentation and generator for configpanel/manifest options
This commit is contained in:
parent
9134515604
commit
1221fd1458
2 changed files with 851 additions and 0 deletions
365
doc/generate_options_doc.py
Normal file
365
doc/generate_options_doc.py
Normal file
|
@ -0,0 +1,365 @@
|
|||
import ast
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
version = open("../debian/changelog").readlines()[0].split()[1].strip("()")
|
||||
today = datetime.datetime.now().strftime("%d/%m/%Y")
|
||||
|
||||
|
||||
def get_current_commit():
|
||||
p = subprocess.Popen(
|
||||
"git rev-parse --verify HEAD",
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
|
||||
current_commit = stdout.strip().decode("utf-8")
|
||||
return current_commit
|
||||
|
||||
|
||||
current_commit = get_current_commit()
|
||||
|
||||
|
||||
print(
|
||||
f"""---
|
||||
title: Options
|
||||
template: docs
|
||||
taxonomy:
|
||||
category: docs
|
||||
routes:
|
||||
default: '/packaging_apps_options'
|
||||
---
|
||||
|
||||
# Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{current_commit}/doc/generate_options_doc.py) on {today} (YunoHost version {version})
|
||||
|
||||
# Options
|
||||
|
||||
Options are fields declaration that renders as form items in the web-admin and prompts in cli.
|
||||
They are used in app manifests to declare the before installation form and in config panels.
|
||||
|
||||
## Glossary
|
||||
|
||||
You may encounter some named types which are used for simplicity.
|
||||
|
||||
- `Translation`: a translated property
|
||||
- used for properties: `ask`, `help` and `Pattern.error`
|
||||
- a `dict` with locales as keys and translations as values:
|
||||
```toml
|
||||
ask.en = "The text in english"
|
||||
ask.fr = "Le texte en français"
|
||||
```
|
||||
It is not currently possible for translators to translate those string in weblate.
|
||||
- a single `str` for a single english default string
|
||||
```toml
|
||||
help = "The text in english"
|
||||
```
|
||||
- `JSExpression`: a `str` JS expression to be evaluated to `true` or `false`:
|
||||
- used for properties: `visible` and `enabled`
|
||||
- operators availables: `==`, `!=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%`, `match()`
|
||||
- [examples available in the advanced section](#advanced-use-cases)
|
||||
- `Binding`: bind a value to a file/property/variable/getter/setter/validator
|
||||
- save the value in `settings.yaml` when not defined (`None`)
|
||||
- nothing at all with `"null"`
|
||||
- a custom getter/setter/validator with `"null"` + a function starting with `get__`, `set__`, `validate__` in `scripts/config`
|
||||
- a variable/property in a file with `:__FINALPATH__/my_file.php`
|
||||
- a whole file with `__FINALPATH__/my_file.php`
|
||||
- [examples available in the advanced section](#bind)
|
||||
- `Pattern`: a `dict` with a regex to match the value against and an error message
|
||||
```toml
|
||||
pattern.regexp = "^[A-F]\d\d$"
|
||||
pattern.error = "Provide a room like F12: one uppercase and 2 numbers"
|
||||
# or with translated error
|
||||
pattern.error.en = "Provide a room like F12: one uppercase and 2 numbers"
|
||||
pattern.error.fr = "Entrez un numéro de salle comme F12: une lettre majuscule et deux chiffres."
|
||||
```
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
fname = "../src/utils/form.py"
|
||||
content = open(fname).read()
|
||||
|
||||
# NB: This magic is because we want to be able to run this script outside of a YunoHost context,
|
||||
# in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports...
|
||||
tree = ast.parse(content)
|
||||
|
||||
OptionClasses = [
|
||||
c
|
||||
for c in tree.body
|
||||
if isinstance(c, ast.ClassDef) and c.name.endswith("Option")
|
||||
]
|
||||
|
||||
OptionDocString = {}
|
||||
|
||||
for c in OptionClasses:
|
||||
if not isinstance(c.body[0], ast.Expr):
|
||||
continue
|
||||
option_type = None
|
||||
|
||||
if c.name in {"BaseOption", "BaseInputOption"}:
|
||||
option_type = c.name
|
||||
elif c.body[1].target.id == "type":
|
||||
option_type = c.body[1].value.attr
|
||||
|
||||
docstring = ast.get_docstring(c)
|
||||
if docstring:
|
||||
if "##### Properties" not in docstring:
|
||||
docstring += """
|
||||
##### Properties
|
||||
|
||||
- [common properties](#common-option-properties)
|
||||
"""
|
||||
OptionDocString[option_type] = docstring
|
||||
|
||||
for option_type, doc in OptionDocString.items():
|
||||
print("")
|
||||
if option_type == "BaseOption":
|
||||
print("## Common Option properties")
|
||||
elif option_type == "BaseInputOption":
|
||||
print("## Common Inputs properties")
|
||||
elif option_type == "display_text":
|
||||
print("----------------")
|
||||
print("## Readonly Options")
|
||||
print(f"### Option `{option_type}`")
|
||||
elif option_type == "string":
|
||||
print("----------------")
|
||||
print("## Input Options")
|
||||
print(f"### Option `{option_type}`")
|
||||
else:
|
||||
print(f"### Option `{option_type}`")
|
||||
print("")
|
||||
print(doc)
|
||||
print("")
|
||||
|
||||
print(
|
||||
"""
|
||||
----------------
|
||||
|
||||
## Advanced use cases
|
||||
|
||||
### `visible` & `enabled` expression evaluation
|
||||
|
||||
Sometimes we may want to conditionaly display a message or prompt for a value, for this we have the `visible` prop.
|
||||
And we may want to allow a user to trigger an action only if some condition are met, for this we have the `enabled` prop.
|
||||
|
||||
Expressions are evaluated against a context containing previous values of the current section's options. This quite limited current design exists because on the web-admin or on the CLI we cannot guarantee that a value will be present in the form if the user queried only a single panel/section/option.
|
||||
In the case of an action, the user will be shown or asked for each of the options of the section in which the button is present.
|
||||
|
||||
The expression has to be written in javascript (this has been designed for the web-admin first and is converted to python on the fly on the cli).
|
||||
|
||||
Available operators are: `==`, `!=`, `>`, `>=`, `<`, `<=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%` and `match()`.
|
||||
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
# simple "my_option_id" is thruthy/falsy
|
||||
visible = "my_option_id"
|
||||
visible = "!my_option_id"
|
||||
# misc
|
||||
visible = "my_value >= 10"
|
||||
visible = "-(my_value + 1) < 0"
|
||||
visible = "!!my_value || my_other_value"
|
||||
```
|
||||
For a more complete set of examples, [check the tests at the end of the file](https://github.com/YunoHost/yunohost/blob/dev/src/tests/test_questions.py).
|
||||
|
||||
##### match()
|
||||
|
||||
For more complex evaluation we can use regex matching.
|
||||
|
||||
```toml
|
||||
[my_string]
|
||||
default = "Lorem ipsum dolor et si qua met!"
|
||||
|
||||
[my_boolean]
|
||||
type = "boolean"
|
||||
visible = "my_string && match(my_string, '^Lorem [ia]psumE?')"
|
||||
```
|
||||
|
||||
Match the content of a file.
|
||||
|
||||
```toml
|
||||
[my_file]
|
||||
type = "file"
|
||||
accept = ".txt"
|
||||
bind = "/etc/random/lorem.txt"
|
||||
|
||||
[my_boolean]
|
||||
type = "boolean"
|
||||
visible = "my_file && match(my_file, '^Lorem [ia]psumE?')"
|
||||
```
|
||||
|
||||
with a file with content like:
|
||||
```txt
|
||||
Lorem ipsum dolor et si qua met!
|
||||
```
|
||||
|
||||
|
||||
### `bind`
|
||||
|
||||
Config panels only
|
||||
|
||||
`bind` allows us to alter the generic behavior of option's values which is: get from and set in `settings.yml`.
|
||||
|
||||
We can:
|
||||
- alter the source the value comes from with getters.
|
||||
- alter the destination with setters
|
||||
- parse/validate the value before destination with validators
|
||||
|
||||
##### Getters
|
||||
|
||||
Define an option's custom getter in a bash script `script/config`.
|
||||
It has to be named after an option's id prepended by `get__`.
|
||||
|
||||
To display a custom alert message for example. We setup a base option in `config_panel.toml`.
|
||||
|
||||
```toml
|
||||
[panel.section.alert]
|
||||
type = "alert"
|
||||
# bind to "null" to inform there's something in `scripts/config`
|
||||
bind = "null"
|
||||
# `ask` & `style` will be injected by a custom getter
|
||||
```
|
||||
|
||||
Then add a custom getter that output yaml, every properties defined here will override any property previously declared.
|
||||
|
||||
```bash
|
||||
get__alert() {
|
||||
if [ "$whatever" ]; then
|
||||
cat << EOF
|
||||
style: success
|
||||
ask: Your VPN is running :)
|
||||
EOF
|
||||
else
|
||||
cat << EOF
|
||||
style: danger
|
||||
ask: Your VPN is down
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
Or to inject a custom value:
|
||||
|
||||
```toml
|
||||
[panel.section.my_hidden_value]
|
||||
type = "number"
|
||||
bind = "null"
|
||||
# option will act as an hidden variable that can be used in context evaluation
|
||||
# (ie: `visible` or `enabled`)
|
||||
readonly = true
|
||||
visible = false
|
||||
# `default` injected by a custom getter
|
||||
```
|
||||
|
||||
```bash
|
||||
get__my_hidden_value() {
|
||||
if [ "$whatever" ]; then
|
||||
# if only a value is needed
|
||||
echo "10"
|
||||
else
|
||||
# or if we need to override some other props
|
||||
# (use `default` or `value` to inject the value)
|
||||
cat << EOF
|
||||
ask: Here's a number
|
||||
visible: true
|
||||
default: 0
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
##### Setters
|
||||
|
||||
Define an option's custom setter in a bash script `script/config`.
|
||||
It has to be named after an option's id prepended by `set__`.
|
||||
|
||||
```toml
|
||||
[panel.section.my_value]
|
||||
type = "string"
|
||||
bind = "null"
|
||||
ask = "gimme complex string"
|
||||
```
|
||||
|
||||
```bash
|
||||
set__my_value() {
|
||||
if [ -n "$my_value" ]; then
|
||||
# split the string into multiple elements or idk
|
||||
fi
|
||||
# To save the value or modified value as a setting:
|
||||
ynh_app_setting_set --app=$app --key=my_value --value="$my_value"
|
||||
}
|
||||
```
|
||||
|
||||
##### Validators
|
||||
|
||||
Define an option's custom validator in a bash script `script/config`.
|
||||
It has to be named after an option's id prepended by `validate__`.
|
||||
|
||||
Validators allows us to return custom error messages depending on the value.
|
||||
|
||||
```toml
|
||||
[panel.section.my_value]
|
||||
type = "string"
|
||||
bind = "null"
|
||||
ask = "Gimme a long string"
|
||||
default = "too short"
|
||||
```
|
||||
|
||||
```bash
|
||||
validate__my_value() {
|
||||
if [[ "${#my_value}" -lt 12 ]]; then echo 'Too short!'; fi
|
||||
}
|
||||
```
|
||||
|
||||
##### Actions
|
||||
|
||||
Define an option's action in a bash script `script/config`.
|
||||
It has to be named after a `button`'s id prepended by `run__`.
|
||||
|
||||
```toml
|
||||
[panel.section.my_action]
|
||||
type = "button"
|
||||
# no need to set `bind` to "null" it is its hard default
|
||||
ask = "Run action"
|
||||
```
|
||||
|
||||
```bash
|
||||
run__my_action() {
|
||||
ynh_print_info "Running 'my_action'..."
|
||||
}
|
||||
```
|
||||
|
||||
A more advanced example could look like:
|
||||
|
||||
```toml
|
||||
[panel.my_action_section]
|
||||
name = "Action section"
|
||||
[panel.my_action_section.my_repo]
|
||||
type = "url"
|
||||
bind = "null" # value will not be saved as a setting
|
||||
ask = "gimme a repo link"
|
||||
|
||||
[panel.my_action_section.my_repo_name]
|
||||
type = "string"
|
||||
bind = "null" # value will not be saved as a setting
|
||||
ask = "gimme a custom folder name"
|
||||
|
||||
[panel.my_action_section.my_action]
|
||||
type = "button"
|
||||
ask = "Clone the repo"
|
||||
# enabled the button only if the above values is defined
|
||||
enabled = "my_repo && my_repo_name"
|
||||
```
|
||||
|
||||
```bash
|
||||
run__my_action() {
|
||||
ynh_print_info "Cloning '$my_repo'..."
|
||||
cd /tmp
|
||||
git clone "$my_repo" "$my_repo_name"
|
||||
}
|
||||
```
|
||||
"""
|
||||
)
|
|
@ -298,6 +298,76 @@ class Pattern(BaseModel):
|
|||
|
||||
|
||||
class BaseOption(BaseModel):
|
||||
"""
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "string"
|
||||
# ask as `str`
|
||||
ask = "The text in english"
|
||||
# ask as `dict`
|
||||
ask.en = "The text in english"
|
||||
ask.fr = "Le texte en français"
|
||||
# advanced props
|
||||
visible = "my_other_option_id != 'success'"
|
||||
readonly = true
|
||||
# much advanced: config panel only?
|
||||
bind = "null"
|
||||
```
|
||||
|
||||
##### Properties
|
||||
|
||||
- `type`:
|
||||
- readonly types:
|
||||
- [`display_text`](#option-display_text)
|
||||
- [`markdown`](#option-markdown)
|
||||
- [`alert`](#option-alert)
|
||||
- [`button`](#option-button)
|
||||
- inputs types:
|
||||
- [`string`](#option-string)
|
||||
- [`text`](#option-text)
|
||||
- [`password`](#option-password)
|
||||
- [`color`](#option-color)
|
||||
- [`number`](#option-number)
|
||||
- [`range`](#option-range)
|
||||
- [`boolean`](#option-boolean)
|
||||
- [`date`](#option-date)
|
||||
- [`time`](#option-time)
|
||||
- [`email`](#option-email)
|
||||
- [`path`](#option-path)
|
||||
- [`url`](#option-url)
|
||||
- [`file`](#option-file)
|
||||
- [`select`](#option-select)
|
||||
- [`tags`](#option-tags)
|
||||
- [`domain`](#option-domain)
|
||||
- [`app`](#option-app)
|
||||
- [`user`](#option-user)
|
||||
- [`group`](#option-group)
|
||||
- `ask`: `Translation` (default to the option's `id` if not defined):
|
||||
- text to display as the option's label for inputs or text to display for readonly options
|
||||
- `visible` (optional): `bool` or `JSExpression` (default: `true`)
|
||||
- define if the option is diplayed/asked
|
||||
- if `false` and used alongside `readonly = true`, you get a context only value that can still be used in `JSExpression`s
|
||||
- `readonly` (optional): `bool` (default: `false`, forced to `true` for readonly types):
|
||||
- If `true` for input types: forbid mutation of its value
|
||||
- `bind` (optional): `Binding` (default: `None`):
|
||||
- (config panels only!) allow to choose where an option's is gathered/stored:
|
||||
- if not specified, the value will be gathered/stored in the `settings.yml`
|
||||
- if `"null"`:
|
||||
- the value will not be stored at all (can still be used in context evaluations)
|
||||
- if in `scripts/config` there's a function named:
|
||||
- `get__my_option_id`: the value will be gathered from this custom getter
|
||||
- `set__my_option_id`: the value will be passed to this custom setter where you can do whatever you want with the value
|
||||
- `validate__my_option_id`: the value will be passed to this custom validator before any custom setter
|
||||
- if `bind` is a file path:
|
||||
- if the path starts with `:`, the value be saved as its id's variable/property counterpart
|
||||
- this only works for first level variables/properties and simple types (no array)
|
||||
- else the value will be stored as the whole content of the file
|
||||
- you can use `__FINALPATH__` in your path to point to dynamic install paths
|
||||
- FIXME are other global variables accessible?
|
||||
"""
|
||||
|
||||
type: OptionType
|
||||
id: str
|
||||
ask: Union[Translation, None]
|
||||
|
@ -364,10 +434,34 @@ class BaseReadonlyOption(BaseOption):
|
|||
|
||||
|
||||
class DisplayTextOption(BaseReadonlyOption):
|
||||
"""
|
||||
Display simple multi-line content.
|
||||
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "display_text"
|
||||
ask = "Simple text rendered as is."
|
||||
```
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.display_text] = OptionType.display_text
|
||||
|
||||
|
||||
class MarkdownOption(BaseReadonlyOption):
|
||||
"""
|
||||
Display markdown multi-line content.
|
||||
Markdown is currently only rendered in the web-admin
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "display_text"
|
||||
ask = "Text **rendered** in markdown."
|
||||
```
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.markdown] = OptionType.markdown
|
||||
|
||||
|
||||
|
@ -379,6 +473,27 @@ class State(str, Enum):
|
|||
|
||||
|
||||
class AlertOption(BaseReadonlyOption):
|
||||
"""
|
||||
Alerts displays a important message with a level of severity.
|
||||
You can use markdown in `ask` but will only be rendered in the web-admin.
|
||||
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "alert"
|
||||
ask = "The configuration seems to be manually modified..."
|
||||
style = "warning"
|
||||
icon = "warning"
|
||||
```
|
||||
##### Properties
|
||||
|
||||
- [common properties](#common-option-properties)
|
||||
- `style`: any of `"success|info|warning|danger"` (default: `"info"`)
|
||||
- `icon` (optional): any icon name from [Fork Awesome](https://forkaweso.me/Fork-Awesome/icons/)
|
||||
- Currently only displayed in the web-admin
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.alert] = OptionType.alert
|
||||
style: State = State.info
|
||||
icon: Union[str, None] = None
|
||||
|
@ -395,6 +510,45 @@ class AlertOption(BaseReadonlyOption):
|
|||
|
||||
|
||||
class ButtonOption(BaseReadonlyOption):
|
||||
"""
|
||||
Triggers actions.
|
||||
Available only in config panels.
|
||||
Renders as a `button` in the web-admin and can be called with `yunohost [app|domain|settings] action run <action_id>` in CLI.
|
||||
|
||||
Every options defined in an action section (a config panel section with at least one `button`) is guaranted to be shown/asked to the user and available in `scripts/config`'s scope.
|
||||
[check examples in advanced use cases](#actions).
|
||||
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
[my_action_id]
|
||||
type = "button"
|
||||
ask = "Break the system"
|
||||
style = "danger"
|
||||
icon = "bug"
|
||||
# enabled only if another option's value (a `boolean` for example) is positive
|
||||
enabled = "aknowledged"
|
||||
```
|
||||
|
||||
To be able to trigger an action we have to add a bash function starting with `run__` in your `scripts/config`
|
||||
|
||||
```bash
|
||||
run__my_action_id() {
|
||||
ynh_print_info "Running 'my_action_id' action"
|
||||
}
|
||||
```
|
||||
|
||||
##### Properties
|
||||
|
||||
- [common properties](#common-option-properties)
|
||||
- `bind`: forced to `"null"`
|
||||
- `style`: any of `"success|info|warning|danger"` (default: `"success"`)
|
||||
- `enabled`: `Binding` or `bool` (default: `true`)
|
||||
- when used with `Binding` you can enable/disable the button depending on context
|
||||
- `icon` (optional): any icon name from [Fork Awesome](https://forkaweso.me/Fork-Awesome/icons/)
|
||||
- Currently only displayed in the web-admin
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.button] = OptionType.button
|
||||
bind: Literal["null"] = "null"
|
||||
help: Union[Translation, None] = None
|
||||
|
@ -415,6 +569,35 @@ class ButtonOption(BaseReadonlyOption):
|
|||
|
||||
|
||||
class BaseInputOption(BaseOption):
|
||||
"""
|
||||
Rest of the option types available are considered `inputs`.
|
||||
|
||||
##### Examples
|
||||
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "string"
|
||||
# …any common props… +
|
||||
optional = false
|
||||
redact = False
|
||||
default = "some default string"
|
||||
help = "You can enter almost anything!"
|
||||
example = "an example string"
|
||||
placeholder = "write something…"
|
||||
```
|
||||
|
||||
##### Properties
|
||||
|
||||
- [common properties](#common-option-properties)
|
||||
- `optional`: `bool` (default: `false`, but `true` in config panels)
|
||||
- `redact`: `bool` (default: `false`), to redact the value in the logs when the value contain private information
|
||||
- `default`: depends on `type`, the default value to assign to the option
|
||||
- in case of readonly values, you can use this `default` to assign a value (or return a dynamic `default` from a custom getter)
|
||||
- `help` (optional): `Translation`, to display a short help message in cli and web-admin
|
||||
- `example` (optional): `str`, to display an example value in web-admin only
|
||||
- `placeholder` (optional): `str`, shown in the web-admin fields only
|
||||
"""
|
||||
|
||||
help: Union[Translation, None] = None
|
||||
example: Union[str, None] = None
|
||||
placeholder: Union[str, None] = None
|
||||
|
@ -563,10 +746,44 @@ class BaseStringOption(BaseInputOption):
|
|||
|
||||
|
||||
class StringOption(BaseStringOption):
|
||||
"""
|
||||
Ask for a simple string.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "string"
|
||||
default = "E10"
|
||||
pattern.regexp = "^[A-F]\d\d$"
|
||||
pattern.error = "Provide a room like F12 : one uppercase and 2 numbers"
|
||||
```
|
||||
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `pattern` (optional): `Pattern`, a regex to match the value against
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.string] = OptionType.string
|
||||
|
||||
|
||||
class TextOption(BaseStringOption):
|
||||
"""
|
||||
Ask for a multiline string.
|
||||
Renders as a `textarea` in the web-admin and by opening a text editor on the CLI.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "text"
|
||||
default = "multi\\nline\\ncontent"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `pattern` (optional): `Pattern`, a regex to match the value against
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.text] = OptionType.text
|
||||
|
||||
|
||||
|
@ -574,6 +791,22 @@ FORBIDDEN_PASSWORD_CHARS = r"{}"
|
|||
|
||||
|
||||
class PasswordOption(BaseInputOption):
|
||||
"""
|
||||
Ask for a password.
|
||||
The password is tested as a regular user password (at least 8 chars)
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "password"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: forced to `""`
|
||||
- `redact`: forced to `true`
|
||||
- `example`: forbidden
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.password] = OptionType.password
|
||||
example: Literal[None] = None
|
||||
default: Literal[None] = None
|
||||
|
@ -610,6 +843,21 @@ class PasswordOption(BaseInputOption):
|
|||
|
||||
|
||||
class ColorOption(BaseInputOption):
|
||||
"""
|
||||
Ask for a color represented as a hex value (with possibly an alpha channel).
|
||||
Renders as color picker in the web-admin and as a prompt that accept named color like `yellow` in CLI.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "color"
|
||||
default = "#ff0"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.color] = OptionType.color
|
||||
default: Union[str, None]
|
||||
_annotation = Color
|
||||
|
@ -642,6 +890,26 @@ class ColorOption(BaseInputOption):
|
|||
|
||||
|
||||
class NumberOption(BaseInputOption):
|
||||
"""
|
||||
Ask for a number (an integer).
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "number"
|
||||
default = 100
|
||||
min = 50
|
||||
max = 200
|
||||
step = 5
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `type`: `number` or `range` (input or slider in the web-admin)
|
||||
- `min` (optional): minimal int value inclusive
|
||||
- `max` (optional): maximal int value inclusive
|
||||
- `step` (optional): currently only used in the webadmin as the `<input/>` step jump
|
||||
"""
|
||||
|
||||
# `number` and `range` are exactly the same, but `range` does render as a slider in web-admin
|
||||
type: Literal[OptionType.number, OptionType.range] = OptionType.number
|
||||
default: Union[int, None]
|
||||
|
@ -696,6 +964,27 @@ class NumberOption(BaseInputOption):
|
|||
|
||||
|
||||
class BooleanOption(BaseInputOption):
|
||||
"""
|
||||
Ask for a boolean.
|
||||
Renders as a switch in the web-admin and a yes/no prompt in CLI.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "boolean"
|
||||
default = 1
|
||||
yes = "agree"
|
||||
no = "disagree"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `0`
|
||||
- `yes` (optional): (default: `1`) define as what the thruthy value should output
|
||||
- can be `true`, `True`, `"yes"`, etc.
|
||||
- `no` (optional): (default: `0`) define as what the thruthy value should output
|
||||
- can be `0`, `"false"`, `"n"`, etc.
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.boolean] = OptionType.boolean
|
||||
yes: Any = 1
|
||||
no: Any = 0
|
||||
|
@ -801,6 +1090,23 @@ class BooleanOption(BaseInputOption):
|
|||
|
||||
|
||||
class DateOption(BaseInputOption):
|
||||
"""
|
||||
Ask for a date in the form `"2025-06-14"`.
|
||||
Renders as a date-picker in the web-admin and a regular prompt in CLI.
|
||||
|
||||
Can also take a timestamp as value that will output as an ISO date string.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "date"
|
||||
default = "2070-12-31"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.date] = OptionType.date
|
||||
default: Union[str, None]
|
||||
_annotation = datetime.date
|
||||
|
@ -816,6 +1122,21 @@ class DateOption(BaseInputOption):
|
|||
|
||||
|
||||
class TimeOption(BaseInputOption):
|
||||
"""
|
||||
Ask for an hour in the form `"22:35"`.
|
||||
Renders as a date-picker in the web-admin and a regular prompt in CLI.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "time"
|
||||
default = "12:26"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.time] = OptionType.time
|
||||
default: Union[str, int, None]
|
||||
_annotation = datetime.time
|
||||
|
@ -835,12 +1156,41 @@ class TimeOption(BaseInputOption):
|
|||
|
||||
|
||||
class EmailOption(BaseInputOption):
|
||||
"""
|
||||
Ask for an email. Validation made with [python-email-validator](https://github.com/JoshData/python-email-validator)
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "email"
|
||||
default = "Abc.123@test-example.com"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.email] = OptionType.email
|
||||
default: Union[EmailStr, None]
|
||||
_annotation = EmailStr
|
||||
|
||||
|
||||
class WebPathOption(BaseStringOption):
|
||||
"""
|
||||
Ask for an web path (the part of an url after the domain). Used by default in app install to define from where the app will be accessible.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "path"
|
||||
default = "/"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `pattern` (optional): `Pattern`, a regex to match the value against
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.path] = OptionType.path
|
||||
|
||||
@staticmethod
|
||||
|
@ -874,6 +1224,21 @@ class WebPathOption(BaseStringOption):
|
|||
|
||||
|
||||
class URLOption(BaseStringOption):
|
||||
"""
|
||||
Ask for any url.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "url"
|
||||
default = "https://example.xn--zfr164b/@handle/"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `pattern` (optional): `Pattern`, a regex to match the value against
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.url] = OptionType.url
|
||||
_annotation = HttpUrl
|
||||
|
||||
|
@ -882,6 +1247,25 @@ class URLOption(BaseStringOption):
|
|||
|
||||
|
||||
class FileOption(BaseInputOption):
|
||||
"""
|
||||
Ask for file.
|
||||
Renders a file prompt in the web-admin and ask for a path in CLI.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "file"
|
||||
accept = ".json"
|
||||
# bind the file to a location to save the file there
|
||||
bind = "/tmp/my_file.json"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `accept`: a comma separated list of extension to accept like `".conf, .ini`
|
||||
- /!\ currently only work on the web-admin
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.file] = OptionType.file
|
||||
# `FilePath` for CLI (path must exists and must be a file)
|
||||
# `bytes` for API (a base64 encoded file actually)
|
||||
|
@ -991,6 +1375,24 @@ class BaseChoicesOption(BaseInputOption):
|
|||
|
||||
|
||||
class SelectOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for value from a limited set of values.
|
||||
Renders as a regular `<select/>` in the web-admin and as a regular prompt in CLI with autocompletion of `choices`.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "select"
|
||||
choices = ["one", "two", "three"]
|
||||
choices = "one,two,three"
|
||||
default = "two"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`, obviously the default has to be empty or an available `choices` item.
|
||||
- `choices`: a (coma separated) list of values
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.select] = OptionType.select
|
||||
filter: Literal[None] = None
|
||||
choices: Union[dict[str, Any], list[Any]]
|
||||
|
@ -999,6 +1401,31 @@ class SelectOption(BaseChoicesOption):
|
|||
|
||||
|
||||
class TagsOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for series of values. Optionally from a limited set of values.
|
||||
Renders as a multi select in the web-admin and as a regular prompt in CLI without autocompletion of `choices`.
|
||||
|
||||
This output as a coma separated list of strings `"one,two,three"`
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "tags"
|
||||
default = "word,another word"
|
||||
|
||||
[my_other_option_id]
|
||||
type = "tags"
|
||||
choices = ["one", "two", "three"]
|
||||
# choices = "one,two,three"
|
||||
default = "two,three"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`, obviously the default has to be empty or an available `choices` item.
|
||||
- `pattern` (optional): `Pattern`, a regex to match all the values against
|
||||
- `choices` (optional): a (coma separated) list of values
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.tags] = OptionType.tags
|
||||
filter: Literal[None] = None
|
||||
choices: Union[list[str], None] = None
|
||||
|
@ -1081,6 +1508,20 @@ class TagsOption(BaseChoicesOption):
|
|||
|
||||
|
||||
class DomainOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for a user domain.
|
||||
Renders as a select in the web-admin and as a regular prompt in CLI with autocompletion of registered domains.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "domain"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: forced to the instance main domain
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.domain] = OptionType.domain
|
||||
filter: Literal[None] = None
|
||||
choices: Union[dict[str, str], None]
|
||||
|
@ -1121,6 +1562,22 @@ class DomainOption(BaseChoicesOption):
|
|||
|
||||
|
||||
class AppOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for a user app.
|
||||
Renders as a select in the web-admin and as a regular prompt in CLI with autocompletion of installed apps.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "app"
|
||||
filter = "is_webapp"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `""`
|
||||
- `filter` (optional): `JSExpression` with what `yunohost app info <app_id> --full` returns as context (only first level keys)
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.app] = OptionType.app
|
||||
filter: Union[JSExpression, None] = None
|
||||
add_yunohost_portal_to_choices: bool = False
|
||||
|
@ -1158,6 +1615,20 @@ class AppOption(BaseChoicesOption):
|
|||
|
||||
|
||||
class UserOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for a user.
|
||||
Renders as a select in the web-admin and as a regular prompt in CLI with autocompletion of available usernames.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "user"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: the first admin user found
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.user] = OptionType.user
|
||||
filter: Literal[None] = None
|
||||
choices: Union[dict[str, str], None]
|
||||
|
@ -1204,6 +1675,21 @@ class UserOption(BaseChoicesOption):
|
|||
|
||||
|
||||
class GroupOption(BaseChoicesOption):
|
||||
"""
|
||||
Ask for a group.
|
||||
Renders as a select in the web-admin and as a regular prompt in CLI with autocompletion of available groups.
|
||||
|
||||
##### Examples
|
||||
```toml
|
||||
[my_option_id]
|
||||
type = "group"
|
||||
default = "visitors"
|
||||
```
|
||||
##### Properties
|
||||
- [common inputs properties](#common-inputs-properties)
|
||||
- `default`: `"all_users"`, `"visitors"` or `"admins"` (default: `"all_users"`)
|
||||
"""
|
||||
|
||||
type: Literal[OptionType.group] = OptionType.group
|
||||
filter: Literal[None] = None
|
||||
choices: Union[dict[str, str], None]
|
||||
|
|
Loading…
Add table
Reference in a new issue