From a26a024092de78c7711f67f5bacf3297f1253c63 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 11 May 2020 19:13:10 +0200 Subject: [PATCH 1/8] [wip] Allow file upload from config-panel --- src/yunohost/app.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 2ca931a90..32bb1ece3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -33,6 +33,7 @@ import re import subprocess import glob import urllib.parse +import base64 import tempfile from collections import OrderedDict @@ -1867,6 +1868,7 @@ def app_config_apply(operation_logger, app, args): } args = dict(urllib.parse.parse_qsl(args, keep_blank_values=True)) if args else {} + upload_dir = None for tab in config_panel.get("panel", []): tab_id = tab["id"] # this makes things easier to debug on crash for section in tab.get("sections", []): @@ -1878,6 +1880,23 @@ def app_config_apply(operation_logger, app, args): ).upper() if generated_name in args: + # Upload files from API + # A file arg contains a string with "FILENAME:BASE64_CONTENT" + if option["type"] == "file" and msettings.get('interface') == 'api': + if upload_dir is None: + upload_dir = tempfile.mkdtemp(prefix='tmp_configpanel_') + filename, args[generated_name] = args[generated_name].split(':') + logger.debug("Save uploaded file %s from API into %s", filename, upload_dir) + file_path = os.join(upload_dir, filename) + try: + with open(file_path, 'wb') as f: + f.write(args[generated_name]) + except IOError as e: + raise YunohostError("cannot_write_file", file=file_path, error=str(e)) + except Exception as e: + raise YunohostError("error_writing_file", file=file_path, error=str(e)) + args[generated_name] = file_path + logger.debug( "include into env %s=%s", generated_name, args[generated_name] ) @@ -1899,6 +1918,11 @@ def app_config_apply(operation_logger, app, args): env=env, )[0] + # Delete files uploaded from API + if msettings.get('interface') == 'api': + if upload_dir is not None: + shutil.rmtree(upload_dir) + if return_code != 0: msg = ( "'script/config apply' return value code: %s (considered as an error)" From 4939bbeb2e4b7a0362ee55d955fa33271bcf8c50 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 8 Jun 2020 19:18:22 +0200 Subject: [PATCH 2/8] [fix] Several files with same name --- src/yunohost/app.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 32bb1ece3..ae6accab0 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1882,15 +1882,21 @@ def app_config_apply(operation_logger, app, args): if generated_name in args: # Upload files from API # A file arg contains a string with "FILENAME:BASE64_CONTENT" - if option["type"] == "file" and msettings.get('interface') == 'api': + if 'type' in option and option["type"] == "file" \ + and msettings.get('interface') == 'api': if upload_dir is None: upload_dir = tempfile.mkdtemp(prefix='tmp_configpanel_') - filename, args[generated_name] = args[generated_name].split(':') + filename = args[generated_name + '[name]'] + content = args[generated_name] logger.debug("Save uploaded file %s from API into %s", filename, upload_dir) - file_path = os.join(upload_dir, filename) + file_path = os.path.join(upload_dir, filename) + i = 2 + while os.path.exists(file_path): + file_path = os.path.join(upload_dir, filename + (".%d" % i)) + i += 1 try: with open(file_path, 'wb') as f: - f.write(args[generated_name]) + f.write(content.decode("base64")) except IOError as e: raise YunohostError("cannot_write_file", file=file_path, error=str(e)) except Exception as e: @@ -1907,7 +1913,7 @@ def app_config_apply(operation_logger, app, args): # for debug purpose for key in args: if key not in env: - logger.warning( + logger.debug( "Ignore key '%s' from arguments because it is not in the config", key ) From 3bc45b5672f332e7cdbe314c756fcf4aac74e11f Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Wed, 7 Oct 2020 00:31:20 +0200 Subject: [PATCH 3/8] [enh] Replace os.path.join to improve security --- src/yunohost/app.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ae6accab0..f017521d2 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1889,10 +1889,14 @@ def app_config_apply(operation_logger, app, args): filename = args[generated_name + '[name]'] content = args[generated_name] logger.debug("Save uploaded file %s from API into %s", filename, upload_dir) - file_path = os.path.join(upload_dir, filename) + + # Filename is given by user of the API. For security reason, we have replaced + # os.path.join to avoid the user to be able to rewrite a file in filesystem + # i.e. os.path.join("/foo", "/etc/passwd") == "/etc/passwd" + file_path = os.path.normpath(upload_dir + "/" + filename) i = 2 while os.path.exists(file_path): - file_path = os.path.join(upload_dir, filename + (".%d" % i)) + file_path = os.path.normpath(upload_dir + "/" + filename + (".%d" % i)) i += 1 try: with open(file_path, 'wb') as f: From a5508b1db45d2f5ae94578f44b6026fd5b45d017 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 31 May 2021 16:32:19 +0200 Subject: [PATCH 4/8] [fix] Base64 python3 change --- src/yunohost/app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f017521d2..49033d8b4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1845,7 +1845,7 @@ def app_config_apply(operation_logger, app, args): logger.warning(m18n.n("experimental_feature")) from yunohost.hook import hook_exec - + from base64 import b64decode installed = _is_installed(app) if not installed: raise YunohostValidationError( @@ -1889,8 +1889,8 @@ def app_config_apply(operation_logger, app, args): filename = args[generated_name + '[name]'] content = args[generated_name] logger.debug("Save uploaded file %s from API into %s", filename, upload_dir) - - # Filename is given by user of the API. For security reason, we have replaced + + # Filename is given by user of the API. For security reason, we have replaced # os.path.join to avoid the user to be able to rewrite a file in filesystem # i.e. os.path.join("/foo", "/etc/passwd") == "/etc/passwd" file_path = os.path.normpath(upload_dir + "/" + filename) @@ -1900,7 +1900,7 @@ def app_config_apply(operation_logger, app, args): i += 1 try: with open(file_path, 'wb') as f: - f.write(content.decode("base64")) + f.write(b64decode(content)) except IOError as e: raise YunohostError("cannot_write_file", file=file_path, error=str(e)) except Exception as e: From 27ba82bd307ae28268f7e56c1b3a6a40060c62e8 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 1 Jun 2021 00:41:37 +0200 Subject: [PATCH 5/8] [enh] Add configpanel helpers --- data/helpers.d/configpanel | 259 +++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 data/helpers.d/configpanel diff --git a/data/helpers.d/configpanel b/data/helpers.d/configpanel new file mode 100644 index 000000000..f648826e4 --- /dev/null +++ b/data/helpers.d/configpanel @@ -0,0 +1,259 @@ +#!/bin/bash + +ynh_lowerdot_to_uppersnake() { + local lowerdot + lowerdot=$(echo "$1" | cut -d= -f1 | sed "s/\./_/g") + echo "${lowerdot^^}" +} + +# Get a value from heterogeneous file (yaml, json, php, python...) +# +# usage: ynh_value_get --file=PATH --key=KEY +# | arg: -f, --file= - the path to the file +# | arg: -k, --key= - the key to get +# +# This helpers match several var affectation use case in several languages +# We don't use jq or equivalent to keep comments and blank space in files +# This helpers work line by line, it is not able to work correctly +# if you have several identical keys in your files +# +# Example of line this helpers can managed correctly +# .yml +# title: YunoHost documentation +# email: 'yunohost@yunohost.org' +# .json +# "theme": "colib'ris", +# "port": 8102 +# "some_boolean": false, +# "user": null +# .ini +# some_boolean = On +# action = "Clear" +# port = 20 +# .php +# $user= +# user => 20 +# .py +# USER = 8102 +# user = 'https://donate.local' +# CUSTOM['user'] = 'YunoHost' +# Requires YunoHost version 4.3 or higher. +ynh_value_get() { + # Declare an array to define the options of this helper. + local legacy_args=fk + local -A args_array=( [f]=file= [k]=key= ) + local file + local key + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + local var_part="[ \t]*(\$?\w*\[)?[ \t]*[\"']?${key}[\"']?[ \t]*\]?[ \t]*[:=]>?[ \t]*" + + local crazy_value="$(grep -i -o -P "^${var_part}\K.*(?=[ \t,\n;]*\$)" ${file} | head -n1)" + + local first_char="${crazy_value:0:1}" + if [[ "$first_char" == '"' ]] ; then + echo "$crazy_value" | grep -m1 -o -P '"\K([^"](\\")?)*[^\\](?=")' | head -n1 | sed 's/\\"/"/g' + elif [[ "$first_char" == "'" ]] ; then + echo "$crazy_value" | grep -m1 -o -P "'\K([^'](\\\\')?)*[^\\\\](?=')" | head -n1 | sed "s/\\\\'/'/g" + else + echo "$crazy_value" + fi +} + +# Set a value into heterogeneous file (yaml, json, php, python...) +# +# usage: ynh_value_set --file=PATH --key=KEY --value=VALUE +# | arg: -f, --file= - the path to the file +# | arg: -k, --key= - the key to set +# | arg: -v, --value= - the value to set +# +# Requires YunoHost version 4.3 or higher. +ynh_value_set() { + # Declare an array to define the options of this helper. + local legacy_args=fkv + local -A args_array=( [f]=file= [k]=key= [v]=value=) + local file + local key + local value + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + + local var_part="[ \t]*(\$?\w*\[)?[ \t]*[\"']?${key}[\"']?[ \t]*\]?[ \t]*[:=]>?[ \t]*" + + local crazy_value="$(grep -i -o -P "^${var_part}\K.*(?=[ \t,\n;]*\$)" ${file} | head -n1)" + local var_part="^[ \t]*(\$?\w*\[)?[ \t]*[\"']?${key}[\"']?[ \t]*\]?[ \t]*[:=]>?[ \t]*" + local first_char="${crazy_value:0:1}" + if [[ "$first_char" == '"' ]] ; then + value="$(echo "$value" | sed 's/"/\\"/g')" + sed -ri "s%^(${var_part}\")[^\"]*(\"[ \t\n,;]*)\$%\1${value}\2%i" ${file} + elif [[ "$first_char" == "'" ]] ; then + value="$(echo "$value" | sed "s/'/\\\\'/g")" + sed -ri "s%^(${var_part}')[^']*('[ \t\n,;]*)\$%\1${value}\2%i" ${file} + else + if [[ "$value" == *"'"* ]] || [[ "$value" == *'"'* ]] ; then + value="\"$(echo "$value" | sed 's/"/\\"/g')\"" + fi + sed -ri "s%^(${var_part}')[^']*('[ \t\n,;]*)\$%\1${value}\2%i" ${file} + fi +} + +_ynh_panel_get() { + + # From settings + local params_sources + params_sources=`python3 << EOL +import toml +from collections import OrderedDict +with open("/etc/yunohost/apps/vpnclient/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 isinstance(panel, dict): + for section_name, section in panel.items(): + if isinstance(section, dict): + for name, param in section.items(): + if isinstance(param, dict) and param.get('source', '') == 'settings': + print("%s.%s.%s=%s" %(panel_name, section_name, name, param.get('source', 'settings'))) +EOL +` + for param_source in params_sources + do + local _dot_setting=$(echo "$param_source" | cut -d= -f1) + local _snake_setting="YNH_CONFIG_$(ynh_lowerdot_to_uppersnake $dot_setting)" + local short_setting=$(echo "$_dot_setting" | cut -d. -f3) + local _getter="get__${short_setting}" + local source="$(echo $param_source | cut -d= -f2)" + + # Get value from getter if exists + if type $getter | grep -q '^function$' 2>/dev/null; then + old[$short_setting]="$($getter)" + + # Get value from app settings + elif [[ "$source" == "settings" ]] ; then + old[$short_setting]="$(ynh_app_setting_get $app $short_setting)" + + # Get value from a kind of key/value file + elif [[ "$source" == *":"* ]] ; then + local source_key="$(echo "$source" | cut -d: -f1)" + source_key=${source_key:-$short_setting} + local source_file="$(echo "$source" | cut -d: -f2)" + old[$short_setting]="$(ynh_value_get --file="${source_file}" --key="${source_key}")" + + # Specific case for files (all content of the file is the source) + else + old[$short_setting]="$source" + fi + + done + + +} + +_ynh_panel_apply() { + for short_setting in "${!dot_settings[@]}" + do + local setter="set__${short_setting}" + local source="$sources[$short_setting]" + + # Apply setter if exists + if type $setter | grep -q '^function$' 2>/dev/null; then + $setter + + # Copy file in right place + elif [[ "$source" == "settings" ]] ; then + ynh_app_setting_set $app $short_setting "$new[$short_setting]" + + # Get value from a kind of key/value file + elif [[ "$source" == *":"* ]] + then + local source_key="$(echo "$source" | cut -d: -f1)" + source_key=${source_key:-$short_setting} + local source_file="$(echo "$source" | cut -d: -f2)" + ynh_value_set --file="${source_file}" --key="${source_key}" --value="$new[$short_setting]" + + # Specific case for files (all content of the file is the source) + else + cp "$new[$short_setting]" "$source" + fi + done +} + +_ynh_panel_show() { + for short_setting in "${!old[@]}" + do + local key="YNH_CONFIG_$(ynh_lowerdot_to_uppersnake $dot_settings[$short_setting])" + ynh_return "$key=${old[$short_setting]}" + done +} + +_ynh_panel_validate() { + # Change detection + local is_error=true + #for changed_status in "${!changed[@]}" + for short_setting in "${!dot_settings[@]}" + do + #TODO file hash + file_hash[$setting]=$(sha256sum "$_source" | cut -d' ' -f1) + file_hash[$form_setting]=$(sha256sum "${!form_setting}" | cut -d' ' -f1) + if [[ "${file_hash[$setting]}" != "${file_hash[$form_setting]}" ]] + then + changed[$setting]=true + fi + if [[ "$new[$short_setting]" == "$old[$short_setting]" ]] + then + changed[$short_setting]=false + else + changed[$short_setting]=true + is_error=false + fi + done + + # Run validation if something is changed + if [[ "$is_error" == "false" ]] + then + + for short_setting in "${!dot_settings[@]}" + do + local result="$(validate__$short_setting)" + local key="YNH_ERROR_$(ynh_lowerdot_to_uppersnake $dot_settings[$short_setting])" + if [ -n "$result" ] + then + ynh_return "$key=$result" + is_error=true + fi + done + fi + + if [[ "$is_error" == "true" ]] + then + ynh_die + fi + +} + +ynh_panel_get() { + _ynh_panel_get +} + +ynh_panel_init() { + declare -A old=() + declare -A changed=() + declare -A file_hash=() + + ynh_panel_get +} + +ynh_panel_show() { + _ynh_panel_show +} + +ynh_panel_validate() { + _ynh_panel_validate +} + +ynh_panel_apply() { + _ynh_panel_apply +} + From 5fec35ccea72b9073ef75b18570c8df0e38aff7b Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 1 Jun 2021 01:29:26 +0200 Subject: [PATCH 6/8] [fix] No validate function in config panel --- data/helpers.d/configpanel | 41 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/data/helpers.d/configpanel b/data/helpers.d/configpanel index f648826e4..83130cfe6 100644 --- a/data/helpers.d/configpanel +++ b/data/helpers.d/configpanel @@ -123,7 +123,7 @@ EOL local _dot_setting=$(echo "$param_source" | cut -d= -f1) local _snake_setting="YNH_CONFIG_$(ynh_lowerdot_to_uppersnake $dot_setting)" local short_setting=$(echo "$_dot_setting" | cut -d. -f3) - local _getter="get__${short_setting}" + local getter="get__${short_setting}" local source="$(echo $param_source | cut -d= -f2)" # Get value from getter if exists @@ -195,12 +195,12 @@ _ynh_panel_validate() { for short_setting in "${!dot_settings[@]}" do #TODO file hash - file_hash[$setting]=$(sha256sum "$_source" | cut -d' ' -f1) - file_hash[$form_setting]=$(sha256sum "${!form_setting}" | cut -d' ' -f1) - if [[ "${file_hash[$setting]}" != "${file_hash[$form_setting]}" ]] - then - changed[$setting]=true - fi + file_hash[$setting]=$(sha256sum "$_source" | cut -d' ' -f1) + file_hash[$form_setting]=$(sha256sum "${!form_setting}" | cut -d' ' -f1) + if [[ "${file_hash[$setting]}" != "${file_hash[$form_setting]}" ]] + then + changed[$setting]=true + fi if [[ "$new[$short_setting]" == "$old[$short_setting]" ]] then changed[$short_setting]=false @@ -216,10 +216,13 @@ _ynh_panel_validate() { for short_setting in "${!dot_settings[@]}" do - local result="$(validate__$short_setting)" - local key="YNH_ERROR_$(ynh_lowerdot_to_uppersnake $dot_settings[$short_setting])" + local result="" + if type validate__$short_setting | grep -q '^function$' 2>/dev/null; then + result="$(validate__$short_setting)" + fi if [ -n "$result" ] then + local key="YNH_ERROR_$(ynh_lowerdot_to_uppersnake $dot_settings[$short_setting])" ynh_return "$key=$result" is_error=true fi @@ -237,14 +240,6 @@ ynh_panel_get() { _ynh_panel_get } -ynh_panel_init() { - declare -A old=() - declare -A changed=() - declare -A file_hash=() - - ynh_panel_get -} - ynh_panel_show() { _ynh_panel_show } @@ -257,3 +252,15 @@ ynh_panel_apply() { _ynh_panel_apply } +ynh_panel_run() { + declare -A old=() + declare -A changed=() + declare -A file_hash=() + + ynh_panel_get + case $1 in + show) ynh_panel_show;; + apply) ynh_panel_validate && ynh_panel_apply;; + esac +} + From 619b26f73c2356b5d256909c84142c64a8b06020 Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 13 Aug 2021 13:38:06 +0200 Subject: [PATCH 7/8] [fix] tons of things --- data/helpers.d/configpanel | 110 ++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/data/helpers.d/configpanel b/data/helpers.d/configpanel index 83130cfe6..5ab199aea 100644 --- a/data/helpers.d/configpanel +++ b/data/helpers.d/configpanel @@ -99,13 +99,13 @@ ynh_value_set() { } _ynh_panel_get() { - + set +x # From settings local params_sources params_sources=`python3 << EOL import toml from collections import OrderedDict -with open("/etc/yunohost/apps/vpnclient/config_panel.toml", "r") as f: +with open("../config_panel.toml", "r") as f: file_content = f.read() loaded_toml = toml.loads(file_content, _dict=OrderedDict) @@ -114,20 +114,23 @@ for panel_name,panel in loaded_toml.items(): for section_name, section in panel.items(): if isinstance(section, dict): for name, param in section.items(): - if isinstance(param, dict) and param.get('source', '') == 'settings': + if isinstance(param, dict) and param.get('type', 'string') not in ['info', 'warning', 'error']: print("%s.%s.%s=%s" %(panel_name, section_name, name, param.get('source', 'settings'))) EOL ` - for param_source in params_sources + for param_source in $params_sources do local _dot_setting=$(echo "$param_source" | cut -d= -f1) - local _snake_setting="YNH_CONFIG_$(ynh_lowerdot_to_uppersnake $dot_setting)" + local _snake_setting="YNH_CONFIG_$(ynh_lowerdot_to_uppersnake $_dot_setting)" local short_setting=$(echo "$_dot_setting" | cut -d. -f3) local getter="get__${short_setting}" local source="$(echo $param_source | cut -d= -f2)" + sources[${short_setting}]="$source" + file_hash[${short_setting}]="" + dot_settings[${short_setting}]="${_dot_setting}" # Get value from getter if exists - if type $getter | grep -q '^function$' 2>/dev/null; then + if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null; then old[$short_setting]="$($getter)" # Get value from app settings @@ -144,38 +147,43 @@ EOL # Specific case for files (all content of the file is the source) else old[$short_setting]="$source" + file_hash[$short_setting]="true" fi - + set +u + new[$short_setting]="${!_snake_setting}" + set -u done + set -x } _ynh_panel_apply() { - for short_setting in "${!dot_settings[@]}" + for short_setting in "${!old[@]}" do local setter="set__${short_setting}" - local source="$sources[$short_setting]" - - # Apply setter if exists - if type $setter | grep -q '^function$' 2>/dev/null; then - $setter + local source="${sources[$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 - # Copy file in right place - elif [[ "$source" == "settings" ]] ; then - ynh_app_setting_set $app $short_setting "$new[$short_setting]" - - # Get value from a kind of key/value file - elif [[ "$source" == *":"* ]] - then - local source_key="$(echo "$source" | cut -d: -f1)" - source_key=${source_key:-$short_setting} - local source_file="$(echo "$source" | cut -d: -f2)" - ynh_value_set --file="${source_file}" --key="${source_key}" --value="$new[$short_setting]" + # Copy file in right place + elif [[ "$source" == "settings" ]] ; then + ynh_app_setting_set $app $short_setting "${new[$short_setting]}" + + # Get value from a kind of key/value file + elif [[ "$source" == *":"* ]] + then + local source_key="$(echo "$source" | cut -d: -f1)" + source_key=${source_key:-$short_setting} + local source_file="$(echo "$source" | cut -d: -f2)" + ynh_value_set --file="${source_file}" --key="${source_key}" --value="${new[$short_setting]}" - # Specific case for files (all content of the file is the source) - else - cp "$new[$short_setting]" "$source" + # Specific case for files (all content of the file is the source) + else + cp "${new[$short_setting]}" "$source" + fi fi done } @@ -189,24 +197,32 @@ _ynh_panel_show() { } _ynh_panel_validate() { + set +x # Change detection local is_error=true #for changed_status in "${!changed[@]}" - for short_setting in "${!dot_settings[@]}" + for short_setting in "${!old[@]}" do - #TODO file hash - file_hash[$setting]=$(sha256sum "$_source" | cut -d' ' -f1) - file_hash[$form_setting]=$(sha256sum "${!form_setting}" | cut -d' ' -f1) - if [[ "${file_hash[$setting]}" != "${file_hash[$form_setting]}" ]] - then - changed[$setting]=true - fi - if [[ "$new[$short_setting]" == "$old[$short_setting]" ]] - then - changed[$short_setting]=false + changed[$short_setting]=false + 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) + fi + if [ -f "${new[$short_setting]}" ] ; then + file_hash[new__$short_setting]=$(sha256sum "${new[$short_setting]}" | cut -d' ' -f1) + if [[ "${file_hash[old__$short_setting]}" != "${file_hash[new__$short_setting]}" ]] + then + changed[$short_setting]=true + fi + fi else - changed[$short_setting]=true - is_error=false + if [[ "${new[$short_setting]}" != "${old[$short_setting]}" ]] + then + changed[$short_setting]=true + is_error=false + fi fi done @@ -214,10 +230,10 @@ _ynh_panel_validate() { if [[ "$is_error" == "false" ]] then - for short_setting in "${!dot_settings[@]}" + for short_setting in "${!old[@]}" do local result="" - if type validate__$short_setting | grep -q '^function$' 2>/dev/null; then + if type -t validate__$short_setting | grep -q '^function$' 2>/dev/null; then result="$(validate__$short_setting)" fi if [ -n "$result" ] @@ -233,6 +249,7 @@ _ynh_panel_validate() { then ynh_die fi + set -x } @@ -253,9 +270,12 @@ ynh_panel_apply() { } ynh_panel_run() { - declare -A old=() - declare -A changed=() - declare -A file_hash=() + declare -Ag old=() + declare -Ag new=() + declare -Ag changed=() + declare -Ag file_hash=() + declare -Ag sources=() + declare -Ag dot_settings=() ynh_panel_get case $1 in From 596d05ae81d24712c87ec0de72f8deb8248bca9a Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 14 Aug 2021 14:38:45 +0200 Subject: [PATCH 8/8] [fix] Missing name or bad format management --- src/yunohost/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 49033d8b4..cf218823f 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2145,7 +2145,7 @@ def _get_app_config_panel(app_id): for key, value in panels: panel = { "id": key, - "name": value["name"], + "name": value.get("name", ""), "sections": [], } @@ -2158,7 +2158,7 @@ def _get_app_config_panel(app_id): for section_key, section_value in sections: section = { "id": section_key, - "name": section_value["name"], + "name": section_value.get("name", ""), "options": [], }