Merge branch 'dev' into enh-dns-autoconf

This commit is contained in:
Alexandre Aubin 2021-09-19 20:48:09 +02:00
commit f9b3265f71
11 changed files with 90 additions and 102 deletions

View file

@ -123,7 +123,10 @@ _ynh_app_config_apply() {
ynh_print_info --message="File '$bind_file' removed" ynh_print_info --message="File '$bind_file' removed"
else else
ynh_backup_if_checksum_is_different --file="$bind_file" ynh_backup_if_checksum_is_different --file="$bind_file"
cp "${!short_setting}" "$bind_file" if [[ "${!short_setting}" != "$bind_file" ]]
then
cp "${!short_setting}" "$bind_file"
fi
ynh_store_file_checksum --file="$bind_file" --update_only ynh_store_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' overwrited with ${!short_setting}" ynh_print_info --message="File '$bind_file' overwrited with ${!short_setting}"
fi fi

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
YNH_APP_BASEDIR=$(realpath $([[ "$(basename $0)" =~ ^backup|restore$ ]] && echo '../settings' || [[ -n "${YNH_ACTION:-}" ]] && echo '.' || echo '..' )) YNH_APP_BASEDIR=${YNH_APP_BASEDIR:-$(realpath ..)}
# Handle script crashes / failures # Handle script crashes / failures
# #

View file

@ -12,7 +12,7 @@ ynh_backup --src_path="./manually_modified_files_list"
for file in $(cat ./manually_modified_files_list) for file in $(cat ./manually_modified_files_list)
do do
ynh_backup --src_path="$file" [[ -e $file ]] && ynh_backup --src_path="$file"
done done
ynh_backup --src_path="/etc/ssowat/conf.json.persistent" ynh_backup --src_path="/etc/ssowat/conf.json.persistent"

View file

@ -10,11 +10,10 @@ routes:
Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{{ current_commit }}/doc/generate_helper_doc.py) on {{data.date}} (YunoHost version {{data.version}}) Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{{ current_commit }}/doc/generate_helper_doc.py) on {{data.date}} (YunoHost version {{data.version}})
{% for category, helpers in data.helpers %} {% for category, helpers in data.helpers %}
### {{ category.upper() }} ## {{ category.upper() }}
{% for h in helpers %} {% for h in helpers %}
**{{ h.name }}**<br/> #### {{ h.name }}
[details summary="<i>{{ h.brief }}</i>" class="helper-card-subtitle text-muted"] [details summary="<i>{{ h.brief }}</i>" class="helper-card-subtitle text-muted"]
<p></p>
**Usage**: `{{ h.usage }}` **Usage**: `{{ h.usage }}`
{%- if h.args %} {%- if h.args %}

View file

@ -150,7 +150,7 @@
"config_validate_color": "Should be a valid RGB hexadecimal color", "config_validate_color": "Should be a valid RGB hexadecimal color",
"config_validate_date": "Should be a valid date like in the format YYYY-MM-DD", "config_validate_date": "Should be a valid date like in the format YYYY-MM-DD",
"config_validate_email": "Should be a valid email", "config_validate_email": "Should be a valid email",
"config_validate_time": "Should be a valid time like XX:YY", "config_validate_time": "Should be a valid time like HH:MM",
"config_validate_url": "Should be a valid web URL", "config_validate_url": "Should be a valid web URL",
"config_version_not_supported": "Config panel versions '{version}' are not supported.", "config_version_not_supported": "Config panel versions '{version}' are not supported.",
"confirm_app_install_danger": "DANGER! This app is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or breaks your system... If you are willing to take that risk anyway, type '{answers}'", "confirm_app_install_danger": "DANGER! This app is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or breaks your system... If you are willing to take that risk anyway, type '{answers}'",

View file

