From 7a60703ef58b17de4e1cde9e7861fa4e50298e6e Mon Sep 17 00:00:00 2001 From: axolotle Date: Tue, 18 Apr 2023 20:13:01 +0200 Subject: [PATCH] configpanel: update _ask --- src/utils/configpanel.py | 95 +++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/utils/configpanel.py b/src/utils/configpanel.py index 22ea5b3b8..22318e5e0 100644 --- a/src/utils/configpanel.py +++ b/src/utils/configpanel.py @@ -19,7 +19,6 @@ import glob import os import re -import urllib.parse from collections import OrderedDict from logging import getLogger from typing import TYPE_CHECKING, Any, Literal, Sequence, Type, Union @@ -36,19 +35,19 @@ from yunohost.utils.form import ( BaseOption, BaseReadonlyOption, FileOption, - FormModel, OptionsModel, OptionType, Translation, - ask_questions_and_parse_answers, build_form, evaluate_simple_js_expression, parse_prefilled_values, + prompt_or_validate_form, ) from yunohost.utils.i18n import _value_for_locale if TYPE_CHECKING: from pydantic.fields import ModelField + from yunohost.utils.form import FormModel, Hooks logger = getLogger("yunohost.configpanel") @@ -279,7 +278,8 @@ class ConfigPanel: save_mode = "full" filter_key: "FilterKey" = (None, None, None) config: Union[ConfigPanelModel, None] = None - form: Union[FormModel, None] = None + form: Union["FormModel", None] = None + hooks: "Hooks" = {} @classmethod def list(cls): @@ -602,7 +602,7 @@ class ConfigPanel: def _get_config_panel( self, prevalidate: bool = False - ) -> tuple[ConfigPanelModel, FormModel]: + ) -> tuple[ConfigPanelModel, "FormModel"]: raw_config = self._get_partial_raw_config() config = ConfigPanelModel(**raw_config) config, raw_settings = self._get_partial_raw_settings_and_mutate_config(config) @@ -623,58 +623,62 @@ class ConfigPanel: return (config, settings) - def _ask(self, action=None): + def ask( + self, + config: ConfigPanelModel, + settings: "FormModel", + prefilled_answers: dict[str, Any] = {}, + action_id: Union[str, None] = None, + hooks: "Hooks" = {}, + ) -> "FormModel": + # FIXME could be turned into a staticmethod logger.debug("Ask unanswered question and prevalidate data") - if "i18n" in self.config: - for panel, section, option in self._iterate(): - if "ask" not in option: - option["ask"] = m18n.n(self.config["i18n"] + "_" + option["id"]) - # auto add i18n help text if present in locales - if m18n.key_exists(self.config["i18n"] + "_" + option["id"] + "_help"): - option["help"] = m18n.n( - self.config["i18n"] + "_" + option["id"] + "_help" - ) + interactive = Moulinette.interface.type == "cli" and os.isatty(1) - 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")) + if interactive: + config.translate() - for panel, section, obj in self._iterate(["panel", "section"]): - if ( - section - and section.get("visible") - and not evaluate_simple_js_expression( - section["visible"], context=self.future_values + for panel in config.panels: + if interactive: + Moulinette.display( + colorize(f"\n{'='*40}\n>>>> {panel.name}\n{'='*40}", "purple") ) - ): - continue - # Ugly hack to skip action section ... except when when explicitly running actions - if not action: - if section and section["is_action_section"]: + # A section or option may only evaluate its conditions (`visible` + # and `enabled`) with its panel's local context that is built + # prompt after prompt. + # That means that a condition can only reference options of its + # own panel and options that are previously defined. + context: dict[str, Any] = {} + + for section in panel.sections: + if ( + action_id is None and section.is_action_section + ) or not section.is_visible(context): + # FIXME useless? + Moulinette.display("Skipping section '{panel.id}.{section.id}'…") continue - if panel == obj: - name = _value_for_locale(panel["name"]) - display_header(f"\n{'='*40}\n>>>> {name}\n{'='*40}") - else: - name = _value_for_locale(section["name"]) - if name: - display_header(f"\n# {name}") - elif section: + if interactive and section.name: + Moulinette.display(colorize(f"\n# {section.name}", "purple")) + # filter action section options in case of multiple buttons - section["options"] = [ + options = [ option - for option in section["options"] - if option.get("type", OptionType.string) != OptionType.button - or option["id"] == action + for option in section.options + if option.type is not OptionType.button or option.id == action_id ] - if panel == obj: - continue + settings = prompt_or_validate_form( + options, + settings, + prefilled_answers=prefilled_answers, + context=context, + hooks=hooks, + ) +<<<<<<< HEAD # Check and ask unanswered questions prefilled_answers = self.args.copy() prefilled_answers.update(self.new_values) @@ -692,6 +696,9 @@ class ConfigPanel: if not question.readonly and question.value is not None } ) +======= + return settings +>>>>>>> be777b928 (configpanel: update _ask) def _apply(self): logger.info("Saving the new configuration...")