mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Following the specification of yaml (https://yaml.org/spec/1.2.2/#single-quoted-style), when we use single quote we only need to escapte signle quote. By this we avoid to need to excape mulitple other chars wich could be complicated.
354 lines
14 KiB
Bash
354 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
_ynh_app_config_get_one() {
|
|
local short_setting="$1"
|
|
local type="$2"
|
|
local bind="$3"
|
|
local getter="get__${short_setting}"
|
|
# Get value from getter if exists
|
|
if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
old[$short_setting]="$($getter)"
|
|
formats[${short_setting}]="yaml"
|
|
|
|
elif [[ "$bind" == *"("* ]] && type -t "get__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
old[$short_setting]="$("get__${bind%%(*}" $short_setting $type $bind)"
|
|
formats[${short_setting}]="yaml"
|
|
|
|
elif [[ "$bind" == "null" ]]; then
|
|
old[$short_setting]="YNH_NULL"
|
|
|
|
# Get value from app settings or from another file
|
|
elif [[ "$type" == "file" ]]; then
|
|
if [[ "$bind" == "settings" ]]; then
|
|
ynh_die --message="File '${short_setting}' can't be stored in settings"
|
|
fi
|
|
old[$short_setting]="$(ls "$(echo $bind | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)" 2>/dev/null || echo YNH_NULL)"
|
|
file_hash[$short_setting]="true"
|
|
|
|
# Get multiline text from settings or from a full file
|
|
elif [[ "$type" == "text" ]]; then
|
|
if [[ "$bind" == "settings" ]]; then
|
|
old[$short_setting]="$(ynh_app_setting_get $app $short_setting)"
|
|
elif [[ "$bind" == *":"* ]]; then
|
|
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
|
|
else
|
|
old[$short_setting]="$(cat $(echo $bind | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2>/dev/null || echo YNH_NULL)"
|
|
fi
|
|
|
|
# Get value from a kind of key/value file
|
|
else
|
|
local bind_after=""
|
|
if [[ "$bind" == "settings" ]]; then
|
|
bind=":/etc/yunohost/apps/$app/settings.yml"
|
|
fi
|
|
local bind_key_="$(echo "$bind" | cut -d: -f1)"
|
|
bind_key_=${bind_key_:-$short_setting}
|
|
if [[ "$bind_key_" == *">"* ]]; then
|
|
bind_after="$(echo "${bind_key_}" | cut -d'>' -f1)"
|
|
bind_key_="$(echo "${bind_key_}" | cut -d'>' -f2)"
|
|
fi
|
|
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
|
old[$short_setting]="$(ynh_read_var_in_file --file="${bind_file}" --key="${bind_key_}" --after="${bind_after}")"
|
|
|
|
fi
|
|
}
|
|
_ynh_app_config_apply_one() {
|
|
local short_setting="$1"
|
|
local setter="set__${short_setting}"
|
|
local bind="${binds[$short_setting]}"
|
|
local type="${types[$short_setting]}"
|
|
if [ "${changed[$short_setting]}" == "true" ]; then
|
|
# Apply setter if exists
|
|
if type -t $setter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
$setter
|
|
|
|
elif [[ "$bind" == *"("* ]] && type -t "set__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
"set__${bind%%(*}" $short_setting $type $bind
|
|
|
|
elif [[ "$bind" == "null" ]]; then
|
|
return
|
|
|
|
# Save in a file
|
|
elif [[ "$type" == "file" ]]; then
|
|
if [[ "$bind" == "settings" ]]; then
|
|
ynh_die --message="File '${short_setting}' can't be stored in settings"
|
|
fi
|
|
local bind_file="$(echo "$bind" | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
|
if [[ "${!short_setting}" == "" ]]; then
|
|
ynh_backup_if_checksum_is_different --file="$bind_file"
|
|
ynh_secure_remove --file="$bind_file"
|
|
ynh_delete_file_checksum --file="$bind_file"
|
|
ynh_print_info --message="File '$bind_file' removed"
|
|
else
|
|
ynh_backup_if_checksum_is_different --file="$bind_file"
|
|
if [[ "${!short_setting}" != "$bind_file" ]]; then
|
|
cp "${!short_setting}" "$bind_file"
|
|
fi
|
|
ynh_store_file_checksum --file="$bind_file" --update_only
|
|
ynh_print_info --message="File '$bind_file' overwritten with ${!short_setting}"
|
|
fi
|
|
|
|
# Save value in app settings
|
|
elif [[ "$bind" == "settings" ]]; then
|
|
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
|
|
ynh_print_info --message="Configuration key '$short_setting' edited in app settings"
|
|
|
|
# Save multiline text in a file
|
|
elif [[ "$type" == "text" ]]; then
|
|
if [[ "$bind" == *":"* ]]; then
|
|
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
|
|
fi
|
|
local bind_file="$(echo "$bind" | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
|
ynh_backup_if_checksum_is_different --file="$bind_file"
|
|
echo "${!short_setting}" >"$bind_file"
|
|
ynh_store_file_checksum --file="$bind_file" --update_only
|
|
ynh_print_info --message="File '$bind_file' overwritten with the content provided in question '${short_setting}'"
|
|
|
|
# Set value into a kind of key/value file
|
|
else
|
|
local bind_after=""
|
|
local bind_key_="$(echo "$bind" | cut -d: -f1)"
|
|
if [[ "$bind_key_" == *">"* ]]; then
|
|
bind_after="$(echo "${bind_key_}" | cut -d'>' -f1)"
|
|
bind_key_="$(echo "${bind_key_}" | cut -d'>' -f2)"
|
|
fi
|
|
bind_key_=${bind_key_:-$short_setting}
|
|
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__INSTALL_DIR__@$install_dir@ | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
|
|
|
ynh_backup_if_checksum_is_different --file="$bind_file"
|
|
ynh_write_var_in_file --file="${bind_file}" --key="${bind_key_}" --value="${!short_setting}" --after="${bind_after}"
|
|
ynh_store_file_checksum --file="$bind_file" --update_only
|
|
|
|
# We stored the info in settings in order to be able to upgrade the app
|
|
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
|
|
ynh_print_info --message="Configuration key '$bind_key_' edited into $bind_file"
|
|
|
|
fi
|
|
fi
|
|
}
|
|
_ynh_app_config_get() {
|
|
# From settings
|
|
local lines
|
|
lines=$(
|
|
python3 <<EOL
|
|
import toml
|
|
from collections import OrderedDict
|
|
with open("../config_panel.toml", "r") as f:
|
|
file_content = f.read()
|
|
loaded_toml = toml.loads(file_content, _dict=OrderedDict)
|
|
|
|
for panel_name, panel in loaded_toml.items():
|
|
if not isinstance(panel, dict): continue
|
|
bind_panel = panel.get('bind')
|
|
for section_name, section in panel.items():
|
|
if not isinstance(section, dict): continue
|
|
bind_section = section.get('bind')
|
|
if not bind_section:
|
|
bind_section = bind_panel
|
|
elif bind_section[-1] == ":" and bind_panel and ":" in bind_panel:
|
|
regex, bind_panel_file = bind_panel.split(":")
|
|
if ">" in bind_section:
|
|
bind_section = bind_section + bind_panel_file
|
|
else:
|
|
bind_section = regex + bind_section + bind_panel_file
|
|
|
|
for name, param in section.items():
|
|
if not isinstance(param, dict):
|
|
continue
|
|
|
|
bind = param.get('bind')
|
|
|
|
if not bind:
|
|
if bind_section:
|
|
bind = bind_section
|
|
else:
|
|
bind = 'settings'
|
|
elif bind[-1] == ":" and bind_section and ":" in bind_section:
|
|
regex, bind_file = bind_section.split(":")
|
|
if ">" in bind:
|
|
bind = bind + bind_file
|
|
else:
|
|
bind = regex + bind + bind_file
|
|
if bind == "settings" and param.get('type', 'string') == 'file':
|
|
bind = 'null'
|
|
|
|
print('|'.join([
|
|
name,
|
|
param.get('type', 'string'),
|
|
bind
|
|
]))
|
|
EOL
|
|
)
|
|
for line in $lines; do
|
|
# Split line into short_setting, type and bind
|
|
IFS='|' read short_setting type bind <<<"$line"
|
|
binds[${short_setting}]="$bind"
|
|
types[${short_setting}]="$type"
|
|
file_hash[${short_setting}]=""
|
|
formats[${short_setting}]=""
|
|
ynh_app_config_get_one $short_setting $type $bind
|
|
done
|
|
|
|
}
|
|
|
|
_ynh_app_config_apply() {
|
|
for short_setting in "${!old[@]}"; do
|
|
ynh_app_config_apply_one $short_setting
|
|
done
|
|
}
|
|
|
|
_ynh_app_config_show() {
|
|
for short_setting in "${!old[@]}"; do
|
|
if [[ "${old[$short_setting]}" != YNH_NULL ]]; then
|
|
if [[ "${formats[$short_setting]}" == "yaml" ]]; then
|
|
ynh_return "${short_setting}:"
|
|
ynh_return "$(echo "${old[$short_setting]}" | sed 's/^/ /g')"
|
|
else
|
|
ynh_return "${short_setting}: '$(echo "${old[$short_setting]}" | sed "s/'/''/g" | sed ':a;N;$!ba;s/\n/\n\n/g')'"
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
_ynh_app_config_validate() {
|
|
# Change detection
|
|
ynh_script_progression --message="Checking what changed in the new configuration..." --weight=1
|
|
local nothing_changed=true
|
|
local changes_validated=true
|
|
for short_setting in "${!old[@]}"; do
|
|
changed[$short_setting]=false
|
|
if [ -z ${!short_setting+x} ]; then
|
|
# Assign the var with the old value in order to allows multiple
|
|
# args validation
|
|
declare -g "$short_setting"="${old[$short_setting]}"
|
|
continue
|
|
fi
|
|
if [ ! -z "${file_hash[${short_setting}]}" ]; then
|
|
file_hash[old__$short_setting]=""
|
|
file_hash[new__$short_setting]=""
|
|
if [ -f "${old[$short_setting]}" ]; then
|
|
file_hash[old__$short_setting]=$(sha256sum "${old[$short_setting]}" | cut -d' ' -f1)
|
|
if [ -z "${!short_setting}" ]; then
|
|
changed[$short_setting]=true
|
|
nothing_changed=false
|
|
fi
|
|
fi
|
|
if [ -f "${!short_setting}" ]; then
|
|
file_hash[new__$short_setting]=$(sha256sum "${!short_setting}" | cut -d' ' -f1)
|
|
if [[ "${file_hash[old__$short_setting]}" != "${file_hash[new__$short_setting]}" ]]; then
|
|
changed[$short_setting]=true
|
|
nothing_changed=false
|
|
fi
|
|
fi
|
|
else
|
|
if [[ "${!short_setting}" != "${old[$short_setting]}" ]]; then
|
|
changed[$short_setting]=true
|
|
nothing_changed=false
|
|
fi
|
|
fi
|
|
done
|
|
if [[ "$nothing_changed" == "true" ]]; then
|
|
ynh_print_info --message="Nothing has changed"
|
|
exit 0
|
|
fi
|
|
|
|
# Run validation if something is changed
|
|
ynh_script_progression --message="Validating the new configuration..." --weight=1
|
|
|
|
for short_setting in "${!old[@]}"; do
|
|
[[ "${changed[$short_setting]}" == "false" ]] && continue
|
|
local result=""
|
|
if type -t validate__$short_setting | grep -q '^function$' 2>/dev/null; then
|
|
result="$(validate__$short_setting)"
|
|
elif [[ "$bind" == *"("* ]] && type -t "validate__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
"validate__${bind%%(*}" $short_setting
|
|
fi
|
|
if [ -n "$result" ]; then
|
|
#
|
|
# Return a yaml such as:
|
|
#
|
|
# validation_errors:
|
|
# some_key: "An error message"
|
|
# some_other_key: "Another error message"
|
|
#
|
|
# We use changes_validated to know if this is
|
|
# the first validation error
|
|
if [[ "$changes_validated" == true ]]; then
|
|
ynh_return "validation_errors:"
|
|
fi
|
|
ynh_return " ${short_setting}: \"$result\""
|
|
changes_validated=false
|
|
fi
|
|
done
|
|
|
|
# If validation failed, exit the script right now (instead of going into apply)
|
|
# Yunohost core will pick up the errors returned via ynh_return previously
|
|
if [[ "$changes_validated" == "false" ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
}
|
|
|
|
ynh_app_config_get_one() {
|
|
_ynh_app_config_get_one $1 $2 $3
|
|
}
|
|
|
|
ynh_app_config_get() {
|
|
_ynh_app_config_get
|
|
}
|
|
|
|
ynh_app_config_show() {
|
|
_ynh_app_config_show
|
|
}
|
|
|
|
ynh_app_config_validate() {
|
|
_ynh_app_config_validate
|
|
}
|
|
|
|
ynh_app_config_apply_one() {
|
|
_ynh_app_config_apply_one $1
|
|
}
|
|
ynh_app_config_apply() {
|
|
_ynh_app_config_apply
|
|
}
|
|
|
|
ynh_app_action_run() {
|
|
local runner="run__$1"
|
|
# Get value from getter if exists
|
|
if type -t "$runner" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
|
|
$runner
|
|
#ynh_return "result:"
|
|
#ynh_return "$(echo "${result}" | sed 's/^/ /g')"
|
|
else
|
|
ynh_die "No handler defined in app's script for action $1. If you are the maintainer of this app, you should define '$runner'"
|
|
fi
|
|
}
|
|
|
|
ynh_app_config_run() {
|
|
declare -Ag old=()
|
|
declare -Ag changed=()
|
|
declare -Ag file_hash=()
|
|
declare -Ag binds=()
|
|
declare -Ag types=()
|
|
declare -Ag formats=()
|
|
|
|
case $1 in
|
|
show)
|
|
ynh_app_config_get
|
|
ynh_app_config_show
|
|
;;
|
|
apply)
|
|
max_progression=4
|
|
ynh_script_progression --message="Reading config panel description and current configuration..."
|
|
ynh_app_config_get
|
|
|
|
ynh_app_config_validate
|
|
|
|
ynh_script_progression --message="Applying the new configuration..."
|
|
ynh_app_config_apply
|
|
ynh_script_progression --message="Configuration of $app completed" --last
|
|
;;
|
|
*)
|
|
ynh_app_action_run $1
|
|
esac
|
|
}
|