Merge pull request #1653 from YunoHost/options-doc

ConfigPanel and app install form Options documentation
This commit is contained in:
Alexandre Aubin 2023-10-30 19:05:56 +01:00 committed by GitHub
commit 1acaf2af2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 716 additions and 0 deletions

View file

@ -13,9 +13,11 @@ generate-helpers-doc:
- cd doc
- python3 generate_helper_doc.py
- python3 generate_resource_doc.py > resources.md
- python3 generate_configpanel_and_formoptions_doc.py > forms.md
- hub clone https://$GITHUB_TOKEN:x-oauth-basic@github.com/YunoHost/doc.git doc_repo
- cp helpers.md doc_repo/pages/06.contribute/10.packaging_apps/80.resources/11.helpers/packaging_apps_helpers.md
- cp resources.md doc_repo/pages/06.contribute/10.packaging_apps/80.resources/15.appresources/packaging_apps_resources.md
- cp forms doc_repo/pages/06.contribute/15.dev/03.forms/forms.md
- cd doc_repo
# replace ${CI_COMMIT_REF_NAME} with ${CI_COMMIT_TAG} ?
- hub checkout -b "${CI_COMMIT_REF_NAME}"

View file

@ -0,0 +1,171 @@
import sys
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()
def print_config_panel_docs():
fname = "../src/utils/configpanel.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)
ConfigPanelClasses = reversed(
[
c
for c in tree.body
if isinstance(c, ast.ClassDef)
and c.name in {"SectionModel", "PanelModel", "ConfigPanelModel"}
]
)
print("## Configuration panel structure")
for c in ConfigPanelClasses:
doc = ast.get_docstring(c)
print("")
print(f"### {c.name.replace('Model', '')}")
print("")
print(doc)
print("")
print("---")
def print_form_doc():
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 = {}
print("## List of all option types")
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
generaltype = c.bases[0].id.replace("Option", "").replace("Base", "").lower() if c.bases else None
docstring = ast.get_docstring(c)
if docstring:
if "#### Properties" not in docstring:
docstring += """
#### Properties
- [common properties](#common-properties)"""
OptionDocString[option_type] = {"doc": docstring, "generaltype": generaltype}
# Dirty hack to have "BaseOption" as first and "BaseInputOption" as 2nd in list
base = OptionDocString.pop("BaseOption")
baseinput = OptionDocString.pop("BaseInputOption")
OptionDocString2 = {
"BaseOption": base,
"BaseInputOption": baseinput,
}
OptionDocString2.update(OptionDocString)
for option_type, infos in OptionDocString2.items():
if option_type == "display_text":
# display_text is kind of legacy x_x
continue
print("")
if option_type == "BaseOption":
print("### Common properties")
elif option_type == "BaseInputOption":
print("### Common inputs properties")
else:
print(f"### `{option_type}`" + (f" ({infos['generaltype']})" if infos["generaltype"] else ""))
print("")
print(infos["doc"])
print("")
print("---")
print(
f"""---
title: Technical details for config panel structure and form option types
template: docs
taxonomy:
category: docs
routes:
default: '/dev/forms'
---
Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{current_commit}/doc/generate_options_doc.py) on {today} (YunoHost version {version})
## 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: `==`, `!=`, `>`, `>=`, `<`, `<=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%` and `match()`
- `Binding`: bind a value to a file/property/variable/getter/setter/validator
- save the value in `settings.yaml` when not defined
- 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`
- `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 number such as F12: one uppercase and 2 numbers"
# or with translated error
pattern.error.en = "Provide a room number such as F12: one uppercase and 2 numbers"
pattern.error.fr = "Entrez un numéro de salle comme F12: une lettre majuscule et deux chiffres."
```
- IMPORTANT: your `pattern.regexp` should be between simple quote, not double.
"""
)
print_config_panel_docs()
print_form_doc()

View file

@ -84,6 +84,38 @@ class ContainerModel(BaseModel):
class SectionModel(ContainerModel, OptionsModel):
"""
Sections are, basically, options grouped together. Sections are `dict`s defined inside a Panel and require a unique id (in the below example, the id is `customization` prepended by the panel's id `main`). Keep in mind that this combined id will be used in CLI to refer to the section, so choose something short and meaningfull. Also make sure to not make a typo in the panel id, which would implicitly create an other entire panel.
If at least one `button` is present it then become an action section.
Options in action sections are not considered settings and therefor are not saved, they are more like parameters that exists only during the execution of an action.
FIXME i'm not sure we have this in code.
#### Examples
```toml
[main]
[main.customization]
name.en = "Advanced configuration"
name.fr = "Configuration avancée"
help = "Every form items in this section are not saved."
services = ["__APP__", "nginx"]
[main.customization.option_id]
type = "string"
# …refer to Options doc
```
#### Properties
- `name` (optional): `Translation` or `str`, displayed as the section's title if any
- `help`: `Translation` or `str`, text to display before the first option
- `services` (optional): `list` of services names to `reload-or-restart` when any option's value contained in the section changes
- `"__APP__` will refer to the app instance name
- `optional`: `bool` (default: `true`), set the default `optional` prop of all Options in the section
- `visible`: `bool` or `JSExpression` (default: `true`), allow to conditionally display a section depending on user's answers to previous questions.
- Be careful that the `visible` property should only refer to **previous** options's value. Hence, it should not make sense to have a `visible` property on the very first section.
"""
visible: Union[bool, str] = True
optional: bool = True
is_action_section: bool = False
@ -138,6 +170,28 @@ class SectionModel(ContainerModel, OptionsModel):
class PanelModel(ContainerModel):
"""
Panels are, basically, sections grouped together. Panels are `dict`s defined inside a ConfigPanel file and require a unique id (in the below example, the id is `main`). Keep in mind that this id will be used in CLI to refer to the panel, so choose something short and meaningfull.
#### Examples
```toml
[main]
name.en = "Main configuration"
name.fr = "Configuration principale"
help = ""
services = ["__APP__", "nginx"]
[main.customization]
# …refer to Sections doc
```
#### Properties
- `name`: `Translation` or `str`, displayed as the panel title
- `help` (optional): `Translation` or `str`, text to display before the first section
- `services` (optional): `list` of services names to `reload-or-restart` when any option's value contained in the panel changes
- `"__APP__` will refer to the app instance name
- `actions`: FIXME not sure what this does
"""
# FIXME what to do with `actions?
actions: dict[str, Translation] = {"apply": {"en": "Apply"}}
sections: list[SectionModel]
@ -177,6 +231,26 @@ class PanelModel(ContainerModel):
class ConfigPanelModel(BaseModel):
"""
This is the 'root' level of the config panel toml file
#### Examples
```toml
version = 1.0
[config]
# …refer to Panels doc
```
#### Properties
- `version`: `float` (default: `1.0`), version that the config panel supports in terms of features.
- `i18n` (optional): `str`, an i18n property that let you internationalize options text.
- However this feature is only available in core configuration panel (like `yunohost domain config`), prefer the use `Translation` in `name`, `help`, etc.
"""
version: float = CONFIG_PANEL_VERSION_SUPPORTED
i18n: Union[str, None] = None
panels: list[PanelModel]