@ -56,7 +56,6 @@ from yunohost.utils import packages
from yunohost.utils.config import ( from yunohost.utils.config import (
ConfigPanel, ConfigPanel,
parse_args_in_yunohost_format, 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
@ -460,19 +459,21 @@ def app_change_url(operation_logger, app, domain, path):
# TODO: Allow to specify arguments # TODO: Allow to specify arguments
args_odict = _parse_args_from_manifest(manifest, "change_url") args_odict = _parse_args_from_manifest(manifest, "change_url")
tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app)
# Prepare env. var. to pass to script # Prepare env. var. to pass to script
env_dict = _make_environment_for_app_script(app, args=args_odict) env_dict = _make_environment_for_app_script(app, args=args_odict)
env_dict["YNH_APP_OLD_DOMAIN"] = old_domain env_dict["YNH_APP_OLD_DOMAIN"] = old_domain
env_dict["YNH_APP_OLD_PATH"] = old_path env_dict["YNH_APP_OLD_PATH"] = old_path
env_dict["YNH_APP_NEW_DOMAIN"] = domain env_dict["YNH_APP_NEW_DOMAIN"] = domain
env_dict["YNH_APP_NEW_PATH"] = path env_dict["YNH_APP_NEW_PATH"] = path
env_dict["YNH_APP_BASEDIR"] = tmp_workdir_for_app
if domain != old_domain: if domain != old_domain:
operation_logger.related_to.append(("domain", old_domain)) operation_logger.related_to.append(("domain", old_domain))
operation_logger.extra.update({"env": env_dict}) operation_logger.extra.update({"env": env_dict})
operation_logger.start() operation_logger.start()
tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app)
change_url_script = os.path.join(tmp_workdir_for_app, "scripts/change_url") change_url_script = os.path.join(tmp_workdir_for_app, "scripts/change_url")
# Execute App change_url script # Execute App change_url script
@ -623,6 +624,7 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
env_dict["YNH_APP_MANIFEST_VERSION"] = str(app_new_version) env_dict["YNH_APP_MANIFEST_VERSION"] = str(app_new_version)
env_dict["YNH_APP_CURRENT_VERSION"] = str(app_current_version) env_dict["YNH_APP_CURRENT_VERSION"] = str(app_current_version)
env_dict["NO_BACKUP_UPGRADE"] = "1" if no_safety_backup else "0" env_dict["NO_BACKUP_UPGRADE"] = "1" if no_safety_backup else "0"
env_dict["YNH_APP_BASEDIR"] = extracted_app_folder
# We'll check that the app didn't brutally edit some system configuration # We'll check that the app didn't brutally edit some system configuration
manually_modified_files_before_install = manually_modified_files() manually_modified_files_before_install = manually_modified_files()
@ -920,19 +922,6 @@ def app_install(
# We'll check that the app didn't brutally edit some system configuration # We'll check that the app didn't brutally edit some system configuration
manually_modified_files_before_install = manually_modified_files() manually_modified_files_before_install = manually_modified_files()
# Tell the operation_logger to redact all password-type args
# Also redact the % escaped version of the password that might appear in
# the 'args' section of metadata (relevant for password with non-alphanumeric char)
data_to_redact = [
value[0] for value in args_odict.values() if value[1] == "password"
]
data_to_redact += [
urllib.parse.quote(data)
for data in data_to_redact
if urllib.parse.quote(data) != data
]
operation_logger.data_to_redact.extend(data_to_redact)
operation_logger.related_to = [ operation_logger.related_to = [
s for s in operation_logger.related_to if s[0] != "app" s for s in operation_logger.related_to if s[0] != "app"
] ]
@ -988,6 +977,7 @@ def app_install(
# Prepare env. var. to pass to script # Prepare env. var. to pass to script
env_dict = _make_environment_for_app_script(app_instance_name, args=args_odict) env_dict = _make_environment_for_app_script(app_instance_name, args=args_odict)
env_dict["YNH_APP_BASEDIR"] = extracted_app_folder
env_dict_for_logging = env_dict.copy() env_dict_for_logging = env_dict.copy()
for arg_name, arg_value_and_type in args_odict.items(): for arg_name, arg_value_and_type in args_odict.items():
@ -1052,6 +1042,7 @@ def app_install(
env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name
env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number)
env_dict_remove["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") env_dict_remove["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
env_dict_remove["YNH_APP_BASEDIR"] = extracted_app_folder
# Execute remove script # Execute remove script
operation_logger_remove = OperationLogger( operation_logger_remove = OperationLogger(
@ -1169,6 +1160,8 @@ def app_remove(operation_logger, app, purge=False):
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?") env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
env_dict["YNH_APP_PURGE"] = str(purge) env_dict["YNH_APP_PURGE"] = str(purge)
env_dict["YNH_APP_BASEDIR"] = tmp_workdir_for_app
operation_logger.extra.update({"env": env_dict}) operation_logger.extra.update({"env": env_dict})
operation_logger.flush() operation_logger.flush()
@ -1654,12 +1647,14 @@ def app_action_run(operation_logger, app, action, args=None):
) )
args_odict = _parse_args_for_action(actions[action], args=args_dict) args_odict = _parse_args_for_action(actions[action], args=args_dict)
tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app)
env_dict = _make_environment_for_app_script( env_dict = _make_environment_for_app_script(
app, args=args_odict, args_prefix="ACTION_" app, args=args_odict, args_prefix="ACTION_"
) )
env_dict["YNH_ACTION"] = action env_dict["YNH_ACTION"] = action
env_dict["YNH_APP_BASEDIR"] = tmp_workdir_for_app
tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app)
_, action_script = tempfile.mkstemp(dir=tmp_workdir_for_app) _, action_script = tempfile.mkstemp(dir=tmp_workdir_for_app)
with open(action_script, "w") as script: with open(action_script, "w") as script:
@ -1731,8 +1726,6 @@ def app_config_set(
config_ = AppConfigPanel(app) config_ = AppConfigPanel(app)
Question.operation_logger = operation_logger
return config_.set(key, value, args, args_file, operation_logger=operation_logger) return config_.set(key, value, args, args_file, operation_logger=operation_logger)
@ -1787,7 +1780,8 @@ ynh_app_config_run $1
"app_id": app_id, "app_id": app_id,
"app": self.app, "app": self.app,
"app_instance_nb": str(app_instance_nb), "app_instance_nb": str(app_instance_nb),
"final_path": settings.get("final_path", "") "final_path": settings.get("final_path", ""),
"YNH_APP_BASEDIR": os.path.join(APPS_SETTING_PATH, self.app),
} }
) )

