Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config panel #175

Merged
merged 21 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions config_panel.toml.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@

## Config panel are available from webadmin > Apps > YOUR_APP > Config Panel Button
## Those panels let user configure some params on there apps to avoid them to change
## its by hand in configuration file and be abliged to reapply their changes at each
## app upgrade.

## -----------------------------------------------------------------------------
## IMPORTANT: YunoHost spirits is simplicity, please don't expose here tons of
## misunderstanble app settings or not really useful feature.
## -----------------------------------------------------------------------------
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## The top level describe the entire config panels screen.

## The version is a required property.
## Here a small reminder to associate config panel version with YunoHost version
## | Config | YNH | Config panel small change log |
## | ------ | --- | ------------------------------------------------------- |
## | 0.1 | 3.x | 0.1 config script not compatible with YNH >= 4.3 |
## | 1.0 | 4.3.x | The new config panel system with 'bind' property |
version = "1.0"

## (optional) i18n property let you internationalize questions, however this feature
## is only available in core configuration panel (like yunohost domain config).
## So in app config panel this key is ignored for now, but you can internationalize
## by using a lang dictionary (see property name bellow)
# i18n = "prefix_translation_key"

################################################################################
#### ABOUT PANELS
################################################################################

## The next level describes web admin panels
## You have to choose an ID for each panel, in this example the ID is "main"
## Keep in mind this id will be used in cli to refer to your question, so choose
## something short and meaningfull.
## Note: each panel is a distinct HTML form.
zamentur marked this conversation as resolved.
Show resolved Hide resolved
[main]

## (recommended) You should define the label of your panel (and associated tab)
## If you don't define it, the ID will be used as title
name = "Main configuration"

## To internationalize the name, and other textual properties you can suggest
## translation like this:
# name.en = "Main configuration"
# name.fr = "Configuration principale"
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (optional) If you need to trigger a service reload-or-restart after the user
## change a question in this panel, you can add your service in the list.
services = ["nginx", "__APP__"]
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (optional) This help properties is a short help displayed on the same line
## than the panel title but not displayed in the tab.
# help = ""

############################################################################
#### ABOUT SECTIONS
############################################################################

## A panel is compound of one or several sections.
## You have to choose an ID for your section and prefix it with the panel ID
## Be sure to not make a typo in panel prefix, or you will get an unwanted
## panel in more.
## For this example we imagine, we package the pepettes_ynh app.
## It's a really simple donation form without administration panel, so we
## want to expose some settings
[main.customization]
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (optional) A section could have a title, or not. It depends of what you
## are doing exactly. In web admin it will display an <h3> title.
name = ""
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (optional) This help properties is a short help displayed on the same line
## than the section title.
# help = ""
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (optional) As for panel, you can specify to trigger a service
## reload-or-restart after the user change a question in this section.
## This property is added to the panel property, it doesn't deactivate it.
## So no need to replicate, the service list from panel services property.
# services = []

## (optional) By default all questions are optionals, but you can specify a
## default behaviour for question in the section
optional = false

## (optional) It's also possible with the 'visible' property to display the
## question only if the user answer the form in a specific way.
## However, you should not refer to questions after the point where you put
## the visible property. SO the first section should never have a visible
## property
## In more this feature is available in webadmin but not in cli, so keep in
## mind cli user could be prompted for the question...
# visible = true
zamentur marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should provide a few examples of what the visible syntax looks like


########################################################################
#### ABOUT QUESTIONS
########################################################################

## A section is compound of one or several questions.

## ---------------------------------------------------------------------
## IMPORTANT: as for panel and section 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.
## ---------------------------------------------------------------------

## You can use same questions types and properties than in manifest.yml
## install part. However, in YNH 4.3, a lot of change has been made to
## extend availables questions types list.
## See: TODO DOC LINK
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#todo


[main.customization.project_name]

## (required) The ask property is equivalent to the ask property in
## manifest.yml. However, in config panel questions are displayed on the
## left. So, it's more a label than a complete question, make short.
ask = "Name of the project"
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## (required) The type property indicates how the question should be
## displayed, validated and managed. Some types have specific properties.
##
## Types available: string, boolean, number, range, text, password, path
## email, url, date, time, color, select, domain, user, tags, file.
##
## For a complete list with specific properties, see: TODO DOC LINK
type = "string"