View file

@ -304,6 +304,59 @@ class Pattern(BaseModel):
class BaseOption(BaseModel):
"""
Options are fields declaration that renders as form items, button, alert or text in the web-admin and printed or prompted in CLI.
They are used in app manifests to declare the before installation form and in config panels.
[Have a look at the app config panel doc](/packaging_apps_config_panels) for details about Panels and Sections.
! IMPORTANT: as for Panels and Sections you have to choose an id, but this one should be unique in all this document, even if the question is in an other panel.
#### Example
```toml
[section.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`: the actual type of the option, such as 'markdown', 'password', 'number', 'email', ...
- `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
- in config panels, questions are displayed on the left side and therefore have not much space to be rendered. Therefore, it is better to use a short question, and use the `help` property to provide additional details if necessary.
- `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`, config panels only! A powerful feature that let you configure how and where the setting will be read, validated and written
- if not specified, the value will be read/written in the app `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__` or `__INSTALL_DIR__` in your path to point to dynamic install paths
- FIXME are other global variables accessible?
- [refer to `bind` doc for explaination and examples](#read-and-write-values-the)
"""
type: OptionType
id: str
ask: Union[Translation, None]
@ -375,10 +428,34 @@ class BaseReadonlyOption(BaseOption):
class DisplayTextOption(BaseReadonlyOption):
"""
Display simple multi-line content.
#### Example
```toml
[section.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
#### Example
```toml
[section.my_option_id]
type = "display_text"
ask = "Text **rendered** in markdown."
```
"""
type: Literal[OptionType.markdown] = OptionType.markdown
@ -390,6 +467,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.
#### Example
```toml
[section.my_option_id]
type = "alert"
ask = "The configuration seems to be manually modified..."
style = "warning"
icon = "warning"
```
#### Properties
- [common properties](#common-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
@ -406,6 +504,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).
#### Example
```toml
[section.my_option_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-properties)
- `bind`: forced to `"null"`
- `style`: any of `"success|info|warning|danger"` (default: `"success"`)
- `enabled`: `JSExpression` or `bool` (default: `true`)
- when used with `JSExpression` 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
@ -426,6 +563,35 @@ class ButtonOption(BaseReadonlyOption):
class BaseInputOption(BaseOption):
"""
Rest of the option types available are considered `inputs`.
#### Example
```toml
[section.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-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
@ -574,10 +740,44 @@ class BaseStringOption(BaseInputOption):
class StringOption(BaseStringOption):
"""
Ask for a simple string.
#### Example
```toml
[section.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.
#### Example
```toml
[section.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
@ -585,6 +785,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)
#### Example
```toml
[section.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
@ -621,6 +837,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.
#### Example
```toml
[section.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
@ -653,6 +884,26 @@ class ColorOption(BaseInputOption):
class NumberOption(BaseInputOption):
"""
Ask for a number (an integer).
#### Example
```toml
[section.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]
@ -707,6 +958,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.
#### Example
```toml
[section.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
@ -812,6 +1084,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.
#### Example
```toml
[section.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
@ -827,6 +1116,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.
#### Example
```toml
[section.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
@ -846,12 +1150,41 @@ class TimeOption(BaseInputOption):
class EmailOption(BaseInputOption):
"""
Ask for an email. Validation made with [python-email-validator](https://github.com/JoshData/python-email-validator)
#### Example
```toml
[section.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.
#### Example
```toml
[section.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
@ -885,6 +1218,21 @@ class WebPathOption(BaseStringOption):
class URLOption(BaseStringOption):
"""
Ask for any url.
#### Example
```toml
[section.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
@ -893,6 +1241,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.
#### Example
```toml
[section.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)
@ -1002,6 +1369,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`.
#### Example
```toml
[section.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]]
@ -1010,6 +1395,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"`
#### Example
```toml
[section.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
@ -1092,6 +1502,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.
#### Example
```toml
[section.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]
@ -1132,6 +1556,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.
#### Example
```toml
[section.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
@ -1169,6 +1609,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.
#### Example
```toml
[section.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]
@ -1215,6 +1669,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.
#### Example
```toml
[section.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]