View file

@ -707,6 +707,7 @@ class BackupManager:
# Prepare environment # Prepare environment
env_dict = self._get_env_var(app) env_dict = self._get_env_var(app)
env_dict["YNH_APP_BASEDIR"] = os.path.join(self.work_dir, "apps", app, "settings")
tmp_app_bkp_dir = env_dict["YNH_APP_BACKUP_DIR"] tmp_app_bkp_dir = env_dict["YNH_APP_BACKUP_DIR"]
settings_dir = os.path.join(self.work_dir, "apps", app, "settings") settings_dir = os.path.join(self.work_dir, "apps", app, "settings")
@ -1487,6 +1488,7 @@ class RestoreManager:
"YNH_APP_BACKUP_DIR": os.path.join( "YNH_APP_BACKUP_DIR": os.path.join(
self.work_dir, "apps", app_instance_name, "backup" self.work_dir, "apps", app_instance_name, "backup"
), ),
"YNH_APP_BASEDIR": os.path.join(self.work_dir, "apps", app_instance_name, "settings"),
} }
) )
@ -1524,6 +1526,7 @@ class RestoreManager:
# Setup environment for remove script # Setup environment for remove script
env_dict_remove = _make_environment_for_app_script(app_instance_name) env_dict_remove = _make_environment_for_app_script(app_instance_name)
env_dict_remove["YNH_APP_BASEDIR"] = os.path.join(self.work_dir, "apps", app_instance_name, "settings")
remove_operation_logger = OperationLogger( remove_operation_logger = OperationLogger(
"remove_on_failed_restore", "remove_on_failed_restore",

View file

@ -75,7 +75,7 @@ def log_list(limit=None, with_details=False, with_suboperations=False):
# If we displaying only parent, we are still gonna load up to limit * 5 logs # If we displaying only parent, we are still gonna load up to limit * 5 logs
# because many of them are suboperations which are not gonna be kept # because many of them are suboperations which are not gonna be kept
# Yet we still want to obtain ~limit number of logs # Yet we still want to obtain ~limit number of logs
logs = logs[:limit * 5] logs = logs[: limit * 5]
for log in logs: for log in logs:
@ -186,12 +186,17 @@ def log_show(
r"DEBUG - \+ exit (1|0)$", r"DEBUG - \+ exit (1|0)$",
] ]
filters = [re.compile(f) for f in filters] filters = [re.compile(f) for f in filters]
return [line for line in lines if not any(f.search(line.strip()) for f in filters)] return [
line
for line in lines
if not any(f.search(line.strip()) for f in filters)
]
else: else:
def _filter(lines): def _filter(lines):
return lines return lines
# Normalize log/metadata paths and filenames # Normalize log/metadata paths and filenames
abs_path = path abs_path = path
log_path = None log_path = None

View file

@ -457,22 +457,26 @@ def permission_create(
"permission_creation_failed", permission=permission, error=e "permission_creation_failed", permission=permission, error=e
) )
permission_url( try:
permission, permission_url(
url=url, permission,
add_url=additional_urls, url=url,
auth_header=auth_header, add_url=additional_urls,
sync_perm=False, auth_header=auth_header,
) sync_perm=False,
)
new_permission = _update_ldap_group_permission( new_permission = _update_ldap_group_permission(
permission=permission, permission=permission,
allowed=allowed, allowed=allowed,
label=label, label=label,
show_tile=show_tile, show_tile=show_tile,
protected=protected, protected=protected,
sync_perm=sync_perm, sync_perm=sync_perm,
) )
except:
permission_delete(permission, force=True)
raise
logger.debug(m18n.n("permission_created", permission=permission)) logger.debug(m18n.n("permission_created", permission=permission))
return new_permission return new_permission

