diff --git a/data/helpers.d/config b/data/helpers.d/config index 52454ff91..6223a17b2 100644 --- a/data/helpers.d/config +++ b/data/helpers.d/config @@ -67,7 +67,7 @@ EOL local source_key="$(echo "$source" | cut -d: -f1)" source_key=${source_key:-$short_setting} 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 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/)" 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 # We stored the info in settings in order to be able to upgrade the app diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 14e7ebe4a..3389101a6 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -475,7 +475,7 @@ ynh_replace_vars () { # 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: -k, --key= - the key to get # @@ -504,8 +504,9 @@ ynh_replace_vars () { # USER = 8102 # user = 'https://donate.local' # CUSTOM['user'] = 'YunoHost' +# # 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. local legacy_args=fk local -A args_array=( [f]=file= [k]=key= ) @@ -515,10 +516,9 @@ ynh_get_var() { ynh_handle_getopts_args "$@" 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}" if [[ "$first_char" == '"' ]] ; then 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...) # -# 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: -k, --key= - the key to set # | arg: -v, --value= - the value to set # # 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. local legacy_args=fkv local -A args_array=( [f]=file= [k]=key= [v]=value=) @@ -547,7 +547,7 @@ ynh_set_var() { # 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 '^[ \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 first_char="${crazy_value:0:1}" diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py index 4ab5a6c0d..177ec590f 100644 --- a/data/hooks/diagnosis/80-apps.py +++ b/data/hooks/diagnosis/80-apps.py @@ -6,6 +6,7 @@ from yunohost.app import app_list from yunohost.diagnosis import Diagnoser + class AppDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] @@ -30,13 +31,17 @@ class AppDiagnoser(Diagnoser): if not app["issues"]: 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( meta={"test": "apps", "app": app["name"]}, status=level, summary="diagnosis_apps_issue", - details=[issue[1] for issue in app["issues"]] + details=[issue[1] for issue in app["issues"]], ) def issues(self, app): @@ -45,14 +50,19 @@ class AppDiagnoser(Diagnoser): if not app.get("from_catalog") or app["from_catalog"].get("state") != "working": 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") elif app["from_catalog"]["level"] <= 4: yield ("warning", "diagnosis_apps_bad_quality") # 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."): yield ("error", "diagnosis_apps_outdated_ynh_requirement") @@ -64,11 +74,21 @@ class AppDiagnoser(Diagnoser): "yunohost tools port-available", ] 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") - old_arg_regex = r'^domain=\${?[0-9]' - if os.system(f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install") == 0: + old_arg_regex = r"^domain=\${?[0-9]" + if ( + os.system( + f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install" + ) + == 0 + ): yield ("error", "diagnosis_apps_deprecated_practices") diff --git a/src/yunohost/app.py b/src/yunohost/app.py index e9df193c7..bc84d0da0 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -36,7 +36,6 @@ import urllib.parse import tempfile from collections import OrderedDict -from moulinette.interfaces.cli import colorize from moulinette import Moulinette, m18n from moulinette.core import MoulinetteError 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.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.error import YunohostError, YunohostValidationError 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!") -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 """ - config = AppConfigPanel(app) - return config.get(key, mode) + config_ = AppConfigPanel(app) + return config_.get(key, mode) @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 """ - config = AppConfigPanel(app) + config_ = AppConfigPanel(app) Question.operation_logger = operation_logger operation_logger.start() - result = config.set(key, value, args, args_file) + result = config_.set(key, value, args, args_file) if "errors" not in result: operation_logger.success() return result + class AppConfigPanel(ConfigPanel): def __init__(self, app): @@ -1791,10 +1797,10 @@ class AppConfigPanel(ConfigPanel): super().__init__(config_path=config_path) def _load_current_values(self): - self.values = self._call_config_script('show') + self.values = self._call_config_script("show") 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={}): from yunohost.hook import hook_exec @@ -1814,22 +1820,23 @@ ynh_app_config_run $1 # Call config script to extract current values logger.debug(f"Calling '{action}' action from config script") app_id, app_instance_nb = _parse_app_instance_name(self.app) - env.update({ - "app_id": app_id, - "app": self.app, - "app_instance_nb": str(app_instance_nb), - }) - - ret, values = hook_exec( - config_script, args=[action], env=env + env.update( + { + "app_id": app_id, + "app": self.app, + "app_instance_nb": str(app_instance_nb), + } ) + + ret, values = hook_exec(config_script, args=[action], env=env) if ret != 0: - if action == 'show': + if action == "show": raise YunohostError("app_config_unable_to_read_values") else: raise YunohostError("app_config_unable_to_apply_values_correctly") return values + def _get_all_installed_apps_id(): """ Return something like: @@ -2455,9 +2462,6 @@ def _parse_args_for_action(action, args={}): return parse_args_in_yunohost_format(args, action_args) - - - def _validate_and_normalize_webpath(args_dict, app_folder): # If there's only one "domain" and "path", validate that domain/path diff --git a/src/yunohost/utils/config.py b/src/yunohost/utils/config.py index 34883dcf7..8fcf493ed 100644 --- a/src/yunohost/utils/config.py +++ b/src/yunohost/utils/config.py @@ -21,9 +21,9 @@ import os import re -import toml import urllib.parse import tempfile +import shutil from collections import OrderedDict from moulinette.interfaces.cli import colorize @@ -37,16 +37,14 @@ from moulinette.utils.filesystem import ( 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.error import YunohostError, YunohostValidationError logger = getActionLogger("yunohost.config") CONFIG_PANEL_VERSION_SUPPORTED = 1.0 -class ConfigPanel: +class ConfigPanel: def __init__(self, config_path, save_path=None): self.config_path = config_path self.save_path = save_path @@ -54,8 +52,8 @@ class ConfigPanel: self.values = {} self.new_values = {} - def get(self, key='', mode='classic'): - self.filter_key = key or '' + def get(self, key="", mode="classic"): + self.filter_key = key or "" # Read config panel toml self._get_config_panel() @@ -68,12 +66,12 @@ class ConfigPanel: self._hydrate() # Format result in full mode - if mode == 'full': + if mode == "full": return self.config # In 'classic' mode, we display the current value if key refer to an option - if self.filter_key.count('.') == 2 and mode == 'classic': - option = self.filter_key.split('.')[-1] + if self.filter_key.count(".") == 2 and mode == "classic": + option = self.filter_key.split(".")[-1] return self.values.get(option, None) # Format result in 'classic' or 'export' mode @@ -81,17 +79,17 @@ class ConfigPanel: result = {} for panel, section, option in self._iterate(): key = f"{panel['id']}.{section['id']}.{option['id']}" - if mode == 'export': - result[option['id']] = option.get('current_value') + if mode == "export": + result[option["id"]] = option.get("current_value") else: - result[key] = { 'ask': _value_for_locale(option['ask']) } - if 'current_value' in option: - result[key]['value'] = option['current_value'] + result[key] = {"ask": _value_for_locale(option["ask"])} + if "current_value" in option: + result[key]["value"] = option["current_value"] return result 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 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: 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") # 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) - self.args = { key: ','.join(value_) for key, value_ in args.items() } + args = urllib.parse.parse_qs(args or "", keep_blank_values=True) + self.args = {key: ",".join(value_) for key, value_ in args.items()} if args_file: # 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: - self.args = {self.filter_key.split('.')[-1]: value} + self.args = {self.filter_key.split(".")[-1]: value} # Read or get values and hydrate the config self._load_current_values() @@ -144,7 +142,7 @@ class ConfigPanel: if self.errors: return { - "errors": errors, + "errors": self.errors, } self._reload_services() @@ -155,10 +153,9 @@ class ConfigPanel: def _get_toml(self): return read_toml(self.config_path) - def _get_config_panel(self): # Split filter_key - filter_key = dict(enumerate(self.filter_key.split('.'))) + filter_key = dict(enumerate(self.filter_key.split("."))) if len(filter_key) > 3: raise YunohostError("config_too_much_sub_keys") @@ -174,20 +171,18 @@ class ConfigPanel: # Transform toml format into internal format defaults = { - 'toml': { - 'version': 1.0 - }, - 'panels': { - 'name': '', - 'services': [], - 'actions': {'apply': {'en': 'Apply'}} - }, # help - 'sections': { - 'name': '', - 'services': [], - 'optional': True - }, # visibleIf help - 'options': {} + "toml": {"version": 1.0}, + "panels": { + "name": "", + "services": [], + "actions": {"apply": {"en": "Apply"}}, + }, # help + "sections": { + "name": "", + "services": [], + "optional": True, + }, # visibleIf help + "options": {} # ask type source help helpLink example style icon placeholder visibleIf # 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 i = list(defaults).index(node_type) 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(): # 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 if search_key and key != search_key: continue subnode = convert(value, subnode_type) - subnode['id'] = key - if node_type == 'sections': - subnode['name'] = key # legacy - subnode.setdefault('optional', toml_node.get('optional', True)) + subnode["id"] = key + if node_type == "sections": + subnode["name"] = key # legacy + subnode.setdefault("optional", toml_node.get("optional", True)) node.setdefault(subnode_type, []).append(subnode) # Key/value are a property else: # 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 - self.config = convert(toml_config_panel, 'toml') + self.config = convert(toml_config_panel, "toml") try: - self.config['panels'][0]['sections'][0]['options'][0] + self.config["panels"][0]["sections"][0]["options"][0] except (KeyError, IndexError): raise YunohostError( "config_empty_or_bad_filter_key", filter_key=self.filter_key @@ -242,36 +243,41 @@ class ConfigPanel: # Hydrating config panel with current value logger.debug("Hydrating config with current values") for _, _, option in self._iterate(): - if option['name'] not in self.values: + if option["name"] not in self.values: continue - value = self.values[option['name']] + value = self.values[option["name"]] # In general, the value is just a simple value. # 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) return self.values def _ask(self): logger.debug("Ask unanswered question and prevalidate data") + def display_header(message): - """ CLI panel/section header display - """ - if Moulinette.interface.type == 'cli' and self.filter_key.count('.') < 2: - Moulinette.display(colorize(message, 'purple')) - for panel, section, obj in self._iterate(['panel', 'section']): + """CLI panel/section header display""" + if Moulinette.interface.type == "cli" and self.filter_key.count(".") < 2: + Moulinette.display(colorize(message, "purple")) + + for panel, section, obj in self._iterate(["panel", "section"]): if panel == obj: - name = _value_for_locale(panel['name']) + name = _value_for_locale(panel["name"]) display_header(f"\n{'='*40}\n>>>> {name}\n{'='*40}") continue - name = _value_for_locale(section['name']) + name = _value_for_locale(section["name"]) display_header(f"\n# {name}") # Check and ask unanswered questions - self.new_values.update(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.update( + 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 + } def _apply(self): logger.info("Running config script...") @@ -281,36 +287,37 @@ class ConfigPanel: # Save the settings to the .yaml file write_to_yaml(self.save_path, self.new_values) - def _reload_services(self): + + from yunohost.service import _run_service_command, _get_services + logger.info("Reloading services...") services_to_reload = set() - for panel, section, obj in self._iterate(['panel', 'section', 'option']): - services_to_reload |= set(obj.get('services', [])) + for panel, section, obj in self._iterate(["panel", "section", "option"]): + services_to_reload |= set(obj.get("services", [])) 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: - if '__APP__': - service = service.replace('__APP__', self.app) + if "__APP__": + service = service.replace("__APP__", self.app) 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() - test_conf = services[service].get('test_conf', 'true') - errors = check_output(f"{test_conf}; exit 0") if test_conf else '' + test_conf = services[service].get("test_conf", "true") + errors = check_output(f"{test_conf}; exit 0") if test_conf else "" raise YunohostError( - "config_failed_service_reload", - service=service, errors=errors + "config_failed_service_reload", service=service, errors=errors ) - def _iterate(self, trigger=['option']): + def _iterate(self, trigger=["option"]): for panel in self.config.get("panels", []): - if 'panel' in trigger: + if "panel" in trigger: yield (panel, None, panel) for section in panel.get("sections", []): - if 'section' in trigger: + if "section" in trigger: yield (panel, section, section) - if 'option' in trigger: + if "option" in trigger: for option in section.get("options", []): yield (panel, section, option) @@ -327,17 +334,17 @@ class YunoHostArgumentFormatParser(object): parsed_question = Question() 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.current_value = question.get("current_value") parsed_question.optional = question.get("optional", False) parsed_question.choices = question.get("choices", []) 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.helpLink = question.get("helpLink") 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 if parsed_question.default == "": @@ -350,7 +357,7 @@ class YunoHostArgumentFormatParser(object): while True: # 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( question ) @@ -368,10 +375,9 @@ class YunoHostArgumentFormatParser(object): is_password=self.hide_user_input_in_prompt, confirm=self.hide_user_input_in_prompt, prefill=prefill, - is_multiline=(question.type == "text") + is_multiline=(question.type == "text"), ) - # Apply default value if question.value in [None, ""] and question.default is not None: question.value = ( @@ -384,9 +390,9 @@ class YunoHostArgumentFormatParser(object): try: self._prevalidate(question) except YunohostValidationError as e: - if Moulinette.interface.type== 'api': + if Moulinette.interface.type == "api": raise - Moulinette.display(str(e), 'error') + Moulinette.display(str(e), "error") question.value = None continue break @@ -398,17 +404,17 @@ class YunoHostArgumentFormatParser(object): def _prevalidate(self, question): if question.value in [None, ""] and not question.optional: - raise YunohostValidationError( - "app_argument_required", name=question.name - ) + raise YunohostValidationError("app_argument_required", name=question.name) # we have an answer, do some post checks if question.value is not None: if question.choices and question.value not in question.choices: 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( - question.pattern['error'], + question.pattern["error"], name=question.name, value=question.value, ) @@ -434,7 +440,7 @@ class YunoHostArgumentFormatParser(object): text_for_user_input_in_cli += _value_for_locale(question.help) if question.helpLink: 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']}" return text_for_user_input_in_cli @@ -467,18 +473,18 @@ class StringArgumentParser(YunoHostArgumentFormatParser): argument_type = "string" default_value = "" + class TagsArgumentParser(YunoHostArgumentFormatParser): argument_type = "tags" def _prevalidate(self, question): values = question.value - for value in values.split(','): + for value in values.split(","): question.value = value super()._prevalidate(question) question.value = values - class PasswordArgumentParser(YunoHostArgumentFormatParser): hide_user_input_in_prompt = True argument_type = "password" @@ -522,9 +528,7 @@ class BooleanArgumentParser(YunoHostArgumentFormatParser): default_value = False def parse_question(self, question, user_answers): - question = super().parse_question( - question, user_answers - ) + question = super().parse_question(question, user_answers) if question.default is None: question.default = False @@ -616,11 +620,9 @@ class NumberArgumentParser(YunoHostArgumentFormatParser): default_value = "" def parse_question(self, question, user_answers): - question_parsed = super().parse_question( - question, user_answers - ) - question_parsed.min = question.get('min', None) - question_parsed.max = question.get('max', None) + question_parsed = super().parse_question(question, user_answers) + question_parsed.min = question.get("min", None) + question_parsed.max = question.get("max", None) if question_parsed.default is None: question_parsed.default = 0 @@ -628,19 +630,27 @@ class NumberArgumentParser(YunoHostArgumentFormatParser): def _prevalidate(self, 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( - "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: 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: 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): @@ -660,29 +670,28 @@ class DisplayTextArgumentParser(YunoHostArgumentFormatParser): readonly = True def parse_question(self, question, user_answers): - question_parsed = super().parse_question( - question, user_answers - ) + question_parsed = super().parse_question(question, user_answers) question_parsed.optional = True - question_parsed.style = question.get('style', 'info') + question_parsed.style = question.get("style", "info") return question_parsed 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 = { - 'success': 'green', - 'info': 'cyan', - 'warning': 'yellow', - 'danger': 'red' + "success": "green", + "info": "cyan", + "warning": "yellow", + "danger": "red", } return colorize(m18n.g(question.style), color[question.style]) + f" {text}" else: return text + class FileArgumentParser(YunoHostArgumentFormatParser): argument_type = "file" upload_dirs = [] @@ -690,60 +699,77 @@ class FileArgumentParser(YunoHostArgumentFormatParser): @classmethod def clean_upload_dirs(cls): # Delete files uploaded from API - if Moulinette.interface.type== 'api': + if Moulinette.interface.type == "api": for upload_dir in cls.upload_dirs: if os.path.exists(upload_dir): shutil.rmtree(upload_dir) def parse_question(self, question, user_answers): - question_parsed = super().parse_question( - question, user_answers - ) - if question.get('accept'): - question_parsed.accept = question.get('accept').replace(' ', '').split(',') + question_parsed = super().parse_question(question, user_answers) + if question.get("accept"): + question_parsed.accept = question.get("accept").replace(" ", "").split(",") else: question_parsed.accept = [] - if Moulinette.interface.type== 'api': + if Moulinette.interface.type == "api": if user_answers.get(f"{question_parsed.name}[name]"): question_parsed.value = { - 'content': question_parsed.value, - 'filename': user_answers.get(f"{question_parsed.name}[name]", question_parsed.name), + "content": question_parsed.value, + "filename": user_answers.get( + f"{question_parsed.name}[name]", question_parsed.name + ), } # 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 return question_parsed def _prevalidate(self, 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( - "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 - filename = question.value if isinstance(question.value, str) else question.value['filename'] - if '.' not in filename or '.' + filename.split('.')[-1] not in question.accept: + filename = ( + 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( - "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): from base64 import b64decode + # Upload files from API # A file arg contains a string with "FILENAME:BASE64_CONTENT" if not 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] - filename = question.value['filename'] - logger.debug(f"Save uploaded file {question.value['filename']} from API into {upload_dir}") + filename = question.value["filename"] + 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 # 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): file_path = os.path.normpath(upload_dir + "/" + filename + (".%d" % i)) i += 1 - content = question.value['content'] + content = question.value["content"] try: - with open(file_path, 'wb') as f: + with open(file_path, "wb") as f: f.write(b64decode(content)) except IOError as e: raise YunohostError("cannot_write_file", file=file_path, error=str(e)) @@ -790,6 +816,7 @@ ARGUMENTS_TYPE_PARSERS = { "file": FileArgumentParser, } + def parse_args_in_yunohost_format(user_answers, argument_questions): """Parse arguments store in either manifest.json or actions.json or from a 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 return parsed_answers_dict - diff --git a/src/yunohost/utils/i18n.py b/src/yunohost/utils/i18n.py index 89d1d0b34..a0daf8181 100644 --- a/src/yunohost/utils/i18n.py +++ b/src/yunohost/utils/i18n.py @@ -18,7 +18,8 @@ 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): """ @@ -42,5 +43,3 @@ def _value_for_locale(values): # Fallback to first value return list(values.values())[0] - - diff --git a/tests/test_helpers.d/ynhtest_config.sh b/tests/test_helpers.d/ynhtest_config.sh new file mode 100644 index 000000000..7b749adf5 --- /dev/null +++ b/tests/test_helpers.d/ynhtest_config.sh @@ -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 + "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 + "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 +}