########################################################################
#### ABOUT THE BIND PROPERTY
########################################################################

## (recommended) 'bind' property is a powerful feature that let you
## configure how the data will be read, validated and write.
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## By default, 'bind property is in "settings" mode, it means it will
## read / write the value in application settings file.
## bind = "settings"

## But in general, you prefer use the ":FILE" mode to read/write a
## specific variable in a file.
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## Configuration file format supported: yaml, toml, json, ini, env, php,
## python. The feature probably works with others formats, but you need
## to test it carefully.
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## Unsupported: XML format, custom config function call, php define(),
## array/list on several lines.
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## More info on TODO
# bind = ":/var/www/__APP__/settings.py"

## NOTE: in pepettes, the python variable is called 'name' and not
## 'project_name', wo here we need to specify the variable name by hand
## before columns
## Here pepettes config file to understand: https://github.com/YunoHost-Apps/pepettes_ynh/blob/5cc2d3ffd6529cc7356ff93af92dbb6785c3ab9a/conf/settings.py##L11

bind = "name:/var/www/__APP__/settings.py"
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## ---------------------------------------------------------------------
## IMPORTANT: other 'bind' mode exists:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part should also explain the foo>bar:filename syntax

##
## The null mode, to explicitly disable default read / write in settings.
# bind = "null"
##
## Without columns before the path it means all the file will be replaced
## by the value (reserved for file and multiline text question):
# bind = "/var/www/__APP__/img/logo.png"
##
## Finally, if you define a custom getter, setter or validator in config
## script it will use it instead of apply default bind behaviour.
## getter: get__PROPERTY()
## setter: set__PROPERTY()
## validator: validate__PROPERTY()
## You can also specify a common getter / setter / validator, with the
## function 'bind' mode, for example here it will try to run
## get__array_settings() first.
# bind = "array_settings()"
## ---------------------------------------------------------------------
zamentur marked this conversation as resolved.
Show resolved Hide resolved

## ---------------------------------------------------------------------
## IMPORTANT: during install/upgrade you should save a first value in
## the source of the bind key and in app settings.
## During upgrade you should reset values in template files based on
## value saved in app settings.
## ---------------------------------------------------------------------
zamentur marked this conversation as resolved.
Show resolved Hide resolved

########################################################################
#### OTHER GENERIC PROPERTY FOR QUESTIONS
########################################################################

## (optional) An help text for the question
help = "Fill the name of the project which will received donation"

## (optional) An example display as placeholder in web form
# example = "YunoHost"

## (optional) set to true in order to redact the value in operation logs
# redact = false

## (optional) A validation pattern
## ---------------------------------------------------------------------
## IMPORTANT: your pattern should be between simple quote, not double.
## ---------------------------------------------------------------------
pattern.regexp = '^\w{3,30}$'
pattern.error = "The name should be at least 3 chars and less than 30 chars. Alphanumeric chars are accepted"

## Note: visible and optional properties are also available for questions


[main.customization.contact_url]
ask = "Contact url"
type = "url"
example = "mailto: [email protected]"
help = "mailto: accepted"
pattern.regexp = "^mailto:[^@]+@[^@]+|https://$"
zamentur marked this conversation as resolved.
Show resolved Hide resolved
pattern.error = "Should be https or mailto:"
bind = ":/var/www/__APP__/settings.py"

[main.customization.logo]
ask = "Logo"
type = "file"
accept = ".png"
help = "Fill with an already resized logo"
source="__FINALPATH__/img/logo.png"
zamentur marked this conversation as resolved.
Show resolved Hide resolved

[main.customization.favicon]
ask = "Favicon"
type = "file"
accept = ".png"
help = "Fill with an already sized favicon"
source="__FINALPATH__/img/favicon.png"
zamentur marked this conversation as resolved.
Show resolved Hide resolved


[main.stripe]
name = "Stripe general info"
optional = false

# The next alert is overwrited with a getter from the config script
[main.stripe.amount]
ask = "Donation in the month : XX €
type = "alert"
style = "success"

[main.stripe.publishable_key]
ask = "Publishable key"
type = "string"
redact = true
help = "Indicate here the stripe publishable key"
bind = ":/var/www/__APP__/settings.py"

[main.stripe.secret_key]
ask = "Secret key"
type = "string"
redact = true
help = "Indicate here the stripe secret key"
bind = ":/var/www/__APP__/settings.py"

[main.stripe.prices]
ask = "Prices ID"
type = "tags"
help = """\
Indicates here the prices ID of donation products you created in stripe interfaces. \
Go on [Stripe products](https://dashboard.stripe.com/products) to create those donation products. \
Fill it tag with 'FREQUENCY/CURRENCY/PRICE_ID' \
FREQUENCY: 'one_time' or 'recuring' \
CURRENCY: 'EUR' or 'USD' \
PRICE_ID: ID from stripe interfaces starting with 'price_' \
"""
pattern.regexp = '^(one_time|recuring)/(EUR|USD)/price_.*$'
pattern.error = "Please respect the format describe in help text for each price ID"
102 changes: 102 additions & 0 deletions scripts/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/bin/bash
# In simple cases, you don't need a config script.

# With a simple config_panel.toml, you can write in the app settings, in the
# upstream config file or replace complete files (logo ...) and restart services.

# The config scripts allows you to go further, to handle specific cases
# (validation of several interdependent fields, specific getter/setter for a value,
# display dynamic informations or choices, pre-loading of config type .cube... ).

#=================================================
# GENERIC STARTING
#=================================================
# IMPORT GENERIC HELPERS
#=================================================

source /usr/share/yunohost/helpers

ynh_abort_if_errors

#=================================================
# RETRIEVE ARGUMENTS
#=================================================

final_path=$(ynh_app_setting_get $app final_path)

#=================================================
# SPECIFIC GETTERS FOR TOML SHORT KEY
#=================================================

get__amount() {
# Here we can imagine to have an API call to stripe to know the amount of donation during a month
local amount = 200

# It's possible to change some properties of the question by overriding it:
if [ $amount -gt 100 ]
then
cat << EOF
style: success
value: $amount
ask:
en: A lot of donation this month: **$amount €**
EOF
else
cat << EOF
style: danger
value: $amount
ask:
en: Not so much donation this month: $amount €
EOF
fi
}

get__prices() {
local prices = "$(grep "DONATION\['" "$final_path/settings.py" | sed -r "s@^DONATION\['([^']*)'\]\['([^']*)'\] = '([^']*)'@\1/\2/\3@g" | sed -z 's/\n/,/g;s/,$/\n/')"
if [ "$prices" == "," ];
then
# Return YNH_NULL if you prefer to not return a value at all.
echo YNH_NULL
else
echo $prices
fi
}


#=================================================
# SPECIFIC VALIDATORS FOR TOML SHORT KEYS
#=================================================
validate__publishable_key() {

# We can imagine here we test if the key is really a publisheable key
(is_secret_key $publishable_key) &&
echo 'This key seems to be a secret key'
}

#=================================================
# SPECIFIC SETTERS FOR TOML SHORT KEYS
#=================================================
set__prices() {

#---------------------------------------------
# IMPORTANT: setter are trigger only if a change is detected
#---------------------------------------------
for price in $(echo $prices | sed "s/,/ /"); do
frequency=$(echo $price | cut -d/ -f1)
currency=$(echo $price | cut -d/ -f2)
price_id=$(echo $price | cut -d/ -f3)
sed "d/DONATION\['$frequency'\]\['$currency'\]" "$final_path/settings.py"

echo "DONATION['$frequency']['$currency'] = '$price_id'" >> "$final_path/settings.py"
done

#---------------------------------------------
# IMPORTANT: to be able to upgrade properly, you have to saved the value in settings too
#---------------------------------------------
ynh_app_setting_set $app prices $prices
}

#=================================================
# GENERIC FINALIZATION
#=================================================
ynh_app_config_run $1