View file

@ -348,9 +348,7 @@ def test_question_password():
] ]
answers = {"some_password": "some_value"} answers = {"some_password": "some_value"}
expected_result = OrderedDict({"some_password": ("some_value", "password")}) expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock() assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(Question.operation_logger, "data_to_redact", create=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
def test_question_password_no_input(): def test_question_password_no_input():
@ -375,13 +373,9 @@ def test_question_password_input():
} }
] ]
answers = {} answers = {}
Question.operation_logger = {"data_to_redact": []}
expected_result = OrderedDict({"some_password": ("some_value", "password")}) expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock() with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
): ):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -397,10 +391,7 @@ def test_question_password_input_no_ask():
answers = {} answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")}) expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock() with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
): ):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -417,20 +408,14 @@ def test_question_password_no_input_optional():
answers = {} answers = {}
expected_result = OrderedDict({"some_password": ("", "password")}) expected_result = OrderedDict({"some_password": ("", "password")})
Question.operation_logger = MagicMock() with patch.object(os, "isatty", return_value=False):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
questions = [ questions = [
{"name": "some_password", "type": "password", "optional": True, "default": ""} {"name": "some_password", "type": "password", "optional": True, "default": ""}
] ]
Question.operation_logger = MagicMock() with patch.object(os, "isatty", return_value=False):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -446,10 +431,7 @@ def test_question_password_optional_with_input():
answers = {} answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")}) expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock() with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
): ):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -467,10 +449,7 @@ def test_question_password_optional_with_empty_input():
answers = {} answers = {}
expected_result = OrderedDict({"some_password": ("", "password")}) expected_result = OrderedDict({"some_password": ("", "password")})
Question.operation_logger = MagicMock() with patch.object(Moulinette, "prompt", return_value=""), patch.object(
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
): ):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -487,10 +466,7 @@ def test_question_password_optional_with_input_without_ask():
answers = {} answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")}) expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock() with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
): ):
assert parse_args_in_yunohost_format(answers, questions) == expected_result assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -540,10 +516,7 @@ def test_question_password_input_test_ask():
] ]
answers = {} answers = {}
Question.operation_logger = MagicMock()
with patch.object( with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value" Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object( ) as prompt, patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
@ -572,10 +545,7 @@ def test_question_password_input_test_ask_with_example():
] ]
answers = {} answers = {}
Question.operation_logger = MagicMock()
with patch.object( with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value" Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object( ) as prompt, patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True
@ -599,10 +569,7 @@ def test_question_password_input_test_ask_with_help():
] ]
answers = {} answers = {}
Question.operation_logger = MagicMock()
with patch.object( with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value" Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object( ) as prompt, patch.object(
os, "isatty", return_value=True os, "isatty", return_value=True

View file

@ -39,6 +39,7 @@ from moulinette.utils.filesystem import (
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.log import OperationLogger
logger = getActionLogger("yunohost.config") logger = getActionLogger("yunohost.config")
CONFIG_PANEL_VERSION_SUPPORTED = 1.0 CONFIG_PANEL_VERSION_SUPPORTED = 1.0
@ -453,7 +454,6 @@ class ConfigPanel:
class Question(object): class Question(object):
hide_user_input_in_prompt = False hide_user_input_in_prompt = False
operation_logger = None
pattern = None pattern = None
def __init__(self, question, user_answers): def __init__(self, question, user_answers):
@ -490,7 +490,7 @@ class Question(object):
self.value = Moulinette.prompt( self.value = Moulinette.prompt(
message=text, message=text,
is_password=self.hide_user_input_in_prompt, is_password=self.hide_user_input_in_prompt,
confirm=False, # We doesn't want to confirm this kind of password like in webadmin confirm=False, # We doesn't want to confirm this kind of password like in webadmin
prefill=prefill, prefill=prefill,
is_multiline=(self.type == "text"), is_multiline=(self.type == "text"),
) )
@ -587,13 +587,9 @@ class Question(object):
for data in data_to_redact for data in data_to_redact
if urllib.parse.quote(data) != data if urllib.parse.quote(data) != data
] ]
if self.operation_logger:
self.operation_logger.data_to_redact.extend(data_to_redact) for operation_logger in OperationLogger._instances:
elif data_to_redact: operation_logger.data_to_redact.extend(data_to_redact)
raise YunohostError(
f"Can't redact {self.name} because no operation logger available in the context",
raw_msg=True,
)
return self.value return self.value
@ -658,6 +654,12 @@ class TagsQuestion(Question):
return ",".join(value) return ",".join(value)
return value return value
@staticmethod
def normalize(value, option={}):
if isinstance(value, list):
return ",".join(value)
return value
def _prevalidate(self): def _prevalidate(self):
values = self.value values = self.value
if isinstance(values, str): if isinstance(values, str):
@ -669,6 +671,11 @@ class TagsQuestion(Question):
super()._prevalidate() super()._prevalidate()
self.value = values self.value = values
def _post_parse_value(self):
if isinstance(self.value, list):
self.value = ",".join(self.value)
return super()._post_parse_value()
class PasswordQuestion(Question): class PasswordQuestion(Question):
hide_user_input_in_prompt = True hide_user_input_in_prompt = True
@ -706,11 +713,17 @@ class PasswordQuestion(Question):
def _format_text_for_user_input_in_cli(self): def _format_text_for_user_input_in_cli(self):
need_column = self.current_value or self.optional need_column = self.current_value or self.optional
text_for_user_input_in_cli = super()._format_text_for_user_input_in_cli(need_column) text_for_user_input_in_cli = super()._format_text_for_user_input_in_cli(
need_column
)
if self.current_value: if self.current_value:
text_for_user_input_in_cli += "\n - " + m18n.n("app_argument_password_help_keep") text_for_user_input_in_cli += "\n - " + m18n.n(
"app_argument_password_help_keep"
)
if self.optional: if self.optional:
text_for_user_input_in_cli += "\n - " + m18n.n("app_argument_password_help_optional") text_for_user_input_in_cli += "\n - " + m18n.n(
"app_argument_password_help_optional"
)
return text_for_user_input_in_cli return text_for_user_input_in_cli
@ -834,7 +847,7 @@ class UserQuestion(Question):
raise YunohostValidationError( raise YunohostValidationError(
"app_argument_invalid", "app_argument_invalid",
name=self.name, name=self.name,
error="You should create a YunoHost user first." error="You should create a YunoHost user first.",
) )
if self.default is None: if self.default is None:
@ -949,11 +962,11 @@ class FileQuestion(Question):
"content": self.value, "content": self.value,
"filename": user_answers.get(f"{self.name}[name]", self.name), "filename": user_answers.get(f"{self.name}[name]", self.name),
} }
# If path file are the same
if self.value and str(self.value) == self.current_value:
self.value = None
def _prevalidate(self): def _prevalidate(self):
if self.value is None:
self.value = self.current_value
super()._prevalidate() super()._prevalidate()
if ( if (
isinstance(self.value, str) isinstance(self.value, str)
@ -988,7 +1001,7 @@ class FileQuestion(Question):
if not self.value: if not self.value:
return self.value return self.value
if Moulinette.interface.type == "api": if Moulinette.interface.type == "api" and isinstance(self.value, dict):
upload_dir = tempfile.mkdtemp(prefix="tmp_configpanel_") upload_dir = tempfile.mkdtemp(prefix="tmp_configpanel_")
FileQuestion.upload_dirs += [upload_dir] FileQuestion.upload_dirs += [upload_dir]