mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge branch 'enh-config-panel-file' of github.com:YunoHost/yunohost into enh-config-panel-file
This commit is contained in:
commit
9f9dfdbea7
7 changed files with 874 additions and 180 deletions
|
@ -67,7 +67,7 @@ EOL
|
||||||
local source_key="$(echo "$source" | cut -d: -f1)"
|
local source_key="$(echo "$source" | cut -d: -f1)"
|
||||||
source_key=${source_key:-$short_setting}
|
source_key=${source_key:-$short_setting}
|
||||||
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
old[$short_setting]="$(ynh_get_var --file="${source_file}" --key="${source_key}")"
|
old[$short_setting]="$(ynh_read_var_in_file --file="${source_file}" --key="${source_key}")"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -130,7 +130,7 @@ _ynh_app_config_apply() {
|
||||||
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
local source_file="$(echo "$source" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
|
||||||
|
|
||||||
ynh_backup_if_checksum_is_different --file="$source_file"
|
ynh_backup_if_checksum_is_different --file="$source_file"
|
||||||
ynh_set_var --file="${source_file}" --key="${source_key}" --value="${!short_setting}"
|
ynh_write_var_in_file --file="${source_file}" --key="${source_key}" --value="${!short_setting}"
|
||||||
ynh_store_file_checksum --file="$source_file" --update_only
|
ynh_store_file_checksum --file="$source_file" --update_only
|
||||||
|
|
||||||
# We stored the info in settings in order to be able to upgrade the app
|
# We stored the info in settings in order to be able to upgrade the app
|
||||||
|
|
|
@ -475,7 +475,7 @@ ynh_replace_vars () {
|
||||||
|
|
||||||
# Get a value from heterogeneous file (yaml, json, php, python...)
|
# Get a value from heterogeneous file (yaml, json, php, python...)
|
||||||
#
|
#
|
||||||
# usage: ynh_get_var --file=PATH --key=KEY
|
# usage: ynh_read_var_in_file --file=PATH --key=KEY
|
||||||
# | arg: -f, --file= - the path to the file
|
# | arg: -f, --file= - the path to the file
|
||||||
# | arg: -k, --key= - the key to get
|
# | arg: -k, --key= - the key to get
|
||||||
#
|
#
|
||||||
|
@ -504,8 +504,9 @@ ynh_replace_vars () {
|
||||||
# USER = 8102
|
# USER = 8102
|
||||||
# user = 'https://donate.local'
|
# user = 'https://donate.local'
|
||||||
# CUSTOM['user'] = 'YunoHost'
|
# CUSTOM['user'] = 'YunoHost'
|
||||||
|
#
|
||||||
# Requires YunoHost version 4.3 or higher.
|
# Requires YunoHost version 4.3 or higher.
|
||||||
ynh_get_var() {
|
ynh_read_var_in_file() {
|
||||||
# Declare an array to define the options of this helper.
|
# Declare an array to define the options of this helper.
|
||||||
local legacy_args=fk
|
local legacy_args=fk
|
||||||
local -A args_array=( [f]=file= [k]=key= )
|
local -A args_array=( [f]=file= [k]=key= )
|
||||||
|
@ -515,10 +516,9 @@ ynh_get_var() {
|
||||||
ynh_handle_getopts_args "$@"
|
ynh_handle_getopts_args "$@"
|
||||||
|
|
||||||
local var_part='^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*'
|
local var_part='^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*'
|
||||||
|
|
||||||
local crazy_value="$((grep -i -o -P '^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*\K.*(?=[ \t,\n;]*$)' ${file} || echo YNH_NULL) | head -n1)"
|
local crazy_value="$(grep -i -o -P '^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*\K.*(?=[ \t,\n;]*$)' ${file} || echo YNH_NULL | head -n1)"
|
||||||
#"
|
|
||||||
|
|
||||||
local first_char="${crazy_value:0:1}"
|
local first_char="${crazy_value:0:1}"
|
||||||
if [[ "$first_char" == '"' ]] ; then
|
if [[ "$first_char" == '"' ]] ; then
|
||||||
echo "$crazy_value" | grep -m1 -o -P '"\K([^"](\\")?)*[^\\](?=")' | head -n1 | sed 's/\\"/"/g'
|
echo "$crazy_value" | grep -m1 -o -P '"\K([^"](\\")?)*[^\\](?=")' | head -n1 | sed 's/\\"/"/g'
|
||||||
|
@ -531,13 +531,13 @@ ynh_get_var() {
|
||||||
|
|
||||||
# Set a value into heterogeneous file (yaml, json, php, python...)
|
# Set a value into heterogeneous file (yaml, json, php, python...)
|
||||||
#
|
#
|
||||||
# usage: ynh_set_var --file=PATH --key=KEY --value=VALUE
|
# usage: ynh_write_var_in_file --file=PATH --key=KEY --value=VALUE
|
||||||
# | arg: -f, --file= - the path to the file
|
# | arg: -f, --file= - the path to the file
|
||||||
# | arg: -k, --key= - the key to set
|
# | arg: -k, --key= - the key to set
|
||||||
# | arg: -v, --value= - the value to set
|
# | arg: -v, --value= - the value to set
|
||||||
#
|
#
|
||||||
# Requires YunoHost version 4.3 or higher.
|
# Requires YunoHost version 4.3 or higher.
|
||||||
ynh_set_var() {
|
ynh_write_var_in_file() {
|
||||||
# Declare an array to define the options of this helper.
|
# Declare an array to define the options of this helper.
|
||||||
local legacy_args=fkv
|
local legacy_args=fkv
|
||||||
local -A args_array=( [f]=file= [k]=key= [v]=value=)
|
local -A args_array=( [f]=file= [k]=key= [v]=value=)
|
||||||
|
@ -547,7 +547,7 @@ ynh_set_var() {
|
||||||
# Manage arguments with getopts
|
# Manage arguments with getopts
|
||||||
ynh_handle_getopts_args "$@"
|
ynh_handle_getopts_args "$@"
|
||||||
local var_part='[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*'
|
local var_part='[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*'
|
||||||
|
|
||||||
local crazy_value="$(grep -i -o -P '^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*\K.*(?=[ \t,\n;]*$)' ${file} | head -n1)"
|
local crazy_value="$(grep -i -o -P '^[ \t]*\$?(\w*\[)?[ \t]*["'"']?${key}['"'"]?[ \t]*\]?[ \t]*[:=]>?[ \t]*\K.*(?=[ \t,\n;]*$)' ${file} | head -n1)"
|
||||||
# local crazy_value="$(grep -i -o -P "^${var_part}\K.*(?=[ \t,\n;]*\$)" ${file} | head -n1)"
|
# local crazy_value="$(grep -i -o -P "^${var_part}\K.*(?=[ \t,\n;]*\$)" ${file} | head -n1)"
|
||||||
local first_char="${crazy_value:0:1}"
|
local first_char="${crazy_value:0:1}"
|
||||||
|
|
|
@ -6,6 +6,7 @@ from yunohost.app import app_list
|
||||||
|
|
||||||
from yunohost.diagnosis import Diagnoser
|
from yunohost.diagnosis import Diagnoser
|
||||||
|
|
||||||
|
|
||||||
class AppDiagnoser(Diagnoser):
|
class AppDiagnoser(Diagnoser):
|
||||||
|
|
||||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||||
|
@ -30,13 +31,17 @@ class AppDiagnoser(Diagnoser):
|
||||||
if not app["issues"]:
|
if not app["issues"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
level = "ERROR" if any(issue[0] == "error" for issue in app["issues"]) else "WARNING"
|
level = (
|
||||||
|
"ERROR"
|
||||||
|
if any(issue[0] == "error" for issue in app["issues"])
|
||||||
|
else "WARNING"
|
||||||
|
)
|
||||||
|
|
||||||
yield dict(
|
yield dict(
|
||||||
meta={"test": "apps", "app": app["name"]},
|
meta={"test": "apps", "app": app["name"]},
|
||||||
status=level,
|
status=level,
|
||||||
summary="diagnosis_apps_issue",
|
summary="diagnosis_apps_issue",
|
||||||
details=[issue[1] for issue in app["issues"]]
|
details=[issue[1] for issue in app["issues"]],
|
||||||
)
|
)
|
||||||
|
|
||||||
def issues(self, app):
|
def issues(self, app):
|
||||||
|
@ -45,14 +50,19 @@ class AppDiagnoser(Diagnoser):
|
||||||
|
|
||||||
if not app.get("from_catalog") or app["from_catalog"].get("state") != "working":
|
if not app.get("from_catalog") or app["from_catalog"].get("state") != "working":
|
||||||
yield ("error", "diagnosis_apps_not_in_app_catalog")
|
yield ("error", "diagnosis_apps_not_in_app_catalog")
|
||||||
elif not isinstance(app["from_catalog"].get("level"), int) or app["from_catalog"]["level"] == 0:
|
elif (
|
||||||
|
not isinstance(app["from_catalog"].get("level"), int)
|
||||||
|
or app["from_catalog"]["level"] == 0
|
||||||
|
):
|
||||||
yield ("error", "diagnosis_apps_broken")
|
yield ("error", "diagnosis_apps_broken")
|
||||||
elif app["from_catalog"]["level"] <= 4:
|
elif app["from_catalog"]["level"] <= 4:
|
||||||
yield ("warning", "diagnosis_apps_bad_quality")
|
yield ("warning", "diagnosis_apps_bad_quality")
|
||||||
|
|
||||||
# Check for super old, deprecated practices
|
# Check for super old, deprecated practices
|
||||||
|
|
||||||
yunohost_version_req = app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ")
|
yunohost_version_req = (
|
||||||
|
app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ")
|
||||||
|
)
|
||||||
if yunohost_version_req.startswith("2."):
|
if yunohost_version_req.startswith("2."):
|
||||||
yield ("error", "diagnosis_apps_outdated_ynh_requirement")
|
yield ("error", "diagnosis_apps_outdated_ynh_requirement")
|
||||||
|
|
||||||
|
@ -64,11 +74,21 @@ class AppDiagnoser(Diagnoser):
|
||||||
"yunohost tools port-available",
|
"yunohost tools port-available",
|
||||||
]
|
]
|
||||||
for deprecated_helper in deprecated_helpers:
|
for deprecated_helper in deprecated_helpers:
|
||||||
if os.system(f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/") == 0:
|
if (
|
||||||
|
os.system(
|
||||||
|
f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/"
|
||||||
|
)
|
||||||
|
== 0
|
||||||
|
):
|
||||||
yield ("error", "diagnosis_apps_deprecated_practices")
|
yield ("error", "diagnosis_apps_deprecated_practices")
|
||||||
|
|
||||||
old_arg_regex = r'^domain=\${?[0-9]'
|
old_arg_regex = r"^domain=\${?[0-9]"
|
||||||
if os.system(f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install") == 0:
|
if (
|
||||||
|
os.system(
|
||||||
|
f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install"
|
||||||
|
)
|
||||||
|
== 0
|
||||||
|
):
|
||||||
yield ("error", "diagnosis_apps_deprecated_practices")
|
yield ("error", "diagnosis_apps_deprecated_practices")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ import urllib.parse
|
||||||
import tempfile
|
import tempfile
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from moulinette.interfaces.cli import colorize
|
|
||||||
from moulinette import Moulinette, m18n
|
from moulinette import Moulinette, m18n
|
||||||
from moulinette.core import MoulinetteError
|
from moulinette.core import MoulinetteError
|
||||||
from moulinette.utils.log import getActionLogger
|
from moulinette.utils.log import getActionLogger
|
||||||
|
@ -55,7 +54,11 @@ from moulinette.utils.filesystem import (
|
||||||
|
|
||||||
from yunohost.service import service_status, _run_service_command
|
from yunohost.service import service_status, _run_service_command
|
||||||
from yunohost.utils import packages, config
|
from yunohost.utils import packages, config
|
||||||
from yunohost.utils.config import ConfigPanel, parse_args_in_yunohost_format, Question
|
from yunohost.utils.config import (
|
||||||
|
ConfigPanel,
|
||||||
|
parse_args_in_yunohost_format,
|
||||||
|
Question,
|
||||||
|
)
|
||||||
from yunohost.utils.i18n import _value_for_locale
|
from yunohost.utils.i18n import _value_for_locale
|
||||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||||
from yunohost.utils.filesystem import free_space_in_directory
|
from yunohost.utils.filesystem import free_space_in_directory
|
||||||
|
@ -1756,30 +1759,33 @@ def app_action_run(operation_logger, app, action, args=None):
|
||||||
return logger.success("Action successed!")
|
return logger.success("Action successed!")
|
||||||
|
|
||||||
|
|
||||||
def app_config_get(app, key='', mode='classic'):
|
def app_config_get(app, key="", mode="classic"):
|
||||||
"""
|
"""
|
||||||
Display an app configuration in classic, full or export mode
|
Display an app configuration in classic, full or export mode
|
||||||
"""
|
"""
|
||||||
config = AppConfigPanel(app)
|
config_ = AppConfigPanel(app)
|
||||||
return config.get(key, mode)
|
return config_.get(key, mode)
|
||||||
|
|
||||||
|
|
||||||
@is_unit_operation()
|
@is_unit_operation()
|
||||||
def app_config_set(operation_logger, app, key=None, value=None, args=None, args_file=None):
|
def app_config_set(
|
||||||
|
operation_logger, app, key=None, value=None, args=None, args_file=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Apply a new app configuration
|
Apply a new app configuration
|
||||||
"""
|
"""
|
||||||
|
|
||||||
config = AppConfigPanel(app)
|
config_ = AppConfigPanel(app)
|
||||||
|
|
||||||
Question.operation_logger = operation_logger
|
Question.operation_logger = operation_logger
|
||||||
operation_logger.start()
|
operation_logger.start()
|
||||||
|
|
||||||
result = config.set(key, value, args, args_file)
|
result = config_.set(key, value, args, args_file)
|
||||||
if "errors" not in result:
|
if "errors" not in result:
|
||||||
operation_logger.success()
|
operation_logger.success()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class AppConfigPanel(ConfigPanel):
|
class AppConfigPanel(ConfigPanel):
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
|
|
||||||
|
@ -1791,10 +1797,10 @@ class AppConfigPanel(ConfigPanel):
|
||||||
super().__init__(config_path=config_path)
|
super().__init__(config_path=config_path)
|
||||||
|
|
||||||
def _load_current_values(self):
|
def _load_current_values(self):
|
||||||
self.values = self._call_config_script('show')
|
self.values = self._call_config_script("show")
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(self):
|
||||||
self.errors = self._call_config_script('apply', self.new_values)
|
self.errors = self._call_config_script("apply", self.new_values)
|
||||||
|
|
||||||
def _call_config_script(self, action, env={}):
|
def _call_config_script(self, action, env={}):
|
||||||
from yunohost.hook import hook_exec
|
from yunohost.hook import hook_exec
|
||||||
|
@ -1814,22 +1820,23 @@ ynh_app_config_run $1
|
||||||
# Call config script to extract current values
|
# Call config script to extract current values
|
||||||
logger.debug(f"Calling '{action}' action from config script")
|
logger.debug(f"Calling '{action}' action from config script")
|
||||||
app_id, app_instance_nb = _parse_app_instance_name(self.app)
|
app_id, app_instance_nb = _parse_app_instance_name(self.app)
|
||||||
env.update({
|
env.update(
|
||||||
"app_id": app_id,
|
{
|
||||||
"app": self.app,
|
"app_id": app_id,
|
||||||
"app_instance_nb": str(app_instance_nb),
|
"app": self.app,
|
||||||
})
|
"app_instance_nb": str(app_instance_nb),
|
||||||
|
}
|
||||||
ret, values = hook_exec(
|
|
||||||
config_script, args=[action], env=env
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ret, values = hook_exec(config_script, args=[action], env=env)
|
||||||
if ret != 0:
|
if ret != 0:
|
||||||
if action == 'show':
|
if action == "show":
|
||||||
raise YunohostError("app_config_unable_to_read_values")
|
raise YunohostError("app_config_unable_to_read_values")
|
||||||
else:
|
else:
|
||||||
raise YunohostError("app_config_unable_to_apply_values_correctly")
|
raise YunohostError("app_config_unable_to_apply_values_correctly")
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
def _get_all_installed_apps_id():
|
def _get_all_installed_apps_id():
|
||||||
"""
|
"""
|
||||||
Return something like:
|
Return something like:
|
||||||
|
@ -2455,9 +2462,6 @@ def _parse_args_for_action(action, args={}):
|
||||||
return parse_args_in_yunohost_format(args, action_args)
|
return parse_args_in_yunohost_format(args, action_args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_and_normalize_webpath(args_dict, app_folder):
|
def _validate_and_normalize_webpath(args_dict, app_folder):
|
||||||
|
|
||||||
# If there's only one "domain" and "path", validate that domain/path
|
# If there's only one "domain" and "path", validate that domain/path
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import toml
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import shutil
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from moulinette.interfaces.cli import colorize
|
from moulinette.interfaces.cli import colorize
|
||||||
|
@ -37,16 +37,14 @@ from moulinette.utils.filesystem import (
|
||||||
mkdir,
|
mkdir,
|
||||||
)
|
)
|
||||||
|
|
||||||
from yunohost.service import _get_services
|
|
||||||
from yunohost.service import _run_service_command, _get_services
|
|
||||||
from yunohost.utils.i18n import _value_for_locale
|
from yunohost.utils.i18n import _value_for_locale
|
||||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||||
|
|
||||||
logger = getActionLogger("yunohost.config")
|
logger = getActionLogger("yunohost.config")
|
||||||
CONFIG_PANEL_VERSION_SUPPORTED = 1.0
|
CONFIG_PANEL_VERSION_SUPPORTED = 1.0
|
||||||
|
|
||||||
class ConfigPanel:
|
|
||||||
|
|
||||||
|
class ConfigPanel:
|
||||||
def __init__(self, config_path, save_path=None):
|
def __init__(self, config_path, save_path=None):
|
||||||
self.config_path = config_path
|
self.config_path = config_path
|
||||||
self.save_path = save_path
|
self.save_path = save_path
|
||||||
|
@ -54,8 +52,8 @@ class ConfigPanel:
|
||||||
self.values = {}
|
self.values = {}
|
||||||
self.new_values = {}
|
self.new_values = {}
|
||||||
|
|
||||||
def get(self, key='', mode='classic'):
|
def get(self, key="", mode="classic"):
|
||||||
self.filter_key = key or ''
|
self.filter_key = key or ""
|
||||||
|
|
||||||
# Read config panel toml
|
# Read config panel toml
|
||||||
self._get_config_panel()
|
self._get_config_panel()
|
||||||
|
@ -68,12 +66,12 @@ class ConfigPanel:
|
||||||
self._hydrate()
|
self._hydrate()
|
||||||
|
|
||||||
# Format result in full mode
|
# Format result in full mode
|
||||||
if mode == 'full':
|
if mode == "full":
|
||||||
return self.config
|
return self.config
|
||||||
|
|
||||||
# In 'classic' mode, we display the current value if key refer to an option
|
# In 'classic' mode, we display the current value if key refer to an option
|
||||||
if self.filter_key.count('.') == 2 and mode == 'classic':
|
if self.filter_key.count(".") == 2 and mode == "classic":
|
||||||
option = self.filter_key.split('.')[-1]
|
option = self.filter_key.split(".")[-1]
|
||||||
return self.values.get(option, None)
|
return self.values.get(option, None)
|
||||||
|
|
||||||
# Format result in 'classic' or 'export' mode
|
# Format result in 'classic' or 'export' mode
|
||||||
|
@ -81,17 +79,17 @@ class ConfigPanel:
|
||||||
result = {}
|
result = {}
|
||||||
for panel, section, option in self._iterate():
|
for panel, section, option in self._iterate():
|
||||||
key = f"{panel['id']}.{section['id']}.{option['id']}"
|
key = f"{panel['id']}.{section['id']}.{option['id']}"
|
||||||
if mode == 'export':
|
if mode == "export":
|
||||||
result[option['id']] = option.get('current_value')
|
result[option["id"]] = option.get("current_value")
|
||||||
else:
|
else:
|
||||||
result[key] = { 'ask': _value_for_locale(option['ask']) }
|
result[key] = {"ask": _value_for_locale(option["ask"])}
|
||||||
if 'current_value' in option:
|
if "current_value" in option:
|
||||||
result[key]['value'] = option['current_value']
|
result[key]["value"] = option["current_value"]
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set(self, key=None, value=None, args=None, args_file=None):
|
def set(self, key=None, value=None, args=None, args_file=None):
|
||||||
self.filter_key = key or ''
|
self.filter_key = key or ""
|
||||||
|
|
||||||
# Read config panel toml
|
# Read config panel toml
|
||||||
self._get_config_panel()
|
self._get_config_panel()
|
||||||
|
@ -102,20 +100,20 @@ class ConfigPanel:
|
||||||
if (args is not None or args_file is not None) and value is not None:
|
if (args is not None or args_file is not None) and value is not None:
|
||||||
raise YunohostError("config_args_value")
|
raise YunohostError("config_args_value")
|
||||||
|
|
||||||
if self.filter_key.count('.') != 2 and not value is None:
|
if self.filter_key.count(".") != 2 and not value is None:
|
||||||
raise YunohostError("config_set_value_on_section")
|
raise YunohostError("config_set_value_on_section")
|
||||||
|
|
||||||
# Import and parse pre-answered options
|
# Import and parse pre-answered options
|
||||||
logger.debug("Import and parse pre-answered options")
|
logger.debug("Import and parse pre-answered options")
|
||||||
args = urllib.parse.parse_qs(args or '', keep_blank_values=True)
|
args = urllib.parse.parse_qs(args or "", keep_blank_values=True)
|
||||||
self.args = { key: ','.join(value_) for key, value_ in args.items() }
|
self.args = {key: ",".join(value_) for key, value_ in args.items()}
|
||||||
|
|
||||||
if args_file:
|
if args_file:
|
||||||
# Import YAML / JSON file but keep --args values
|
# Import YAML / JSON file but keep --args values
|
||||||
self.args = { **read_yaml(args_file), **self.args }
|
self.args = {**read_yaml(args_file), **self.args}
|
||||||
|
|
||||||
if value is not None:
|
if value is not None:
|
||||||
self.args = {self.filter_key.split('.')[-1]: value}
|
self.args = {self.filter_key.split(".")[-1]: value}
|
||||||
|
|
||||||
# Read or get values and hydrate the config
|
# Read or get values and hydrate the config
|
||||||
self._load_current_values()
|
self._load_current_values()
|
||||||
|
@ -144,7 +142,7 @@ class ConfigPanel:
|
||||||
|
|
||||||
if self.errors:
|
if self.errors:
|
||||||
return {
|
return {
|
||||||
"errors": errors,
|
"errors": self.errors,
|
||||||
}
|
}
|
||||||
|
|
||||||
self._reload_services()
|
self._reload_services()
|
||||||
|
@ -155,10 +153,9 @@ class ConfigPanel:
|
||||||
def _get_toml(self):
|
def _get_toml(self):
|
||||||
return read_toml(self.config_path)
|
return read_toml(self.config_path)
|
||||||
|
|
||||||
|
|
||||||
def _get_config_panel(self):
|
def _get_config_panel(self):
|
||||||
# Split filter_key
|
# Split filter_key
|
||||||
filter_key = dict(enumerate(self.filter_key.split('.')))
|
filter_key = dict(enumerate(self.filter_key.split(".")))
|
||||||
if len(filter_key) > 3:
|
if len(filter_key) > 3:
|
||||||
raise YunohostError("config_too_much_sub_keys")
|
raise YunohostError("config_too_much_sub_keys")
|
||||||
|
|
||||||
|
@ -174,20 +171,18 @@ class ConfigPanel:
|
||||||
|
|
||||||
# Transform toml format into internal format
|
# Transform toml format into internal format
|
||||||
defaults = {
|
defaults = {
|
||||||
'toml': {
|
"toml": {"version": 1.0},
|
||||||
'version': 1.0
|
"panels": {
|
||||||
},
|
"name": "",
|
||||||
'panels': {
|
"services": [],
|
||||||
'name': '',
|
"actions": {"apply": {"en": "Apply"}},
|
||||||
'services': [],
|
}, # help
|
||||||
'actions': {'apply': {'en': 'Apply'}}
|
"sections": {
|
||||||
}, # help
|
"name": "",
|
||||||
'sections': {
|
"services": [],
|
||||||
'name': '',
|
"optional": True,
|
||||||
'services': [],
|
}, # visibleIf help
|
||||||
'optional': True
|
"options": {}
|
||||||
}, # visibleIf help
|
|
||||||
'options': {}
|
|
||||||
# ask type source help helpLink example style icon placeholder visibleIf
|
# ask type source help helpLink example style icon placeholder visibleIf
|
||||||
# optional choices pattern limit min max step accept redact
|
# optional choices pattern limit min max step accept redact
|
||||||
}
|
}
|
||||||
|
@ -207,30 +202,36 @@ class ConfigPanel:
|
||||||
# Define the filter_key part to use and the children type
|
# Define the filter_key part to use and the children type
|
||||||
i = list(defaults).index(node_type)
|
i = list(defaults).index(node_type)
|
||||||
search_key = filter_key.get(i)
|
search_key = filter_key.get(i)
|
||||||
subnode_type = list(defaults)[i+1] if node_type != 'options' else None
|
subnode_type = list(defaults)[i + 1] if node_type != "options" else None
|
||||||
|
|
||||||
for key, value in toml_node.items():
|
for key, value in toml_node.items():
|
||||||
# Key/value are a child node
|
# Key/value are a child node
|
||||||
if isinstance(value, OrderedDict) and key not in default and subnode_type:
|
if (
|
||||||
|
isinstance(value, OrderedDict)
|
||||||
|
and key not in default
|
||||||
|
and subnode_type
|
||||||
|
):
|
||||||
# We exclude all nodes not referenced by the filter_key
|
# We exclude all nodes not referenced by the filter_key
|
||||||
if search_key and key != search_key:
|
if search_key and key != search_key:
|
||||||
continue
|
continue
|
||||||
subnode = convert(value, subnode_type)
|
subnode = convert(value, subnode_type)
|
||||||
subnode['id'] = key
|
subnode["id"] = key
|
||||||
if node_type == 'sections':
|
if node_type == "sections":
|
||||||
subnode['name'] = key # legacy
|
subnode["name"] = key # legacy
|
||||||
subnode.setdefault('optional', toml_node.get('optional', True))
|
subnode.setdefault("optional", toml_node.get("optional", True))
|
||||||
node.setdefault(subnode_type, []).append(subnode)
|
node.setdefault(subnode_type, []).append(subnode)
|
||||||
# Key/value are a property
|
# Key/value are a property
|
||||||
else:
|
else:
|
||||||
# Todo search all i18n keys
|
# Todo search all i18n keys
|
||||||
node[key] = value if key not in ['ask', 'help', 'name'] else { 'en': value }
|
node[key] = (
|
||||||
|
value if key not in ["ask", "help", "name"] else {"en": value}
|
||||||
|
)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
self.config = convert(toml_config_panel, 'toml')
|
self.config = convert(toml_config_panel, "toml")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.config['panels'][0]['sections'][0]['options'][0]
|
self.config["panels"][0]["sections"][0]["options"][0]
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
raise YunohostError(
|
raise YunohostError(
|
||||||
"config_empty_or_bad_filter_key", filter_key=self.filter_key
|
"config_empty_or_bad_filter_key", filter_key=self.filter_key
|
||||||
|
@ -242,36 +243,41 @@ class ConfigPanel:
|
||||||
# Hydrating config panel with current value
|
# Hydrating config panel with current value
|
||||||
logger.debug("Hydrating config with current values")
|
logger.debug("Hydrating config with current values")
|
||||||
for _, _, option in self._iterate():
|
for _, _, option in self._iterate():
|
||||||
if option['name'] not in self.values:
|
if option["name"] not in self.values:
|
||||||
continue
|
continue
|
||||||
value = self.values[option['name']]
|
value = self.values[option["name"]]
|
||||||
# In general, the value is just a simple value.
|
# In general, the value is just a simple value.
|
||||||
# Sometimes it could be a dict used to overwrite the option itself
|
# Sometimes it could be a dict used to overwrite the option itself
|
||||||
value = value if isinstance(value, dict) else {'current_value': value }
|
value = value if isinstance(value, dict) else {"current_value": value}
|
||||||
option.update(value)
|
option.update(value)
|
||||||
|
|
||||||
return self.values
|
return self.values
|
||||||
|
|
||||||
def _ask(self):
|
def _ask(self):
|
||||||
logger.debug("Ask unanswered question and prevalidate data")
|
logger.debug("Ask unanswered question and prevalidate data")
|
||||||
|
|
||||||
def display_header(message):
|
def display_header(message):
|
||||||
""" CLI panel/section header display
|
"""CLI panel/section header display"""
|
||||||
"""
|
if Moulinette.interface.type == "cli" and self.filter_key.count(".") < 2:
|
||||||
if Moulinette.interface.type == 'cli' and self.filter_key.count('.') < 2:
|
Moulinette.display(colorize(message, "purple"))
|
||||||
Moulinette.display(colorize(message, 'purple'))
|
|
||||||
for panel, section, obj in self._iterate(['panel', 'section']):
|
for panel, section, obj in self._iterate(["panel", "section"]):
|
||||||
if panel == obj:
|
if panel == obj:
|
||||||
name = _value_for_locale(panel['name'])
|
name = _value_for_locale(panel["name"])
|
||||||
display_header(f"\n{'='*40}\n>>>> {name}\n{'='*40}")
|
display_header(f"\n{'='*40}\n>>>> {name}\n{'='*40}")
|
||||||
continue
|
continue
|
||||||
name = _value_for_locale(section['name'])
|
name = _value_for_locale(section["name"])
|
||||||
display_header(f"\n# {name}")
|
display_header(f"\n# {name}")
|
||||||
|
|
||||||
# Check and ask unanswered questions
|
# Check and ask unanswered questions
|
||||||
self.new_values.update(parse_args_in_yunohost_format(
|
self.new_values.update(
|
||||||
self.args, section['options']
|
parse_args_in_yunohost_format(self.args, section["options"])
|
||||||
))
|
)
|
||||||
self.new_values = {key: str(value[0]) for key, value in self.new_values.items() if not value[0] is None}
|
self.new_values = {
|
||||||
|
key: str(value[0])
|
||||||
|
for key, value in self.new_values.items()
|
||||||
|
if not value[0] is None
|
||||||
|
}
|
||||||
|
|
||||||
def _apply(self):
|
def _apply(self):
|
||||||
logger.info("Running config script...")
|
logger.info("Running config script...")
|
||||||
|
@ -281,36 +287,37 @@ class ConfigPanel:
|
||||||
# Save the settings to the .yaml file
|
# Save the settings to the .yaml file
|
||||||
write_to_yaml(self.save_path, self.new_values)
|
write_to_yaml(self.save_path, self.new_values)
|
||||||
|
|
||||||
|
|
||||||
def _reload_services(self):
|
def _reload_services(self):
|
||||||
|
|
||||||
|
from yunohost.service import _run_service_command, _get_services
|
||||||
|
|
||||||
logger.info("Reloading services...")
|
logger.info("Reloading services...")
|
||||||
services_to_reload = set()
|
services_to_reload = set()
|
||||||
for panel, section, obj in self._iterate(['panel', 'section', 'option']):
|
for panel, section, obj in self._iterate(["panel", "section", "option"]):
|
||||||
services_to_reload |= set(obj.get('services', []))
|
services_to_reload |= set(obj.get("services", []))
|
||||||
|
|
||||||
services_to_reload = list(services_to_reload)
|
services_to_reload = list(services_to_reload)
|
||||||
services_to_reload.sort(key = 'nginx'.__eq__)
|
services_to_reload.sort(key="nginx".__eq__)
|
||||||
for service in services_to_reload:
|
for service in services_to_reload:
|
||||||
if '__APP__':
|
if "__APP__":
|
||||||
service = service.replace('__APP__', self.app)
|
service = service.replace("__APP__", self.app)
|
||||||
logger.debug(f"Reloading {service}")
|
logger.debug(f"Reloading {service}")
|
||||||
if not _run_service_command('reload-or-restart', service):
|
if not _run_service_command("reload-or-restart", service):
|
||||||
services = _get_services()
|
services = _get_services()
|
||||||
test_conf = services[service].get('test_conf', 'true')
|
test_conf = services[service].get("test_conf", "true")
|
||||||
errors = check_output(f"{test_conf}; exit 0") if test_conf else ''
|
errors = check_output(f"{test_conf}; exit 0") if test_conf else ""
|
||||||
raise YunohostError(
|
raise YunohostError(
|
||||||
"config_failed_service_reload",
|
"config_failed_service_reload", service=service, errors=errors
|
||||||
service=service, errors=errors
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _iterate(self, trigger=['option']):
|
def _iterate(self, trigger=["option"]):
|
||||||
for panel in self.config.get("panels", []):
|
for panel in self.config.get("panels", []):
|
||||||
if 'panel' in trigger:
|
if "panel" in trigger:
|
||||||
yield (panel, None, panel)
|
yield (panel, None, panel)
|
||||||
for section in panel.get("sections", []):
|
for section in panel.get("sections", []):
|
||||||
if 'section' in trigger:
|
if "section" in trigger:
|
||||||
yield (panel, section, section)
|
yield (panel, section, section)
|
||||||
if 'option' in trigger:
|
if "option" in trigger:
|
||||||
for option in section.get("options", []):
|
for option in section.get("options", []):
|
||||||
yield (panel, section, option)
|
yield (panel, section, option)
|
||||||
|
|
||||||
|
@ -327,17 +334,17 @@ class YunoHostArgumentFormatParser(object):
|
||||||
parsed_question = Question()
|
parsed_question = Question()
|
||||||
|
|
||||||
parsed_question.name = question["name"]
|
parsed_question.name = question["name"]
|
||||||
parsed_question.type = question.get("type", 'string')
|
parsed_question.type = question.get("type", "string")
|
||||||
parsed_question.default = question.get("default", None)
|
parsed_question.default = question.get("default", None)
|
||||||
parsed_question.current_value = question.get("current_value")
|
parsed_question.current_value = question.get("current_value")
|
||||||
parsed_question.optional = question.get("optional", False)
|
parsed_question.optional = question.get("optional", False)
|
||||||
parsed_question.choices = question.get("choices", [])
|
parsed_question.choices = question.get("choices", [])
|
||||||
parsed_question.pattern = question.get("pattern")
|
parsed_question.pattern = question.get("pattern")
|
||||||
parsed_question.ask = question.get("ask", {'en': f"{parsed_question.name}"})
|
parsed_question.ask = question.get("ask", {"en": f"{parsed_question.name}"})
|
||||||
parsed_question.help = question.get("help")
|
parsed_question.help = question.get("help")
|
||||||
parsed_question.helpLink = question.get("helpLink")
|
parsed_question.helpLink = question.get("helpLink")
|
||||||
parsed_question.value = user_answers.get(parsed_question.name)
|
parsed_question.value = user_answers.get(parsed_question.name)
|
||||||
parsed_question.redact = question.get('redact', False)
|
parsed_question.redact = question.get("redact", False)
|
||||||
|
|
||||||
# Empty value is parsed as empty string
|
# Empty value is parsed as empty string
|
||||||
if parsed_question.default == "":
|
if parsed_question.default == "":
|
||||||
|
@ -350,7 +357,7 @@ class YunoHostArgumentFormatParser(object):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Display question if no value filled or if it's a readonly message
|
# Display question if no value filled or if it's a readonly message
|
||||||
if Moulinette.interface.type== 'cli':
|
if Moulinette.interface.type == "cli":
|
||||||
text_for_user_input_in_cli = self._format_text_for_user_input_in_cli(
|
text_for_user_input_in_cli = self._format_text_for_user_input_in_cli(
|
||||||
question
|
question
|
||||||
)
|
)
|
||||||
|
@ -368,10 +375,9 @@ class YunoHostArgumentFormatParser(object):
|
||||||
is_password=self.hide_user_input_in_prompt,
|
is_password=self.hide_user_input_in_prompt,
|
||||||
confirm=self.hide_user_input_in_prompt,
|
confirm=self.hide_user_input_in_prompt,
|
||||||
prefill=prefill,
|
prefill=prefill,
|
||||||
is_multiline=(question.type == "text")
|
is_multiline=(question.type == "text"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Apply default value
|
# Apply default value
|
||||||
if question.value in [None, ""] and question.default is not None:
|
if question.value in [None, ""] and question.default is not None:
|
||||||
question.value = (
|
question.value = (
|
||||||
|
@ -384,9 +390,9 @@ class YunoHostArgumentFormatParser(object):
|
||||||
try:
|
try:
|
||||||
self._prevalidate(question)
|
self._prevalidate(question)
|
||||||
except YunohostValidationError as e:
|
except YunohostValidationError as e:
|
||||||
if Moulinette.interface.type== 'api':
|
if Moulinette.interface.type == "api":
|
||||||
raise
|
raise
|
||||||
Moulinette.display(str(e), 'error')
|
Moulinette.display(str(e), "error")
|
||||||
question.value = None
|
question.value = None
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
|
@ -398,17 +404,17 @@ class YunoHostArgumentFormatParser(object):
|
||||||
|
|
||||||
def _prevalidate(self, question):
|
def _prevalidate(self, question):
|
||||||
if question.value in [None, ""] and not question.optional:
|
if question.value in [None, ""] and not question.optional:
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError("app_argument_required", name=question.name)
|
||||||
"app_argument_required", name=question.name
|
|
||||||
)
|
|
||||||
|
|
||||||
# we have an answer, do some post checks
|
# we have an answer, do some post checks
|
||||||
if question.value is not None:
|
if question.value is not None:
|
||||||
if question.choices and question.value not in question.choices:
|
if question.choices and question.value not in question.choices:
|
||||||
self._raise_invalid_answer(question)
|
self._raise_invalid_answer(question)
|
||||||
if question.pattern and not re.match(question.pattern['regexp'], str(question.value)):
|
if question.pattern and not re.match(
|
||||||
|
question.pattern["regexp"], str(question.value)
|
||||||
|
):
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
question.pattern['error'],
|
question.pattern["error"],
|
||||||
name=question.name,
|
name=question.name,
|
||||||
value=question.value,
|
value=question.value,
|
||||||
)
|
)
|
||||||
|
@ -434,7 +440,7 @@ class YunoHostArgumentFormatParser(object):
|
||||||
text_for_user_input_in_cli += _value_for_locale(question.help)
|
text_for_user_input_in_cli += _value_for_locale(question.help)
|
||||||
if question.helpLink:
|
if question.helpLink:
|
||||||
if not isinstance(question.helpLink, dict):
|
if not isinstance(question.helpLink, dict):
|
||||||
question.helpLink = {'href': question.helpLink}
|
question.helpLink = {"href": question.helpLink}
|
||||||
text_for_user_input_in_cli += f"\n - See {question.helpLink['href']}"
|
text_for_user_input_in_cli += f"\n - See {question.helpLink['href']}"
|
||||||
return text_for_user_input_in_cli
|
return text_for_user_input_in_cli
|
||||||
|
|
||||||
|
@ -467,18 +473,18 @@ class StringArgumentParser(YunoHostArgumentFormatParser):
|
||||||
argument_type = "string"
|
argument_type = "string"
|
||||||
default_value = ""
|
default_value = ""
|
||||||
|
|
||||||
|
|
||||||
class TagsArgumentParser(YunoHostArgumentFormatParser):
|
class TagsArgumentParser(YunoHostArgumentFormatParser):
|
||||||
argument_type = "tags"
|
argument_type = "tags"
|
||||||
|
|
||||||
def _prevalidate(self, question):
|
def _prevalidate(self, question):
|
||||||
values = question.value
|
values = question.value
|
||||||
for value in values.split(','):
|
for value in values.split(","):
|
||||||
question.value = value
|
question.value = value
|
||||||
super()._prevalidate(question)
|
super()._prevalidate(question)
|
||||||
question.value = values
|
question.value = values
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
||||||
hide_user_input_in_prompt = True
|
hide_user_input_in_prompt = True
|
||||||
argument_type = "password"
|
argument_type = "password"
|
||||||
|
@ -522,9 +528,7 @@ class BooleanArgumentParser(YunoHostArgumentFormatParser):
|
||||||
default_value = False
|
default_value = False
|
||||||
|
|
||||||
def parse_question(self, question, user_answers):
|
def parse_question(self, question, user_answers):
|
||||||
question = super().parse_question(
|
question = super().parse_question(question, user_answers)
|
||||||
question, user_answers
|
|
||||||
)
|
|
||||||
|
|
||||||
if question.default is None:
|
if question.default is None:
|
||||||
question.default = False
|
question.default = False
|
||||||
|
@ -616,11 +620,9 @@ class NumberArgumentParser(YunoHostArgumentFormatParser):
|
||||||
default_value = ""
|
default_value = ""
|
||||||
|
|
||||||
def parse_question(self, question, user_answers):
|
def parse_question(self, question, user_answers):
|
||||||
question_parsed = super().parse_question(
|
question_parsed = super().parse_question(question, user_answers)
|
||||||
question, user_answers
|
question_parsed.min = question.get("min", None)
|
||||||
)
|
question_parsed.max = question.get("max", None)
|
||||||
question_parsed.min = question.get('min', None)
|
|
||||||
question_parsed.max = question.get('max', None)
|
|
||||||
if question_parsed.default is None:
|
if question_parsed.default is None:
|
||||||
question_parsed.default = 0
|
question_parsed.default = 0
|
||||||
|
|
||||||
|
@ -628,19 +630,27 @@ class NumberArgumentParser(YunoHostArgumentFormatParser):
|
||||||
|
|
||||||
def _prevalidate(self, question):
|
def _prevalidate(self, question):
|
||||||
super()._prevalidate(question)
|
super()._prevalidate(question)
|
||||||
if not isinstance(question.value, int) and not (isinstance(question.value, str) and question.value.isdigit()):
|
if not isinstance(question.value, int) and not (
|
||||||
|
isinstance(question.value, str) and question.value.isdigit()
|
||||||
|
):
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_argument_invalid", field=question.name, error=m18n.n("invalid_number")
|
"app_argument_invalid",
|
||||||
|
field=question.name,
|
||||||
|
error=m18n.n("invalid_number"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if question.min is not None and int(question.value) < question.min:
|
if question.min is not None and int(question.value) < question.min:
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_argument_invalid", field=question.name, error=m18n.n("invalid_number")
|
"app_argument_invalid",
|
||||||
|
field=question.name,
|
||||||
|
error=m18n.n("invalid_number"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if question.max is not None and int(question.value) > question.max:
|
if question.max is not None and int(question.value) > question.max:
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_argument_invalid", field=question.name, error=m18n.n("invalid_number")
|
"app_argument_invalid",
|
||||||
|
field=question.name,
|
||||||
|
error=m18n.n("invalid_number"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _post_parse_value(self, question):
|
def _post_parse_value(self, question):
|
||||||
|
@ -660,29 +670,28 @@ class DisplayTextArgumentParser(YunoHostArgumentFormatParser):
|
||||||
readonly = True
|
readonly = True
|
||||||
|
|
||||||
def parse_question(self, question, user_answers):
|
def parse_question(self, question, user_answers):
|
||||||
question_parsed = super().parse_question(
|
question_parsed = super().parse_question(question, user_answers)
|
||||||
question, user_answers
|
|
||||||
)
|
|
||||||
|
|
||||||
question_parsed.optional = True
|
question_parsed.optional = True
|
||||||
question_parsed.style = question.get('style', 'info')
|
question_parsed.style = question.get("style", "info")
|
||||||
|
|
||||||
return question_parsed
|
return question_parsed
|
||||||
|
|
||||||
def _format_text_for_user_input_in_cli(self, question):
|
def _format_text_for_user_input_in_cli(self, question):
|
||||||
text = question.ask['en']
|
text = question.ask["en"]
|
||||||
|
|
||||||
if question.style in ['success', 'info', 'warning', 'danger']:
|
if question.style in ["success", "info", "warning", "danger"]:
|
||||||
color = {
|
color = {
|
||||||
'success': 'green',
|
"success": "green",
|
||||||
'info': 'cyan',
|
"info": "cyan",
|
||||||
'warning': 'yellow',
|
"warning": "yellow",
|
||||||
'danger': 'red'
|
"danger": "red",
|
||||||
}
|
}
|
||||||
return colorize(m18n.g(question.style), color[question.style]) + f" {text}"
|
return colorize(m18n.g(question.style), color[question.style]) + f" {text}"
|
||||||
else:
|
else:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
class FileArgumentParser(YunoHostArgumentFormatParser):
|
class FileArgumentParser(YunoHostArgumentFormatParser):
|
||||||
argument_type = "file"
|
argument_type = "file"
|
||||||
upload_dirs = []
|
upload_dirs = []
|
||||||
|
@ -690,60 +699,77 @@ class FileArgumentParser(YunoHostArgumentFormatParser):
|
||||||
@classmethod
|
@classmethod
|
||||||
def clean_upload_dirs(cls):
|
def clean_upload_dirs(cls):
|
||||||
# Delete files uploaded from API
|
# Delete files uploaded from API
|
||||||
if Moulinette.interface.type== 'api':
|
if Moulinette.interface.type == "api":
|
||||||
for upload_dir in cls.upload_dirs:
|
for upload_dir in cls.upload_dirs:
|
||||||
if os.path.exists(upload_dir):
|
if os.path.exists(upload_dir):
|
||||||
shutil.rmtree(upload_dir)
|
shutil.rmtree(upload_dir)
|
||||||
|
|
||||||
def parse_question(self, question, user_answers):
|
def parse_question(self, question, user_answers):
|
||||||
question_parsed = super().parse_question(
|
question_parsed = super().parse_question(question, user_answers)
|
||||||
question, user_answers
|
if question.get("accept"):
|
||||||
)
|
question_parsed.accept = question.get("accept").replace(" ", "").split(",")
|
||||||
if question.get('accept'):
|
|
||||||
question_parsed.accept = question.get('accept').replace(' ', '').split(',')
|
|
||||||
else:
|
else:
|
||||||
question_parsed.accept = []
|
question_parsed.accept = []
|
||||||
if Moulinette.interface.type== 'api':
|
if Moulinette.interface.type == "api":
|
||||||
if user_answers.get(f"{question_parsed.name}[name]"):
|
if user_answers.get(f"{question_parsed.name}[name]"):
|
||||||
question_parsed.value = {
|
question_parsed.value = {
|
||||||
'content': question_parsed.value,
|
"content": question_parsed.value,
|
||||||
'filename': user_answers.get(f"{question_parsed.name}[name]", question_parsed.name),
|
"filename": user_answers.get(
|
||||||
|
f"{question_parsed.name}[name]", question_parsed.name
|
||||||
|
),
|
||||||
}
|
}
|
||||||
# If path file are the same
|
# If path file are the same
|
||||||
if question_parsed.value and str(question_parsed.value) == question_parsed.current_value:
|
if (
|
||||||
|
question_parsed.value
|
||||||
|
and str(question_parsed.value) == question_parsed.current_value
|
||||||
|
):
|
||||||
question_parsed.value = None
|
question_parsed.value = None
|
||||||
|
|
||||||
return question_parsed
|
return question_parsed
|
||||||
|
|
||||||
def _prevalidate(self, question):
|
def _prevalidate(self, question):
|
||||||
super()._prevalidate(question)
|
super()._prevalidate(question)
|
||||||
if isinstance(question.value, str) and question.value and not os.path.exists(question.value):
|
if (
|
||||||
|
isinstance(question.value, str)
|
||||||
|
and question.value
|
||||||
|
and not os.path.exists(question.value)
|
||||||
|
):
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_argument_invalid", field=question.name, error=m18n.n("invalid_number1")
|
"app_argument_invalid",
|
||||||
|
field=question.name,
|
||||||
|
error=m18n.n("invalid_number1"),
|
||||||
)
|
)
|
||||||
if question.value in [None, ''] or not question.accept:
|
if question.value in [None, ""] or not question.accept:
|
||||||
return
|
return
|
||||||
|
|
||||||
filename = question.value if isinstance(question.value, str) else question.value['filename']
|
filename = (
|
||||||
if '.' not in filename or '.' + filename.split('.')[-1] not in question.accept:
|
question.value
|
||||||
|
if isinstance(question.value, str)
|
||||||
|
else question.value["filename"]
|
||||||
|
)
|
||||||
|
if "." not in filename or "." + filename.split(".")[-1] not in question.accept:
|
||||||
raise YunohostValidationError(
|
raise YunohostValidationError(
|
||||||
"app_argument_invalid", field=question.name, error=m18n.n("invalid_number2")
|
"app_argument_invalid",
|
||||||
|
field=question.name,
|
||||||
|
error=m18n.n("invalid_number2"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _post_parse_value(self, question):
|
def _post_parse_value(self, question):
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
# Upload files from API
|
# Upload files from API
|
||||||
# A file arg contains a string with "FILENAME:BASE64_CONTENT"
|
# A file arg contains a string with "FILENAME:BASE64_CONTENT"
|
||||||
if not question.value:
|
if not question.value:
|
||||||
return question.value
|
return question.value
|
||||||
|
|
||||||
if Moulinette.interface.type== 'api':
|
if Moulinette.interface.type == "api":
|
||||||
|
|
||||||
upload_dir = tempfile.mkdtemp(prefix='tmp_configpanel_')
|
upload_dir = tempfile.mkdtemp(prefix="tmp_configpanel_")
|
||||||
FileArgumentParser.upload_dirs += [upload_dir]
|
FileArgumentParser.upload_dirs += [upload_dir]
|
||||||
filename = question.value['filename']
|
filename = question.value["filename"]
|
||||||
logger.debug(f"Save uploaded file {question.value['filename']} from API into {upload_dir}")
|
logger.debug(
|
||||||
|
f"Save uploaded file {question.value['filename']} from API into {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
|
# os.path.join to avoid the user to be able to rewrite a file in filesystem
|
||||||
|
@ -755,9 +781,9 @@ class FileArgumentParser(YunoHostArgumentFormatParser):
|
||||||
while os.path.exists(file_path):
|
while os.path.exists(file_path):
|
||||||
file_path = os.path.normpath(upload_dir + "/" + filename + (".%d" % i))
|
file_path = os.path.normpath(upload_dir + "/" + filename + (".%d" % i))
|
||||||
i += 1
|
i += 1
|
||||||
content = question.value['content']
|
content = question.value["content"]
|
||||||
try:
|
try:
|
||||||
with open(file_path, 'wb') as f:
|
with open(file_path, "wb") as f:
|
||||||
f.write(b64decode(content))
|
f.write(b64decode(content))
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
raise YunohostError("cannot_write_file", file=file_path, error=str(e))
|
raise YunohostError("cannot_write_file", file=file_path, error=str(e))
|
||||||
|
@ -790,6 +816,7 @@ ARGUMENTS_TYPE_PARSERS = {
|
||||||
"file": FileArgumentParser,
|
"file": FileArgumentParser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_args_in_yunohost_format(user_answers, argument_questions):
|
def parse_args_in_yunohost_format(user_answers, argument_questions):
|
||||||
"""Parse arguments store in either manifest.json or actions.json or from a
|
"""Parse arguments store in either manifest.json or actions.json or from a
|
||||||
config panel against the user answers when they are present.
|
config panel against the user answers when they are present.
|
||||||
|
@ -811,4 +838,3 @@ def parse_args_in_yunohost_format(user_answers, argument_questions):
|
||||||
parsed_answers_dict[question["name"]] = answer
|
parsed_answers_dict[question["name"]] = answer
|
||||||
|
|
||||||
return parsed_answers_dict
|
return parsed_answers_dict
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
along with this program; if not, see http://www.gnu.org/licenses
|
along with this program; if not, see http://www.gnu.org/licenses
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from moulinette import Moulinette, m18n
|
from moulinette import m18n
|
||||||
|
|
||||||
|
|
||||||
def _value_for_locale(values):
|
def _value_for_locale(values):
|
||||||
"""
|
"""
|
||||||
|
@ -42,5 +43,3 @@ def _value_for_locale(values):
|
||||||
|
|
||||||
# Fallback to first value
|
# Fallback to first value
|
||||||
return list(values.values())[0]
|
return list(values.values())[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
645
tests/test_helpers.d/ynhtest_config.sh
Normal file
645
tests/test_helpers.d/ynhtest_config.sh
Normal file
|
@ -0,0 +1,645 @@
|
||||||
|
|
||||||
|
#################
|
||||||
|
# _ __ _ _ #
|
||||||
|
# | '_ \| | | | #
|
||||||
|
# | |_) | |_| | #
|
||||||
|
# | .__/ \__, | #
|
||||||
|
# | | __/ | #
|
||||||
|
# |_| |___/ #
|
||||||
|
# #
|
||||||
|
#################
|
||||||
|
|
||||||
|
_read_py() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
python3 -c "exec(open('$file').read()); print($key)"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_read_py() {
|
||||||
|
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.py"
|
||||||
|
|
||||||
|
cat << EOF > $dummy_dir/dummy.py
|
||||||
|
# Some comment
|
||||||
|
FOO = None
|
||||||
|
ENABLED = False
|
||||||
|
# TITLE = "Old title"
|
||||||
|
TITLE = "Lorem Ipsum"
|
||||||
|
THEME = "colib'ris"
|
||||||
|
EMAIL = "root@example.com"
|
||||||
|
PORT = 1234
|
||||||
|
URL = 'https://yunohost.org'
|
||||||
|
DICT = {}
|
||||||
|
DICT['ldap_base'] = "ou=users,dc=yunohost,dc=org"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "FOO")" == "None"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "FOO")" == "None"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "ENABLED")" == "False"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ENABLED")" == "False"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "TITLE")" == "Lorem Ipsum"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "TITLE")" == "Lorem Ipsum"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "THEME")" == "colib'ris"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "THEME")" == "colib'ris"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "EMAIL")" == "root@example.com"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "EMAIL")" == "root@example.com"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "PORT")" == "1234"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "PORT")" == "1234"
|
||||||
|
|
||||||
|
test "$(_read_py "$file" "URL")" == "https://yunohost.org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "URL")" == "https://yunohost.org"
|
||||||
|
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
! _read_py "$file" "NONEXISTENT"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "NONEXISTENT")" == "YNH_NULL"
|
||||||
|
|
||||||
|
! _read_py "$file" "ENABLE"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ENABLE")" == "YNH_NULL"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_write_py() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.py"
|
||||||
|
|
||||||
|
cat << EOF > $dummy_dir/dummy.py
|
||||||
|
# Some comment
|
||||||
|
FOO = None
|
||||||
|
ENABLED = False
|
||||||
|
# TITLE = "Old title"
|
||||||
|
TITLE = "Lorem Ipsum"
|
||||||
|
THEME = "colib'ris"
|
||||||
|
EMAIL = "root@example.com"
|
||||||
|
PORT = 1234
|
||||||
|
URL = 'https://yunohost.org'
|
||||||
|
DICT = {}
|
||||||
|
DICT['ldap_base'] = "ou=users,dc=yunohost,dc=org"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "FOO" "bar"
|
||||||
|
#test "$(_read_py "$file" "FOO")" == "bar" # FIXME FIXME FIXME
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "FOO")" == "bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ENABLED" "True"
|
||||||
|
test "$(_read_py "$file" "ENABLED")" == "True"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ENABLED")" == "True"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "TITLE" "Foo Bar"
|
||||||
|
test "$(_read_py "$file" "TITLE")" == "Foo Bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "TITLE")" == "Foo Bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "THEME" "super-awesome-theme"
|
||||||
|
test "$(_read_py "$file" "THEME")" == "super-awesome-theme"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "THEME")" == "super-awesome-theme"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "EMAIL" "sam@domain.tld"
|
||||||
|
test "$(_read_py "$file" "EMAIL")" == "sam@domain.tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "EMAIL")" == "sam@domain.tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "PORT" "5678"
|
||||||
|
test "$(_read_py "$file" "PORT")" == "5678"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "PORT")" == "5678"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "URL" "https://domain.tld/foobar"
|
||||||
|
test "$(_read_py "$file" "URL")" == "https://domain.tld/foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "URL")" == "https://domain.tld/foobar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ldap_base" "ou=users,dc=yunohost,dc=org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "NONEXISTENT" "foobar"
|
||||||
|
! _read_py "$file" "NONEXISTENT"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "NONEXISTENT")" == "YNH_NULL"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ENABLE" "foobar"
|
||||||
|
! _read_py "$file" "ENABLE"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ENABLE")" == "YNH_NULL"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
###############
|
||||||
|
# _ _ #
|
||||||
|
# (_) (_) #
|
||||||
|
# _ _ __ _ #
|
||||||
|
# | | '_ \| | #
|
||||||
|
# | | | | | | #
|
||||||
|
# |_|_| |_|_| #
|
||||||
|
# #
|
||||||
|
###############
|
||||||
|
|
||||||
|
_read_ini() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
python3 -c "import configparser; c = configparser.ConfigParser(); c.read('$file'); print(c['main']['$key'])"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_read_ini() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.yml"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
# Some comment
|
||||||
|
; Another comment
|
||||||
|
[main]
|
||||||
|
foo = null
|
||||||
|
enabled = False
|
||||||
|
# title = Old title
|
||||||
|
title = Lorem Ipsum
|
||||||
|
theme = colib'ris
|
||||||
|
email = root@example.com
|
||||||
|
port = 1234
|
||||||
|
url = https://yunohost.org
|
||||||
|
[dict]
|
||||||
|
ldap_base = ou=users,dc=yunohost,dc=org
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "foo")" == "null"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "foo")" == "null"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "enabled")" == "False"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "False"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "title")" == "Lorem Ipsum"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Lorem Ipsum"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "theme")" == "colib'ris"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "colib'ris"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "email")" == "root@example.com"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "root@example.com"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "port")" == "1234"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "1234"
|
||||||
|
|
||||||
|
test "$(_read_ini "$file" "url")" == "https://yunohost.org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://yunohost.org"
|
||||||
|
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
! _read_ini "$file" "nonexistent"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
! _read_ini "$file" "enable"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_write_ini() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.ini"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
# Some comment
|
||||||
|
; Another comment
|
||||||
|
[main]
|
||||||
|
foo = null
|
||||||
|
enabled = False
|
||||||
|
# title = Old title
|
||||||
|
title = Lorem Ipsum
|
||||||
|
theme = colib'ris
|
||||||
|
email = root@example.com
|
||||||
|
port = 1234
|
||||||
|
url = https://yunohost.org
|
||||||
|
[dict]
|
||||||
|
ldap_base = ou=users,dc=yunohost,dc=org
|
||||||
|
EOF
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "foo" "bar"
|
||||||
|
test "$(_read_ini "$file" "foo")" == "bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "foo")" == "bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enabled" "True"
|
||||||
|
test "$(_read_ini "$file" "enabled")" == "True"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "True"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "title" "Foo Bar"
|
||||||
|
test "$(_read_ini "$file" "title")" == "Foo Bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Foo Bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "theme" "super-awesome-theme"
|
||||||
|
test "$(_read_ini "$file" "theme")" == "super-awesome-theme"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "super-awesome-theme"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "email" "sam@domain.tld"
|
||||||
|
test "$(_read_ini "$file" "email")" == "sam@domain.tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "sam@domain.tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "port" "5678"
|
||||||
|
test "$(_read_ini "$file" "port")" == "5678"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "5678"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "url" "https://domain.tld/foobar"
|
||||||
|
test "$(_read_ini "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ldap_base" "ou=users,dc=yunohost,dc=org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "nonexistent" "foobar"
|
||||||
|
! _read_ini "$file" "nonexistent"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enable" "foobar"
|
||||||
|
! _read_ini "$file" "enable"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# _ #
|
||||||
|
# | | #
|
||||||
|
# _ _ __ _ _ __ ___ | | #
|
||||||
|
# | | | |/ _` | '_ ` _ \| | #
|
||||||
|
# | |_| | (_| | | | | | | | #
|
||||||
|
# \__, |\__,_|_| |_| |_|_| #
|
||||||
|
# __/ | #
|
||||||
|
# |___/ #
|
||||||
|
# #
|
||||||
|
#############################
|
||||||
|
|
||||||
|
_read_yaml() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
python3 -c "import yaml; print(yaml.safe_load(open('$file'))['$key'])"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_read_yaml() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.yml"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
# Some comment
|
||||||
|
foo:
|
||||||
|
enabled: false
|
||||||
|
# title: old title
|
||||||
|
title: Lorem Ipsum
|
||||||
|
theme: colib'ris
|
||||||
|
email: root@example.com
|
||||||
|
port: 1234
|
||||||
|
url: https://yunohost.org
|
||||||
|
dict:
|
||||||
|
ldap_base: ou=users,dc=yunohost,dc=org
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "foo")" == "None"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "foo")" == ""
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "enabled")" == "False"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "false"
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "title")" == "Lorem Ipsum"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Lorem Ipsum"
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "theme")" == "colib'ris"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "colib'ris"
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "email")" == "root@example.com"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "root@example.com"
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "port")" == "1234"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "1234"
|
||||||
|
|
||||||
|
test "$(_read_yaml "$file" "url")" == "https://yunohost.org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://yunohost.org"
|
||||||
|
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
! _read_yaml "$file" "nonexistent"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
! _read_yaml "$file" "enable"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ynhtest_config_write_yaml() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.yml"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
# Some comment
|
||||||
|
foo:
|
||||||
|
enabled: false
|
||||||
|
# title: old title
|
||||||
|
title: Lorem Ipsum
|
||||||
|
theme: colib'ris
|
||||||
|
email: root@example.com
|
||||||
|
port: 1234
|
||||||
|
url: https://yunohost.org
|
||||||
|
dict:
|
||||||
|
ldap_base: ou=users,dc=yunohost,dc=org
|
||||||
|
EOF
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "foo" "bar"
|
||||||
|
# cat $dummy_dir/dummy.yml # to debug
|
||||||
|
#! test "$(_read_yaml "$file" "foo")" == "bar" # FIXME FIXME FIXME : writing broke the yaml syntax... "foo:bar" (no space aftr :)
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "foo")" == "bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enabled" "true"
|
||||||
|
test "$(_read_yaml "$file" "enabled")" == "True"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "true"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "title" "Foo Bar"
|
||||||
|
test "$(_read_yaml "$file" "title")" == "Foo Bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Foo Bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "theme" "super-awesome-theme"
|
||||||
|
test "$(_read_yaml "$file" "theme")" == "super-awesome-theme"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "super-awesome-theme"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "email" "sam@domain.tld"
|
||||||
|
test "$(_read_yaml "$file" "email")" == "sam@domain.tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "sam@domain.tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "port" "5678"
|
||||||
|
test "$(_read_yaml "$file" "port")" == "5678"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "5678"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "url" "https://domain.tld/foobar"
|
||||||
|
test "$(_read_yaml "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ldap_base" "ou=foobar,dc=domain,dc=tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=foobar,dc=domain,dc=tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "nonexistent" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enable" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# _ #
|
||||||
|
# (_) #
|
||||||
|
# _ ___ ___ _ __ #
|
||||||
|
# | / __|/ _ \| '_ \ #
|
||||||
|
# | \__ \ (_) | | | | #
|
||||||
|
# | |___/\___/|_| |_| #
|
||||||
|
# _/ | #
|
||||||
|
# |__/ #
|
||||||
|
# #
|
||||||
|
#########################
|
||||||
|
|
||||||
|
_read_json() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
python3 -c "import json; print(json.load(open('$file'))['$key'])"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_read_json() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.json"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
{
|
||||||
|
"foo": null,
|
||||||
|
"enabled": false,
|
||||||
|
"title": "Lorem Ipsum",
|
||||||
|
"theme": "colib'ris",
|
||||||
|
"email": "root@example.com",
|
||||||
|
"port": 1234,
|
||||||
|
"url": "https://yunohost.org",
|
||||||
|
"dict": {
|
||||||
|
"ldap_base": "ou=users,dc=yunohost,dc=org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "foo")" == "None"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "foo")" == "null," # FIXME FIXME FIXME trailing ,
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "enabled")" == "False"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "false," # FIXME FIXME FIXME trailing ,
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "title")" == "Lorem Ipsum"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Lorem Ipsum"
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "theme")" == "colib'ris"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "colib'ris"
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "email")" == "root@example.com"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "root@example.com"
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "port")" == "1234"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "1234," # FIXME FIXME FIXME trailing ,
|
||||||
|
|
||||||
|
test "$(_read_json "$file" "url")" == "https://yunohost.org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://yunohost.org"
|
||||||
|
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
! _read_json "$file" "nonexistent"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
! _read_json "$file" "enable"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ynhtest_config_write_json() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.json"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
{
|
||||||
|
"foo": null,
|
||||||
|
"enabled": false,
|
||||||
|
"title": "Lorem Ipsum",
|
||||||
|
"theme": "colib'ris",
|
||||||
|
"email": "root@example.com",
|
||||||
|
"port": 1234,
|
||||||
|
"url": "https://yunohost.org",
|
||||||
|
"dict": {
|
||||||
|
"ldap_base": "ou=users,dc=yunohost,dc=org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "foo" "bar"
|
||||||
|
#cat $file
|
||||||
|
#test "$(_read_json "$file" "foo")" == "bar" # FIXME FIXME FIXME
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "foo")" == "bar"
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "enabled" "true"
|
||||||
|
#test "$(_read_json "$file" "enabled")" == "True" # FIXME FIXME FIXME
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "enabled")" == "true"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "title" "Foo Bar"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_json "$file" "title")" == "Foo Bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Foo Bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "theme" "super-awesome-theme"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_json "$file" "theme")" == "super-awesome-theme"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "super-awesome-theme"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "email" "sam@domain.tld"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_json "$file" "email")" == "sam@domain.tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "sam@domain.tld"
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "port" "5678"
|
||||||
|
#cat $file
|
||||||
|
#test "$(_read_json "$file" "port")" == "5678" # FIXME FIXME FIXME
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "port")" == "5678"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "url" "https://domain.tld/foobar"
|
||||||
|
test "$(_read_json "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ldap_base" "ou=foobar,dc=domain,dc=tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=foobar,dc=domain,dc=tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "nonexistent" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enable" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "enabled")" == "true" # FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# _ #
|
||||||
|
# | | #
|
||||||
|
# _ __ | |__ _ __ #
|
||||||
|
# | '_ \| '_ \| '_ \ #
|
||||||
|
# | |_) | | | | |_) | #
|
||||||
|
# | .__/|_| |_| .__/ #
|
||||||
|
# | | | | #
|
||||||
|
# |_| |_| #
|
||||||
|
# #
|
||||||
|
#######################
|
||||||
|
|
||||||
|
_read_php() {
|
||||||
|
local file="$1"
|
||||||
|
local key="$2"
|
||||||
|
php -r "include '$file'; echo var_export(\$$key);" | sed "s/^'//g" | sed "s/'$//g"
|
||||||
|
}
|
||||||
|
|
||||||
|
ynhtest_config_read_php() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.php"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
<?php
|
||||||
|
// Some comment
|
||||||
|
\$foo = NULL;
|
||||||
|
\$enabled = false;
|
||||||
|
// \$title = "old title";
|
||||||
|
\$title = "Lorem Ipsum";
|
||||||
|
\$theme = "colib'ris";
|
||||||
|
\$email = "root@example.com";
|
||||||
|
\$port = 1234;
|
||||||
|
\$url = "https://yunohost.org";
|
||||||
|
\$dict = [
|
||||||
|
'ldap_base' => "ou=users,dc=yunohost,dc=org",
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "foo")" == "NULL"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "foo")" == "NULL;" # FIXME FIXME FIXME trailing ;
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "enabled")" == "false"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enabled")" == "false;" # FIXME FIXME FIXME trailing ;
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "title")" == "Lorem Ipsum"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Lorem Ipsum"
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "theme")" == "colib\\'ris"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "colib'ris"
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "email")" == "root@example.com"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "root@example.com"
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "port")" == "1234"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "port")" == "1234;" # FIXME FIXME FIXME trailing ;
|
||||||
|
|
||||||
|
test "$(_read_php "$file" "url")" == "https://yunohost.org"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://yunohost.org"
|
||||||
|
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=users,dc=yunohost,dc=org"
|
||||||
|
|
||||||
|
! _read_php "$file" "nonexistent"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
! _read_php "$file" "enable"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ynhtest_config_write_php() {
|
||||||
|
local dummy_dir="$(mktemp -d -p $VAR_WWW)"
|
||||||
|
file="$dummy_dir/dummy.php"
|
||||||
|
|
||||||
|
cat << EOF > $file
|
||||||
|
<?php
|
||||||
|
// Some comment
|
||||||
|
\$foo = NULL;
|
||||||
|
\$enabled = false;
|
||||||
|
// \$title = "old title";
|
||||||
|
\$title = "Lorem Ipsum";
|
||||||
|
\$theme = "colib'ris";
|
||||||
|
\$email = "root@example.com";
|
||||||
|
\$port = 1234;
|
||||||
|
\$url = "https://yunohost.org";
|
||||||
|
\$dict = [
|
||||||
|
'ldap_base' => "ou=users,dc=yunohost,dc=org",
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "foo" "bar"
|
||||||
|
#cat $file
|
||||||
|
#test "$(_read_php "$file" "foo")" == "bar"
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "foo")" == "bar" # FIXME FIXME FIXME
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "enabled" "true"
|
||||||
|
#cat $file
|
||||||
|
#test "$(_read_php "$file" "enabled")" == "true"
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "enabled")" == "true" # FIXME FIXME FIXME
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "title" "Foo Bar"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_php "$file" "title")" == "Foo Bar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "title")" == "Foo Bar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "theme" "super-awesome-theme"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_php "$file" "theme")" == "super-awesome-theme"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "theme")" == "super-awesome-theme"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "email" "sam@domain.tld"
|
||||||
|
cat $file
|
||||||
|
test "$(_read_php "$file" "email")" == "sam@domain.tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "email")" == "sam@domain.tld"
|
||||||
|
|
||||||
|
#ynh_write_var_in_file "$file" "port" "5678"
|
||||||
|
#cat $file
|
||||||
|
#test "$(_read_php "$file" "port")" == "5678" # FIXME FIXME FIXME
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "port")" == "5678"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "url" "https://domain.tld/foobar"
|
||||||
|
test "$(_read_php "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "url")" == "https://domain.tld/foobar"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "ldap_base" "ou=foobar,dc=domain,dc=tld"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "ldap_base")" == "ou=foobar,dc=domain,dc=tld"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "nonexistent" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "nonexistent")" == "YNH_NULL"
|
||||||
|
|
||||||
|
ynh_write_var_in_file "$file" "enable" "foobar"
|
||||||
|
test "$(ynh_read_var_in_file "$file" "enable")" == "YNH_NULL"
|
||||||
|
#test "$(ynh_read_var_in_file "$file" "enabled")" == "true" # FIXME
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue