From 1810808acd8148b5da368fc5874de18cd87d53ea Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 28 Jun 2019 16:34:15 +0200 Subject: [PATCH 001/299] Fix wrong usage of ynh_mysql_user_exists --- data/helpers.d/mysql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/mysql b/data/helpers.d/mysql index 372819025..e9cf59b3c 100644 --- a/data/helpers.d/mysql +++ b/data/helpers.d/mysql @@ -231,7 +231,7 @@ ynh_mysql_remove_db () { fi # Remove mysql user if it exists - if $(ynh_mysql_user_exists --user=$db_user); then + if ynh_mysql_user_exists --user=$db_user; then ynh_mysql_drop_user $db_user fi } From 2e6932928b80c4a5f01638a68b57a31954aa0696 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 5 Jul 2019 22:24:48 +0200 Subject: [PATCH 002/299] Split handling of hook_exec according to hook type --- src/yunohost/hook.py | 71 ++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 22 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 67e77f033..e9cabcbc3 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -311,7 +311,6 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, user -- User with which to run the command """ - from moulinette.utils.process import call_async_output # Validate hook path if path[0] != '/': @@ -319,6 +318,49 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, if not os.path.isfile(path): raise YunohostError('file_does_not_exist', path=path) + # Check the type of the hook (bash by default) + hook_type = "bash" + # (non-bash hooks shall start with something like "#!/usr/bin/env language") + hook_ext = os.path.splitext(path)[1] + if hook_ext == ".py": + hook_type = "python" + else: + # TODO / FIXME : if needed in the future, implement support for other + # languages... + assert hook_ext == "", "hook_exec only supports bash and python hooks for now" + + # Define output loggers and call command + loggers = ( + lambda l: logger.debug(l.rstrip()+"\r"), + lambda l: logger.warning(l.rstrip()), + lambda l: logger.info(l.rstrip()) + ) + + if hook_type == "bash": + returncode, returndata = _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers) + elif hook_type == "python": + returncode, returndata = _hook_exec_python(path, args, env, loggers) + else: + # Doesn't happen ... c.f. previous assertion + returncode, returndata = None, {} + + # Check and return process' return code + if returncode is None: + if raise_on_error: + raise YunohostError('hook_exec_not_terminated', path=path) + else: + logger.error(m18n.n('hook_exec_not_terminated', path=path)) + return 1, {} + elif raise_on_error and returncode != 0: + raise YunohostError('hook_exec_failed', path=path) + + return returncode, returndata + + +def _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers): + + from moulinette.utils.process import call_async_output + # Construct command variables cmd_args = '' if args and isinstance(args, list): @@ -369,33 +411,13 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, else: logger.debug(m18n.n('executing_script', script=path)) - # Define output callbacks and call command - callbacks = ( - lambda l: logger.debug(l.rstrip()+"\r"), - lambda l: logger.warning(l.rstrip()), - ) - - if stdinfo: - callbacks = (callbacks[0], callbacks[1], - lambda l: logger.info(l.rstrip())) - logger.debug("About to run the command '%s'" % command) returncode = call_async_output( - command, callbacks, shell=False, cwd=chdir, + command, loggers, shell=False, cwd=chdir, stdinfo=stdinfo ) - # Check and return process' return code - if returncode is None: - if raise_on_error: - raise YunohostError('hook_exec_not_terminated', path=path) - else: - logger.error(m18n.n('hook_exec_not_terminated', path=path)) - return 1, {} - elif raise_on_error and returncode != 0: - raise YunohostError('hook_exec_failed', path=path) - raw_content = None try: with open(stdreturn, 'r') as f: @@ -427,6 +449,11 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, return returncode, returncontent +def _hook_exec_python(path, args, env, loggers): + + pass + + def _extract_filename_parts(filename): """Extract hook parts from filename""" if '-' in filename: From 491959f16e87c1b411c1c3a01483ca755ee3f13d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 5 Jul 2019 22:27:53 +0200 Subject: [PATCH 003/299] Implement Python hooks handling --- src/yunohost/hook.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index e9cabcbc3..8c679b601 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -25,8 +25,10 @@ """ import os import re +import sys import tempfile from glob import iglob +from importlib import import_module from moulinette import m18n, msettings from yunohost.utils.error import YunohostError @@ -179,7 +181,7 @@ def hook_list(action, list_by='name', show_info=False): def _append_folder(d, folder): # Iterate over and add hook from a folder for f in os.listdir(folder + action): - if f[0] == '.' or f[-1] == '~': + if f[0] == '.' or f[-1] == '~' or f.endswith(".pyc"): continue path = '%s%s/%s' % (folder, action, f) priority, name = _extract_filename_parts(f) @@ -451,7 +453,16 @@ def _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, logge def _hook_exec_python(path, args, env, loggers): - pass + dir_ = os.path.dirname(path) + name = os.path.splitext(os.path.basename(path))[0] + + if not dir_ in sys.path: + sys.path = [dir_] + sys.path + module = import_module(name) + + # TODO : We might want to check here that it's a tuple + # containing an int + a dict ? + return module.main(args, env, loggers) def _extract_filename_parts(filename): @@ -461,6 +472,9 @@ def _extract_filename_parts(filename): else: priority = '50' action = filename + + # Remove extension if there's one + action = os.path.splitext(action)[0] return priority, action From ce4fb552aee393fd9a782ce1cbfa5a743c9278fb Mon Sep 17 00:00:00 2001 From: yalh76 Date: Wed, 10 Jul 2019 23:29:38 +0200 Subject: [PATCH 004/299] upgrade n version --- data/helpers.d/nodejs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/nodejs b/data/helpers.d/nodejs index 9295c4348..3f4cfd4f3 100644 --- a/data/helpers.d/nodejs +++ b/data/helpers.d/nodejs @@ -16,8 +16,8 @@ ynh_install_n () { ynh_print_info --message="Installation of N - Node.js version management" # Build an app.src for n mkdir -p "../conf" - echo "SOURCE_URL=https://github.com/tj/n/archive/v2.1.7.tar.gz -SOURCE_SUM=2ba3c9d4dd3c7e38885b37e02337906a1ee91febe6d5c9159d89a9050f2eea8f" > "../conf/n.src" + echo "SOURCE_URL=https://github.com/tj/n/archive/v4.1.0.tar.gz +SOURCE_SUM=3983fa3f00d4bf85ba8e21f1a590f6e28938093abe0bb950aeea52b1717471fc" > "../conf/n.src" # Download and extract n ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n # Install n From 8c4d136a7f30e823b09345c1eb0b2912722024f2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 13 Jul 2019 18:29:37 +0200 Subject: [PATCH 005/299] Check python hook did return the expected format --- src/yunohost/hook.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 8c679b601..a9fd3186c 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -462,7 +462,13 @@ def _hook_exec_python(path, args, env, loggers): # TODO : We might want to check here that it's a tuple # containing an int + a dict ? - return module.main(args, env, loggers) + ret = module.main(args, env, loggers) + assert isinstance(ret, tuple) \ + and len(ret) == 2 \ + and isinstance(ret[0],int) \ + and isinstance(ret[1],dict), \ + "Module %s did not return a (int, dict) tuple !" % module + return ret def _extract_filename_parts(filename): From 81843b2b957ff88c4c1b7b8f897bed567510c6bf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 3 Aug 2019 20:56:36 +0200 Subject: [PATCH 006/299] Support .sh as an exception for bash hooks --- src/yunohost/hook.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index a9fd3186c..64b4cc2ee 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -329,7 +329,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, else: # TODO / FIXME : if needed in the future, implement support for other # languages... - assert hook_ext == "", "hook_exec only supports bash and python hooks for now" + assert hook_ext in ["", ".sh"], "hook_exec only supports bash and python hooks for now" # Define output loggers and call command loggers = ( From bee4fb70fda3857bbfef72b95c7d50198034ad10 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 3 Aug 2019 21:21:07 +0200 Subject: [PATCH 007/299] [mod] Move this block to have a whole part dedicated to migrations --- src/yunohost/tools.py | 68 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 5e14082b5..8b0b981b2 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -970,6 +970,43 @@ def tools_reboot(operation_logger, force=False): subprocess.check_call(['systemctl', 'reboot']) +def tools_shell(command=None): + """ + Launch an (i)python shell in the YunoHost context. + + This is entirely aim for development. + """ + + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + + if command: + exec(command) + return + + logger.warn("The \033[1;34mldap\033[0m interface is available in this context") + try: + from IPython import embed + embed() + except ImportError: + logger.warn("You don't have IPython installed, consider installing it as it is way better than the standard shell.") + logger.warn("Falling back on the standard shell.") + + import readline # will allow Up/Down/History in the console + readline # to please pyflakes + import code + vars = globals().copy() + vars.update(locals()) + shell = code.InteractiveConsole(vars) + shell.interact() + + +# ############################################ # +# # +# Migrations management # +# # +# ############################################ # + def tools_migrations_list(pending=False, done=False): """ List existing migrations @@ -1155,37 +1192,6 @@ def tools_migrations_state(): return read_json(MIGRATIONS_STATE_PATH) -def tools_shell(command=None): - """ - Launch an (i)python shell in the YunoHost context. - - This is entirely aim for development. - """ - - from yunohost.utils.ldap import _get_ldap_interface - ldap = _get_ldap_interface() - - if command: - exec(command) - return - - logger.warn("The \033[1;34mldap\033[0m interface is available in this context") - try: - from IPython import embed - embed() - except ImportError: - logger.warn("You don't have IPython installed, consider installing it as it is way better than the standard shell.") - logger.warn("Falling back on the standard shell.") - - import readline # will allow Up/Down/History in the console - readline # to please pyflakes - import code - vars = globals().copy() - vars.update(locals()) - shell = code.InteractiveConsole(vars) - shell.interact() - - def _get_migrations_list(): migrations = [] From 2723300dcaa1ec9ba803496ad15ebc5c31397566 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 4 Aug 2019 00:28:14 +0200 Subject: [PATCH 008/299] Rework the migration system to have independent, non-ordered migrations, so each migration has a state skipped / done / pending and can be ran independently from others --- data/actionsmap/yunohost.yml | 24 ++-- src/yunohost/tools.py | 228 +++++++++++++++++------------------ 2 files changed, 125 insertions(+), 127 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 6401bdebc..f54cc2254 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1726,22 +1726,26 @@ tools: ### tools_migrations_migrate() migrate: - action_help: Perform migrations + action_help: Run migrations api: POST /migrations/migrate arguments: - -t: - help: target migration number (or 0), latest one by default - type: int - full: --target - -s: - help: skip the migration(s), use it only if you know what you are doing - full: --skip + targets: + help: Migrations to run (all pendings by default) + nargs: "*" + --skip: + help: Skip specified migrations (to be used only if you know what you are doing) + action: store_true + --revert: + help: Attempt to revert already-ran migrations + action: store_true + --force-rerun: + help: Re-run already-ran migrations (to be used only if you know what you are doing) action: store_true --auto: - help: automatic mode, won't run manual migrations, use it only if you know what you are doing + help: Automatic mode, won't run manual migrations (to be used only if you know what you are doing) action: store_true --accept-disclaimer: - help: accept disclaimers of migration (please read them before using this option) + help: Accept disclaimers of migrations (please read them before using this option) action: store_true ### tools_migrations_state() diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 8b0b981b2..f44e98a8d 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -37,7 +37,7 @@ from collections import OrderedDict from moulinette import msignals, m18n from moulinette.utils.log import getActionLogger from moulinette.utils.process import check_output, call_async_output -from moulinette.utils.filesystem import read_json, write_to_json +from moulinette.utils.filesystem import read_json, write_to_json, read_yaml, write_to_yaml from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, app_list, _install_appslist_fetch_cron from yunohost.domain import domain_add, domain_list, _get_maindomain, _set_maindomain from yunohost.dyndns import _dyndns_available, _dyndns_provides @@ -52,7 +52,8 @@ from yunohost.log import is_unit_operation, OperationLogger # FIXME this is a duplicate from apps.py APPS_SETTING_PATH = '/etc/yunohost/apps/' -MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations_state.json" +MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations.yaml" +OLD_MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations_state.json" logger = getActionLogger('yunohost.tools') @@ -1019,131 +1020,126 @@ def tools_migrations_list(pending=False, done=False): # Get all migrations migrations = _get_migrations_list() - # If asked, filter pending or done migrations - if pending or done: - last_migration = tools_migrations_state()["last_run_migration"] - last_migration = last_migration["number"] if last_migration else -1 - if done: - migrations = [m for m in migrations if m.number <= last_migration] - if pending: - migrations = [m for m in migrations if m.number > last_migration] - # Reduce to dictionnaries migrations = [{"id": migration.id, "number": migration.number, "name": migration.name, "mode": migration.mode, + "state": migration.state, "description": migration.description, "disclaimer": migration.disclaimer} for migration in migrations] + # If asked, filter pending or done migrations + if pending or done: + if done: + migrations = [m for m in migrations if m["state"] != "pending"] + if pending: + migrations = [m for m in migrations if m["state"] == "pending"] + return {"migrations": migrations} -def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclaimer=False): +def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=False, revert=False, accept_disclaimer=False): """ Perform migrations + + targets : a list of migrations to act on (by default : all pending) + auto : automatic mode, run only 'automatic' migrations (compared to manual migrations). Option meant to be used in debian's postinst script during upgades. + skip : skip specified migrations (must explicit which migrations) + revert : to revert already-ran migrations (must explicit which migrations) + force_rerun : to re-run already ran migrations (if you know what you're doing...) (must explicit which migrations) + accept_disclaimer : accept disclaimer for manual migration that requires it (only valid for one migration) """ - # state is a datastructure that represents the last run migration - # it has this form: - # { - # "last_run_migration": { - # "number": "00xx", - # "name": "some name", - # } - # } - state = tools_migrations_state() + all_migrations = _get_migrations_list() - last_run_migration_number = state["last_run_migration"]["number"] if state["last_run_migration"] else 0 + # auto, skip, revert and force are exclusive options + if auto + skip + revert + force_rerun > 1: + raise YunohostError("--auto, --skip, --revert and --force-rerun are exclusive options.") - # load all migrations - migrations = _get_migrations_list() - migrations = sorted(migrations, key=lambda x: x.number) + # If no target specified + if not targets: + # skip, revert or force require explicit targets + if (revert or force_rerun): + raise YunohostError("You must provide explicit targets when using --skip, --revert or --force-rerun") - if not migrations: + # Otherwise, targets are all pending migrations + targets = [m for m in all_migrations if m.state == "pending"] + + # If explicit targets are provided, we shall validate them + else: + def get_matching_migration(target): + for m in all_migrations: + if m.id == target or m.name == target or m.id.split("_")[0] == target: + return m + + raise YunohostError("No such migration called %s" % target) + + targets = [get_matching_migration(t) for t in targets] + done = [t.id for t in targets if t.state != "pending"] + pending = [t.id for t in targets if t.state == "pending"] + + if skip and done: + raise YunohostError("Those migrations are not pending so cannot be skipped: %s" % ', '.join(done)) + if (revert or force_rerun) and pending: + raise YunohostError("Those migrations were not already ran so cannot be reverted or reran: %s" % ', '.join(pending)) + + # So, is there actually something to do ? + if not targets: logger.info(m18n.n('migrations_no_migrations_to_run')) return - all_migration_numbers = [x.number for x in migrations] + # Actually run selected migrations + for migration in targets: - if target is None: - target = migrations[-1].number + # If we are migrating in "automatic mode" (i.e. from debian configure + # during an upgrade of the package) but we are asked to run migrations + # to be ran manually by the user, stop there and ask the user to + # run the migration manually. + if auto and migration.mode == "manual": + logger.warn(m18n.n('migrations_to_be_ran_manually', + number=migration.number, + name=migration.name)) - # validate input, target must be "0" or a valid number - elif target != 0 and target not in all_migration_numbers: - raise YunohostError('migrations_bad_value_for_target', ", ".join(map(str, all_migration_numbers))) + # We go to the next migration + continue - logger.debug(m18n.n('migrations_current_target', target)) - - # no new migrations to run - if target == last_run_migration_number: - logger.info(m18n.n('migrations_no_migrations_to_run')) - return - - logger.debug(m18n.n('migrations_show_last_migration', last_run_migration_number)) - - # we need to run missing migrations - if last_run_migration_number < target: - logger.debug(m18n.n('migrations_forward')) - # drop all already run migrations - migrations = filter(lambda x: target >= x.number > last_run_migration_number, migrations) - mode = "forward" - - # we need to go backward on already run migrations - elif last_run_migration_number > target: - logger.debug(m18n.n('migrations_backward')) - # drop all not already run migrations - migrations = filter(lambda x: target < x.number <= last_run_migration_number, migrations) - mode = "backward" - - else: # can't happen, this case is handle before - raise Exception() - - # effectively run selected migrations - for migration in migrations: - - if not skip: - # If we are migrating in "automatic mode" (i.e. from debian configure - # during an upgrade of the package) but we are asked to run migrations - # to be ran manually by the user, stop there and ask the user to - # run the migration manually. - if auto and migration.mode == "manual": - logger.warn(m18n.n('migrations_to_be_ran_manually', + # If some migrations have disclaimers (and we're not trying to skip them) + if migration.disclaimer and not skip: + # require the --accept-disclaimer option. + # Otherwise, go to the next migration + if not accept_disclaimer: + logger.warn(m18n.n('migrations_need_to_accept_disclaimer', number=migration.number, - name=migration.name)) - break - - # If some migrations have disclaimers, - if migration.disclaimer: - # require the --accept-disclaimer option. Otherwise, stop everything - # here and display the disclaimer - if not accept_disclaimer: - logger.warn(m18n.n('migrations_need_to_accept_disclaimer', - number=migration.number, - name=migration.name, - disclaimer=migration.disclaimer)) - break - # --accept-disclaimer will only work for the first migration - else: - accept_disclaimer = False + name=migration.name, + disclaimer=migration.disclaimer)) + continue + # --accept-disclaimer will only work for the first migration + else: + accept_disclaimer = False # Start register change on system + mode = "backward" if revert else "forward" operation_logger = OperationLogger('tools_migrations_migrate_' + mode) operation_logger.start() - if not skip: + if skip: + logger.warn(m18n.n('migrations_skip_migration', + number=migration.number, + name=migration.name)) + _write_migration_state(migration.id, "skipped") + operation_logger.success() + else: logger.info(m18n.n('migrations_show_currently_running_migration', number=migration.number, name=migration.name)) try: migration.operation_logger = operation_logger - if mode == "forward": - migration.migrate() - elif mode == "backward": + if revert: migration.backward() - else: # can't happen - raise Exception("Illegal state for migration: '%s', should be either 'forward' or 'backward'" % mode) + else: + migration.migrate() except Exception as e: # migration failed, let's stop here but still update state because # we managed to run the previous ones @@ -1153,33 +1149,11 @@ def tools_migrations_migrate(target=None, skip=False, auto=False, accept_disclai name=migration.name) logger.error(msg, exc_info=1) operation_logger.error(msg) - break else: logger.success(m18n.n('migrations_success', number=migration.number, name=migration.name)) - - else: # if skip - logger.warn(m18n.n('migrations_skip_migration', - number=migration.number, - name=migration.name)) - - # update the state to include the latest run migration - state["last_run_migration"] = { - "number": migration.number, - "name": migration.name - } - - operation_logger.success() - - # Skip migrations one at a time - if skip: - break - - # special case where we want to go back from the start - if target == 0: - state["last_run_migration"] = None - - write_to_json(MIGRATIONS_STATE_PATH, state) + _write_migration_state(migration.id, "done") + operation_logger.success() def tools_migrations_state(): @@ -1187,9 +1161,16 @@ def tools_migrations_state(): Show current migration state """ if not os.path.exists(MIGRATIONS_STATE_PATH): - return {"last_run_migration": None} + return {"migrations": {}} - return read_json(MIGRATIONS_STATE_PATH) + return read_yaml(MIGRATIONS_STATE_PATH) + + +def _write_migration_state(migration_id, state): + + current_states = tools_migrations_state() + current_states["migrations"][migration_id] = state + write_to_yaml(MIGRATIONS_STATE_PATH, current_states) def _get_migrations_list(): @@ -1207,8 +1188,21 @@ def _get_migrations_list(): logger.warn(m18n.n('migrations_cant_reach_migration_file', migrations_path)) return migrations + # states is a datastructure that represents the last run migration + # it has this form: + # { + # "0001_foo": "skipped", + # "0004_baz": "done", + # "0002_bar": "skipped", + # "0005_zblerg": "done", + # } + # (in particular, pending migrations / not already ran are not listed + states = tools_migrations_state()["migrations"] + for migration_file in filter(lambda x: re.match("^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)): - migrations.append(_load_migration(migration_file)) + m = _load_migration(migration_file) + m.state = states.get(m.id, "pending") + migrations.append(m) return sorted(migrations, key=lambda m: m.id) From da64778b0d73ae9a3b41f92800f62538a7deced7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 4 Aug 2019 00:57:00 +0200 Subject: [PATCH 009/299] Migrate the old migration json to the new yaml format --- src/yunohost/tools.py | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index f44e98a8d..a15b5593e 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -53,7 +53,6 @@ from yunohost.log import is_unit_operation, OperationLogger # FIXME this is a duplicate from apps.py APPS_SETTING_PATH = '/etc/yunohost/apps/' MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations.yaml" -OLD_MIGRATIONS_STATE_PATH = "/etc/yunohost/migrations_state.json" logger = getActionLogger('yunohost.tools') @@ -1160,12 +1159,48 @@ def tools_migrations_state(): """ Show current migration state """ + if os.path.exists("/etc/yunohost/migrations_state.json"): + _migrate_legacy_migration_json() + if not os.path.exists(MIGRATIONS_STATE_PATH): return {"migrations": {}} return read_yaml(MIGRATIONS_STATE_PATH) +def _migrate_legacy_migration_json(): + + from moulinette.utils.filesystem import read_json + + logger.debug("Migrating legacy migration state json to yaml...") + + # We fetch the old state containing the last run migration + old_state = read_json("/etc/yunohost/migrations_state.json")["last_run_migration"] + last_run_migration_id = str(old_state["number"]) + "_" + old_state["name"] + + # Extract the list of migration ids + try: + from . import data_migrations + except ImportError: + # Well ugh what are we supposed to do if we can't do this >.>... + pass + migrations_path = data_migrations.__path__[0] + migration_files = filter(lambda x: re.match("^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)) + # (here we remove the .py extension and make sure the ids are sorted) + migration_ids = sorted([f.rsplit(".", 1)[0] for f in migration_files]) + + # So now build the new dict for every id up to the last run migration + migrations = {} + for migration_id in migration_ids: + migrations[migration_id] = "done" + if last_run_migration_id in migration_id: + break + + # Write the new file and rename the old one + write_to_yaml(MIGRATIONS_STATE_PATH, {"migrations": migrations}) + os.rename("/etc/yunohost/migrations_state.json", "/etc/yunohost/migrations_state.json.old") + + def _write_migration_state(migration_id, state): current_states = tools_migrations_state() From add3bd29b014cfe87ce4cc77b5b0eeeebc24ea8a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 5 Aug 2019 21:10:03 +0200 Subject: [PATCH 010/299] Add dependencies between migrations --- locales/en.json | 2 +- .../0003_migrate_to_stretch.py | 2 +- .../0004_php5_to_php7_pools.py | 2 ++ .../0005_postgresql_9p4_to_9p6.py | 2 ++ ...0008_ssh_conf_managed_by_yunohost_step2.py | 4 ++- src/yunohost/tools.py | 25 +++++++++++++------ 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index ecfba2947..14aee512d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -321,6 +321,7 @@ "migrate_tsig_wait_3": "1min…", "migrate_tsig_wait_4": "30 secondes…", "migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed!", + "migration_backward_impossible": "The migration {name} cannot be reverted.", "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", "migration_description_0002_migrate_to_tsig_sha256": "Improve security of dyndns TSIG by using SHA512 instead of MD5", "migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0", @@ -332,7 +333,6 @@ "migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services", "migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead", "migration_description_0011_setup_group_permission": "Setup user group and setup permission for apps and services", - "migration_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists…", "migration_0003_main_upgrade": "Starting main upgrade…", diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 0db719e15..8b7586701 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -31,7 +31,7 @@ class MyMigration(Migration): def backward(self): - raise YunohostError("migration_0003_backward_impossible") + raise YunohostError("migration_backward_impossible", name=self.name) def migrate(self): diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index 46a5eb91d..faf9ccbbc 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -22,6 +22,8 @@ class MyMigration(Migration): "Migrate php5-fpm 'pool' conf files to php7 stuff" + dependencies = ["migrate_to_stretch"] + def migrate(self): # Get list of php5 pool files diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 5ae729b60..50c0561cb 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -14,6 +14,8 @@ class MyMigration(Migration): "Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch" + dependencies = ["migrate_to_stretch"] + def migrate(self): if not self.package_is_installed("postgresql-9.4"): diff --git a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py index 8984440bd..0ea0ddc3a 100644 --- a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py @@ -33,6 +33,8 @@ class MyMigration(Migration): shown - and the user may also choose to skip this migration. """ + dependencies = ["ssh_conf_managed_by_yunohost_step1"] + def migrate(self): settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) regen_conf(names=['ssh'], force=True) @@ -44,7 +46,7 @@ class MyMigration(Migration): def backward(self): - raise YunohostError("migration_0008_backward_impossible") + raise YunohostError("migration_backward_impossible", name=self.name) @property def mode(self): diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a15b5593e..9117f42de 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1052,6 +1052,15 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal all_migrations = _get_migrations_list() + # Small utility that allows up to get a migration given a name, id or number later + def get_matching_migration(target): + for m in all_migrations: + if m.id == target or m.name == target or m.id.split("_")[0] == target: + return m + + raise YunohostError("No such migration called %s" % target) + + # auto, skip, revert and force are exclusive options if auto + skip + revert + force_rerun > 1: raise YunohostError("--auto, --skip, --revert and --force-rerun are exclusive options.") @@ -1067,13 +1076,6 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # If explicit targets are provided, we shall validate them else: - def get_matching_migration(target): - for m in all_migrations: - if m.id == target or m.name == target or m.id.split("_")[0] == target: - return m - - raise YunohostError("No such migration called %s" % target) - targets = [get_matching_migration(t) for t in targets] done = [t.id for t in targets if t.state != "pending"] pending = [t.id for t in targets if t.state == "pending"] @@ -1103,6 +1105,14 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # We go to the next migration continue + # Check for migration dependencies + if not revert and not skip: + dependencies = [get_matching_migration(dep) for dep in migration.dependencies] + pending_dependencies = [dep.id for dep in dependencies if dep.state == "pending"] + if pending_dependencies: + logger.error("Can't run migration %s because first you need to run these migrations: %s" % (migration.id, ', '.join(pending_dependencies))) + continue + # If some migrations have disclaimers (and we're not trying to skip them) if migration.disclaimer and not skip: # require the --accept-disclaimer option. @@ -1308,6 +1318,7 @@ class Migration(object): # Those are to be implemented by daughter classes mode = "auto" + dependencies = [] # List of migration ids required before running this migration def forward(self): raise NotImplementedError() From dedc85e52a9b63df171287395e4da96a3a1227dc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 5 Aug 2019 21:45:45 +0200 Subject: [PATCH 011/299] Implement / clean strings + fix some edge cases --- locales/en.json | 30 ++++++++++-------- src/yunohost/tools.py | 73 +++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/locales/en.json b/locales/en.json index 14aee512d..471f0f9dc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -369,22 +369,26 @@ "migration_0011_rollback_success": "Rollback succeeded.", "migration_0011_update_LDAP_database": "Updating LDAP database...", "migration_0011_update_LDAP_schema": "Updating LDAP schema...", - "migrations_backward": "Migrating backward.", - "migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}", + "migrations_already_ran": "Those migrations have already been ran: {ids}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", - "migrations_current_target": "Migration target is {}", - "migrations_error_failed_to_load_migration": "ERROR: failed to load migration {number} {name}", - "migrations_forward": "Migrating forward", + "migrations_dependencies_not_satisfied": "Can't run migration {id} because first you need to run these migrations: {dependencies_id}", + "migrations_failed_to_load_migration": "Failed to load migration {id} : {error}", + "migrations_exclusive_options": "--auto, --skip, --revert and --force-rerun are exclusive options.", "migrations_list_conflict_pending_done": "You cannot use both --previous and --done at the same time.", - "migrations_loading_migration": "Loading migration {number} {name}…", - "migrations_migration_has_failed": "Migration {number} {name} has failed with exception {exception}, aborting", + "migrations_loading_migration": "Loading migration {id}…", + "migrations_migration_has_failed": "Migration {id} has failed, aborting. Error: {exception}", + "migrations_must_provide_explicit_targets": "You must provide explicit targets when using --skip, --revert or --force-rerun", + "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option --accept-disclaimer.", "migrations_no_migrations_to_run": "No migrations to run", - "migrations_show_currently_running_migration": "Running migration {number} {name}…", - "migrations_show_last_migration": "Last ran migration is {}", - "migrations_skip_migration": "Skipping migration {number} {name}…", - "migrations_success": "Successfully ran migration {number} {name}!", - "migrations_to_be_ran_manually": "Migration {number} {name} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", - "migrations_need_to_accept_disclaimer": "To run the migration {number} {name}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option --accept-disclaimer.", + "migrations_no_such_migration": "No such migration called {id}", + "migrations_not_pending_cant_skip": "Those migrations are not pending so cannot be skipped: {ids}", + "migrations_pending_cant_revert_or_rerun": "Those migrations are still pending so cannot be reverted or reran: {ids}", + "migrations_running_forward": "Running migration {id}…", + "migrations_running_backward": "Attempting to revert migration {id}…", + "migrations_skip_migration": "Skipping migration {id}…", + "migrations_success_forward": "Successfully ran migration {id}!", + "migrations_success_revert": "Successfully reverted migration {id}!", + "migrations_to_be_ran_manually": "Migration {id} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", "monitor_disabled": "The server monitoring has been disabled", "monitor_enabled": "The server monitoring has been enabled", "monitor_glances_con_failed": "Unable to connect to Glances server", diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 9117f42de..fa46721c9 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1042,12 +1042,12 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal """ Perform migrations - targets : a list of migrations to act on (by default : all pending) - auto : automatic mode, run only 'automatic' migrations (compared to manual migrations). Option meant to be used in debian's postinst script during upgades. - skip : skip specified migrations (must explicit which migrations) - revert : to revert already-ran migrations (must explicit which migrations) - force_rerun : to re-run already ran migrations (if you know what you're doing...) (must explicit which migrations) - accept_disclaimer : accept disclaimer for manual migration that requires it (only valid for one migration) + targets A list migrations to run (all pendings by default) + --skip Skip specified migrations (to be used only if you know what you are doing) (must explicit which migrations) + --auto Automatic mode, won't run manual migrations (to be used only if you know what you are doing) (must explicit which migrations) + --force-rerun Re-run already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) + --revert Attempt to revert already-ran migrations (must explicit which migrations) + --accept-disclaimer Accept disclaimers of migrations (please read them before using this option) (only valid for one migration) """ all_migrations = _get_migrations_list() @@ -1058,18 +1058,18 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal if m.id == target or m.name == target or m.id.split("_")[0] == target: return m - raise YunohostError("No such migration called %s" % target) + raise YunohostError("migrations_no_such_migration", id=target) # auto, skip, revert and force are exclusive options if auto + skip + revert + force_rerun > 1: - raise YunohostError("--auto, --skip, --revert and --force-rerun are exclusive options.") + raise YunohostError("migrations_exclusive_options") # If no target specified if not targets: # skip, revert or force require explicit targets if (revert or force_rerun): - raise YunohostError("You must provide explicit targets when using --skip, --revert or --force-rerun") + raise YunohostError("migrations_must_provide_explicit_targets") # Otherwise, targets are all pending migrations targets = [m for m in all_migrations if m.state == "pending"] @@ -1081,9 +1081,11 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal pending = [t.id for t in targets if t.state == "pending"] if skip and done: - raise YunohostError("Those migrations are not pending so cannot be skipped: %s" % ', '.join(done)) + raise YunohostError("migrations_not_pending_cant_skip", ids=', '.join(done)) if (revert or force_rerun) and pending: - raise YunohostError("Those migrations were not already ran so cannot be reverted or reran: %s" % ', '.join(pending)) + raise YunohostError("migrations_pending_cant_revert_or_rerun", ids=', '.join(pending)) + if not (skip or revert or force_rerun) and done: + raise YunohostError("migrations_already_ran", ids=', '.join(done)) # So, is there actually something to do ? if not targets: @@ -1098,9 +1100,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # to be ran manually by the user, stop there and ask the user to # run the migration manually. if auto and migration.mode == "manual": - logger.warn(m18n.n('migrations_to_be_ran_manually', - number=migration.number, - name=migration.name)) + logger.warn(m18n.n('migrations_to_be_ran_manually', id=migration.id)) # We go to the next migration continue @@ -1110,17 +1110,18 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal dependencies = [get_matching_migration(dep) for dep in migration.dependencies] pending_dependencies = [dep.id for dep in dependencies if dep.state == "pending"] if pending_dependencies: - logger.error("Can't run migration %s because first you need to run these migrations: %s" % (migration.id, ', '.join(pending_dependencies))) + logger.error(m18n.n('migrations_dependencies_not_satisfied', + id=migration.id, + dependencies_id=', '.join(pending_dependencies))) continue # If some migrations have disclaimers (and we're not trying to skip them) - if migration.disclaimer and not skip: + if migration.disclaimer and not skip and not revert: # require the --accept-disclaimer option. # Otherwise, go to the next migration if not accept_disclaimer: logger.warn(m18n.n('migrations_need_to_accept_disclaimer', - number=migration.number, - name=migration.name, + id=migration.id, disclaimer=migration.disclaimer)) continue # --accept-disclaimer will only work for the first migration @@ -1133,35 +1134,37 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal operation_logger.start() if skip: - logger.warn(m18n.n('migrations_skip_migration', - number=migration.number, - name=migration.name)) + logger.warn(m18n.n('migrations_skip_migration', id=migration.id)) + migration.state = "skipped" _write_migration_state(migration.id, "skipped") operation_logger.success() else: - logger.info(m18n.n('migrations_show_currently_running_migration', - number=migration.number, name=migration.name)) - try: migration.operation_logger = operation_logger if revert: + logger.info(m18n.n('migrations_running_backward', id=migration.id)) migration.backward() else: + logger.info(m18n.n('migrations_running_forward', id=migration.id)) migration.migrate() except Exception as e: # migration failed, let's stop here but still update state because # we managed to run the previous ones msg = m18n.n('migrations_migration_has_failed', - exception=e, - number=migration.number, - name=migration.name) + exception=e, id=migration.id) logger.error(msg, exc_info=1) operation_logger.error(msg) else: - logger.success(m18n.n('migrations_success', - number=migration.number, name=migration.name)) - _write_migration_state(migration.id, "done") + if revert: + logger.success(m18n.n('migrations_success_revert', id=migration.id)) + migration.state = "pending" + _write_migration_state(migration.id, "pending") + else: + logger.success(m18n.n('migrations_success_forward', id=migration.id)) + migration.state = "done" + _write_migration_state(migration.id, "done") + operation_logger.success() @@ -1274,10 +1277,7 @@ def _load_migration(migration_file): migration_id = migration_file[:-len(".py")] - number, name = migration_id.split("_", 1) - - logger.debug(m18n.n('migrations_loading_migration', - number=number, name=name)) + logger.debug(m18n.n('migrations_loading_migration', id=migration_id)) try: # this is python builtin method to import a module using a name, we @@ -1285,12 +1285,11 @@ def _load_migration(migration_file): # able to run it in the next loop module = import_module("yunohost.data_migrations.{}".format(migration_id)) return module.MyMigration(migration_id) - except Exception: + except Exception as e: import traceback traceback.print_exc() - raise YunohostError('migrations_error_failed_to_load_migration', - number=number, name=name) + raise YunohostError('migrations_failed_to_load_migration', id=migration_id, error=e) def _skip_all_migrations(): From 74b5ea5982099a7661f169c36e89efb0d6b69df2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 5 Aug 2019 22:07:48 +0200 Subject: [PATCH 012/299] Propagate changes to _skip_all_migrations --- src/yunohost/tools.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index fa46721c9..a6379e62b 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1298,18 +1298,11 @@ def _skip_all_migrations(): This is meant to be used during postinstall to initialize the migration system. """ - state = tools_migrations_state() - - # load all migrations - migrations = _get_migrations_list() - migrations = sorted(migrations, key=lambda x: x.number) - last_migration = migrations[-1] - - state["last_run_migration"] = { - "number": last_migration.number, - "name": last_migration.name - } - write_to_json(MIGRATIONS_STATE_PATH, state) + all_migrations = _get_migrations_list() + new_states = {"migrations": {}} + for migration in all_migrations: + new_states["migrations"][migration.id] = "skipped" + write_to_yaml(MIGRATIONS_STATE_PATH, new_states) class Migration(object): From fc426cff69536417374f334c08dc4e75a59f7941 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 12:07:39 +0200 Subject: [PATCH 013/299] [fix] Avoid to revert migrations without backward routine --- src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py | 4 ---- src/yunohost/data_migrations/0003_migrate_to_stretch.py | 6 +----- src/yunohost/data_migrations/0004_php5_to_php7_pools.py | 2 +- src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py | 6 +----- .../data_migrations/0006_sync_admin_and_root_passwords.py | 5 +---- .../0007_ssh_conf_managed_by_yunohost_step1.py | 2 +- .../0008_ssh_conf_managed_by_yunohost_step2.py | 6 +----- .../0009_decouple_regenconf_from_services.py | 5 +---- src/yunohost/data_migrations/0010_migrate_to_apps_json.py | 2 +- src/yunohost/data_migrations/0011_setup_group_permission.py | 2 +- src/yunohost/tools.py | 2 +- 11 files changed, 10 insertions(+), 32 deletions(-) diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index 824245c82..cc5d4c19b 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -19,10 +19,6 @@ class MyMigration(Migration): "Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG" - def backward(self): - # Not possible because that's a non-reversible operation ? - pass - def migrate(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None): if domain is None or private_key_path is None: diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 8b7586701..0db6cfd6d 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -29,11 +29,7 @@ class MyMigration(Migration): mode = "manual" - def backward(self): - - raise YunohostError("migration_backward_impossible", name=self.name) - - def migrate(self): + def forward(self): self.logfile = "/var/log/yunohost/{}.log".format(self.name) diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index faf9ccbbc..e916a9cd7 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -24,7 +24,7 @@ class MyMigration(Migration): dependencies = ["migrate_to_stretch"] - def migrate(self): + def forward(self): # Get list of php5 pool files php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 50c0561cb..9cb6e51bd 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -16,7 +16,7 @@ class MyMigration(Migration): dependencies = ["migrate_to_stretch"] - def migrate(self): + def forward(self): if not self.package_is_installed("postgresql-9.4"): logger.warning(m18n.n("migration_0005_postgresql_94_not_installed")) @@ -34,10 +34,6 @@ class MyMigration(Migration): subprocess.check_call("pg_dropcluster --stop 9.4 main", shell=True) subprocess.check_call("service postgresql start", shell=True) - def backward(self): - - pass - def package_is_installed(self, package_name): p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True) diff --git a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py index cd13d680d..953652111 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -20,16 +20,13 @@ class MyMigration(Migration): "Synchronize admin and root passwords" - def migrate(self): + def forward(self): new_hash = self._get_admin_hash() self._replace_root_hash(new_hash) logger.info(m18n.n("root_password_replaced_by_admin_password")) - def backward(self): - pass - @property def mode(self): diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py index feffdc27c..7ddcec7fd 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py @@ -34,7 +34,7 @@ class MyMigration(Migration): use the recommended configuration, with an appropriate disclaimer. """ - def migrate(self): + def forward(self): # Check if deprecated DSA Host Key is in config dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$' diff --git a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py index 0ea0ddc3a..c158192f9 100644 --- a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py @@ -35,7 +35,7 @@ class MyMigration(Migration): dependencies = ["ssh_conf_managed_by_yunohost_step1"] - def migrate(self): + def forward(self): settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) regen_conf(names=['ssh'], force=True) @@ -44,10 +44,6 @@ class MyMigration(Migration): if os.path.isdir(ARCHIVES_PATH): chown(ARCHIVES_PATH, uid="admin", gid="root") - def backward(self): - - raise YunohostError("migration_backward_impossible", name=self.name) - @property def mode(self): diff --git a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py b/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py index d552d7c9c..ffe2279a8 100644 --- a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py +++ b/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py @@ -17,7 +17,7 @@ class MyMigration(Migration): Decouple the regen conf mechanism from the concept of services """ - def migrate(self): + def forward(self): if "conffiles" not in read_file("/etc/yunohost/services.yml") \ or os.path.exists(REGEN_CONF_FILE): @@ -37,6 +37,3 @@ class MyMigration(Migration): # (Actually save the modification of services) _save_services(services) - def backward(self): - - pass diff --git a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py index 43ae9a86f..7092d92c7 100644 --- a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py +++ b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py @@ -15,7 +15,7 @@ class MyMigration(Migration): "Migrate from official.json to apps.json" - def migrate(self): + def forward(self): # Backup current app list json os.system("cp %s %s" % (APPSLISTS_JSON, APPSLISTS_BACKUP)) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 05c426936..0e371944e 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -92,7 +92,7 @@ class MyMigration(Migration): app_setting(app, 'allowed_users', delete=True) - def migrate(self): + def forward(self): # Check if the migration can be processed ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) # By this we check if the have been customized diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a6379e62b..59758acdf 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1316,7 +1316,7 @@ class Migration(object): raise NotImplementedError() def backward(self): - pass + raise YunohostError("migration_backward_impossible", name=self.name) @property def disclaimer(self): From 81ae41a3e4d5b28b6f44c46ad64a76d2e6cc3bfc Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 12:27:32 +0200 Subject: [PATCH 014/299] [enh] Warn user about --revert option --- data/actionsmap/yunohost.yml | 2 +- src/yunohost/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index f54cc2254..a4dc8d453 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1736,7 +1736,7 @@ tools: help: Skip specified migrations (to be used only if you know what you are doing) action: store_true --revert: - help: Attempt to revert already-ran migrations + help: Attempt to revert already-ran migrations (to be used only if you know what you are doing) action: store_true --force-rerun: help: Re-run already-ran migrations (to be used only if you know what you are doing) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 59758acdf..39333bcaf 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1046,7 +1046,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal --skip Skip specified migrations (to be used only if you know what you are doing) (must explicit which migrations) --auto Automatic mode, won't run manual migrations (to be used only if you know what you are doing) (must explicit which migrations) --force-rerun Re-run already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) - --revert Attempt to revert already-ran migrations (must explicit which migrations) + --revert Attempt to revert already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) --accept-disclaimer Accept disclaimers of migrations (please read them before using this option) (only valid for one migration) """ From c81eab396b5d430b268c1581f942eb234148c272 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 12:52:47 +0200 Subject: [PATCH 015/299] [enh] Pep8 --- src/yunohost/tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 39333bcaf..8c07855a3 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1060,7 +1060,6 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal raise YunohostError("migrations_no_such_migration", id=target) - # auto, skip, revert and force are exclusive options if auto + skip + revert + force_rerun > 1: raise YunohostError("migrations_exclusive_options") From 7a0dbf79773785634b0e16720ba6067fb2c034d3 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 13:05:23 +0200 Subject: [PATCH 016/299] [fix] Ask for specifying migrations to skip --- data/actionsmap/yunohost.yml | 4 ++-- src/yunohost/tools.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index a4dc8d453..054171133 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1736,10 +1736,10 @@ tools: help: Skip specified migrations (to be used only if you know what you are doing) action: store_true --revert: - help: Attempt to revert already-ran migrations (to be used only if you know what you are doing) + help: Attempt to revert already-ran specified migrations (to be used only if you know what you are doing) action: store_true --force-rerun: - help: Re-run already-ran migrations (to be used only if you know what you are doing) + help: Re-run already-ran specified migration (to be used only if you know what you are doing) action: store_true --auto: help: Automatic mode, won't run manual migrations (to be used only if you know what you are doing) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 8c07855a3..9d341ca3d 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1044,7 +1044,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal targets A list migrations to run (all pendings by default) --skip Skip specified migrations (to be used only if you know what you are doing) (must explicit which migrations) - --auto Automatic mode, won't run manual migrations (to be used only if you know what you are doing) (must explicit which migrations) + --auto Automatic mode, won't run manual migrations (to be used only if you know what you are doing) --force-rerun Re-run already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) --revert Attempt to revert already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) --accept-disclaimer Accept disclaimers of migrations (please read them before using this option) (only valid for one migration) @@ -1067,7 +1067,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # If no target specified if not targets: # skip, revert or force require explicit targets - if (revert or force_rerun): + if (skip or revert or force_rerun): raise YunohostError("migrations_must_provide_explicit_targets") # Otherwise, targets are all pending migrations From f0c0eff41da7a3a1683d6bf4dc4a028a9865f6e9 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 13:13:59 +0200 Subject: [PATCH 017/299] [enh] english sentence in a comment --- src/yunohost/tools.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 9d341ca3d..d84ca3199 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1095,9 +1095,9 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal for migration in targets: # If we are migrating in "automatic mode" (i.e. from debian configure - # during an upgrade of the package) but we are asked to run migrations - # to be ran manually by the user, stop there and ask the user to - # run the migration manually. + # during an upgrade of the package) but we are asked for running + # migrations to be ran manually by the user, stop there and ask the + # user to run the migration manually. if auto and migration.mode == "manual": logger.warn(m18n.n('migrations_to_be_ran_manually', id=migration.id)) From ed3e014feea6abf17f3baab0f5844510c05dba22 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 16:46:14 +0200 Subject: [PATCH 018/299] [enh] Remove revert mechanism --- data/actionsmap/yunohost.yml | 3 - locales/en.json | 2 - .../0001_change_cert_group_to_sslcert.py | 15 +-- .../0002_migrate_to_tsig_sha256.py | 2 +- .../0003_migrate_to_stretch.py | 2 +- .../0004_php5_to_php7_pools.py | 116 +++++++++--------- .../0005_postgresql_9p4_to_9p6.py | 2 +- .../0006_sync_admin_and_root_passwords.py | 2 +- ...0007_ssh_conf_managed_by_yunohost_step1.py | 28 ++--- ...0008_ssh_conf_managed_by_yunohost_step2.py | 2 +- .../0009_decouple_regenconf_from_services.py | 2 +- .../0010_migrate_to_apps_json.py | 28 +++-- .../0011_setup_group_permission.py | 2 +- src/yunohost/tools.py | 50 +++----- 14 files changed, 118 insertions(+), 138 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 054171133..725f14a03 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1735,9 +1735,6 @@ tools: --skip: help: Skip specified migrations (to be used only if you know what you are doing) action: store_true - --revert: - help: Attempt to revert already-ran specified migrations (to be used only if you know what you are doing) - action: store_true --force-rerun: help: Re-run already-ran specified migration (to be used only if you know what you are doing) action: store_true diff --git a/locales/en.json b/locales/en.json index 471f0f9dc..850d89032 100644 --- a/locales/en.json +++ b/locales/en.json @@ -297,7 +297,6 @@ "log_user_permission_remove": "Update '{}' permission", "log_tools_maindomain": "Make '{}' as main domain", "log_tools_migrations_migrate_forward": "Migrate forward", - "log_tools_migrations_migrate_backward": "Migrate backward", "log_tools_postinstall": "Postinstall your YunoHost server", "log_tools_upgrade": "Upgrade system packages", "log_tools_shutdown": "Shutdown your server", @@ -384,7 +383,6 @@ "migrations_not_pending_cant_skip": "Those migrations are not pending so cannot be skipped: {ids}", "migrations_pending_cant_revert_or_rerun": "Those migrations are still pending so cannot be reverted or reran: {ids}", "migrations_running_forward": "Running migration {id}…", - "migrations_running_backward": "Attempting to revert migration {id}…", "migrations_skip_migration": "Skipping migration {id}…", "migrations_success_forward": "Successfully ran migration {id}!", "migrations_success_revert": "Successfully reverted migration {id}!", diff --git a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py index 6485861b7..4338e10f3 100644 --- a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py +++ b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py @@ -10,10 +10,11 @@ class MyMigration(Migration): all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem") - def forward(self): - for filename in self.all_certificate_files: - chown(filename, uid="root", gid="ssl-cert") - - def backward(self): - for filename in self.all_certificate_files: - chown(filename, uid="root", gid="metronome") + def run(self): + try: + for filename in self.all_certificate_files: + chown(filename, uid="root", gid="ssl-cert") + except Exception: + for filename in self.all_certificate_files: + chown(filename, uid="root", gid="metronome") + raise diff --git a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py index cc5d4c19b..65158ba2c 100644 --- a/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py +++ b/src/yunohost/data_migrations/0002_migrate_to_tsig_sha256.py @@ -19,7 +19,7 @@ class MyMigration(Migration): "Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG" - def migrate(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None): + def run(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None): if domain is None or private_key_path is None: try: diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 0db6cfd6d..19793bbec 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -29,7 +29,7 @@ class MyMigration(Migration): mode = "manual" - def forward(self): + def run(self): self.logfile = "/var/log/yunohost/{}.log".format(self.name) diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index e916a9cd7..7bb9123ec 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -24,77 +24,77 @@ class MyMigration(Migration): dependencies = ["migrate_to_stretch"] - def forward(self): + def run(self): + try: + # Get list of php5 pool files + php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) - # Get list of php5 pool files - php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) + # Keep only basenames + php5_pool_files = [os.path.basename(f) for f in php5_pool_files] - # Keep only basenames - php5_pool_files = [os.path.basename(f) for f in php5_pool_files] + # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) + php5_pool_files = [f for f in php5_pool_files if f != "www.conf"] - # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) - php5_pool_files = [f for f in php5_pool_files if f != "www.conf"] + for f in php5_pool_files: - for f in php5_pool_files: + # Copy the files to the php7 pool + src = "{}/{}".format(PHP5_POOLS, f) + dest = "{}/{}".format(PHP7_POOLS, f) + copy2(src, dest) - # Copy the files to the php7 pool - src = "{}/{}".format(PHP5_POOLS, f) - dest = "{}/{}".format(PHP7_POOLS, f) - copy2(src, dest) + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest) + os.system(c) - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest) - os.system(c) + # Also add a comment that it was automatically moved from php5 + # (for human traceability and backward migration) + c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) + os.system(c) - # Also add a comment that it was automatically moved from php5 - # (for human traceability and backward migration) - c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) - os.system(c) + # Some old comments starting with '#' instead of ';' are not + # compatible in php7 + c = "sed -i 's/^#/;#/g' {}".format(dest) + os.system(c) - # Some old comments starting with '#' instead of ';' are not - # compatible in php7 - c = "sed -i 's/^#/;#/g' {}".format(dest) - os.system(c) + # Reload/restart the php pools + _run_service_command("restart", "php7.0-fpm") + _run_service_command("enable", "php7.0-fpm") + os.system("systemctl stop php5-fpm") + os.system("systemctl disable php5-fpm") + os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy - # Reload/restart the php pools - _run_service_command("restart", "php7.0-fpm") - _run_service_command("enable", "php7.0-fpm") - os.system("systemctl stop php5-fpm") - os.system("systemctl disable php5-fpm") - os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy + # Get list of nginx conf file + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f) + os.system(c) - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f) - os.system(c) + # Reload nginx + _run_service_command("reload", "nginx") + except Exception: - # Reload nginx - _run_service_command("reload", "nginx") + # Get list of php7 pool files + php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) - def backward(self): + # Keep only files which have the migration comment + php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT] - # Get list of php7 pool files - php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) + # Delete those files + for f in php7_pool_files: + os.remove(f) - # Keep only files which have the migration comment - php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT] + # Reload/restart the php pools + _run_service_command("stop", "php7.0-fpm") + os.system("systemctl start php5-fpm") - # Delete those files - for f in php7_pool_files: - os.remove(f) + # Get list of nginx conf file + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f) + os.system(c) - # Reload/restart the php pools - _run_service_command("stop", "php7.0-fpm") - os.system("systemctl start php5-fpm") - - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f) - os.system(c) - - # Reload nginx - _run_service_command("reload", "nginx") + # Reload nginx + _run_service_command("reload", "nginx") + raise diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 9cb6e51bd..3127f2c65 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -16,7 +16,7 @@ class MyMigration(Migration): dependencies = ["migrate_to_stretch"] - def forward(self): + def run(self): if not self.package_is_installed("postgresql-9.4"): logger.warning(m18n.n("migration_0005_postgresql_94_not_installed")) diff --git a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py index 953652111..844e3028c 100644 --- a/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py +++ b/src/yunohost/data_migrations/0006_sync_admin_and_root_passwords.py @@ -20,7 +20,7 @@ class MyMigration(Migration): "Synchronize admin and root passwords" - def forward(self): + def run(self): new_hash = self._get_admin_hash() self._replace_root_hash(new_hash) diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py index 7ddcec7fd..99eb90b19 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py @@ -34,7 +34,7 @@ class MyMigration(Migration): use the recommended configuration, with an appropriate disclaimer. """ - def forward(self): + def run(self): # Check if deprecated DSA Host Key is in config dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$' @@ -55,19 +55,13 @@ class MyMigration(Migration): # right after the regenconf, such that it will appear as # "manually modified". if os.path.exists('/etc/yunohost/from_script'): - rm('/etc/yunohost/from_script') - copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') - regen_conf(names=['ssh'], force=True) - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - - # Restart ssh and backward if it fail - if not _run_service_command('restart', 'ssh'): - self.backward() - raise YunohostError("migration_0007_cancel") - - def backward(self): - - # We don't backward completely but it should be enough - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - if not _run_service_command('restart', 'ssh'): - raise YunohostError("migration_0007_cannot_restart") + try: + rm('/etc/yunohost/from_script') + copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') + regen_conf(names=['ssh'], force=True) + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) + except Exception: + if os.path.exists('/etc/yunohost/sshd_config.bkp'): + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) + _run_service_command('restart', 'ssh') + raise diff --git a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py index c158192f9..ecc8cfdcb 100644 --- a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py @@ -35,7 +35,7 @@ class MyMigration(Migration): dependencies = ["ssh_conf_managed_by_yunohost_step1"] - def forward(self): + def run(self): settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) regen_conf(names=['ssh'], force=True) diff --git a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py b/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py index ffe2279a8..c190e2aaa 100644 --- a/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py +++ b/src/yunohost/data_migrations/0009_decouple_regenconf_from_services.py @@ -17,7 +17,7 @@ class MyMigration(Migration): Decouple the regen conf mechanism from the concept of services """ - def forward(self): + def run(self): if "conffiles" not in read_file("/etc/yunohost/services.yml") \ or os.path.exists(REGEN_CONF_FILE): diff --git a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py index 7092d92c7..fbcec5117 100644 --- a/src/yunohost/data_migrations/0010_migrate_to_apps_json.py +++ b/src/yunohost/data_migrations/0010_migrate_to_apps_json.py @@ -15,7 +15,7 @@ class MyMigration(Migration): "Migrate from official.json to apps.json" - def forward(self): + def run(self): # Backup current app list json os.system("cp %s %s" % (APPSLISTS_JSON, APPSLISTS_BACKUP)) @@ -28,17 +28,21 @@ class MyMigration(Migration): "labriqueinter.net/apps/labriqueinternet.json", "labriqueinter.net/internetcube.json" ] + try: + appslists = _read_appslist_list() + for appslist, infos in appslists.items(): + if infos["url"].split("//")[-1] in lists_to_remove: + app_removelist(name=appslist) - appslists = _read_appslist_list() - for appslist, infos in appslists.items(): - if infos["url"].split("//")[-1] in lists_to_remove: - app_removelist(name=appslist) + # Replace by apps.json list + app_fetchlist(name="yunohost", + url="https://app.yunohost.org/apps.json") + except Exception: + if os.path.exists(APPSLISTS_BACKUP): + os.system("cp %s %s" % (APPSLISTS_BACKUP, APPSLISTS_JSON)) + raise + else: + if os.path.exists(APPSLISTS_BACKUP): + os.remove(APPSLISTS_BACKUP) - # Replace by apps.json list - app_fetchlist(name="yunohost", - url="https://app.yunohost.org/apps.json") - def backward(self): - - if os.path.exists(APPSLISTS_BACKUP): - os.system("cp %s %s" % (APPSLISTS_BACKUP, APPSLISTS_JSON)) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 0e371944e..17fd8f4a5 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -92,7 +92,7 @@ class MyMigration(Migration): app_setting(app, 'allowed_users', delete=True) - def forward(self): + def run(self): # Check if the migration can be processed ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) # By this we check if the have been customized diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index d84ca3199..1c185e90c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1038,7 +1038,7 @@ def tools_migrations_list(pending=False, done=False): return {"migrations": migrations} -def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=False, revert=False, accept_disclaimer=False): +def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=False, accept_disclaimer=False): """ Perform migrations @@ -1046,7 +1046,6 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal --skip Skip specified migrations (to be used only if you know what you are doing) (must explicit which migrations) --auto Automatic mode, won't run manual migrations (to be used only if you know what you are doing) --force-rerun Re-run already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) - --revert Attempt to revert already-ran migrations (to be used only if you know what you are doing)(must explicit which migrations) --accept-disclaimer Accept disclaimers of migrations (please read them before using this option) (only valid for one migration) """ @@ -1060,14 +1059,14 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal raise YunohostError("migrations_no_such_migration", id=target) - # auto, skip, revert and force are exclusive options - if auto + skip + revert + force_rerun > 1: + # auto, skip and force are exclusive options + if auto + skip + force_rerun > 1: raise YunohostError("migrations_exclusive_options") # If no target specified if not targets: # skip, revert or force require explicit targets - if (skip or revert or force_rerun): + if (skip or force_rerun): raise YunohostError("migrations_must_provide_explicit_targets") # Otherwise, targets are all pending migrations @@ -1081,9 +1080,9 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal if skip and done: raise YunohostError("migrations_not_pending_cant_skip", ids=', '.join(done)) - if (revert or force_rerun) and pending: + if force_rerun and pending: raise YunohostError("migrations_pending_cant_revert_or_rerun", ids=', '.join(pending)) - if not (skip or revert or force_rerun) and done: + if not (skip or force_rerun) and done: raise YunohostError("migrations_already_ran", ids=', '.join(done)) # So, is there actually something to do ? @@ -1105,7 +1104,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal continue # Check for migration dependencies - if not revert and not skip: + if not skip: dependencies = [get_matching_migration(dep) for dep in migration.dependencies] pending_dependencies = [dep.id for dep in dependencies if dep.state == "pending"] if pending_dependencies: @@ -1115,7 +1114,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal continue # If some migrations have disclaimers (and we're not trying to skip them) - if migration.disclaimer and not skip and not revert: + if migration.disclaimer and not skip: # require the --accept-disclaimer option. # Otherwise, go to the next migration if not accept_disclaimer: @@ -1128,8 +1127,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal accept_disclaimer = False # Start register change on system - mode = "backward" if revert else "forward" - operation_logger = OperationLogger('tools_migrations_migrate_' + mode) + operation_logger = OperationLogger('tools_migrations_migrate_forward') operation_logger.start() if skip: @@ -1141,12 +1139,8 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal try: migration.operation_logger = operation_logger - if revert: - logger.info(m18n.n('migrations_running_backward', id=migration.id)) - migration.backward() - else: logger.info(m18n.n('migrations_running_forward', id=migration.id)) - migration.migrate() + migration.run() except Exception as e: # migration failed, let's stop here but still update state because # we managed to run the previous ones @@ -1155,14 +1149,9 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal logger.error(msg, exc_info=1) operation_logger.error(msg) else: - if revert: - logger.success(m18n.n('migrations_success_revert', id=migration.id)) - migration.state = "pending" - _write_migration_state(migration.id, "pending") - else: - logger.success(m18n.n('migrations_success_forward', id=migration.id)) - migration.state = "done" - _write_migration_state(migration.id, "done") + logger.success(m18n.n('migrations_success_forward', id=migration.id)) + migration.state = "done" + _write_migration_state(migration.id, "done") operation_logger.success() @@ -1304,6 +1293,9 @@ def _skip_all_migrations(): write_to_yaml(MIGRATIONS_STATE_PATH, new_states) +def _get_revert_dependencies(migration, all_migrations): + + class Migration(object): # Those are to be implemented by daughter classes @@ -1311,20 +1303,14 @@ class Migration(object): mode = "auto" dependencies = [] # List of migration ids required before running this migration - def forward(self): - raise NotImplementedError() - - def backward(self): - raise YunohostError("migration_backward_impossible", name=self.name) - @property def disclaimer(self): return None # The followings shouldn't be overriden - def migrate(self): - self.forward() + def run(self): + raise NotImplementedError() def __init__(self, id_): self.id = id_ From fac44f44d06f120625cbc69fefbb3e28f57093d2 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 17:29:24 +0200 Subject: [PATCH 019/299] [enh] Avoid to modify untestable migrations --- .../0001_change_cert_group_to_sslcert.py | 9 ++------- .../0007_ssh_conf_managed_by_yunohost_step1.py | 14 ++++---------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py index 4338e10f3..5670f3329 100644 --- a/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py +++ b/src/yunohost/data_migrations/0001_change_cert_group_to_sslcert.py @@ -11,10 +11,5 @@ class MyMigration(Migration): all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem") def run(self): - try: - for filename in self.all_certificate_files: - chown(filename, uid="root", gid="ssl-cert") - except Exception: - for filename in self.all_certificate_files: - chown(filename, uid="root", gid="metronome") - raise + for filename in self.all_certificate_files: + chown(filename, uid="root", gid="ssl-cert") diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py index 99eb90b19..818760e17 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py @@ -55,13 +55,7 @@ class MyMigration(Migration): # right after the regenconf, such that it will appear as # "manually modified". if os.path.exists('/etc/yunohost/from_script'): - try: - rm('/etc/yunohost/from_script') - copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') - regen_conf(names=['ssh'], force=True) - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - except Exception: - if os.path.exists('/etc/yunohost/sshd_config.bkp'): - copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) - _run_service_command('restart', 'ssh') - raise + rm('/etc/yunohost/from_script') + copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') + regen_conf(names=['ssh'], force=True) + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) From 0a2ba102c883c0a5b96cf451ab8a7a0c6fedb0dd Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 17:37:43 +0200 Subject: [PATCH 020/299] [enh] Avoid to modify untestable migrations --- .../0004_php5_to_php7_pools.py | 77 +++++++++---------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index 7bb9123ec..3bc6bacc4 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -25,54 +25,54 @@ class MyMigration(Migration): dependencies = ["migrate_to_stretch"] def run(self): - try: - # Get list of php5 pool files - php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) + # Get list of php5 pool files + php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS)) - # Keep only basenames - php5_pool_files = [os.path.basename(f) for f in php5_pool_files] + # Keep only basenames + php5_pool_files = [os.path.basename(f) for f in php5_pool_files] - # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) - php5_pool_files = [f for f in php5_pool_files if f != "www.conf"] + # Ignore the "www.conf" (default stuff, probably don't want to touch it ?) + php5_pool_files = [f for f in php5_pool_files if f != "www.conf"] - for f in php5_pool_files: + for f in php5_pool_files: - # Copy the files to the php7 pool - src = "{}/{}".format(PHP5_POOLS, f) - dest = "{}/{}".format(PHP7_POOLS, f) - copy2(src, dest) + # Copy the files to the php7 pool + src = "{}/{}".format(PHP5_POOLS, f) + dest = "{}/{}".format(PHP7_POOLS, f) + copy2(src, dest) - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest) - os.system(c) + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest) + os.system(c) - # Also add a comment that it was automatically moved from php5 - # (for human traceability and backward migration) - c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) - os.system(c) + # Also add a comment that it was automatically moved from php5 + # (for human traceability and backward migration) + c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest) + os.system(c) - # Some old comments starting with '#' instead of ';' are not - # compatible in php7 - c = "sed -i 's/^#/;#/g' {}".format(dest) - os.system(c) + # Some old comments starting with '#' instead of ';' are not + # compatible in php7 + c = "sed -i 's/^#/;#/g' {}".format(dest) + os.system(c) - # Reload/restart the php pools - _run_service_command("restart", "php7.0-fpm") - _run_service_command("enable", "php7.0-fpm") - os.system("systemctl stop php5-fpm") - os.system("systemctl disable php5-fpm") - os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy + # Reload/restart the php pools + _run_service_command("restart", "php7.0-fpm") + _run_service_command("enable", "php7.0-fpm") + os.system("systemctl stop php5-fpm") + os.system("systemctl disable php5-fpm") + os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f) - os.system(c) + # Get list of nginx conf file + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f) + os.system(c) - # Reload nginx - _run_service_command("reload", "nginx") - except Exception: + # Reload nginx + _run_service_command("reload", "nginx") + + def backward(self): # Get list of php7 pool files php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) @@ -97,4 +97,3 @@ class MyMigration(Migration): # Reload nginx _run_service_command("reload", "nginx") - raise From f98ad4f191d7ac7aea84a17df09c36c7d2538e70 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 17:39:23 +0200 Subject: [PATCH 021/299] [enh] Avoid to modify untestable migrations --- .../0004_php5_to_php7_pools.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index 3bc6bacc4..1b90c4ff0 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -72,28 +72,28 @@ class MyMigration(Migration): # Reload nginx _run_service_command("reload", "nginx") - def backward(self): + def backward(self): - # Get list of php7 pool files - php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) + # Get list of php7 pool files + php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS)) - # Keep only files which have the migration comment - php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT] + # Keep only files which have the migration comment + php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT] - # Delete those files - for f in php7_pool_files: - os.remove(f) + # Delete those files + for f in php7_pool_files: + os.remove(f) - # Reload/restart the php pools - _run_service_command("stop", "php7.0-fpm") - os.system("systemctl start php5-fpm") + # Reload/restart the php pools + _run_service_command("stop", "php7.0-fpm") + os.system("systemctl start php5-fpm") - # Get list of nginx conf file - nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") - for f in nginx_conf_files: - # Replace the socket prefix if it's found - c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f) - os.system(c) + # Get list of nginx conf file + nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf") + for f in nginx_conf_files: + # Replace the socket prefix if it's found + c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f) + os.system(c) - # Reload nginx - _run_service_command("reload", "nginx") + # Reload nginx + _run_service_command("reload", "nginx") From b54266cbf3ffb5f3b38ba3280474b01ef1aedcb5 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 14 Aug 2019 17:55:17 +0200 Subject: [PATCH 022/299] [fix] Unwanted residual code --- src/yunohost/tools.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 1c185e90c..d2015adff 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1139,8 +1139,8 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal try: migration.operation_logger = operation_logger - logger.info(m18n.n('migrations_running_forward', id=migration.id)) - migration.run() + logger.info(m18n.n('migrations_running_forward', id=migration.id)) + migration.run() except Exception as e: # migration failed, let's stop here but still update state because # we managed to run the previous ones @@ -1293,9 +1293,6 @@ def _skip_all_migrations(): write_to_yaml(MIGRATIONS_STATE_PATH, new_states) -def _get_revert_dependencies(migration, all_migrations): - - class Migration(object): # Those are to be implemented by daughter classes From 82f48213ab21247fff96ae493cea879accfd9897 Mon Sep 17 00:00:00 2001 From: tufek yamero Date: Tue, 13 Aug 2019 19:40:47 +0000 Subject: [PATCH 023/299] Translated using Weblate (French) Currently translated at 89.9% (523 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index cce49c7a3..e89e999a5 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -17,7 +17,7 @@ "app_manifest_invalid": "Manifeste d’application incorrect : {error}", "app_no_upgrade": "Aucune application à mettre à jour", "app_not_correctly_installed": "{app:s} semble être mal installé", - "app_not_installed": "{app:s} n’est pas installé", + "app_not_installed": "L'application '{app:s}' n’est pas installé. Vous trouverez ici la liste des applications installées: {all_apps}", "app_not_properly_removed": "{app:s} n’a pas été supprimé correctement", "app_package_need_update": "Le paquet de l’application {app} doit être mis à jour pour être en adéquation avec les changements de YunoHost", "app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost", @@ -561,5 +561,6 @@ "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques ...", "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", - "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}" + "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", + "apps_permission_not_found": "Aucune permission n'a été trouvée pour les applications installées" } From 4c7c740aba53c5dad52e44254c6887d12cbfde80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?= Date: Wed, 14 Aug 2019 22:09:21 +0000 Subject: [PATCH 024/299] Translated using Weblate (French) Currently translated at 89.9% (523 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index e89e999a5..f3981335a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -370,7 +370,7 @@ "dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.", "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine {domain} car il est déjà utilisé par l'application '{other_app}'", "app_upgrade_app_name": "Mise à jour de l’application {app} …", - "backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier d’archives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas, vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.", + "backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier d’archives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.", "migrate_tsig_end": "La migration à hmac-sha512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers hmac-sha512 qui est plus sécurisé", @@ -418,7 +418,7 @@ "service_description_yunohost-api": "permet les interactions entre l’interface web de YunoHost et le système", "service_description_yunohost-firewall": "gère l'ouverture et la fermeture des ports de connexion aux services", "experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas l’utiliser à moins que vous ne sachiez ce que vous faites.", - "log_corrupted_md_file": "Le fichier yaml de metadata associé aux logs est corrompu : '{md_file}'", + "log_corrupted_md_file": "Le fichier yaml de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}", "log_category_404": "Le journal de la catégorie '{category}' n’existe pas", "log_link_to_log": "Journal historisé complet de cette opération : ' {desc} '", "log_help_to_get_log": "Pour voir le journal historisé de cette opération '{desc}', utilisez la commande 'yunohost log display {name}'", From 00e8268c8ac9336e41f5da26d89f943f32216898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?= Date: Wed, 14 Aug 2019 22:11:51 +0000 Subject: [PATCH 025/299] Translated using Weblate (French) Currently translated at 90.0% (524 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index f3981335a..607a2a348 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -17,7 +17,7 @@ "app_manifest_invalid": "Manifeste d’application incorrect : {error}", "app_no_upgrade": "Aucune application à mettre à jour", "app_not_correctly_installed": "{app:s} semble être mal installé", - "app_not_installed": "L'application '{app:s}' n’est pas installé. Vous trouverez ici la liste des applications installées: {all_apps}", + "app_not_installed": "L'application « {app:s} » n’est pas installée. Voici la liste des applications installées: {all_apps}", "app_not_properly_removed": "{app:s} n’a pas été supprimé correctement", "app_package_need_update": "Le paquet de l’application {app} doit être mis à jour pour être en adéquation avec les changements de YunoHost", "app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost", @@ -562,5 +562,5 @@ "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", - "apps_permission_not_found": "Aucune permission n'a été trouvée pour les applications installées" + "apps_permission_not_found": "Aucune permission trouvée pour les applications installées" } From b3398e75682f97960d30f12946cf0cc53db3ebf5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 18 Aug 2019 13:13:56 +0200 Subject: [PATCH 026/299] Update README.md --- README.md | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4bd070bea..900b58930 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,45 @@ # YunoHost core -- [YunoHost project website](https://yunohost.org) - This repository is the core of YunoHost code. - -Translation status - +- [YunoHost project website](https://yunohost.org) +- [Butracker](https://github.com/YunoHost/issues). -## Issues -- [Please report issues on YunoHost bugtracker](https://github.com/YunoHost/issues). +## Contributing -## Contribute -- You can develop on this repository using [ynh-dev tool](https://github.com/YunoHost/ynh-dev) with `use-git` sub-command. -- On this repository we are [following this workflow](https://yunohost.org/#/build_system_en): `stable <— testing <— branch`. +- You can develop on this repository using [ynh-dev](https://github.com/YunoHost/ynh-dev) with `use-git` sub-command. +- On this repository we are [following this workflow](https://yunohost.org/#/build_system_en): `stable <- testing <- unstable <- your_branch`. - Note: if you modify python scripts, you will have to modifiy the actions map. +- You can help with translation on [our translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) + +Translation status + ## Repository content -- [YunoHost core Python 2.7 scripts](https://github.com/YunoHost/yunohost/tree/stable/src/yunohost). -- [An actionsmap](https://github.com/YunoHost/yunohost/blob/stable/data/actionsmap/yunohost.yml) used by moulinette. -- [Services configuration templates](https://github.com/YunoHost/yunohost/tree/stable/data/templates). -- [Hooks](https://github.com/YunoHost/yunohost/tree/stable/data/hooks). -- [Locales](https://github.com/YunoHost/yunohost/tree/stable/locales) for translations of `yunohost` command. -- [Shell helpers](https://github.com/YunoHost/yunohost/tree/stable/data/helpers.d) for [application packaging](https://yunohost.org/#/packaging_apps_helpers_en). -- [Modules for the XMPP server Metronome](https://github.com/YunoHost/yunohost/tree/stable/lib/metronome/modules). -- [Debian files](https://github.com/YunoHost/yunohost/tree/stable/debian) for package creation. + +- [YunoHost core Python 2.7 scripts](./src/yunohost). +- [An actionsmap](./data/actionsmap/yunohost.yml) used by moulinette. +- [Services configuration templates](./data/templates). +- [Hooks](./data/hooks). +- [Locales](./locales) for translations of `yunohost` command. +- [Shell helpers](./helpers.d) for [application packaging](https://yunohost.org/#/packaging_apps_helpers_en). +- [Modules for the XMPP server Metronome](./lib/metronome/modules). +- [Debian files](./debian) for package creation. ## How does it work? + - Python core scripts are accessible through two interfaces thanks to the [moulinette framework](https://github.com/YunoHost/moulinette): - [CLI](https://en.wikipedia.org/wiki/Command-line_interface) for `yunohost` command. - [API](https://en.wikipedia.org/wiki/Application_programming_interface) for [web administration module](https://github.com/YunoHost/yunohost-admin) (other modules could be implemented). - You can find more details about how YunoHost works on this [documentation (in french)](https://yunohost.org/#/package_list_fr). ## Dependencies + - [Python 2.7](https://www.python.org/download/releases/2.7) - [Moulinette](https://github.com/YunoHost/moulinette) - [Bash](https://www.gnu.org/software/bash/bash.html) -- [Debian Jessie](https://www.debian.org/releases/jessie) +- [Debian Stretch](https://www.debian.org/releases/stretch) ## License + As [other components of YunoHost core code](https://yunohost.org/#/faq_en), this repository is under GNU AGPL v.3 license. From 63d364e54758a97b607ae464a45d1a96063d7aed Mon Sep 17 00:00:00 2001 From: Kayou Date: Sun, 18 Aug 2019 16:56:18 +0200 Subject: [PATCH 027/299] [Fix] Upgrade: Not enough arguments for format string --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4831f050c..4a14c5e4b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -695,7 +695,7 @@ def app_upgrade(app=[], url=None, file=None): json.dump(status, f) # Replace scripts and manifest and conf (if exists) - os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path)) + os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path, app_setting_path)) if os.path.exists(os.path.join(extracted_app_folder, "manifest.json")): os.system('mv "%s/manifest.json" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) From 98423a7f914a953f83a9bdd2e312800feb19e5f6 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Tue, 20 Aug 2019 20:23:45 +0200 Subject: [PATCH 028/299] adding token to not be logged --- src/yunohost/log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 8f8c92010..cf44ec21f 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -310,7 +310,7 @@ class RedactingFormatter(Formatter): try: # This matches stuff like db_pwd=the_secret or admin_password=other_secret # (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=") - match = re.search(r'(pwd|pass|password|secret|key)=(\S{3,})$', record.strip()) + match = re.search(r'(pwd|pass|password|secret|key|token)=(\S{3,})$', record.strip()) if match and match.group(2) not in self.data_to_redact: self.data_to_redact.append(match.group(2)) except Exception as e: From 2ffb413a46142e97e976ff4e4be9cbb56760dfbf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Aug 2019 15:49:25 +0200 Subject: [PATCH 029/299] This TODO is implemented now --- src/yunohost/hook.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 64b4cc2ee..8831370bd 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -460,9 +460,8 @@ def _hook_exec_python(path, args, env, loggers): sys.path = [dir_] + sys.path module = import_module(name) - # TODO : We might want to check here that it's a tuple - # containing an int + a dict ? ret = module.main(args, env, loggers) + # # Assert that the return is a (int, dict) tuple assert isinstance(ret, tuple) \ and len(ret) == 2 \ and isinstance(ret[0],int) \ From ff2caae18300449c0dcb3264e9bb9635dcc2af45 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Aug 2019 15:56:52 +0200 Subject: [PATCH 030/299] Now guess hook type using mimetypes lib --- src/yunohost/hook.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 8831370bd..05c5a6b6b 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -27,6 +27,7 @@ import os import re import sys import tempfile +import mimetypes from glob import iglob from importlib import import_module @@ -320,17 +321,6 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, if not os.path.isfile(path): raise YunohostError('file_does_not_exist', path=path) - # Check the type of the hook (bash by default) - hook_type = "bash" - # (non-bash hooks shall start with something like "#!/usr/bin/env language") - hook_ext = os.path.splitext(path)[1] - if hook_ext == ".py": - hook_type = "python" - else: - # TODO / FIXME : if needed in the future, implement support for other - # languages... - assert hook_ext in ["", ".sh"], "hook_exec only supports bash and python hooks for now" - # Define output loggers and call command loggers = ( lambda l: logger.debug(l.rstrip()+"\r"), @@ -338,13 +328,13 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, lambda l: logger.info(l.rstrip()) ) - if hook_type == "bash": - returncode, returndata = _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers) - elif hook_type == "python": + # Check the type of the hook (bash by default) + # For now we support only python and bash hooks. + hook_type = mimetypes.MimeTypes().guess_type(path)[0] + if hook_type == 'text/x-python': returncode, returndata = _hook_exec_python(path, args, env, loggers) else: - # Doesn't happen ... c.f. previous assertion - returncode, returndata = None, {} + returncode, returndata = _hook_exec_bash(path, args, no_trace, chdir, env, user, return_format, loggers) # Check and return process' return code if returncode is None: From 21c6d1b159d7b477e6e111298d0ee36fa89de5d1 Mon Sep 17 00:00:00 2001 From: madtibo Date: Wed, 24 Jul 2019 16:11:07 +0200 Subject: [PATCH 031/299] change PostgreSQL -password' authentication to 'md5' --- data/helpers.d/postgresql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/postgresql b/data/helpers.d/postgresql index a76580b11..d252ae2dc 100644 --- a/data/helpers.d/postgresql +++ b/data/helpers.d/postgresql @@ -283,11 +283,11 @@ ynh_psql_test_if_first_run() { sudo --login --user=postgres psql -c"ALTER user postgres WITH PASSWORD '$psql_root_password'" postgres - # force all user to connect to local database using passwords + # force all user to connect to local databases using hashed passwords # https://www.postgresql.org/docs/current/static/auth-pg-hba-conf.html#EXAMPLE-PG-HBA.CONF # Note: we can't use peer since YunoHost create users with nologin # See: https://github.com/YunoHost/yunohost/blob/unstable/data/helpers.d/user - ynh_replace_string --match_string="local\(\s*\)all\(\s*\)all\(\s*\)peer" --replace_string="local\1all\2all\3password" --target_file="$pg_hba" + ynh_replace_string --match_string="local\(\s*\)all\(\s*\)all\(\s*\)peer" --replace_string="local\1all\2all\3md5" --target_file="$pg_hba" # Advertise service in admin panel yunohost service add postgresql --log "$logfile" From 2fc13a7af01a0c979386c571deadb68eeb3824db Mon Sep 17 00:00:00 2001 From: madtibo Date: Fri, 26 Jul 2019 13:45:33 +0200 Subject: [PATCH 032/299] switch from password to md5 authentication in postgresql --- ...stgresql_password_to_md5_authentication.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py diff --git a/src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py b/src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py new file mode 100644 index 000000000..19f6ada69 --- /dev/null +++ b/src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py @@ -0,0 +1,21 @@ +import glob +import re +from yunohost.tools import Migration +from moulinette.utils.filesystem import chown + + +class MyMigration(Migration): + + "Force authentication in md5 for local connexions" + + all_hba_files = glob.glob("/etc/postgresql/*/*/pg_hba.conf") + + def forward(self): + for filename in self.all_hba_files: + pg_hba_in = read_file(filename) + write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)password", "local\\1all\\2all\\3md5", pg_hba_in)) + + def backward(self): + for filename in self.all_hba_files: + pg_hba_in = read_file(filename) + write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)md5", "local\\1all\\2all\\3password", pg_hba_in)) From fcae50a6e0e8ab5bcbd6f5e86462c7efd6d086d0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Aug 2019 18:14:16 +0200 Subject: [PATCH 033/299] Move / test / fix migration for postgresql auth --- locales/en.json | 1 + ...ion.py => 0012_postgresql_password_to_md5_authentication.py} | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename src/yunohost/data_migrations/{0011_postgresql_password_to_md5_authentication.py => 0012_postgresql_password_to_md5_authentication.py} (91%) diff --git a/locales/en.json b/locales/en.json index b45739149..903aa325a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -331,6 +331,7 @@ "migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services", "migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead", "migration_description_0011_setup_group_permission": "Setup user group and setup permission for apps and services", + "migration_description_0012_postgresql_password_to_md5_authentication": "Force postgresql authentication to use md5 for local connections", "migration_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists…", diff --git a/src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py b/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py similarity index 91% rename from src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py rename to src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py index 19f6ada69..5d36b3e23 100644 --- a/src/yunohost/data_migrations/0011_postgresql_password_to_md5_authentication.py +++ b/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py @@ -1,7 +1,7 @@ import glob import re from yunohost.tools import Migration -from moulinette.utils.filesystem import chown +from moulinette.utils.filesystem import read_file, write_to_file class MyMigration(Migration): From 68e97245175e846e7c737394f5d1577273d8b496 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 25 Aug 2019 16:40:41 +0200 Subject: [PATCH 034/299] [mod] remove catchall bad exception --- locales/en.json | 2 +- src/yunohost/dyndns.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index b45739149..9cdb14483 100644 --- a/locales/en.json +++ b/locales/en.json @@ -185,7 +185,7 @@ "dyndns_could_not_check_provide": "Could not check if {provider:s} can provide {domain:s}.", "dyndns_could_not_check_available": "Could not check if {domain:s} is available on {provider:s}.", "dyndns_cron_installed": "The DynDNS cron job has been installed", - "dyndns_cron_remove_failed": "Unable to remove the DynDNS cron job", + "dyndns_cron_remove_failed": "Unable to remove the DynDNS cron job because: {error}", "dyndns_cron_removed": "The DynDNS cron job has been removed", "dyndns_ip_update_failed": "Unable to update IP address on DynDNS", "dyndns_ip_updated": "Your IP address has been updated on DynDNS", diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 2dadcef52..c4558d79c 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -325,8 +325,8 @@ def dyndns_removecron(): """ try: os.remove("/etc/cron.d/yunohost-dyndns") - except: - raise YunohostError('dyndns_cron_remove_failed') + except Exception as e: + raise YunohostError('dyndns_cron_remove_failed', error=e) logger.success(m18n.n('dyndns_cron_removed')) From 0849adbee14d576d365adee538f7689db177b730 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 25 Aug 2019 16:48:21 +0200 Subject: [PATCH 035/299] [mod] remove useless weird comment --- src/yunohost/utils/network.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index 1f82a87b0..af63a0e7d 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -71,9 +71,6 @@ def get_gateway(): return addr.popitem()[1] if len(addr) == 1 else None -# - - def _extract_inet(string, skip_netmask=False, skip_loopback=True): """ Extract IP addresses (v4 and/or v6) from a string limited to one From 19dbe87167a96c81ce20e4b36838b51e30c0892a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Sun, 25 Aug 2019 16:50:30 +0200 Subject: [PATCH 036/299] [mod] regex must be stored in raw strings --- src/yunohost/utils/network.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index af63a0e7d..4e23516c3 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -48,9 +48,9 @@ def get_network_interfaces(): # Get network devices and their addresses (raw infos from 'ip addr') devices_raw = {} output = subprocess.check_output('ip addr show'.split()) - for d in re.split('^(?:[0-9]+: )', output, flags=re.MULTILINE): + for d in re.split(r'^(?:[0-9]+: )', output, flags=re.MULTILINE): # Extract device name (1) and its addresses (2) - m = re.match('([^\s@]+)(?:@[\S]+)?: (.*)', d, flags=re.DOTALL) + m = re.match(r'([^\s@]+)(?:@[\S]+)?: (.*)', d, flags=re.DOTALL) if m: devices_raw[m.group(1)] = m.group(2) @@ -63,7 +63,7 @@ def get_network_interfaces(): def get_gateway(): output = subprocess.check_output('ip route show'.split()) - m = re.search('default via (.*) dev ([a-z]+[0-9]?)', output) + m = re.search(r'default via (.*) dev ([a-z]+[0-9]?)', output) if not m: return None @@ -86,10 +86,10 @@ def _extract_inet(string, skip_netmask=False, skip_loopback=True): A dict of {protocol: address} with protocol one of 'ipv4' or 'ipv6' """ - ip4_pattern = '((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' - ip6_pattern = '(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)' - ip4_pattern += '/[0-9]{1,2})' if not skip_netmask else ')' - ip6_pattern += '/[0-9]{1,3})' if not skip_netmask else ')' + ip4_pattern = r'((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}' + ip6_pattern = r'(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)' + ip4_pattern += r'/[0-9]{1,2})' if not skip_netmask else ')' + ip6_pattern += r'/[0-9]{1,3})' if not skip_netmask else ')' result = {} for m in re.finditer(ip4_pattern, string): From b8f27b75934c15f616d1580613218aad0cac04b2 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Mon, 26 Aug 2019 17:35:18 +0200 Subject: [PATCH 037/299] [enh] add --force to 'dyndns update' --- data/actionsmap/yunohost.yml | 4 ++++ src/yunohost/dyndns.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 2b87b6daa..454ccc76e 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1533,6 +1533,10 @@ dyndns: -i: full: --ipv4 help: IP address to send + -f: + full: --force + help: Force the update (for debugging only) + action: store_true -6: full: --ipv6 help: IPv6 address to send diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index c4558d79c..0a5b8c180 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -176,7 +176,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom @is_unit_operation() def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, key=None, - ipv4=None, ipv6=None): + ipv4=None, ipv6=None, force=False): """ Update IP on DynDNS platform @@ -249,7 +249,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, logger.debug("Requested IPv4/v6 are (%s, %s)" % (ipv4, ipv6)) # no need to update - if old_ipv4 == ipv4 and old_ipv6 == ipv6: + if not force and (old_ipv4 == ipv4 and old_ipv6 == ipv6): logger.info("No updated needed.") return else: From 87435775ee6208b3773288f879e8b79ead1430b6 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Tue, 27 Aug 2019 19:45:33 +0200 Subject: [PATCH 038/299] [enh] add --dry-run option to dyndns update --- data/actionsmap/yunohost.yml | 4 ++++ src/yunohost/dyndns.py | 23 ++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 454ccc76e..7c864cce0 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1537,6 +1537,10 @@ dyndns: full: --force help: Force the update (for debugging only) action: store_true + -D: + full: --dry-run + help: Only display the generated zone + action: store_true -6: full: --ipv6 help: IPv6 address to send diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 0a5b8c180..70b964b7c 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -33,7 +33,7 @@ import subprocess from moulinette import m18n from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import write_to_file +from moulinette.utils.filesystem import write_to_file, read_file from moulinette.utils.network import download_json from moulinette.utils.process import check_output @@ -176,7 +176,7 @@ def dyndns_subscribe(operation_logger, subscribe_host="dyndns.yunohost.org", dom @is_unit_operation() def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, key=None, - ipv4=None, ipv6=None, force=False): + ipv4=None, ipv6=None, force=False, dry_run=False): """ Update IP on DynDNS platform @@ -249,7 +249,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, logger.debug("Requested IPv4/v6 are (%s, %s)" % (ipv4, ipv6)) # no need to update - if not force and (old_ipv4 == ipv4 and old_ipv6 == ipv6): + if (not force and not dry_run) and (old_ipv4 == ipv4 and old_ipv6 == ipv6): logger.info("No updated needed.") return else: @@ -296,13 +296,18 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, logger.debug("Now pushing new conf to DynDNS host...") - try: - command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE] - subprocess.check_call(command) - except subprocess.CalledProcessError: - raise YunohostError('dyndns_ip_update_failed') + if not dry_run: + try: + command = ["/usr/bin/nsupdate", "-k", key, DYNDNS_ZONE] + subprocess.check_call(command) + except subprocess.CalledProcessError: + raise YunohostError('dyndns_ip_update_failed') - logger.success(m18n.n('dyndns_ip_updated')) + logger.success(m18n.n('dyndns_ip_updated')) + else: + print(read_file(DYNDNS_ZONE)) + print("") + print("Warning: dry run, this is only the generated config, it won't be applied") def dyndns_installcron(): From 2aa5e7f6400a97a04bf69dfdd024db7dbe302c8a Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Tue, 27 Aug 2019 19:45:45 +0200 Subject: [PATCH 039/299] [mod] raw string for correct escape sequence --- src/yunohost/dyndns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 70b964b7c..a35d39736 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -279,7 +279,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, # should be muc.the.domain.tld. or the.domain.tld if record["value"] == "@": record["value"] = domain - record["value"] = record["value"].replace(";", "\;") + record["value"] = record["value"].replace(";", r"\;") action = "update add {name}.{domain}. {ttl} {type} {value}".format(domain=domain, **record) action = action.replace(" @.", " ") From 6365a26cd3f705ff8600f6dacb9beaf84a030f58 Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Wed, 28 Aug 2019 18:16:39 +0200 Subject: [PATCH 040/299] Add basic Travis CI configuration --- .travis.yml | 19 +++++++++++++++---- README.md | 3 +++ pytest.ini | 4 ++++ setup.cfg | 2 ++ tox.ini | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index 25fe0e5fc..8674d4d03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,16 @@ language: python -install: "pip install pytest pyyaml" -python: - - "2.7" -script: "py.test tests" + +matrix: + allow_failures: + - env: TOXENV=lint + include: + - python: 2.7 + env: TOXENV=py27 + - python: 2.7 + env: TOXENV=lint + +install: + - pip install tox + +script: + - tox diff --git a/README.md b/README.md index 900b58930..d06e97c45 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Build Status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) +[![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) + # YunoHost core This repository is the core of YunoHost code. diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..f9200ab9c --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +addopts = -s -v +norecursedirs = dist doc build .tox .eggs +testpaths = tests/ diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..db1dde69d --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +ignore = E501,E128,E731,E722 diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..ac109609c --- /dev/null +++ b/tox.ini @@ -0,0 +1,18 @@ +[tox] +envlist = + py27 + lint +skipdist = True + +[testenv] +skip_install=True +deps = + pytest >= 4.6.3, < 5.0 + pyyaml >= 5.1.2, < 6.0 +commands = + pytest {posargs} + +[testenv:lint] +skip_install=True +commands = flake8 src doc data tests +deps = flake8 From ae920866b6882d1d7564a2dc3e57e95ecdd080c2 Mon Sep 17 00:00:00 2001 From: ppr Date: Fri, 16 Aug 2019 08:08:20 +0000 Subject: [PATCH 041/299] Translated using Weblate (French) Currently translated at 92.8% (540 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 607a2a348..8d81a5cab 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -120,7 +120,7 @@ "ldap_initialized": "L’annuaire LDAP a été initialisé", "license_undefined": "indéfinie", "mail_alias_remove_failed": "Impossible de supprimer l’alias de courriel '{mail:s}'", - "mail_domain_unknown": "Le domaine d'adresse du courriel '{domain:s}' est inconnu", + "mail_domain_unknown": "Le domaine '{domain:s}' pour l'adresse de courriel est inconnu", "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "maindomain_change_failed": "Impossible de modifier le domaine principal", "maindomain_changed": "Le domaine principal a été modifié", @@ -465,7 +465,7 @@ "migration_description_0004_php5_to_php7_pools": "Reconfiguration des groupes PHP pour utiliser PHP 7 au lieu de PHP 5", "migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6", "migration_0005_postgresql_94_not_installed": "PostgreSQL n’a pas été installé sur votre système. Rien à faire !", - "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système :( …", + "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système :(", "migration_0005_not_enough_space": "Il n’y a pas assez d’espace libre de disponible sur {path} pour lancer maintenant la migration :(.", "recommend_to_add_first_user": "La post-installation est terminée mais YunoHost a besoin d’au moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant la commande 'yunohost user create $nomdutilisateur' ou bien via l’interface d’administration web.", "service_description_php7.0-fpm": "exécute des applications écrites en PHP avec Nginx", From 57756d1d9a600777d011389927e69b7b86107872 Mon Sep 17 00:00:00 2001 From: troll Date: Fri, 16 Aug 2019 20:26:15 +0000 Subject: [PATCH 042/299] Translated using Weblate (French) Currently translated at 92.8% (540 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 8d81a5cab..e17a2c6d5 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -562,5 +562,16 @@ "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", - "apps_permission_not_found": "Aucune permission trouvée pour les applications installées" + "apps_permission_not_found": "Aucune permission trouvée pour les applications installées", + "apps_permission_restoration_failed": "L'autorisation '{permission:s}' pour la restauration de l'application {app:s} a échoué", + "backup_permission": "Autorisation de sauvegarde pour l'application {app:s}", + "edit_group_not_allowed": "Vous n'êtes pas autorisé à modifier le groupe {group:s}", + "error_when_removing_sftpuser_group": "Erreur en essayant de supprimer le groupe sftpusers", + "group_created": "Le groupe '{group}' a créé avec succès", + "group_deleted": "Le groupe '{group}' a été supprimé", + "group_deletion_not_allowed": "Le groupe {group:s} ne peut pas être supprimé manuellement.", + "group_info_failed": "L'information sur le groupe a échoué", + "group_unknown": "Le groupe {group:s} est inconnu", + "group_updated": "Le groupe '{group}' a été mis à jour", + "group_update_failed": "La mise à jour du groupe '{group}' a échoué" } From c562b05edfe748c12a9c7ef01e152e5811d06196 Mon Sep 17 00:00:00 2001 From: htsr Date: Fri, 16 Aug 2019 20:42:20 +0000 Subject: [PATCH 043/299] Translated using Weblate (French) Currently translated at 92.8% (540 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index e17a2c6d5..93ebef20f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -573,5 +573,10 @@ "group_info_failed": "L'information sur le groupe a échoué", "group_unknown": "Le groupe {group:s} est inconnu", "group_updated": "Le groupe '{group}' a été mis à jour", - "group_update_failed": "La mise à jour du groupe '{group}' a échoué" + "group_update_failed": "La mise à jour du groupe '{group}' a échoué", + "group_already_allowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' activée pour l'application '{app:s}'", + "group_already_disallowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' désactivée pour l'application '{app:s}'", + "group_name_already_exist": "Le groupe {name:s} existe déjà", + "group_creation_failed": "Échec de la création du groupe '{group}'", + "group_deletion_failed": "Échec de la suppression du groupe '{group}'" } From 9bea4ee3048bb7cfd9bc22ca7b3ac581cfa805ca Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Wed, 21 Aug 2019 08:25:42 +0000 Subject: [PATCH 044/299] Translated using Weblate (Catalan) Currently translated at 100.0% (582 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 824745c12..a9a0c2e0f 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -25,7 +25,7 @@ "app_manifest_invalid": "Manifest d'aplicació incorrecte: {error}", "app_no_upgrade": "No hi ha cap aplicació per actualitzar", "app_not_correctly_installed": "{app:s} sembla estar mal instal·lada", - "app_not_installed": "{app:s} no està instal·lada", + "app_not_installed": "L'aplicació «{app:s}» no està instal·lada. Aquí hi ha la llista d'aplicacions instal·lades: {all_apps}", "app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament", "app_package_need_update": "El paquet de l'aplicació {app} ha de ser actualitzat per poder seguir els canvis de YunoHost", "app_removed": "{app:s} ha estat suprimida", @@ -175,7 +175,7 @@ "domain_dyndns_dynette_is_unreachable": "No s'ha pogut abastar la dynette YunoHost, o bé YunoHost no està connectat a internet correctament o bé el servidor dynette està caigut. Error: {error}", "domain_dyndns_invalid": "Domini no vàlid per utilitzar amb DynDNS", "domain_dyndns_root_unknown": "Domini DynDNS principal desconegut", - "domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió", + "domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió. Això podria causar problemes més tard (no és segur ... podria no passar res).", "domain_uninstall_app_first": "Hi ha una o més aplicacions instal·lades en aquest domini. Desinstal·leu les abans d'eliminar el domini", "domain_unknown": "Domini desconegut", "domain_zone_exists": "El fitxer de zona DNS ja existeix", @@ -236,7 +236,7 @@ "invalid_url_format": "Format d'URL invàlid", "ip6tables_unavailable": "No podeu modificar les ip6tables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", "iptables_unavailable": "No podeu modificar les iptables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", - "log_corrupted_md_file": "El fitxer de metadades yaml associat amb els registres està malmès: « {md_file} »", + "log_corrupted_md_file": "El fitxer de metadades yaml associat amb els registres està malmès: « {md_file} »\nError: {error}", "log_category_404": "La categoria de registres « {category} » no existeix", "log_link_to_log": "El registre complet d'aquesta operació: «{desc}»", "log_help_to_get_log": "Per veure el registre de l'operació « {desc} », utilitzeu l'ordre « yunohost log display {name} »", @@ -523,5 +523,63 @@ "yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.", "yunohost_configured": "S'ha configurat YunoHost", "yunohost_installing": "Instal·lació de YunoHost…", - "yunohost_not_installed": "YunoHost no està instal·lat o no està instal·lat correctament. Executeu «yunohost tools postinstall»" + "yunohost_not_installed": "YunoHost no està instal·lat o no està instal·lat correctament. Executeu «yunohost tools postinstall»", + "apps_permission_not_found": "No s'ha trobat cap permís per les aplicacions instal·lades", + "apps_permission_restoration_failed": "Ha fallat el permís «{permission:s}» per la restauració de l'aplicació {app:s}", + "backup_permission": "Permís de còpia de seguretat per l'aplicació {app:s}", + "edit_group_not_allowed": "No teniu autorització per modificar el grup {group:s}", + "edit_permission_with_group_all_users_not_allowed": "No podeu modificar els permisos del grup «all_users», s'ha d'utlilitzar «yunohost user permission clear APP» o «yunohost user permission add APP -u USER».", + "error_when_removing_sftpuser_group": "Error intentant eliminar el gruo sftpusers", + "group_already_allowed": "El grup «{group:s}» ja té el permís «{permission:s}» activat per l'aplicació «{app:s}»", + "group_already_disallowed": "El grup «{group:s}» ja té els permisos «{permission:s}» desactivats per l'aplicació «{app:s}»", + "group_name_already_exist": "El grup {name:s} ja existeix", + "group_created": "S'ha creat el grup «{group}»", + "group_creation_failed": "No s'ha pogut crear el grup «{group}»", + "group_deleted": "S'ha eliminat el grup «{group}»", + "group_deletion_failed": "No s'ha pogut eliminar el grup «{group}»", + "group_deletion_not_allowed": "El grup {group:s} no es pot eliminar manualment.", + "group_info_failed": "Ha fallat la informació del grup", + "group_unknown": "Grup {group:s} desconegut", + "group_updated": "S'ha actualitzat el grup «{group}»", + "group_update_failed": "No s'ha pogut actualitzat el grup «{group}»", + "log_permission_add": "Afegir el permís «{}» per l'aplicació «{}»", + "log_permission_remove": "Suprimir el permís «{}»", + "log_permission_update": "Actualitzar el permís «{}» per l'aplicació «{}»", + "log_user_group_add": "Afegir grup «{}»", + "log_user_group_delete": "Eliminar grup «{}»", + "log_user_group_update": "Actualitzar grup «{}»", + "log_user_permission_add": "Actualitzar el permís «{}»", + "log_user_permission_remove": "Actualitzar el permís «{}»", + "mailbox_disabled": "La bústia de correu està desactivada per als usuaris {user:s}", + "migration_description_0011_setup_group_permission": "Configurar el grup d'usuaris i els permisos per les aplicacions i els serveis", + "migration_0011_backup_before_migration": "Creant una còpia de seguretat de la base de dades LDAP i la configuració de les aplicacions abans d'efectuar la migració.", + "migration_0011_can_not_backup_before_migration": "No s'ha pogut fer la còpia de seguretat abans de la migració. No s'ha pogut fer la migració. Error: {error:s}", + "migration_0011_create_group": "Creant un grup per a cada usuari…", + "migration_0011_done": "Migració completa. Ja podeu gestionar grups d'usuaris.", + "migration_0011_LDAP_config_dirty": "Sembla que heu modificat manualment la configuració LDAP. Per fer aquesta migració s'ha d'actualitzar la configuració LDAP.\nGuardeu la configuració actual, reinicieu la configuració original amb l'ordre «yunohost tools regen-conf -f» i torneu a intentar la migració", + "migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}", + "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP…", + "migration_0011_migration_failed_trying_to_rollback": "La migració ha fallat … s'intenta tornar el sistema a l'estat anterior.", + "migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.", + "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP…", + "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP…", + "need_define_permission_before": "Heu de tornar a redefinir els permisos utilitzant «yunohost user permission add -u USER» abans d'eliminar un grup permès", + "permission_already_clear": "Ja s'ha donat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_already_exist": "El permís «{permission:s}» ja existeix per l'aplicació {app:s}", + "permission_created": "S'ha creat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_creation_failed": "La creació del permís ha fallat", + "permission_deleted": "S'ha eliminat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_deletion_failed": "L'eliminació del permís «{permission:s}» per l'aplicació {app:s} ha fallat", + "permission_not_found": "No s'ha trobat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_name_not_valid": "El nom del permís «{permission:s}» no és vàlid", + "permission_update_failed": "L'actualització del permís ha fallat", + "permission_generated": "S'ha actualitzat la base de dades del permís", + "permission_updated": "S'ha actualitzat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_update_nothing_to_do": "No hi ha cap permís per actualitzar", + "remove_main_permission_not_allowed": "No es pot eliminar el permís principal", + "remove_user_of_group_not_allowed": "No es pot eliminar l'usuari {user:s} del grup {group:s}", + "system_groupname_exists": "El nom de grup ja existeix en el sistema de grups", + "tools_update_failed_to_app_fetchlist": "No s'ha pogut actualitzar la llista d'aplicacions de YunoHost a causa de: {error}", + "user_already_in_group": "L'usuari {user:s} ja és en el grup {group:s}", + "user_not_in_group": "L'usuari {user:s} no és en el grup {group:s}" } From abee5583266e6c5b106ee55535708a570fffb266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Mon, 19 Aug 2019 19:57:38 +0000 Subject: [PATCH 045/299] Translated using Weblate (Occitan) Currently translated at 93.5% (544 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/locales/oc.json b/locales/oc.json index fc6f6946c..a65154c35 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -9,7 +9,7 @@ "app_install_files_invalid": "Fichièrs d’installacion incorrèctes", "app_no_upgrade": "Pas cap d’aplicacion de metre a jorn", "app_not_correctly_installed": "{app:s} sembla pas ben installat", - "app_not_installed": "{app:s} es pas installat", + "app_not_installed": "L’aplicacion {app:s} es pas installat. Vaquí la lista de las aplicacions installadas : {all_apps}", "app_not_properly_removed": "{app:s} es pas estat corrèctament suprimit", "app_removed": "{app:s} es estat suprimit", "app_unknown": "Aplicacion desconeguda", @@ -148,7 +148,7 @@ "domain_dyndns_invalid": "Domeni incorrècte per una utilizacion amb DynDNS", "domain_dyndns_root_unknown": "Domeni DynDNS màger desconegut", "domain_exists": "Lo domeni existís ja", - "domain_hostname_failed": "Fracàs de la creacion d’un nòu nom d’òst", + "domain_hostname_failed": "Fracàs de la creacion d’un nòu nom d’òst. Aquò poirà provocar de problèmas mai tard (mas es pas segur… benlèu que coparà pas res).", "domain_unknown": "Domeni desconegut", "domain_zone_exists": "Lo fichièr zòna DNS existís ja", "domain_zone_not_found": "Fichèr de zòna DNS introbable pel domeni {:s}", @@ -404,7 +404,7 @@ "backup_no_uncompress_archive_dir": "Lo repertòri de l’archiu descomprimit existís pas", "pattern_username": "Deu èsser compausat solament de caractèrs alfanumerics en letras minusculas e de tirets basses", "experimental_feature": "Atencion : aquesta foncionalitat es experimentala e deu pas èsser considerada coma establa, deuriatz pas l’utilizar levat que sapiatz çò que fasètz.", - "log_corrupted_md_file": "Lo fichièr yaml de metadonada amb los jornals d’audit es corromput : « {md_file} »", + "log_corrupted_md_file": "Lo fichièr yaml de metadonada amb los jornals d’audit es corromput : « {md_file} »\nError : {error:s}", "log_category_404": "La categoria de jornals d’audit « {category} » existís pas", "log_link_to_log": "Jornal complèt d’aquesta operacion : {desc}", "log_help_to_get_log": "Per veire lo jornal d’aquesta operacion « {desc} », utilizatz la comanda « yunohost log display {name} »", @@ -547,5 +547,25 @@ "tools_upgrade_special_packages": "Actualizacion dels paquets « especials » (ligats a YunoHost)…", "tools_upgrade_special_packages_explanation": "Aquesta accion s’acabarà mas l’actualizacion especiala actuala contunharà en rèire-plan. Comencetz pas cap d’autra accion sul servidor dins las ~ 10 minutas que venon (depend de la velocitat de la maquina). Un còp acabat, benlèu que vos calrà vos tornar connectar a l’interfàcia d’administracion. Los jornals d’audit de l’actualizacion seràn disponibles a Aisinas > Jornals d’audit (dins l’interfàcia d’administracion) o amb « yunohost log list » (en linha de comanda).", "update_apt_cache_failed": "I a agut d’errors en actualizar la memòria cache d’APT (lo gestionari de paquets de Debian). Aquí avètz las linhas de sources.list que pòdon vos ajudar a identificar las linhas problematicas : \n{sourceslist}", - "update_apt_cache_warning": "I a agut d’errors en actualizar la memòria cache d’APT (lo gestionari de paquets de Debian). Aquí avètz las linhas de sources.list que pòdon vos ajudar a identificar las linhas problematicas : \n{sourceslist}" + "update_apt_cache_warning": "I a agut d’errors en actualizar la memòria cache d’APT (lo gestionari de paquets de Debian). Aquí avètz las linhas de sources.list que pòdon vos ajudar a identificar las linhas problematicas : \n{sourceslist}", + "apps_permission_not_found": "Cap d’autorizacion pas trobada per las aplicacions installadas", + "backup_permission": "Autorizacion de salvagarda per l’aplicacion {app:s}", + "edit_group_not_allowed": "Sètz pas autorizat a cambiar lo grop {group:s}", + "error_when_removing_sftpuser_group": "Error en ensajar de suprimir lo grop sftpusers", + "group_name_already_exist": "Lo grop {name:s} existís ja", + "group_created": "Lo grop « {group} » es estat corrèctament creat", + "group_creation_failed": "Fracàs de la creacion del grop « {group} »", + "group_deleted": "Lo grop « {group} » es estat suprimit", + "group_deletion_failed": "Fracàs de la supression del grop « {group} »", + "group_deletion_not_allowed": "Lo grop « {group} » pòt pas èsser suprimir manualament.", + "group_info_failed": "Recuperacion de las informacions del grop « {group} » impossibla", + "group_unknown": "Lo grop « {group} » es desconegut", + "log_user_group_add": "Ajustar lo grop « {} »", + "log_user_group_delete": "Suprimir lo grop « {} »", + "migration_0011_backup_before_migration": "Creacion d’una còpia de seguretat de la basa de donadas LDAP e de la configuracion de las aplicacions abans d’efectuar la migracion.", + "migration_0011_create_group": "Creacion d’un grop per cada utilizaire…", + "migration_0011_done": "Migracion complèta. Ara podètz gerir de grops d’utilizaires.", + "migration_0011_LDAP_update_failed": "Fracàs de la mesa a jorn de LDAP. Error : {error:s}", + "migration_0011_migration_failed_trying_to_rollback": "La migracion a fracassat… ensag de tornar lo sistèma a l’estat anterio.", + "migration_0011_rollback_success": "Restauracion del sistèma reüssida." } From 6563fdda8629bad18e4d2e48185abdc483fcd8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?= Date: Mon, 19 Aug 2019 17:03:02 +0000 Subject: [PATCH 046/299] Translated using Weblate (Esperanto) Currently translated at 7.0% (41 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/locales/eo.json b/locales/eo.json index b9d973557..1a6a2ea9a 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -32,5 +32,12 @@ "service_description_yunohost-api": "mastrumas interagojn inter la YunoHost retinterfaco kaj la sistemo", "service_description_yunohost-firewall": "mastrumas malfermitajn kaj fermitajn konektejojn al servoj", "service_disable_failed": "Neebla malaktivigi servon '{service:s}'\n\nFreŝaj protokoloj de la servo : {logs:s}", - "service_disabled": "Servo '{service:s}' estas malaktivigita" + "service_disabled": "Servo '{service:s}' estas malaktivigita", + "action_invalid": "Nevalida ago « {action:s} »", + "admin_password": "Pasvorto de la estro", + "admin_password_too_long": "Bonvolu elekti pasvorton pli mallonga ol 127 signoj", + "already_up_to_date": "Neniu estas farenda! Ĉiu jam estas ĝisdata!", + "app_argument_choice_invalid": "Nevalida elekto por argumento « {name:s} », ĝi devas esti unu el {choices:s}", + "app_argument_invalid": "Nevalida valoro por argumento « {name:s} » : {error:s}", + "app_change_url_failed_nginx_reload": "Reŝargi nginx malsuksesis. Jen la eligo de « nginx -t » :\n{nginx_errors:s}" } From 340d276219af764467fce346092c90909cf962d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?= Date: Mon, 19 Aug 2019 16:57:03 +0000 Subject: [PATCH 047/299] Translated using Weblate (French) Currently translated at 94.0% (547 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 93ebef20f..d164ca5f5 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -281,7 +281,7 @@ "certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l'adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème d’hairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.", "certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Let’s Encrypt. L’installation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.", "appslist_retrieve_bad_format": "Le fichier récupéré pour la liste d’applications {appslist:s} n’est pas valide", - "domain_hostname_failed": "Échec de la création d’un nouveau nom d’hôte", + "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (mais ce n’est pas sûr… peut-être que ça n’en causera pas).", "yunohost_ca_creation_success": "L’autorité de certification locale a été créée.", "appslist_name_already_tracked": "Il y a déjà une liste d’applications enregistrée avec le nom {name:s}.", "appslist_url_already_tracked": "Il y a déjà une liste d’applications enregistrée avec l’URL {url:s}.", @@ -465,7 +465,7 @@ "migration_description_0004_php5_to_php7_pools": "Reconfiguration des groupes PHP pour utiliser PHP 7 au lieu de PHP 5", "migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6", "migration_0005_postgresql_94_not_installed": "PostgreSQL n’a pas été installé sur votre système. Rien à faire !", - "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système :(", + "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système… :(", "migration_0005_not_enough_space": "Il n’y a pas assez d’espace libre de disponible sur {path} pour lancer maintenant la migration :(.", "recommend_to_add_first_user": "La post-installation est terminée mais YunoHost a besoin d’au moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant la commande 'yunohost user create $nomdutilisateur' ou bien via l’interface d’administration web.", "service_description_php7.0-fpm": "exécute des applications écrites en PHP avec Nginx", From 7af9ad4991155672a87b55f2626563f5c87e1cda Mon Sep 17 00:00:00 2001 From: ppr Date: Mon, 19 Aug 2019 17:58:34 +0000 Subject: [PATCH 048/299] Translated using Weblate (French) Currently translated at 94.0% (547 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index d164ca5f5..2f22f1397 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -578,5 +578,15 @@ "group_already_disallowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' désactivée pour l'application '{app:s}'", "group_name_already_exist": "Le groupe {name:s} existe déjà", "group_creation_failed": "Échec de la création du groupe '{group}'", - "group_deletion_failed": "Échec de la suppression du groupe '{group}'" + "group_deletion_failed": "Échec de la suppression du groupe '{group}'", + "edit_permission_with_group_all_users_not_allowed": "Vous n'êtes pas autorisé à modifier les permissions pour le groupe 'all_users', utilisez'yunohost user permission clear APP' ou 'yunohost user permission add APP -u USER'.", + "log_permission_add": "Ajouter l'autorisation '{}' pour l'application '{}'", + "log_permission_remove": "Supprimer l'autorisation '{}'", + "log_permission_update": "Mise à jour de l'autorisation '{}' pour l'application '{}'", + "log_user_group_add": "Ajouter '{}' au groupe", + "log_user_group_delete": "Supprimer le groupe '{}'", + "log_user_group_update": "Mettre à jour '{}' pour le groupe", + "log_user_permission_add": "Mettre à jour l'autorisation pour '{}'", + "log_user_permission_remove": "Mettre à jour l'autorisation pour '{}'", + "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}" } From 0cb81512f32cc7e7b37f17fe95408a44cb175f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Wed, 28 Aug 2019 18:02:28 +0000 Subject: [PATCH 049/299] Translated using Weblate (Occitan) Currently translated at 99.1% (577 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 65 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/locales/oc.json b/locales/oc.json index a65154c35..d65a71d1e 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -7,16 +7,16 @@ "installation_complete": "Installacion acabada", "app_id_invalid": "Id d’aplicacion incorrècte", "app_install_files_invalid": "Fichièrs d’installacion incorrèctes", - "app_no_upgrade": "Pas cap d’aplicacion de metre a jorn", + "app_no_upgrade": "Pas cap d’aplicacion d’actualizar", "app_not_correctly_installed": "{app:s} sembla pas ben installat", "app_not_installed": "L’aplicacion {app:s} es pas installat. Vaquí la lista de las aplicacions installadas : {all_apps}", "app_not_properly_removed": "{app:s} es pas estat corrèctament suprimit", "app_removed": "{app:s} es estat suprimit", "app_unknown": "Aplicacion desconeguda", - "app_upgrade_app_name": "Mesa a jorn de l’aplicacion {app}…", - "app_upgrade_failed": "Impossible de metre a jorn {app:s}", - "app_upgrade_some_app_failed": "D’aplicacions se pòdon pas metre a jorn", - "app_upgraded": "{app:s} es estat mes a jorn", + "app_upgrade_app_name": "Actualizacion de l’aplicacion {app}…", + "app_upgrade_failed": "Impossible d’actualizar {app:s}", + "app_upgrade_some_app_failed": "D’aplicacions se pòdon pas actualizar", + "app_upgraded": "{app:s} es estada actualizada", "appslist_fetched": "Recuperacion de la lista d’aplicacions {appslist:s} corrèctament realizada", "appslist_migrating": "Migracion de la lista d’aplicacion{appslist:s}…", "appslist_name_already_tracked": "I a ja una lista d’aplicacion enregistrada amb lo nom {name:s}.", @@ -51,7 +51,7 @@ "app_incompatible": "L’aplicacion {app} es pas compatibla amb vòstra version de YunoHost", "app_location_already_used": "L’aplicacion « {app} » es ja installada a aqueste emplaçament ({path})", "app_manifest_invalid": "Manifest d’aplicacion incorrècte : {error}", - "app_package_need_update": "Lo paquet de l’aplicacion {app} deu èsser mes a jorn per seguir los cambiaments de YunoHost", + "app_package_need_update": "Lo paquet de l’aplicacion {app} deu èsser actualizat per poder seguir los cambiaments de YunoHost", "app_requirements_checking": "Verificacion dels paquets requesits per {app}…", "app_sources_fetch_failed": "Recuperacion dels fichièrs fonts impossibla, l’URL es corrècta ?", "app_unsupported_remote_type": "Lo tipe alonhat utilizat per l’aplicacion es pas suportat", @@ -67,8 +67,8 @@ "backup_creating_archive": "Creacion de l’archiu de salvagarda…", "backup_creation_failed": "Impossible de crear la salvagarda", "app_already_installed_cant_change_url": "Aquesta aplicacion es ja installada. Aquesta foncion pòt pas simplament cambiar l’URL. Agachatz « app changeurl » s’es disponible.", - "app_change_no_change_url_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, poiretz aver de la metre a jorn.", - "app_change_url_no_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, benlèu que vos cal la metre a jorn.", + "app_change_no_change_url_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, poiretz aver de l’actualizar.", + "app_change_url_no_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, benlèu que vos cal l’actualizar.", "app_make_default_location_already_used": "Impossible de configurar l’aplicacion « {app} » per defaut pel domeni {domain} perque es ja utilizat per l’aplicacion {other_app}", "app_location_install_failed": "Impossible d’installar l’aplicacion a aqueste emplaçament per causa de conflicte amb l’aplicacion {other_app} qu’es ja installada sus {other_path}", "app_location_unavailable": "Aquesta URL es pas disponibla o en conflicte amb una aplicacion existenta :\n{apps:s}", @@ -264,7 +264,7 @@ "migrate_tsig_failed": "La migracion del domeni dyndns {domain} cap a hmac-sha512 a pas capitat, anullacion de las modificacions. Error : {error_code} - {error}", "migrate_tsig_wait": "Esperem 3 minutas que lo servidor dyndns prenga en compte la novèla clau…", "migrate_tsig_not_needed": "Sembla qu’utilizatz pas un domeni dyndns, donc cap de migracion es pas necessària !", - "migration_0003_yunohost_upgrade": "Aviada de la mesa a nivèl del paquet YunoHost… La migracion acabarà, mas la mesa a jorn reala se realizarà tot bèl aprèp. Un còp acabada, poiretz vos reconnectar a l’administracion web.", + "migration_0003_yunohost_upgrade": "Aviada de la mesa a nivèl del paquet YunoHost… La migracion acabarà, mas l’actualizacion reala se realizarà tot bèl aprèp. Un còp acabada, poiretz vos reconnectar a l’administracion web.", "migration_0003_system_not_fully_up_to_date": "Lo sistèma es pas complètament a jorn. Mercés de lançar una mesa a jorn classica abans de començar la migracion per Stretch.", "migration_0003_modified_files": "Mercés de notar que los fichièrs seguents son estats detectats coma modificats manualament e poiràn èsser escafats a la fin de la mesa a nivèl : {manually_modified_files}", "monitor_period_invalid": "Lo periòde de temps es incorrècte", @@ -421,7 +421,7 @@ "log_app_change_url": "Cambiar l’URL de l’aplicacion « {} »", "log_app_install": "Installar l’aplicacion « {} »", "log_app_remove": "Levar l’aplicacion « {} »", - "log_app_upgrade": "Metre a jorn l’aplicacion « {} »", + "log_app_upgrade": "Actualizacion de l’aplicacion « {} »", "log_app_makedefault": "Far venir « {} » l’aplicacion per defaut", "log_available_on_yunopaste": "Lo jornal es ara disponible via {url}", "log_backup_restore_system": "Restaurar lo sistèma a partir d’una salvagarda", @@ -431,7 +431,7 @@ "log_domain_add": "Ajustar lo domeni « {} » dins la configuracion sistèma", "log_domain_remove": "Tirar lo domeni « {} » d’a la configuracion sistèma", "log_dyndns_subscribe": "S’abonar al subdomeni YunoHost « {} »", - "log_dyndns_update": "Metre a jorn l’adreça IP ligada a vòstre jos-domeni YunoHost « {} »", + "log_dyndns_update": "Actualizar l’adreça IP ligada a vòstre jos-domeni YunoHost « {} »", "log_letsencrypt_cert_install": "Installar lo certificat Let's encrypt sul domeni « {} »", "log_selfsigned_cert_install": "Installar lo certificat auto-signat sul domeni « {} »", "log_letsencrypt_cert_renew": "Renovar lo certificat Let's encrypt de « {} »", @@ -439,12 +439,12 @@ "log_service_regen_conf": "Regenerar la configuracion sistèma de « {} »", "log_user_create": "Ajustar l’utilizaire « {} »", "log_user_delete": "Levar l’utilizaire « {} »", - "log_user_update": "Metre a jorn las informacions a l’utilizaire « {} »", + "log_user_update": "Actualizar las informacions a l’utilizaire « {} »", "log_tools_maindomain": "Far venir « {} » lo domeni màger", "log_tools_migrations_migrate_forward": "Migrar", "log_tools_migrations_migrate_backward": "Tornar en arrièr", "log_tools_postinstall": "Realizar la post installacion del servidor YunoHost", - "log_tools_upgrade": "Mesa a jorn dels paquets sistèma", + "log_tools_upgrade": "Actualizacion dels paquets sistèma", "log_tools_shutdown": "Atudar lo servidor", "log_tools_reboot": "Reaviar lo servidor", "mail_unavailable": "Aquesta adreça electronica es reservada e deu èsser automaticament atribuida al tot bèl just primièr utilizaire", @@ -473,7 +473,7 @@ "app_start_remove": "Supression de l’aplicacion {app}…", "app_start_backup": "Recuperacion dels fichièrs de salvagardar per {app}…", "app_start_restore": "Restauracion de l’aplicacion {app}…", - "app_upgrade_several_apps": "Las aplicacions seguentas seràn mesas a jorn : {apps}", + "app_upgrade_several_apps": "Las aplicacions seguentas seràn actualizadas : {apps}", "ask_new_domain": "Nòu domeni", "ask_new_path": "Nòu camin", "backup_actually_backuping": "Creacion d’un archiu de seguretat a partir dels fichièrs recuperats…", @@ -565,7 +565,40 @@ "migration_0011_backup_before_migration": "Creacion d’una còpia de seguretat de la basa de donadas LDAP e de la configuracion de las aplicacions abans d’efectuar la migracion.", "migration_0011_create_group": "Creacion d’un grop per cada utilizaire…", "migration_0011_done": "Migracion complèta. Ara podètz gerir de grops d’utilizaires.", - "migration_0011_LDAP_update_failed": "Fracàs de la mesa a jorn de LDAP. Error : {error:s}", + "migration_0011_LDAP_update_failed": "Actualizacion impossibla de LDAP. Error : {error:s}", "migration_0011_migration_failed_trying_to_rollback": "La migracion a fracassat… ensag de tornar lo sistèma a l’estat anterio.", - "migration_0011_rollback_success": "Restauracion del sistèma reüssida." + "migration_0011_rollback_success": "Restauracion del sistèma reüssida.", + "apps_permission_restoration_failed": "Fracàs de la permission « {permission:s} » per la restauracion de l’aplicacion {app:s}", + "group_already_allowed": "Lo grop « {group:s} » a ja la permission « {permission:s} » activada per l’aplicacion « {app:s} »", + "group_already_disallowed": "Lo grop « {group:s} »a ja las permissions « {permission:s} » desactivadas per l’aplicacion « {app:s} »", + "group_updated": "Lo grop « {group} » es estat actualizat", + "group_update_failed": "Actualizacion impossibla del grop « {group} »", + "log_permission_add": "Ajustar la permission « {} » per l’aplicacion « {} »", + "log_permission_remove": "Suprimir la permission « {} »", + "log_permission_update": "Actualizacion de la permission « {} » per l’aplicacion « {} »", + "log_user_group_update": "Actualizar lo grop « {} »", + "log_user_permission_add": "Actualizar la permission « {} »", + "log_user_permission_remove": "Actualizar la permission « {} »", + "migration_description_0011_setup_group_permission": "Configurar lo grop d’utilizaire e las permission de las aplicacions e dels servicis", + "migration_0011_can_not_backup_before_migration": "La salvagarda del sistèma abans la migracion a pas capitat. La migracion a fracassat. Error : {error:s}", + "migration_0011_migrate_permission": "Migracion de las permission dels paramètres d’aplicacion a LDAP…", + "migration_0011_update_LDAP_database": "Actualizacion de la basa de donadas LDAP…", + "migration_0011_update_LDAP_schema": "Actualizacion de l’esquèma LDAP…", + "permission_already_exist": "La permission « {permission:s} » per l’aplicacion {app:s} existís ja", + "permission_created": "Permission creada « {permission:s} » per l’aplicacion{app:s}", + "permission_creation_failed": "Creacion impossibla de la permission", + "permission_deleted": "Permission « {permission:s} » per l’aplicacion {app:s} suprimida", + "permission_deletion_failed": "Fracàs de la supression de la permission « {permission:s} » per l’aplicacion {app:s}", + "permission_not_found": "Permission « {permission:s} » pas trobada per l’aplicacion {app:s}", + "permission_name_not_valid": "Lo nom de la permission « {permission:s} » es pas valid", + "permission_update_failed": "Fracàs de l’actualizacion de la permission", + "permission_generated": "La basa de donadas de las permission es estada actualizada", + "permission_updated": "La permission « {permission:s} » per l’aplicacion {app:s} es estada actualizada", + "permission_update_nothing_to_do": "Cap de permission d’actualizar", + "remove_main_permission_not_allowed": "Se pòt pas suprimir la permission màger", + "remove_user_of_group_not_allowed": "Sètz pas autorizat a suprimir {user:s} del grop {group:s}", + "system_groupname_exists": "Lo nom del grop existís ja dins lo sistèma de grops", + "tools_update_failed_to_app_fetchlist": "Fracàs de l’actualizacion de la lista d’aplicacions de YunoHost a causa de : {error}", + "user_already_in_group": "L’utilizaire {user:} es ja dins lo grop {group:s}", + "user_not_in_group": "L’utilizaire {user:} es pas dins lo grop {group:s}" } From 42ccb0973be72f847de4195d6ea7249c62c90f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Fri, 30 Aug 2019 19:08:56 +0000 Subject: [PATCH 050/299] Translated using Weblate (Occitan) Currently translated at 99.8% (581 of 582 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/locales/oc.json b/locales/oc.json index d65a71d1e..8981fbadd 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -600,5 +600,9 @@ "system_groupname_exists": "Lo nom del grop existís ja dins lo sistèma de grops", "tools_update_failed_to_app_fetchlist": "Fracàs de l’actualizacion de la lista d’aplicacions de YunoHost a causa de : {error}", "user_already_in_group": "L’utilizaire {user:} es ja dins lo grop {group:s}", - "user_not_in_group": "L’utilizaire {user:} es pas dins lo grop {group:s}" + "user_not_in_group": "L’utilizaire {user:} es pas dins lo grop {group:s}", + "edit_permission_with_group_all_users_not_allowed": "Podètz pas modificar las permissions del grop « all_users », utilizatz « yunohost user permission clear APP » o « yunohost user permission add APP -u USER ».", + "mailbox_disabled": "La bóstia de las letras es desactivada per l’utilizaire {user:s}", + "migration_0011_LDAP_config_dirty": "Sembla qu’avètz modificat manualament la configuracion LDAP. Per far aquesta migracion cal actualizar la configuracion LDAP.\nSalvagardatz la configuracion actuala, reïnicializatz la configuracion originala amb la comanda « yunohost tools regen-conf -f » e tornatz ensajar la migracion", + "need_define_permission_before": "Vos cal tornar definir las permission en utilizant « yunohost user permission add -u USER » abans de suprimir un grop permés" } From 70cf0db0ad9073c0f7ca27b5dab52c26414ce59b Mon Sep 17 00:00:00 2001 From: "ljf (zamentur)" Date: Wed, 4 Sep 2019 13:35:06 +0200 Subject: [PATCH 051/299] [enh] Rename permission http api route in plural --- data/actionsmap/yunohost.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 7c864cce0..45f003b78 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -274,7 +274,7 @@ user: ### user_permission_list() list: action_help: List access to user and group - api: GET /users/permission/ + api: GET /users/permissions/ arguments: -a: full: --app @@ -300,7 +300,7 @@ user: ### user_permission_add() add: action_help: Grant access right to users and group - api: POST /users/permission/ + api: POST /users/permissions/ arguments: app: help: Application to manage the permission @@ -328,7 +328,7 @@ user: ### user_permission_remove() remove: action_help: Revoke access right to users and group - api: PUT /users/permission/ + api: PUT /users/permissions/ arguments: app: help: Application to manage the permission @@ -356,7 +356,7 @@ user: ## user_permission_clear() clear: action_help: Reset access rights for the app - api: DELETE /users/permission/ + api: DELETE /users/permissions/ arguments: app: help: Application to manage the permission From 8a92727fb1b625faa0d5e150291c6aa3bae57f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Sat, 7 Sep 2019 13:01:45 +0200 Subject: [PATCH 052/299] Check that exist before the calculate the checksum --- data/helpers.d/backup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/backup b/data/helpers.d/backup index dcf306085..d3ffffcd3 100644 --- a/data/helpers.d/backup +++ b/data/helpers.d/backup @@ -339,7 +339,7 @@ ynh_backup_if_checksum_is_different () { backup_file_checksum="" if [ -n "$checksum_value" ] then # Proceed only if a value was stored into the app settings - if ! echo "$checksum_value $file" | sudo md5sum -c --status + if [ -e $file ] && ! echo "$checksum_value $file" | sudo md5sum -c --status then # If the checksum is now different backup_file_checksum="/home/yunohost.conf/backup/$file.backup.$(date '+%Y%m%d.%H%M%S')" sudo mkdir -p "$(dirname "$backup_file_checksum")" From 92fa21641bfa8c3c323fe87a4dbb27086a9c8b38 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 9 Sep 2019 12:07:44 +0200 Subject: [PATCH 053/299] Fix some stuff about removing the --revert option --- locales/en.json | 8 +++----- .../0007_ssh_conf_managed_by_yunohost_step1.py | 9 +++++++++ src/yunohost/tools.py | 6 +++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/locales/en.json b/locales/en.json index 850d89032..a4596941f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -320,7 +320,6 @@ "migrate_tsig_wait_3": "1min…", "migrate_tsig_wait_4": "30 secondes…", "migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed!", - "migration_backward_impossible": "The migration {name} cannot be reverted.", "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", "migration_description_0002_migrate_to_tsig_sha256": "Improve security of dyndns TSIG by using SHA512 instead of MD5", "migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0", @@ -372,20 +371,19 @@ "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", "migrations_dependencies_not_satisfied": "Can't run migration {id} because first you need to run these migrations: {dependencies_id}", "migrations_failed_to_load_migration": "Failed to load migration {id} : {error}", - "migrations_exclusive_options": "--auto, --skip, --revert and --force-rerun are exclusive options.", + "migrations_exclusive_options": "--auto, --skip, and --force-rerun are exclusive options.", "migrations_list_conflict_pending_done": "You cannot use both --previous and --done at the same time.", "migrations_loading_migration": "Loading migration {id}…", "migrations_migration_has_failed": "Migration {id} has failed, aborting. Error: {exception}", - "migrations_must_provide_explicit_targets": "You must provide explicit targets when using --skip, --revert or --force-rerun", + "migrations_must_provide_explicit_targets": "You must provide explicit targets when using --skip or --force-rerun", "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option --accept-disclaimer.", "migrations_no_migrations_to_run": "No migrations to run", "migrations_no_such_migration": "No such migration called {id}", "migrations_not_pending_cant_skip": "Those migrations are not pending so cannot be skipped: {ids}", - "migrations_pending_cant_revert_or_rerun": "Those migrations are still pending so cannot be reverted or reran: {ids}", + "migrations_pending_cant_rerun": "Those migrations are still pending so cannot be reran: {ids}", "migrations_running_forward": "Running migration {id}…", "migrations_skip_migration": "Skipping migration {id}…", "migrations_success_forward": "Successfully ran migration {id}!", - "migrations_success_revert": "Successfully reverted migration {id}!", "migrations_to_be_ran_manually": "Migration {id} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", "monitor_disabled": "The server monitoring has been disabled", "monitor_enabled": "The server monitoring has been enabled", diff --git a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py index 818760e17..624288210 100644 --- a/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py +++ b/src/yunohost/data_migrations/0007_ssh_conf_managed_by_yunohost_step1.py @@ -59,3 +59,12 @@ class MyMigration(Migration): copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp') regen_conf(names=['ssh'], force=True) copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) + + # Restart ssh and rollback if it failed + if not _run_service_command('restart', 'ssh'): + # We don't rollback completely but it should be enough + copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF) + if not _run_service_command('restart', 'ssh'): + raise YunohostError("migration_0007_cannot_restart") + else: + raise YunohostError("migration_0007_cancelled") diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index d2015adff..8e2635e2c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1081,7 +1081,7 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal if skip and done: raise YunohostError("migrations_not_pending_cant_skip", ids=', '.join(done)) if force_rerun and pending: - raise YunohostError("migrations_pending_cant_revert_or_rerun", ids=', '.join(pending)) + raise YunohostError("migrations_pending_cant_rerun", ids=', '.join(pending)) if not (skip or force_rerun) and done: raise YunohostError("migrations_already_ran", ids=', '.join(done)) @@ -1304,11 +1304,11 @@ class Migration(object): def disclaimer(self): return None - # The followings shouldn't be overriden - def run(self): raise NotImplementedError() + # The followings shouldn't be overriden + def __init__(self, id_): self.id = id_ self.number = int(self.id.split("_", 1)[0]) From b29731c28b2688547dbbd518cd3897cdede94102 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 10 Sep 2019 14:25:51 +0200 Subject: [PATCH 054/299] We don't need this try/except --- src/yunohost/tools.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 8e2635e2c..7b2ee8526 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1180,11 +1180,7 @@ def _migrate_legacy_migration_json(): last_run_migration_id = str(old_state["number"]) + "_" + old_state["name"] # Extract the list of migration ids - try: - from . import data_migrations - except ImportError: - # Well ugh what are we supposed to do if we can't do this >.>... - pass + from . import data_migrations migrations_path = data_migrations.__path__[0] migration_files = filter(lambda x: re.match("^\d+_[a-zA-Z0-9_]+\.py$", x), os.listdir(migrations_path)) # (here we remove the .py extension and make sure the ids are sorted) From ec3856a25c532b8b552a428c869bc8a47000b1de Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 10 Sep 2019 17:15:01 +0200 Subject: [PATCH 055/299] Propagate changes about revert mechanism and naming on migration 12 --- .../0012_postgresql_password_to_md5_authentication.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py b/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py index 5d36b3e23..636b4f12b 100644 --- a/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py +++ b/src/yunohost/data_migrations/0012_postgresql_password_to_md5_authentication.py @@ -10,12 +10,7 @@ class MyMigration(Migration): all_hba_files = glob.glob("/etc/postgresql/*/*/pg_hba.conf") - def forward(self): + def run(self): for filename in self.all_hba_files: pg_hba_in = read_file(filename) write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)password", "local\\1all\\2all\\3md5", pg_hba_in)) - - def backward(self): - for filename in self.all_hba_files: - pg_hba_in = read_file(filename) - write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)md5", "local\\1all\\2all\\3password", pg_hba_in)) From 84fe23786c21e343a0925b1846bea1a460a7482d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josu=C3=A9=20Tille?= Date: Tue, 3 Sep 2019 14:47:08 +0200 Subject: [PATCH 056/299] Fix permission sychronization --- src/yunohost/permission.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 3beb3ac2b..897ffadd8 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -484,8 +484,9 @@ def permission_sync_to_user(force=False): for per in ldap.search('ou=permission,dc=yunohost,dc=org', '(objectclass=permissionYnh)', ['cn', 'inheritPermission', 'groupPermission', 'memberUid']): + print(per) if 'groupPermission' not in per: - continue + per['groupPermission'] = [] user_permission = set() for group in per['groupPermission']: group = group.split("=")[1].split(",")[0] From a4ec76e77bb25abfc12be17889c949a4beacb627 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 10 Sep 2019 14:36:28 +0200 Subject: [PATCH 057/299] Remove debug print --- src/yunohost/permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 897ffadd8..f7fa307da 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -484,7 +484,7 @@ def permission_sync_to_user(force=False): for per in ldap.search('ou=permission,dc=yunohost,dc=org', '(objectclass=permissionYnh)', ['cn', 'inheritPermission', 'groupPermission', 'memberUid']): - print(per) + if 'groupPermission' not in per: per['groupPermission'] = [] user_permission = set() From fd99ef0d6c3ce870211debc36da44e9dd4b1e41f Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Wed, 11 Sep 2019 03:56:53 +0200 Subject: [PATCH 058/299] [ux] make dns-conf help sentence obvious that it's a sample conf --- data/actionsmap/yunohost.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 45f003b78..def754220 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -468,7 +468,7 @@ domain: ### domain_dns_conf() dns-conf: - action_help: Generate DNS configuration for a domain + action_help: Generate sample DNS configuration for a domain api: GET /domains//dns arguments: domain: From 92a6315514b42e125024303ea1cc91cd77e2c461 Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Wed, 11 Sep 2019 05:20:08 +0200 Subject: [PATCH 059/299] [ux] apparently is super nerdy and you should use --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index fd62a6b91..962f52562 100644 --- a/locales/en.json +++ b/locales/en.json @@ -447,7 +447,7 @@ "port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections", "port_available": "Port {port:d} is available", "port_unavailable": "Port {port:d} is not available", - "recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create $username' or the admin interface.", + "recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create ' or the admin interface.", "remove_main_permission_not_allowed": "Removing the main permission is not allowed", "remove_user_of_group_not_allowed": "You are not allowed to remove the user {user:s} in the group {group:s}", "regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'", From 6599ae1ad00e4b499e03340e0badba4ba635c2b8 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Tue, 10 Sep 2019 11:24:28 +0000 Subject: [PATCH 060/299] Translated using Weblate (Arabic) Currently translated at 65.5% (382 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ar/ --- locales/ar.json | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 285a0f819..c8a1bba7c 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -301,7 +301,7 @@ "service_add_failed": "تعذرت إضافة خدمة '{service:s}'", "service_added": "The service '{service:s}' has been added", "service_already_started": "Service '{service:s}' has already been started", - "service_already_stopped": "Service '{service:s}' has already been stopped", + "service_already_stopped": "إنّ خدمة '{service:s}' متوقفة مِن قبلُ", "service_cmd_exec_failed": "Unable to execute command '{command:s}'", "service_conf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'", "service_conf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'", @@ -329,7 +329,7 @@ "service_started": "تم إطلاق تشغيل خدمة '{service:s}'", "service_status_failed": "Unable to determine status of service '{service:s}'", "service_stop_failed": "", - "service_stopped": "The service '{service:s}' has been stopped", + "service_stopped": "تمّ إيقاف خدمة '{service:s}'", "service_unknown": "Unknown service '{service:s}'", "ssowat_conf_generated": "The SSOwat configuration has been generated", "ssowat_conf_updated": "The SSOwat configuration has been updated", @@ -343,7 +343,7 @@ "unlimit": "دون تحديد الحصة", "unrestore_app": "App '{app:s}' will not be restored", "update_cache_failed": "Unable to update APT cache", - "updating_apt_cache": "جارٍ تحديث قائمة الحُزم المتوفرة …", + "updating_apt_cache": "جارٍ جلب قائمة حُزم النظام المحدّثة المتوفرة…", "upgrade_complete": "إكتملت عملية الترقية و التحديث", "upgrading_packages": "عملية ترقية الحُزم جارية …", "upnp_dev_not_found": "No UPnP device found", @@ -412,7 +412,7 @@ "service_description_dnsmasq": "مُكلَّف بتحليل أسماء النطاقات (DNS)", "service_description_mysql": "يقوم بتخزين بيانات التطبيقات (قواعد بيانات SQL)", "service_description_rspamd": "يقوم بتصفية البريد المزعج و إدارة ميزات أخرى للبريد", - "service_description_yunohost-firewall": "يريد فتح و غلق منافذ الإتصال إلى الخدمات", + "service_description_yunohost-firewall": "يُدير فتح وإغلاق منافذ الإتصال إلى الخدمات", "users_available": "المستخدمون المتوفرون:", "aborting": "إلغاء.", "admin_password_too_long": "يرجى اختيار كلمة سرية أقصر مِن 127 حرف", @@ -426,5 +426,8 @@ "global_settings_setting_security_password_admin_strength": "قوة الكلمة السرية الإدارية", "global_settings_setting_security_password_user_strength": "قوة الكلمة السرية للمستخدم", "log_app_addaccess": "إضافة ترخيص بالنفاذ إلى '{}'", - "password_too_simple_1": "يجب أن يكون طول الكلمة السرية على الأقل 8 حروف" + "password_too_simple_1": "يجب أن يكون طول الكلمة السرية على الأقل 8 حروف", + "service_description_php7.0-fpm": "يُشغّل التطبيقات المكتوبة بلغة الـ PHP على Nginx", + "updating_app_lists": "جارٍ جلب التحديثات المتوفرة الخاصة بالتطبيقات…", + "already_up_to_date": "كل شيء على ما يرام! ليس هناك ما يتطلّب تحديثًا!" } From 4204f418900dcc09deae4cc721f4be142ca04310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carles=20Sadurn=C3=AD=20Anguita?= Date: Sun, 8 Sep 2019 09:07:26 +0000 Subject: [PATCH 061/299] Translated using Weblate (Catalan) Currently translated at 100.0% (583 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index a9a0c2e0f..f5c040670 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -198,7 +198,7 @@ "executing_script": "Execució de l'script « {script:s} »…", "extracting": "Extracció en curs…", "dyndns_cron_installed": "S'ha instal·lat la tasca cron pel DynDNS", - "dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron pel DynDNS", + "dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron per a DynDNS: {error}", "dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS", "experimental_feature": "Atenció: aquesta funcionalitat és experimental i no es considera estable, no s'ha d'utilitzar a excepció de saber el que esteu fent.", "field_invalid": "Camp incorrecte « {:s} »", @@ -581,5 +581,6 @@ "system_groupname_exists": "El nom de grup ja existeix en el sistema de grups", "tools_update_failed_to_app_fetchlist": "No s'ha pogut actualitzar la llista d'aplicacions de YunoHost a causa de: {error}", "user_already_in_group": "L'usuari {user:s} ja és en el grup {group:s}", - "user_not_in_group": "L'usuari {user:s} no és en el grup {group:s}" + "user_not_in_group": "L'usuari {user:s} no és en el grup {group:s}", + "migration_description_0012_postgresql_password_to_md5_authentication": "Força l'autenticació postgresql a fer servir md5 per a les connexions locals" } From 8de950a4369baa812e0954dac73e937ab293b663 Mon Sep 17 00:00:00 2001 From: Aksel Kiesling Date: Tue, 3 Sep 2019 06:29:42 +0000 Subject: [PATCH 062/299] Translated using Weblate (German) Currently translated at 55.6% (324 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index a4a6c236b..cef591411 100644 --- a/locales/de.json +++ b/locales/de.json @@ -301,5 +301,58 @@ "backup_archive_writing_error": "Die Dateien konnten nicht in der komprimierte Archiv-Backup hinzugefügt werden", "app_change_url_success": "Erfolgreiche Änderung der URL von {app:s} zu {domain:s}{path:s}", "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...", - "invalid_url_format": "ungültiges URL Format" + "invalid_url_format": "ungültiges URL Format", + "global_settings_bad_type_for_setting": "Falscher Typ für Einstellung {setting: s}. Empfangen: {receive_type: s}, aber erwartet: {expected_type: s}", + "global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting: s}. Habe '{choice: s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices: s}", + "file_does_not_exist": "Die Datei {path: s} existiert nicht.", + "experimental_feature": "Warnung: Diese Funktion ist experimentell und gilt nicht als stabil. Sie sollten sie nur verwenden, wenn Sie wissen, was Sie tun.", + "error_when_removing_sftpuser_group": "Fehler beim Versuch, die Gruppe sftpusers zu entfernen", + "edit_permission_with_group_all_users_not_allowed": "Sie dürfen die Berechtigung für die Gruppe \"all_users\" nicht bearbeiten. Verwenden Sie stattdessen \"yunohost user permission clear APP\" oder \"yunohost user permission add APP -u USER\".", + "edit_group_not_allowed": "Du bist nicht berechtigt zum Bearbeiten der Gruppe {group: s}", + "dyndns_domain_not_provided": "Der Dyndns-Anbieter {provider: s} kann die Domain(s) {domain: s} nicht bereitstellen.", + "dyndns_could_not_check_available": "Konnte nicht überprüfen, ob {domain: s} auf {provider: s} verfügbar ist.", + "dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider: s} die Domain(s) {domain: s} bereitstellen kann.", + "domain_dyndns_dynette_is_unreachable": "YunoHost dynette kann nicht erreicht werden, entweder ist Ihr YunoHost nicht korrekt mit dem Internet verbunden oder der dynette-Server ist inaktiv. Fehler: {error}", + "domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen, was die * empfohlene * Konfiguration ist. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.", + "dpkg_lock_not_available": "Dieser Befehl kann momentan nicht ausgeführt werden, da anscheinend ein anderes Programm die Sperre von dpkg (dem Systempaket-Manager) verwendet", + "confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Deines Systems beeinträchtigen. Du solltest es wahrscheinlich NICHT installieren, es sei denn, Du weisst, was Du tust. Bist du bereit, dieses Risiko einzugehen? [{answers: s}] ", + "confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"funktioniert nicht\") und es ist wahrscheinlich, dass Dein System Schaden nimmt! Du solltest es wahrscheinlich NICHT installieren, es sei denn, Du weisst, was Du tust. Bist du bereit, dieses Risiko einzugehen? [{answers: s}] ", + "confirm_app_install_warning": "Warnung: Diese Anwendung funktioniert möglicherweise, ist jedoch nicht gut in YunoHost integriert. Einige Funktionen wie Single Sign-On und Backup / Restore sind möglicherweise nicht verfügbar. Trotzdem installieren? [{answers: s}] ", + "backup_with_no_restore_script_for_app": "App {app: s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.", + "backup_with_no_backup_script_for_app": "App {app: s} hat kein Sicherungsskript. Ignoriere es.", + "backup_unable_to_organize_files": "Dateien im Archiv können mit der schnellen Methode nicht organisiert werden", + "backup_system_part_failed": "Der Systemteil '{part: s}' kann nicht gesichert werden", + "backup_permission": "Sicherungsberechtigung für App {app: s}", + "backup_output_symlink_dir_broken": "Sie haben einen fehlerhaften Symlink anstelle Ihres Archivverzeichnisses '{path: s}'. Möglicherweise haben Sie ein spezielles Setup, um Ihre Daten auf einem anderen Dateisystem zu sichern. In diesem Fall haben Sie wahrscheinlich vergessen, Ihre Festplatte oder Ihren USB-Schlüssel erneut einzuhängen oder anzuschließen.", + "backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten…", + "backup_method_tar_finished": "Sicherungs-Tar-Archiv erstellt", + "backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method: s}' beendet", + "backup_method_copy_finished": "Sicherungskopie beendet", + "backup_method_borg_finished": "Backup in Borg beendet", + "backup_custom_need_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Braucht ein Einhängen/Verbinden\" (need_mount) ein Fehler aufgetreten", + "backup_custom_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Einhängen/Verbinden\" ein Fehler aufgetreten", + "backup_custom_backup_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Sicherung\" ein Fehler aufgetreten", + "backup_csv_creation_failed": "Die CSV-Datei, die für zukünftige Wiederherstellungsvorgänge erforderlich ist, kann nicht erstellt werden", + "backup_couldnt_bind": "{Src: s} konnte nicht an {dest: s} angebunden werden.", + "backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert", + "backup_ask_for_copying_if_needed": "Einige Dateien konnten mit der Methode, die es vermeidet vorübergehend Speicherplatz auf dem System zu verschwenden, nicht gesichert werden. Zur Durchführung der Sicherung sollten vorübergehend {size: s} MB verwendet werden. Sind Sie einverstanden?", + "backup_actually_backuping": "Erstelle nun ein Backup-Archiv aus den gesammelten Dateien …", + "ask_path": "Pfad", + "ask_new_path": "Neuer Pfad", + "ask_new_domain": "Neue Domain", + "apps_permission_restoration_failed": "Die Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} ist fehlgeschlagen", + "apps_permission_not_found": "Keine Berechtigung für die installierten Apps gefunden", + "app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden", + "app_upgrade_app_name": "App {App} wird jetzt aktualisiert…", + "app_upgrade_several_apps": "Die folgenden Apps werden aktualisiert: {apps}", + "app_start_restore": "Anwendung {app} wird wiederhergestellt…", + "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen…", + "app_start_remove": "Anwendung {app} wird entfernt…", + "app_start_install": "Anwendung {app} wird installiert…", + "app_not_upgraded": "Die folgenden Apps wurden nicht aktualisiert: {apps}", + "app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der anderen App \"{other_app}\" verwendet", + "aborting": "Breche ab.", + "app_action_cannot_be_ran_because_required_services_down": "Diese App erfordert einige Dienste, die derzeit nicht verfügbar sind. Bevor Sie fortfahren, sollten Sie versuchen, die folgenden Dienste neu zu starten (und möglicherweise untersuchen, warum sie nicht verfügbar sind): {services}", + "already_up_to_date": "Nichts zu tun! Alles ist bereits auf dem neusten Stand!", + "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen." } From ad417057270e1d547444822e6bd4f1e051840915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Wed, 4 Sep 2019 18:47:59 +0000 Subject: [PATCH 063/299] Translated using Weblate (Occitan) Currently translated at 99.8% (582 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/oc.json b/locales/oc.json index 8981fbadd..2cb33f4bd 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -157,7 +157,7 @@ "downloading": "Telecargament…", "dyndns_could_not_check_provide": "Impossible de verificar se {provider:s} pòt provesir {domain:s}.", "dyndns_cron_installed": "La tasca cron pel domeni DynDNS es installada", - "dyndns_cron_remove_failed": "Impossible de levar la tasca cron pel domeni DynDNS", + "dyndns_cron_remove_failed": "Impossible de levar la tasca cron pel domeni DynDNS a causa de {error}", "dyndns_cron_removed": "La tasca cron pel domeni DynDNS es levada", "dyndns_ip_update_failed": "Impossible d’actualizar l’adreça IP sul domeni DynDNS", "dyndns_ip_updated": "Vòstra adreça IP es estada actualizada pel domeni DynDNS", @@ -604,5 +604,7 @@ "edit_permission_with_group_all_users_not_allowed": "Podètz pas modificar las permissions del grop « all_users », utilizatz « yunohost user permission clear APP » o « yunohost user permission add APP -u USER ».", "mailbox_disabled": "La bóstia de las letras es desactivada per l’utilizaire {user:s}", "migration_0011_LDAP_config_dirty": "Sembla qu’avètz modificat manualament la configuracion LDAP. Per far aquesta migracion cal actualizar la configuracion LDAP.\nSalvagardatz la configuracion actuala, reïnicializatz la configuracion originala amb la comanda « yunohost tools regen-conf -f » e tornatz ensajar la migracion", - "need_define_permission_before": "Vos cal tornar definir las permission en utilizant « yunohost user permission add -u USER » abans de suprimir un grop permés" + "need_define_permission_before": "Vos cal tornar definir las permission en utilizant « yunohost user permission add -u USER » abans de suprimir un grop permés", + "permission_already_clear": "La permission « {permission:s} » ja levada per l’aplicacion {app:s}", + "migration_description_0012_postgresql_password_to_md5_authentication": "Forçar l’autentificacion postgresql a utilizar md5 per las connexions localas" } From d1c54fc2ca4e52e5dcaf714d051c0487be46649d Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Wed, 11 Sep 2019 10:04:08 +0000 Subject: [PATCH 064/299] Translated using Weblate (Arabic) Currently translated at 67.6% (394 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ar/ --- locales/ar.json | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/locales/ar.json b/locales/ar.json index c8a1bba7c..52e86d90b 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -429,5 +429,17 @@ "password_too_simple_1": "يجب أن يكون طول الكلمة السرية على الأقل 8 حروف", "service_description_php7.0-fpm": "يُشغّل التطبيقات المكتوبة بلغة الـ PHP على Nginx", "updating_app_lists": "جارٍ جلب التحديثات المتوفرة الخاصة بالتطبيقات…", - "already_up_to_date": "كل شيء على ما يرام! ليس هناك ما يتطلّب تحديثًا!" + "already_up_to_date": "كل شيء على ما يرام! ليس هناك ما يتطلّب تحديثًا!", + "service_description_nslcd": "يدير اتصال متسخدمي واي يونوهوست عبر طرفية سطر الأوامر", + "service_description_slapd": "يخزّن المستخدمين والنطاقات والمعلومات المتعلقة بها", + "service_reloaded": "تم إعادة تحميل خدمة '{service:s}'", + "service_restarted": "تم إعادة تشغيل خدمة '{service:s}'", + "group_unknown": "الفريق {group:s} مجهول", + "group_deletion_failed": "فشلت عملية حذف الفريق '{group}'", + "group_deleted": "تم حذف الفريق '{group}'", + "group_created": "تم إنشاء الفريق '{group}' بنجاح", + "group_name_already_exist": "الفريق {name:s} موجود بالفعل", + "error_when_removing_sftpuser_group": "حدث خطأ أثناء محاولة حذف فريق sftpusers", + "dyndns_could_not_check_available": "لا يمكن التحقق مِن أنّ {domain:s} متوفر على {provider:s}.", + "backup_mount_archive_for_restore": "جارٍ تهيئة النسخة الاحتياطية للاسترجاع…" } From 777b7248cc4c3de88d97da32946ea1088bfea048 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Wed, 11 Sep 2019 12:20:30 +0000 Subject: [PATCH 065/299] Translated using Weblate (French) Currently translated at 93.8% (547 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 2f22f1397..c94406a07 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -89,7 +89,7 @@ "done": "Terminé", "downloading": "Téléchargement en cours …", "dyndns_cron_installed": "La tâche cron pour le domaine DynDNS a été installée", - "dyndns_cron_remove_failed": "Impossible de supprimer la tâche cron pour le domaine DynDNS", + "dyndns_cron_remove_failed": "Impossible de supprimer la tâche cron DynDNS parce que: {error}", "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS a été enlevée", "dyndns_ip_update_failed": "Impossible de mettre à jour l’adresse IP sur le domaine DynDNS", "dyndns_ip_updated": "Votre adresse IP a été mise à jour pour le domaine DynDNS", @@ -579,7 +579,7 @@ "group_name_already_exist": "Le groupe {name:s} existe déjà", "group_creation_failed": "Échec de la création du groupe '{group}'", "group_deletion_failed": "Échec de la suppression du groupe '{group}'", - "edit_permission_with_group_all_users_not_allowed": "Vous n'êtes pas autorisé à modifier les permissions pour le groupe 'all_users', utilisez'yunohost user permission clear APP' ou 'yunohost user permission add APP -u USER'.", + "edit_permission_with_group_all_users_not_allowed": "Vous n'êtes pas autorisé à modifier les permissions pour le groupe 'all_users', utilisez 'yunohost user permission clear APP' ou 'yunohost user permission add APP -u USER' à la place.", "log_permission_add": "Ajouter l'autorisation '{}' pour l'application '{}'", "log_permission_remove": "Supprimer l'autorisation '{}'", "log_permission_update": "Mise à jour de l'autorisation '{}' pour l'application '{}'", From 51171b84bf0a8c30b46170ee7a040bc6d655f54b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 10 Sep 2019 22:24:49 +0200 Subject: [PATCH 066/299] main.metronome -> main.xmpp --- data/other/ldap_scheme.yml | 4 ++-- data/templates/metronome/domain.tpl.cfg.lua | 2 +- src/yunohost/backup.py | 4 ++-- src/yunohost/tests/test_permission.py | 14 +++++++------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/data/other/ldap_scheme.yml b/data/other/ldap_scheme.yml index 11504bbe8..d013149af 100644 --- a/data/other/ldap_scheme.yml +++ b/data/other/ldap_scheme.yml @@ -67,8 +67,8 @@ depends_children: - permissionYnh groupPermission: - "cn=all_users,ou=groups,dc=yunohost,dc=org" - cn=main.metronome,ou=permission: - cn: main.metronome + cn=main.xmpp,ou=permission: + cn: main.xmpp gidNumber: "5002" objectClass: - posixGroup diff --git a/data/templates/metronome/domain.tpl.cfg.lua b/data/templates/metronome/domain.tpl.cfg.lua index 2ee9cfaae..d523365db 100644 --- a/data/templates/metronome/domain.tpl.cfg.lua +++ b/data/templates/metronome/domain.tpl.cfg.lua @@ -8,7 +8,7 @@ VirtualHost "{{ domain }}" hostname = "localhost", user = { basedn = "ou=users,dc=yunohost,dc=org", - filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.metronome,ou=permission,dc=yunohost,dc=org))", + filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.xmpp,ou=permission,dc=yunohost,dc=org))", usernamefield = "mail", namefield = "cn", }, diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index bd5d5750d..55b6678b8 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1191,7 +1191,7 @@ class RestoreManager(): old_apps_permission = [] try: old_apps_permission = ldap.search('ou=permission,dc=yunohost,dc=org', - '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))', + '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.xmpp))(!(cn=main.sftp)))', ['cn', 'objectClass', 'groupPermission', 'URL', 'gidNumber']) except: logger.info(m18n.n('apps_permission_not_found')) @@ -1247,7 +1247,7 @@ class RestoreManager(): # Remove all permission for all app which sill in the LDAP for per in ldap.search('ou=permission,dc=yunohost,dc=org', - '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.metronome))(!(cn=main.sftp)))', + '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.xmpp))(!(cn=main.sftp)))', ['cn']): if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]): raise YunohostError('permission_deletion_failed', diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index d309a8211..3b9815f63 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -135,7 +135,7 @@ def check_LDAP_db_integrity(): def check_permission_for_apps(): # We check that the for each installed apps we have at last the "main" permission # and we don't have any permission linked to no apps. The only exception who is not liked to an app - # is mail, metronome, and sftp + # is mail, xmpp, and sftp from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -146,7 +146,7 @@ def check_permission_for_apps(): installed_apps = {app['id'] for app in app_list(installed=True)['apps']} permission_list_set = {permission['cn'][0].split(".")[1] for permission in permission_search} - extra_service_permission = set(['mail', 'metronome']) + extra_service_permission = set(['mail', 'xmpp']) if 'sftp' in permission_list_set: extra_service_permission.add('sftp') assert installed_apps == permission_list_set - extra_service_permission @@ -164,8 +164,8 @@ def test_list_permission(): assert "main" in res['blog'] assert "mail" in res assert "main" in res['mail'] - assert "metronome" in res - assert "main" in res['metronome'] + assert "xmpp" in res + assert "main" in res['xmpp'] assert ["all_users"] == res['wiki']['main']['allowed_groups'] assert ["alice"] == res['blog']['main']['allowed_groups'] assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users']) @@ -220,9 +220,9 @@ def test_remove_bad_permission(): assert "blog" in res assert "main" in res['blog'] assert "mail" in res - assert "main" in res ['mail'] - assert "metronome" in res - assert "main" in res['metronome'] + assert "main" in res['mail'] + assert "xmpp" in res + assert "main" in res['xmpp'] def test_remove_main_permission(): with pytest.raises(YunohostError): From 0f688caccd33f425059081d0c283afce075e328b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 10 Sep 2019 22:55:37 +0200 Subject: [PATCH 067/299] Swap 'main' in permission namespace --- data/other/ldap_scheme.yml | 8 ++++---- data/templates/dovecot/dovecot-ldap.conf | 4 ++-- data/templates/metronome/domain.tpl.cfg.lua | 2 +- data/templates/postfix/plain/ldap-accounts.cf | 2 +- data/templates/postfix/plain/ldap-aliases.cf | 2 +- src/yunohost/app.py | 2 +- src/yunohost/backup.py | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/other/ldap_scheme.yml b/data/other/ldap_scheme.yml index d013149af..caa8fffb2 100644 --- a/data/other/ldap_scheme.yml +++ b/data/other/ldap_scheme.yml @@ -59,16 +59,16 @@ children: - groupOfNamesYnh depends_children: - cn=main.mail,ou=permission: - cn: main.mail + cn=mail.main,ou=permission: + cn: mail.main gidNumber: "5001" objectClass: - posixGroup - permissionYnh groupPermission: - "cn=all_users,ou=groups,dc=yunohost,dc=org" - cn=main.xmpp,ou=permission: - cn: main.xmpp + cn=xmpp.main,ou=permission: + cn: xmpp.main gidNumber: "5002" objectClass: - posixGroup diff --git a/data/templates/dovecot/dovecot-ldap.conf b/data/templates/dovecot/dovecot-ldap.conf index c7c9785fd..3a80ba47f 100644 --- a/data/templates/dovecot/dovecot-ldap.conf +++ b/data/templates/dovecot/dovecot-ldap.conf @@ -3,7 +3,7 @@ auth_bind = yes ldap_version = 3 base = ou=users,dc=yunohost,dc=org user_attrs = uidNumber=500,gidNumber=8,mailuserquota=quota_rule=*:bytes=%$ -user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org)) -pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org)) +user_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org)) +pass_filter = (&(objectClass=inetOrgPerson)(uid=%n)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org)) default_pass_scheme = SSHA diff --git a/data/templates/metronome/domain.tpl.cfg.lua b/data/templates/metronome/domain.tpl.cfg.lua index d523365db..e7f6bcef7 100644 --- a/data/templates/metronome/domain.tpl.cfg.lua +++ b/data/templates/metronome/domain.tpl.cfg.lua @@ -8,7 +8,7 @@ VirtualHost "{{ domain }}" hostname = "localhost", user = { basedn = "ou=users,dc=yunohost,dc=org", - filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=main.xmpp,ou=permission,dc=yunohost,dc=org))", + filter = "(&(objectClass=posixAccount)(mail=*@{{ domain }})(permission=cn=xmpp.main,ou=permission,dc=yunohost,dc=org))", usernamefield = "mail", namefield = "cn", }, diff --git a/data/templates/postfix/plain/ldap-accounts.cf b/data/templates/postfix/plain/ldap-accounts.cf index 9f6f94e6d..75f38cf58 100644 --- a/data/templates/postfix/plain/ldap-accounts.cf +++ b/data/templates/postfix/plain/ldap-accounts.cf @@ -1,5 +1,5 @@ server_host = localhost server_port = 389 search_base = dc=yunohost,dc=org -query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org)) +query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org)) result_attribute = uid diff --git a/data/templates/postfix/plain/ldap-aliases.cf b/data/templates/postfix/plain/ldap-aliases.cf index 5e7d3a6c1..46563ae22 100644 --- a/data/templates/postfix/plain/ldap-aliases.cf +++ b/data/templates/postfix/plain/ldap-aliases.cf @@ -1,5 +1,5 @@ server_host = localhost server_port = 389 search_base = dc=yunohost,dc=org -query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=main.mail,ou=permission,dc=yunohost,dc=org)) +query_filter = (&(objectClass=mailAccount)(mail=%s)(permission=cn=mail.main,ou=permission,dc=yunohost,dc=org)) result_attribute = maildrop diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4a14c5e4b..105d4faf7 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -432,7 +432,7 @@ def app_map(app=None, raw=False, user=None): if user is not None: ldap = _get_ldap_interface() if not ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=main.%s)(inheritPermission=uid=%s,ou=users,dc=yunohost,dc=org))' % (app_id, user), + filter='(&(objectclass=permissionYnh)(cn=%s.main)(inheritPermission=uid=%s,ou=users,dc=yunohost,dc=org))' % (app_id, user), attrs=['cn']): continue diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 55b6678b8..9a27031ae 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1247,7 +1247,7 @@ class RestoreManager(): # Remove all permission for all app which sill in the LDAP for per in ldap.search('ou=permission,dc=yunohost,dc=org', - '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.xmpp))(!(cn=main.sftp)))', + '(&(objectClass=permissionYnh)(!(cn=mail.main))(!(cn=xmpp.main))(!(cn=sftp.main)))', ['cn']): if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]): raise YunohostError('permission_deletion_failed', From 34df84e222e04a975e0315e8155f5045ba9b9f0c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 00:57:32 +0200 Subject: [PATCH 068/299] group add -> group create, to be consistent with user create/delete --- data/actionsmap/yunohost.yml | 4 ++-- locales/en.json | 2 +- .../data_migrations/0011_setup_group_permission.py | 4 ++-- src/yunohost/tests/test_user-group.py | 12 ++++++------ src/yunohost/user.py | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index cbb7756b0..8a6c10b5f 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -212,8 +212,8 @@ user: help: fields to fetch nargs: "+" - ### user_group_add() - add: + ### user_group_create() + create: action_help: Create group api: POST /users/groups arguments: diff --git a/locales/en.json b/locales/en.json index be00d5b1e..815c40b75 100644 --- a/locales/en.json +++ b/locales/en.json @@ -288,7 +288,7 @@ "log_regen_conf": "Regenerate system configurations '{}'", "log_user_create": "Add '{}' user", "log_user_delete": "Delete '{}' user", - "log_user_group_add": "Add '{}' group", + "log_user_group_create": "Create '{}' group", "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", "log_user_update": "Update information of '{}' user", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 17fd8f4a5..9699d2cd9 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -7,7 +7,7 @@ from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger from yunohost.tools import Migration -from yunohost.user import user_group_add, user_group_update +from yunohost.user import user_group_create, user_group_update from yunohost.app import app_setting, app_list from yunohost.regenconf import regen_conf from yunohost.permission import permission_add, permission_sync_to_user @@ -65,7 +65,7 @@ class MyMigration(Migration): username = user_info['uid'][0] ldap.update('uid=%s,ou=users' % username, {'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']}) - user_group_add(username, gid=user_info['uidNumber'][0], sync_perm=False) + user_group_create(username, gid=user_info['uidNumber'][0], sync_perm=False) user_group_update(groupname=username, add_user=username, force=True, sync_perm=False) user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=False) diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 3973f0c7d..1defce466 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -1,7 +1,7 @@ import pytest from moulinette.core import MoulinetteError -from yunohost.user import user_list, user_info, user_group_list, user_create, user_delete, user_update, user_group_add, user_group_delete, user_group_update, user_group_info +from yunohost.user import user_list, user_info, user_group_list, user_create, user_delete, user_update, user_group_create, user_group_delete, user_group_update, user_group_info from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError from yunohost.tests.test_permission import check_LDAP_db_integrity @@ -24,8 +24,8 @@ def setup_function(function): user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") user_create("jack", "Jack", "Black", "jack@" + maindomain, "test123Ynh") - user_group_add("dev") - user_group_add("apps") + user_group_create("dev") + user_group_create("apps") user_group_update("dev", add_user=["alice"]) user_group_update("apps", add_user=["bob"]) @@ -83,7 +83,7 @@ def test_del_user(): assert "alice" not in group_res['all_users']['members'] def test_add_group(): - user_group_add("adminsys") + user_group_create("adminsys") group_res = user_group_list()['groups'] assert "adminsys" in group_res @@ -122,12 +122,12 @@ def test_del_bad_user_1(): def test_add_bad_group_1(): # Check groups already exist with special group "all_users" with pytest.raises(YunohostError): - user_group_add("all_users") + user_group_create("all_users") def test_add_bad_group_2(): # Check groups already exist (for standard groups) with pytest.raises(MoulinetteError): - user_group_add("dev") + user_group_create("dev") def test_del_bad_group_1(): # Check not allowed to remove this groups diff --git a/src/yunohost/user.py b/src/yunohost/user.py index a6c262ed7..e5480ca92 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -218,7 +218,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, exc_info=1) # Create group for user and add to group 'all_users' - user_group_add(groupname=username, gid=uid, sync_perm=False) + user_group_create(groupname=username, gid=uid, sync_perm=False) user_group_update(groupname=username, add_user=username, force=True, sync_perm=False) user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=True) @@ -554,7 +554,7 @@ def user_group_list(fields=None): @is_unit_operation([('groupname', 'user')]) -def user_group_add(operation_logger, groupname, gid=None, sync_perm=True): +def user_group_create(operation_logger, groupname, gid=None, sync_perm=True): """ Create group From f60af2053f959d02fe958310ff9eaa40a9877be3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 01:06:20 +0200 Subject: [PATCH 069/299] permission_add/remove becomes create/delete to be consistent with user and group create/delete. In the context of permissions, add/remove shall instead be related to adding/removing an existing permission for a user or group. --- data/helpers.d/setting | 4 ++-- src/yunohost/app.py | 10 +++++----- src/yunohost/backup.py | 4 ++-- .../0011_setup_group_permission.py | 4 ++-- src/yunohost/permission.py | 4 ++-- src/yunohost/tests/test_permission.py | 20 +++++++++---------- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index da711b4bd..e002afa57 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -249,7 +249,7 @@ ynh_permission_create() { if [[ -n ${urls:-} ]]; then urls=",urls=['${urls//';'/"','"}']" fi - yunohost tools shell -c "from yunohost.permission import permission_add; permission_add('$app', '$permission' ${defaultdisallow:-} ${urls:-}, sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app', '$permission' ${defaultdisallow:-} ${urls:-}, sync_perm=False)" } # Remove a permission for the app (note that when the app is removed all permission is automatically removed) @@ -263,7 +263,7 @@ ynh_permission_remove() { local permission ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_remove; permission_remove('$app', '$permission', sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app', '$permission', sync_perm=False)" } # Add a path managed by the SSO diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 105d4faf7..c2fb87acf 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -738,7 +738,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import permission_add, permission_update, permission_remove, permission_sync_to_user + from yunohost.permission import permission_create, permission_update, permission_delete, permission_sync_to_user ldap = _get_ldap_interface() # Fetch or extract sources @@ -875,7 +875,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Create permission before the install (useful if the install script redefine the permission) # Note that sync_perm is disabled to avoid triggering a whole bunch of code and messages # can't be sure that we don't have one case when it's needed - permission_add(app=app_instance_name, permission="main", sync_perm=False) + permission_create(app=app_instance_name, permission="main", sync_perm=False) # Execute the app install script install_retcode = 1 @@ -914,7 +914,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) permission_list = [p['cn'][0] for p in result] for l in permission_list: - permission_remove(app_instance_name, l.split('.')[0], force=True) + permission_delete(app_instance_name, l.split('.')[0], force=True) if remove_retcode != 0: msg = m18n.n('app_not_properly_removed', @@ -980,7 +980,7 @@ def app_remove(operation_logger, app): """ from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_exec, hook_remove, hook_callback - from yunohost.permission import permission_remove, permission_sync_to_user + from yunohost.permission import permission_delete, permission_sync_to_user if not _is_installed(app): raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id()) @@ -1031,7 +1031,7 @@ def app_remove(operation_logger, app): filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app, attrs=['cn']) permission_list = [p['cn'][0] for p in result] for l in permission_list: - permission_remove(app, l.split('.')[0], force=True, sync_perm=False) + permission_delete(app, l.split('.')[0], force=True, sync_perm=False) permission_sync_to_user() diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 9a27031ae..9e90ece2a 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1303,7 +1303,7 @@ class RestoreManager(): """ from moulinette.utils.filesystem import read_ldif from yunohost.user import user_group_list - from yunohost.permission import permission_remove + from yunohost.permission import permission_delete from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -1441,7 +1441,7 @@ class RestoreManager(): filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) permission_list = [p['cn'][0] for p in result] for l in permission_list: - permission_remove(app_instance_name, l.split('.')[0], force=True) + permission_delete(app_instance_name, l.split('.')[0], force=True) # TODO Cleaning app hooks else: diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 9699d2cd9..d0bae2d95 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -10,7 +10,7 @@ from yunohost.tools import Migration from yunohost.user import user_group_create, user_group_update from yunohost.app import app_setting, app_list from yunohost.regenconf import regen_conf -from yunohost.permission import permission_add, permission_sync_to_user +from yunohost.permission import permission_create, permission_sync_to_user from yunohost.user import user_permission_add logger = getActionLogger('yunohost.migration') @@ -85,7 +85,7 @@ class MyMigration(Migration): domain = app_setting(app, 'domain') urls = [domain + path] if domain and path else None - permission_add(app, permission='main', urls=urls, default_allow=True, sync_perm=False) + permission_create(app, permission='main', urls=urls, default_allow=True, sync_perm=False) if permission: allowed_group = permission.split(',') user_permission_add([app], permission='main', group=allowed_group, sync_perm=False) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index f7fa307da..3f9131018 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -317,7 +317,7 @@ def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=T @is_unit_operation(['permission', 'app']) -def permission_add(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True): +def permission_create(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True): """ Create a new permission for a specific application @@ -431,7 +431,7 @@ def permission_update(operation_logger, app, permission, add_url=None, remove_ur @is_unit_operation(['permission', 'app']) -def permission_remove(operation_logger, app, permission, force=False, sync_perm=True): +def permission_delete(operation_logger, app, permission, force=False, sync_perm=True): """ Remove a permission for a specific application diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 3b9815f63..8222248b1 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -3,7 +3,7 @@ import pytest from moulinette.core import MoulinetteError from yunohost.app import app_install, app_remove, app_change_url, app_list from yunohost.user import user_list, user_create, user_permission_list, user_delete, user_group_list, user_group_delete, user_permission_add, user_permission_remove, user_permission_clear -from yunohost.permission import permission_add, permission_update, permission_remove +from yunohost.permission import permission_create, permission_update, permission_delete from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError @@ -21,15 +21,15 @@ def clean_user_groups_permission(): for a, per in user_permission_list()['permissions'].items(): if a in ['wiki', 'blog', 'site']: for p in per: - permission_remove(a, p, force=True, sync_perm=False) + permission_delete(a, p, force=True, sync_perm=False) def setup_function(function): clean_user_groups_permission() user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") - permission_add("wiki", "main", [maindomain + "/wiki"], sync_perm=False) - permission_add("blog", "main", sync_perm=False) + permission_create("wiki", "main", [maindomain + "/wiki"], sync_perm=False) + permission_create("blog", "main", sync_perm=False) user_permission_add(["blog"], "main", group="alice") @@ -177,7 +177,7 @@ def test_list_permission(): # def test_add_permission_1(): - permission_add("site", "test") + permission_create("site", "test") res = user_permission_list()['permissions'] assert "site" in res @@ -186,7 +186,7 @@ def test_add_permission_1(): assert set(["alice", "bob"]) == set(res['site']['test']['allowed_users']) def test_add_permission_2(): - permission_add("site", "main", default_allow=False) + permission_create("site", "main", default_allow=False) res = user_permission_list()['permissions'] assert "site" in res @@ -195,7 +195,7 @@ def test_add_permission_2(): assert [] == res['site']['main']['allowed_users'] def test_remove_permission(): - permission_remove("wiki", "main", force=True) + permission_delete("wiki", "main", force=True) res = user_permission_list()['permissions'] assert "wiki" not in res @@ -207,12 +207,12 @@ def test_remove_permission(): def test_add_bad_permission(): # Create permission with same name with pytest.raises(YunohostError): - permission_add("wiki", "main") + permission_create("wiki", "main") def test_remove_bad_permission(): # Remove not existant permission with pytest.raises(MoulinetteError): - permission_remove("non_exit", "main", force=True) + permission_delete("non_exit", "main", force=True) res = user_permission_list()['permissions'] assert "wiki" in res @@ -226,7 +226,7 @@ def test_remove_bad_permission(): def test_remove_main_permission(): with pytest.raises(YunohostError): - permission_remove("blog", "main") + permission_delete("blog", "main") res = user_permission_list()['permissions'] assert "mail" in res From a6d68c76c4eaa998db3fa77e7ece0c811e85e8ad Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 01:31:22 +0200 Subject: [PATCH 070/299] permission_update -> permission_urls (+ tweak the helper name) so that it's more differentiable from user_permission_update --- data/helpers.d/setting | 10 +++++----- src/yunohost/permission.py | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index e002afa57..e6311fc1f 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -268,18 +268,18 @@ ynh_permission_remove() { # Add a path managed by the SSO # -# usage: ynh_permission_add_path --app "app" --permission "permission" --url "url" ["url" ...] +# usage: ynh_permission_add_url --app "app" --permission "permission" --url "url" ["url" ...] # | arg: app - the application id # | arg: permission - the name for the permission # | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin) -ynh_permission_add_path() { +ynh_permission_add_url() { declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= ) local app local permission local url ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)" } # Remove a path managed by the SSO @@ -288,12 +288,12 @@ ynh_permission_add_path() { # | arg: app - the application id # | arg: permission - the name for the permission # | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin) -ynh_permission_del_path() { +ynh_permission_remove_url() { declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= ) local app local permission local url ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_update; permission_update('$app', '$permission', remove_url=['${url//';'/"','"}'], sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app', '$permission', remove_url=['${url//';'/"','"}'], sync_perm=False)" } diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 3f9131018..1a5465674 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -35,6 +35,12 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') +# +# +# The followings are the methods exposed through the "yunohost user permission" interface +# +# + def user_permission_list(app=None, permission=None, username=None, group=None): """ @@ -315,6 +321,14 @@ def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=T return user_permission_list(app, permission) +# +# +# The followings methods are *not* directly exposed. +# They are used to create/delete the permissions (e.g. during app install/remove) +# and by some app helpers to possibly add additional permissions and tweak the urls +# +# + @is_unit_operation(['permission', 'app']) def permission_create(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True): @@ -374,9 +388,9 @@ def permission_create(operation_logger, app, permission, urls=None, default_allo @is_unit_operation(['permission', 'app']) -def permission_update(operation_logger, app, permission, add_url=None, remove_url=None, sync_perm=True): +def permission_urls(operation_logger, app, permission, add_url=None, remove_url=None, sync_perm=True): """ - Update a permission for a specific application + Update urls related to a permission for a specific application Keyword argument: app -- an application OR sftp, xmpp (metronome), mail From 0f7b8c3515e80afe648d55445517db60d848f609 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 02:08:46 +0200 Subject: [PATCH 071/299] Simplify group list interface and code --- data/actionsmap/yunohost.yml | 11 ++++-- src/yunohost/backup.py | 2 +- src/yunohost/permission.py | 12 +++--- src/yunohost/user.py | 76 +++++++++++++----------------------- src/yunohost/utils/ldap.py | 14 +++++++ 5 files changed, 55 insertions(+), 60 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 8a6c10b5f..b463df646 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -208,9 +208,14 @@ user: action_help: List group api: GET /users/groups arguments: - --fields: - help: fields to fetch - nargs: "+" + -n: + full: --names-only + help: Only list the name of the groups without any additional info + action: store_true + -f: + full: --full + help: List all the info available for each groups + action: store_true ### user_group_create() create: diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 9e90ece2a..f96146ea0 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1374,7 +1374,7 @@ class RestoreManager(): filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass', 'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid'] entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries) - group_list = user_group_list(['cn'])['groups'] + group_list = user_group_list()['groups'] for dn, entry in entries: # Remove the group which has been removed for group in entry['groupPermission']: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 1a5465674..0b77a3e5c 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -169,13 +169,13 @@ def user_permission_update(operation_logger, app=[], permission=None, add_userna # Validate that the group exist for g in add_group: - if g not in user_group_list(['cn'])['groups']: + if g not in user_group_list()['groups']: raise YunohostError('group_unknown', group=g) for u in add_username: if u not in user_list(['uid'])['users']: raise YunohostError('user_unknown', user=u) for g in del_group: - if g not in user_group_list(['cn'])['groups']: + if g not in user_group_list()['groups']: raise YunohostError('group_unknown', group=g) for u in del_username: if u not in user_list(['uid'])['users']: @@ -244,14 +244,12 @@ def user_permission_update(operation_logger, app=[], permission=None, add_userna for a in app: allowed_users = set() disallowed_users = set() - group_list = user_group_list(['member'])['groups'] + group_list = user_group_list()['groups'] for g in add_group: - if 'members' in group_list[g]: - allowed_users.union(group_list[g]['members']) + allowed_users.union(group_list[g]['members']) for g in del_group: - if 'members' in group_list[g]: - disallowed_users.union(group_list[g]['members']) + disallowed_users.union(group_list[g]['members']) allowed_users = ','.join(allowed_users) disallowed_users = ','.join(disallowed_users) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index e5480ca92..787058fbc 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -489,66 +489,44 @@ def user_info(username): # # Group subcategory # -def user_group_list(fields=None): +def user_group_list(names_only=False, full=False): """ List users Keyword argument: - filter -- LDAP filter used to search - offset -- Starting number for user fetching - limit -- Maximum number of user fetched - fields -- fields to fetch - + names-only -- Only list the name of the groups without any additional info + full -- List all the info available for each groups """ - from yunohost.utils.ldap import _get_ldap_interface + + # Fetch relevant informations + + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() - group_attr = { - 'cn': 'groupname', - 'member': 'members', - 'permission': 'permission' - } - attrs = ['cn'] - groups = {} - if fields: - keys = group_attr.keys() - for attr in fields: - if attr in keys: - attrs.append(attr) - else: - raise YunohostError('field_invalid', attr) + if names_only: + fields_to_fetch = ["cn"] + elif full: + fields_to_fetch = ["cn", "member", "permission"] else: - attrs = ['cn', 'member'] + fields_to_fetch = ["cn", "member"] - result = ldap.search('ou=groups,dc=yunohost,dc=org', - '(objectclass=groupOfNamesYnh)', - attrs) + groups_infos = ldap.search('ou=groups,dc=yunohost,dc=org', + '(objectclass=groupOfNamesYnh)', + fields_to_fetch) - for group in result: - # The group "admins" should be hidden for the user - if group_attr['cn'] == "admins": - continue - entry = {} - for attr, values in group.items(): - if values: - if attr == "member": - entry[group_attr[attr]] = [] - for v in values: - entry[group_attr[attr]].append(v.split("=")[1].split(",")[0]) - elif attr == "permission": - entry[group_attr[attr]] = {} - for v in values: - permission = v.split("=")[1].split(",")[0].split(".")[1] - pType = v.split("=")[1].split(",")[0].split(".")[0] - if permission in entry[group_attr[attr]]: - entry[group_attr[attr]][permission].append(pType) - else: - entry[group_attr[attr]][permission] = [pType] - else: - entry[group_attr[attr]] = values[0] + # Parse / organize information to be outputed - groupname = entry[group_attr['cn']] - groups[groupname] = entry + groups = {} + for infos in groups_infos: + name = infos["cn"][0] + groups[name] = {} + if "member" in fields_to_fetch: + groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])] + if "permission" in fields_to_fetch: + groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] + + if names_only: + groups = groups.keys() return {'groups': groups} diff --git a/src/yunohost/utils/ldap.py b/src/yunohost/utils/ldap.py index 186cdbdec..c3b5065a1 100644 --- a/src/yunohost/utils/ldap.py +++ b/src/yunohost/utils/ldap.py @@ -40,6 +40,20 @@ def _get_ldap_interface(): return _ldap_interface + +# We regularly want to extract stuff like 'bar' in ldap path like +# foo=bar,dn=users.example.org,ou=example.org,dc=org so this small helper allow +# to do this without relying of dozens of mysterious string.split()[0] +# +# e.g. using _ldap_path_extract(path, "foo") on the previous example will +# return bar + +def _ldap_path_extract(path, info): + for element in path.split(","): + if element.startswith(info + "="): + return element[len(info + "="):] + + # Add this to properly close / delete the ldap interface / authenticator # when Python exits ... # Otherwise there's a risk that some funky error appears at the very end From c5d0a270980188a0a2d04f8f6aece63a727d4206 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 03:00:29 +0200 Subject: [PATCH 072/299] Simplify group info and group update interface and code --- data/actionsmap/yunohost.yml | 8 +- .../0011_setup_group_permission.py | 4 +- src/yunohost/tests/test_user-group.py | 20 ++-- src/yunohost/user.py | 101 ++++++++---------- 4 files changed, 58 insertions(+), 75 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index b463df646..e727b87ef 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -249,15 +249,15 @@ user: extra: pattern: *pattern_groupname -a: - full: --add-user - help: User to add in group + full: --add + help: User(s) to add in the group nargs: "*" metavar: USERNAME extra: pattern: *pattern_username -r: - full: --remove-user - help: User to remove in group + full: --remove + help: User(s) to remove in the group nargs: "*" metavar: USERNAME extra: diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index d0bae2d95..d2924f0af 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -66,8 +66,8 @@ class MyMigration(Migration): ldap.update('uid=%s,ou=users' % username, {'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']}) user_group_create(username, gid=user_info['uidNumber'][0], sync_perm=False) - user_group_update(groupname=username, add_user=username, force=True, sync_perm=False) - user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=False) + user_group_update(groupname=username, add=username, force=True, sync_perm=False) + user_group_update(groupname='all_users', add=username, force=True, sync_perm=False) def migrate_app_permission(self, app=None): diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 1defce466..34e515ea0 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -26,8 +26,8 @@ def setup_function(function): user_group_create("dev") user_group_create("apps") - user_group_update("dev", add_user=["alice"]) - user_group_update("apps", add_user=["bob"]) + user_group_update("dev", add=["alice"]) + user_group_update("apps", add=["bob"]) def teardown_function(function): clean_user_groups() @@ -151,28 +151,28 @@ def test_update_user_1(): assert "NewLast" == info['lastname'] def test_update_group_1(): - user_group_update("dev", add_user=["bob"]) + user_group_update("dev", add=["bob"]) group_res = user_group_list()['groups'] assert set(["alice", "bob"]) == set(group_res['dev']['members']) def test_update_group_2(): # Try to add a user in a group when the user is already in - user_group_update("apps", add_user=["bob"]) + user_group_update("apps", add=["bob"]) group_res = user_group_list()['groups'] assert ["bob"] == group_res['apps']['members'] def test_update_group_3(): # Try to remove a user in a group - user_group_update("apps", remove_user=["bob"]) + user_group_update("apps", remove=["bob"]) group_res = user_group_list()['groups'] assert "members" not in group_res['apps'] def test_update_group_4(): # Try to remove a user in a group when it is not already in - user_group_update("apps", remove_user=["jack"]) + user_group_update("apps", remove=["jack"]) group_res = user_group_list()['groups'] assert ["bob"] == group_res['apps']['members'] @@ -190,21 +190,21 @@ def test_bad_update_user_1(): def bad_update_group_1(): # Check groups not found with pytest.raises(YunohostError): - user_group_update("not_exit", add_user=["alice"]) + user_group_update("not_exit", add=["alice"]) def test_bad_update_group_2(): # Check remove user in groups "all_users" not allowed with pytest.raises(YunohostError): - user_group_update("all_users", remove_user=["alice"]) + user_group_update("all_users", remove=["alice"]) def test_bad_update_group_3(): # Check remove user in it own group not allowed with pytest.raises(YunohostError): - user_group_update("alice", remove_user=["alice"]) + user_group_update("alice", remove=["alice"]) def test_bad_update_group_1(): # Check add bad user in group with pytest.raises(YunohostError): - user_group_update("dev", add_user=["not_exist"]) + user_group_update("dev", add=["not_exist"]) assert "not_exist" not in user_group_list()["groups"]["dev"] diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 787058fbc..a1a671ccf 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -32,6 +32,7 @@ import crypt import random import string import subprocess +import copy from moulinette import m18n from yunohost.utils.error import YunohostError @@ -219,8 +220,8 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, # Create group for user and add to group 'all_users' user_group_create(groupname=username, gid=uid, sync_perm=False) - user_group_update(groupname=username, add_user=username, force=True, sync_perm=False) - user_group_update(groupname='all_users', add_user=username, force=True, sync_perm=True) + user_group_update(groupname=username, add=username, force=True, sync_perm=False) + user_group_update(groupname='all_users', add=username, force=True, sync_perm=True) # TODO: Send a welcome mail to user logger.success(m18n.n('user_created')) @@ -610,84 +611,67 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): @is_unit_operation([('groupname', 'user')]) -def user_group_update(operation_logger, groupname, add_user=None, remove_user=None, force=False, sync_perm=True): +def user_group_update(operation_logger, groupname, add=None, remove=None, force=False, sync_perm=True): """ Update user informations Keyword argument: groupname -- Groupname to update - add_user -- User to add in group - remove_user -- User to remove in group + add -- User(s) to add in group + remove -- User(s) to remove in group """ from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface + # FIXME : we should also refuse to edit the main group of a user (e.g. group 'sam' related to user 'sam') + if (groupname == 'all_users' or groupname == 'admins') and not force: raise YunohostError('edit_group_not_allowed', group=groupname) ldap = _get_ldap_interface() - # Populate group informations - attrs_to_fetch = ['member'] - result = ldap.search(base='ou=groups,dc=yunohost,dc=org', - filter='cn=' + groupname, attrs=attrs_to_fetch) - if not result: - raise YunohostError('group_unknown', group=groupname) - group = result[0] + # We extract the uid for each member of the group to keep a simple flat list of members + current_group = user_group_info(groupname)["members"] + new_group = copy.copy(current_group) - new_group_list = {'member': set(), 'memberUid': set()} - if 'member' in group: - new_group_list['member'] = set(group['member']) - else: - group['member'] = [] + existing_users = user_list()['users'].keys() - existing_users = user_list(fields=['uid'])['users'].keys() + if add: + users_to_add = [add] if not isinstance(add, list) else add - if add_user: - if not isinstance(add_user, list): - add_user = [add_user] - - for user in add_user: + for user in users_to_add: if user not in existing_users: raise YunohostError('user_unknown', user=user) - for user in add_user: - userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org" - if userDN in group['member']: + if user in current_group: logger.warning(m18n.n('user_already_in_group', user=user, group=groupname)) - new_group_list['member'].add(userDN) - if remove_user: - if not isinstance(remove_user, list): - remove_user = [remove_user] + new_group += users_to_add - for user in remove_user: + if remove: + users_to_remove = [remove] if not isinstance(remove, list) else remove + + for user in users_to_remove: if user == groupname: + # FIXME : well if the user equals the group, why pass the two info... + # anyway we should just forbid this from the very beginning ... (editing a user-related group) raise YunohostError('remove_user_of_group_not_allowed', user=user, group=groupname) - for user in remove_user: - userDN = "uid=" + user + ",ou=users,dc=yunohost,dc=org" - if 'member' in group and userDN in group['member']: - new_group_list['member'].remove(userDN) - else: + if user not in current_group: logger.warning(m18n.n('user_not_in_group', user=user, group=groupname)) - # Sychronise memberUid with member (to keep the posix group structure) - # In posixgroup the main group of each user is only written in the gid number of the user - for member in new_group_list['member']: - member_Uid = member.split("=")[1].split(",")[0] - # Don't add main user in the group. - # Note that in the Unix system the main user of the group is linked by the gid in the user attribute. - # So the main user need to be not in the memberUid list of his own group. - if member_Uid != groupname: - new_group_list['memberUid'].add(member_Uid) + # Remove users_to_remove from new_group + # Kinda like a new_group -= users_to_remove + new_group = [u for u in new_group if u not in users_to_remove] + + new_group_dns = ["uid=" + user + ",ou=users,dc=yunohost,dc=org" for user in new_group] operation_logger.start() - if new_group_list['member'] != set(group['member']): - if not ldap.update('cn=%s,ou=groups' % groupname, new_group_list): + if set(new_group) != set(current_group): + if not ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)}): raise YunohostError('group_update_failed', group=groupname) logger.success(m18n.n('group_updated', group=groupname)) @@ -705,26 +689,25 @@ def user_group_info(groupname): """ - from yunohost.utils.ldap import _get_ldap_interface + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() - group_attrs = [ - 'cn', 'member', 'permission' - ] - result = ldap.search('ou=groups,dc=yunohost,dc=org', "cn=" + groupname, group_attrs) + # Fetch info for this group + result = ldap.search('ou=groups,dc=yunohost,dc=org', + "cn=" + groupname, + ["cn", "member", "permission"]) if not result: raise YunohostError('group_unknown', group=groupname) - group = result[0] + infos = result[0] - result_dict = { - 'groupname': group['cn'][0], - 'member': None + # Format data + + return { + 'members': [_ldap_path_extract(p, "uid") for p in infos.get("member", [])], + 'permissions': [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] } - if 'member' in group: - result_dict['member'] = {m.split("=")[1].split(",")[0] for m in group['member']} - return result_dict # From 97c637f44c4b3d1d2625c1c34bdeece51ace0ea4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 03:00:59 +0200 Subject: [PATCH 073/299] Fix group command descriptions in the actionmap --- data/actionsmap/yunohost.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e727b87ef..d4940c043 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -201,11 +201,11 @@ user: subcategories: group: - subcategory_help: Manage group + subcategory_help: Manage user groups actions: ### user_group_list() list: - action_help: List group + action_help: List existing groups api: GET /users/groups arguments: -n: @@ -223,7 +223,7 @@ user: api: POST /users/groups arguments: groupname: - help: The unique group name to add + help: Name of the group to be created extra: pattern: &pattern_groupname - !!str ^[a-z0-9_]+$ @@ -235,7 +235,7 @@ user: api: DELETE /users/groups/ arguments: groupname: - help: Username to delete + help: Name of the group to be deleted extra: pattern: *pattern_groupname @@ -245,7 +245,7 @@ user: api: PUT /users/groups/ arguments: groupname: - help: Username to update + help: Name of the group to be updated extra: pattern: *pattern_groupname -a: @@ -265,11 +265,11 @@ user: ### user_group_info() info: - action_help: Get group information + action_help: Get information for a specific group api: GET /users/groups/ arguments: groupname: - help: Groupname to get information + help: Name of the group to get info about extra: pattern: *pattern_username From 112976f8ee17e1ebb0c74edb2fbe5c9dc6441fa3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 03:30:51 +0200 Subject: [PATCH 074/299] Refuse to edit user primary groups --- locales/en.json | 5 ++--- src/yunohost/user.py | 43 ++++++++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/locales/en.json b/locales/en.json index 815c40b75..d3abf4fd0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -196,7 +196,6 @@ "dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.", "dyndns_unavailable": "Domain {domain:s} is not available.", - "edit_group_not_allowed": "You are not allowed to edit the group {group:s}", "edit_permission_with_group_all_users_not_allowed": "You are not allowed to edit permission for group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.", "error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", "executing_command": "Executing command '{command:s}'…", @@ -235,9 +234,10 @@ "group_name_already_exist": "Group {name:s} already exist", "group_created": "Group '{group}' successfully created", "group_creation_failed": "Group creation failed for group '{group}'", + "group_cannot_be_edited": "The group {group:s} cannot be edited manually.", + "group_cannot_be_deleted": "The group {group:s} cannot be deleted manually.", "group_deleted": "Group '{group}' deleted", "group_deletion_failed": "Group '{group} 'deletion failed", - "group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.", "group_info_failed": "Group info failed", "group_unknown": "Group {group:s} unknown", "group_updated": "Group '{group}' updated", @@ -449,7 +449,6 @@ "port_unavailable": "Port {port:d} is not available", "recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create ' or the admin interface.", "remove_main_permission_not_allowed": "Removing the main permission is not allowed", - "remove_user_of_group_not_allowed": "You are not allowed to remove the user {user:s} in the group {group:s}", "regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'", "regenconf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'", "regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but has been kept back.", diff --git a/src/yunohost/user.py b/src/yunohost/user.py index a1a671ccf..8427cbd42 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -219,8 +219,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, exc_info=1) # Create group for user and add to group 'all_users' - user_group_create(groupname=username, gid=uid, sync_perm=False) - user_group_update(groupname=username, add=username, force=True, sync_perm=False) + user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False) user_group_update(groupname='all_users', add=username, force=True, sync_perm=True) # TODO: Send a welcome mail to user @@ -533,7 +532,7 @@ def user_group_list(names_only=False, full=False): @is_unit_operation([('groupname', 'user')]) -def user_group_create(operation_logger, groupname, gid=None, sync_perm=True): +def user_group_create(operation_logger, groupname, gid=None, primary_group=False, sync_perm=True): """ Create group @@ -575,6 +574,12 @@ def user_group_create(operation_logger, groupname, gid=None, sync_perm=True): 'gidNumber': gid, } + # Here we handle the creation of a primary group + # We want to initialize this group to contain the corresponding user + # (then we won't be able to add/remove any user in this group) + if primary_group: + attr_dict["member"] = ["uid=" + groupname + ",ou=users,dc=yunohost,dc=org"] + if ldap.add('cn=%s,ou=groups' % groupname, attr_dict): logger.success(m18n.n('group_created', group=groupname)) if sync_perm: @@ -596,9 +601,14 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface - forbidden_groups = ["all_users", "admins"] + user_list(fields=['uid'])['users'].keys() - if not force and groupname in forbidden_groups: - raise YunohostError('group_deletion_not_allowed', group=groupname) + # Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam') + # without the force option... + # + # We also can't delete "all_users" because that's a special group... + existing_users = user_list()['users'].keys() + undeletable_groups = existing_users + ["all_users", "admins"] + if groupname in undeletable_groups and not force: + raise YunohostError('group_cannot_be_deleted', group=groupname) operation_logger.start() ldap = _get_ldap_interface() @@ -625,19 +635,18 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface - # FIXME : we should also refuse to edit the main group of a user (e.g. group 'sam' related to user 'sam') - - if (groupname == 'all_users' or groupname == 'admins') and not force: - raise YunohostError('edit_group_not_allowed', group=groupname) - - ldap = _get_ldap_interface() + # Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam') + # Those kind of group should only ever contain the user (e.g. sam) and only this one. + # We also can't edit "all_users" without the force option because that's a special group... + existing_users = user_list()['users'].keys() + uneditable_groups = existing_users + ["all_users", "admins"] + if groupname in uneditable_groups and not force: + raise YunohostError('group_cannot_be_edited', group=groupname) # We extract the uid for each member of the group to keep a simple flat list of members current_group = user_group_info(groupname)["members"] new_group = copy.copy(current_group) - existing_users = user_list()['users'].keys() - if add: users_to_add = [add] if not isinstance(add, list) else add @@ -654,11 +663,6 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= users_to_remove = [remove] if not isinstance(remove, list) else remove for user in users_to_remove: - if user == groupname: - # FIXME : well if the user equals the group, why pass the two info... - # anyway we should just forbid this from the very beginning ... (editing a user-related group) - raise YunohostError('remove_user_of_group_not_allowed', user=user, group=groupname) - if user not in current_group: logger.warning(m18n.n('user_not_in_group', user=user, group=groupname)) @@ -671,6 +675,7 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= operation_logger.start() if set(new_group) != set(current_group): + ldap = _get_ldap_interface() if not ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)}): raise YunohostError('group_update_failed', group=groupname) From 6276485665977aae1e6407714988ca354fad5779 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 04:06:12 +0200 Subject: [PATCH 075/299] Simplify permission_list ... it really sounds like we don't need all these options --- data/actionsmap/yunohost.yml | 27 ++---------- locales/en.json | 1 - src/yunohost/backup.py | 6 +-- src/yunohost/permission.py | 79 +++++++----------------------------- src/yunohost/user.py | 6 +-- 5 files changed, 23 insertions(+), 96 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index d4940c043..2bca684cd 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -274,33 +274,12 @@ user: pattern: *pattern_username permission: - subcategory_help: Manage user permission + subcategory_help: Manage permissions actions: ### user_permission_list() list: - action_help: List access to user and group - api: GET /users/permissions/ - arguments: - -a: - full: --app - help: Application to manage the permission - nargs: "*" - metavar: APP - -p: - full: --permission - help: Name of permission (main by default) - nargs: "*" - metavar: PERMISSION - -u: - full: --username - help: Username - nargs: "*" - metavar: USER - -g: - full: --group - help: Group name - nargs: "*" - metavar: GROUP + action_help: List permissions and corresponding accesses + api: GET /users/permissions/ ### user_permission_add() add: diff --git a/locales/en.json b/locales/en.json index d3abf4fd0..b02bf2238 100644 --- a/locales/en.json +++ b/locales/en.json @@ -438,7 +438,6 @@ "permission_deleted": "Permission '{permission:s}' for app {app:s} deleted", "permission_deletion_failed": "Permission '{permission:s}' for app {app:s} deletion failed", "permission_not_found": "Permission '{permission:s}' not found for application {app:s}", - "permission_name_not_valid": "Permission name '{permission:s}' not valid", "permission_update_failed": "Permission update failed", "permission_generated": "The permission database has been updated", "permission_updated": "Permission '{permission:s}' for app {app:s} updated", diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index f96146ea0..fdbd8c62c 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1256,10 +1256,8 @@ class RestoreManager(): # Restore permission for the app which is installed for per in old_apps_permission: - try: - permission_name, app_name = per['cn'][0].split('.') - except: - logger.warning(m18n.n('permission_name_not_valid', permission=per['cn'][0])) + # FIXME : will come here later to fix this following previous commits ... + permission_name, app_name = per['cn'][0].split('.') if _is_installed(app_name): if not ldap.add('cn=%s,ou=permission' % per['cn'][0], per): raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 0b77a3e5c..fbb43e8b3 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -42,79 +42,30 @@ logger = getActionLogger('yunohost.user') # -def user_permission_list(app=None, permission=None, username=None, group=None): +def user_permission_list(): """ - List permission for specific application - - Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) - username -- Username to get informations - group -- Groupname to get informations + List permissions and corresponding accesses """ - from yunohost.utils.ldap import _get_ldap_interface + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract + + # Fetch all permissions objects ldap = _get_ldap_interface() - - permission_attrs = [ - 'cn', - 'groupPermission', - 'inheritPermission', - 'URL', - ] - - # Normally app is alway defined but it should be possible to set it - if app and not isinstance(app, list): - app = [app] - if permission and not isinstance(permission, list): - permission = [permission] - if not isinstance(username, list): - username = [username] - if not isinstance(group, list): - group = [group] + permissions_infos = ldap.search('ou=permission,dc=yunohost,dc=org', + '(objectclass=permissionYnh)', + ['cn', 'groupPermission', 'inheritPermission', 'URL']) permissions = {} + for infos in permissions_infos: - result = ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', permission_attrs) + name = infos['cn'][0] - for res in result: - try: - permission_name, app_name = res['cn'][0].split('.') - except: - logger.warning(m18n.n('permission_name_not_valid', permission=res['cn'][0])) - group_name = [] - if 'groupPermission' in res: - for g in res['groupPermission']: - group_name.append(g.split("=")[1].split(",")[0]) - user_name = [] - if 'inheritPermission' in res: - for u in res['inheritPermission']: - user_name.append(u.split("=")[1].split(",")[0]) - - # Don't show the result if the user defined a specific permission, user or group - if app and app_name not in app: - continue - if permission and permission_name not in permission: - continue - if username[0] and not set(username) & set(user_name): - continue - if group[0] and not set(group) & set(group_name): - continue - - if app_name not in permissions: - permissions[app_name] = {} - - permissions[app_name][permission_name] = {'allowed_users': [], 'allowed_groups': []} - for g in group_name: - permissions[app_name][permission_name]['allowed_groups'].append(g) - for u in user_name: - permissions[app_name][permission_name]['allowed_users'].append(u) - if 'URL' in res: - permissions[app_name][permission_name]['URL'] = [] - for u in res['URL']: - permissions[app_name][permission_name]['URL'].append(u) + permissions[name] = { + "allowed_users": [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])], + "allowed_groups": [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])], + "urls": infos.get("URL", []) + } return {'permissions': permissions} diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 8427cbd42..3eb329f4e 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -453,7 +453,7 @@ def user_info(username): if service_status("dovecot")["status"] != "running": logger.warning(m18n.n('mailbox_used_space_dovecot_down')) - elif not user_permission_list(app="mail", permission="main", username=username)['permissions']: + elif username not in user_permission_list()["permissions"]["mail.main"]["allowed_users"]: logger.warning(m18n.n('mailbox_disabled', user=username)) else: cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] @@ -719,9 +719,9 @@ def user_group_info(groupname): # Permission subcategory # -def user_permission_list(app=None, permission=None, username=None, group=None, sync_perm=True): +def user_permission_list(): import yunohost.permission - return yunohost.permission.user_permission_list(app, permission, username, group) + return yunohost.permission.user_permission_list() @is_unit_operation([('app', 'user')]) From 41e6f1b81cdf032949f960fb7095a697d4af4256 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 14:37:15 +0200 Subject: [PATCH 076/299] Simplify permission_add/remove to just permission_update with --add and --remove, similar to what's done for groups --- data/actionsmap/yunohost.yml | 63 +++--------- locales/en.json | 6 +- src/yunohost/permission.py | 180 ++++++++++++----------------------- src/yunohost/user.py | 18 +--- 4 files changed, 84 insertions(+), 183 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 2bca684cd..ebdd2b982 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -276,64 +276,31 @@ user: permission: subcategory_help: Manage permissions actions: + ### user_permission_list() list: action_help: List permissions and corresponding accesses api: GET /users/permissions/ - ### user_permission_add() - add: - action_help: Grant access right to users and group - api: POST /users/permissions/ + ### user_permission_update() + update: + action_help: Grant / remove permissions to groups or users + api: POST /users/permissions/ arguments: - app: - help: Application to manage the permission - nargs: "+" - -p: - full: --permission - help: Name of permission (main by default) + permission: + help: Permission to manage (e.g. mail.main or wordpress.editors) + -a: + full: --add + help: Group or user names to add to this permission nargs: "*" - metavar: PERMISSION - -u: - full: --username - help: Username - nargs: "*" - metavar: USER + metavar: GROUP_OR_USER extra: pattern: *pattern_username - -g: - full: --group - help: Group name + -r: + full: --remove + help: Group or user names to remove from this permission nargs: "*" - metavar: GROUP - extra: - pattern: *pattern_username - - ### user_permission_remove() - remove: - action_help: Revoke access right to users and group - api: PUT /users/permissions/ - arguments: - app: - help: Application to manage the permission - nargs: "+" - -p: - full: --permission - help: Name of permission (main by default) - nargs: "*" - metavar: PERMISSION - -u: - full: --username - help: Username - nargs: "*" - metavar: USER - extra: - pattern: *pattern_username - -g: - full: --group - help: Group name - nargs: "*" - metavar: GROUP + metavar: GROUP_OR_USER extra: pattern: *pattern_username diff --git a/locales/en.json b/locales/en.json index b02bf2238..725bb1f8c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -196,7 +196,6 @@ "dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.", "dyndns_unavailable": "Domain {domain:s} is not available.", - "edit_permission_with_group_all_users_not_allowed": "You are not allowed to edit permission for group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.", "error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", "executing_command": "Executing command '{command:s}'…", "executing_script": "Executing script '{script:s}'…", @@ -229,8 +228,8 @@ "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", - "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' enabled for app '{app:s}'", - "group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' disabled for app '{app:s}'", + "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' enabled'", + "group_already_disallowed": "Group '{group:s}' already has permission '{permission:s}' disabled'", "group_name_already_exist": "Group {name:s} already exist", "group_created": "Group '{group}' successfully created", "group_creation_failed": "Group creation failed for group '{group}'", @@ -397,7 +396,6 @@ "mysql_db_creation_failed": "MySQL database creation failed", "mysql_db_init_failed": "MySQL database init failed", "mysql_db_initialized": "The MySQL database has been initialized", - "need_define_permission_before": "You need to redefine the permission using 'yunohost user permission add -u USER' before removing an allowed group", "network_check_mx_ko": "DNS MX record is not set", "network_check_smtp_ko": "Outbound mail (SMTP port 25) seems to be blocked by your network", "network_check_smtp_ok": "Outbound mail (SMTP port 25) is not blocked", diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index fbb43e8b3..20c34ada8 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -24,6 +24,7 @@ Manage permissions """ +import copy import grp import random @@ -45,7 +46,6 @@ logger = getActionLogger('yunohost.user') def user_permission_list(): """ List permissions and corresponding accesses - """ from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract @@ -70,146 +70,92 @@ def user_permission_list(): return {'permissions': permissions} -def user_permission_update(operation_logger, app=[], permission=None, add_username=None, add_group=None, del_username=None, del_group=None, sync_perm=True): +def user_permission_update(operation_logger, permission, add=None, remove=None, sync_perm=True): """ Allow or Disallow a user or group to a permission for a specific application Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) - add_username -- Username to allow - add_group -- Groupname to allow - del_username -- Username to disallow - del_group -- Groupname to disallow - + permission -- Name of the permission (e.g. mail.mail or wordpress.editors) + add -- List of groups or usernames to add to this permission + remove -- List of groups or usernames to remove from to this permission """ from yunohost.hook import hook_callback from yunohost.user import user_group_list - from yunohost.utils.ldap import _get_ldap_interface + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() - if permission: - if not isinstance(permission, list): - permission = [permission] - else: - permission = ["main"] + # Fetch currently allowed groups for this permission - if add_group: - if not isinstance(add_group, list): - add_group = [add_group] - else: - add_group = [] - - if add_username: - if not isinstance(add_username, list): - add_username = [add_username] - else: - add_username = [] - - if del_group: - if not isinstance(del_group, list): - del_group = [del_group] - else: - del_group = [] - - if del_username: - if not isinstance(del_username, list): - del_username = [del_username] - else: - del_username = [] - - # Validate that the group exist - for g in add_group: - if g not in user_group_list()['groups']: - raise YunohostError('group_unknown', group=g) - for u in add_username: - if u not in user_list(['uid'])['users']: - raise YunohostError('user_unknown', user=u) - for g in del_group: - if g not in user_group_list()['groups']: - raise YunohostError('group_unknown', group=g) - for u in del_username: - if u not in user_list(['uid'])['users']: - raise YunohostError('user_unknown', user=u) - - # Merge user and group (note that we consider all user as a group) - add_group.extend(add_username) - del_group.extend(del_username) - - if 'all_users' in add_group or 'all_users' in del_group: - raise YunohostError('edit_permission_with_group_all_users_not_allowed') - - # Populate permission informations - permission_attrs = [ - 'cn', - 'groupPermission', - ] result = ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', permission_attrs) + '(objectclass=permissionYnh)', + ["cn", "groupPermission"]) result = {p['cn'][0]: p for p in result} + if permission not in result: + raise YunohostError('permission_not_found', permission=permission) - new_per_dict = {} + current_allowed_groups = [_ldap_path_extract(p, "cn") for p in result[permission].get("groupPermission", [])] - for a in app: - for per in permission: - permission_name = per + '.' + a - if permission_name not in result: - raise YunohostError('permission_not_found', permission=per, app=a) - new_per_dict[permission_name] = set() - if 'groupPermission' in result[permission_name]: - new_per_dict[permission_name] = set(result[permission_name]['groupPermission']) + # Compute new allowed group list (and make sure what we're doing make sense) - for g in del_group: - if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]: - raise YunohostError('need_define_permission_before') - group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org' - if group_name not in new_per_dict[permission_name]: - logger.warning(m18n.n('group_already_disallowed', permission=per, app=a, group=g)) - else: - new_per_dict[permission_name].remove(group_name) + new_allowed_groups = copy.copy(current_allowed_groups) - if 'cn=all_users,ou=groups,dc=yunohost,dc=org' in new_per_dict[permission_name]: - new_per_dict[permission_name].remove('cn=all_users,ou=groups,dc=yunohost,dc=org') - for g in add_group: - group_name = 'cn=' + g + ',ou=groups,dc=yunohost,dc=org' - if group_name in new_per_dict[permission_name]: - logger.warning(m18n.n('group_already_allowed', permission=per, app=a, group=g)) - else: - new_per_dict[permission_name].add(group_name) + if add: + existing_groups = user_group_list()['groups'].keys() + groups_to_add = [add] if not isinstance(add, list) else add + for group in groups_to_add: + if group not in existing_groups: + raise YunohostError('group_unknown', group=group) + if group in current_allowed_groups: + logger.warning(m18n.n('group_already_allowed', permission=permission, group=group)) + new_allowed_groups += groups_to_add + + if remove: + groups_to_remove = [remove] if not isinstance(remove, list) else remove + for group in groups_to_remove: + if group not in existing_groups: + raise YunohostError('group_unknown', group=group) + if group not in current_allowed_groups: + logger.warning(m18n.n('group_already_disallowed', permission=permission, group=group)) + + new_allowed_groups = [g for g in new_allowed_groups if g not in groups_to_remove] + + # If we end up with something like allowed groups is ["all_users", "volunteers"] + # we shall warn the users that they should probably choose between one or the other, + # because the current situation is probably not what they expect / is temporary ? + + if len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups: + # FIXME : write a better explanation + logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.") + + # Commit the new allowed group list operation_logger.start() - for per, val in new_per_dict.items(): - # Don't update LDAP if we update exactly the same values - if val == set(result[per]['groupPermission'] if 'groupPermission' in result[per] else []): - continue - if ldap.update('cn=%s,ou=permission' % per, {'groupPermission': val}): - p = per.split('.') - logger.debug(m18n.n('permission_updated', permission=p[0], app=p[1])) - else: - raise YunohostError('permission_update_failed') + # Don't update LDAP if we update exactly the same values + if set(new_allowed_groups) == set(current_allowed_groups): + logger.warning("No change was applied because not relevant modification were found") + elif ldap.update('cn=%s,ou=permission' % permission, + {'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]}): + logger.debug(m18n.n('permission_updated', permission=permission)) - if sync_perm: - permission_sync_to_user() + # Trigger permission sync if asked - for a in app: - allowed_users = set() - disallowed_users = set() - group_list = user_group_list()['groups'] + if sync_perm: + permission_sync_to_user() - for g in add_group: - allowed_users.union(group_list[g]['members']) - for g in del_group: - disallowed_users.union(group_list[g]['members']) + # Trigger app callbacks - allowed_users = ','.join(allowed_users) - disallowed_users = ','.join(disallowed_users) - if add_group: - hook_callback('post_app_addaccess', args=[app, allowed_users]) - if del_group: - hook_callback('post_app_removeaccess', args=[app, disallowed_users]) + # FIXME FIXME FIXME - return user_permission_list(app, permission) + #if groups_to_add: + # hook_callback('post_app_addaccess', args=[app, allowed_users]) + #if groups_to_remove: + # hook_callback('post_app_removeaccess', args=[app, disallowed_users]) + + else: + raise YunohostError('permission_update_failed') + + return user_permission_list()["permissions"][permission] def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=True): diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 3eb329f4e..92bdcf7a4 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -724,21 +724,11 @@ def user_permission_list(): return yunohost.permission.user_permission_list() -@is_unit_operation([('app', 'user')]) -def user_permission_add(operation_logger, app, permission="main", username=None, group=None, sync_perm=True): +@is_unit_operation([('permission', 'user')]) +def user_permission_update(operation_logger, permission, add=None, remove=None, sync_perm=True): import yunohost.permission - return yunohost.permission.user_permission_update(operation_logger, app, permission=permission, - add_username=username, add_group=group, - del_username=None, del_group=None, - sync_perm=sync_perm) - - -@is_unit_operation([('app', 'user')]) -def user_permission_remove(operation_logger, app, permission="main", username=None, group=None, sync_perm=True): - import yunohost.permission - return yunohost.permission.user_permission_update(operation_logger, app, permission=permission, - add_username=None, add_group=None, - del_username=username, del_group=group, + return yunohost.permission.user_permission_update(operation_logger, permission, + add=add, remove=remove, sync_perm=sync_perm) From 45483f4116e487be9d344fadff222007087a28e7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 15:16:09 +0200 Subject: [PATCH 077/299] --short and --full options for group_list and permission_list --- data/actionsmap/yunohost.yml | 18 ++++++++++++++---- src/yunohost/permission.py | 24 +++++++++++++++--------- src/yunohost/user.py | 28 +++++++++++----------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index ebdd2b982..ece642d0d 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -208,13 +208,13 @@ user: action_help: List existing groups api: GET /users/groups arguments: - -n: - full: --names-only - help: Only list the name of the groups without any additional info + -s: + full: --short + help: List only the names of groups action: store_true -f: full: --full - help: List all the info available for each groups + help: Display all informations known about each groups action: store_true ### user_group_create() @@ -281,6 +281,16 @@ user: list: action_help: List permissions and corresponding accesses api: GET /users/permissions/ + arguments: + -s: + full: --short + help: List only the names of permissions + action: store_true + -f: + full: --full + help: Display all informations known about each permissions, including the full list of users corresponding to allowed groups. + action: store_true + ### user_permission_update() update: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 20c34ada8..ab79ff7ed 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -43,29 +43,35 @@ logger = getActionLogger('yunohost.user') # -def user_permission_list(): +def user_permission_list(short=False, full=False): """ List permissions and corresponding accesses """ - from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract + # Fetch relevant informations - # Fetch all permissions objects + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() permissions_infos = ldap.search('ou=permission,dc=yunohost,dc=org', '(objectclass=permissionYnh)', - ['cn', 'groupPermission', 'inheritPermission', 'URL']) + ["cn", 'groupPermission', 'inheritPermission', 'URL']) + + # Parse / organize information to be outputed permissions = {} for infos in permissions_infos: name = infos['cn'][0] + permissions[name] = {} - permissions[name] = { - "allowed_users": [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])], - "allowed_groups": [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])], - "urls": infos.get("URL", []) - } + permissions[name]["allowed"] = [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])] + + if full: + permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])], + permissions[name]["urls"] = infos.get("URL", []) + + if short: + permissions = permissions.keys() return {'permissions': permissions} diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 92bdcf7a4..80f558809 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -489,12 +489,12 @@ def user_info(username): # # Group subcategory # -def user_group_list(names_only=False, full=False): +def user_group_list(short=False, full=False): """ List users Keyword argument: - names-only -- Only list the name of the groups without any additional info + short -- Only list the name of the groups without any additional info full -- List all the info available for each groups """ @@ -502,30 +502,24 @@ def user_group_list(names_only=False, full=False): from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() - - if names_only: - fields_to_fetch = ["cn"] - elif full: - fields_to_fetch = ["cn", "member", "permission"] - else: - fields_to_fetch = ["cn", "member"] - groups_infos = ldap.search('ou=groups,dc=yunohost,dc=org', '(objectclass=groupOfNamesYnh)', - fields_to_fetch) + ["cn", "member", "permission"]) # Parse / organize information to be outputed groups = {} for infos in groups_infos: + name = infos["cn"][0] groups[name] = {} - if "member" in fields_to_fetch: - groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])] - if "permission" in fields_to_fetch: + + groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])] + + if full: groups[name]["permissions"] = [_ldap_path_extract(p, "cn") for p in infos.get("permission", [])] - if names_only: + if short: groups = groups.keys() return {'groups': groups} @@ -719,9 +713,9 @@ def user_group_info(groupname): # Permission subcategory # -def user_permission_list(): +def user_permission_list(short=False, full=False): import yunohost.permission - return yunohost.permission.user_permission_list() + return yunohost.permission.user_permission_list(short, full) @is_unit_operation([('permission', 'user')]) From e5676c4b30db38f63ec740ac987aa697f473173a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 15:16:26 +0200 Subject: [PATCH 078/299] Propagate change in permission_list to permission_update --- src/yunohost/permission.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index ab79ff7ed..3a6bde077 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -92,33 +92,31 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Fetch currently allowed groups for this permission - result = ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', - ["cn", "groupPermission"]) - result = {p['cn'][0]: p for p in result} - if permission not in result: + permissions = user_permission_list(full=True)["permissions"] + if permission not in permissions: raise YunohostError('permission_not_found', permission=permission) - current_allowed_groups = [_ldap_path_extract(p, "cn") for p in result[permission].get("groupPermission", [])] + current_allowed_groups = permissions[permission]["allowed"] + all_existing_groups = user_group_list()['groups'].keys() # Compute new allowed group list (and make sure what we're doing make sense) new_allowed_groups = copy.copy(current_allowed_groups) if add: - existing_groups = user_group_list()['groups'].keys() groups_to_add = [add] if not isinstance(add, list) else add for group in groups_to_add: - if group not in existing_groups: + if group not in all_existing_groups: raise YunohostError('group_unknown', group=group) if group in current_allowed_groups: logger.warning(m18n.n('group_already_allowed', permission=permission, group=group)) + new_allowed_groups += groups_to_add if remove: groups_to_remove = [remove] if not isinstance(remove, list) else remove for group in groups_to_remove: - if group not in existing_groups: + if group not in all_existing_groups: raise YunohostError('group_unknown', group=group) if group not in current_allowed_groups: logger.warning(m18n.n('group_already_disallowed', permission=permission, group=group)) @@ -130,7 +128,8 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # because the current situation is probably not what they expect / is temporary ? if len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups: - # FIXME : write a better explanation + # FIXME : i18n + # FIXME : write a better explanation ? logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.") # Commit the new allowed group list @@ -139,6 +138,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): + # FIXME : i18n logger.warning("No change was applied because not relevant modification were found") elif ldap.update('cn=%s,ou=permission' % permission, {'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]}): @@ -149,20 +149,20 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if sync_perm: permission_sync_to_user() + new_permission = user_permission_list(full=True)["permissions"][permission] + # Trigger app callbacks + app = permission.split(".")[0] + if add: + hook_callback('post_app_addaccess', args=[app, new_permission["corresponding_users"]]) + if remove: + hook_callback('post_app_removeaccess', args=[app, new_permission["corresponding_users"]]) - # FIXME FIXME FIXME - - #if groups_to_add: - # hook_callback('post_app_addaccess', args=[app, allowed_users]) - #if groups_to_remove: - # hook_callback('post_app_removeaccess', args=[app, disallowed_users]) + return new_permission else: raise YunohostError('permission_update_failed') - return user_permission_list()["permissions"][permission] - def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=True): """ From a1d3376613993a8420373eec1b05bddc08a7720f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 15:49:07 +0200 Subject: [PATCH 079/299] Simplify permission_clear, now named permission_reset --- data/actionsmap/yunohost.yml | 16 +++----- locales/en.json | 1 - src/yunohost/permission.py | 75 +++++++++++++----------------------- src/yunohost/user.py | 4 +- 4 files changed, 34 insertions(+), 62 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index ece642d0d..49dde373b 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -314,19 +314,13 @@ user: extra: pattern: *pattern_username - ## user_permission_clear() - clear: - action_help: Reset access rights for the app + ## user_permission_reset() + reset: + action_help: Reset allowed groups to the default (all_users) for a given permission api: DELETE /users/permissions/ arguments: - app: - help: Application to manage the permission - nargs: "+" - -p: - full: --permission - help: Name of permission (main by default) - nargs: "*" - metavar: PERMISSION + permission: + help: Permission to be resetted (e.g. mail.main or wordpress.editors) ssh: subcategory_help: Manage ssh access diff --git a/locales/en.json b/locales/en.json index 725bb1f8c..ebbb89fa8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -429,7 +429,6 @@ "pattern_positive_number": "Must be a positive number", "pattern_username": "Must be lower-case alphanumeric and underscore characters only", "pattern_password_app": "Sorry, passwords should not contain the following characters: {forbidden_chars}", - "permission_already_clear": "Permission '{permission:s}' already clear for app {app:s}", "permission_already_exist": "Permission '{permission:s}' for app {app:s} already exist", "permission_created": "Permission '{permission:s}' for app {app:s} created", "permission_creation_failed": "Permission creation failed", diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 3a6bde077..8816ad950 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -152,11 +152,13 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, new_permission = user_permission_list(full=True)["permissions"][permission] # Trigger app callbacks - app = permission.split(".")[0] - if add: - hook_callback('post_app_addaccess', args=[app, new_permission["corresponding_users"]]) - if remove: - hook_callback('post_app_removeaccess', args=[app, new_permission["corresponding_users"]]) + # FIXME : this is not how this hook works... gotta compute the list of user actually added / removed + + #app = permission.split(".")[0] + #if add: + # hook_callback('post_app_addaccess', args=[app, new_permission["corresponding_users"]]) + #if remove: + # hook_callback('post_app_removeaccess', args=[app, new_permission["corresponding_users"]]) return new_permission @@ -164,63 +166,40 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, raise YunohostError('permission_update_failed') -def user_permission_clear(operation_logger, app=[], permission=None, sync_perm=True): +def user_permission_reset(operation_logger, permission, sync_perm=True): """ - Reset the permission for a specific application + Reset a given permission to just 'all_users' Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) - username -- Username to get informations (all by default) - group -- Groupname to get informations (all by default) - + permission -- The name of the permission to be reseted """ from yunohost.hook import hook_callback from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - if permission: - if not isinstance(permission, list): - permission = [permission] - else: - permission = ["main"] + # Fetch existing permission + + existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) + if existing_permission is None: + raise YunohostError('permission_not_found', permission=permission) + + # Update permission with default (all_users) default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']} + if ldap.update('cn=%s,ou=permission' % permission, default_permission): + logger.debug(m18n.n('permission_updated', permission=permission)) + else: + raise YunohostError('permission_update_failed') - # Populate permission informations - permission_attrs = [ - 'cn', - 'groupPermission', - ] - result = ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', permission_attrs) - result = {p['cn'][0]: p for p in result} + if sync_perm: + permission_sync_to_user() - for a in app: - for per in permission: - permission_name = per + '.' + a - if permission_name not in result: - raise YunohostError('permission_not_found', permission=per, app=a) - if 'groupPermission' in result[permission_name] and 'cn=all_users,ou=groups,dc=yunohost,dc=org' in result[permission_name]['groupPermission']: - logger.warning(m18n.n('permission_already_clear', permission=per, app=a)) - continue - if ldap.update('cn=%s,ou=permission' % permission_name, default_permission): - logger.debug(m18n.n('permission_updated', permission=per, app=a)) - else: - raise YunohostError('permission_update_failed') + new_permission = user_permission_list(full=True)["permissions"][permission] - permission_sync_to_user() + # FIXME : trigger app callbacks + # app = permission.split(".")[0] - for a in app: - permission_name = 'main.' + a - result = ldap.search('ou=permission,dc=yunohost,dc=org', - filter='cn=' + permission_name, attrs=['inheritPermission']) - if result: - allowed_users = result[0]['inheritPermission'] - new_user_list = ','.join(allowed_users) - hook_callback('post_app_removeaccess', args=[app, new_user_list]) - - return user_permission_list(app, permission) + return new_permission # # diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 80f558809..2bf36cfd6 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -727,9 +727,9 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, @is_unit_operation([('app', 'user')]) -def user_permission_clear(operation_logger, app, permission=None, sync_perm=True): +def user_permission_reset(operation_logger, permission, sync_perm=True): import yunohost.permission - return yunohost.permission.user_permission_clear(operation_logger, app, permission, + return yunohost.permission.user_permission_reset(operation_logger, permission, sync_perm=sync_perm) From 3535cb655f17be09ede5157473b28cdc09060b2b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 16:36:17 +0200 Subject: [PATCH 080/299] Fix call of app add/remove access hooks --- src/yunohost/permission.py | 42 +++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 8816ad950..fbfaa43a2 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -67,7 +67,7 @@ def user_permission_list(short=False, full=False): permissions[name]["allowed"] = [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])] if full: - permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])], + permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])] permissions[name]["urls"] = infos.get("URL", []) if short: @@ -92,11 +92,11 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Fetch currently allowed groups for this permission - permissions = user_permission_list(full=True)["permissions"] - if permission not in permissions: + existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) + if existing_permission is None: raise YunohostError('permission_not_found', permission=permission) - current_allowed_groups = permissions[permission]["allowed"] + current_allowed_groups = existing_permission["allowed"] all_existing_groups = user_group_list()['groups'].keys() # Compute new allowed group list (and make sure what we're doing make sense) @@ -152,13 +152,19 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, new_permission = user_permission_list(full=True)["permissions"][permission] # Trigger app callbacks - # FIXME : this is not how this hook works... gotta compute the list of user actually added / removed - #app = permission.split(".")[0] - #if add: - # hook_callback('post_app_addaccess', args=[app, new_permission["corresponding_users"]]) - #if remove: - # hook_callback('post_app_removeaccess', args=[app, new_permission["corresponding_users"]]) + app = permission.split(".")[0] + + old_allowed_users = set(existing_permission["corresponding_users"]) + new_allowed_users = set(new_permission["corresponding_users"]) + + effectively_added_users = new_allowed_users - old_allowed_users + effectively_removed_users = old_allowed_users - new_allowed_users + + if effectively_added_users: + hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)]) + if effectively_removed_users: + hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)]) return new_permission @@ -196,8 +202,20 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): new_permission = user_permission_list(full=True)["permissions"][permission] - # FIXME : trigger app callbacks - # app = permission.split(".")[0] + # Trigger app callbacks + + app = permission.split(".")[0] + + old_allowed_users = set(existing_permission["corresponding_users"]) + new_allowed_users = set(new_permission["corresponding_users"]) + + effectively_added_users = new_allowed_users - old_allowed_users + effectively_removed_users = old_allowed_users - new_allowed_users + + if effectively_added_users: + hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)]) + if effectively_removed_users: + hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)]) return new_permission From 574e9aea44a8e25078b506240bc7cedf155dd8e0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 18:13:30 +0200 Subject: [PATCH 081/299] Simplify permission_create/urls/delete interface and code --- data/helpers.d/setting | 57 +++++++-------- src/yunohost/permission.py | 138 ++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 102 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index e6311fc1f..0e432d916 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -230,70 +230,63 @@ ynh_webpath_register () { # Create a new permission for the app # -# usage: ynh_permission_create --app "app" --permission "permission" --defaultdisallow [--urls "url" ["url" ...]] -# | arg: app - the application id +# usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]] # | arg: permission - the name for the permission (by default a permission named "main" already exist) -# | arg: defaultdisallow - define if all user will be allowed by default -# | arg: urls - the list of urls for the the permission +# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# +# example: ynh_permission_create --permission admin --urls domain.tld/blog/admin ynh_permission_create() { - declare -Ar args_array=( [a]=app= [p]=permission= [d]=defaultdisallow [u]=urls= ) - local app + declare -Ar args_array=( [p]=permission= [u]=urls= ) local permission - local defaultdisallow local urls ynh_handle_getopts_args "$@" - if [[ -n ${defaultdisallow:-} ]]; then - defaultdisallow=",default_allow=False" - fi if [[ -n ${urls:-} ]]; then urls=",urls=['${urls//';'/"','"}']" fi - yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app', '$permission' ${defaultdisallow:-} ${urls:-}, sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' ${urls:-}, sync_perm=False)" } # Remove a permission for the app (note that when the app is removed all permission is automatically removed) # -# usage: ynh_permission_remove --app "app" --permission "permission" -# | arg: app - the application id +# usage: ynh_permission_remove --permission "permission" # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) -ynh_permission_remove() { - declare -Ar args_array=( [a]=app= [p]=permission= ) - local app +# +# example: ynh_permission_delete --permission editors +ynh_permission_delete() { + declare -Ar args_array=( [p]=permission= ) local permission ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app', '$permission', sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission', sync_perm=False)" } # Add a path managed by the SSO # -# usage: ynh_permission_add_url --app "app" --permission "permission" --url "url" ["url" ...] -# | arg: app - the application id -# | arg: permission - the name for the permission -# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin) +# usage: ynh_permission_add_url --permission "permission" --url "url" ["url" ...] +# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) +# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# ynh_permission_add_url() { - declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= ) - local app + declare -Ar args_array=([p]=permission= [u]=urls= ) local permission - local url + local urls ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app', '$permission', add_url=['${url//';'/"','"}'], sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission', add=['${urls//';'/"','"}'], sync_perm=False)" } # Remove a path managed by the SSO # # usage: ynh_permission_del_path --app "app" --permission "permission" --url "url" ["url" ...] -# | arg: app - the application id -# | arg: permission - the name for the permission -# | arg: url - the FULL url for the the permission (ex domain.tld/apps/admin) +# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) +# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# ynh_permission_remove_url() { - declare -Ar args_array=( [a]=app= [p]=permission= [u]=url= ) - local app + declare -Ar args_array=([p]=permission= [u]=urls= ) local permission - local url + local urls ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app', '$permission', remove_url=['${url//';'/"','"}'], sync_perm=False)" + yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission', remove=['${url//';'/"','"}'], sync_perm=False)" } diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index fbfaa43a2..960c0853c 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -229,27 +229,22 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): @is_unit_operation(['permission', 'app']) -def permission_create(operation_logger, app, permission, urls=None, default_allow=True, sync_perm=True): +def permission_create(operation_logger, permission, urls=None, sync_perm=True): """ Create a new permission for a specific application Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) + permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) urls -- list of urls to specify for the permission - """ - from yunohost.domain import _normalize_domain_path + from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() # Validate uniqueness of permission in LDAP - permission_name = str(permission + '.' + app) # str(...) Fix encoding issue - conflict = ldap.get_conflict({ - 'cn': permission_name - }, base_dn='ou=permission,dc=yunohost,dc=org') - if conflict: - raise YunohostError('permission_already_exist', permission=permission, app=app) + if ldap.get_conflict({'cn': permission}, + base_dn='ou=permission,dc=yunohost,dc=org'): + raise YunohostError('permission_already_exist', permission=permission) # Get random GID all_gid = {x.gr_gid for x in grp.getgrall()} @@ -261,110 +256,106 @@ def permission_create(operation_logger, app, permission, urls=None, default_allo attr_dict = { 'objectClass': ['top', 'permissionYnh', 'posixGroup'], - 'cn': permission_name, + 'cn': permission, 'gidNumber': gid, } - if default_allow: + + # For main permission, we add all users by default + if permission.endswith(".main"): attr_dict['groupPermission'] = 'cn=all_users,ou=groups,dc=yunohost,dc=org' if urls: - attr_dict['URL'] = [] - for url in urls: - domain = url[:url.index('/')] - path = url[url.index('/'):] - domain, path = _normalize_domain_path(domain, path) - attr_dict['URL'].append(domain + path) + attr_dict['URL'] = [_normalize_url(url) for url in urls] operation_logger.start() - if ldap.add('cn=%s,ou=permission' % permission_name, attr_dict): + if ldap.add('cn=%s,ou=permission' % permission, attr_dict): if sync_perm: permission_sync_to_user() - logger.debug(m18n.n('permission_created', permission=permission, app=app)) - return user_permission_list(app, permission) - - raise YunohostError('permission_creation_failed') + logger.debug(m18n.n('permission_created', permission=permission)) + return user_permission_list(full=True)["permissions"][permission] + else: + raise YunohostError('permission_creation_failed') @is_unit_operation(['permission', 'app']) -def permission_urls(operation_logger, app, permission, add_url=None, remove_url=None, sync_perm=True): +def permission_urls(operation_logger, permission, add=None, remove=None, sync_perm=True): """ Update urls related to a permission for a specific application Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) - add_url -- Add a new url for a permission - remove_url -- Remove a url for a permission + permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) + add -- List of urls to add + remove -- List of urls to remove """ - from yunohost.domain import _normalize_domain_path from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - permission_name = str(permission + '.' + app) # str(...) Fix encoding issue + # Fetch existing permission - # Populate permission informations - result = ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='cn=' + permission_name, attrs=['URL']) - if not result: - raise YunohostError('permission_not_found', permission=permission, app=app) - permission_obj = result[0] + existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) + if not existing_permission: + raise YunohostError('permission_not_found', permission=permission) - if 'URL' not in permission_obj: - permission_obj['URL'] = [] + # Compute new url list - url = set(permission_obj['URL']) + new_urls = copy.copy(existing_permission["urls"]) - if add_url: - for u in add_url: - domain = u[:u.index('/')] - path = u[u.index('/'):] - domain, path = _normalize_domain_path(domain, path) - url.add(domain + path) - if remove_url: - for u in remove_url: - domain = u[:u.index('/')] - path = u[u.index('/'):] - domain, path = _normalize_domain_path(domain, path) - url.discard(domain + path) + if add: + urls_to_add = [add] if not isinstance(add, list) else add + urls_to_add = [_normalize_url(url) for url in urls_to_add] + new_urls += urls_to_add + if remove: + urls_to_remove = [remove] if not isinstance(remove, list) else remove + urls_to_remove = [_normalize_url(url) for url in urls_to_remove] + new_urls = [u for u in new_urls if u not in urls_to_remove] - if url == set(permission_obj['URL']): + if set(new_urls) == set(existing_permission["urls"]): logger.warning(m18n.n('permission_update_nothing_to_do')) - return user_permission_list(app, permission) + return existing_permission + + # Actually commit the change operation_logger.start() - if ldap.update('cn=%s,ou=permission' % permission_name, {'cn': permission_name, 'URL': url}): + if ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls}): if sync_perm: permission_sync_to_user() - logger.debug(m18n.n('permission_updated', permission=permission, app=app)) - return user_permission_list(app, permission) - - raise YunohostError('premission_update_failed') + logger.debug(m18n.n('permission_updated', permission=permission)) + return user_permission_list(full=True)["permissions"][permission] + else: + raise YunohostError('premission_update_failed') @is_unit_operation(['permission', 'app']) -def permission_delete(operation_logger, app, permission, force=False, sync_perm=True): +def permission_delete(operation_logger, permission, force=False, sync_perm=True): """ - Remove a permission for a specific application + Delete a permission Keyword argument: - app -- an application OR sftp, xmpp (metronome), mail - permission -- name of the permission ("main" by default) - + permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) """ - if permission == "main" and not force: + if permission.endswith("main") and not force: raise YunohostError('remove_main_permission_not_allowed') from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() + # Make sure this permission exists + + existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) + if not existing_permission: + raise YunohostError('permission_not_found', permission=permission) + + # Actually delete the permission + operation_logger.start() - if not ldap.remove('cn=%s,ou=permission' % str(permission + '.' + app)): - raise YunohostError('permission_deletion_failed', permission=permission, app=app) - if sync_perm: - permission_sync_to_user() - logger.debug(m18n.n('permission_deleted', permission=permission, app=app)) + if ldap.remove('cn=%s,ou=permission' % permission): + if sync_perm: + permission_sync_to_user() + logger.debug(m18n.n('permission_deleted', permission=permission)) + else: + raise YunohostError('permission_deletion_failed', permission=permission) def permission_sync_to_user(force=False): @@ -438,3 +429,10 @@ def permission_sync_to_user(force=False): # Reload unscd, otherwise the group ain't propagated to the LDAP database os.system('nscd --invalidate=passwd') os.system('nscd --invalidate=group') + +def _normalize_url(url): + from yunohost.domain import _normalize_domain_path + domain = url[:url.index('/')] + path = url[url.index('/'):] + domain, path = _normalize_domain_path(domain, path) + return domain + path From 853c6a161a07ca2a935b1eeb3424094f1f8e6a7c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 20:56:56 +0200 Subject: [PATCH 082/299] Simplify permission_sync_to_user ... force is never set to True so I dropped it... --- src/yunohost/permission.py | 71 +++++++++++--------------------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 960c0853c..54aa25e23 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -358,70 +358,41 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True) raise YunohostError('permission_deletion_failed', permission=permission) -def permission_sync_to_user(force=False): +def permission_sync_to_user(): """ Sychronise the inheritPermission attribut in the permission object from the user<->group link and the group<->permission link - - Keyword argument: - force -- Force to recreate all attributes. Used generally with the - backup which uses "slapadd" which doesnt' use the memberOf overlay. - Note that by removing all value and adding a new time, we force the - overlay to update all attributes """ - # Note that a LDAP operation with the same value that is in LDAP crash SLAP. - # So we need to check before each ldap operation that we really change something in LDAP import os from yunohost.app import app_ssowatconf + from yunohost.user import user_group_list from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - permission_attrs = [ - 'cn', - 'member', - ] - group_info = ldap.search('ou=groups,dc=yunohost,dc=org', - '(objectclass=groupOfNamesYnh)', permission_attrs) - group_info = {g['cn'][0]: g for g in group_info} + groups = user_group_list(full=True)["groups"] + permissions = user_permission_list(full=True)["permissions"] - for per in ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', - ['cn', 'inheritPermission', 'groupPermission', 'memberUid']): + for permission_name, permission_infos in permissions.items(): - if 'groupPermission' not in per: - per['groupPermission'] = [] - user_permission = set() - for group in per['groupPermission']: - group = group.split("=")[1].split(",")[0] - if 'member' not in group_info[group]: - continue - for user in group_info[group]['member']: - user_permission.add(user) + # These are the users currently allowed because there's an 'inheritPermission' object corresponding to it + currently_allowed_users = set(permission_infos["corresponding_users"]) - if 'inheritPermission' not in per: - per['inheritPermission'] = [] - if 'memberUid' not in per: - per['memberUid'] = [] + # These are the users that should be allowed because they are member of a group that is allowed for this permission ... + should_be_allowed_users = set([user for group in permission_infos["allowed"] for user in groups[group]["members"]]) - uid_val = [v.split("=")[1].split(",")[0] for v in user_permission] - if user_permission == set(per['inheritPermission']) and set(uid_val) == set(per['memberUid']) and not force: + # Note that a LDAP operation with the same value that is in LDAP crash SLAP. + # So we need to check before each ldap operation that we really change something in LDAP + if currently_allowed_users == should_be_allowed_users: + # We're all good, this permission is already correctly synchronized ! continue - inheritPermission = {'inheritPermission': user_permission, 'memberUid': uid_val} - if force: - if per['groupPermission']: - if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': []}): - raise YunohostError('permission_update_failed_clear') - if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'groupPermission': per['groupPermission']}): - raise YunohostError('permission_update_failed_populate') - if per['inheritPermission']: - if not ldap.update('cn=%s,ou=permission' % per['cn'][0], {'inheritPermission': []}): - raise YunohostError('permission_update_failed_clear') - if user_permission: - if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission): - raise YunohostError('permission_update_failed') - else: - if not ldap.update('cn=%s,ou=permission' % per['cn'][0], inheritPermission): - raise YunohostError('permission_update_failed') + + new_inherited_perms = {'inheritPermission': ["uid=%s,ou=users,dc=yunohost,dc=org" % u for u in should_be_allowed_users], + 'memberUid': should_be_allowed_users} + + # Commit the change with the new inherited stuff + if not ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms): + raise YunohostError('permission_update_failed') + logger.debug(m18n.n('permission_generated')) app_ssowatconf() From 98b1c30330af31c7e4bd9e81a0a436f1a3b3c5e2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 21:40:04 +0200 Subject: [PATCH 083/299] Simplify app_ssowatconf code related to permissions --- src/yunohost/app.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c2fb87acf..1a41e2a01 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1414,12 +1414,10 @@ def app_ssowatconf(): skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$") skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$") - permission = {} - for a in user_permission_list()['permissions'].values(): - for p in a.values(): - if 'URL' in p: - for u in p['URL']: - permission[u] = p['allowed_users'] + permissions_per_url = {} + for permission_name, permission_infos in user_permission_list(full=True)['permissions'].items(): + for url in permission_infos["urls"]: + permissions_per_url[url] = permission_infos['corresponding_users'] conf_dict = { 'portal_domain': main_domain, @@ -1441,7 +1439,7 @@ def app_ssowatconf(): 'redirected_regex': redirected_regex, 'users': {username: app_map(user=username) for username in user_list()['users'].keys()}, - 'permission': permission, + 'permissions': permissions_per_url, } with open('/etc/ssowat/conf.json', 'w+') as f: From 38c43f4b9a6f899985fb7bb67d1c9ce7b1cafad9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 22:48:54 +0200 Subject: [PATCH 084/299] Fix the whole operation logger / related to thing + propagate on the legacy addaccess --- locales/en.json | 13 +++++-------- src/yunohost/app.py | 37 +++++++++++++++++++------------------ src/yunohost/log.py | 4 ++-- src/yunohost/permission.py | 31 +++++++++++++++++++++++-------- src/yunohost/user.py | 26 +++++++++++++------------- 5 files changed, 62 insertions(+), 49 deletions(-) diff --git a/locales/en.json b/locales/en.json index ebbb89fa8..2c6363608 100644 --- a/locales/en.json +++ b/locales/en.json @@ -259,9 +259,6 @@ "log_help_to_get_failed_log": "The operation '{desc}' has failed! To get help, please share the full log of this operation using the command 'yunohost log display {name} --share'", "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'", "log_operation_unit_unclosed_properly": "Operation unit has not been closed properly", - "log_app_addaccess": "Add access to '{}'", - "log_app_removeaccess": "Remove access to '{}'", - "log_app_clearaccess": "Remove all access to '{}'", "log_app_fetchlist": "Add an application list", "log_app_removelist": "Remove an application list", "log_app_change_url": "Change the url of '{}' application", @@ -279,9 +276,9 @@ "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", "log_dyndns_update": "Update the ip associated with your YunoHost subdomain '{}'", "log_letsencrypt_cert_install": "Install Let's encrypt certificate on '{}' domain", - "log_permission_add": "Add permission '{}' for app '{}'", - "log_permission_remove": "Remove permission '{}'", - "log_permission_update": "Update permission '{}' for app '{}'", + "log_permission_create": "Create permission '{permission}'", + "log_permission_delete": "Delete permission '{permission}'", + "log_permission_urls": "Update urls related to permission '{permission}'", "log_selfsigned_cert_install": "Install self signed certificate on '{}' domain", "log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate", "log_regen_conf": "Regenerate system configurations '{}'", @@ -291,8 +288,8 @@ "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", "log_user_update": "Update information of '{}' user", - "log_user_permission_add": "Update '{}' permission", - "log_user_permission_remove": "Update '{}' permission", + "log_user_permission_update": "Update accesses for permission '{permission}'", + "log_user_permission_reset": "Reset permission '{permission}'", "log_tools_maindomain": "Make '{}' as main domain", "log_tools_migrations_migrate_forward": "Migrate forward", "log_tools_postinstall": "Postinstall your YunoHost server", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1a41e2a01..14396b1cd 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1039,8 +1039,7 @@ def app_remove(operation_logger, app): raise YunohostError("this_action_broke_dpkg") -@is_unit_operation(['permission','app']) -def app_addaccess(operation_logger, apps, users=[]): +def app_addaccess(apps, users=[]): """ Grant access right to users (everyone by default) @@ -1051,15 +1050,15 @@ def app_addaccess(operation_logger, apps, users=[]): """ from yunohost.permission import user_permission_update - permission = user_permission_update(operation_logger, app=apps, permission="main", add_username=users) + output = {} + for app in apps: + permission = user_permission_update(app+".main", add=users) + output[app] = permission["corresponding_users"] - result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()} - - return {'allowed_users': result} + return {'allowed_users': output} -@is_unit_operation(['permission','app']) -def app_removeaccess(operation_logger, apps, users=[]): +def app_removeaccess(apps, users=[]): """ Revoke access right to users (everyone by default) @@ -1070,15 +1069,15 @@ def app_removeaccess(operation_logger, apps, users=[]): """ from yunohost.permission import user_permission_update - permission = user_permission_update(operation_logger, app=apps, permission="main", del_username=users) + output = {} + for app in apps: + permission = user_permission_update(app+".main", remove=users) + output[app] = permission["corresponding_users"] - result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()} - - return {'allowed_users': result} + return {'allowed_users': output} -@is_unit_operation(['permission','app']) -def app_clearaccess(operation_logger, apps): +def app_clearaccess(apps): """ Reset access rights for the app @@ -1086,13 +1085,15 @@ def app_clearaccess(operation_logger, apps): apps """ - from yunohost.permission import user_permission_clear + from yunohost.permission import user_permission_reset - permission = user_permission_clear(operation_logger, app=apps, permission="main") + output = {} + for app in apps: + permission = user_permission_reset(app+".main") + output[app] = permission["corresponding_users"] - result = {p : v['main']['allowed_users'] for p, v in permission['permissions'].items()} + return {'allowed_users': output} - return {'allowed_users': result} def app_debug(app): """ diff --git a/src/yunohost/log.py b/src/yunohost/log.py index cbb850e44..8b0f893e8 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -44,7 +44,7 @@ CATEGORIES = ['operation', 'history', 'package', 'system', 'access', 'service', 'app'] METADATA_FILE_EXT = '.yml' LOG_FILE_EXT = '.log' -RELATED_CATEGORIES = ['app', 'domain', 'service', 'user'] +RELATED_CATEGORIES = ['app', 'domain', 'group', 'service', 'user'] logger = getActionLogger('yunohost.log') @@ -213,7 +213,7 @@ def log_display(path, number=None, share=False): return infos -def is_unit_operation(entities=['app', 'domain', 'service', 'user'], +def is_unit_operation(entities=['app', 'domain', 'group', 'service', 'user'], exclude=['password'], operation_key=None): """ Configure quickly a unit operation diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 54aa25e23..a4ff9fb15 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -76,6 +76,7 @@ def user_permission_list(short=False, full=False): return {'permissions': permissions} +@is_unit_operation() def user_permission_update(operation_logger, permission, add=None, remove=None, sync_perm=True): """ Allow or Disallow a user or group to a permission for a specific application @@ -98,6 +99,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, current_allowed_groups = existing_permission["allowed"] all_existing_groups = user_group_list()['groups'].keys() + operation_logger.related_to.append(('app', permission.split(".")[0])) # Compute new allowed group list (and make sure what we're doing make sense) @@ -110,6 +112,8 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, raise YunohostError('group_unknown', group=group) if group in current_allowed_groups: logger.warning(m18n.n('group_already_allowed', permission=permission, group=group)) + else: + operation_logger.related_to.append(('group', group)) new_allowed_groups += groups_to_add @@ -120,6 +124,8 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, raise YunohostError('group_unknown', group=group) if group not in current_allowed_groups: logger.warning(m18n.n('group_already_disallowed', permission=permission, group=group)) + else: + operation_logger.related_to.append(('group', group)) new_allowed_groups = [g for g in new_allowed_groups if g not in groups_to_remove] @@ -132,15 +138,17 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # FIXME : write a better explanation ? logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.") - # Commit the new allowed group list - - operation_logger.start() - # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): # FIXME : i18n logger.warning("No change was applied because not relevant modification were found") - elif ldap.update('cn=%s,ou=permission' % permission, + return + + # Commit the new allowed group list + + operation_logger.start() + + if ldap.update('cn=%s,ou=permission' % permission, {'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]}): logger.debug(m18n.n('permission_updated', permission=permission)) @@ -172,6 +180,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, raise YunohostError('permission_update_failed') +@is_unit_operation() def user_permission_reset(operation_logger, permission, sync_perm=True): """ Reset a given permission to just 'all_users' @@ -191,6 +200,9 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): # Update permission with default (all_users) + operation_logger.related_to.append(('app', permission.split(".")[0])) + operation_logger.start() + default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']} if ldap.update('cn=%s,ou=permission' % permission, default_permission): logger.debug(m18n.n('permission_updated', permission=permission)) @@ -228,7 +240,7 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): # -@is_unit_operation(['permission', 'app']) +@is_unit_operation() def permission_create(operation_logger, permission, urls=None, sync_perm=True): """ Create a new permission for a specific application @@ -267,6 +279,7 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): if urls: attr_dict['URL'] = [_normalize_url(url) for url in urls] + operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() if ldap.add('cn=%s,ou=permission' % permission, attr_dict): if sync_perm: @@ -277,7 +290,7 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): raise YunohostError('permission_creation_failed') -@is_unit_operation(['permission', 'app']) +@is_unit_operation() def permission_urls(operation_logger, permission, add=None, remove=None, sync_perm=True): """ Update urls related to a permission for a specific application @@ -316,6 +329,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe # Actually commit the change + operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() if ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls}): if sync_perm: @@ -326,7 +340,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe raise YunohostError('premission_update_failed') -@is_unit_operation(['permission', 'app']) +@is_unit_operation() def permission_delete(operation_logger, permission, force=False, sync_perm=True): """ Delete a permission @@ -349,6 +363,7 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True) # Actually delete the permission + operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() if ldap.remove('cn=%s,ou=permission' % permission): if sync_perm: diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 2bf36cfd6..5631a2204 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -525,7 +525,7 @@ def user_group_list(short=False, full=False): return {'groups': groups} -@is_unit_operation([('groupname', 'user')]) +@is_unit_operation([('groupname', 'group')]) def user_group_create(operation_logger, groupname, gid=None, primary_group=False, sync_perm=True): """ Create group @@ -537,8 +537,6 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface - operation_logger.start() - ldap = _get_ldap_interface() # Validate uniqueness of groupname in LDAP @@ -574,6 +572,7 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False if primary_group: attr_dict["member"] = ["uid=" + groupname + ",ou=users,dc=yunohost,dc=org"] + operation_logger.start() if ldap.add('cn=%s,ou=groups' % groupname, attr_dict): logger.success(m18n.n('group_created', group=groupname)) if sync_perm: @@ -583,7 +582,7 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False raise YunohostError('group_creation_failed', group=groupname) -@is_unit_operation([('groupname', 'user')]) +@is_unit_operation([('groupname', 'group')]) def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): """ Delete user @@ -614,7 +613,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): permission_sync_to_user() -@is_unit_operation([('groupname', 'user')]) +@is_unit_operation([('groupname', 'group')]) def user_group_update(operation_logger, groupname, add=None, remove=None, force=False, sync_perm=True): """ Update user informations @@ -650,6 +649,8 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= if user in current_group: logger.warning(m18n.n('user_already_in_group', user=user, group=groupname)) + else: + operation_logger.related_to.append(('user', user)) new_group += users_to_add @@ -659,6 +660,8 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= for user in users_to_remove: if user not in current_group: logger.warning(m18n.n('user_not_in_group', user=user, group=groupname)) + else: + operation_logger.related_to.append(('user', user)) # Remove users_to_remove from new_group # Kinda like a new_group -= users_to_remove @@ -666,9 +669,8 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= new_group_dns = ["uid=" + user + ",ou=users,dc=yunohost,dc=org" for user in new_group] - operation_logger.start() - if set(new_group) != set(current_group): + operation_logger.start() ldap = _get_ldap_interface() if not ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)}): raise YunohostError('group_update_failed', group=groupname) @@ -718,18 +720,16 @@ def user_permission_list(short=False, full=False): return yunohost.permission.user_permission_list(short, full) -@is_unit_operation([('permission', 'user')]) -def user_permission_update(operation_logger, permission, add=None, remove=None, sync_perm=True): +def user_permission_update(permission, add=None, remove=None, sync_perm=True): import yunohost.permission - return yunohost.permission.user_permission_update(operation_logger, permission, + return yunohost.permission.user_permission_update(permission, add=add, remove=remove, sync_perm=sync_perm) -@is_unit_operation([('app', 'user')]) -def user_permission_reset(operation_logger, permission, sync_perm=True): +def user_permission_reset(permission, sync_perm=True): import yunohost.permission - return yunohost.permission.user_permission_reset(operation_logger, permission, + return yunohost.permission.user_permission_reset(permission, sync_perm=sync_perm) From d5b2fb7a7126e80492d38e455bcb41f11e0b32b8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 11 Sep 2019 23:18:23 +0200 Subject: [PATCH 085/299] Misc fixes/improvements for i18n strings --- locales/en.json | 38 ++++++++++++++++++-------------------- src/yunohost/permission.py | 16 ++++++++-------- src/yunohost/user.py | 8 ++++---- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/locales/en.json b/locales/en.json index 2c6363608..e41250759 100644 --- a/locales/en.json +++ b/locales/en.json @@ -228,19 +228,19 @@ "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", - "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' enabled'", - "group_already_disallowed": "Group '{group:s}' already has permission '{permission:s}' disabled'", - "group_name_already_exist": "Group {name:s} already exist", + "group_already_exist": "Group {group} already exist", + "group_already_exist_on_system": "Group {group} already exists in the system group", "group_created": "Group '{group}' successfully created", "group_creation_failed": "Group creation failed for group '{group}'", - "group_cannot_be_edited": "The group {group:s} cannot be edited manually.", - "group_cannot_be_deleted": "The group {group:s} cannot be deleted manually.", + "group_cannot_be_edited": "The group {group} cannot be edited manually.", + "group_cannot_be_deleted": "The group {group} cannot be deleted manually.", "group_deleted": "Group '{group}' deleted", "group_deletion_failed": "Group '{group} 'deletion failed", - "group_info_failed": "Group info failed", - "group_unknown": "Group {group:s} unknown", + "group_unknown": "Group {group} unknown", "group_updated": "Group '{group}' updated", "group_update_failed": "Group update failed for group '{group}'", + "group_user_already_in_group": "User {user} is already in group {group}", + "group_user_not_in_group": "User {user} is not in group {group}", "hook_exec_failed": "Script execution failed: {path:s}", "hook_exec_not_terminated": "Script execution did not finish properly: {path:s}", "hook_json_return_error": "Failed to read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}", @@ -426,22 +426,23 @@ "pattern_positive_number": "Must be a positive number", "pattern_username": "Must be lower-case alphanumeric and underscore characters only", "pattern_password_app": "Sorry, passwords should not contain the following characters: {forbidden_chars}", - "permission_already_exist": "Permission '{permission:s}' for app {app:s} already exist", - "permission_created": "Permission '{permission:s}' for app {app:s} created", - "permission_creation_failed": "Permission creation failed", - "permission_deleted": "Permission '{permission:s}' for app {app:s} deleted", - "permission_deletion_failed": "Permission '{permission:s}' for app {app:s} deletion failed", - "permission_not_found": "Permission '{permission:s}' not found for application {app:s}", - "permission_update_failed": "Permission update failed", - "permission_generated": "The permission database has been updated", - "permission_updated": "Permission '{permission:s}' for app {app:s} updated", + "permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'", + "permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'", + "permission_already_exist": "Permission '{permission}' already exists", + "permission_cannot_remove_main": "Removing a main permission is not allowed", + "permission_created": "Permission '{permission}' created", + "permission_creation_failed": "Failed to create permission '{permission}'", + "permission_deleted": "Permission '{permission}' deleted", + "permission_deletion_failed": "Failed to delete permission '{permission}'", + "permission_not_found": "Permission '{permission}' does not seem to exist ?", + "permission_update_failed": "Failed to update permission '{permission}'", + "permission_updated": "Permission '{permission}' updated", "permission_update_nothing_to_do": "No permissions to update", "port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections", "port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections", "port_available": "Port {port:d} is available", "port_unavailable": "Port {port:d} is not available", "recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create ' or the admin interface.", - "remove_main_permission_not_allowed": "Removing the main permission is not allowed", "regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'", "regenconf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'", "regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but has been kept back.", @@ -527,7 +528,6 @@ "ssowat_conf_updated": "The SSOwat configuration has been updated", "ssowat_persistent_conf_read_error": "Error while reading SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", "ssowat_persistent_conf_write_error": "Error while saving SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", - "system_groupname_exists": "Groupname already exists in the system group", "system_upgraded": "The system has been upgraded", "system_username_exists": "Username already exists in the system users", "this_action_broke_dpkg": "This action broke dpkg/apt (the system package managers)... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", @@ -556,14 +556,12 @@ "upnp_disabled": "UPnP has been disabled", "upnp_enabled": "UPnP has been enabled", "upnp_port_open_failed": "Unable to open UPnP ports", - "user_already_in_group": "User {user:} already in group {group:s}", "user_created": "The user has been created", "user_creation_failed": "Unable to create user", "user_deleted": "The user has been deleted", "user_deletion_failed": "Unable to delete user", "user_home_creation_failed": "Unable to create user home folder", "user_info_failed": "Unable to retrieve user information", - "user_not_in_group": "User {user:s} not in group {group:s}", "user_unknown": "Unknown user: {user:s}", "user_update_failed": "Unable to update user", "user_updated": "The user has been updated", diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index a4ff9fb15..21ee960fa 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -111,7 +111,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if group not in all_existing_groups: raise YunohostError('group_unknown', group=group) if group in current_allowed_groups: - logger.warning(m18n.n('group_already_allowed', permission=permission, group=group)) + logger.warning(m18n.n('permission_already_allowed', permission=permission, group=group)) else: operation_logger.related_to.append(('group', group)) @@ -123,7 +123,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if group not in all_existing_groups: raise YunohostError('group_unknown', group=group) if group not in current_allowed_groups: - logger.warning(m18n.n('group_already_disallowed', permission=permission, group=group)) + logger.warning(m18n.n('permission_already_disallowed', permission=permission, group=group)) else: operation_logger.related_to.append(('group', group)) @@ -177,7 +177,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, return new_permission else: - raise YunohostError('permission_update_failed') + raise YunohostError('permission_update_failed', permission=permission) @is_unit_operation() @@ -207,7 +207,7 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): if ldap.update('cn=%s,ou=permission' % permission, default_permission): logger.debug(m18n.n('permission_updated', permission=permission)) else: - raise YunohostError('permission_update_failed') + raise YunohostError('permission_update_failed', permission=permission) if sync_perm: permission_sync_to_user() @@ -337,7 +337,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe logger.debug(m18n.n('permission_updated', permission=permission)) return user_permission_list(full=True)["permissions"][permission] else: - raise YunohostError('premission_update_failed') + raise YunohostError('permission_update_failed', permission=permission) @is_unit_operation() @@ -350,7 +350,7 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True) """ if permission.endswith("main") and not force: - raise YunohostError('remove_main_permission_not_allowed') + raise YunohostError('permission_cannot_remove_main') from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -406,9 +406,9 @@ def permission_sync_to_user(): # Commit the change with the new inherited stuff if not ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms): - raise YunohostError('permission_update_failed') + raise YunohostError('permission_update_failed', permission=permission_name) - logger.debug(m18n.n('permission_generated')) + logger.debug("The permission database has been resynchronized") app_ssowatconf() diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 5631a2204..e1719d3a6 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -544,12 +544,12 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False 'cn': groupname }, base_dn='ou=groups,dc=yunohost,dc=org') if conflict: - raise YunohostError('group_name_already_exist', name=groupname) + raise YunohostError('group_already_exist', group=groupname) # Validate uniqueness of groupname in system group all_existing_groupnames = {x.gr_name for x in grp.getgrall()} if groupname in all_existing_groupnames: - raise YunohostError('system_groupname_exists') + raise YunohostError('group_already_exist_on_system', group=groupname) if not gid: # Get random GID @@ -648,7 +648,7 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= raise YunohostError('user_unknown', user=user) if user in current_group: - logger.warning(m18n.n('user_already_in_group', user=user, group=groupname)) + logger.warning(m18n.n('group_user_already_in_group', user=user, group=groupname)) else: operation_logger.related_to.append(('user', user)) @@ -659,7 +659,7 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= for user in users_to_remove: if user not in current_group: - logger.warning(m18n.n('user_not_in_group', user=user, group=groupname)) + logger.warning(m18n.n('group_user_not_in_group', user=user, group=groupname)) else: operation_logger.related_to.append(('user', user)) From a92ff5307774d657b601051b31e0e52634472180 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 00:20:22 +0200 Subject: [PATCH 086/299] Propagate changes to other parts of the code relying on groups and permissions --- src/yunohost/app.py | 29 +++++++----------- src/yunohost/backup.py | 61 ++++++++++++++++---------------------- src/yunohost/permission.py | 9 ++++-- 3 files changed, 44 insertions(+), 55 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 14396b1cd..f505dd088 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -465,7 +465,7 @@ def app_change_url(operation_logger, app, domain, path): """ from yunohost.hook import hook_exec, hook_callback from yunohost.domain import _normalize_domain_path, _get_conflicting_apps - from yunohost.permission import permission_update + from yunohost.permission import permission_urls installed = _is_installed(app) if not installed: @@ -555,7 +555,7 @@ def app_change_url(operation_logger, app, domain, path): app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) - permission_update(app, permission="main", add_url=[domain+path], remove_url=[old_domain+old_path], sync_perm=True) + permission_urls(app+".main", add=[domain+path], remove=[old_domain+old_path], sync_perm=True) # avoid common mistakes if _run_service_command("reload", "nginx") is False: @@ -738,7 +738,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import permission_create, permission_update, permission_delete, permission_sync_to_user + from yunohost.permission import permission_create, permission_urls, permission_delete, permission_sync_to_user ldap = _get_ldap_interface() # Fetch or extract sources @@ -875,7 +875,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Create permission before the install (useful if the install script redefine the permission) # Note that sync_perm is disabled to avoid triggering a whole bunch of code and messages # can't be sure that we don't have one case when it's needed - permission_create(app=app_instance_name, permission="main", sync_perm=False) + permission_create(app_instance_name+".main", sync_perm=False) # Execute the app install script install_retcode = 1 @@ -910,11 +910,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu args=[app_instance_name], env=env_dict_remove )[0] # Remove all permission in LDAP - result = ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) - permission_list = [p['cn'][0] for p in result] - for l in permission_list: - permission_delete(app_instance_name, l.split('.')[0], force=True) + for permission_name in user_permission_list()["permissions"].keys(): + if permission_name.startswith(app_instance_name+"."): + permission_delete(permission_name, force=True) if remove_retcode != 0: msg = m18n.n('app_not_properly_removed', @@ -960,8 +958,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu domain = app_settings.get('domain', None) path = app_settings.get('path', None) if domain and path: - permission_update(app_instance_name, permission="main", add_url=[domain+path], sync_perm=False) - + permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False) permission_sync_to_user() logger.success(m18n.n('installation_complete')) @@ -978,7 +975,6 @@ def app_remove(operation_logger, app): app -- App(s) to delete """ - from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_exec, hook_remove, hook_callback from yunohost.permission import permission_delete, permission_sync_to_user if not _is_installed(app): @@ -1026,12 +1022,9 @@ def app_remove(operation_logger, app): hook_remove(app) # Remove all permission in LDAP - ldap = _get_ldap_interface() - result = ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app, attrs=['cn']) - permission_list = [p['cn'][0] for p in result] - for l in permission_list: - permission_delete(app, l.split('.')[0], force=True, sync_perm=False) + for permission_name in user_permission_list()["permissions"].keys(): + if permission_name.startswith(app+"."): + permission_delete(permission_name, force=True, sync_perm=False) permission_sync_to_user() diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index fdbd8c62c..0527f89bf 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -703,6 +703,10 @@ class BackupManager(): self._import_to_list_to_backup(env_dict["YNH_BACKUP_CSV"]) # backup permissions + # + # FIXME : why can't we instead use a simple yaml file to store the + # relevant info and recreate the permission object from it ... + # logger.debug(m18n.n('backup_permission', app=app)) ldap_url = "ldap:///dc=yunohost,dc=org???(&(objectClass=permissionYnh)(cn=*.%s))" % app os.system("slapcat -b dc=yunohost,dc=org -H '%s' -l '%s/permission.ldif'" % (ldap_url, settings_dir)) @@ -919,7 +923,7 @@ class RestoreManager(): successfull_apps = self.targets.list("apps", include=["Success", "Warning"]) - permission_sync_to_user(force=False) + permission_sync_to_user() if os.path.ismount(self.work_dir): ret = subprocess.call(["umount", self.work_dir]) @@ -1183,18 +1187,11 @@ class RestoreManager(): if system_targets == []: return - from yunohost.utils.ldap import _get_ldap_interface - ldap = _get_ldap_interface() + from yunohost.permission import permission_create, user_permission_update, user_permission_list # Backup old permission for apps # We need to do that because in case of an app is installed we can't remove the permission for this app - old_apps_permission = [] - try: - old_apps_permission = ldap.search('ou=permission,dc=yunohost,dc=org', - '(&(objectClass=permissionYnh)(!(cn=main.mail))(!(cn=main.xmpp))(!(cn=main.sftp)))', - ['cn', 'objectClass', 'groupPermission', 'URL', 'gidNumber']) - except: - logger.info(m18n.n('apps_permission_not_found')) + old_apps_permission = user_permission_list(ignore_system_perms=True)["permissions"] # Start register change on system operation_logger = OperationLogger('backup_restore_system') @@ -1232,12 +1229,11 @@ class RestoreManager(): regen_conf() - # Check if we need to do the migration 0009 : setup group and permission + # Check that at least a group exists (all_users) to know if we need to + # do the migration 0011 : setup group and permission + # # Legacy code - result = ldap.search('ou=groups,dc=yunohost,dc=org', - '(&(objectclass=groupOfNamesYnh)(cn=all_users))', - ['cn']) - if not result: + if not user_group_list["groups"]: from yunohost.tools import _get_migration_by_name setup_group_permission = _get_migration_by_name("setup_group_permission") # Update LDAP schema restart slapd @@ -1245,22 +1241,16 @@ class RestoreManager(): regen_conf(names=['slapd'], force=True) setup_group_permission.migrate_LDAP_db() - # Remove all permission for all app which sill in the LDAP - for per in ldap.search('ou=permission,dc=yunohost,dc=org', - '(&(objectClass=permissionYnh)(!(cn=mail.main))(!(cn=xmpp.main))(!(cn=sftp.main)))', - ['cn']): - if not ldap.remove('cn=%s,ou=permission' % per['cn'][0]): - raise YunohostError('permission_deletion_failed', - permission=per['cn'][0].split('.')[0], - app=per['cn'][0].split('.')[1]) + # Remove all permission for all app which is still in the LDAP + for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys(): + permission_delete(permission_name) # Restore permission for the app which is installed - for per in old_apps_permission: - # FIXME : will come here later to fix this following previous commits ... - permission_name, app_name = per['cn'][0].split('.') + for permission_name, permission_infos in old_apps_permission.items(): + app_name = permission_name.split(".")[0] if _is_installed(app_name): - if not ldap.add('cn=%s,ou=permission' % per['cn'][0], per): - raise YunohostError('apps_permission_restoration_failed', permission=permission_name, app=app_name) + permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False) + user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"]) def _restore_apps(self): @@ -1368,6 +1358,10 @@ class RestoreManager(): restore_script = os.path.join(tmp_folder_for_app_restore, 'restore') # Restore permissions + # + # FIXME : why can't we instead use a simple yaml file to store the + # relevant info and recreate the permission object from it ... + # if os.path.isfile(app_settings_in_archive + '/permission.ldif'): filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass', 'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid'] @@ -1422,7 +1416,6 @@ class RestoreManager(): operation_logger.start() # Execute remove script - # TODO: call app_remove instead if hook_exec(remove_script, args=[app_instance_name], env=env_dict_remove)[0] != 0: msg = m18n.n('app_not_properly_removed', app=app_instance_name) @@ -1434,12 +1427,10 @@ class RestoreManager(): # Cleaning app directory shutil.rmtree(app_settings_new_path, ignore_errors=True) - # Remove all permission in LDAP - result = ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) - permission_list = [p['cn'][0] for p in result] - for l in permission_list: - permission_delete(app_instance_name, l.split('.')[0], force=True) + # Remove all permission in LDAP for this app + for permission_name in user_permission_list()["permissions"].keys(): + if permission_name.startswith(app_instance_name+"."): + permission_delete(permission_name, force=True) # TODO Cleaning app hooks else: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 21ee960fa..4d935d3c0 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -36,6 +36,8 @@ from yunohost.log import is_unit_operation logger = getActionLogger('yunohost.user') +SYSTEM_PERMS = ["mail", "xmpp", "stfp"] + # # # The followings are the methods exposed through the "yunohost user permission" interface @@ -43,7 +45,7 @@ logger = getActionLogger('yunohost.user') # -def user_permission_list(short=False, full=False): +def user_permission_list(short=False, full=False, ignore_system_perms=True): """ List permissions and corresponding accesses """ @@ -62,8 +64,11 @@ def user_permission_list(short=False, full=False): for infos in permissions_infos: name = infos['cn'][0] - permissions[name] = {} + if ignore_system_perms and name.split(".")[0] in SYSTEM_PERMS: + continue + + permissions[name] = {} permissions[name]["allowed"] = [_ldap_path_extract(p, "cn") for p in infos.get('groupPermission', [])] if full: From bbfc62cf3efc298fc297fa4da52d45e420cc0c0c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 00:41:38 +0200 Subject: [PATCH 087/299] Backup/restore app permissions using yaml files which are much simpler to handle... --- locales/en.json | 1 - src/yunohost/backup.py | 55 +++++++++++++++++++----------------------- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/locales/en.json b/locales/en.json index e41250759..e69e06201 100644 --- a/locales/en.json +++ b/locales/en.json @@ -50,7 +50,6 @@ "app_upgrade_some_app_failed": "Unable to upgrade some applications", "app_upgraded": "{app:s} has been upgraded", "apps_permission_not_found": "No permission found for the installed apps", - "apps_permission_restoration_failed": "Permission '{permission:s}' for app {app:s} restoration has failed", "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.", "appslist_could_not_migrate": "Could not migrate app list {appslist:s}! Unable to parse the url… The old cron job has been kept in {bkp_file:s}.", "appslist_fetched": "The application list {appslist:s} has been fetched", diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 0527f89bf..f1ac7ee9c 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -40,7 +40,7 @@ from moulinette import msignals, m18n from yunohost.utils.error import YunohostError from moulinette.utils import filesystem from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_file, mkdir +from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml from yunohost.app import ( app_info, _is_installed, _parse_app_instance_name, _patch_php5 @@ -677,6 +677,8 @@ class BackupManager(): backup_app_failed -- Raised at the end if the app backup script execution failed """ + from yunohost.permission import user_permission_list + app_setting_path = os.path.join('/etc/yunohost/apps/', app) # Prepare environment @@ -703,13 +705,10 @@ class BackupManager(): self._import_to_list_to_backup(env_dict["YNH_BACKUP_CSV"]) # backup permissions - # - # FIXME : why can't we instead use a simple yaml file to store the - # relevant info and recreate the permission object from it ... - # logger.debug(m18n.n('backup_permission', app=app)) - ldap_url = "ldap:///dc=yunohost,dc=org???(&(objectClass=permissionYnh)(cn=*.%s))" % app - os.system("slapcat -b dc=yunohost,dc=org -H '%s' -l '%s/permission.ldif'" % (ldap_url, settings_dir)) + permissions = user_permission_list(full=True)["permissions"] + this_app_permissions = {name: infos for name, infos in permissions.items() if name.startswith(app + ".")} + write_to_yaml("%s/permissions.yml" % settings_dir, this_app_permissions) except: abs_tmp_app_dir = os.path.join(self.work_dir, 'apps/', app) @@ -1289,11 +1288,8 @@ class RestoreManager(): name already exists restore_app_failed -- Raised if the restore bash script failed """ - from moulinette.utils.filesystem import read_ldif from yunohost.user import user_group_list - from yunohost.permission import permission_delete - from yunohost.utils.ldap import _get_ldap_interface - ldap = _get_ldap_interface() + from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): @@ -1358,26 +1354,25 @@ class RestoreManager(): restore_script = os.path.join(tmp_folder_for_app_restore, 'restore') # Restore permissions - # - # FIXME : why can't we instead use a simple yaml file to store the - # relevant info and recreate the permission object from it ... - # - if os.path.isfile(app_settings_in_archive + '/permission.ldif'): - filtred_entries = ['entryUUID', 'creatorsName', 'createTimestamp', 'entryCSN', 'structuralObjectClass', - 'modifiersName', 'modifyTimestamp', 'inheritPermission', 'memberUid'] - entries = read_ldif('%s/permission.ldif' % app_settings_in_archive, filtred_entries) - group_list = user_group_list()['groups'] - for dn, entry in entries: - # Remove the group which has been removed - for group in entry['groupPermission']: - group_name = group.split(',')[0].split('=')[1] - if group_name not in group_list: - entry['groupPermission'].remove(group) - if not ldap.add('cn=%s,ou=permission' % entry['cn'][0], entry): - raise YunohostError('apps_permission_restoration_failed', - permission=entry['cn'][0].split('.')[0], - app=entry['cn'][0].split('.')[1]) + if os.path.isfile('%s/permissions.yml' % app_settings_new_path): + + permissions = read_yaml('%s/permissions.yml' % app_settings_new_path) + existing_groups = user_group_list()['groups'] + + for permission_name, permission_infos in permissions: + + permission_create(permission_name, urls=permission_infos.get("urls", [])) + + if "allowed" not in permissions_infos: + logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s ... You might need to reconfigure permissions yourself!" % (permission_name, app_instance_name)) + else: + groups = [g for g in permission_infos["allowed"] if g in existing_groups] + user_permission_update(permission_name, remove="all_users", add=groups) + + os.remove('%s/permissions.yml' % app_settings_new_path) else: + # Otherwise, we need to migrate the legacy permissions of this + # app (included in its settings.yml) from yunohost.tools import _get_migration_by_name setup_group_permission = _get_migration_by_name("setup_group_permission") setup_group_permission.migrate_app_permission(app=app_instance_name) From e40698ef2062346638a4492924a4dacf32f081f2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 02:25:52 +0200 Subject: [PATCH 088/299] Propagate changes on migration --- locales/en.json | 2 +- .../0011_setup_group_permission.py | 20 ++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/locales/en.json b/locales/en.json index e69e06201..c370f821e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -195,7 +195,6 @@ "dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.", "dyndns_unavailable": "Domain {domain:s} is not available.", - "error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", "executing_command": "Executing command '{command:s}'…", "executing_script": "Executing script '{script:s}'…", "extracting": "Extracting…", @@ -355,6 +354,7 @@ "migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user...", "migration_0011_done": "Migration successful. You are now able to manage groups of users.", + "migration_0011_error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration need to be updated.\nYou need to save your actual configuration, reintialize the original configuration by the command 'yunohost tools regen-conf -f' and after retry the migration", "migration_0011_LDAP_update_failed": "LDAP update failed. Error: {error:s}", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index d2924f0af..720e4ac36 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -1,17 +1,16 @@ -import yaml import time import os from moulinette import m18n from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger +from moulinette.utils.filesystem import read_yaml from yunohost.tools import Migration from yunohost.user import user_group_create, user_group_update from yunohost.app import app_setting, app_list from yunohost.regenconf import regen_conf -from yunohost.permission import permission_create, permission_sync_to_user -from yunohost.user import user_permission_add +from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user logger = getActionLogger('yunohost.migration') @@ -19,6 +18,7 @@ logger = getActionLogger('yunohost.migration') # Tools used also for restoration ################################################### + class MyMigration(Migration): """ Update the LDAP DB to be able to store the permission @@ -38,10 +38,9 @@ class MyMigration(Migration): try: ldap.remove('cn=sftpusers,ou=groups') except: - logger.warn(m18n.n("error_when_removing_sftpuser_group")) + logger.warn(m18n.n("migration_0011_error_when_removing_sftpuser_group")) - with open('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') as f: - ldap_map = yaml.load(f) + ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') try: attr_dict = ldap_map['parents']['ou=permission'] @@ -65,11 +64,9 @@ class MyMigration(Migration): username = user_info['uid'][0] ldap.update('uid=%s,ou=users' % username, {'objectClass': ['mailAccount', 'inetOrgPerson', 'posixAccount', 'userPermissionYnh']}) - user_group_create(username, gid=user_info['uidNumber'][0], sync_perm=False) - user_group_update(groupname=username, add=username, force=True, sync_perm=False) + user_group_create(username, gid=user_info['uidNumber'][0], primary_group=True, sync_perm=False) user_group_update(groupname='all_users', add=username, force=True, sync_perm=False) - def migrate_app_permission(self, app=None): logger.info(m18n.n("migration_0011_migrate_permission")) @@ -85,13 +82,12 @@ class MyMigration(Migration): domain = app_setting(app, 'domain') urls = [domain + path] if domain and path else None - permission_create(app, permission='main', urls=urls, default_allow=True, sync_perm=False) + permission_create(app+".main", urls=urls, sync_perm=False) if permission: allowed_group = permission.split(',') - user_permission_add([app], permission='main', group=allowed_group, sync_perm=False) + user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False) app_setting(app, 'allowed_users', delete=True) - def run(self): # Check if the migration can be processed ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) From c0361430e26d42e0b8f167a330a20abff854655d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 04:02:03 +0200 Subject: [PATCH 089/299] Try to simplify + comment the code of check_LDAP_db_integrity --- src/yunohost/tests/test_permission.py | 79 +++++++++++++++------------ 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 8222248b1..0373a6cbf 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -2,8 +2,10 @@ import pytest from moulinette.core import MoulinetteError from yunohost.app import app_install, app_remove, app_change_url, app_list -from yunohost.user import user_list, user_create, user_permission_list, user_delete, user_group_list, user_group_delete, user_permission_add, user_permission_remove, user_permission_clear -from yunohost.permission import permission_create, permission_update, permission_delete + +from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ + user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info +from yunohost.permission import user_permission_update, user_permission_list, permission_create, permission_urls, permission_delete from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError @@ -57,7 +59,7 @@ def check_LDAP_db_integrity(): # One part should be done automatically by the "memberOf" overlay of LDAP. # The other part is done by the the "permission_sync_to_user" function of the permission module - from yunohost.utils.ldap import _get_ldap_interface + from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract ldap = _get_ldap_interface() user_search = ldap.search('ou=users,dc=yunohost,dc=org', @@ -76,60 +78,65 @@ def check_LDAP_db_integrity(): for user in user_search: user_dn = 'uid=' + user['uid'][0] + ',ou=users,dc=yunohost,dc=org' - group_list = [m.split("=")[1].split(",")[0] for m in user['memberOf']] - permission_list = [] - if 'permission' in user: - permission_list = [m.split("=")[1].split(",")[0] for m in user['permission']] + group_list = [_ldap_path_extract(m, "cn") for m in user['memberOf']] + permission_list = [_ldap_path_extract(m, "cn") for m in user.get('permission', [])] + # This user's DN sould be found in all groups it is a member of for group in group_list: assert user_dn in group_map[group]['member'] + + # This user's DN should be found in all perms it has access to for permission in permission_list: assert user_dn in permission_map[permission]['inheritPermission'] for permission in permission_search: permission_dn = 'cn=' + permission['cn'][0] + ',ou=permission,dc=yunohost,dc=org' - user_list = [] - group_list = [] - if 'inheritPermission' in permission: - user_list = [m.split("=")[1].split(",")[0] for m in permission['inheritPermission']] - assert set(user_list) == set(permission['memberUid']) - if 'groupPermission' in permission: - group_list = [m.split("=")[1].split(",")[0] for m in permission['groupPermission']] + # inheritPermission uid's should match memberUids + user_list = [_ldap_path_extract(m, "uid") for m in permission.get('inheritPermission', [])] + assert set(user_list) == set(permission.get('memberUid', [])) + + # This perm's DN should be found on all related users it is related to for user in user_list: assert permission_dn in user_map[user]['permission'] + + # Same for groups : we should find the permission's DN for all related groups + group_list = [_ldap_path_extract(m, "cn") for m in permission.get('groupPermission', [])] for group in group_list: assert permission_dn in group_map[group]['permission'] - if 'member' in group_map[group]: - user_list_in_group = [m.split("=")[1].split(",")[0] for m in group_map[group]['member']] - assert set(user_list_in_group) <= set(user_list) + + # The list of user in the group should be a subset of all users related to the current permission + users_in_group = [_ldap_path_extract(m, "uid") for m in group_map[group].get("member", [])] + assert set(users_in_group) <= set(user_list) for group in group_search: group_dn = 'cn=' + group['cn'][0] + ',ou=groups,dc=yunohost,dc=org' - user_list = [] - permission_list = [] - if 'member' in group: - user_list = [m.split("=")[1].split(",")[0] for m in group['member']] - if group['cn'][0] in user_list: - # If it's the main group of the user it's normal that it is not in the memberUid - g_list = list(user_list) - g_list.remove(group['cn'][0]) - if 'memberUid' in group: - assert set(g_list) == set(group['memberUid']) - else: - assert g_list == [] - else: - assert set(user_list) == set(group['memberUid']) - if 'permission' in group: - permission_list = [m.split("=")[1].split(",")[0] for m in group['permission']] + user_list = [_ldap_path_extract(m, "uid") for m in group.get("member", [])] + # For primary groups, we should find that : + # - len(user_list) is 1 (a primary group has only 1 member) + # - the group name should be an existing yunohost user + # - memberUid is empty (meaning no other member than the corresponding user) + if group['cn'][0] in user_list: + assert len(user_list) == 1 + assert group["cn"][0] in user_map + assert group.get('memberUid', []) == [] + # Otherwise, user_list and memberUid should have the same content + else: + assert set(user_list) == set(group.get('memberUid', [])) + + # For all users members, this group should be in the "memberOf" on the other side for user in user_list: assert group_dn in user_map[user]['memberOf'] + + # For all the permissions of this group, the group should be among the "groupPermission" on the other side + permission_list = [_ldap_path_extract(m, "cn") for m in group.get('permission', [])] for permission in permission_list: assert group_dn in permission_map[permission]['groupPermission'] - if 'inheritPermission' in permission_map: - allowed_user_list = [m.split("=")[1].split(",")[0] for m in permission_map[permission]['inheritPermission']] - assert set(user_list) <= set(allowed_user_list) + + # And the list of user of this group (user_list) should be a subset of all allowed users for this perm... + allowed_user_list = [_ldap_path_extract(m, "uid") for m in permission_map[permission].get('inheritPermission', [])] + assert set(user_list) <= set(allowed_user_list) def check_permission_for_apps(): From f1f65137965039bca3cc4d7b531b10babfadcd61 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 04:02:36 +0200 Subject: [PATCH 090/299] Small tweaks for user group tests --- src/yunohost/tests/test_user-group.py | 86 ++++++++++++--------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 34e515ea0..88644c3e6 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -1,7 +1,8 @@ import pytest from moulinette.core import MoulinetteError -from yunohost.user import user_list, user_info, user_group_list, user_create, user_delete, user_update, user_group_create, user_group_delete, user_group_update, user_group_info +from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ + user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError from yunohost.tests.test_permission import check_LDAP_db_integrity @@ -82,12 +83,13 @@ def test_del_user(): assert "alice" not in group_res assert "alice" not in group_res['all_users']['members'] -def test_add_group(): +def test_create_group(): user_group_create("adminsys") group_res = user_group_list()['groups'] assert "adminsys" in group_res - assert "members" not in group_res['adminsys'] + assert "members" in group_res['adminsys'].keys() + assert group_res["adminsys"]["members"] == [] def test_del_group(): user_group_delete("dev") @@ -99,112 +101,102 @@ def test_del_group(): # Error on create / remove function # -def test_add_bad_user_1(): - # Check email already exist +def test_create_user_with_mail_address_already_taken(): with pytest.raises(MoulinetteError): user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh") -def test_add_bad_user_2(): - # Check to short password +def test_create_user_with_password_too_simple(): with pytest.raises(MoulinetteError): user_create("other", "Alice", "White", "other@" + maindomain, "12") -def test_add_bad_user_3(): - # Check user already exist +def test_create_user_already_exists(): with pytest.raises(MoulinetteError): user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh") -def test_del_bad_user_1(): - # Check user not found +def test_del_user_that_does_not_exist(): with pytest.raises(MoulinetteError): - user_delete("not_exit") + user_delete("doesnt_exist") -def test_add_bad_group_1(): +def test_create_group_all_users(): # Check groups already exist with special group "all_users" with pytest.raises(YunohostError): user_group_create("all_users") -def test_add_bad_group_2(): - # Check groups already exist (for standard groups) +def test_create_group_already_exists(): + # Check groups already exist (regular groups) with pytest.raises(MoulinetteError): user_group_create("dev") -def test_del_bad_group_1(): - # Check not allowed to remove this groups +def test_del_group_all_users(): with pytest.raises(YunohostError): user_group_delete("all_users") -def test_del_bad_group_2(): - # Check groups not found +def test_del_group_that_does_not_exist(): with pytest.raises(MoulinetteError): - user_group_delete("not_exit") + user_group_delete("doesnt_exist") # # Update function # -def test_update_user_1(): +def test_update_user(): user_update("alice", firstname="NewName", lastname="NewLast") info = user_info("alice") - assert "NewName" == info['firstname'] - assert "NewLast" == info['lastname'] + assert info['firstname'] == "NewName" + assert info['lastname'] == "NewLast" -def test_update_group_1(): +def test_update_group_add_user(): user_group_update("dev", add=["bob"]) group_res = user_group_list()['groups'] - assert set(["alice", "bob"]) == set(group_res['dev']['members']) + assert set(group_res['dev']['members']) == set(["alice", "bob"]) -def test_update_group_2(): - # Try to add a user in a group when the user is already in +def test_update_group_add_user_already_in(): user_group_update("apps", add=["bob"]) group_res = user_group_list()['groups'] - assert ["bob"] == group_res['apps']['members'] + assert group_res['apps']['members'] == ["bob"] -def test_update_group_3(): - # Try to remove a user in a group +def test_update_group_remove_user(): user_group_update("apps", remove=["bob"]) group_res = user_group_list()['groups'] - assert "members" not in group_res['apps'] + assert group_res['apps']['members'] == [] -def test_update_group_4(): - # Try to remove a user in a group when it is not already in +def test_update_group_remove_user_not_already_in(): user_group_update("apps", remove=["jack"]) group_res = user_group_list()['groups'] - assert ["bob"] == group_res['apps']['members'] + assert group_res['apps']['members'] == ["bob"] # # Error on update functions # -def test_bad_update_user_1(): - # Check user not found +def test_update_user_that_doesnt_exist(): with pytest.raises(YunohostError): - user_update("not_exit", firstname="NewName", lastname="NewLast") + user_update("doesnt_exist", firstname="NewName", lastname="NewLast") - -def bad_update_group_1(): +def test_update_group_that_doesnt_exist(): # Check groups not found with pytest.raises(YunohostError): - user_group_update("not_exit", add=["alice"]) + user_group_update("doesnt_exist", add=["alice"]) -def test_bad_update_group_2(): - # Check remove user in groups "all_users" not allowed +def test_update_group_all_users_manually(): with pytest.raises(YunohostError): user_group_update("all_users", remove=["alice"]) -def test_bad_update_group_3(): - # Check remove user in it own group not allowed + assert "alice" in user_group_list()["groups"]["all_users"]["members"] + +def test_update_group_primary_manually(): with pytest.raises(YunohostError): user_group_update("alice", remove=["alice"]) + assert "alice" in user_group_list()["groups"]["alice"]["members"] -def test_bad_update_group_1(): +def test_update_group_add_user_that_doesnt_exist(): # Check add bad user in group with pytest.raises(YunohostError): - user_group_update("dev", add=["not_exist"]) + user_group_update("dev", add=["doesnt_exist"]) - assert "not_exist" not in user_group_list()["groups"]["dev"] + assert "doesnt_exist" not in user_group_list()["groups"]["dev"]["members"] From 68db93cd635a8ce5720f3e8b1ca105e6fa4a27d4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 04:03:04 +0200 Subject: [PATCH 091/299] Fix an issue about groups not being properly cleaned and perms synced when deleting a user --- src/yunohost/user.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index e1719d3a6..ef2a7d523 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -245,9 +245,18 @@ def user_delete(operation_logger, username, purge=False): """ from yunohost.hook import hook_callback from yunohost.utils.ldap import _get_ldap_interface + from yunohost.permission import permission_sync_to_user operation_logger.start() + user_group_update("all_users", remove=username, force=True, sync_perm=False) + for group, infos in user_group_list()["groups"].items(): + # If the user is in this group (and it's not the primary group), + # remove the member from the group + if username != group and username in infos["members"]: + user_group_update(group, remove=username, sync_perm=False) + user_group_delete(username, force=True, sync_perm=True) + ldap = _get_ldap_interface() if ldap.remove('uid=%s,ou=users' % username): # Invalidate passwd to take user deletion into account @@ -259,19 +268,6 @@ def user_delete(operation_logger, username, purge=False): else: raise YunohostError('user_deletion_failed') - user_group_delete(username, force=True, sync_perm=True) - - group_list = ldap.search('ou=groups,dc=yunohost,dc=org', - '(&(objectclass=groupOfNamesYnh)(memberUid=%s))' - % username, ['cn']) - for group in group_list: - user_list = ldap.search('ou=groups,dc=yunohost,dc=org', - 'cn=' + group['cn'][0], - ['memberUid'])[0] - user_list['memberUid'].remove(username) - if not ldap.update('cn=%s,ou=groups' % group['cn'][0], user_list): - raise YunohostError('group_update_failed') - hook_callback('post_user_delete', args=[username, purge]) logger.success(m18n.n('user_deleted')) From fe8f7f2210d9cc398dc27f9fa16b20cc11bd79b9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 17:49:14 +0200 Subject: [PATCH 092/299] Update permission helper : have a single helper to manage urls, and a helper to add/remove groups to permission --- data/helpers.d/setting | 49 +++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index 0e432d916..502da1ed7 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -261,32 +261,51 @@ ynh_permission_delete() { yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission', sync_perm=False)" } -# Add a path managed by the SSO +# Manage urls related to a permission # -# usage: ynh_permission_add_url --permission "permission" --url "url" ["url" ...] +# usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...] # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) -# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# | arg: add - (optional) a list of FULL urls to add to the permission (e.g. domain.tld/apps/admin) +# | arg: remove - (optional) a list of FULL urls to remove from the permission (e.g. other.tld/apps/admin) # -ynh_permission_add_url() { - declare -Ar args_array=([p]=permission= [u]=urls= ) +ynh_permission_urls() { + declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=) local permission - local urls + local add + local remove ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission', add=['${urls//';'/"','"}'], sync_perm=False)" + if [[ -n ${add:-} ]]; then + add=",add=['${add//';'/"','"}']" + fi + if [[ -n ${remove:-} ]]; then + remove=",remove=['${remove//';'/"','"}']" + fi + + yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission' ${add:-} ${remove:-})" } -# Remove a path managed by the SSO +# Update a permission for the app # -# usage: ynh_permission_del_path --app "app" --permission "permission" --url "url" ["url" ...] -# | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) -# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# usage: ynh_permission_update --permission "permission" --add "group" ["group" ...] --remove "group" ["group" ...] +# | arg: permission - the name for the permission (by default a permission named "main" already exist) +# | arg: add - the list of group or users to enable add to the permission +# | arg: remove - the list of group or users to remove from the permission # -ynh_permission_remove_url() { - declare -Ar args_array=([p]=permission= [u]=urls= ) +# example: ynh_permission_update --permission admin --add samdoe --remove all_users +ynh_permission_update() { + declare -Ar args_array=( [p]=permission= [a]=add= [r]=remove= ) local permission - local urls + local add + local remove ynh_handle_getopts_args "$@" - yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission', remove=['${url//';'/"','"}'], sync_perm=False)" + if [[ -n ${add:-} ]]; then + add="--add ${add//';'/" "}" + fi + if [[ -n ${remove:-} ]]; then + remove="--remove ${remove//';'/" "} " + fi + + yunohost user permission update "$app.$permission" ${add:-} ${remove:-} } From b912cd0aecd48ec36d87d611447c91c97d293939 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 17:49:35 +0200 Subject: [PATCH 093/299] Propagate all changes to tests --- src/yunohost/tests/test_permission.py | 353 +++++++++++--------------- 1 file changed, 153 insertions(+), 200 deletions(-) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 0373a6cbf..2df6362a9 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -5,7 +5,8 @@ from yunohost.app import app_install, app_remove, app_change_url, app_list from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info -from yunohost.permission import user_permission_update, user_permission_list, permission_create, permission_urls, permission_delete +from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \ + permission_create, permission_urls, permission_delete from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError @@ -20,20 +21,18 @@ def clean_user_groups_permission(): if g != "all_users": user_group_delete(g) - for a, per in user_permission_list()['permissions'].items(): - if a in ['wiki', 'blog', 'site']: - for p in per: - permission_delete(a, p, force=True, sync_perm=False) + for p in user_permission_list()['permissions']: + if any(p.startswith(name) for name in ["wiki", "blog", "site", "permissions_app"]): + permission_delete(p, force=True, sync_perm=False) def setup_function(function): clean_user_groups_permission() user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") - permission_create("wiki", "main", [maindomain + "/wiki"], sync_perm=False) - permission_create("blog", "main", sync_perm=False) - - user_permission_add(["blog"], "main", group="alice") + permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False) + permission_create("blog.main", sync_perm=False) + user_permission_update("blog.main", remove="all_users", add="alice") def teardown_function(function): clean_user_groups_permission() @@ -144,100 +143,89 @@ def check_permission_for_apps(): # and we don't have any permission linked to no apps. The only exception who is not liked to an app # is mail, xmpp, and sftp - from yunohost.utils.ldap import _get_ldap_interface - ldap = _get_ldap_interface() - permission_search = ldap.search('ou=permission,dc=yunohost,dc=org', - '(objectclass=permissionYnh)', - ['cn', 'groupPermission', 'inheritPermission', 'memberUid']) + app_perms = user_permission_list(ignore_system_perms=True)["permissions"].keys() + + # Keep only the prefix so that + # ["foo.main", "foo.pwet", "bar.main"] + # becomes + # {"bar", "foo"} + # and compare this to the list of installed apps ... + + app_perms_prefix = set(p.split(".")[0] for p in app_perms) installed_apps = {app['id'] for app in app_list(installed=True)['apps']} - permission_list_set = {permission['cn'][0].split(".")[1] for permission in permission_search} - extra_service_permission = set(['mail', 'xmpp']) - if 'sftp' in permission_list_set: - extra_service_permission.add('sftp') - assert installed_apps == permission_list_set - extra_service_permission + assert installed_apps == app_perms_prefix # # List functions # -def test_list_permission(): - res = user_permission_list()['permissions'] +def test_permission_list(): + res = user_permission_list(full=True)['permissions'] - assert "wiki" in res - assert "main" in res['wiki'] - assert "blog" in res - assert "main" in res['blog'] - assert "mail" in res - assert "main" in res['mail'] - assert "xmpp" in res - assert "main" in res['xmpp'] - assert ["all_users"] == res['wiki']['main']['allowed_groups'] - assert ["alice"] == res['blog']['main']['allowed_groups'] - assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users']) - assert ["alice"] == res['blog']['main']['allowed_users'] - assert [maindomain + "/wiki"] == res['wiki']['main']['URL'] + assert "wiki.main" in res + assert "blog.main" in res + assert "mail.main" in res + assert "xmpp.main" in res + assert res['wiki.main']['allowed'] == ["all_users"] + assert res['blog.main']['allowed'] == ["alice"] + assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"]) + assert res['blog.main']['corresponding_users'] == ["alice"] + assert res['wiki.main']['urls'] == [maindomain + "/wiki"] # # Create - Remove functions # -def test_add_permission_1(): - permission_create("site", "test") +def test_permission_create_main(): + permission_create("site.main") + + res = user_permission_list(full=True)['permissions'] + assert "site.main" in res + assert res['site.main']['allowed'] == ["all_users"] + assert set(res['site.main']['corresponding_users']) == set(["alice", "bob"]) + + +def test_permission_create_extra(): + permission_create("site.test") + + res = user_permission_list(full=True)['permissions'] + assert "site.test" in res + # all_users is only enabled by default on .main perms + assert "all_users" not in res['site.test']['allowed'] + assert res['site.test']['corresponding_users'] == [] + +def test_permission_delete(): + permission_delete("wiki.main", force=True) res = user_permission_list()['permissions'] - assert "site" in res - assert "test" in res['site'] - assert "all_users" in res['site']['test']['allowed_groups'] - assert set(["alice", "bob"]) == set(res['site']['test']['allowed_users']) - -def test_add_permission_2(): - permission_create("site", "main", default_allow=False) - - res = user_permission_list()['permissions'] - assert "site" in res - assert "main" in res['site'] - assert [] == res['site']['main']['allowed_groups'] - assert [] == res['site']['main']['allowed_users'] - -def test_remove_permission(): - permission_delete("wiki", "main", force=True) - - res = user_permission_list()['permissions'] - assert "wiki" not in res + assert "wiki.main" not in res # # Error on create - remove function # -def test_add_bad_permission(): - # Create permission with same name +def test_permission_create_already_existing(): with pytest.raises(YunohostError): - permission_create("wiki", "main") + permission_create("wiki.main") -def test_remove_bad_permission(): - # Remove not existant permission +def test_permission_delete_doesnt_existing(): with pytest.raises(MoulinetteError): - permission_delete("non_exit", "main", force=True) + permission_delete("doesnt.exist", force=True) res = user_permission_list()['permissions'] - assert "wiki" in res - assert "main" in res['wiki'] - assert "blog" in res - assert "main" in res['blog'] - assert "mail" in res - assert "main" in res['mail'] - assert "xmpp" in res - assert "main" in res['xmpp'] + assert "wiki.main" in res + assert "blog.main" in res + assert "mail.main" in res + assert "xmpp.main" in res -def test_remove_main_permission(): +def test_permission_delete_main_without_force(): with pytest.raises(YunohostError): - permission_delete("blog", "main") + permission_delete("blog.main") res = user_permission_list()['permissions'] - assert "mail" in res - assert "main" in res['mail'] + assert "blog.main" in res # # Update functions @@ -245,137 +233,100 @@ def test_remove_main_permission(): # user side functions -def test_allow_first_group(): - # Remove permission to all_users and define per users - user_permission_add(["wiki"], "main", group="alice") +def test_permission_add_group(): + user_permission_update("wiki.main", add="alice") - res = user_permission_list()['permissions'] - assert ['alice'] == res['wiki']['main']['allowed_users'] - assert ['alice'] == res['wiki']['main']['allowed_groups'] + res = user_permission_list(full=True)['permissions'] + assert set(res['wiki.main']['allowed']) == set(["all_users", "alice"]) + assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"]) -def test_allow_other_group(): - # Allow new user in a permission - user_permission_add(["blog"], "main", group="bob") +def test_permission_remove_group(): + user_permission_update("blog.main", remove="alice") - res = user_permission_list()['permissions'] - assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users']) - assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_groups']) + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == [] + assert res['blog.main']['corresponding_users'] == [] -def test_disallow_group_1(): - # Disallow a user in a permission - user_permission_remove(["blog"], "main", group="alice") +def test_permission_add_and_remove_group(): + user_permission_update("wiki.main", add="alice", remove="all_users") - res = user_permission_list()['permissions'] - assert [] == res['blog']['main']['allowed_users'] - assert [] == res['blog']['main']['allowed_groups'] + res = user_permission_list(full=True)['permissions'] + assert res['wiki.main']['allowed'] == ["alice"] + assert res['wiki.main']['corresponding_users'] == ["alice"] -def test_allow_group_1(): - # Allow a user when he is already allowed - user_permission_add(["blog"], "main", group="alice") +def test_permission_add_group_already_allowed(): + user_permission_update("blog.main", add="alice") - res = user_permission_list()['permissions'] - assert ["alice"] == res['blog']['main']['allowed_users'] - assert ["alice"] == res['blog']['main']['allowed_groups'] + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == ["alice"] + assert res['blog.main']['corresponding_users'] == ["alice"] -def test_disallow_group_1(): - # Disallow a user when he is already disallowed - user_permission_remove(["blog"], "main", group="bob") +def test_permission_remove_group_already_not_allowed(): + user_permission_update("blog.main", remove="bob") - res = user_permission_list()['permissions'] - assert ["alice"] == res['blog']['main']['allowed_users'] - assert ["alice"] == res['blog']['main']['allowed_groups'] + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == ["alice"] + assert res['blog.main']['corresponding_users'] == ["alice"] -def test_reset_permission(): +def test_permission_reset(): # Reset permission - user_permission_clear(["blog"], "main") + user_permission_reset("blog.main") - res = user_permission_list()['permissions'] - assert set(["alice", "bob"]) == set(res['blog']['main']['allowed_users']) - assert ["all_users"] == res['blog']['main']['allowed_groups'] - -# internal functions - -def test_add_url_1(): - # Add URL in permission which hasn't any URL defined - permission_update("blog", "main", add_url=[maindomain + "/testA"]) - - res = user_permission_list()['permissions'] - assert [maindomain + "/testA"] == res['blog']['main']['URL'] - -def test_add_url_2(): - # Add a second URL in a permission - permission_update("wiki", "main", add_url=[maindomain + "/testA"]) - - res = user_permission_list()['permissions'] - assert set([maindomain + "/testA", maindomain + "/wiki"]) == set(res['wiki']['main']['URL']) - -def test_remove_url_1(): - permission_update("wiki", "main", remove_url=[maindomain + "/wiki"]) - - res = user_permission_list()['permissions'] - assert 'URL' not in res['wiki']['main'] - -def test_add_url_3(): - # Add a url already added - permission_update("wiki", "main", add_url=[maindomain + "/wiki"]) - - res = user_permission_list()['permissions'] - assert [maindomain + "/wiki"] == res['wiki']['main']['URL'] - -def test_remove_url_2(): - # Remove a url not added (with a permission which contain some URL) - permission_update("wiki", "main", remove_url=[maindomain + "/not_exist"]) - - res = user_permission_list()['permissions'] - assert [maindomain + "/wiki"] == res['wiki']['main']['URL'] - -def test_remove_url_2(): - # Remove a url not added (with a permission which contain no URL) - permission_update("blog", "main", remove_url=[maindomain + "/not_exist"]) - - res = user_permission_list()['permissions'] - assert 'URL' not in res['blog']['main'] + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == ["all_users"] + assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"]) # # Error on update function # -def test_disallow_bad_group_1(): - # Disallow a group when the group all_users is allowed +def test_permission_add_group_that_doesnt_exist(): with pytest.raises(YunohostError): - user_permission_remove("wiki", "main", group="alice") + user_permission_update("blog.main", add="doesnt_exist") - res = user_permission_list()['permissions'] - assert ["all_users"] == res['wiki']['main']['allowed_groups'] - assert set(["alice", "bob"]) == set(res['wiki']['main']['allowed_users']) + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == ["alice"] + assert res['blog.main']['corresponding_users'] == ["alice"] -def test_allow_bad_user(): - # Allow a non existant group +def test_permission_update_permission_that_doesnt_exist(): with pytest.raises(YunohostError): - user_permission_add(["blog"], "main", group="not_exist") + user_permission_update("doesnt.exist", add="alice") - res = user_permission_list()['permissions'] - assert ["alice"] == res['blog']['main']['allowed_groups'] - assert ["alice"] == res['blog']['main']['allowed_users'] -def test_disallow_bad_group_2(): - # Disallow a non existant group - with pytest.raises(YunohostError): - user_permission_remove(["blog"], "main", group="not_exist") +# Permission url management - res = user_permission_list()['permissions'] - assert ["alice"] == res['blog']['main']['allowed_groups'] - assert ["alice"] == res['blog']['main']['allowed_users'] +def test_permission_add_url(): + permission_urls("blog.main", add=[maindomain + "/testA"]) -def test_allow_bad_permission_1(): - # Allow a user to a non existant permission - with pytest.raises(YunohostError): - user_permission_add(["wiki"], "not_exit", group="alice") + res = user_permission_list(full=True)['permissions'] + assert res["blog.main"]["urls"] == [maindomain + "/testA"] -def test_allow_bad_permission_2(): - # Allow a user to a non existant permission - with pytest.raises(YunohostError): - user_permission_add(["not_exit"], "main", group="alice") +def test_permission_add_second_url(): + permission_urls("wiki.main", add=[maindomain + "/testA"]) + + res = user_permission_list(full=True)['permissions'] + assert set(res["wiki.main"]["urls"]) == set([maindomain + "/testA", maindomain + "/wiki"]) + +def test_permission_remove_url(): + permission_urls("wiki.main", remove=[maindomain + "/wiki"]) + + res = user_permission_list(full=True)['permissions'] + assert res["wiki.main"]["urls"] == [] + +def test_permission_add_url_already_added(): + res = user_permission_list(full=True)['permissions'] + assert res["wiki.main"]["urls"] == [maindomain + "/wiki"] + + permission_urls("wiki.main", add=[maindomain + "/wiki"]) + + res = user_permission_list(full=True)['permissions'] + assert res["wiki.main"]["urls"] == [maindomain + "/wiki"] + +def test_permission_remove_url_not_added(): + permission_urls("wiki.main", remove=[maindomain + "/doesnt_exist"]) + + res = user_permission_list(full=True)['permissions'] + assert res['wiki.main']['urls'] == [maindomain + "/wiki"] # # Application interaction @@ -385,42 +336,44 @@ def test_install_app(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) - res = user_permission_list()['permissions'] - assert "permissions_app" in res - assert "main" in res['permissions_app'] - assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL'] - assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL'] - assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL'] + res = user_permission_list(full=True)['permissions'] + assert "permissions_app.main" in res + assert "permissions_app.admin" in res + assert "permissions_app.dev" in res + assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] + assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] + assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] - assert ["all_users"] == res['permissions_app']['main']['allowed_groups'] - assert set(["alice", "bob"]) == set(res['permissions_app']['main']['allowed_users']) + assert res['permissions_app.main']['allowed'] == ["all_users"] + assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"]) - assert ["alice"] == res['permissions_app']['admin']['allowed_groups'] - assert ["alice"] == res['permissions_app']['admin']['allowed_users'] + assert res['permissions_app.admin']['allowed'] == ["alice"] + assert res['permissions_app.admin']['corresponding_users'] == ["alice"] - assert ["all_users"] == res['permissions_app']['dev']['allowed_groups'] - assert set(["alice", "bob"]) == set(res['permissions_app']['dev']['allowed_users']) + assert res['permissions_app.dev']['allowed'] == [] + assert set(res['permissions_app.dev']['corresponding_users']) == set() def test_remove_app(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) app_remove("permissions_app") - res = user_permission_list()['permissions'] - assert "permissions_app" not in res + # Check all permissions for this app got deleted + res = user_permission_list(full=True)['permissions'] + assert not any(p.startswith("permissions_app.") for p in res.keys()) def test_change_url(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) - res = user_permission_list()['permissions'] - assert [maindomain + "/urlpermissionapp"] == res['permissions_app']['main']['URL'] - assert [maindomain + "/urlpermissionapp/admin"] == res['permissions_app']['admin']['URL'] - assert [maindomain + "/urlpermissionapp/dev"] == res['permissions_app']['dev']['URL'] + res = user_permission_list(full=True)['permissions'] + assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] + assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] + assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] app_change_url("permissions_app", maindomain, "/newchangeurl") - res = user_permission_list()['permissions'] - assert [maindomain + "/newchangeurl"] == res['permissions_app']['main']['URL'] - assert [maindomain + "/newchangeurl/admin"] == res['permissions_app']['admin']['URL'] - assert [maindomain + "/newchangeurl/dev"] == res['permissions_app']['dev']['URL'] + res = user_permission_list(full=True)['permissions'] + assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"] + assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"] + assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"] From 2e14834e6b98ed29277c57d8f2e4ffce88b04cd8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 17:50:52 +0200 Subject: [PATCH 094/299] Misc fixes following tests --- locales/en.json | 10 +++++----- src/yunohost/app.py | 6 ++---- src/yunohost/permission.py | 6 +++--- src/yunohost/user.py | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/locales/en.json b/locales/en.json index c370f821e..7a16ebd0c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -274,9 +274,9 @@ "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", "log_dyndns_update": "Update the ip associated with your YunoHost subdomain '{}'", "log_letsencrypt_cert_install": "Install Let's encrypt certificate on '{}' domain", - "log_permission_create": "Create permission '{permission}'", - "log_permission_delete": "Delete permission '{permission}'", - "log_permission_urls": "Update urls related to permission '{permission}'", + "log_permission_create": "Create permission '{}'", + "log_permission_delete": "Delete permission '{}'", + "log_permission_urls": "Update urls related to permission '{}'", "log_selfsigned_cert_install": "Install self signed certificate on '{}' domain", "log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate", "log_regen_conf": "Regenerate system configurations '{}'", @@ -286,8 +286,8 @@ "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", "log_user_update": "Update information of '{}' user", - "log_user_permission_update": "Update accesses for permission '{permission}'", - "log_user_permission_reset": "Reset permission '{permission}'", + "log_user_permission_update": "Update accesses for permission '{}'", + "log_user_permission_reset": "Reset permission '{}'", "log_tools_maindomain": "Make '{}' as main domain", "log_tools_migrations_migrate_forward": "Migrate forward", "log_tools_postinstall": "Postinstall your YunoHost server", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f505dd088..b3c36d059 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -735,11 +735,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu if packages.dpkg_is_broken(): raise YunohostError("dpkg_is_broken") - from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import permission_create, permission_urls, permission_delete, permission_sync_to_user - ldap = _get_ldap_interface() + from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user # Fetch or extract sources if not os.path.exists(INSTALL_TMP): @@ -976,7 +974,7 @@ def app_remove(operation_logger, app): """ from yunohost.hook import hook_exec, hook_remove, hook_callback - from yunohost.permission import permission_delete, permission_sync_to_user + from yunohost.permission import user_permission_list, permission_delete, permission_sync_to_user if not _is_installed(app): raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id()) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 4d935d3c0..e5035b0ad 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -45,7 +45,7 @@ SYSTEM_PERMS = ["mail", "xmpp", "stfp"] # -def user_permission_list(short=False, full=False, ignore_system_perms=True): +def user_permission_list(short=False, full=False, ignore_system_perms=False): """ List permissions and corresponding accesses """ @@ -273,13 +273,13 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): attr_dict = { 'objectClass': ['top', 'permissionYnh', 'posixGroup'], - 'cn': permission, + 'cn': str(permission), 'gidNumber': gid, } # For main permission, we add all users by default if permission.endswith(".main"): - attr_dict['groupPermission'] = 'cn=all_users,ou=groups,dc=yunohost,dc=org' + attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org'] if urls: attr_dict['URL'] = [_normalize_url(url) for url in urls] diff --git a/src/yunohost/user.py b/src/yunohost/user.py index ef2a7d523..bb4d6aed2 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -449,7 +449,7 @@ def user_info(username): if service_status("dovecot")["status"] != "running": logger.warning(m18n.n('mailbox_used_space_dovecot_down')) - elif username not in user_permission_list()["permissions"]["mail.main"]["allowed_users"]: + elif username not in user_permission_list(full=True)["permissions"]["mail.main"]["corresponding_users"]: logger.warning(m18n.n('mailbox_disabled', user=username)) else: cmd = 'doveadm -f flow quota get -u %s' % user['uid'][0] From bdad4ffd7114602aea0303793ebdc912da054905 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 12 Sep 2019 18:34:17 +0200 Subject: [PATCH 095/299] c.f. issue 1405 ... those 'if ldap.stuff()' are complete bullshit from the very beginning since they never return False : instead they trigger an exception which means the current error management is completely meaningless ... so this refactorize all the places if found those + add proper error messages --- locales/en.json | 22 ++++---- src/yunohost/domain.py | 16 ++++-- src/yunohost/permission.py | 109 +++++++++++++++++++++---------------- src/yunohost/user.py | 109 ++++++++++++++++++++----------------- 4 files changed, 144 insertions(+), 112 deletions(-) diff --git a/locales/en.json b/locales/en.json index 7a16ebd0c..d60a432ab 100644 --- a/locales/en.json +++ b/locales/en.json @@ -164,9 +164,9 @@ "domain_cannot_remove_main": "Cannot remove main domain. Set a new main domain first", "domain_cert_gen_failed": "Unable to generate certificate", "domain_created": "The domain has been created", - "domain_creation_failed": "Unable to create domain", + "domain_creation_failed": "Failed to create domain {domain}: {error}", "domain_deleted": "The domain has been deleted", - "domain_deletion_failed": "Unable to delete domain", + "domain_deletion_failed": "Failed to delete domain {domain}: {error}", "domain_dns_conf_is_just_a_recommendation": "This command shows you what is the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.", "domain_dyndns_already_subscribed": "You've already subscribed to a DynDNS domain", "domain_dyndns_dynette_is_unreachable": "Unable to reach YunoHost dynette, either your YunoHost is not correctly connected to the internet or the dynette server is down. Error: {error}", @@ -229,14 +229,14 @@ "group_already_exist": "Group {group} already exist", "group_already_exist_on_system": "Group {group} already exists in the system group", "group_created": "Group '{group}' successfully created", - "group_creation_failed": "Group creation failed for group '{group}'", + "group_creation_failed": "Failed to create group {group}: {error}", "group_cannot_be_edited": "The group {group} cannot be edited manually.", "group_cannot_be_deleted": "The group {group} cannot be deleted manually.", "group_deleted": "Group '{group}' deleted", - "group_deletion_failed": "Group '{group} 'deletion failed", + "group_deletion_failed": "Failed to delete group {group}: {error}", "group_unknown": "Group {group} unknown", "group_updated": "Group '{group}' updated", - "group_update_failed": "Group update failed for group '{group}'", + "group_update_failed": "Failed to update group {group}: {error}", "group_user_already_in_group": "User {user} is already in group {group}", "group_user_not_in_group": "User {user} is not in group {group}", "hook_exec_failed": "Script execution failed: {path:s}", @@ -430,11 +430,11 @@ "permission_already_exist": "Permission '{permission}' already exists", "permission_cannot_remove_main": "Removing a main permission is not allowed", "permission_created": "Permission '{permission}' created", - "permission_creation_failed": "Failed to create permission '{permission}'", + "permission_creation_failed": "Failed to create permission '{permission}': {error}", "permission_deleted": "Permission '{permission}' deleted", - "permission_deletion_failed": "Failed to delete permission '{permission}'", + "permission_deletion_failed": "Failed to delete permission '{permission}': {error}", "permission_not_found": "Permission '{permission}' does not seem to exist ?", - "permission_update_failed": "Failed to update permission '{permission}'", + "permission_update_failed": "Failed to update permission '{permission}' : {error}", "permission_updated": "Permission '{permission}' updated", "permission_update_nothing_to_do": "No permissions to update", "port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections", @@ -556,13 +556,13 @@ "upnp_enabled": "UPnP has been enabled", "upnp_port_open_failed": "Unable to open UPnP ports", "user_created": "The user has been created", - "user_creation_failed": "Unable to create user", + "user_creation_failed": "Unable to create user {user}: {error}", "user_deleted": "The user has been deleted", - "user_deletion_failed": "Unable to delete user", + "user_deletion_failed": "Unable to delete user {user}: {error}", "user_home_creation_failed": "Unable to create user home folder", "user_info_failed": "Unable to retrieve user information", "user_unknown": "Unknown user: {user:s}", - "user_update_failed": "Unable to update user", + "user_update_failed": "Unable to update user {user}: {error}", "user_updated": "The user has been updated", "users_available": "Available users:", "yunohost_already_installed": "YunoHost is already installed", diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 42a4881ba..3f906748b 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -112,8 +112,10 @@ def domain_add(operation_logger, domain, dyndns=False): 'virtualdomain': domain, } - if not ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict): - raise YunohostError('domain_creation_failed') + try: + ldap.add('virtualdomain=%s,ou=domains' % domain, attr_dict) + except Exception as e: + raise YunohostError('domain_creation_failed', domain=domain, error=e) # Don't regen these conf if we're still in postinstall if os.path.exists('/etc/yunohost/installed'): @@ -167,10 +169,12 @@ def domain_remove(operation_logger, domain, force=False): operation_logger.start() ldap = _get_ldap_interface() - if ldap.remove('virtualdomain=' + domain + ',ou=domains') or force: - os.system('rm -rf /etc/yunohost/certs/%s' % domain) - else: - raise YunohostError('domain_deletion_failed') + try: + ldap.remove('virtualdomain=' + domain + ',ou=domains') + except Exception as e: + raise YunohostError('domain_deletion_failed', domain=domain, error=e) + + os.system('rm -rf /etc/yunohost/certs/%s' % domain) regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix']) app_ssowatconf() diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index e5035b0ad..984a5d902 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -153,36 +153,37 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, operation_logger.start() - if ldap.update('cn=%s,ou=permission' % permission, - {'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]}): - logger.debug(m18n.n('permission_updated', permission=permission)) + try: + ldap.update('cn=%s,ou=permission' % permission, + {'groupPermission': ['cn=' + g + ',ou=groups,dc=yunohost,dc=org' for g in new_allowed_groups]}) + except Exception as e: + raise YunohostError('permission_update_failed', permission=permission, error=e) - # Trigger permission sync if asked + logger.debug(m18n.n('permission_updated', permission=permission)) - if sync_perm: - permission_sync_to_user() + # Trigger permission sync if asked - new_permission = user_permission_list(full=True)["permissions"][permission] + if sync_perm: + permission_sync_to_user() - # Trigger app callbacks + new_permission = user_permission_list(full=True)["permissions"][permission] - app = permission.split(".")[0] + # Trigger app callbacks - old_allowed_users = set(existing_permission["corresponding_users"]) - new_allowed_users = set(new_permission["corresponding_users"]) + app = permission.split(".")[0] - effectively_added_users = new_allowed_users - old_allowed_users - effectively_removed_users = old_allowed_users - new_allowed_users + old_allowed_users = set(existing_permission["corresponding_users"]) + new_allowed_users = set(new_permission["corresponding_users"]) - if effectively_added_users: - hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)]) - if effectively_removed_users: - hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)]) + effectively_added_users = new_allowed_users - old_allowed_users + effectively_removed_users = old_allowed_users - new_allowed_users - return new_permission + if effectively_added_users: + hook_callback('post_app_addaccess', args=[app, ','.join(effectively_added_users)]) + if effectively_removed_users: + hook_callback('post_app_removeaccess', args=[app, ','.join(effectively_removed_users)]) - else: - raise YunohostError('permission_update_failed', permission=permission) + return new_permission @is_unit_operation() @@ -209,10 +210,12 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): operation_logger.start() default_permission = {'groupPermission': ['cn=all_users,ou=groups,dc=yunohost,dc=org']} - if ldap.update('cn=%s,ou=permission' % permission, default_permission): - logger.debug(m18n.n('permission_updated', permission=permission)) - else: - raise YunohostError('permission_update_failed', permission=permission) + try: + ldap.update('cn=%s,ou=permission' % permission, default_permission) + except Exception as e: + raise YunohostError('permission_update_failed', permission=permission, error=e) + + logger.debug(m18n.n('permission_updated', permission=permission)) if sync_perm: permission_sync_to_user() @@ -286,13 +289,17 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() - if ldap.add('cn=%s,ou=permission' % permission, attr_dict): - if sync_perm: - permission_sync_to_user() - logger.debug(m18n.n('permission_created', permission=permission)) - return user_permission_list(full=True)["permissions"][permission] - else: - raise YunohostError('permission_creation_failed') + + try: + ldap.add('cn=%s,ou=permission' % permission, attr_dict) + except Exception as e: + raise YunohostError('permission_creation_failed', permission=permission, error=e) + + if sync_perm: + permission_sync_to_user() + + logger.debug(m18n.n('permission_created', permission=permission)) + return user_permission_list(full=True)["permissions"][permission] @is_unit_operation() @@ -336,13 +343,17 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() - if ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls}): - if sync_perm: - permission_sync_to_user() - logger.debug(m18n.n('permission_updated', permission=permission)) - return user_permission_list(full=True)["permissions"][permission] - else: - raise YunohostError('permission_update_failed', permission=permission) + + try: + ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls}) + except Exception as e: + raise YunohostError('permission_update_failed', permission=permission, error=e) + + if sync_perm: + permission_sync_to_user() + + logger.debug(m18n.n('permission_updated', permission=permission)) + return user_permission_list(full=True)["permissions"][permission] @is_unit_operation() @@ -370,12 +381,15 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True) operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() - if ldap.remove('cn=%s,ou=permission' % permission): - if sync_perm: - permission_sync_to_user() - logger.debug(m18n.n('permission_deleted', permission=permission)) - else: - raise YunohostError('permission_deletion_failed', permission=permission) + + try: + ldap.remove('cn=%s,ou=permission' % permission) + except Exception as e: + raise YunohostError('permission_deletion_failed', permission=permission, error=e) + + if sync_perm: + permission_sync_to_user() + logger.debug(m18n.n('permission_deleted', permission=permission)) def permission_sync_to_user(): @@ -410,8 +424,10 @@ def permission_sync_to_user(): 'memberUid': should_be_allowed_users} # Commit the change with the new inherited stuff - if not ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms): - raise YunohostError('permission_update_failed', permission=permission_name) + try: + ldap.update('cn=%s,ou=permission' % permission_name, new_inherited_perms) + except Exception as e: + raise YunohostError('permission_update_failed', permission=permission_name, error=e) logger.debug("The permission database has been resynchronized") @@ -421,6 +437,7 @@ def permission_sync_to_user(): os.system('nscd --invalidate=passwd') os.system('nscd --invalidate=group') + def _normalize_url(url): from yunohost.domain import _normalize_domain_path domain = url[:url.index('/')] diff --git a/src/yunohost/user.py b/src/yunohost/user.py index bb4d6aed2..cfb34e44e 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -205,32 +205,34 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, except IOError as e: raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) - if ldap.add('uid=%s,ou=users' % username, attr_dict): - # Invalidate passwd to take user creation into account - subprocess.call(['nscd', '-i', 'passwd']) + try: + ldap.add('uid=%s,ou=users' % username, attr_dict) + except Exception as e: + raise YunohostError('user_creation_failed', user=username, error=e) - try: - # Attempt to create user home folder - subprocess.check_call( - ['su', '-', username, '-c', "''"]) - except subprocess.CalledProcessError: - if not os.path.isdir('/home/{0}'.format(username)): - logger.warning(m18n.n('user_home_creation_failed'), - exc_info=1) + # Invalidate passwd to take user creation into account + subprocess.call(['nscd', '-i', 'passwd']) - # Create group for user and add to group 'all_users' - user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False) - user_group_update(groupname='all_users', add=username, force=True, sync_perm=True) + try: + # Attempt to create user home folder + subprocess.check_call( + ['su', '-', username, '-c', "''"]) + except subprocess.CalledProcessError: + if not os.path.isdir('/home/{0}'.format(username)): + logger.warning(m18n.n('user_home_creation_failed'), + exc_info=1) - # TODO: Send a welcome mail to user - logger.success(m18n.n('user_created')) + # Create group for user and add to group 'all_users' + user_group_create(groupname=username, gid=uid, primary_group=True, sync_perm=False) + user_group_update(groupname='all_users', add=username, force=True, sync_perm=True) - hook_callback('post_user_create', - args=[username, mail, password, firstname, lastname]) + # TODO: Send a welcome mail to user + logger.success(m18n.n('user_created')) - return {'fullname': fullname, 'username': username, 'mail': mail} + hook_callback('post_user_create', + args=[username, mail, password, firstname, lastname]) - raise YunohostError('user_creation_failed') + return {'fullname': fullname, 'username': username, 'mail': mail} @is_unit_operation([('username', 'user')]) @@ -258,15 +260,17 @@ def user_delete(operation_logger, username, purge=False): user_group_delete(username, force=True, sync_perm=True) ldap = _get_ldap_interface() - if ldap.remove('uid=%s,ou=users' % username): - # Invalidate passwd to take user deletion into account - subprocess.call(['nscd', '-i', 'passwd']) + try: + ldap.remove('uid=%s,ou=users' % username) + except Exception as e: + raise YunohostError('user_deletion_failed', user=username, error=e) - if purge: - subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) - subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)]) - else: - raise YunohostError('user_deletion_failed') + # Invalidate passwd to take user deletion into account + subprocess.call(['nscd', '-i', 'passwd']) + + if purge: + subprocess.call(['rm', '-rf', '/home/{0}'.format(username)]) + subprocess.call(['rm', '-rf', '/var/mail/{0}'.format(username)]) hook_callback('post_user_delete', args=[username, purge]) @@ -387,12 +391,14 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail= operation_logger.start() - if ldap.update('uid=%s,ou=users' % username, new_attr_dict): - logger.success(m18n.n('user_updated')) - app_ssowatconf() - return user_info(username) - else: - raise YunohostError('user_update_failed') + try: + ldap.update('uid=%s,ou=users' % username, new_attr_dict) + except Exception as e: + raise YunohostError('user_update_failed', user=username, error=e) + + logger.success(m18n.n('user_updated')) + app_ssowatconf() + return user_info(username) def user_info(username): @@ -476,10 +482,7 @@ def user_info(username): 'use': storage_use } - if result: - return result_dict - else: - raise YunohostError('user_info_failed') + return result_dict # @@ -569,13 +572,16 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False attr_dict["member"] = ["uid=" + groupname + ",ou=users,dc=yunohost,dc=org"] operation_logger.start() - if ldap.add('cn=%s,ou=groups' % groupname, attr_dict): - logger.success(m18n.n('group_created', group=groupname)) - if sync_perm: - permission_sync_to_user() - return {'name': groupname} + try: + ldap.add('cn=%s,ou=groups' % groupname, attr_dict) + except Exception as e: + raise YunohostError('group_creation_failed', group=groupname, error=e) - raise YunohostError('group_creation_failed', group=groupname) + if sync_perm: + permission_sync_to_user() + + logger.success(m18n.n('group_created', group=groupname)) + return {'name': groupname} @is_unit_operation([('groupname', 'group')]) @@ -601,13 +607,16 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): operation_logger.start() ldap = _get_ldap_interface() - if not ldap.remove('cn=%s,ou=groups' % groupname): - raise YunohostError('group_deletion_failed', group=groupname) + try: + ldap.remove('cn=%s,ou=groups' % groupname) + except Exception as e: + raise YunohostError('group_deletion_failed', group=groupname, error=e) - logger.success(m18n.n('group_deleted', group=groupname)) if sync_perm: permission_sync_to_user() + logger.success(m18n.n('group_deleted', group=groupname)) + @is_unit_operation([('groupname', 'group')]) def user_group_update(operation_logger, groupname, add=None, remove=None, force=False, sync_perm=True): @@ -668,8 +677,10 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= if set(new_group) != set(current_group): operation_logger.start() ldap = _get_ldap_interface() - if not ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)}): - raise YunohostError('group_update_failed', group=groupname) + try: + ldap.update('cn=%s,ou=groups' % groupname, {"member": set(new_group_dns), "memberUid": set(new_group)}) + except Exception as e: + raise YunohostError('group_update_failed', group=groupname, error=e) logger.success(m18n.n('group_updated', group=groupname)) if sync_perm: From ec5069b71cd4765c028c593c3cd02da236e0ae2c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 15:35:27 +0200 Subject: [PATCH 096/299] Propagate changes on backup tests + fixes bugs found in the process --- data/hooks/restore/21-conf_ynh_certs | 1 - src/yunohost/backup.py | 15 ++++++------ .../0011_setup_group_permission.py | 5 ++++ src/yunohost/tests/test_backuprestore.py | 23 +++++++++---------- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/data/hooks/restore/21-conf_ynh_certs b/data/hooks/restore/21-conf_ynh_certs index d1eb532ed..34e651319 100644 --- a/data/hooks/restore/21-conf_ynh_certs +++ b/data/hooks/restore/21-conf_ynh_certs @@ -3,6 +3,5 @@ backup_dir="$1/conf/ynh/certs" sudo mkdir -p /etc/yunohost/certs/ sudo cp -a $backup_dir/. /etc/yunohost/certs/ -sudo yunohost app ssowatconf sudo service nginx reload sudo service metronome reload diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index f1ac7ee9c..de2b3f76d 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1134,6 +1134,8 @@ class RestoreManager(): self._restore_system() self._restore_apps() + except Exception as e: + logger.error("The following critical error happened during restoration: %s" % e) finally: self.clean() @@ -1186,11 +1188,12 @@ class RestoreManager(): if system_targets == []: return - from yunohost.permission import permission_create, user_permission_update, user_permission_list + from yunohost.user import user_group_list + from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list # Backup old permission for apps # We need to do that because in case of an app is installed we can't remove the permission for this app - old_apps_permission = user_permission_list(ignore_system_perms=True)["permissions"] + old_apps_permission = user_permission_list(ignore_system_perms=True, full=True)["permissions"] # Start register change on system operation_logger = OperationLogger('backup_restore_system') @@ -1232,7 +1235,7 @@ class RestoreManager(): # do the migration 0011 : setup group and permission # # Legacy code - if not user_group_list["groups"]: + if not "all_users" in user_group_list()["groups"].keys(): from yunohost.tools import _get_migration_by_name setup_group_permission = _get_migration_by_name("setup_group_permission") # Update LDAP schema restart slapd @@ -1251,14 +1254,12 @@ class RestoreManager(): permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False) user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"]) - def _restore_apps(self): """Restore all apps targeted""" apps_targets = self.targets.list("apps", exclude=["Skipped"]) for app in apps_targets: - print(app) self._restore_app(app) def _restore_app(self, app_instance_name): @@ -1359,11 +1360,11 @@ class RestoreManager(): permissions = read_yaml('%s/permissions.yml' % app_settings_new_path) existing_groups = user_group_list()['groups'] - for permission_name, permission_infos in permissions: + for permission_name, permission_infos in permissions.items(): permission_create(permission_name, urls=permission_infos.get("urls", [])) - if "allowed" not in permissions_infos: + if "allowed" not in permission_infos: logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s ... You might need to reconfigure permissions yourself!" % (permission_name, app_instance_name)) else: groups = [g for g in permission_infos["allowed"] if g in existing_groups] diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 720e4ac36..109757bcc 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -89,6 +89,11 @@ class MyMigration(Migration): app_setting(app, 'allowed_users', delete=True) def run(self): + + # FIXME : what do we really want to do here ... + # Imho we should just force-regen the conf in all case, and maybe + # just display a warning if we detect that the conf was manually modified + # Check if the migration can be processed ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) # By this we check if the have been customized diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index 7d384a46a..d2fd03799 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -38,10 +38,10 @@ def setup_function(function): add_archive_wordpress_from_2p4() assert len(backup_list()["archives"]) == 1 - if "with_backup_legacy_app_installed" in markers: - assert not app_is_installed("backup_legacy_app") - install_app("backup_legacy_app_ynh", "/yolo") - assert app_is_installed("backup_legacy_app") + if "with_legacy_app_installed" in markers: + assert not app_is_installed("legacy_app") + install_app("legacy_app_ynh", "/yolo") + assert app_is_installed("legacy_app") if "with_backup_recommended_app_installed" in markers: assert not app_is_installed("backup_recommended_app") @@ -105,7 +105,7 @@ def backup_test_dependencies_are_met(): # Dummy test apps (or backup archives) assert os.path.exists("./tests/apps/backup_wordpress_from_2p4") - assert os.path.exists("./tests/apps/backup_legacy_app_ynh") + assert os.path.exists("./tests/apps/legacy_app_ynh") assert os.path.exists("./tests/apps/backup_recommended_app_ynh") return True @@ -155,8 +155,8 @@ def delete_all_backups(): def uninstall_test_apps_if_needed(): - if _is_installed("backup_legacy_app"): - app_remove("backup_legacy_app") + if _is_installed("legacy_app"): + app_remove("legacy_app") if _is_installed("backup_recommended_app"): app_remove("backup_recommended_app") @@ -497,10 +497,10 @@ def test_restore_app_already_installed(mocker): assert _is_installed("wordpress") -@pytest.mark.with_backup_legacy_app_installed +@pytest.mark.with_legacy_app_installed def test_backup_and_restore_legacy_app(): - _test_backup_and_restore_app("backup_legacy_app") + _test_backup_and_restore_app("legacy_app") @pytest.mark.with_backup_recommended_app_installed @@ -531,7 +531,7 @@ def _test_backup_and_restore_app(app): # Uninstall the app app_remove(app) assert not app_is_installed(app) - assert app not in user_permission_list()['permissions'] + assert app+".main" not in user_permission_list()['permissions'] # Restore the app backup_restore(system=None, name=archives[0], @@ -541,8 +541,7 @@ def _test_backup_and_restore_app(app): # Check permission per_list = user_permission_list()['permissions'] - assert app in per_list - assert "main" in per_list[app] + assert app+".main" in per_list # # Some edge cases # From ccc7583ec4e909e4c2d06511f32310e6d3abba6c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 16:02:02 +0200 Subject: [PATCH 097/299] Add backup/restore test for permission app, and fix a small related bug --- src/yunohost/backup.py | 6 ++- src/yunohost/tests/test_backuprestore.py | 55 ++++++++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index de2b3f76d..420c2d4f8 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1367,8 +1367,10 @@ class RestoreManager(): if "allowed" not in permission_infos: logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s ... You might need to reconfigure permissions yourself!" % (permission_name, app_instance_name)) else: - groups = [g for g in permission_infos["allowed"] if g in existing_groups] - user_permission_update(permission_name, remove="all_users", add=groups) + should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups] + current_allowed = user_permission_list()["permissions"][permission_name]["allowed"] + if should_be_allowed != current_allowed: + user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed) os.remove('%s/permissions.yml' % app_settings_new_path) else: diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index d2fd03799..bdaf25299 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -10,7 +10,7 @@ from yunohost.app import _is_installed from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError -from yunohost.user import user_permission_list +from yunohost.user import user_permission_list, user_create, user_list, user_delete from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps # Get main domain @@ -59,6 +59,13 @@ def setup_function(function): add_archive_system_from_2p4() assert len(backup_list()["archives"]) == 1 + if "with_permission_app_installed" in markers: + assert not app_is_installed("permissions_app") + user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") + install_app("permissions_app_ynh", "/urlpermissionapp" + "&admin=alice") + assert app_is_installed("permissions_app") + def teardown_function(function): @@ -73,6 +80,9 @@ def teardown_function(function): if "clean_opt_dir" in markers: shutil.rmtree("/opt/test_backup_output_directory") + if "alice" in user_list()["users"]: + user_delete("alice") + @pytest.fixture(autouse=True) def check_LDAP_db_integrity_call(): @@ -92,6 +102,9 @@ def check_permission_for_apps_call(): def app_is_installed(app): + if app == "permissions_app": + return _is_installed(app) + # These are files we know should be installed by the app app_files = [] app_files.append("/etc/nginx/conf.d/%s.d/%s.conf" % (maindomain, app)) @@ -155,14 +168,9 @@ def delete_all_backups(): def uninstall_test_apps_if_needed(): - if _is_installed("legacy_app"): - app_remove("legacy_app") - - if _is_installed("backup_recommended_app"): - app_remove("backup_recommended_app") - - if _is_installed("wordpress"): - app_remove("wordpress") + for app in ["legacy_app", "backup_recommended_app", "wordpress", "permissions_app"]: + if _is_installed(app): + app_remove(app) def install_app(app, path, additionnal_args=""): @@ -514,6 +522,35 @@ def test_backup_and_restore_with_ynh_restore(): _test_backup_and_restore_app("backup_recommended_app") +@pytest.mark.with_permission_app_installed +def test_backup_and_restore_permission_app(): + + res = user_permission_list(full=True)['permissions'] + assert "permissions_app.main" in res + assert "permissions_app.admin" in res + assert "permissions_app.dev" in res + assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] + assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] + assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + + assert res['permissions_app.main']['allowed'] == ["all_users"] + assert res['permissions_app.admin']['allowed'] == ["alice"] + assert res['permissions_app.dev']['allowed'] == [] + + _test_backup_and_restore_app("permissions_app") + + res = user_permission_list(full=True)['permissions'] + assert "permissions_app.main" in res + assert "permissions_app.admin" in res + assert "permissions_app.dev" in res + assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] + assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] + assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + + assert res['permissions_app.main']['allowed'] == ["all_users"] + assert res['permissions_app.admin']['allowed'] == ["alice"] + assert res['permissions_app.dev']['allowed'] == [] + def _test_backup_and_restore_app(app): From 302e755f48f853cf1362eb577885c990cc434ca7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 16:50:46 +0200 Subject: [PATCH 098/299] Assume we target the .main permission if it's not given explicitly --- data/actionsmap/yunohost.yml | 4 ++-- src/yunohost/permission.py | 32 ++++++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 49dde373b..05f0de048 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -298,7 +298,7 @@ user: api: POST /users/permissions/ arguments: permission: - help: Permission to manage (e.g. mail.main or wordpress.editors) + help: Permission to manage (e.g. mail or nextcloud or wordpress.editors) -a: full: --add help: Group or user names to add to this permission @@ -320,7 +320,7 @@ user: api: DELETE /users/permissions/ arguments: permission: - help: Permission to be resetted (e.g. mail.main or wordpress.editors) + help: Permission to manage (e.g. mail or nextcloud or wordpress.editors) ssh: subcategory_help: Manage ssh access diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 984a5d902..1472f4b88 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -87,15 +87,19 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, Allow or Disallow a user or group to a permission for a specific application Keyword argument: - permission -- Name of the permission (e.g. mail.mail or wordpress.editors) + permission -- Name of the permission (e.g. mail or or wordpress or wordpress.editors) add -- List of groups or usernames to add to this permission remove -- List of groups or usernames to remove from to this permission """ from yunohost.hook import hook_callback from yunohost.user import user_group_list - from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract + from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() + # By default, manipulate main permission + if "." not in permission: + permission = permission + ".main" + # Fetch currently allowed groups for this permission existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) @@ -146,7 +150,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): # FIXME : i18n - logger.warning("No change was applied because not relevant modification were found") + logger.warning("The permission was not updated all addition/removal requests already match the current state.") return # Commit the new allowed group list @@ -192,12 +196,16 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): Reset a given permission to just 'all_users' Keyword argument: - permission -- The name of the permission to be reseted + permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) """ from yunohost.hook import hook_callback from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() + # By default, manipulate main permission + if "." not in permission: + permission = permission + ".main" + # Fetch existing permission existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) @@ -254,13 +262,17 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): Create a new permission for a specific application Keyword argument: - permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) + permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) urls -- list of urls to specify for the permission """ from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() + # By default, manipulate main permission + if "." not in permission: + permission = permission + ".main" + # Validate uniqueness of permission in LDAP if ldap.get_conflict({'cn': permission}, base_dn='ou=permission,dc=yunohost,dc=org'): @@ -308,7 +320,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe Update urls related to a permission for a specific application Keyword argument: - permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) + permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) add -- List of urls to add remove -- List of urls to remove @@ -362,10 +374,14 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True) Delete a permission Keyword argument: - permission -- Name of the permission (e.g. nextcloud.main or wordpress.editors) + permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) """ - if permission.endswith("main") and not force: + # By default, manipulate main permission + if "." not in permission: + permission = permission + ".main" + + if permission.endswith(".main") and not force: raise YunohostError('permission_cannot_remove_main') from yunohost.utils.ldap import _get_ldap_interface From f950378c63aec6a2536524e3e02c3cfda86a09b9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 17:39:21 +0200 Subject: [PATCH 099/299] Do not display primary groups by default when running yunohost user group list --- data/actionsmap/yunohost.yml | 5 +++++ src/yunohost/user.py | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 05f0de048..1f6966f65 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -216,6 +216,11 @@ user: full: --full help: Display all informations known about each groups action: store_true + -p: + full: --include-primary-groups + help: Also display primary groups (each user has an eponym group that only contains itself) + action: store_true + default: false ### user_group_create() create: diff --git a/src/yunohost/user.py b/src/yunohost/user.py index cfb34e44e..4fe8db420 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -488,13 +488,17 @@ def user_info(username): # # Group subcategory # -def user_group_list(short=False, full=False): +def user_group_list(short=False, full=False, include_primary_groups=True): """ List users Keyword argument: short -- Only list the name of the groups without any additional info full -- List all the info available for each groups + include_primary_groups -- Include groups corresponding to users (which should always only contains this user) + This option is set to false by default in the action map because we don't want to have + these displayed when the user runs `yunohost user group list`, but internally we do want + to list them when called from other functions """ # Fetch relevant informations @@ -507,10 +511,15 @@ def user_group_list(short=False, full=False): # Parse / organize information to be outputed + users = user_list()["users"] groups = {} for infos in groups_infos: name = infos["cn"][0] + + if not include_primary_groups and name in users: + continue + groups[name] = {} groups[name]["members"] = [_ldap_path_extract(p, "uid") for p in infos.get("member", [])] From ea8c0cae9431bd294f838448eb297d84ee6fd5a4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 18:34:26 +0200 Subject: [PATCH 100/299] Deprecate legacy app access system --- data/actionsmap/yunohost.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 1f6966f65..e51f23a14 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -792,6 +792,7 @@ app: addaccess: action_help: Grant access right to users (everyone by default) api: PUT /access + deprecated: true arguments: apps: nargs: "+" @@ -803,6 +804,7 @@ app: removeaccess: action_help: Revoke access right to users (everyone by default) api: DELETE /access + deprecated: true arguments: apps: nargs: "+" @@ -814,6 +816,7 @@ app: clearaccess: action_help: Reset access rights for the app api: POST /access + deprecated: true arguments: apps: nargs: "+" From b995b3254d95008f51591a76d8bbbcfab7bc260c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 18:41:05 +0200 Subject: [PATCH 101/299] Remove some unecessary messages when handling primary groups and all_users --- src/yunohost/user.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 4fe8db420..22bd6d3cf 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -589,7 +589,11 @@ def user_group_create(operation_logger, groupname, gid=None, primary_group=False if sync_perm: permission_sync_to_user() - logger.success(m18n.n('group_created', group=groupname)) + if not primary_group: + logger.success(m18n.n('group_created', group=groupname)) + else: + logger.debug(m18n.n('group_created', group=groupname)) + return {'name': groupname} @@ -624,7 +628,10 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): if sync_perm: permission_sync_to_user() - logger.success(m18n.n('group_deleted', group=groupname)) + if groupname not in existing_users: + logger.success(m18n.n('group_deleted', group=groupname)) + else: + logger.debug(m18n.n('group_deleted', group=groupname)) @is_unit_operation([('groupname', 'group')]) @@ -691,7 +698,11 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= except Exception as e: raise YunohostError('group_update_failed', group=groupname, error=e) - logger.success(m18n.n('group_updated', group=groupname)) + if groupname != "all_users": + logger.success(m18n.n('group_updated', group=groupname)) + else: + logger.debug(m18n.n('group_updated', group=groupname)) + if sync_perm: permission_sync_to_user() return user_group_info(groupname) From 732f8987738bfb585de14a0922d374e7c2c616e4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 19:42:15 +0200 Subject: [PATCH 102/299] Small issue when deleting the user --- src/yunohost/user.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 22bd6d3cf..fbd15018c 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -253,6 +253,8 @@ def user_delete(operation_logger, username, purge=False): user_group_update("all_users", remove=username, force=True, sync_perm=False) for group, infos in user_group_list()["groups"].items(): + if group == "all_users": + continue # If the user is in this group (and it's not the primary group), # remove the member from the group if username != group and username in infos["members"]: From 63fa54171de033a1fe82612a97511e0579d3eff1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 20:13:44 +0200 Subject: [PATCH 103/299] Ugh we really need to make this raise an exception ... --- src/yunohost/backup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 420c2d4f8..305152865 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1135,7 +1135,7 @@ class RestoreManager(): self._restore_system() self._restore_apps() except Exception as e: - logger.error("The following critical error happened during restoration: %s" % e) + raise YunohostError("The following critical error happened during restoration: %s" % e) finally: self.clean() @@ -1245,7 +1245,7 @@ class RestoreManager(): # Remove all permission for all app which is still in the LDAP for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys(): - permission_delete(permission_name) + permission_delete(permission_name, force=True) # Restore permission for the app which is installed for permission_name, permission_infos in old_apps_permission.items(): From 3df6ce17b6b13f1fe784a3bb9e5b782fc541eb2a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 20:34:30 +0200 Subject: [PATCH 104/299] Properly handle all those errors >.> ... --- locales/en.json | 1 + src/yunohost/tests/test_permission.py | 3 +-- src/yunohost/tests/test_user-group.py | 17 ++++++++------ src/yunohost/user.py | 33 +++++++++++++++++++++------ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/locales/en.json b/locales/en.json index d60a432ab..c48532ed7 100644 --- a/locales/en.json +++ b/locales/en.json @@ -555,6 +555,7 @@ "upnp_disabled": "UPnP has been disabled", "upnp_enabled": "UPnP has been enabled", "upnp_port_open_failed": "Unable to open UPnP ports", + "user_already_exists": "User {user} already exists", "user_created": "The user has been created", "user_creation_failed": "Unable to create user {user}: {error}", "user_deleted": "The user has been deleted", diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 2df6362a9..8db1ae825 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -1,6 +1,5 @@ import pytest -from moulinette.core import MoulinetteError from yunohost.app import app_install, app_remove, app_change_url, app_list from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ @@ -211,7 +210,7 @@ def test_permission_create_already_existing(): permission_create("wiki.main") def test_permission_delete_doesnt_existing(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): permission_delete("doesnt.exist", force=True) res = user_permission_list()['permissions'] diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 88644c3e6..30bdeb017 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -1,6 +1,5 @@ import pytest -from moulinette.core import MoulinetteError from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info from yunohost.domain import _get_maindomain @@ -102,19 +101,23 @@ def test_del_group(): # def test_create_user_with_mail_address_already_taken(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh") def test_create_user_with_password_too_simple(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_create("other", "Alice", "White", "other@" + maindomain, "12") def test_create_user_already_exists(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh") +def test_update_user_with_mail_address_already_taken(): + with pytest.raises(YunohostError): + user_update("bob", add_mailalias="alice@" + maindomain) + def test_del_user_that_does_not_exist(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_delete("doesnt_exist") def test_create_group_all_users(): @@ -124,7 +127,7 @@ def test_create_group_all_users(): def test_create_group_already_exists(): # Check groups already exist (regular groups) - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_group_create("dev") def test_del_group_all_users(): @@ -132,7 +135,7 @@ def test_del_group_all_users(): user_group_delete("all_users") def test_del_group_that_does_not_exist(): - with pytest.raises(MoulinetteError): + with pytest.raises(YunohostError): user_group_delete("doesnt_exist") # diff --git a/src/yunohost/user.py b/src/yunohost/user.py index fbd15018c..c6413d7e1 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -127,12 +127,18 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, ldap = _get_ldap_interface() + if username in user_list()["users"]: + raise YunohostError("user_already_exists", user=username) + # Validate uniqueness of username and mail in LDAP - ldap.validate_uniqueness({ - 'uid': username, - 'mail': mail, - 'cn': username - }) + try: + ldap.validate_uniqueness({ + 'uid': username, + 'mail': mail, + 'cn': username + }) + except Exception as e: + raise YunohostError('user_creation_failed', user=username, error=e) # Validate uniqueness of username in system users all_existing_usernames = {x.pw_name for x in pwd.getpwall()} @@ -249,6 +255,9 @@ def user_delete(operation_logger, username, purge=False): from yunohost.utils.ldap import _get_ldap_interface from yunohost.permission import permission_sync_to_user + if username not in user_list()["users"]: + raise YunohostError('user_unknown', user=username) + operation_logger.start() user_group_update("all_users", remove=username, force=True, sync_perm=False) @@ -340,7 +349,10 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail= 'webmaster@' + main_domain, 'postmaster@' + main_domain, ] - ldap.validate_uniqueness({'mail': mail}) + try: + ldap.validate_uniqueness({'mail': mail}) + except Exception as e: + raise YunohostError('user_update_failed', user=username, error=e) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) if mail in aliases: @@ -353,7 +365,10 @@ def user_update(operation_logger, username, firstname=None, lastname=None, mail= if not isinstance(add_mailalias, list): add_mailalias = [add_mailalias] for mail in add_mailalias: - ldap.validate_uniqueness({'mail': mail}) + try: + ldap.validate_uniqueness({'mail': mail}) + except Exception as e: + raise YunohostError('user_update_failed', user=username, error=e) if mail[mail.find('@') + 1:] not in domains: raise YunohostError('mail_domain_unknown', domain=mail[mail.find('@') + 1:]) user['mail'].append(mail) @@ -611,6 +626,10 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface + existing_groups = user_group_list()['groups'].keys() + if groupname not in existing_groups: + raise YunohostError('group_unknown', group=groupname) + # Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam') # without the force option... # From 094a2afe1a7a5eb21c9fdcb51ab79de05c9589a7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 13 Sep 2019 22:45:31 +0200 Subject: [PATCH 105/299] Simplify permission handling in app_map + add tests for it --- src/yunohost/app.py | 10 +++------- src/yunohost/tests/test_permission.py | 15 +++++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index b3c36d059..ab290cb4d 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -406,10 +406,10 @@ def app_map(app=None, raw=False, user=None): """ from yunohost.permission import user_permission_list - from yunohost.utils.ldap import _get_ldap_interface apps = [] result = {} + permissions = user_permission_list(full=True)["permissions"] if app is not None: if not _is_installed(app): @@ -429,12 +429,8 @@ def app_map(app=None, raw=False, user=None): continue if 'no_sso' in app_settings: # I don't think we need to check for the value here continue - if user is not None: - ldap = _get_ldap_interface() - if not ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=%s.main)(inheritPermission=uid=%s,ou=users,dc=yunohost,dc=org))' % (app_id, user), - attrs=['cn']): - continue + if user and user not in permissions[app_id + ".main"]["corresponding_users"]: + continue domain = app_settings['domain'] path = app_settings['path'] diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 8db1ae825..94728505d 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -1,6 +1,6 @@ import pytest -from yunohost.app import app_install, app_remove, app_change_url, app_list +from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info @@ -331,7 +331,7 @@ def test_permission_remove_url_not_added(): # Application interaction # -def test_install_app(): +def test_permission_app_install(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) @@ -352,7 +352,14 @@ def test_install_app(): assert res['permissions_app.dev']['allowed'] == [] assert set(res['permissions_app.dev']['corresponding_users']) == set() -def test_remove_app(): + # Check that we get the right stuff in app_map, which is used to generate the ssowatconf + assert maindomain + "/urlpermissionapp" in app_map(user="alice").keys() + user_permission_update("permissions_app.main", remove="all_users", add="bob") + assert maindomain + "/urlpermissionapp" not in app_map(user="alice").keys() + assert maindomain + "/urlpermissionapp" in app_map(user="bob").keys() + + +def test_permission_app_remove(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) app_remove("permissions_app") @@ -361,7 +368,7 @@ def test_remove_app(): res = user_permission_list(full=True)['permissions'] assert not any(p.startswith("permissions_app.") for p in res.keys()) -def test_change_url(): +def test_permission_app_change_url(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) From 9c383ef06a106505d3f726276d6d2cfba4e4cc41 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 14 Sep 2019 18:21:42 +0200 Subject: [PATCH 106/299] Make migration more robust to re-runs --- locales/en.json | 2 +- .../0011_setup_group_permission.py | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/locales/en.json b/locales/en.json index c48532ed7..ae349edf3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -354,7 +354,6 @@ "migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user...", "migration_0011_done": "Migration successful. You are now able to manage groups of users.", - "migration_0011_error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration need to be updated.\nYou need to save your actual configuration, reintialize the original configuration by the command 'yunohost tools regen-conf -f' and after retry the migration", "migration_0011_LDAP_update_failed": "LDAP update failed. Error: {error:s}", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...", @@ -362,6 +361,7 @@ "migration_0011_rollback_success": "Rollback succeeded.", "migration_0011_update_LDAP_database": "Updating LDAP database...", "migration_0011_update_LDAP_schema": "Updating LDAP schema...", + "migration_0011_failed_to_remove_stale_object": "Failed to remove stale object {dn}: {error}", "migrations_already_ran": "Those migrations have already been ran: {ids}", "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", "migrations_dependencies_not_satisfied": "Can't run migration {id} because first you need to run these migrations: {dependencies_id}", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 109757bcc..8949239e0 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -28,6 +28,28 @@ class MyMigration(Migration): required = True + def remove_if_exists(self, target): + + from yunohost.utils.ldap import _get_ldap_interface + ldap = _get_ldap_interface() + + try: + objects = ldap.search(target + ",dc=yunohost,dc=org") + # ldap search will raise an exception if no corresponding object is found >.> ... + except Exception as e: + logger.debug("%s does not exist, no need to delete it" % target) + return + + objects.reverse() + for o in objects: + for dn in o["dn"]: + dn = dn.replace(",dc=yunohost,dc=org", "") + logger.debug("Deleting old object %s ..." % dn) + try: + ldap.remove(dn) + except Exception as e: + raise YunohostError("migration_0011_failed_to_remove_stale_object", dn=dn, error=e) + def migrate_LDAP_db(self): logger.info(m18n.n("migration_0011_update_LDAP_database")) @@ -35,14 +57,13 @@ class MyMigration(Migration): from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() - try: - ldap.remove('cn=sftpusers,ou=groups') - except: - logger.warn(m18n.n("migration_0011_error_when_removing_sftpuser_group")) - ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') try: + self.remove_if_exists("cn=sftpusers,ou=groups") + self.remove_if_exists("ou=permission") + self.remove_if_exists('cn=all_users,ou=groups') + attr_dict = ldap_map['parents']['ou=permission'] ldap.add('ou=permission', attr_dict) From 930b8378a1d39bfd158e1da6c192e3d3c8a34f9e Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Wed, 7 Aug 2019 02:16:43 +0200 Subject: [PATCH 107/299] [mod] remove unused variable --- src/yunohost/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4a14c5e4b..1bab5eb31 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -590,9 +590,6 @@ def app_upgrade(app=[], url=None, file=None): from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.permission import permission_sync_to_user - # Retrieve interface - is_api = msettings.get('interface') == 'api' - try: app_list() except YunohostError: From 3130bb59ace5fa2ffdbe8ad275e2c76be55ea74d Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Wed, 7 Aug 2019 02:28:37 +0200 Subject: [PATCH 108/299] [mod] stop apps upgrade if one upgrade fail --- locales/en.json | 3 ++- src/yunohost/app.py | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/locales/en.json b/locales/en.json index be00d5b1e..cf15bb6f5 100644 --- a/locales/en.json +++ b/locales/en.json @@ -28,7 +28,8 @@ "app_location_unavailable": "This url is not available or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Invalid app manifest: {error}", "app_no_upgrade": "No apps to upgrade", - "app_not_upgraded": "The following apps were not upgraded: {apps}", + "app_not_upgraded": "The following apps were not upgraded because a the app '{app}' failed to upgrade: {apps}", + "app_upgrade_stoped": "The upgrade of alls applications has been stopped to prevent possible dommages because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1bab5eb31..a45766907 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -618,7 +618,7 @@ def app_upgrade(app=[], url=None, file=None): if len(apps) > 1: logger.info(m18n.n("app_upgrade_several_apps", apps=", ".join(apps))) - for app_instance_name in apps: + for number, app_instance_name in enumerate(apps): logger.info(m18n.n('app_upgrade_app_name', app=app_instance_name)) app_dict = app_info(app_instance_name, raw=True) @@ -672,9 +672,19 @@ def app_upgrade(app=[], url=None, file=None): if hook_exec(extracted_app_folder + '/scripts/upgrade', args=args_list, env=env_dict)[0] != 0: msg = m18n.n('app_upgrade_failed', app=app_instance_name) - not_upgraded_apps.append(app_instance_name) - logger.error(msg) operation_logger.error(msg) + + # display this is there are remaining apps + if apps[number + 1:]: + logger.error(m18n.n('app_upgrade_stoped')) + not_upgraded_apps = apps[number:] + # we don't want to continue upgrading apps here in case that breaks + # everything + raise YunohostError('app_not_upgraded', + failed_app=app_instance_name, + apps=', '.join(not_upgraded_apps)) + else: + raise YunohostError(msg) else: now = int(time.time()) # TODO: Move install_time away from app_setting @@ -709,9 +719,6 @@ def app_upgrade(app=[], url=None, file=None): hook_callback('post_app_upgrade', args=args_list, env=env_dict) operation_logger.success() - if not_upgraded_apps: - raise YunohostError('app_not_upgraded', apps=', '.join(not_upgraded_apps)) - permission_sync_to_user() logger.success(m18n.n('upgrade_complete')) From 889e34888dede81aa5532ebbccdedb055a86ab3b Mon Sep 17 00:00:00 2001 From: Bram Date: Wed, 7 Aug 2019 13:04:56 +0200 Subject: [PATCH 109/299] [mod] typo Co-Authored-By: decentral1se --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a45766907..0ada585c3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -674,7 +674,7 @@ def app_upgrade(app=[], url=None, file=None): msg = m18n.n('app_upgrade_failed', app=app_instance_name) operation_logger.error(msg) - # display this is there are remaining apps + # display this if there are remaining apps if apps[number + 1:]: logger.error(m18n.n('app_upgrade_stoped')) not_upgraded_apps = apps[number:] From 22be1a320b357c0ce652124d46bb369e1049f324 Mon Sep 17 00:00:00 2001 From: Bram Date: Wed, 7 Aug 2019 13:05:13 +0200 Subject: [PATCH 110/299] [mod] typo Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index cf15bb6f5..c8bee8610 100644 --- a/locales/en.json +++ b/locales/en.json @@ -29,7 +29,7 @@ "app_manifest_invalid": "Invalid app manifest: {error}", "app_no_upgrade": "No apps to upgrade", "app_not_upgraded": "The following apps were not upgraded because a the app '{app}' failed to upgrade: {apps}", - "app_upgrade_stoped": "The upgrade of alls applications has been stopped to prevent possible dommages because the previous application failed to upgrade", + "app_upgrade_stoped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", From 7926b761fd9abea76ff843778a81ec643d84f45e Mon Sep 17 00:00:00 2001 From: Bram Date: Wed, 7 Aug 2019 13:05:50 +0200 Subject: [PATCH 111/299] [mod] typo Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index c8bee8610..741af8c6a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -28,7 +28,7 @@ "app_location_unavailable": "This url is not available or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Invalid app manifest: {error}", "app_no_upgrade": "No apps to upgrade", - "app_not_upgraded": "The following apps were not upgraded because a the app '{app}' failed to upgrade: {apps}", + "app_not_upgraded": "The following apps were not upgraded because the app '{app}' failed to upgrade: {apps}", "app_upgrade_stoped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", From a283b436e173f16bd84e46da4669753f4f7c8bbb Mon Sep 17 00:00:00 2001 From: Bram Date: Sun, 18 Aug 2019 18:50:20 +0200 Subject: [PATCH 112/299] [mod] typo Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 741af8c6a..22dd59bb2 100644 --- a/locales/en.json +++ b/locales/en.json @@ -29,7 +29,7 @@ "app_manifest_invalid": "Invalid app manifest: {error}", "app_no_upgrade": "No apps to upgrade", "app_not_upgraded": "The following apps were not upgraded because the app '{app}' failed to upgrade: {apps}", - "app_upgrade_stoped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", + "app_upgrade_stopped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", From ef38f5e7b5f2a893f416baca7fccf164cf2fed81 Mon Sep 17 00:00:00 2001 From: Bram Date: Sun, 18 Aug 2019 18:51:16 +0200 Subject: [PATCH 113/299] [mod] typo Co-Authored-By: Alexandre Aubin --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0ada585c3..684d83569 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -676,7 +676,7 @@ def app_upgrade(app=[], url=None, file=None): # display this if there are remaining apps if apps[number + 1:]: - logger.error(m18n.n('app_upgrade_stoped')) + logger.error(m18n.n('app_upgrade_stopped')) not_upgraded_apps = apps[number:] # we don't want to continue upgrading apps here in case that breaks # everything From fc85ae010229ac3f8efee0ab5c16f12133edabfc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 01:42:04 +0200 Subject: [PATCH 114/299] Key mismatch was causing an error + ended up reworking the sentence --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 22dd59bb2..e520c442d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -28,7 +28,7 @@ "app_location_unavailable": "This url is not available or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Invalid app manifest: {error}", "app_no_upgrade": "No apps to upgrade", - "app_not_upgraded": "The following apps were not upgraded because the app '{app}' failed to upgrade: {apps}", + "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps upgrades have been cancelled: {apps}", "app_upgrade_stopped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", From 8e6ebd7979f4aa133ad477228a777417dc086e04 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Aug 2019 01:31:17 +0200 Subject: [PATCH 115/299] Iteration on sanity checks for app operations --- locales/en.json | 1 + src/yunohost/app.py | 52 +++++++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/locales/en.json b/locales/en.json index e520c442d..cc7955204 100644 --- a/locales/en.json +++ b/locales/en.json @@ -7,6 +7,7 @@ "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do! Everything is already up to date!", "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down) : {services}", + "app_action_broke_system": "This action seem to have broke these important services: {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The url cannot be changed just by this function. Look into `app changeurl` if it's available.", "app_already_up_to_date": "{app:s} is already up to date", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 684d83569..785613283 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -584,9 +584,6 @@ def app_upgrade(app=[], url=None, file=None): url -- Git url to fetch for upgrade """ - if packages.dpkg_is_broken(): - raise YunohostError("dpkg_is_broken") - from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.permission import permission_sync_to_user @@ -638,7 +635,7 @@ def app_upgrade(app=[], url=None, file=None): # Check requirements _check_manifest_requirements(manifest, app_instance_name=app_instance_name) - _check_services_status_for_app(manifest.get("services", [])) + _assert_system_is_sane_for_app(manifest, "pre") app_setting_path = APPS_SETTING_PATH + '/' + app_instance_name @@ -716,6 +713,7 @@ def app_upgrade(app=[], url=None, file=None): # So much win logger.success(m18n.n('app_upgraded', app=app_instance_name)) + _assert_system_is_sane_for_app(manifest, "post") hook_callback('post_app_upgrade', args=args_list, env=env_dict) operation_logger.success() @@ -736,8 +734,6 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu no_remove_on_failure -- Debug option to avoid removing the app on a failed installation force -- Do not ask for confirmation when installing experimental / low-quality apps """ - if packages.dpkg_is_broken(): - raise YunohostError("dpkg_is_broken") from yunohost.utils.ldap import _get_ldap_interface from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback @@ -801,7 +797,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Check requirements _check_manifest_requirements(manifest, app_id) - _check_services_status_for_app(manifest.get("services", [])) + _assert_system_is_sane_for_app(manifest, "pre") # Check if app can be forked instance_number = _installed_instance_number(app_id, last=True) + 1 @@ -894,8 +890,17 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu import traceback logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) finally: + try: + broke_the_system = False + _assert_system_is_sane_for_app(manifest, "post") + except Exception as e: + broke_the_system = True + error_msg = operation_logger.error(str(e)) + if install_retcode != 0: error_msg = operation_logger.error(m18n.n('unexpected_error', error='shell command return code: %s' % install_retcode)) + + if install_retcode != 0 or broke_the_system: if not no_remove_on_failure: # Setup environment for remove script env_dict_remove = {} @@ -926,6 +931,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu logger.warning(msg) operation_logger_remove.error(msg) else: + _assert_system_is_sane_for_app(manifest, "post") operation_logger_remove.success() # Clean tmp folders @@ -934,9 +940,6 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu app_ssowatconf() - if packages.dpkg_is_broken(): - logger.error(m18n.n("this_action_broke_dpkg")) - if install_retcode == -1: msg = m18n.n('operation_interrupted') + " " + error_msg raise YunohostError(msg, raw_msg=True) @@ -1004,6 +1007,8 @@ def app_remove(operation_logger, app): # script might date back from jessie install) _patch_php5(app_setting_path) + manifest = _get_manifest_of_app(app_setting_path) + os.system('cp -a %s /tmp/yunohost_remove && chown -hR admin: /tmp/yunohost_remove' % app_setting_path) os.system('chown -R admin: /tmp/yunohost_remove') os.system('chmod -R u+rX /tmp/yunohost_remove') @@ -1038,9 +1043,7 @@ def app_remove(operation_logger, app): permission_remove(app, l.split('.')[0], force=True, sync_perm=False) permission_sync_to_user() - - if packages.dpkg_is_broken(): - raise YunohostError("this_action_broke_dpkg") + _assert_system_is_sane_for_app(manifest, "post") @is_unit_operation(['permission','app']) @@ -2910,10 +2913,12 @@ def unstable_apps(): return output -def _check_services_status_for_app(services): +def _assert_system_is_sane_for_app(manifest, when): logger.debug("Checking that required services are up and running...") + services = manifest.get("services", []) + # Some apps use php-fpm or php5-fpm which is now php7.0-fpm def replace_alias(service): if service in ["php-fpm", "php5-fpm"]: @@ -2928,11 +2933,26 @@ def _check_services_status_for_app(services): service_filter = ["nginx", "php7.0-fpm", "mysql", "postfix"] services = [str(s) for s in services if s in service_filter] + if "nginx" not in services: + services = ["nginx"] + services + if "fail2ban" not in services: + services.append("fail2ban") + # List services currently down and raise an exception if any are found faulty_services = [s for s in services if service_status(s)["active"] != "active"] if faulty_services: - raise YunohostError('app_action_cannot_be_ran_because_required_services_down', - services=', '.join(faulty_services)) + if when == "pre": + raise YunohostError('app_action_cannot_be_ran_because_required_services_down', + services=', '.join(faulty_services)) + elif when == "post": + raise YunohostError('app_action_broke_system', + services=', '.join(faulty_services)) + + if packages.dpkg_is_broken(): + if when == "pre": + raise YunohostError("dpkg_is_broken") + elif when == "post": + raise YunohostError("this_action_broke_dpkg") def _patch_php5(app_folder): From 08ecace5ec1eddd048e890a9800068e7d9c605d1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 02:21:26 +0200 Subject: [PATCH 116/299] Here we keep need to keep going and only display an error, otherwise the rest of the file ain't properly cleaned up --- src/yunohost/app.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 785613283..833d67402 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -931,8 +931,12 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu logger.warning(msg) operation_logger_remove.error(msg) else: - _assert_system_is_sane_for_app(manifest, "post") - operation_logger_remove.success() + try: + _assert_system_is_sane_for_app(manifest, "post") + except Exception as e: + operation_logger_remove.error(e) + else: + operation_logger_remove.success() # Clean tmp folders shutil.rmtree(app_setting_path) From c530325e293379f685a44e88a95abb7d2e5fce7e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 02:22:29 +0200 Subject: [PATCH 117/299] Properly handle the sanity checks right after upgrades (in combination with managing the regular error code...). This is similar to what's done for app_install --- src/yunohost/app.py | 107 +++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 41 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 833d67402..0784f1ecc 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -666,56 +666,81 @@ def app_upgrade(app=[], url=None, file=None): # Execute App upgrade script os.system('chown -hR admin: %s' % INSTALL_TMP) - if hook_exec(extracted_app_folder + '/scripts/upgrade', - args=args_list, env=env_dict)[0] != 0: - msg = m18n.n('app_upgrade_failed', app=app_instance_name) - operation_logger.error(msg) - # display this if there are remaining apps - if apps[number + 1:]: - logger.error(m18n.n('app_upgrade_stopped')) - not_upgraded_apps = apps[number:] - # we don't want to continue upgrading apps here in case that breaks - # everything - raise YunohostError('app_not_upgraded', - failed_app=app_instance_name, - apps=', '.join(not_upgraded_apps)) + + try: + upgrade_retcode = hook_exec(extracted_app_folder + '/scripts/upgrade', + args=args_list, env=env_dict)[0] + except (KeyboardInterrupt, EOFError): + upgrade_retcode = -1 + except Exception: + import traceback + logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + finally: + + # Did the script succeed ? + if upgrade_retcode != 0: + error_msg = m18n.n('app_upgrade_failed', app=app_instance_name) + operation_logger.error(error_msg) + + # Did it broke the system ? + try: + broke_the_system = False + _assert_system_is_sane_for_app(manifest, "post") + except Exception as e: + broke_the_system = True + error_msg = operation_logger.error(str(e)) + + # If upgrade failed or broke the system, + # raise an error and interrupt all other pending upgrades + if upgrade_retcode != 0 or broke_the_system: + + # display this if there are remaining apps + if apps[number + 1:]: + logger.error(m18n.n('app_upgrade_stopped')) + not_upgraded_apps = apps[number:] + # we don't want to continue upgrading apps here in case that breaks + # everything + raise YunohostError('app_not_upgraded', + failed_app=app_instance_name, + apps=', '.join(not_upgraded_apps)) + else: + raise YunohostError(error_msg, raw_msg=True) + + # Otherwise we're good and keep going ! else: - raise YunohostError(msg) - else: - now = int(time.time()) - # TODO: Move install_time away from app_setting - app_setting(app_instance_name, 'update_time', now) - status['upgraded_at'] = now + now = int(time.time()) + # TODO: Move install_time away from app_setting + app_setting(app_instance_name, 'update_time', now) + status['upgraded_at'] = now - # Clean hooks and add new ones - hook_remove(app_instance_name) - if 'hooks' in os.listdir(extracted_app_folder): - for hook in os.listdir(extracted_app_folder + '/hooks'): - hook_add(app_instance_name, extracted_app_folder + '/hooks/' + hook) + # Clean hooks and add new ones + hook_remove(app_instance_name) + if 'hooks' in os.listdir(extracted_app_folder): + for hook in os.listdir(extracted_app_folder + '/hooks'): + hook_add(app_instance_name, extracted_app_folder + '/hooks/' + hook) - # Store app status - with open(app_setting_path + '/status.json', 'w+') as f: - json.dump(status, f) + # Store app status + with open(app_setting_path + '/status.json', 'w+') as f: + json.dump(status, f) - # Replace scripts and manifest and conf (if exists) - os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path, app_setting_path)) + # Replace scripts and manifest and conf (if exists) + os.system('rm -rf "%s/scripts" "%s/manifest.toml %s/manifest.json %s/conf"' % (app_setting_path, app_setting_path, app_setting_path, app_setting_path)) - if os.path.exists(os.path.join(extracted_app_folder, "manifest.json")): - os.system('mv "%s/manifest.json" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) - if os.path.exists(os.path.join(extracted_app_folder, "manifest.toml")): - os.system('mv "%s/manifest.toml" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) + if os.path.exists(os.path.join(extracted_app_folder, "manifest.json")): + os.system('mv "%s/manifest.json" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) + if os.path.exists(os.path.join(extracted_app_folder, "manifest.toml")): + os.system('mv "%s/manifest.toml" "%s/scripts" %s' % (extracted_app_folder, extracted_app_folder, app_setting_path)) - for file_to_copy in ["actions.json", "actions.toml", "config_panel.json", "config_panel.toml", "conf"]: - if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)): - os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path)) + for file_to_copy in ["actions.json", "actions.toml", "config_panel.json", "config_panel.toml", "conf"]: + if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)): + os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path)) - # So much win - logger.success(m18n.n('app_upgraded', app=app_instance_name)) + # So much win + logger.success(m18n.n('app_upgraded', app=app_instance_name)) - _assert_system_is_sane_for_app(manifest, "post") - hook_callback('post_app_upgrade', args=args_list, env=env_dict) - operation_logger.success() + hook_callback('post_app_upgrade', args=args_list, env=env_dict) + operation_logger.success() permission_sync_to_user() From 488275422148a9fc79338c1a16bf22117178da4b Mon Sep 17 00:00:00 2001 From: Laurent Peuch Date: Wed, 7 Aug 2019 02:28:37 +0200 Subject: [PATCH 118/299] [mod] stop apps upgrade if one upgrade fail --- src/yunohost/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0784f1ecc..d64baeaa3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -667,7 +667,6 @@ def app_upgrade(app=[], url=None, file=None): # Execute App upgrade script os.system('chown -hR admin: %s' % INSTALL_TMP) - try: upgrade_retcode = hook_exec(extracted_app_folder + '/scripts/upgrade', args=args_list, env=env_dict)[0] From 3eb089ffc062b3f3cccd833103b3b218bb35e3ea Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 18 Aug 2019 01:41:34 +0200 Subject: [PATCH 119/299] Add unit/functional tests for apps --- src/yunohost/tests/test_apps.py | 248 ++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 src/yunohost/tests/test_apps.py diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py new file mode 100644 index 000000000..3407c4f39 --- /dev/null +++ b/src/yunohost/tests/test_apps.py @@ -0,0 +1,248 @@ +import glob +import os +import pytest +import shutil +import requests + +from moulinette import m18n +from moulinette.utils.filesystem import mkdir + +from yunohost.app import app_install, app_remove, app_ssowatconf, _is_installed +from yunohost.domain import _get_maindomain, domain_add, domain_remove, domain_list +from yunohost.utils.error import YunohostError +from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps + + +MAIN_DOMAIN = _get_maindomain() + + +def setup_function(function): + + clean() + +def teardown_function(function): + + clean() + +def clean(): + + # Make sure we have a ssowat + os.system("mkdir -p /etc/ssowat/") + app_ssowatconf() + + if _is_installed("legacy_app"): + app_remove("legacy_app") + + to_remove = [] + to_remove += glob.glob("/etc/nginx/conf.d/*.d/*legacy*") + for filepath in to_remove: + os.remove(filepath) + + to_remove = [] + to_remove += glob.glob("/etc/yunohost/apps/*legacy_app*") + to_remove += glob.glob("/var/www/*legacy*") + for folderpath in to_remove: + shutil.rmtree(folderpath, ignore_errors=True) + + +@pytest.fixture(autouse=True) +def check_LDAP_db_integrity_call(): + check_LDAP_db_integrity() + yield + check_LDAP_db_integrity() + + +@pytest.fixture(autouse=True) +def check_permission_for_apps_call(): + check_permission_for_apps() + yield + check_permission_for_apps() + +@pytest.fixture(scope="session") +def secondary_domain(request): + + if "example.test" not in domain_list()["domains"]: + domain_add("example.test") + + def remove_example_domain(): + domain_remove("example.test") + request.addfinalizer(remove_example_domain) + + return "example.test" + + +# +# Helpers # +# + +def app_expected_files(domain, app): + + yield "/etc/nginx/conf.d/%s.d/%s.conf" % (domain, app) + yield "/var/www/%s/index.html" % app + yield "/etc/yunohost/apps/%s/settings.yml" % app + yield "/etc/yunohost/apps/%s/manifest.json" % app + yield "/etc/yunohost/apps/%s/scripts/install" % app + yield "/etc/yunohost/apps/%s/scripts/remove" % app + yield "/etc/yunohost/apps/%s/scripts/backup" % app + yield "/etc/yunohost/apps/%s/scripts/restore" % app + + +def app_is_installed(domain, app): + + return _is_installed(app) and all(os.path.exists(f) for f in app_expected_files(domain, app)) + + +def app_is_not_installed(domain, app): + + return not _is_installed(app) and not all(os.path.exists(f) for f in app_expected_files(domain, app)) + + +def app_is_exposed_on_http(domain, path, message_in_page): + + try: + r = requests.get("http://127.0.0.1" + path + "/", headers={"Host": domain}, timeout=10) + return r.status_code == 200 and message_in_page in r.text + except Exception: + return False + + +def install_legacy_app(domain, path): + + app_install("./tests/apps/legacy_app_ynh", + args="domain=%s&path=%s" % (domain, path), + force=True) + + +def test_legacy_app_install_main_domain(): + + install_legacy_app(MAIN_DOMAIN, "/legacy") + + assert app_is_installed(MAIN_DOMAIN, "legacy_app") + assert app_is_exposed_on_http(MAIN_DOMAIN, "/legacy", "This is a dummy app") + + app_remove("legacy_app") + + assert app_is_not_installed(MAIN_DOMAIN, "legacy_app") + + +def test_legacy_app_install_secondary_domain(secondary_domain): + + install_legacy_app(secondary_domain, "/legacy") + + assert app_is_installed(secondary_domain, "legacy_app") + assert app_is_exposed_on_http(secondary_domain, "/legacy", "This is a dummy app") + + app_remove("legacy_app") + + assert app_is_not_installed(secondary_domain, "legacy_app") + + +def test_legacy_app_install_secondary_domain_on_root(secondary_domain): + + install_legacy_app(secondary_domain, "/") + + assert app_is_installed(secondary_domain, "legacy_app") + assert app_is_exposed_on_http(secondary_domain, "/", "This is a dummy app") + + app_remove("legacy_app") + + assert app_is_not_installed(secondary_domain, "legacy_app") + + +def test_legacy_app_install_private(secondary_domain): + + install_legacy_app(secondary_domain, "/legacy") + + settings = open("/etc/yunohost/apps/legacy_app/settings.yml", "r").read() + new_settings = settings.replace("\nunprotected_uris: /", "") + assert new_settings != settings + open("/etc/yunohost/apps/legacy_app/settings.yml", "w").write(new_settings) + app_ssowatconf() + + assert app_is_installed(secondary_domain, "legacy_app") + assert not app_is_exposed_on_http(secondary_domain, "/legacy", "This is a dummy app") + + app_remove("legacy_app") + + assert app_is_not_installed(secondary_domain, "legacy_app") + + +def test_legacy_app_install_unknown_domain(): + + with pytest.raises(YunohostError): + install_legacy_app("whatever.nope", "/legacy") + # TODO check error message + + assert app_is_not_installed("whatever.nope", "legacy_app") + + +def test_legacy_app_install_multiple_instances(secondary_domain): + + install_legacy_app(secondary_domain, "/foo") + install_legacy_app(secondary_domain, "/bar") + + assert app_is_installed(secondary_domain, "legacy_app") + assert app_is_exposed_on_http(secondary_domain, "/foo", "This is a dummy app") + + assert app_is_installed(secondary_domain, "legacy_app__2") + assert app_is_exposed_on_http(secondary_domain, "/bar", "This is a dummy app") + + app_remove("legacy_app") + + assert app_is_not_installed(secondary_domain, "legacy_app") + assert app_is_installed(secondary_domain, "legacy_app__2") + + app_remove("legacy_app__2") + + assert app_is_not_installed(secondary_domain, "legacy_app") + assert app_is_not_installed(secondary_domain, "legacy_app__2") + + +def test_legacy_app_install_path_unavailable(secondary_domain): + + # These will be removed in teardown + install_legacy_app(secondary_domain, "/legacy") + + with pytest.raises(YunohostError): + install_legacy_app(secondary_domain, "/") + # TODO check error message + + assert app_is_installed(secondary_domain, "legacy_app") + assert app_is_not_installed(secondary_domain, "legacy_app__2") + + +def test_legacy_app_failed_install(secondary_domain): + + mkdir("/var/www/legacy_app/", 0o750) + + with pytest.raises(YunohostError): + install_legacy_app(secondary_domain, "/legacy") + # TODO check error message + + assert app_is_not_installed(secondary_domain, "legacy_app") + + +def test_legacy_app_install_with_nginx_down(secondary_domain): + + os.system("systemctl stop nginx") + + with pytest.raises(YunohostError): + install_legacy_app(secondary_domain, "/legacy") + + os.system("systemctl start nginx") + + +def test_legacy_app_failed_remove(): + + # FIXME What's supposed to happen lol + raise NotImplementedError + + +def test_legacy_app_install_fucksup_nginx(): + + # FIXME What's supposed to happen lol + raise NotImplementedError + +def test_legacy_app_install_with_dpkg_fuckedup(): + + raise NotImplementedError From 799c68f1a88faa1036c874007c09c6e3e8d40620 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Aug 2019 00:15:42 +0200 Subject: [PATCH 120/299] Moar tests for apps breaking the system --- src/yunohost/tests/test_apps.py | 119 +++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 25 deletions(-) diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py index 3407c4f39..1dcffbf0e 100644 --- a/src/yunohost/tests/test_apps.py +++ b/src/yunohost/tests/test_apps.py @@ -13,9 +13,6 @@ from yunohost.utils.error import YunohostError from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps -MAIN_DOMAIN = _get_maindomain() - - def setup_function(function): clean() @@ -33,17 +30,24 @@ def clean(): if _is_installed("legacy_app"): app_remove("legacy_app") + if _is_installed("break_yo_system"): + app_remove("break_yo_system") + to_remove = [] to_remove += glob.glob("/etc/nginx/conf.d/*.d/*legacy*") + to_remove += glob.glob("/etc/nginx/conf.d/*.d/*break_yo_system*") for filepath in to_remove: os.remove(filepath) to_remove = [] to_remove += glob.glob("/etc/yunohost/apps/*legacy_app*") + to_remove += glob.glob("/etc/yunohost/apps/*break_yo_system*") to_remove += glob.glob("/var/www/*legacy*") for folderpath in to_remove: shutil.rmtree(folderpath, ignore_errors=True) + os.system("systemctl start nginx") + @pytest.fixture(autouse=True) def check_LDAP_db_integrity_call(): @@ -78,13 +82,12 @@ def secondary_domain(request): def app_expected_files(domain, app): yield "/etc/nginx/conf.d/%s.d/%s.conf" % (domain, app) - yield "/var/www/%s/index.html" % app + if app.startswith("legacy_app"): + yield "/var/www/%s/index.html" % app yield "/etc/yunohost/apps/%s/settings.yml" % app yield "/etc/yunohost/apps/%s/manifest.json" % app yield "/etc/yunohost/apps/%s/scripts/install" % app yield "/etc/yunohost/apps/%s/scripts/remove" % app - yield "/etc/yunohost/apps/%s/scripts/backup" % app - yield "/etc/yunohost/apps/%s/scripts/restore" % app def app_is_installed(domain, app): @@ -113,16 +116,25 @@ def install_legacy_app(domain, path): force=True) +def install_break_yo_system(domain, breakwhat): + + app_install("./tests/apps/break_yo_system_ynh", + args="domain=%s&breakwhat=%s" % (domain, breakwhat), + force=True) + + def test_legacy_app_install_main_domain(): - install_legacy_app(MAIN_DOMAIN, "/legacy") + main_domain = _get_maindomain() - assert app_is_installed(MAIN_DOMAIN, "legacy_app") - assert app_is_exposed_on_http(MAIN_DOMAIN, "/legacy", "This is a dummy app") + install_legacy_app(main_domain, "/legacy") + + assert app_is_installed(main_domain, "legacy_app") + assert app_is_exposed_on_http(main_domain, "/legacy", "This is a dummy app") app_remove("legacy_app") - assert app_is_not_installed(MAIN_DOMAIN, "legacy_app") + assert app_is_not_installed(main_domain, "legacy_app") def test_legacy_app_install_secondary_domain(secondary_domain): @@ -211,15 +223,10 @@ def test_legacy_app_install_path_unavailable(secondary_domain): assert app_is_not_installed(secondary_domain, "legacy_app__2") -def test_legacy_app_failed_install(secondary_domain): - - mkdir("/var/www/legacy_app/", 0o750) +def test_legacy_app_install_bad_args(): with pytest.raises(YunohostError): - install_legacy_app(secondary_domain, "/legacy") - # TODO check error message - - assert app_is_not_installed(secondary_domain, "legacy_app") + install_legacy_app("this.domain.does.not.exists", "/legacy") def test_legacy_app_install_with_nginx_down(secondary_domain): @@ -229,20 +236,82 @@ def test_legacy_app_install_with_nginx_down(secondary_domain): with pytest.raises(YunohostError): install_legacy_app(secondary_domain, "/legacy") - os.system("systemctl start nginx") + +def test_legacy_app_failed_install(secondary_domain): + + # This will conflict with the folder that the app + # attempts to create, making the install fail + mkdir("/var/www/legacy_app/", 0o750) + + with pytest.raises(YunohostError): + install_legacy_app(secondary_domain, "/legacy") + # TODO check error message + + assert app_is_not_installed(secondary_domain, "legacy_app") -def test_legacy_app_failed_remove(): +def test_legacy_app_failed_remove(secondary_domain): - # FIXME What's supposed to happen lol - raise NotImplementedError + install_legacy_app(secondary_domain, "/legacy") + + # The remove script runs with set -eu and attempt to remove this + # file without -f, so will fail if it's not there ;) + os.remove("/etc/nginx/conf.d/%s.d/%s.conf" % (secondary_domain, "legacy_app")) + with pytest.raises(YunohostError): + app_remove("legacy") + + # + # Well here, we hit the classical issue where if an app removal script + # fails, so far there's no obvious way to make sure that all files related + # to this app got removed ... + # + assert app_is_not_installed(secondary_domain, "legacy") -def test_legacy_app_install_fucksup_nginx(): +def test_systemfuckedup_during_app_install(secondary_domain): - # FIXME What's supposed to happen lol - raise NotImplementedError + with pytest.raises(YunohostError): + install_break_yo_system(secondary_domain, breakwhat="install") + os.system("nginx -t") + os.system("systemctl status nginx") -def test_legacy_app_install_with_dpkg_fuckedup(): + assert app_is_not_installed(secondary_domain, "break_yo_system") + + +def test_systemfuckedup_during_app_remove(secondary_domain): + + install_break_yo_system(secondary_domain, breakwhat="remove") + + with pytest.raises(YunohostError): + app_remove("break_yo_system") + os.system("nginx -t") + os.system("systemctl status nginx") + + assert app_is_not_installed(secondary_domain, "break_yo_system") + + +def test_systemfuckedup_during_app_install_and_remove(secondary_domain): + + with pytest.raises(YunohostError): + install_break_yo_system(secondary_domain, breakwhat="everything") + + assert app_is_not_installed(secondary_domain, "break_yo_system") + + +def test_systemfuckedup_during_app_upgrade(secondary_domain): raise NotImplementedError + + install_break_yo_system(secondary_domain, breakwhat="upgrade") + + #app_upgrade("break_yo_system", ...) + + +def test_failed_multiple_app_upgrade(secondary_domain): + + raise NotImplementedError + + install_legacy_app(secondary_domain, "/legacy") + install_break_yo_system(secondary_domain, breakwhat="upgrade") + + app_upgrade(["break_yo_system", "legacy"]) From 28c73cb336aeef910f460308eb89465078ea2aab Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 02:11:37 +0200 Subject: [PATCH 121/299] Implement those remaining tests --- src/yunohost/tests/test_apps.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py index 1dcffbf0e..9c85df1e9 100644 --- a/src/yunohost/tests/test_apps.py +++ b/src/yunohost/tests/test_apps.py @@ -7,7 +7,7 @@ import requests from moulinette import m18n from moulinette.utils.filesystem import mkdir -from yunohost.app import app_install, app_remove, app_ssowatconf, _is_installed +from yunohost.app import app_install, app_remove, app_ssowatconf, _is_installed, app_upgrade from yunohost.domain import _get_maindomain, domain_add, domain_remove, domain_list from yunohost.utils.error import YunohostError from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps @@ -27,12 +27,15 @@ def clean(): os.system("mkdir -p /etc/ssowat/") app_ssowatconf() - if _is_installed("legacy_app"): - app_remove("legacy_app") - + # Gotta first remove break yo system + # because some remaining stuff might + # make the other app_remove crashs ;P if _is_installed("break_yo_system"): app_remove("break_yo_system") + if _is_installed("legacy_app"): + app_remove("legacy_app") + to_remove = [] to_remove += glob.glob("/etc/nginx/conf.d/*.d/*legacy*") to_remove += glob.glob("/etc/nginx/conf.d/*.d/*break_yo_system*") @@ -46,6 +49,7 @@ def clean(): for folderpath in to_remove: shutil.rmtree(folderpath, ignore_errors=True) + os.system("systemctl reset-failed nginx") # Reset failed quota for service to avoid running into start-limit rate ? os.system("systemctl start nginx") @@ -300,18 +304,18 @@ def test_systemfuckedup_during_app_install_and_remove(secondary_domain): def test_systemfuckedup_during_app_upgrade(secondary_domain): - raise NotImplementedError - install_break_yo_system(secondary_domain, breakwhat="upgrade") - #app_upgrade("break_yo_system", ...) + with pytest.raises(YunohostError): + app_upgrade("break_yo_system", file="./tests/apps/break_yo_system_ynh") def test_failed_multiple_app_upgrade(secondary_domain): - raise NotImplementedError - install_legacy_app(secondary_domain, "/legacy") install_break_yo_system(secondary_domain, breakwhat="upgrade") - app_upgrade(["break_yo_system", "legacy"]) + with pytest.raises(YunohostError): + app_upgrade(["break_yo_system", "legacy_app"], + file={"break_yo_system": "./tests/apps/break_yo_system_ynh", + "legacy": "./tests/apps/legacy_app_ynh"}) From a476deb7fbeb73b23d6282b869e07cbf23f316be Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 02:11:53 +0200 Subject: [PATCH 122/299] Tweak test conf for easier debugging --- src/yunohost/tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index a2dc585bd..ce321933c 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -41,11 +41,11 @@ def pytest_cmdline_main(config): root_handlers = set(handlers) # Define loggers level - level = 'INFO' + level = 'DEBUG' if config.option.yunodebug: tty_level = 'DEBUG' else: - tty_level = 'SUCCESS' + tty_level = 'INFO' # Custom logging configuration logging = { From aa3687ba029abae89f9a01d6e45df3ab843e93ea Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 02:13:02 +0200 Subject: [PATCH 123/299] Small trick needed to be able to test chained app upgrades --- src/yunohost/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index d64baeaa3..6b75d82d0 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -620,7 +620,10 @@ def app_upgrade(app=[], url=None, file=None): app_dict = app_info(app_instance_name, raw=True) - if file: + if file and isinstance(file, dict): + # We use this dirty hack to test chained upgrades in unit/functional tests + manifest, extracted_app_folder = _extract_app_from_file(file[app_instance_name]) + elif file: manifest, extracted_app_folder = _extract_app_from_file(file) elif url: manifest, extracted_app_folder = _fetch_app_from_git(url) From cc59501b55b269747be983aacc392f9d99aa8522 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 16:59:34 +0200 Subject: [PATCH 124/299] Naive implementation of visitors group (without any relation to the ssowat conf yet) --- data/other/ldap_scheme.yml | 6 ++++++ locales/en.json | 3 +++ .../0011_setup_group_permission.py | 4 ++++ src/yunohost/permission.py | 13 +++++++++---- src/yunohost/user.py | 15 ++++++++++----- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/data/other/ldap_scheme.yml b/data/other/ldap_scheme.yml index caa8fffb2..660d6fbb5 100644 --- a/data/other/ldap_scheme.yml +++ b/data/other/ldap_scheme.yml @@ -57,6 +57,12 @@ children: objectClass: - posixGroup - groupOfNamesYnh + cn=visitors,ou=groups: + cn: visitors + gidNumber: "4003" + objectClass: + - posixGroup + - groupOfNamesYnh depends_children: cn=mail.main,ou=permission: diff --git a/locales/en.json b/locales/en.json index ae349edf3..5df21b684 100644 --- a/locales/en.json +++ b/locales/en.json @@ -230,6 +230,9 @@ "group_already_exist_on_system": "Group {group} already exists in the system group", "group_created": "Group '{group}' successfully created", "group_creation_failed": "Failed to create group {group}: {error}", + "group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in Yunohost", + "group_cannot_edit_visitors": "The group 'visitors' cannot be edited manually. It is a special group representing anonymous visitors", + "group_cannot_edit_primary_group": "The group '{group}' cannot be edited manually. It is the primary group meant to contain only one specific user.", "group_cannot_be_edited": "The group {group} cannot be edited manually.", "group_cannot_be_deleted": "The group {group} cannot be deleted manually.", "group_deleted": "Group '{group}' deleted", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 8949239e0..b3e11cb14 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -63,6 +63,7 @@ class MyMigration(Migration): self.remove_if_exists("cn=sftpusers,ou=groups") self.remove_if_exists("ou=permission") self.remove_if_exists('cn=all_users,ou=groups') + self.remove_if_exists('cn=visitors,ou=groups') attr_dict = ldap_map['parents']['ou=permission'] ldap.add('ou=permission', attr_dict) @@ -70,6 +71,9 @@ class MyMigration(Migration): attr_dict = ldap_map['children']['cn=all_users,ou=groups'] ldap.add('cn=all_users,ou=groups', attr_dict) + attr_dict = ldap_map['children']['cn=visitors,ou=groups'] + ldap.add('cn=visitors,ou=groups', attr_dict) + for rdn, attr_dict in ldap_map['depends_children'].items(): ldap.add(rdn, attr_dict) except Exception as e: diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 1472f4b88..dbfc6e6f5 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -142,10 +142,15 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # we shall warn the users that they should probably choose between one or the other, # because the current situation is probably not what they expect / is temporary ? - if len(new_allowed_groups) > 1 and "all_users" in new_allowed_groups: - # FIXME : i18n - # FIXME : write a better explanation ? - logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the specific groups currently allowed.") + if len(new_allowed_groups) > 1: + if "all_users" in new_allowed_groups: + # FIXME : i18n + # FIXME : write a better explanation ? + logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups currently allowed.") + if "visitors" in new_allowed_groups: + # FIXME : i18n + # FIXME : write a better explanation ? + logger.warning("This permission is currently enabled for visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups currently allowed.") # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): diff --git a/src/yunohost/user.py b/src/yunohost/user.py index c6413d7e1..581354f77 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -635,7 +635,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True): # # We also can't delete "all_users" because that's a special group... existing_users = user_list()['users'].keys() - undeletable_groups = existing_users + ["all_users", "admins"] + undeletable_groups = existing_users + ["all_users", "visitors"] if groupname in undeletable_groups and not force: raise YunohostError('group_cannot_be_deleted', group=groupname) @@ -670,13 +670,18 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= from yunohost.permission import permission_sync_to_user from yunohost.utils.ldap import _get_ldap_interface + existing_users = user_list()['users'].keys() + # Refuse to edit a primary group of a user (e.g. group 'sam' related to user 'sam') # Those kind of group should only ever contain the user (e.g. sam) and only this one. # We also can't edit "all_users" without the force option because that's a special group... - existing_users = user_list()['users'].keys() - uneditable_groups = existing_users + ["all_users", "admins"] - if groupname in uneditable_groups and not force: - raise YunohostError('group_cannot_be_edited', group=groupname) + if not force: + if groupname == "all_users": + raise YunohostError('group_cannot_edit_all_users') + elif groupname == "all_users": + raise YunohostError('group_cannot_edit_visitors') + elif groupname in existing_users: + raise YunohostError('group_cannot_edit_primary_group', group=groupname) # We extract the uid for each member of the group to keep a simple flat list of members current_group = user_group_info(groupname)["members"] From 95a8dfa71c22103152a9241bc508de96f04cfe19 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 16:59:44 +0200 Subject: [PATCH 125/299] Simplify part of app_ssowatconf --- src/yunohost/app.py | 49 ++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ab290cb4d..a9c91aaf5 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -41,7 +41,7 @@ from datetime import datetime from moulinette import msignals, m18n, msettings from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_json, read_toml +from moulinette.utils.filesystem import read_json, read_toml, read_yaml from yunohost.service import service_log, service_status, _run_service_command from yunohost.utils import packages @@ -1366,34 +1366,29 @@ def app_ssowatconf(): return s.split(',') if s else [] for app in apps_list: - with open(APPS_SETTING_PATH + app['id'] + '/settings.yml') as f: - app_settings = yaml.load(f) - if 'no_sso' in app_settings: - continue + app_settings = read_yaml(APPS_SETTING_PATH + app['id'] + '/settings.yml') - for item in _get_setting(app_settings, 'skipped_uris'): - if item[-1:] == '/': - item = item[:-1] - skipped_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item) - for item in _get_setting(app_settings, 'skipped_regex'): - skipped_regex.append(item) - for item in _get_setting(app_settings, 'unprotected_uris'): - if item[-1:] == '/': - item = item[:-1] - unprotected_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item) - for item in _get_setting(app_settings, 'unprotected_regex'): - unprotected_regex.append(item) - for item in _get_setting(app_settings, 'protected_uris'): - if item[-1:] == '/': - item = item[:-1] - protected_urls.append(app_settings['domain'] + app_settings['path'].rstrip('/') + item) - for item in _get_setting(app_settings, 'protected_regex'): - protected_regex.append(item) - if 'redirected_urls' in app_settings: - redirected_urls.update(app_settings['redirected_urls']) - if 'redirected_regex' in app_settings: - redirected_regex.update(app_settings['redirected_regex']) + if 'no_sso' in app_settings: + continue + + app_root_webpath = app_settings['domain'] + app_settings['path'].rstrip('/') + + # Skipped + skipped_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'skipped_uris')] + skipped_regex += _get_setting(app_settings, 'skipped_regex') + + # Unprotected + unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'unprotected_uris')] + unprotected_regex += _get_setting(app_settings, 'unprotected_regex') + + # Protected + unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'protected_uris')] + unprotected_regex += _get_setting(app_settings, 'protected_regex') + + # Redirected + redirected_urls.update(app_settings.get('redirected_urls', {})) + redirected_regex.update(app_settings.get('redirected_regex', {})) for domain in domains: skipped_urls.extend([domain + '/yunohost/admin', domain + '/yunohost/api']) From 8abfd2a6e60bfd53d6fb398261e002e223a0c217 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 17:58:41 +0200 Subject: [PATCH 126/299] Naive implementation of protected/unprotected inplementation using the visitors group --- src/yunohost/app.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index a9c91aaf5..52371ff29 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1345,6 +1345,7 @@ def app_ssowatconf(): main_domain = _get_maindomain() domains = domain_list()['domains'] + all_permissions = user_permission_list(full=True)['permissions'] skipped_urls = [] skipped_regex = [] @@ -1378,18 +1379,32 @@ def app_ssowatconf(): skipped_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'skipped_uris')] skipped_regex += _get_setting(app_settings, 'skipped_regex') - # Unprotected - unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'unprotected_uris')] - unprotected_regex += _get_setting(app_settings, 'unprotected_regex') - - # Protected - unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'protected_uris')] - unprotected_regex += _get_setting(app_settings, 'protected_regex') - # Redirected redirected_urls.update(app_settings.get('redirected_urls', {})) redirected_regex.update(app_settings.get('redirected_regex', {})) + # Legacy permission system using (un)protected_uris and _regex managed in app settings... + unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'unprotected_uris')] + unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'protected_uris')] + unprotected_regex += _get_setting(app_settings, 'unprotected_regex') + unprotected_regex += _get_setting(app_settings, 'protected_regex') + + # New permission system + this_app_perms = {name: info for name, info in all_permissions.items if name.startswith(app + ".")} + for perm_name, perm_info in this_app_perms: + urls = [url.rstrip("/") for url in perm_info["urls"]] + if "visitors" in perm_info["allowed"]: + unprotected_urls += urls + + # Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier... + protected_urls = [u for u in protected_urls if u not in urls] + else: + # TODO : small optimization to implement : we don't need to explictly add all the app roots + protected_urls += urls + + # Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier... + unprotected_urls = [u for u in unprotected_urls if u not in urls] + for domain in domains: skipped_urls.extend([domain + '/yunohost/admin', domain + '/yunohost/api']) @@ -1397,8 +1412,10 @@ def app_ssowatconf(): skipped_regex.append("^[^/]*/%.well%-known/acme%-challenge/.*$") skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$") + + permissions_per_url = {} - for permission_name, permission_infos in user_permission_list(full=True)['permissions'].items(): + for permission_name, permission_infos in all_permissions.items(): for url in permission_infos["urls"]: permissions_per_url[url] = permission_infos['corresponding_users'] From c4743398e687738a8b55bd500f219f7a69afe28e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 18:10:58 +0200 Subject: [PATCH 127/299] Deprecate (un)protected_uris and _regex settings + more explicit deprecation warning for app_add/remove/clearaccess --- data/helpers.d/setting | 4 +++- src/yunohost/app.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index 502da1ed7..d083ed563 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -176,6 +176,8 @@ else: elif action == "set": if key in ['redirected_urls', 'redirected_regex']: value = yaml.load(value) + if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage public/private access.") settings[key] = value else: raise ValueError("action should either be get, set or delete") @@ -249,7 +251,7 @@ ynh_permission_create() { # Remove a permission for the app (note that when the app is removed all permission is automatically removed) # -# usage: ynh_permission_remove --permission "permission" +# usage: ynh_permission_delete --permission "permission" # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) # # example: ynh_permission_delete --permission editors diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 52371ff29..8b0c99d46 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1037,6 +1037,8 @@ def app_addaccess(apps, users=[]): """ from yunohost.permission import user_permission_update + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + output = {} for app in apps: permission = user_permission_update(app+".main", add=users) @@ -1056,6 +1058,8 @@ def app_removeaccess(apps, users=[]): """ from yunohost.permission import user_permission_update + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + output = {} for app in apps: permission = user_permission_update(app+".main", remove=users) @@ -1074,6 +1078,8 @@ def app_clearaccess(apps): """ from yunohost.permission import user_permission_reset + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + output = {} for app in apps: permission = user_permission_reset(app+".main") @@ -1181,6 +1187,8 @@ def app_setting(app, key, value=None, delete=False): # FIXME: Allow multiple values for some keys? if key in ['redirected_urls', 'redirected_regex']: value = yaml.load(value) + if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: + logger.warning("/!\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage public/private access.") app_settings[key] = value _set_app_settings(app, app_settings) From b2a26a64a74d2f7289d857b7bb780ac2f91741ce Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 18:33:31 +0200 Subject: [PATCH 128/299] Naively migrate legacy and classical unprotected_uris = / that sets the app as public --- src/yunohost/app.py | 8 +++++++- .../data_migrations/0011_setup_group_permission.py | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 8b0c99d46..537616e68 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -733,7 +733,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user + from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user, user_permission_update # Fetch or extract sources if not os.path.exists(INSTALL_TMP): @@ -952,7 +952,13 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu domain = app_settings.get('domain', None) path = app_settings.get('path', None) if domain and path: + # FIXME : might want to move this to before running the install script because some app need to run install script during initialization etc (idk) ? permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False) + + # Migrate classic public app still using the legacy unprotected_uris + if app_settings.get("unprotected_uris", None) == "/": + user_permission_update(app_instance_name+".main", remove="all_users", add="visitors", sync_perm=False) + permission_sync_to_user() logger.success(m18n.n('installation_complete')) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index b3e11cb14..c79d80e0c 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -113,6 +113,12 @@ class MyMigration(Migration): user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False) app_setting(app, 'allowed_users', delete=True) + # Migrate classic public app still using the legacy unprotected_uris + if app_setting(app, "unprotected_uris") == "/": + user_permission_update(app+".main", remove="all_users", add="visitors", sync_perm=False) + + permission_sync_to_user() + def run(self): # FIXME : what do we really want to do here ... From 821a3ac4ff0f3180ff5b5884f020c02b3a982b34 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Sep 2019 18:53:25 +0200 Subject: [PATCH 129/299] Draft tests to check that permissions are actually propagated and effective on the SSO --- src/yunohost/tests/test_permission.py | 55 ++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 94728505d..1c81e015f 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -333,7 +333,7 @@ def test_permission_remove_url_not_added(): def test_permission_app_install(): app_install("./tests/apps/permissions_app_ynh", - args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) + args="domain=%s&path=%s&is_public=0&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) res = user_permission_list(full=True)['permissions'] assert "permissions_app.main" in res @@ -361,7 +361,7 @@ def test_permission_app_install(): def test_permission_app_remove(): app_install("./tests/apps/permissions_app_ynh", - args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) + args="domain=%s&path=%s&is_public=0&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) app_remove("permissions_app") # Check all permissions for this app got deleted @@ -383,3 +383,54 @@ def test_permission_app_change_url(): assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"] assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"] assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"] + + +def test_permission_app_propagation_on_ssowat(): + + # TODO / FIXME : To be actually implemented later .... + raise NotImplementedError + + app_install("./tests/apps/permissions_app_ynh", + args="domain=%s&path=%s&is_public=1&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) + + res = user_permission_list(full=True)['permissions'] + assert res['permissions_app.main']['allowed'] == ["all_users"] + + assert can_access(maindomain + "/urlpermissionapp", logged_as=None) + assert can_access(maindomain + "/urlpermissionapp", logged_as="alice") + + user_permission_update("permissions_app.main", remove="visitors", add="bob") + res = user_permission_list(full=True)['permissions'] + + assert cannot_access(maindomain + "/urlpermissionapp", logged_as=None) + assert cannot_access(maindomain + "/urlpermissionapp", logged_as="alice") + assert can_access(maindomain + "/urlpermissionapp", logged_as="bob") + + # Test admin access, as configured during install, only alice should be able to access it + + assert cannot_access(maindomain + "/urlpermissionapp/admin", logged_as=None) + assert cannot_access(maindomain + "/urlpermissionapp/admin", logged_as="alice") + assert can_access(maindomain + "/urlpermissionapp/admin", logged_as="bob") + +def test_permission_legacy_app_propagation_on_ssowat(): + + # TODO / FIXME : To be actually implemented later .... + raise NotImplementedError + + app_install("./tests/apps/legacy_app_ynh", + args="domain=%s&path=%s" % (maindomain, "/legacy"), force=True) + + # App is configured as public by default using the legacy unprotected_uri mechanics + # It should automatically be migrated during the install + assert res['permissions_app.main']['allowed'] == ["visitors"] + + assert can_access(maindomain + "/legacy", logged_as=None) + assert can_access(maindomain + "/legacy", logged_as="alice") + + # Try to update the permission and check that permissions are still consistent + user_permission_update("legacy_app.main", remove="visitors", add="bob") + res = user_permission_list(full=True)['permissions'] + + assert cannot_access(maindomain + "/legacy", logged_as=None) + assert cannot_access(maindomain + "/legacy", logged_as="alice") + assert can_access(maindomain + "/legacy", logged_as="bob") From 875c570c6dbcf406025478054ba09e98b6e09e81 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 16 Sep 2019 00:13:41 +0200 Subject: [PATCH 130/299] Check if the upgrade got manually interrupted, c.f. same stuff in app_install --- locales/en.json | 1 + src/yunohost/app.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index cc7955204..d584d80d9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -409,6 +409,7 @@ "no_ipv6_connectivity": "IPv6 connectivity is not available", "no_restore_script": "No restore script found for the app '{app:s}'", "not_enough_disk_space": "Not enough free disk space on '{path:s}'", + "operation_interrupted": "The operation was manually interrupted?", "package_not_installed": "Package '{pkgname}' is not installed", "package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'", "package_unknown": "Unknown package '{pkgname}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0784f1ecc..41c3faed6 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -679,7 +679,10 @@ def app_upgrade(app=[], url=None, file=None): finally: # Did the script succeed ? - if upgrade_retcode != 0: + if upgrade_retcode == -1: + error_msg = m18n.n('operation_interrupted') + operation_logger.error(error_msg) + elif upgrade_retcode != 0: error_msg = m18n.n('app_upgrade_failed', app=app_instance_name) operation_logger.error(error_msg) From a2813bd774c35a7cb89403f86d5251c3a92d653d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Tue, 17 Sep 2019 20:23:36 +0200 Subject: [PATCH 131/299] Language reworked --- locales/en.json | 746 ++++++++++++++++++++++++------------------------ 1 file changed, 373 insertions(+), 373 deletions(-) diff --git a/locales/en.json b/locales/en.json index be00d5b1e..cab2afff8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -2,67 +2,67 @@ "aborting": "Aborting.", "action_invalid": "Invalid action '{action:s}'", "admin_password": "Administration password", - "admin_password_change_failed": "Unable to change password", - "admin_password_changed": "The administration password has been changed", + "admin_password_change_failed": "Cannot change password", + "admin_password_changed": "The administration password now changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", - "already_up_to_date": "Nothing to do! Everything is already up to date!", - "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down) : {services}", + "already_up_to_date": "Nothing to do. Everything is already up-to-date.", + "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down): {services}", "app_already_installed": "{app:s} is already installed", - "app_already_installed_cant_change_url": "This app is already installed. The url cannot be changed just by this function. Look into `app changeurl` if it's available.", - "app_already_up_to_date": "{app:s} is already up to date", - "app_argument_choice_invalid": "Invalid choice for argument '{name:s}', it must be one of {choices:s}", - "app_argument_invalid": "Invalid value for argument '{name:s}': {error:s}", + "app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Look into `app changeurl` if it's available.", + "app_already_up_to_date": "{app:s} is already up-to-date", + "app_argument_choice_invalid": "Use one of these choices '{choices:s}' for the argument '{name:s}'", + "app_argument_invalid": "Pick a valid value for the argument '{name:s}': {error:s}", "app_argument_required": "Argument '{name:s}' is required", - "app_change_no_change_url_script": "The application {app_name:s} doesn't support changing it's URL yet, you might need to upgrade it.", - "app_change_url_failed_nginx_reload": "Failed to reload nginx. Here is the output of 'nginx -t':\n{nginx_errors:s}", + "app_change_no_change_url_script": "The application {app_name:s} doesn't support changing its URL yet, you might need to upgrade it.", + "app_change_url_failed_nginx_reload": "Could not reload NGINX. Here is the output of 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain:s}{path:s}'), nothing to do.", - "app_change_url_no_script": "This application '{app_name:s}' doesn't support url modification yet. Maybe you should upgrade the application.", - "app_change_url_success": "Successfully changed {app:s} url to {domain:s}{path:s}", - "app_extraction_failed": "Unable to extract installation files", - "app_id_invalid": "Invalid app id", + "app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", + "app_change_url_success": "{app:s} URL is now {domain:s}{path:s}", + "app_extraction_failed": "Could not extract the installation files", + "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", - "app_install_files_invalid": "Invalid installation files", - "app_location_already_used": "The app '{app}' is already installed on that location ({path})", - "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain {domain} is already used by the other app '{other_app}'", - "app_location_install_failed": "Unable to install the app in this location because it conflit with the app '{other_app}' already installed on '{other_path}'", - "app_location_unavailable": "This url is not available or conflicts with the already installed app(s):\n{apps:s}", - "app_manifest_invalid": "Invalid app manifest: {error}", - "app_no_upgrade": "No apps to upgrade", - "app_not_upgraded": "The following apps were not upgraded: {apps}", + "app_install_files_invalid": "These files cannot be installed", + "app_location_already_used": "The app '{app}' is already installed in ({path})", + "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", + "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", + "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", + "app_manifest_invalid": "Something is wrong with the app manifest: {error}", + "app_no_upgrade": "Everything is up-to-date", + "app_not_upgraded": "These apps were not upgraded: {apps}", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", - "app_not_installed": "The application '{app:s}' is not installed. Here is the list of all installed apps: {all_apps}", + "app_not_installed": "The application '{app:s}' is not to be found among all installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", "app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes", - "app_removed": "{app:s} has been removed", + "app_removed": "{app:s} removed", "app_requirements_checking": "Checking required packages for {app}…", - "app_requirements_failed": "Unable to meet requirements for {app}: {error}", + "app_requirements_failed": "Did not meet requirements for {app}: {error}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", - "app_sources_fetch_failed": "Unable to fetch sources files, is the url correct?", + "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", "app_start_install": "Installing application {app}…", "app_start_remove": "Removing application {app}…", - "app_start_backup": "Collecting files to be backuped for {app}…", + "app_start_backup": "Collecting files to be backed up for {app}…", "app_start_restore": "Restoring application {app}…", "app_unknown": "Unknown app", "app_unsupported_remote_type": "Unsupported remote type used for the app", - "app_upgrade_several_apps": "The following apps will be upgraded : {apps}", - "app_upgrade_app_name": "Now upgrading app {app}…", - "app_upgrade_failed": "Unable to upgrade {app:s}", - "app_upgrade_some_app_failed": "Unable to upgrade some applications", - "app_upgraded": "{app:s} has been upgraded", + "app_upgrade_several_apps": "The following apps will be upgraded: {apps}", + "app_upgrade_app_name": "Now upgrading {app}…", + "app_upgrade_failed": "Could not upgrade {app:s}", + "app_upgrade_some_app_failed": "Some applications could not be upgraded", + "app_upgraded": "{app:s} upgraded", "apps_permission_not_found": "No permission found for the installed apps", - "apps_permission_restoration_failed": "Permission '{permission:s}' for app {app:s} restoration has failed", - "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.", - "appslist_could_not_migrate": "Could not migrate app list {appslist:s}! Unable to parse the url… The old cron job has been kept in {bkp_file:s}.", - "appslist_fetched": "The application list {appslist:s} has been fetched", + "apps_permission_restoration_failed": "Grant the permission permission '{permission:s}' to restore {app:s}", + "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is damaged.", + "appslist_could_not_migrate": "Could not migrate the app list {appslist:s}! Could not parse the URL… The old cron job was kept kept in {bkp_file:s}.", + "appslist_fetched": "Updated application list {appslist:s} fetched", "appslist_migrating": "Migrating application list {appslist:s}…", - "appslist_name_already_tracked": "There is already a registered application list with name {name:s}.", - "appslist_removed": "The application list {appslist:s} has been removed", - "appslist_retrieve_bad_format": "Retrieved file for application list {appslist:s} is not valid", - "appslist_retrieve_error": "Unable to retrieve the remote application list {appslist:s}: {error:s}", + "appslist_name_already_tracked": "A registered application list with name {name:s} already exists.", + "appslist_removed": "{appslist:s} application list removed", + "appslist_retrieve_bad_format": "Could not read the fetched application list {appslist:s}", + "appslist_retrieve_error": "Cannot retrieve the remote application list {appslist:s}: {error:s}", "appslist_unknown": "Application list {appslist:s} unknown.", - "appslist_url_already_tracked": "There is already a registered application list with url {url:s}.", + "appslist_url_already_tracked": "There is already a registered application list with the URL {url:s}.", "ask_current_admin_password": "Current administration password", - "ask_email": "Email address", + "ask_email": "E-mail address", "ask_firstname": "First name", "ask_lastname": "Last name", "ask_list_to_remove": "List to remove", @@ -72,109 +72,109 @@ "ask_new_path": "New path", "ask_password": "Password", "ask_path": "Path", - "backup_abstract_method": "This backup method hasn't yet been implemented", + "backup_abstract_method": "This backup method has yet to be implemented", "backup_action_required": "You must specify something to save", - "backup_actually_backuping": "Now creating a backup archive from the files collected…", - "backup_app_failed": "Unable to back up the app '{app:s}'", + "backup_actually_backuping": "Creating a backup archive from the collected files…", + "backup_app_failed": "Could not back up the app '{app:s}'", "backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", "backup_applying_method_copy": "Copying all files to backup…", "backup_applying_method_custom": "Calling the custom backup method '{method:s}'…", - "backup_applying_method_tar": "Creating the backup tar archive…", - "backup_archive_app_not_found": "App '{app:s}' not found in the backup archive", - "backup_archive_broken_link": "Unable to access backup archive (broken link to {path:s})", - "backup_archive_mount_failed": "Mounting the backup archive failed", - "backup_archive_name_exists": "The backup's archive name already exists", + "backup_applying_method_tar": "Creating the backup TAR archive…", + "backup_archive_app_not_found": "Could not find the app '{app:s}' in the backup archive", + "backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})", + "backup_archive_mount_failed": "Could not mounting the backup archive", + "backup_archive_name_exists": "A backup archive with this name already exists.", "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", - "backup_archive_open_failed": "Unable to open the backup archive", - "backup_archive_system_part_not_available": "System part '{part:s}' not available in this backup", - "backup_archive_writing_error": "Unable to add files '{source:s}' (named in the archive: '{dest:s}') to backup into the compressed archive '{archive:s}'", - "backup_ask_for_copying_if_needed": "Some files couldn't be prepared to be backuped using the method that avoid to temporarily waste space on the system. To perform the backup, {size:s}MB should be used temporarily. Do you agree?", - "backup_borg_not_implemented": "Borg backup method is not yet implemented", - "backup_cant_mount_uncompress_archive": "Unable to mount in readonly mode the uncompress archive directory", - "backup_cleaning_failed": "Unable to clean-up the temporary backup directory", + "backup_archive_open_failed": "Could not open the backup archive", + "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", + "backup_archive_writing_error": "Could not add the add files '{source:s}' (named in the archive: '{dest:s}') to be backed up into the compressed archive '{archive:s}'", + "backup_ask_for_copying_if_needed": "Some files could not be prepared for bacup using the method that avoids temporarily wasting space on the system. To perform the backup, {size:s}MB will be temporarily. Do you agree?", + "backup_borg_not_implemented": "The Borg backup method is not yet implemented", + "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive write protected", + "backup_cleaning_failed": "Could not clean-up the temporary backup folder", "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", - "backup_couldnt_bind": "Couldn't bind {src:s} to {dest:s}.", + "backup_couldnt_bind": "Could not bind {src:s} to {dest:s}.", "backup_created": "Backup created", "backup_creating_archive": "Creating the backup archive…", - "backup_creation_failed": "Backup creation failed", - "backup_csv_addition_failed": "Unable to add files to backup into the CSV file", - "backup_csv_creation_failed": "Unable to create the CSV file needed for future restore operations", - "backup_custom_backup_error": "Custom backup method failure on 'backup' step", - "backup_custom_mount_error": "Custom backup method failure on 'mount' step", - "backup_custom_need_mount_error": "Custom backup method failure on 'need_mount' step", - "backup_delete_error": "Unable to delete '{path:s}'", - "backup_deleted": "The backup has been deleted", - "backup_extracting_archive": "Extracting the backup archive…", - "backup_hook_unknown": "Backup hook '{hook:s}' unknown", - "backup_invalid_archive": "Invalid backup archive", - "backup_method_borg_finished": "Backup into borg finished", - "backup_method_copy_finished": "Backup copy finished", + "backup_creation_failed": "Could not create the backup creation", + "backup_csv_addition_failed": "Could not add files to backup into the CSV file", + "backup_csv_creation_failed": "Could not create the CSV file needed for restoration", + "backup_custom_backup_error": "Custom backup method could not get past the 'backup' step", + "backup_custom_mount_error": "Custom backup method could not get past the 'mount' step", + "backup_custom_need_mount_error": "Custom backup method could not get past 'need_mount' step", + "backup_delete_error": "Could not delete '{path:s}'", + "backup_deleted": "Backup deleted", + "backup_extracting_archive": "Extracting backup archive…", + "backup_hook_unknown": "The backup hook '{hook:s}' is unknown", + "backup_invalid_archive": "This is not a backup archive", + "backup_method_borg_finished": "Backup into Borg finished", + "backup_method_copy_finished": "Backup copy finalized", "backup_method_custom_finished": "Custom backup method '{method:s}' finished", - "backup_method_tar_finished": "Backup tar archive created", + "backup_method_tar_finished": "TAR backup archive created", "backup_mount_archive_for_restore": "Preparing archive for restoration…", - "backup_no_uncompress_archive_dir": "Uncompress archive directory doesn't exist", - "backup_nothings_done": "There is nothing to save", - "backup_output_directory_forbidden": "Forbidden output directory. Backups can't be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", - "backup_output_directory_not_empty": "The output directory is not empty", + "backup_no_uncompress_archive_dir": "There is no such uncompressed archive directory", + "backup_nothings_done": "Nothing to save", + "backup_output_directory_forbidden": "Pick a different output directory. Backups can not be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", + "backup_output_directory_not_empty": "You should pick an empty output directory", "backup_output_directory_required": "You must provide an output directory for the backup", - "backup_output_symlink_dir_broken": "You have a broken symlink instead of your archives directory '{path:s}'. You may have a specific setup to backup your data on an other filesystem, in this case you probably forgot to remount or plug your hard dirve or usb key.", + "backup_output_symlink_dir_broken": "You have a broken symlink in place of your archive directory '{path:s}'. You may have a specific setup to backup your data on another filesystem, in this case you probably forgot to remount or plug in your hard-drive or USB key.", "backup_permission": "Backup permission for app {app:s}", - "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support php7, your php apps may fail to restore (reason: {error:s})", + "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", "backup_running_hooks": "Running backup hooks…", - "backup_system_part_failed": "Unable to backup the '{part:s}' system part", - "backup_unable_to_organize_files": "Unable to organize files in the archive with the quick method", - "backup_with_no_backup_script_for_app": "App {app:s} has no backup script. Ignoring.", - "backup_with_no_restore_script_for_app": "App {app:s} has no restore script, you won't be able to automatically restore the backup of this app.", - "certmanager_acme_not_configured_for_domain": "Certificate for domain {domain:s} does not appear to be correctly installed. Please run cert-install for this domain first.", - "certmanager_attempt_to_renew_nonLE_cert": "The certificate for domain {domain:s} is not issued by Let's Encrypt. Cannot renew it automatically!", - "certmanager_attempt_to_renew_valid_cert": "The certificate for domain {domain:s} is not about to expire! (You may use --force if you know what you're doing)", + "backup_system_part_failed": "Could not backup the '{part:s}' system part", + "backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive", + "backup_with_no_backup_script_for_app": "The app '{app:s}' has no backup script. Ignoring.", + "backup_with_no_restore_script_for_app": "The '{app:s}' has no restoration script, you will not be able to automatically restore the backup of this app.", + "certmanager_acme_not_configured_for_domain": "Certificate for the domain '{domain:s}' does not appear to be correctly installed. Please run 'cert-install' for this domain first.", + "certmanager_attempt_to_renew_nonLE_cert": "The certificate for the domain '{domain:s}' is not issued by Let's Encrypt. Cannot renew it automatically!", + "certmanager_attempt_to_renew_valid_cert": "The certificate for the domain '{domain:s}' is not about to expire! (You may use --force if you know what you're doing)", "certmanager_attempt_to_replace_valid_cert": "You are attempting to overwrite a good and valid certificate for domain {domain:s}! (Use --force to bypass)", "certmanager_cannot_read_cert": "Something wrong happened when trying to open current certificate for domain {domain:s} (file: {file:s}), reason: {reason:s}", - "certmanager_cert_install_success": "Successfully installed Let's Encrypt certificate for domain {domain:s}!", - "certmanager_cert_install_success_selfsigned": "Successfully installed a self-signed certificate for domain {domain:s}!", - "certmanager_cert_renew_success": "Successfully renewed Let's Encrypt certificate for domain {domain:s}!", - "certmanager_cert_signing_failed": "Signing the new certificate failed", - "certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow…", - "certmanager_conflicting_nginx_file": "Unable to prepare domain for ACME challenge: the nginx configuration file {filepath:s} is conflicting and should be removed first", - "certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted - please try again later.", - "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use --force)", - "certmanager_domain_dns_ip_differs_from_public_ip": "The DNS 'A' record for domain {domain:s} is different from this server IP. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use --no-checks to disable those checks.)", - "certmanager_domain_http_not_working": "It seems that the domain {domain:s} cannot be accessed through HTTP. Please check your DNS and nginx configuration is okay", - "certmanager_domain_not_resolved_locally": "The domain {domain:s} cannot be resolved from inside your Yunohost server. This might happen if you recently modified your DNS record. If so, please wait a few hours for it to propagate. If the issue persists, consider adding {domain:s} to /etc/hosts. (If you know what you are doing, use --no-checks to disable those checks.)", - "certmanager_domain_unknown": "Unknown domain {domain:s}", - "certmanager_error_no_A_record": "No DNS 'A' record found for {domain:s}. You need to make your domain name point to your machine to be able to install a Let's Encrypt certificate! (If you know what you are doing, use --no-checks to disable those checks.)", - "certmanager_hit_rate_limit": "Too many certificates already issued for exact set of domains {domain:s} recently. Please try again later. See https://letsencrypt.org/docs/rate-limits/ for more details", - "certmanager_http_check_timeout": "Timed out when server tried to contact itself through HTTP using public IP address (domain {domain:s} with ip {ip:s}). You may be experiencing hairpinning issue or the firewall/router ahead of your server is misconfigured.", - "certmanager_no_cert_file": "Unable to read certificate file for domain {domain:s} (file: {file:s})", - "certmanager_self_ca_conf_file_not_found": "Configuration file not found for self-signing authority (file: {file:s})", - "certmanager_unable_to_parse_self_CA_name": "Unable to parse name of self-signing authority (file: {file:s})", - "confirm_app_install_warning": "Warning: this application may work but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", + "certmanager_cert_install_success": "Let's Encrypt certificate now installed for the domain '{domain:s}'", + "certmanager_cert_install_success_selfsigned": "Self-signed certificate now installed for the domain '{domain:s}'", + "certmanager_cert_renew_success": "Let's Encrypt certificate renewed for the domain '{domain:s}'", + "certmanager_cert_signing_failed": "Could not sign the new certificate", + "certmanager_certificate_fetching_or_enabling_failed": "It seems actually using the new certificate for {domain:s} did not work…", + "certmanager_conflicting_nginx_file": "Could not prepare domain for ACME challenge: the NGINX configuration file {filepath:s} is conflicting and should be removed first", + "certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted—please try again later.", + "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use '--force' to do so.)", + "certmanager_domain_dns_ip_differs_from_public_ip": "The DNS 'A' record for the domain '{domain:s}' is different from this server IP. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use '--no-checks' to turn off those checks.)", + "certmanager_domain_http_not_working": "It seems the domain {domain:s} cannot be accessed through HTTP. Check that your DNS and NGINX configuration is correct", + "certmanager_domain_not_resolved_locally": "The domain {domain:s} cannot be resolved from inside your YunoHost server. This might happen if you recently modified your DNS record. If so, please wait a few hours for it to propagate. If the issue persists, consider adding {domain:s} to /etc/hosts. (If you know what you are doing, use '--no-checks' to turn off those checks.)", + "certmanager_domain_unknown": "Unknown domain '{domain:s}'", + "certmanager_error_no_A_record": "No DNS 'A' record found for '{domain:s}'. You need to make your domain name point to your machine to be able to install a Let's Encrypt certificate. (If you know what you are doing, use '--no-checks' to turn off those checks.)", + "certmanager_hit_rate_limit": "Too many certificates already issued for this exact set of domains {domain:s} recently. Please try again later. See https://letsencrypt.org/docs/rate-limits/ for more details", + "certmanager_http_check_timeout": "Timed out when server tried to contact itself through HTTP using a public IP address (domain '{domain:s}' with IP '{ip:s}'). You may be experiencing a hairpinning issue, or the firewall/router ahead of your server is misconfigured.", + "certmanager_no_cert_file": "Could not read the certificate file for the domain {domain:s} (file: {file:s})", + "certmanager_self_ca_conf_file_not_found": "Could not find configuration file for self-signing authority (file: {file:s})", + "certmanager_unable_to_parse_self_CA_name": "Could not parse name of self-signing authority (file: {file:s})", + "confirm_app_install_warning": "Warning: This application may work, but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", "confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", - "confirm_app_install_thirdparty": "WARNING! Installing 3rd party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", + "confirm_app_install_thirdparty": "WARNING! Installing third-party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", - "diagnosis_debian_version_error": "Can't retrieve the Debian version: {error}", - "diagnosis_kernel_version_error": "Can't retrieve kernel version: {error}", - "diagnosis_monitor_disk_error": "Can't monitor disks: {error}", - "diagnosis_monitor_network_error": "Can't monitor network: {error}", - "diagnosis_monitor_system_error": "Can't monitor system: {error}", + "diagnosis_debian_version_error": "Could not retrieve the Debian version: {error}", + "diagnosis_kernel_version_error": "Could not retrieve kernel version: {error}", + "diagnosis_monitor_disk_error": "Could not monitor disks: {error}", + "diagnosis_monitor_network_error": "Could not monitor network: {error}", + "diagnosis_monitor_system_error": "Could not monitor system: {error}", "diagnosis_no_apps": "No installed application", - "dpkg_is_broken": "You cannot do this right now because dpkg/apt (the system package managers) seems to be in a broken state... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", + "dpkg_is_broken": "You cannot do this right now because dpkg/APT (the system package managers) seems to be in a broken state… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", "dpkg_lock_not_available": "This command can't be ran right now because another program seems to be using the lock of dpkg (the system package manager)", - "dnsmasq_isnt_installed": "dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install dnsmasq'", - "domain_cannot_remove_main": "Cannot remove main domain. Set a new main domain first", - "domain_cert_gen_failed": "Unable to generate certificate", - "domain_created": "The domain has been created", - "domain_creation_failed": "Unable to create domain", - "domain_deleted": "The domain has been deleted", - "domain_deletion_failed": "Unable to delete domain", - "domain_dns_conf_is_just_a_recommendation": "This command shows you what is the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.", - "domain_dyndns_already_subscribed": "You've already subscribed to a DynDNS domain", - "domain_dyndns_dynette_is_unreachable": "Unable to reach YunoHost dynette, either your YunoHost is not correctly connected to the internet or the dynette server is down. Error: {error}", - "domain_dyndns_invalid": "Invalid domain to use with DynDNS", + "dnsmasq_isnt_installed": "Dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install it'", + "domain_cannot_remove_main": "Cannot remove main domain. Set one first", + "domain_cert_gen_failed": "Could not generate certificate", + "domain_created": "Domain created", + "domain_creation_failed": "Could not create domain", + "domain_deleted": "Domain deleted", + "domain_deletion_failed": "Could not delete domain", + "domain_dns_conf_is_just_a_recommendation": "This command shows you the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.", + "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", + "domain_dyndns_dynette_is_unreachable": "Could not reach YunoHost dynette, either your YunoHost is not correctly connected to the Internet, or the dynette server is down. Error: {error}", + "domain_dyndns_invalid": "This domain can not be used with DynDNS", "domain_dyndns_root_unknown": "Unknown DynDNS root domain", - "domain_exists": "Domain already exists", - "domain_hostname_failed": "Failed to set new hostname. This might cause issue later (not sure about it... it might be fine).", + "domain_exists": "The domain already exists", + "domain_hostname_failed": "Could not set new hostname. This might cause an issue later (it might be fine).", "domain_uninstall_app_first": "One or more apps are installed on this domain. Please uninstall them before proceeding to domain removal", "domain_unknown": "Unknown domain", "domain_zone_exists": "DNS zone file already exists", @@ -184,80 +184,80 @@ "downloading": "Downloading…", "dyndns_could_not_check_provide": "Could not check if {provider:s} can provide {domain:s}.", "dyndns_could_not_check_available": "Could not check if {domain:s} is available on {provider:s}.", - "dyndns_cron_installed": "The DynDNS cron job has been installed", - "dyndns_cron_remove_failed": "Unable to remove the DynDNS cron job because: {error}", - "dyndns_cron_removed": "The DynDNS cron job has been removed", - "dyndns_ip_update_failed": "Unable to update IP address on DynDNS", - "dyndns_ip_updated": "Your IP address has been updated on DynDNS", - "dyndns_key_generating": "DNS key is being generated, it may take a while…", + "dyndns_cron_installed": "DynDNS cron job created", + "dyndns_cron_remove_failed": "Could not remove the DynDNS cron job because: {error}", + "dyndns_cron_removed": "DynDNS cron job removed", + "dyndns_ip_update_failed": "Could not update IP address to DynDNS", + "dyndns_ip_updated": "Updated your IP on DynDNS", + "dyndns_key_generating": "Generating DNS key… It may take a while.", "dyndns_key_not_found": "DNS key not found for the domain", - "dyndns_no_domain_registered": "No domain has been registered with DynDNS", - "dyndns_registered": "The DynDNS domain has been registered", - "dyndns_registration_failed": "Unable to register DynDNS domain: {error:s}", - "dyndns_domain_not_provided": "Dyndns provider {provider:s} cannot provide domain {domain:s}.", - "dyndns_unavailable": "Domain {domain:s} is not available.", + "dyndns_no_domain_registered": "No domain registered with DynDNS", + "dyndns_registered": "DynDNS domain registered", + "dyndns_registration_failed": "Could not register DynDNS domain: {error:s}", + "dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.", + "dyndns_unavailable": "The domain '{domain:s}' is unavailable.", "edit_group_not_allowed": "You are not allowed to edit the group {group:s}", - "edit_permission_with_group_all_users_not_allowed": "You are not allowed to edit permission for group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.", - "error_when_removing_sftpuser_group": "Error when trying remove sftpusers group", + "edit_permission_with_group_all_users_not_allowed": "You are not allowed to edit permission for the group 'all_users', use 'yunohost user permission clear APP' or 'yunohost user permission add APP -u USER' instead.", + "error_when_removing_sftpuser_group": "Could not remove the sftpusers group", "executing_command": "Executing command '{command:s}'…", "executing_script": "Executing script '{script:s}'…", "extracting": "Extracting…", - "experimental_feature": "Warning: this feature is experimental and not consider stable, you shouldn't be using it except if you know what you are doing.", + "experimental_feature": "Warning: This feature is experimental and not considered stable, you should not use it unless you know what you are doing.", "field_invalid": "Invalid field '{:s}'", - "file_does_not_exist": "The file {path:s} does not exists.", - "firewall_reload_failed": "Unable to reload the firewall", - "firewall_reloaded": "The firewall has been reloaded", - "firewall_rules_cmd_failed": "Some firewall rules commands have failed. For more information, see the log.", + "file_does_not_exist": "The file {path:s} does not exist.", + "firewall_reload_failed": "Could not reload the firewall", + "firewall_reloaded": "Firewall reloaded", + "firewall_rules_cmd_failed": "Some firewall rules commands have failed. More info in log.", "format_datetime_short": "%m/%d/%Y %I:%M %p", - "global_settings_bad_choice_for_enum": "Bad choice for setting {setting:s}, received '{choice:s}' but available choices are : {available_choices:s}", - "global_settings_bad_type_for_setting": "Bad type for setting {setting:s}, received {received_type:s}, except {expected_type:s}", - "global_settings_cant_open_settings": "Failed to open settings file, reason: {reason:s}", - "global_settings_cant_serialize_settings": "Failed to serialize settings data, reason: {reason:s}", - "global_settings_cant_write_settings": "Failed to write settings file, reason: {reason:s}", - "global_settings_key_doesnt_exists": "The key '{settings_key:s}' doesn't exists in the global settings, you can see all the available keys by doing 'yunohost settings list'", - "global_settings_reset_success": "Success. Your previous settings have been backuped in {path:s}", + "global_settings_bad_choice_for_enum": "Bad choice for setting {setting:s}, received '{choice:s}', but available choices are: {available_choices:s}", + "global_settings_bad_type_for_setting": "Bad type for setting {setting:s}, received {received_type:s}, expected {expected_type:s}", + "global_settings_cant_open_settings": "Could not open settings file, reason: {reason:s}", + "global_settings_cant_serialize_settings": "Could not serialize settings data, reason: {reason:s}", + "global_settings_cant_write_settings": "Could not save settings file, reason: {reason:s}", + "global_settings_key_doesnt_exists": "The key '{settings_key:s}' does not exist in the global settings, you can see all the available keys by running 'yunohost settings list'", + "global_settings_reset_success": "Previous settings now backed up to {path:s}", "global_settings_setting_example_bool": "Example boolean option", "global_settings_setting_example_enum": "Example enum option", "global_settings_setting_example_int": "Example int option", "global_settings_setting_example_string": "Example string option", - "global_settings_setting_security_nginx_compatibility": "Compatibility vs. security tradeoff for the web server nginx. Affects the ciphers (and other security-related aspects)", + "global_settings_setting_security_nginx_compatibility": "Compatibility vs. security tradeoff for the web server NGINX. Affects the ciphers (and other security-related aspects)", "global_settings_setting_security_password_admin_strength": "Admin password strength", "global_settings_setting_security_password_user_strength": "User password strength", "global_settings_setting_security_ssh_compatibility": "Compatibility vs. security tradeoff for the SSH server. Affects the ciphers (and other security-related aspects)", "global_settings_setting_security_postfix_compatibility": "Compatibility vs. security tradeoff for the Postfix server. Affects the ciphers (and other security-related aspects)", - "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/settings-unknown.json", + "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard it and save it in /etc/yunohost/settings-unknown.json", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration", - "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.", - "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", - "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).", - "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' enabled for app '{app:s}'", - "group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' disabled for app '{app:s}'", + "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.", + "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at-least 8 characters—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).", + "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters—though it is good practice to use longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", + "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' turned on for the app '{app:s}'", + "group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' turned off for the app '{app:s}'", "group_name_already_exist": "Group {name:s} already exist", - "group_created": "Group '{group}' successfully created", - "group_creation_failed": "Group creation failed for group '{group}'", + "group_created": "Group '{group}' created", + "group_creation_failed": "Could not create the group '{group}'", "group_deleted": "Group '{group}' deleted", - "group_deletion_failed": "Group '{group} 'deletion failed", + "group_deletion_failed": "Could not delete the group '{group}'", "group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.", - "group_info_failed": "Group info failed", - "group_unknown": "Group {group:s} unknown", + "group_info_failed": "Could not present group info", + "group_unknown": "The group '{group:s}' is unknown", "group_updated": "Group '{group}' updated", - "group_update_failed": "Group update failed for group '{group}'", - "hook_exec_failed": "Script execution failed: {path:s}", - "hook_exec_not_terminated": "Script execution did not finish properly: {path:s}", - "hook_json_return_error": "Failed to read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}", - "hook_list_by_invalid": "Invalid property to list hook by", + "group_update_failed": "Could not update the group '{group}'", + "hook_exec_failed": "Could not run script: {path:s}", + "hook_exec_not_terminated": "Script did not finish properly: {path:s}", + "hook_json_return_error": "Could not read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}", + "hook_list_by_invalid": "This property can not be used to list hooks", "hook_name_unknown": "Unknown hook name '{name:s}'", "installation_complete": "Installation complete", - "installation_failed": "Installation failed", - "invalid_url_format": "Invalid URL format", + "installation_failed": "Something went wrong with the installation", + "invalid_url_format": "Something is wrong with the URL", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", - "log_corrupted_md_file": "The yaml metadata file associated with logs is corrupted: '{md_file}\nError: {error}'", + "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", "log_category_404": "The log category '{category}' does not exist", "log_link_to_log": "Full log of this operation: '{desc}'", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'", - "log_link_to_failed_log": "The operation '{desc}' has failed! To get help, please provide the full log of this operation by clicking here", - "log_help_to_get_failed_log": "The operation '{desc}' has failed! To get help, please share the full log of this operation using the command 'yunohost log display {name} --share'", + "log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by clicking here to get help", + "log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log display {name} --share' to get help", "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'", "log_operation_unit_unclosed_properly": "Operation unit has not been closed properly", "log_app_addaccess": "Add access to '{}'", @@ -265,11 +265,11 @@ "log_app_clearaccess": "Remove all access to '{}'", "log_app_fetchlist": "Add an application list", "log_app_removelist": "Remove an application list", - "log_app_change_url": "Change the url of '{}' application", - "log_app_install": "Install '{}' application", - "log_app_remove": "Remove '{}' application", - "log_app_upgrade": "Upgrade '{}' application", - "log_app_makedefault": "Make '{}' as default application", + "log_app_change_url": "Change the URL of '{}' application", + "log_app_install": "Install the '{}' application", + "log_app_remove": "Remove the '{}' application", + "log_app_upgrade": "Upgrade the '{}' application", + "log_app_makedefault": "Make '{}' the default application", "log_available_on_yunopaste": "This log is now available via {url}", "log_backup_restore_system": "Restore system from a backup archive", "log_backup_restore_app": "Restore '{}' from a backup archive", @@ -278,9 +278,9 @@ "log_domain_add": "Add '{}' domain into system configuration", "log_domain_remove": "Remove '{}' domain from system configuration", "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", - "log_dyndns_update": "Update the ip associated with your YunoHost subdomain '{}'", - "log_letsencrypt_cert_install": "Install Let's encrypt certificate on '{}' domain", - "log_permission_add": "Add permission '{}' for app '{}'", + "log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'", + "log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain", + "log_permission_add": "Add the '{}' permission for the app '{}'", "log_permission_remove": "Remove permission '{}'", "log_permission_update": "Update permission '{}' for app '{}'", "log_selfsigned_cert_install": "Install self signed certificate on '{}' domain", @@ -291,104 +291,104 @@ "log_user_group_add": "Add '{}' group", "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", - "log_user_update": "Update information of '{}' user", + "log_user_update": "Update info of '{}' user", "log_user_permission_add": "Update '{}' permission", "log_user_permission_remove": "Update '{}' permission", - "log_tools_maindomain": "Make '{}' as main domain", + "log_tools_maindomain": "Make '{}' the main domain", "log_tools_migrations_migrate_forward": "Migrate forward", "log_tools_postinstall": "Postinstall your YunoHost server", "log_tools_upgrade": "Upgrade system packages", "log_tools_shutdown": "Shutdown your server", "log_tools_reboot": "Reboot your server", - "ldap_init_failed_to_create_admin": "LDAP initialization failed to create admin user", - "ldap_initialized": "LDAP has been initialized", + "ldap_init_failed_to_create_admin": "LDAP initialization could not create admin user", + "ldap_initialized": "LDAP initialized", "license_undefined": "undefined", - "mail_alias_remove_failed": "Unable to remove mail alias '{mail:s}'", - "mail_domain_unknown": "Unknown mail address domain '{domain:s}'", - "mail_forward_remove_failed": "Unable to remove mail forward '{mail:s}'", - "mailbox_disabled": "Mailbox disabled for user {user:s}", - "mailbox_used_space_dovecot_down": "Dovecot mailbox service need to be up, if you want to get mailbox used space", - "mail_unavailable": "This email address is reserved and shall be automatically allocated to the very first user", - "maindomain_change_failed": "Unable to change the main domain", - "maindomain_changed": "The main domain has been changed", - "migrate_tsig_end": "Migration to hmac-sha512 finished", - "migrate_tsig_failed": "Migrating the dyndns domain {domain} to hmac-sha512 failed, rolling back. Error: {error_code} - {error}", - "migrate_tsig_start": "Not secure enough key algorithm detected for TSIG signature of domain '{domain}', initiating migration to the more secure one hmac-sha512", - "migrate_tsig_wait": "Let's wait 3min for the dyndns server to take the new key into account…", + "mail_alias_remove_failed": "Could not remove e-mail alias '{mail:s}'", + "mail_domain_unknown": "Unknown e-mail address for domain '{domain:s}'", + "mail_forward_remove_failed": "Could not remove e-mail forwarding '{mail:s}'", + "mailbox_disabled": "E-mail turned off for user {user:s}", + "mailbox_used_space_dovecot_down": "the Dovecot mailbox service needs to be up, if you want to fetch used mailbox space", + "mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user", + "maindomain_change_failed": "Could not change the main domain", + "maindomain_changed": "The main domain now changed", + "migrate_tsig_end": "Migration to HMAC-SHA-512 finished", + "migrate_tsig_failed": "Could not migrate the DynDNS domain '{domain}' to HMAC-SHA-512, rolling back. Error: {error_code}, {error}", + "migrate_tsig_start": "Insufficiently secure key algorithm detected for TSIG signature of the domain '{domain}', initiating migration to the more secure HMAC-SHA-512", + "migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account…", "migrate_tsig_wait_2": "2min…", "migrate_tsig_wait_3": "1min…", - "migrate_tsig_wait_4": "30 secondes…", - "migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed!", + "migrate_tsig_wait_4": "30 seconds…", + "migrate_tsig_not_needed": "You do not appear to use a DynDNS domain, so no migration is needed.", "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", - "migration_description_0002_migrate_to_tsig_sha256": "Improve security of dyndns TSIG by using SHA512 instead of MD5", + "migration_description_0002_migrate_to_tsig_sha256": "Improve security of DynDNS TSIG updates by using SHA-512 instead of MD5", "migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0", "migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5", - "migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from postgresql 9.4 to 9.6", + "migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from PostgreSQL 9.4 to 9.6", "migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords", "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)", "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)", "migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services", - "migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead", - "migration_description_0011_setup_group_permission": "Setup user group and setup permission for apps and services", - "migration_description_0012_postgresql_password_to_md5_authentication": "Force postgresql authentication to use md5 for local connections", + "migration_description_0010_migrate_to_apps_json": "Remove deprecated applists and use the new unified 'apps.json' list instead", + "migration_description_0011_setup_group_permission": "Set up user group and set up permission for apps and services", + "migration_description_0012_postgresql_password_to_md5_authentication": "Force PostgreSQL authentication to use MD5 for local connections", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists…", "migration_0003_main_upgrade": "Starting main upgrade…", - "migration_0003_fail2ban_upgrade": "Starting the fail2ban upgrade…", - "migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset back to its original state first… The previous file will be available as {backup_dest}.", - "migration_0003_yunohost_upgrade": "Starting the yunohost package upgrade… The migration will end, but the actual upgrade will happen right after. After the operation is complete, you might have to re-log on the webadmin.", - "migration_0003_not_jessie": "The current debian distribution is not Jessie!", - "migration_0003_system_not_fully_up_to_date": "Your system is not fully up to date. Please perform a regular upgrade before running the migration to stretch.", - "migration_0003_still_on_jessie_after_main_upgrade": "Something wrong happened during the main upgrade: system is still on Jessie!? To investigate the issue, please look at {log}:s…", - "migration_0003_general_warning": "Please note that this migration is a delicate operation. While the YunoHost team did its best to review and test it, the migration might still break parts of the system or apps.\n\nTherefore, we recommend you to:\n - Perform a backup of any critical data or app. More infos on https://yunohost.org/backup;\n - Be patient after launching the migration: depending on your internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external email clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port 465 will automatically be closed and the new port 587 will be opened in the firewall. You and your users *will* have to adapt the configuration of your email clients accordingly!", - "migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an applist or are not flagged as 'working'. Consequently, we cannot guarantee that they will still work after the upgrade: {problematic_apps}", - "migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten at the end of the upgrade: {manually_modified_files}", - "migration_0005_postgresql_94_not_installed": "Postgresql was not installed on your system. Nothing to do!", - "migration_0005_postgresql_96_not_installed": "Postgresql 9.4 has been found to be installed, but not postgresql 9.6!? Something weird might have happened on your system:(…", - "migration_0005_not_enough_space": "Not enough space is available in {path} to run the migration right now:(.", + "migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade…", + "migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset to its original state first… The previous file will be available as {backup_dest}.", + "migration_0003_yunohost_upgrade": "Starting the YunoHost package upgrade… The migration will end, but the actual upgrade will happen immediately afterwards. After the operation is complete, you might have to log in on the webadmin page again.", + "migration_0003_not_jessie": "The current Debian distribution is not Jessie!", + "migration_0003_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Stretch.", + "migration_0003_still_on_jessie_after_main_upgrade": "Something went wrong during the main upgrade: Is the system still on Jessie‽ To investigate the issue, please look at {log}:s…", + "migration_0003_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external e-mail clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port (465) will automatically be closed, and the new port (587) will be opened in the firewall. You and your users *will* have to adapt the configuration of your e-mail clients accordingly.", + "migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an applist, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}", + "migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}", + "migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", + "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system:(…", + "migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.", "migration_0006_disclaimer": "YunoHost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", "migration_0007_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.", "migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", - "migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH configuration differs from the recommended configuration. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change in the following way:", - "migration_0008_port": " - you will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;", - "migration_0008_root": " - you will not be able to connect as root through SSH. Instead you should use the admin user;", - "migration_0008_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;", + "migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:", + "migration_0008_port": "• You will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;", + "migration_0008_root": "• You will not be able to connect as root through SSH. Instead you should use the admin user;", + "migration_0008_dsa": "• The DSA key will be turned off. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;", "migration_0008_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", - "migration_0008_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;)! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", - "migration_0009_not_needed": "This migration already happened somehow ? Skipping.", + "migration_0008_no_warning": "No major risk indentified concerning overriding your SSH configuration—one can however not be absolutely sure ;)! Run the migration to override it. Otherwise, you can also skip the migration - though it is not recommended.", + "migration_0009_not_needed": "This migration already happened somehow… (?) Skipping.", "migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.", - "migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}", - "migration_0011_create_group": "Creating a group for each user...", - "migration_0011_done": "Migration successful. You are now able to manage groups of users.", - "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration need to be updated.\nYou need to save your actual configuration, reintialize the original configuration by the command 'yunohost tools regen-conf -f' and after retry the migration", - "migration_0011_LDAP_update_failed": "LDAP update failed. Error: {error:s}", - "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...", - "migration_0011_migration_failed_trying_to_rollback": "Migration failed ... trying to rollback the system.", - "migration_0011_rollback_success": "Rollback succeeded.", - "migration_0011_update_LDAP_database": "Updating LDAP database...", - "migration_0011_update_LDAP_schema": "Updating LDAP schema...", - "migrations_already_ran": "Those migrations have already been ran: {ids}", - "migrations_cant_reach_migration_file": "Can't access migrations files at path %s", - "migrations_dependencies_not_satisfied": "Can't run migration {id} because first you need to run these migrations: {dependencies_id}", - "migrations_failed_to_load_migration": "Failed to load migration {id} : {error}", - "migrations_exclusive_options": "--auto, --skip, and --force-rerun are exclusive options.", - "migrations_list_conflict_pending_done": "You cannot use both --previous and --done at the same time.", + "migration_0011_can_not_backup_before_migration": "Could not back up the system prior to migration. Error: {error:s}", + "migration_0011_create_group": "Creating a group for each user…", + "migration_0011_done": "Migration successful. You are now able to manage usergroups.", + "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your actual configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration", + "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", + "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…", + "migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.", + "migration_0011_rollback_success": "System rolled back.", + "migration_0011_update_LDAP_database": "Updating LDAP database…", + "migration_0011_update_LDAP_schema": "Updating LDAP schema…", + "migrations_already_ran": "Those migrations are already done: {ids}", + "migrations_cant_reach_migration_file": "Could not access migrations files at path %s", + "migrations_dependencies_not_satisfied": "Cannot run migration {id} because first you need to run these migrations: {dependencies_id}", + "migrations_failed_to_load_migration": "Could not load migration {id}: {error}", + "migrations_exclusive_options": "'--auto', '--skip', and '--force-rerun' are mutually exclusive options.", + "migrations_list_conflict_pending_done": "You cannot use both '--previous' and '--done' at the same time.", "migrations_loading_migration": "Loading migration {id}…", - "migrations_migration_has_failed": "Migration {id} has failed, aborting. Error: {exception}", - "migrations_must_provide_explicit_targets": "You must provide explicit targets when using --skip or --force-rerun", - "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option --accept-disclaimer.", + "migrations_migration_has_failed": "Migration {id} did not complete, aborting. Error: {exception}", + "migrations_must_provide_explicit_targets": "You must provide explicit targets when using '--skip' or '--force-rerun'", + "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option '--accept-disclaimer'.", "migrations_no_migrations_to_run": "No migrations to run", - "migrations_no_such_migration": "No such migration called {id}", - "migrations_not_pending_cant_skip": "Those migrations are not pending so cannot be skipped: {ids}", - "migrations_pending_cant_rerun": "Those migrations are still pending so cannot be reran: {ids}", + "migrations_no_such_migration": "There is no migration called {id}", + "migrations_not_pending_cant_skip": "Those migrations are not pending, so cannot be skipped: {ids}", + "migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}", "migrations_running_forward": "Running migration {id}…", "migrations_skip_migration": "Skipping migration {id}…", - "migrations_success_forward": "Successfully ran migration {id}!", - "migrations_to_be_ran_manually": "Migration {id} has to be ran manually. Please go to Tools > Migrations on the webadmin, or run `yunohost tools migrations migrate`.", - "monitor_disabled": "The server monitoring has been disabled", - "monitor_enabled": "The server monitoring has been enabled", - "monitor_glances_con_failed": "Unable to connect to Glances server", - "monitor_not_enabled": "Server monitoring is not enabled", + "migrations_success_forward": "Ran migration {id}", + "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", + "monitor_disabled": "Server monitoring now turned off", + "monitor_enabled": "Server monitoring now turned on", + "monitor_glances_con_failed": "Could not connect to Glances server", + "monitor_not_enabled": "Server monitoring is off", "monitor_period_invalid": "Invalid time period", "monitor_stats_file_not_found": "Statistics file not found", "monitor_stats_no_update": "No monitoring statistics to update", @@ -396,190 +396,190 @@ "mountpoint_unknown": "Unknown mountpoint", "mysql_db_creation_failed": "MySQL database creation failed", "mysql_db_init_failed": "MySQL database init failed", - "mysql_db_initialized": "The MySQL database has been initialized", - "need_define_permission_before": "You need to redefine the permission using 'yunohost user permission add -u USER' before removing an allowed group", + "mysql_db_initialized": "The MySQL database now initialized", + "need_define_permission_before": "Redefine the permission using 'yunohost user permission add -u USER' before removing an allowed group", "network_check_mx_ko": "DNS MX record is not set", - "network_check_smtp_ko": "Outbound mail (SMTP port 25) seems to be blocked by your network", - "network_check_smtp_ok": "Outbound mail (SMTP port 25) is not blocked", + "network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network", + "network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked", "new_domain_required": "You must provide the new main domain", "no_appslist_found": "No app list found", - "no_internet_connection": "Server is not connected to the Internet", + "no_internet_connection": "Server not connected to the Internet", "no_ipv6_connectivity": "IPv6 connectivity is not available", "no_restore_script": "No restore script found for the app '{app:s}'", - "not_enough_disk_space": "Not enough free disk space on '{path:s}'", + "not_enough_disk_space": "Not enough free space on '{path:s}'", "package_not_installed": "Package '{pkgname}' is not installed", "package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'", "package_unknown": "Unknown package '{pkgname}'", "packages_upgrade_critical_later": "Critical packages ({packages:s}) will be upgraded later", - "packages_upgrade_failed": "Unable to upgrade all of the packages", - "password_listed": "This password is among the most used password in the world. Please choose something a bit more unique.", - "password_too_simple_1": "Password needs to be at least 8 characters long", - "password_too_simple_2": "Password needs to be at least 8 characters long and contains digit, upper and lower characters", - "password_too_simple_3": "Password needs to be at least 8 characters long and contains digit, upper, lower and special characters", - "password_too_simple_4": "Password needs to be at least 12 characters long and contains digit, upper, lower and special characters", - "path_removal_failed": "Unable to remove path {:s}", - "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, and alphanumeric and -_. characters only", + "packages_upgrade_failed": "Could not upgrade all the packages", + "password_listed": "This password is among the most used password in the world. Please choose something more unique.", + "password_too_simple_1": "The password needs to be at least 8 characters long", + "password_too_simple_2": "The password needs to be at least 8 characters long and contain a digit, upper and lower characters", + "password_too_simple_3": "The password needs to be at least 8 characters long and contain a digit, upper, lower and special characters", + "password_too_simple_4": "The password needs to be at least 12 characters long and contain a digit, upper, lower and special characters", + "path_removal_failed": "Could not remove path {:s}", + "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, alphanumeric and -_. characters only", "pattern_domain": "Must be a valid domain name (e.g. my-domain.org)", "pattern_email": "Must be a valid email address (e.g. someone@domain.org)", "pattern_firstname": "Must be a valid first name", "pattern_lastname": "Must be a valid last name", "pattern_listname": "Must be alphanumeric and underscore characters only", - "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to disable the quota", + "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not havea quota", "pattern_password": "Must be at least 3 characters long", "pattern_port": "Must be a valid port number (i.e. 0-65535)", "pattern_port_or_range": "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)", "pattern_positive_number": "Must be a positive number", "pattern_username": "Must be lower-case alphanumeric and underscore characters only", - "pattern_password_app": "Sorry, passwords should not contain the following characters: {forbidden_chars}", + "pattern_password_app": "Sorry, passwords can not contain the following characters: {forbidden_chars}", "permission_already_clear": "Permission '{permission:s}' already clear for app {app:s}", "permission_already_exist": "Permission '{permission:s}' for app {app:s} already exist", "permission_created": "Permission '{permission:s}' for app {app:s} created", - "permission_creation_failed": "Permission creation failed", + "permission_creation_failed": "Could not grant permission", "permission_deleted": "Permission '{permission:s}' for app {app:s} deleted", - "permission_deletion_failed": "Permission '{permission:s}' for app {app:s} deletion failed", - "permission_not_found": "Permission '{permission:s}' not found for application {app:s}", - "permission_name_not_valid": "Permission name '{permission:s}' not valid", - "permission_update_failed": "Permission update failed", - "permission_generated": "The permission database has been updated", - "permission_updated": "Permission '{permission:s}' for app {app:s} updated", + "permission_deletion_failed": "Missing permission '{permission:s}' to delete the app '{app:s}'", + "permission_not_found": "Permission '{permission:s}' not found for the application '{app:s}'", + "permission_name_not_valid": "Pick an allowed permission name for '{permission:s}'", + "permission_update_failed": "Could not update permission", + "permission_generated": "Permission database updated", + "permission_updated": "Permission '{permission:s}' for the app '{app:s}' updated", "permission_update_nothing_to_do": "No permissions to update", "port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections", "port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections", "port_available": "Port {port:d} is available", "port_unavailable": "Port {port:d} is not available", - "recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create ' or the admin interface.", + "recommend_to_add_first_user": "The post-install is finished, but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create ' or do it from the admin interface.", "remove_main_permission_not_allowed": "Removing the main permission is not allowed", - "remove_user_of_group_not_allowed": "You are not allowed to remove the user {user:s} in the group {group:s}", - "regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'", - "regenconf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'", - "regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but has been kept back.", + "remove_user_of_group_not_allowed": "You are not allowed to remove the user '{user:s}' in the group '{group:s}'", + "regenconf_file_backed_up": "Configuration file '{conf}' backed up to '{backup}'", + "regenconf_file_copy_failed": "Could not copy the new configuration file '{new}' to '{conf}'", + "regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but was kept back.", "regenconf_file_manually_modified": "The configuration file '{conf}' has been manually modified and will not be updated", - "regenconf_file_manually_removed": "The configuration file '{conf}' has been manually removed and will not be created", - "regenconf_file_remove_failed": "Unable to remove the configuration file '{conf}'", - "regenconf_file_removed": "The configuration file '{conf}' has been removed", - "regenconf_file_updated": "The configuration file '{conf}' has been updated", + "regenconf_file_manually_removed": "The configuration file '{conf}' was removed manually, and will not be created", + "regenconf_file_remove_failed": "Could not remove the configuration file '{conf}'", + "regenconf_file_removed": "Configuration file '{conf}' removed", + "regenconf_file_updated": "Configuration file '{conf}' updated", "regenconf_now_managed_by_yunohost": "The configuration file '{conf}' is now managed by YunoHost (category {category}).", "regenconf_up_to_date": "The configuration is already up-to-date for category '{category}'", - "regenconf_updated": "The configuration has been updated for category '{category}'", + "regenconf_updated": "Configuration for category '{category}' updated", "regenconf_would_be_updated": "The configuration would have been updated for category '{category}'", "regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…", - "regenconf_failed": "Unable to regenerate the configuration for category(s): {categories}", + "regenconf_failed": "Could not regenerate the configuration for category(s): {categories}", "regenconf_pending_applying": "Applying pending configuration for category '{category}'…", - "restore_action_required": "You must specify something to restore", - "restore_already_installed_app": "An app is already installed with the id '{app:s}'", - "restore_app_failed": "Unable to restore the app '{app:s}'", - "restore_cleaning_failed": "Unable to clean-up the temporary restoration directory", - "restore_complete": "Restore complete", + "restore_action_required": "You must pick something to restore", + "restore_already_installed_app": "An app with the ID '{app:s}' is already installed", + "restore_app_failed": "Could not restore the app '{app:s}'", + "restore_cleaning_failed": "Could not clean up the temporary restoration directory", + "restore_complete": "Restored", "restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]", "restore_extracting": "Extracting needed files from the archive…", - "restore_failed": "Unable to restore the system", - "restore_hook_unavailable": "Restoration script for '{part:s}' not available on your system and not in the archive either", - "restore_may_be_not_enough_disk_space": "Your system seems not to have enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", + "restore_failed": "Could not restore system", + "restore_hook_unavailable": "The restoration script for '{part:s}' not available on your system and not in the archive either", + "restore_may_be_not_enough_disk_space": "Your system seems does not have enough space (free: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", "restore_mounting_archive": "Mounting archive into '{path:s}'", - "restore_not_enough_disk_space": "Not enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", - "restore_nothings_done": "Nothing has been restored", - "restore_removing_tmp_dir_failed": "Unable to remove an old temporary directory", - "restore_running_app_script": "Running restore script of app '{app:s}'…", + "restore_not_enough_disk_space": "Not enough space (space: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", + "restore_nothings_done": "Nothing was restored", + "restore_removing_tmp_dir_failed": "Could not remove an old temporary directory", + "restore_running_app_script": "Restoring the app '{app:s}'…", "restore_running_hooks": "Running restoration hooks…", - "restore_system_part_failed": "Unable to restore the '{part:s}' system part", - "root_password_desynchronized": "The admin password has been changed, but YunoHost was unable to propagate this on the root password!", + "restore_system_part_failed": "Could not restore the '{part:s}' system part", + "root_password_desynchronized": "The admin password was changed, but YunoHost could not propagate this to the root password!", "root_password_replaced_by_admin_password": "Your root password have been replaced by your admin password.", - "server_shutdown": "The server will shutdown", + "server_shutdown": "The server will shut down", "server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]", "server_reboot": "The server will reboot", "server_reboot_confirm": "The server will reboot immediatly, are you sure? [{answers:s}]", - "service_add_failed": "Unable to add service '{service:s}'", - "service_added": "The service '{service:s}' has been added", - "service_already_started": "Service '{service:s}' has already been started", - "service_already_stopped": "Service '{service:s}' has already been stopped", - "service_cmd_exec_failed": "Unable to execute command '{command:s}'", - "service_description_avahi-daemon": "allows to reach your server using yunohost.local on your local network", - "service_description_dnsmasq": "handles domain name resolution (DNS)", - "service_description_dovecot": "allows e-mail client to access/fetch email (via IMAP and POP3)", - "service_description_fail2ban": "protects against bruteforce and other kind of attacks from the Internet", - "service_description_glances": "monitors system information on your server", - "service_description_metronome": "manage XMPP instant messaging accounts", - "service_description_mysql": "stores applications data (SQL database)", - "service_description_nginx": "serves or provides access to all the websites hosted on your server", - "service_description_nslcd": "handles YunoHost user shell connection", - "service_description_php7.0-fpm": "runs applications written in PHP with nginx", - "service_description_postfix": "used to send and receive emails", - "service_description_redis-server": "a specialized database used for rapid data access, task queue and communication between programs", - "service_description_rmilter": "checks various parameters in emails", - "service_description_rspamd": "filters spam, and other email-related features", - "service_description_slapd": "stores users, domains and related information", - "service_description_ssh": "allows you to connect remotely to your server via a terminal (SSH protocol)", - "service_description_yunohost-api": "manages interactions between the YunoHost web interface and the system", - "service_description_yunohost-firewall": "manages open and close connexion ports to services", - "service_disable_failed": "Unable to disable service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_disabled": "The service '{service:s}' has been disabled", - "service_enable_failed": "Unable to enable service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_enabled": "The service '{service:s}' has been enabled", + "service_add_failed": "Could not add the service '{service:s}'", + "service_added": "The service '{service:s}' added", + "service_already_started": "The service '{service:s}' has already been started", + "service_already_stopped": "The service '{service:s}' has already been stopped", + "service_cmd_exec_failed": "Could not execute the command '{command:s}'", + "service_description_avahi-daemon": "allows you to reach your server using 'yunohost.local' in your local network", + "service_description_dnsmasq": "Handles domain name resolution (DNS)", + "service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)", + "service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet", + "service_description_glances": "Monitors system info on your server", + "service_description_metronome": "Manage XMPP instant messaging accounts", + "service_description_mysql": "Stores applications data (SQL database)", + "service_description_nginx": "Serves or provides access to all the websites hosted on your server", + "service_description_nslcd": "Handles YunoHost user shell connection", + "service_description_php7.0-fpm": "runs applications written in PHP with NGINX", + "service_description_postfix": "Used to send and receive e-mails", + "service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs", + "service_description_rmilter": "Checks various parameters in e-mails", + "service_description_rspamd": "Filters spam, and other e-mail related features", + "service_description_slapd": "Stores users, domains and related info", + "service_description_ssh": "Allows you to connect remotely to your server via a terminal (SSH protocol)", + "service_description_yunohost-api": "Manages interactions between the YunoHost web interface and the system", + "service_description_yunohost-firewall": "Manages open and close connexion ports to services", + "service_disable_failed": "Could not turn off the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_disabled": "'{service:s}' service turned off", + "service_enable_failed": "Could not turn on the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_enabled": "'{service:s}' service turned off", "service_no_log": "No log to display for service '{service:s}'", "service_regen_conf_is_deprecated": "'yunohost service regen-conf' is deprecated! Please use 'yunohost tools regen-conf' instead.", - "service_remove_failed": "Unable to remove service '{service:s}'", - "service_removed": "The service '{service:s}' has been removed", - "service_reload_failed": "Unable to reload service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_reloaded": "The service '{service:s}' has been reloaded", - "service_restart_failed": "Unable to restart service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_restarted": "The service '{service:s}' has been restarted", - "service_reload_or_restart_failed": "Unable to reload or restart service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_reloaded_or_restarted": "The service '{service:s}' has been reloaded or restarted", - "service_start_failed": "Unable to start service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_started": "The service '{service:s}' has been started", - "service_status_failed": "Unable to determine status of service '{service:s}'", - "service_stop_failed": "Unable to stop service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_stopped": "The service '{service:s}' has been stopped", + "service_remove_failed": "Could not remove the service '{service:s}'", + "service_removed": "'{service:s}' service removed", + "service_reload_failed": "Could not reload the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_reloaded": "'{service:s}' service reloaded", + "service_restart_failed": "Could not restart the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_restarted": "'{service:s}' service restarted", + "service_reload_or_restart_failed": "Could not reload or restart the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_reloaded_or_restarted": "'{service:s}' service reloaded or restarted", + "service_start_failed": "Could not start the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_started": "'{service:s}' service started", + "service_status_failed": "Could not determine status of the service '{service:s}'", + "service_stop_failed": "Could not stop the service '{service:s}'\n\nRecent service logs:{logs:s}", + "service_stopped": "'{service:s}' service stopped", "service_unknown": "Unknown service '{service:s}'", - "ssowat_conf_generated": "The SSOwat configuration has been generated", - "ssowat_conf_updated": "The SSOwat configuration has been updated", - "ssowat_persistent_conf_read_error": "Error while reading SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", - "ssowat_persistent_conf_write_error": "Error while saving SSOwat persistent configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", + "ssowat_conf_generated": "SSOwat configuration generated", + "ssowat_conf_updated": "SSOwat configuration updated", + "ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", + "ssowat_persistent_conf_write_error": "Could not save persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", "system_groupname_exists": "Groupname already exists in the system group", - "system_upgraded": "The system has been upgraded", - "system_username_exists": "Username already exists in the system users", - "this_action_broke_dpkg": "This action broke dpkg/apt (the system package managers)... You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", - "tools_update_failed_to_app_fetchlist": "Failed to update YunoHost's applists because: {error}", - "tools_upgrade_at_least_one": "Please specify --apps OR --system", + "system_upgraded": "System upgraded", + "system_username_exists": "Username already exists in the list of system users", + "this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers)… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", + "tools_update_failed_to_app_fetchlist": "Could not update YunoHost's applists because: {error}", + "tools_upgrade_at_least_one": "Please specify '--apps', or '--system'", "tools_upgrade_cant_both": "Cannot upgrade both system and apps at the same time", - "tools_upgrade_cant_hold_critical_packages": "Unable to hold critical packages ...", - "tools_upgrade_cant_unhold_critical_packages": "Unable to unhold critical packages ...", - "tools_upgrade_regular_packages": "Now upgrading 'regular' (non-yunohost-related) packages ...", - "tools_upgrade_regular_packages_failed": "Unable to upgrade packages: {packages_list}", - "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages ...", - "tools_upgrade_special_packages_explanation": "This action will end but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it's done, you may have to re-log on the webadmin. The upgrade log will be available in Tools > Log (in the webadmin) or through 'yunohost log list' (in command line).", - "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed !\nPress [Enter] to get the command line back", + "tools_upgrade_cant_hold_critical_packages": "Could not hold critical packages…", + "tools_upgrade_cant_unhold_critical_packages": "Could not to unhold critical packages…", + "tools_upgrade_regular_packages": "Now upgrading 'regular' (non-yunohost-related) packages…", + "tools_upgrade_regular_packages_failed": "Could not upgrade packages: {packages_list}", + "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages…", + "tools_upgrade_special_packages_explanation": "This action will end, but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it i done, you may have to log in on the webadmin page again. The upgrade log will be available in Tools → Log (on the webadmin page) or through 'yunohost log list' (from the command line).", + "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed.\nPress [Enter] to get the command line back", "unbackup_app": "App '{app:s}' will not be saved", - "unexpected_error": "An unexpected error occured: {error}", + "unexpected_error": "Something unexpected went wrong: {error}", "unit_unknown": "Unknown unit '{unit:s}'", "unlimit": "No quota", "unrestore_app": "App '{app:s}' will not be restored", - "update_apt_cache_failed": "Unable to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}", - "update_apt_cache_warning": "Some errors happened while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}", + "update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", + "update_apt_cache_warning": "Something went wrong while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "updating_apt_cache": "Fetching available upgrades for system packages…", "updating_app_lists": "Fetching available upgrades for applications…", "upgrade_complete": "Upgrade complete", "upgrading_packages": "Upgrading packages…", "upnp_dev_not_found": "No UPnP device found", - "upnp_disabled": "UPnP has been disabled", - "upnp_enabled": "UPnP has been enabled", - "upnp_port_open_failed": "Unable to open UPnP ports", - "user_already_in_group": "User {user:} already in group {group:s}", - "user_created": "The user has been created", - "user_creation_failed": "Unable to create user", - "user_deleted": "The user has been deleted", - "user_deletion_failed": "Unable to delete user", - "user_home_creation_failed": "Unable to create user home folder", - "user_info_failed": "Unable to retrieve user information", - "user_not_in_group": "User {user:s} not in group {group:s}", + "upnp_disabled": "UPnP turned off", + "upnp_enabled": "UPnP turned on", + "upnp_port_open_failed": "Could not open port via UPnP", + "user_already_in_group": "The User '{user:}' already in the group {group:s}", + "user_created": "User created", + "user_creation_failed": "Could not create user", + "user_deleted": "User deleted", + "user_deletion_failed": "Could not delete user", + "user_home_creation_failed": "Could not create 'home' folder for user", + "user_info_failed": "Could not retrieve user info", + "user_not_in_group": "The user '{user:s}' is not in the group {group:s}", "user_unknown": "Unknown user: {user:s}", - "user_update_failed": "Unable to update user", - "user_updated": "The user has been updated", + "user_update_failed": "Could not change user info", + "user_updated": "User info changed", "users_available": "Available users:", "yunohost_already_installed": "YunoHost is already installed", - "yunohost_ca_creation_failed": "Unable to create certificate authority", - "yunohost_ca_creation_success": "The local certification authority has been created.", - "yunohost_configured": "YunoHost has been configured", + "yunohost_ca_creation_failed": "Could not create certificate authority", + "yunohost_ca_creation_success": "Local certification authority created.", + "yunohost_configured": "YunoHost now configured", "yunohost_installing": "Installing YunoHost…", - "yunohost_not_installed": "YunoHost is not or not correctly installed. Please execute 'yunohost tools postinstall'" + "yunohost_not_installed": "YunoHost is incorrectly or not correctly installed. Please run 'yunohost tools postinstall'" } From 16ea8f45d64b92e94af4eb3ffceec1341f16e662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Tue, 17 Sep 2019 20:32:57 +0200 Subject: [PATCH 132/299] Language reworked --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d06e97c45..b860c27d7 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -[![Build Status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) +[![Build status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) [![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) # YunoHost core This repository is the core of YunoHost code. -- [YunoHost project website](https://yunohost.org) -- [Butracker](https://github.com/YunoHost/issues). +- [Project website](https://yunohost.org) +- [Bugtracker](https://github.com/YunoHost/issues). ## Contributing - You can develop on this repository using [ynh-dev](https://github.com/YunoHost/ynh-dev) with `use-git` sub-command. -- On this repository we are [following this workflow](https://yunohost.org/#/build_system_en): `stable <- testing <- unstable <- your_branch`. -- Note: if you modify python scripts, you will have to modifiy the actions map. -- You can help with translation on [our translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) +- On this repository we are [following this workflow](https://yunohost.org/#/build_system_en): `stable ← testing ← unstable ← your_branch`. +- Note: If you modify Python scripts, you will have to modifiy the actions map. +- You can help translate YunoHost on our [translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) Translation status @@ -34,7 +34,7 @@ This repository is the core of YunoHost code. - Python core scripts are accessible through two interfaces thanks to the [moulinette framework](https://github.com/YunoHost/moulinette): - [CLI](https://en.wikipedia.org/wiki/Command-line_interface) for `yunohost` command. - [API](https://en.wikipedia.org/wiki/Application_programming_interface) for [web administration module](https://github.com/YunoHost/yunohost-admin) (other modules could be implemented). -- You can find more details about how YunoHost works on this [documentation (in french)](https://yunohost.org/#/package_list_fr). +- You can find more details about how YunoHost works on this [documentation (in French)](https://yunohost.org/#/package_list_fr). ## Dependencies @@ -45,4 +45,4 @@ This repository is the core of YunoHost code. ## License -As [other components of YunoHost core code](https://yunohost.org/#/faq_en), this repository is under GNU AGPL v.3 license. +As [other components of YunoHost core code](https://yunohost.org/#/faq_en), this repository is licensed GNU AGPL v3. From 4f9b56c29917d501fd55b1a5a71e31948c7421bc Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Tue, 17 Sep 2019 14:33:24 +0000 Subject: [PATCH 133/299] Translated using Weblate (Arabic) Currently translated at 66.2% (386 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ar/ --- locales/ar.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 52e86d90b..b6f3ece23 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -23,7 +23,7 @@ "app_location_install_failed": "Unable to install the app in this location because it conflit with the app '{other_app}' already installed on '{other_path}'", "app_location_unavailable": "This url is not available or conflicts with an already installed app", "app_manifest_invalid": "Invalid app manifest: {error}", - "app_no_upgrade": "البرمجيات لا تحتاج إلى تحديث", + "app_no_upgrade": "ليس هناك أي تطبيق بحاجة إلى تحديث", "app_not_correctly_installed": "يبدو أن التطبيق {app:s} لم يتم تنصيبه بشكل صحيح", "app_not_installed": "إنّ التطبيق {app:s} غير مُنصَّب", "app_not_properly_removed": "لم يتم حذف تطبيق {app:s} بشكلٍ جيّد", @@ -37,17 +37,17 @@ "app_unsupported_remote_type": "Unsupported remote type used for the app", "app_upgrade_app_name": "جارٍ تحديث تطبيق {app}…", "app_upgrade_failed": "تعذرت عملية ترقية {app:s}", - "app_upgrade_some_app_failed": "تعذرت عملية ترقية بعض البرمجيات", + "app_upgrade_some_app_failed": "تعذرت عملية ترقية بعض التطبيقات", "app_upgraded": "تم تحديث التطبيق {app:s}", "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.", "appslist_could_not_migrate": "Could not migrate app list {appslist:s} ! Unable to parse the url... The old cron job has been kept in {bkp_file:s}.", "appslist_fetched": "تم جلب قائمة تطبيقات {appslist:s}", "appslist_migrating": "Migrating application list {appslist:s} …", "appslist_name_already_tracked": "There is already a registered application list with name {name:s}.", - "appslist_removed": "تم حذف قائمة البرمجيات {appslist:s}", + "appslist_removed": "تم حذف قائمة التطبيقات {appslist:s}", "appslist_retrieve_bad_format": "Retrieved file for application list {appslist:s} is not valid", "appslist_retrieve_error": "Unable to retrieve the remote application list {appslist:s}: {error:s}", - "appslist_unknown": "قائمة البرمجيات {appslist:s} مجهولة.", + "appslist_unknown": "قائمة التطبيقات {appslist:s} مجهولة.", "appslist_url_already_tracked": "There is already a registered application list with url {url:s}.", "ask_current_admin_password": "كلمة السر الإدارية الحالية", "ask_email": "عنوان البريد الإلكتروني", @@ -114,7 +114,7 @@ "certmanager_attempt_to_replace_valid_cert": "You are attempting to overwrite a good and valid certificate for domain {domain:s}! (Use --force to bypass)", "certmanager_cannot_read_cert": "Something wrong happened when trying to open current certificate for domain {domain:s} (file: {file:s}), reason: {reason:s}", "certmanager_cert_install_success": "تمت عملية تنصيب شهادة Let's Encrypt بنجاح على النطاق {domain:s} !", - "certmanager_cert_install_success_selfsigned": "Successfully installed a self-signed certificate for domain {domain:s}!", + "certmanager_cert_install_success_selfsigned": "نجحت عملية تثبيت الشهادة الموقعة ذاتيا الخاصة بالنطاق {domain:s}!", "certmanager_cert_renew_success": "نجحت عملية تجديد شهادة Let's Encrypt الخاصة باسم النطاق {domain:s} !", "certmanager_cert_signing_failed": "فشل إجراء توقيع الشهادة الجديدة", "certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow…", @@ -232,7 +232,7 @@ "migrations_no_migrations_to_run": "No migrations to run", "migrations_show_currently_running_migration": "Running migration {number} {name}…", "migrations_show_last_migration": "Last ran migration is {}", - "migrations_skip_migration": "جارٍ تجاهل التهجير {number} {name}…", + "migrations_skip_migration": "جارٍ تجاهل التهجير {id}…", "monitor_disabled": "The server monitoring has been disabled", "monitor_enabled": "The server monitoring has been enabled", "monitor_glances_con_failed": "Unable to connect to Glances server", @@ -316,7 +316,7 @@ "service_conf_updated": "The configuration has been updated for service '{service}'", "service_conf_would_be_updated": "The configuration would have been updated for service '{service}'", "service_disable_failed": "", - "service_disabled": "The service '{service:s}' has been disabled", + "service_disabled": "تم تعطيل خدمة '{service:s}'", "service_enable_failed": "", "service_enabled": "تم تنشيط خدمة '{service:s}'", "service_no_log": "ليس لخدمة '{service:s}' أي سِجلّ للعرض", @@ -347,7 +347,7 @@ "upgrade_complete": "إكتملت عملية الترقية و التحديث", "upgrading_packages": "عملية ترقية الحُزم جارية …", "upnp_dev_not_found": "No UPnP device found", - "upnp_disabled": "UPnP has been disabled", + "upnp_disabled": "تم تعطيل UPnP", "upnp_enabled": "UPnP has been enabled", "upnp_port_open_failed": "Unable to open UPnP ports", "user_created": "تم إنشاء المستخدم", @@ -441,5 +441,6 @@ "group_name_already_exist": "الفريق {name:s} موجود بالفعل", "error_when_removing_sftpuser_group": "حدث خطأ أثناء محاولة حذف فريق sftpusers", "dyndns_could_not_check_available": "لا يمكن التحقق مِن أنّ {domain:s} متوفر على {provider:s}.", - "backup_mount_archive_for_restore": "جارٍ تهيئة النسخة الاحتياطية للاسترجاع…" + "backup_mount_archive_for_restore": "جارٍ تهيئة النسخة الاحتياطية للاسترجاع…", + "root_password_replaced_by_admin_password": "لقد تم استبدال كلمة سر الجذر root بالكلمة الإدارية لـ admin." } From 60e602a35e4a587c96a57a46b9cc1e3bf5e7544f Mon Sep 17 00:00:00 2001 From: advocatux Date: Sun, 15 Sep 2019 12:23:09 +0000 Subject: [PATCH 134/299] Translated using Weblate (Spanish) Currently translated at 100.0% (583 of 583 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 334 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 307 insertions(+), 27 deletions(-) diff --git a/locales/es.json b/locales/es.json index 8861c15b8..c34a7e33c 100644 --- a/locales/es.json +++ b/locales/es.json @@ -16,15 +16,15 @@ "app_manifest_invalid": "El manifiesto de la aplicación no es válido: {error}", "app_no_upgrade": "No hay aplicaciones para actualizar", "app_not_correctly_installed": "La aplicación {app:s} 8 parece estar incorrectamente instalada", - "app_not_installed": "{app:s} 9 no está instalada", + "app_not_installed": "La aplicación «{app:s}» no está instalada. Esta es la lista de todas las aplicaciones instaladas: {all_apps}", "app_not_properly_removed": "La {app:s} 0 no ha sido desinstalada correctamente", "app_package_need_update": "El paquete de la aplicación {app} necesita ser actualizada debido a los cambios en YunoHost", "app_recent_version_required": "{:s} requiere una versión más reciente de moulinette ", "app_removed": "{app:s} ha sido eliminada", - "app_requirements_checking": "Comprobando los paquetes requeridos por {app}...", + "app_requirements_checking": "Comprobando los paquetes necesarios para {app}…", "app_requirements_failed": "No se cumplen los requisitos para {app}: {error}", "app_requirements_unmeet": "No se cumplen los requisitos para {app}, el paquete {pkgname} ({version}) debe ser {spec}", - "app_sources_fetch_failed": "No se pudieron descargar los archivos del código fuente", + "app_sources_fetch_failed": "No se pudo obtener los archivos con el código fuente, ¿es la URL correcta?", "app_unknown": "Aplicación desconocida", "app_unsupported_remote_type": "Tipo remoto no soportado por la aplicación", "app_upgrade_failed": "No se pudo actualizar la aplicación {app:s}", @@ -50,11 +50,11 @@ "backup_archive_open_failed": "No se pudo abrir la copia de seguridad", "backup_cleaning_failed": "No se puede limpiar el directorio temporal de copias de seguridad", "backup_created": "Se ha creado la copia de seguridad", - "backup_creating_archive": "Creando copia de seguridad...", + "backup_creating_archive": "Creando el archivo de copia de seguridad…", "backup_creation_failed": "No se pudo crear la copia de seguridad", "backup_delete_error": "No se puede eliminar '{path:s}'", "backup_deleted": "La copia de seguridad ha sido eliminada", - "backup_extracting_archive": "Extrayendo la copia de seguridad...", + "backup_extracting_archive": "Extrayendo el archivo de la copia de seguridad…", "backup_hook_unknown": "Hook de copia de seguridad desconocido '{hook:s}'", "backup_invalid_archive": "La copia de seguridad no es válida", "backup_nothings_done": "No hay nada que guardar", @@ -86,21 +86,21 @@ "domain_zone_exists": "El archivo de zona del DNS ya existe", "domain_zone_not_found": "No se ha encontrado el archivo de zona del DNS para el dominio [:s]", "done": "Hecho.", - "downloading": "Descargando...", + "downloading": "Descargando…", "dyndns_cron_installed": "La tarea cron para DynDNS ha sido instalada", - "dyndns_cron_remove_failed": "No se pudo eliminar la tarea cron DynDNS", + "dyndns_cron_remove_failed": "No se pudo eliminar la tarea cron de DynDNS por: {error}", "dyndns_cron_removed": "La tarea cron DynDNS ha sido eliminada", "dyndns_ip_update_failed": "No se pudo actualizar la dirección IP en el DynDNS", "dyndns_ip_updated": "Su dirección IP ha sido actualizada en el DynDNS", - "dyndns_key_generating": "Se está generando la clave del DNS. Esto podría tardar unos minutos...", + "dyndns_key_generating": "Generando la clave del DNS. Esto podría tardar un rato…", "dyndns_key_not_found": "No se ha encontrado la clave DNS para el dominio", "dyndns_no_domain_registered": "Ningún dominio ha sido registrado con DynDNS", "dyndns_registered": "El dominio DynDNS ha sido registrado", "dyndns_registration_failed": "No se pudo registrar el dominio DynDNS: {error:s}", "dyndns_unavailable": "El dominio {domain:s} no está disponible.", - "executing_command": "Ejecutando el comando '{command:s}'...", - "executing_script": "Ejecutando el script '{script:s}'...", - "extracting": "Extrayendo...", + "executing_command": "Ejecutando la orden «{command:s}»…", + "executing_script": "Ejecutando el guión «{script:s}»…", + "extracting": "Extrayendo…", "field_invalid": "Campo no válido '{:s}'", "firewall_reload_failed": "No se pudo recargar el cortafuegos", "firewall_reloaded": "El cortafuegos ha sido recargado", @@ -176,8 +176,8 @@ "restore_failed": "No se pudo restaurar el sistema", "restore_hook_unavailable": "El script de restauración '{part:s}' no está disponible en su sistema y tampoco en el archivo", "restore_nothings_done": "No se ha restaurado nada", - "restore_running_app_script": "Ejecutando el script de restauración de la aplicación '{app:s}'...", - "restore_running_hooks": "Ejecutando los hooks de restauración...", + "restore_running_app_script": "Ejecutando el guión de restauración de la aplicación «{app:s}»…", + "restore_running_hooks": "Ejecutando los ganchos de restauración…", "service_add_failed": "No se pudo añadir el servicio '{service:s}'", "service_added": "Servicio '{service:s}' ha sido añadido", "service_already_started": "El servicio '{service:s}' ya ha sido inicializado", @@ -220,9 +220,9 @@ "unlimit": "Sin cuota", "unrestore_app": "La aplicación '{app:s}' no será restaurada", "update_cache_failed": "No se pudo actualizar la caché de APT", - "updating_apt_cache": "Actualizando lista de paquetes disponibles...", + "updating_apt_cache": "Obteniendo las actualizaciones disponibles para los paquetes del sistema…", "upgrade_complete": "Actualización finalizada", - "upgrading_packages": "Actualizando paquetes...", + "upgrading_packages": "Actualizando paquetes…", "upnp_dev_not_found": "No se encontró ningún dispositivo UPnP", "upnp_disabled": "UPnP ha sido deshabilitado", "upnp_enabled": "UPnP ha sido habilitado", @@ -239,7 +239,7 @@ "yunohost_already_installed": "YunoHost ya está instalado", "yunohost_ca_creation_failed": "No se pudo crear el certificado de autoridad", "yunohost_configured": "YunoHost ha sido configurado", - "yunohost_installing": "Instalando YunoHost...", + "yunohost_installing": "Instalando YunoHost…", "yunohost_not_installed": "YunoHost no está instalado o ha habido errores en la instalación. Ejecute 'yunohost tools postinstall'", "ldap_init_failed_to_create_admin": "La inicialización de LDAP falló al crear el usuario administrador", "mailbox_used_space_dovecot_down": "El servicio de e-mail Dovecot debe estar funcionando si desea obtener el espacio utilizado por el buzón de correo", @@ -248,9 +248,9 @@ "certmanager_attempt_to_replace_valid_cert": "Está intentando sobrescribir un certificado correcto y válido para el dominio {domain:s}! (Use --force para omitir este mensaje)", "certmanager_domain_unknown": "Dominio desconocido {domain:s}", "certmanager_domain_cert_not_selfsigned": "El certificado para el dominio {domain:s} no es un certificado autofirmado. ¿Está seguro de que quiere reemplazarlo? (Use --force para omitir este mensaje)", - "certmanager_certificate_fetching_or_enabling_failed": "Parece que al habilitar el nuevo certificado para el dominio {domain:s} ha fallado de alguna manera...", + "certmanager_certificate_fetching_or_enabling_failed": "Suena como que habilitar el nuevo certificado para {domain:s} fallara de algún modo…", "certmanager_attempt_to_renew_nonLE_cert": "El certificado para el dominio {domain:s} no ha sido emitido por Let's Encrypt. ¡No se puede renovar automáticamente!", - "certmanager_attempt_to_renew_valid_cert": "El certificado para el dominio {domain:s} no está a punto de expirar! Utilice --force para omitir este mensaje", + "certmanager_attempt_to_renew_valid_cert": "¡El certificado para el dominio {domain:s} no está a punto de expirar! (Puede usar --force si sabe lo que está haciendo)", "certmanager_domain_http_not_working": "Parece que no se puede acceder al dominio {domain:s} a través de HTTP. Compruebe que la configuración del DNS y de nginx es correcta", "certmanager_error_no_A_record": "No se ha encontrado un registro DNS 'A' para el dominio {domain:s}. Debe hacer que su nombre de dominio apunte a su máquina para poder instalar un certificado Let's Encrypt. (Si sabe lo que está haciendo, use --no-checks para desactivar esas comprobaciones.)", "certmanager_domain_dns_ip_differs_from_public_ip": "El registro DNS 'A' para el dominio {domain:s} es diferente de la IP de este servidor. Si recientemente modificó su registro A, espere a que se propague (existen algunos controladores de propagación DNS disponibles en línea). (Si sabe lo que está haciendo, use --no-checks para desactivar esas comprobaciones.)", @@ -273,7 +273,7 @@ "certmanager_http_check_timeout": "Plazo expirado, el servidor no ha podido contactarse a si mismo a través de HTTP usando su dirección IP pública (dominio {domain:s} con ip {ip:s}). Puede ser debido a hairpinning o a una mala configuración del cortafuego/router al que está conectado su servidor.", "certmanager_couldnt_fetch_intermediate_cert": "Plazo expirado, no se ha podido descargar el certificado intermedio de Let's Encrypt. La instalación/renovación del certificado ha sido cancelada - vuelva a intentarlo más tarde.", "appslist_retrieve_bad_format": "El archivo obtenido para la lista de aplicaciones {appslist:s} no es válido", - "domain_hostname_failed": "Error al establecer nuevo nombre de host", + "domain_hostname_failed": "Error al establecer un nuevo nombre de host («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).", "yunohost_ca_creation_success": "Se ha creado la autoridad de certificación local.", "app_already_installed_cant_change_url": "Esta aplicación ya está instalada. No se puede cambiar el URL únicamente mediante esta función. Compruebe si está disponible la opción 'app changeurl'.", "app_change_no_change_url_script": "La aplicacion {app_name:s} aún no permite cambiar su URL, es posible que deba actualizarla.", @@ -285,19 +285,19 @@ "app_already_up_to_date": "La aplicación {app:s} ya está actualizada", "appslist_name_already_tracked": "Ya existe una lista de aplicaciones registrada con el nombre {name:s}.", "appslist_url_already_tracked": "Ya existe una lista de aplicaciones registrada con el URL {url:s}.", - "appslist_migrating": "Migrando la lista de aplicaciones {appslist:s} ...", + "appslist_migrating": "Migrando la lista de aplicaciones {appslist:s}…", "appslist_could_not_migrate": "No se pudo migrar la lista de aplicaciones {appslist:s}! No se pudo analizar el URL ... El antiguo cronjob se ha mantenido en {bkp_file:s}.", "appslist_corrupted_json": "No se pudieron cargar las listas de aplicaciones. Parece que {filename:s} está dañado.", "invalid_url_format": "Formato de URL no válido", "app_upgrade_some_app_failed": "No se pudieron actualizar algunas aplicaciones", "app_make_default_location_already_used": "No puede hacer la aplicación '{app}' por defecto en el dominio {domain} dado que está siendo usado por otra aplicación '{other_app}'", - "app_upgrade_app_name": "Actualizando la aplicación {app}...", + "app_upgrade_app_name": "Actualizando la aplicación {app}…", "ask_path": "Camino", "backup_abstract_method": "Este método de backup no ha sido implementado aún", - "backup_applying_method_borg": "Enviando todos los ficheros al backup en el repositorio borg-backup...", - "backup_applying_method_copy": "Copiado todos los ficheros al backup...", - "backup_applying_method_custom": "Llamando el método de backup {method:s} ...", - "backup_applying_method_tar": "Creando el archivo tar de backup...", + "backup_applying_method_borg": "Enviando todos los archivos para la copia de seguridad al repositorio de borg-backup…", + "backup_applying_method_copy": "Copiando todos los archivos a la copia de seguridad…", + "backup_applying_method_custom": "Llamando al método de copia de seguridad personalizado «{method:s}»…", + "backup_applying_method_tar": "Creando el archivo tar de la copia de seguridad…", "backup_archive_mount_failed": "Fallo en el montado del archivo de backup", "backup_archive_system_part_not_available": "La parte del sistema {part:s} no está disponible en este backup", "backup_archive_writing_error": "No se pueden añadir archivos de backup en el archivo comprimido", @@ -305,7 +305,7 @@ "backup_borg_not_implemented": "Método de backup Borg no está implementado aún", "backup_cant_mount_uncompress_archive": "No se puede montar en modo solo lectura el directorio del archivo descomprimido", "backup_copying_to_organize_the_archive": "Copiando {size:s}MB para organizar el archivo", - "backup_couldnt_bind": "No puede enlazar {src:s} con {dest:s}", + "backup_couldnt_bind": "No puede enlazar {src:s} con {dest:s}.", "backup_csv_addition_failed": "No puede añadir archivos al backup en el archivo CSV", "backup_csv_creation_failed": "No se puede crear el archivo CSV necesario para futuras operaciones de restauración", "backup_custom_mount_error": "Fracaso del método de copia de seguridad personalizada en la etapa \"mount\"", @@ -323,5 +323,285 @@ "password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud", "password_too_simple_2": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas y minúsculas", "password_too_simple_3": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales", - "password_too_simple_4": "La contraseña debe tener al menos 12 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales" + "password_too_simple_4": "La contraseña debe tener al menos 12 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales", + "users_available": "Usuarios disponibles:", + "user_not_in_group": "Usuario {user:s} no está en el grupo {group:s}", + "user_already_in_group": "Usuario {user:} ya está en el grupo {group:s}", + "updating_app_lists": "Obteniendo actualizaciones disponibles para las aplicaciones…", + "update_apt_cache_warning": "Ocurrieron algunos errores durante la actualización de la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", + "update_apt_cache_failed": "No se puede actualizar la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", + "tools_upgrade_special_packages_completed": "¡Actualización de paquetes de YunoHost completada!\nPulse [Intro] para recuperar la línea de órdenes", + "tools_upgrade_special_packages_explanation": "Esta acción terminará pero la actualización especial real continuará en segundo plano. No inicie ninguna otra acción en su servidor en aproximadamente 10 minutos (dependiendo de la velocidad de su hardware). Una vez que esté hecho, podría tener que volver a iniciar sesión en la administración web. El registro de actualización estará disponible en Herramientas > Registro (en la administración web) o mediante «yunohost log list» (en la línea de órdenes).", + "tools_upgrade_special_packages": "Actualizando ahora paquetes «especiales» (relacionados con YunoHost)...", + "tools_upgrade_regular_packages_failed": "No se pueden actualizar los paquetes: {packages_list}", + "tools_upgrade_regular_packages": "Actualizando ahora paquetes «normales» (no relacionados con YunoHost)...", + "tools_upgrade_cant_unhold_critical_packages": "No se pueden liberar los paquetes críticos...", + "tools_upgrade_cant_hold_critical_packages": "No se pueden retener los paquetes críticos...", + "tools_upgrade_cant_both": "No se puede actualizar el sistema y las aplicaciones al mismo tiempo", + "tools_upgrade_at_least_one": "Especifique --apps O --system", + "tools_update_failed_to_app_fetchlist": "Error al actualizar la lista de aplicaciones de YunoHost porque: {error}", + "this_action_broke_dpkg": "Esta acción rompió dpkg/apt (los gestores de paquetes del sistema)... Puede tratar de solucionar este problema conectando mediante SSH y ejecutando `sudo dpkg --configure -a`.", + "system_groupname_exists": "El nombre de grupo ya existe en el grupo del sistema", + "service_reloaded_or_restarted": "El servicio «{service:s}» ha sido recargado o reiniciado", + "service_reload_or_restart_failed": "No se puede recargar o reiniciar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", + "service_restarted": "El servicio «{service:s}» ha sido reiniciado", + "service_restart_failed": "No se puede reiniciar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", + "service_reloaded": "El servicio «{service:s}» ha sido recargado", + "service_reload_failed": "No se puede recargar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", + "service_regen_conf_is_deprecated": "¡«yunohost service regen-conf» está obsoleto! Use «yunohost tools regen-conf» en su lugar.", + "service_description_yunohost-firewall": "gestiona los puertos de conexiones abiertos y cerrados a los servicios", + "service_description_yunohost-api": "gestiona las interacciones entre la interfaz web de YunoHost y el sistema", + "service_description_ssh": "le permite conectar a su servidor remotamente mediante un terminal (protocolo SSH)", + "service_description_slapd": "almacena usuarios, dominios e información relacionada", + "service_description_rspamd": "filtra correo no deseado y otras características relacionadas con el correo", + "service_description_rmilter": "comprueba varios parámetros en el correo", + "service_description_redis-server": "una base de datos especializada usada para el acceso rápido de datos, cola de tareas y comunicación entre programas", + "service_description_postfix": "usado para enviar y recibir correos", + "service_description_php7.0-fpm": "ejecuta aplicaciones escritas en PHP con nginx", + "service_description_nslcd": "maneja la conexión del intérprete («shell») de usuario de YunoHost", + "service_description_nginx": "sirve o proporciona acceso a todos los sitios web alojados en su servidor", + "service_description_mysql": "almacena los datos de las aplicaciones (base de datos SQL)", + "service_description_metronome": "gestionar las cuentas XMPP de mensajería instantánea", + "service_description_glances": "supervisa la información del sistema en su servidor", + "service_description_fail2ban": "protege contra ataques de fuerza bruta y otra clase de ataques desde Internet", + "service_description_dovecot": "permite al cliente de correo acceder/traer correo (vía IMAP y POP3)", + "service_description_dnsmasq": "maneja la resolución de nombres de dominio (DNS)", + "service_description_avahi-daemon": "permite acceder a su servidor usando yunohost.local en su red local", + "server_reboot_confirm": "El servidor se reiniciará inmediatamente ¿está seguro? [{answers:s}]", + "server_reboot": "El servidor se reiniciará", + "server_shutdown_confirm": "El servidor se apagará inmediatamente ¿está seguro? [{answers:s}]", + "server_shutdown": "El servidor se apagará", + "root_password_replaced_by_admin_password": "Su contraseña de root ha sido sustituida por su contraseña de administración.", + "root_password_desynchronized": "La contraseña de administración ha sido cambiada pero ¡YunoHost no pudo propagar esto en la contraseña de root!", + "restore_system_part_failed": "No se puede restaurar la parte del sistema «{part:s}»", + "restore_removing_tmp_dir_failed": "No se puede eliminar un antiguo directorio temporal", + "restore_not_enough_disk_space": "Insuficiente espacio en disco (espacio libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", + "restore_mounting_archive": "Montando archivo en «{path:s}»", + "restore_may_be_not_enough_disk_space": "Parece que su sistema no tiene suficiente espacio de disco libre (espacio libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", + "restore_extracting": "Extrayendo los archivos necesarios para el archivo…", + "regenconf_pending_applying": "Aplicando la configuración pendiente para la categoría «{category}»…", + "regenconf_failed": "No se puede regenerar la configuración para la(s) categoría(s): {categories}", + "regenconf_dry_pending_applying": "Comprobando la configuración pendiente que habría sido aplicada para la categoría «{category}»…", + "regenconf_would_be_updated": "La configuración habría sido actualizada para la categoría «{category}»", + "regenconf_updated": "Ha sido actualizada la configuración para la categoría «{category}»", + "regenconf_up_to_date": "Ya está actualizada la configuración para la categoría «{category}»", + "regenconf_now_managed_by_yunohost": "El archivo de configuración «{conf}» está gestionado ahora por YunoHost (categoría {category}).", + "regenconf_file_updated": "El archivo de configuración «{conf}» ha sido actualizado", + "regenconf_file_removed": "El archivo de configuración «{conf}» ha sido eliminado", + "regenconf_file_remove_failed": "No se puede eliminar el archivo de configuración «{conf}»", + "regenconf_file_manually_removed": "El archivo de configuración «{conf}» ha sido eliminado manualmente y no se creará", + "regenconf_file_manually_modified": "El archivo de configuración «{conf}» ha sido modificado manualmente y no será actualizado", + "regenconf_file_kept_back": "Se espera que el archivo de configuración «{conf}» sea eliminado por regen-conf (categoría {category}) pero ha sido retenido.", + "regenconf_file_copy_failed": "No se puede copiar el nuevo archivo de configuración «{new}» a «{conf}»", + "regenconf_file_backed_up": "El archivo de configuración «{conf}» ha sido respaldado en «{backup}»", + "remove_user_of_group_not_allowed": "No tiene permiso para eliminar al usuario {user:s} en el grupo {group:s}", + "remove_main_permission_not_allowed": "No se permite eliminar el permiso principal", + "recommend_to_add_first_user": "La posinstalación ha terminado pero YunoHost necesita al menos un usuario para funcionar correctamente, debe añadir uno ejecutando «yunohost user create » o usando la interfaz de administración.", + "permission_update_nothing_to_do": "No hay permisos para actualizar", + "permission_updated": "Permiso «{permission:s}» para la aplicación {app:s} actualizado", + "permission_generated": "La base de datos de permisos se ha actualizado", + "permission_update_failed": "Actualización de permiso fallida", + "permission_name_not_valid": "Nombre de permiso «{permission:s}» no válido", + "permission_not_found": "Permiso «{permission:s}» no encontrado para la aplicación {app:s}", + "permission_deletion_failed": "Permiso «{permission:s}» para eliminar la aplicación «{app:s}» fallido", + "permission_deleted": "Eliminado el permiso «{permission:s}» para la aplicación {app:s}", + "permission_creation_failed": "Ha fallado la creación del permiso", + "permission_created": "Creado el permiso «{permission:s}» para la aplicación {app:s}", + "permission_already_exist": "El permiso «{permission:s}» para la aplicación {app:s} ya existe", + "permission_already_clear": "El permiso «{permission:s}» ya está definido para la aplicación {app:s}", + "pattern_password_app": "Las contraseñas no deben incluir los siguientes caracteres: {forbidden_chars}", + "need_define_permission_before": "Necesita redefinir los permisos ejecutando «yunohost user permission add -u USUARIO» antes de eliminar un grupo permitido", + "migrations_to_be_ran_manually": "La migración {id} hay que ejecutarla manualmente. Vaya a Herramientas > Migraciones en la web de administración o ejecute `yunohost tools migrations migrate`.", + "migrations_success_forward": "¡Migración {id} ejecutada correctamente!", + "migrations_skip_migration": "Omitiendo migración {id}…", + "migrations_running_forward": "Ejecutando migración {id}…", + "migrations_pending_cant_rerun": "Esas migraciones están aún pendientes así que no se pueden volver a ejecutar: {ids}", + "migrations_not_pending_cant_skip": "Esas migraciones no están pendientes así que no pueden ser omitidas: {ids}", + "migrations_no_such_migration": "No existe una migración llamada {id}", + "migrations_no_migrations_to_run": "No hay migraciones que ejecutar", + "migrations_need_to_accept_disclaimer": "Para ejecutar la migración {id} debe aceptar el siguiente descargo de responsabilidad:\n---\n{disclaimer}\n---\nSi acepta ejecutar la migración, vuelva a ejecutar la orden con la opción --accept-disclaimer.", + "migrations_must_provide_explicit_targets": "Necesita proporcionar objetivos explícitos al usar --skip o --force-rerun", + "migrations_migration_has_failed": "Migración {id} fallida, cancelando. Error: {exception}", + "migrations_loading_migration": "Cargando migración {id}…", + "migrations_list_conflict_pending_done": "No puede usar --previous y --done al mismo tiempo.", + "migrations_exclusive_options": "--auto, --skip, and --force-rerun son opciones excluyentes.", + "migrations_failed_to_load_migration": "Error al cargar la migración {id} : {error}", + "migrations_dependencies_not_satisfied": "No se puede ejecutar la migración {id} porque primero necesita ejecutar estas migraciones: {dependencies_id}", + "migrations_cant_reach_migration_file": "No se pueden acceder los archivos de migración en la ruta %s", + "migrations_already_ran": "Esas migraciones ya se han ejecutado: {ids}", + "migration_0011_update_LDAP_schema": "Actualizando el esquema de LDAP...", + "migration_0011_update_LDAP_database": "Actualizando la base de datos de LDAP...", + "migration_0011_rollback_success": "Revertido correctamente.", + "migration_0011_migration_failed_trying_to_rollback": "Migración fallida... intentando revertir el sistema.", + "migration_0011_migrate_permission": "Migrando permisos desde la configuración de las aplicaciones a LDAP...", + "migration_0011_LDAP_update_failed": "Actualización de LDAP fallida. Error: {error:s}", + "migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, restaurar la configuración original con la orden «yunohost tools regen-conf -f» y reintentar después la migración", + "migration_0011_done": "Migración exitosa. Ahora puede gestionar los grupos de usuarios.", + "migration_0011_create_group": "Creando un grupo para cada usuario...", + "migration_0011_can_not_backup_before_migration": "Falló la copia de seguridad del sistema antes de la migración. Migración fallida. Error: {error:s}", + "migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.", + "migration_0009_not_needed": "¿La migración ya ocurrió de algún modo? Omitiendo.", + "migration_0008_no_warning": "No se ha detectado ningún riesgo importante sobre la anulación de su configuración SSH ¡pero no existe una certeza absoluta ;)! Si permite a YunoHost anular su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", + "migration_0008_warning": "Si entiende esos avisos y permite a YunoHost anular su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", + "migration_0008_dsa": " - se desactivará la clave DSA. Así que podría tener que anular un aviso espeluznante de su cliente SSH y volver a comprobar la huella de su servidor;", + "migration_0008_root": " - no podrá conectarse como «root» a través de SSH. En su lugar debería usar el usuario de administración;", + "migration_0008_port": " - tendrá que conectarse usando el puerto 22 en vez de su actual puerto SSH personalizado. No dude en reconfigurarlo;", + "migration_0008_general_disclaimer": "Para mejorar la seguridad de su servidor, es recomendable permitir a YunoHost gestionar la configuración SSH. Su actual configuración SSH difiere de la configuración recomendada. Si permite a YunoHost reconfigurarla, la manera en la que conecta con su servidor a través de SSH cambiará en el siguiente modo:", + "migration_0007_cannot_restart": "No se puede reiniciar SSH después de intentar cancelar la migración número 6.", + "migration_0007_cancelled": "YunoHost no ha podido mejorar el modo en el que se gestiona su configuración de SSH.", + "migration_0006_disclaimer": "YunoHost espera ahora que las contraseñas de «admin» y «root» estén sincronizadas. Al ejecutar esta migración, su contraseña de «root» será reemplazada por la contraseña de administración.", + "migration_0005_not_enough_space": "No hay suficiente espacio libre disponible en {path} para ejecutar la migración en este momento:(.", + "migration_0005_postgresql_96_not_installed": "¿¡Se encontró postgresql 9.4 para ser instalado pero no postgresql 9.6!? Algo raro podría haber ocurrido en su sistema:(…", + "migration_0005_postgresql_94_not_installed": "Postgresql no estaba instalado en su sistema. ¡Nada que hacer!", + "migration_0003_modified_files": "Tenga en cuenta que se encontró que los siguientes archivos fueron modificados manualmente y podrían ser sobrescritos al final de la actualización: {manually_modified_files}", + "migration_0003_problematic_apps_warning": "Tenga en cuenta que se detectaron las siguientes aplicaciones instaladas posiblemente problemáticas. Parece que no fueron instaladas desde una lista de aplicaciones o no estaban etiquetadas como «funciona». Así que no hay garantía de que aún funcionen después de la actualización: {problematic_apps}", + "migration_0003_general_warning": "Tenga en cuenta que esta migración es una operación delicada. Aunque el equipo de YunoHost hizo todo lo posible para revisarla y probarla, la migración aún podría romper parte del sistema o de las aplicaciones.\n\nPor lo tanto le recomendamos que:\n - Realice una copia de seguridad de cualquier dato crítico o aplicación. Más información en https://yunohost.org/backup;\n - Tenga paciencia tras iniciar la migración: dependiendo de su conexión a internet y de su hardware, podría tardar unas cuantas horas hasta que todo se actualice.\n\nAdemás, el puerto para SMTP usado por los clientes de correo externos (como Thunderbird o K9-Mail) cambió de 465 (SSL/TLS) a 587 (STARTTLS). El antiguo puerto 465 se cerrará automáticamente y el nuevo puerto 587 se abrirá en el cortafuegos. ¡Todos los usuarios *tendrán* que adaptar la configuración de sus clientes de correo por lo tanto!", + "migration_0003_still_on_jessie_after_main_upgrade": "Algo fue mal durante la actualización principal: ¿¡el sistema está aún en Jessie!? Para investigar el problema, vea {log}:s…", + "migration_0003_system_not_fully_up_to_date": "Su sistema no está totalmente actualizado. Realice una actualización normal antes de ejecutar la migración a Stretch.", + "migration_0003_not_jessie": "¡La distribución de Debian actual no es Jessie!", + "migration_0003_yunohost_upgrade": "Iniciando la actualización del paquete «yunohost»… La migración finalizará pero la actualización real ocurrirá justo después. Después de que la operación esté completada, podría tener que reiniciar sesión en la administración web.", + "migration_0003_restoring_origin_nginx_conf": "Su archivo /etc/nginx/nginx.conf ha sido editado de algún modo. La migración lo devolverá a su estado original primero… El archivo anterior estará disponible como {backup_dest}.", + "migration_0003_fail2ban_upgrade": "Iniciando la actualización de «fail2ban»…", + "migration_0003_main_upgrade": "Iniciando la actualización principal…", + "migration_0003_patching_sources_list": "Corrigiendo «sources.lists»…", + "migration_0003_start": "Iniciando migración a Stretch. El registro estará disponible en {logfile}.", + "migration_description_0012_postgresql_password_to_md5_authentication": "Forzar a la autentificación de postgresql a usar md5 para las conexiones locales", + "migration_description_0011_setup_group_permission": "Configurar grupo de usuario y configurar permisos para aplicaciones y servicios", + "migration_description_0010_migrate_to_apps_json": "Eliminar la obsoleta «appslists» y usar la nueva lista unificada «apps.json»", + "migration_description_0009_decouple_regenconf_from_services": "Separar el mecanismo «regen-conf» de los servicios", + "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Permitir que la configuración de SSH la gestione YunoHost (paso 2, manual)", + "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Permitir que la configuración de SSH la gestione YunoHost (paso 1, automático)", + "migration_description_0006_sync_admin_and_root_passwords": "Sincronizar las contraseñas de «admin» y «root»", + "migration_description_0005_postgresql_9p4_to_9p6": "Migrar las bases de datos de postgresql 9.4 a 9.6", + "migration_description_0004_php5_to_php7_pools": "Reconfigurar los «pools» de PHP para usar PHP 7 en vez de 5", + "migration_description_0003_migrate_to_stretch": "Actualizar el sistema a Debian Stretch y YunoHost 3.0", + "migration_description_0002_migrate_to_tsig_sha256": "Mejorar la seguridad de la TSIG de dyndns usando SHA512 en vez de MD5", + "migration_description_0001_change_cert_group_to_sslcert": "Cambiar los permisos de grupo de certificados de «metronome» a «ssl-cert»", + "migrate_tsig_not_needed": "Parece que no usa un dominio dyndns ¡así que no es necesario migrar!", + "migrate_tsig_wait_4": "30 segundos…", + "migrate_tsig_wait_3": "1 min. …", + "migrate_tsig_wait_2": "2 min. …", + "migrate_tsig_wait": "Esperar 3 min. para que el servidor dyndns tenga en cuenta la nueva clave…", + "migrate_tsig_start": "Detectado algoritmo de clave insuficientemente seguro para la firma TSIG del dominio «{domain}», iniciando migración al más seguro hmac-sha512", + "migrate_tsig_failed": "Error al migrar el dominio de dyndns {domain} a hmac-sha512, revertiendo. Error: {error_code} - {error}", + "migrate_tsig_end": "Terminada la migración a hmac-sha512", + "mail_unavailable": "Esta dirección de correo está reservada y será asignada automáticamente al primer usuario", + "mailbox_disabled": "Mailbox desactivado para usuario {user:s}", + "log_tools_reboot": "Reiniciar el servidor", + "log_tools_shutdown": "Apagar el servidor", + "log_tools_upgrade": "Actualizar paquetes del sistema", + "log_tools_postinstall": "Posinstalación del servidor YunoHost", + "log_tools_migrations_migrate_forward": "Migrar hacia adelante", + "log_tools_maindomain": "Convertir «{}» en dominio principal", + "log_user_permission_remove": "Actualizar permiso «{}»", + "log_user_permission_add": "Actualizar permiso «{}»", + "log_user_update": "Actualizar información del usuario «{}»", + "log_user_group_update": "Actualizar grupo «{}»", + "log_user_group_delete": "Eliminar grupo «{}»", + "log_user_group_add": "Añadir grupo «{}»", + "log_user_delete": "Eliminar usuario «{}»", + "log_user_create": "Añadir usuario «{}»", + "log_regen_conf": "Regenerar la configuración del sistema «{}»", + "log_letsencrypt_cert_renew": "Renovar el certificado «{}» de Let's encrypt", + "log_selfsigned_cert_install": "Instalar certificado autofirmado en el dominio «{}»", + "log_permission_update": "Actualizar permiso «{}» para la aplicación «{}»", + "log_permission_remove": "Eliminar permiso «{}»", + "log_permission_add": "Añadir permiso «{}» para la aplicación «{}»", + "log_letsencrypt_cert_install": "Instalar certificado de Let's encrypt en el dominio «{}»", + "log_dyndns_update": "Actualizar la IP asociada con su subdominio de YunoHost «{}»", + "log_dyndns_subscribe": "Subscribirse a un subdomino de YunoHost «{}»", + "log_domain_remove": "Eliminar el dominio «{}» de la configuración del sistema", + "log_domain_add": "Añadir el dominio «{}» a la configuración del sistema", + "log_remove_on_failed_install": "Eliminar «{}» después de una instalación fallida", + "log_remove_on_failed_restore": "Eliminar «{}» después de una restauración fallida desde un archivo de respaldo", + "log_backup_restore_app": "Restaurar «{}» desde un archivo de respaldo", + "log_backup_restore_system": "Restaurar sistema desde un archivo de respaldo", + "log_available_on_yunopaste": "Este registro está ahora disponible vía {url}", + "log_app_makedefault": "Convertir «{}» en aplicación predeterminada", + "log_app_upgrade": "Actualizar la aplicación «{}»", + "log_app_remove": "Eliminar la aplicación «{}»", + "log_app_install": "Instalar la aplicación «{}»", + "log_app_change_url": "Cambiar la url de la aplicación «{}»", + "log_app_removelist": "Eliminar una lista de aplicaciones", + "log_app_fetchlist": "Añadir una lista de aplicaciones", + "log_app_clearaccess": "Eliminar todos los accesos a «{}»", + "log_app_removeaccess": "Eliminar acceso a «{}»", + "log_app_addaccess": "Añadir acceso a «{}»", + "log_operation_unit_unclosed_properly": "La unidad de operación no se ha cerrado correctamente", + "log_does_exists": "No existe ningún registro de actividades con el nombre «{log}», ejecute «yunohost log list» para ver todos los registros de actividades disponibles", + "log_help_to_get_failed_log": "¡La operación «{desc}» ha fallado! Para obtener ayuda, comparta el registro completo de esta operación ejecutando la orden «yunohost log display {name} --share»", + "log_link_to_failed_log": "¡La operación «{desc}» ha fallado! Para obtener ayuda, proporcione el registro completo de esta operación pulsando aquí", + "log_help_to_get_log": "Para ver el registro de la operación «{desc}», ejecute la orden «yunohost log display {name}»", + "log_link_to_log": "Registro completo de esta operación: «{desc}»", + "log_category_404": "La categoría de registro «{category}» no existe", + "log_corrupted_md_file": "El archivo de metadatos yaml asociado con el registro está dañado: «{md_file}\nError: {error}»", + "hook_json_return_error": "Error al leer la respuesta del gancho {path:s}. Error: {msg:s}. Contenido sin procesar: {raw_content}", + "group_update_failed": "Error en la actualización del grupo «{group}»", + "group_updated": "Grupo «{group}» actualizado", + "group_unknown": "Grupo {group:s} desconocido", + "group_info_failed": "Error en la información del grupo", + "group_deletion_not_allowed": "No se puede eliminar el grupo {group:s} manualmente.", + "group_deletion_failed": "Error al eliminar el grupo «{group}»", + "group_deleted": "Eliminado el grupo «{group}»", + "group_creation_failed": "Error al crear el grupo «{group}»", + "group_created": "Grupo «{group}» creado correctamente", + "group_name_already_exist": "El grupo {name:s} ya existe", + "group_already_disallowed": "El grupo '{group:s}' ya tiene desactivado el permiso «{permission:s}» para la aplicación «{app:s}»", + "group_already_allowed": "El grupo '{group:s}' ya tiene activado el permiso «{permission:s}» para la aplicación «{app:s}»", + "good_practices_about_admin_password": "Va a determinar una nueva contraseña de administración. La contraseña debería tener al menos 8 caracteres, aunque es una buena práctica usar contraseñas más extensas (esto es, una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).", + "global_settings_unknown_type": "Situación imprevista, la configuración {setting:s} parece tener el tipo {unknown_type:s} pero no es un tipo compatible con el sistema.", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permitir el uso de la llave (obsoleta) DSA para la configuración del demonio SSH", + "global_settings_unknown_setting_from_settings_file": "Clave desconocida en la configuración: «{setting_key:s}», desechada y guardada en /etc/yunohost/settings-unknown.json", + "global_settings_setting_security_postfix_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor Postfix. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", + "global_settings_setting_security_ssh_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor SSH. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", + "global_settings_setting_security_password_user_strength": "Seguridad de la contraseña de usuario", + "global_settings_setting_security_password_admin_strength": "Seguridad de la contraseña del administrador", + "global_settings_setting_security_nginx_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor web nginx. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", + "global_settings_setting_example_string": "Ejemplo de opción de cadena", + "global_settings_setting_example_int": "Ejemplo de opción «int»", + "global_settings_setting_example_enum": "Ejemplo de opción «enum»", + "global_settings_setting_example_bool": "Ejemplo de opción booleana", + "global_settings_reset_success": "Éxito. Se ha respaldado su configuración previa en {path:s}", + "global_settings_key_doesnt_exists": "La clave «{settings_key:s}» no existe en la configuración global, puede ver todas las claves disponibles ejecutando «yunohost settings list»", + "global_settings_cant_write_settings": "Error al escribir el archivo de configuración, motivo: {reason:s}", + "global_settings_cant_serialize_settings": "Error al seriar los datos de configuración, motivo: {reason:s}", + "global_settings_cant_open_settings": "Error al abrir el archivo de configuración, motivo: {reason:s}", + "global_settings_bad_type_for_setting": "Tipo erróneo para la configuración {setting:s}, obtuvo {received_type:s}, excepto {expected_type:s}", + "global_settings_bad_choice_for_enum": "Mala elección para la configuración {setting:s}, obtuvo «{choice:s}» pero las opciones disponibles son: {available_choices:s}", + "file_does_not_exist": "El archivo {path:s} no existe.", + "error_when_removing_sftpuser_group": "Error al probar «remove sftpusers group»", + "edit_permission_with_group_all_users_not_allowed": "No puede editar el permiso para el grupo «all_users», utilice «yunohost user permission clear APLICACIÓN» o «yunohost user permission add APLICACIÓN -u USUARIO».", + "edit_group_not_allowed": "No tiene permiso para editar el grupo {group:s}", + "dyndns_could_not_check_available": "No se pudo comprobar si {domain:s} está disponible en {provider:s}.", + "domain_dyndns_dynette_is_unreachable": "No se pudo conectar al dynette de YunoHost, o su YunoHost no está correctamente conectado a internet o el servidor dynette está caído. Error: {error}", + "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra cuál es la configuración *recomendada*. No configura el DNS. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", + "dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento porque otro programa parece que está usando el bloqueo de dpkg (el gestor de paquetes del sistema)", + "dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.", + "confirm_app_install_thirdparty": "¡AVISO! Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarlas salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", + "confirm_app_install_danger": "¡AVISO! Esta aplicación es aún experimental (si no está funcionando expresamente) y ¡es probable que rompa su sistema! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", + "confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ", + "backup_unable_to_organize_files": "No se pueden organizar los archivos en el archivo con el método rápido", + "backup_permission": "Permiso de respaldo para la aplicación {app:s}", + "backup_output_symlink_dir_broken": "Tiene un enlace simbólico roto en vez del directorio «{path:s}» de sus archivos. Puede que tenga una configuración específica para respaldar sus datos en otro sistema de archivos, en este caso probablemente olvidó remontar o conectar su disco duro o clave usb.", + "backup_mount_archive_for_restore": "Preparando el archivo para la restauración…", + "backup_method_tar_finished": "Creado el archivo de respaldo tar", + "backup_method_custom_finished": "Terminado el método «{method:s}» de respaldo personalizado", + "backup_method_copy_finished": "Terminada la copia de seguridad", + "backup_method_borg_finished": "Terminado el respaldo en borg", + "backup_custom_backup_error": "Fallo del método de respaldo personalizado en el paso «copia de seguridad»", + "backup_actually_backuping": "Creando un archivo de respaldo de los archivos obtenidos…", + "ask_new_path": "Nueva ruta", + "ask_new_domain": "Nuevo dominio", + "apps_permission_restoration_failed": "El permiso «{permission:s}» para la restauración de la aplicación {app:s} ha fallado", + "apps_permission_not_found": "No se han encontrado permisos para las aplicaciones instaladas", + "app_upgrade_several_apps": "Las siguientes aplicaciones se actualizarán: {apps}", + "app_start_restore": "Restaurando aplicación {app}…", + "app_start_backup": "Obteniendo archivos de respaldo para {app}…", + "app_start_remove": "Eliminando aplicación {app}…", + "app_start_install": "Instalando aplicación {app}…", + "app_not_upgraded": "Las siguientes aplicaciones no se actualizaron: {apps}", + "app_action_cannot_be_ran_because_required_services_down": "Esta aplicación necesita algunos servicios que no están funcionando ahora. Antes de continuar, debería intentar reiniciar los siguientes servicios (y posiblemente investigar por qué no funcionan): {services}", + "already_up_to_date": "¡Nada que hacer! ¡Todo está actualizado!", + "admin_password_too_long": "Elija una contraseña de menos de 127 caracteres", + "aborting": "Cancelando." } From d11c76713a08c3174afb40b35d8834aa4643c408 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Tue, 17 Sep 2019 19:02:30 +0000 Subject: [PATCH 135/299] Translated using Weblate (Arabic) Currently translated at 66.1% (386 of 584 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ar/ --- locales/ar.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/ar.json b/locales/ar.json index b6f3ece23..46f9315af 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -442,5 +442,6 @@ "error_when_removing_sftpuser_group": "حدث خطأ أثناء محاولة حذف فريق sftpusers", "dyndns_could_not_check_available": "لا يمكن التحقق مِن أنّ {domain:s} متوفر على {provider:s}.", "backup_mount_archive_for_restore": "جارٍ تهيئة النسخة الاحتياطية للاسترجاع…", - "root_password_replaced_by_admin_password": "لقد تم استبدال كلمة سر الجذر root بالكلمة الإدارية لـ admin." + "root_password_replaced_by_admin_password": "لقد تم استبدال كلمة سر الجذر root بالكلمة الإدارية لـ admin.", + "app_upgrade_stopped": "لقد تم إلغاء تحديث كافة التطبيقات لتجنب حادث بسبب فشل تحديث التطبيق السابق" } From abe294079e9dfd838fc7e828ca91305410e8b3c5 Mon Sep 17 00:00:00 2001 From: advocatux Date: Tue, 17 Sep 2019 19:02:19 +0000 Subject: [PATCH 136/299] Translated using Weblate (Spanish) Currently translated at 100.0% (584 of 584 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/es.json b/locales/es.json index c34a7e33c..d028a7a8e 100644 --- a/locales/es.json +++ b/locales/es.json @@ -599,9 +599,10 @@ "app_start_backup": "Obteniendo archivos de respaldo para {app}…", "app_start_remove": "Eliminando aplicación {app}…", "app_start_install": "Instalando aplicación {app}…", - "app_not_upgraded": "Las siguientes aplicaciones no se actualizaron: {apps}", + "app_not_upgraded": "Error al actualizar la aplicación «{failed_app}» y como consecuencia se han cancelado las actualizaciones de las siguientes aplicaciones: {apps}", "app_action_cannot_be_ran_because_required_services_down": "Esta aplicación necesita algunos servicios que no están funcionando ahora. Antes de continuar, debería intentar reiniciar los siguientes servicios (y posiblemente investigar por qué no funcionan): {services}", "already_up_to_date": "¡Nada que hacer! ¡Todo está actualizado!", "admin_password_too_long": "Elija una contraseña de menos de 127 caracteres", - "aborting": "Cancelando." + "aborting": "Cancelando.", + "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar" } From 64e388fa7d952690c3f35d8b13f67d52869bb383 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 17 Sep 2019 23:38:17 +0200 Subject: [PATCH 137/299] Implement helper function to test if we're able to access a webpage being logged in (or not) as user --- src/yunohost/tests/test_permission.py | 56 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 1c81e015f..51bf6a4c6 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -1,3 +1,4 @@ +import requests import pytest from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map @@ -11,6 +12,7 @@ from yunohost.utils.error import YunohostError # Get main domain maindomain = _get_maindomain() +dummy_password = "test123Ynh" def clean_user_groups_permission(): for u in user_list()['users']: @@ -27,8 +29,8 @@ def clean_user_groups_permission(): def setup_function(function): clean_user_groups_permission() - user_create("alice", "Alice", "White", "alice@" + maindomain, "test123Ynh") - user_create("bob", "Bob", "Snow", "bob@" + maindomain, "test123Ynh") + user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password) + user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password) permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False) permission_create("blog.main", sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") @@ -156,6 +158,30 @@ def check_permission_for_apps(): assert installed_apps == app_perms_prefix + +def can_access_webpage(webpath, logged_as=None): + + webpath = webpath.rstrip("/") + webroot = webpath.rsplit("/", 1)[0] + sso_url = webroot+"/yunohost/sso" + + # Anonymous access + if not logged_as: + r = requests.get(webpath, verify=False) + # Login as a user using dummy password + else: + with requests.Session() as session: + session.post(sso_url, + data={"user": logged_as, + "password": dummy_password}, + headers={"Referer": sso_url, + "Content-Type": "application/x-www-form-urlencoded"}, + verify=False) + r = session.get(webpath, verify=False) + + # If we can't access it, we got redirected to the sso + return not r.url.startswith(sso_url) + # # List functions # @@ -396,21 +422,21 @@ def test_permission_app_propagation_on_ssowat(): res = user_permission_list(full=True)['permissions'] assert res['permissions_app.main']['allowed'] == ["all_users"] - assert can_access(maindomain + "/urlpermissionapp", logged_as=None) - assert can_access(maindomain + "/urlpermissionapp", logged_as="alice") + assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as=None) + assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as="alice") user_permission_update("permissions_app.main", remove="visitors", add="bob") res = user_permission_list(full=True)['permissions'] - assert cannot_access(maindomain + "/urlpermissionapp", logged_as=None) - assert cannot_access(maindomain + "/urlpermissionapp", logged_as="alice") - assert can_access(maindomain + "/urlpermissionapp", logged_as="bob") + assert not can_access_webpage(maindomain + "/urlpermissionapp", logged_as=None) + assert not can_access_webpage(maindomain + "/urlpermissionapp", logged_as="alice") + assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as="bob") # Test admin access, as configured during install, only alice should be able to access it - assert cannot_access(maindomain + "/urlpermissionapp/admin", logged_as=None) - assert cannot_access(maindomain + "/urlpermissionapp/admin", logged_as="alice") - assert can_access(maindomain + "/urlpermissionapp/admin", logged_as="bob") + assert not can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as=None) + assert not can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as="alice") + assert can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as="bob") def test_permission_legacy_app_propagation_on_ssowat(): @@ -424,13 +450,13 @@ def test_permission_legacy_app_propagation_on_ssowat(): # It should automatically be migrated during the install assert res['permissions_app.main']['allowed'] == ["visitors"] - assert can_access(maindomain + "/legacy", logged_as=None) - assert can_access(maindomain + "/legacy", logged_as="alice") + assert can_access_webpage(maindomain + "/legacy", logged_as=None) + assert can_access_webpage(maindomain + "/legacy", logged_as="alice") # Try to update the permission and check that permissions are still consistent user_permission_update("legacy_app.main", remove="visitors", add="bob") res = user_permission_list(full=True)['permissions'] - assert cannot_access(maindomain + "/legacy", logged_as=None) - assert cannot_access(maindomain + "/legacy", logged_as="alice") - assert can_access(maindomain + "/legacy", logged_as="bob") + assert not can_access_webpage(maindomain + "/legacy", logged_as=None) + assert not can_access_webpage(maindomain + "/legacy", logged_as="alice") + assert can_access_webpage(maindomain + "/legacy", logged_as="bob") From 62e74d937e2a271ad1d56f70b00ce6e4be3b33d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 18 Sep 2019 02:01:20 +0200 Subject: [PATCH 138/299] Could not mount Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index cab2afff8..da5da0789 100644 --- a/locales/en.json +++ b/locales/en.json @@ -82,7 +82,7 @@ "backup_applying_method_tar": "Creating the backup TAR archive…", "backup_archive_app_not_found": "Could not find the app '{app:s}' in the backup archive", "backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})", - "backup_archive_mount_failed": "Could not mounting the backup archive", + "backup_archive_mount_failed": "Could not mount the backup archive", "backup_archive_name_exists": "A backup archive with this name already exists.", "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", "backup_archive_open_failed": "Could not open the backup archive", From 2e05a04cb945b21a677e36292b963502b4936eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 18 Sep 2019 02:04:57 +0200 Subject: [PATCH 139/299] for backup Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index da5da0789..29c5e0885 100644 --- a/locales/en.json +++ b/locales/en.json @@ -88,7 +88,7 @@ "backup_archive_open_failed": "Could not open the backup archive", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_writing_error": "Could not add the add files '{source:s}' (named in the archive: '{dest:s}') to be backed up into the compressed archive '{archive:s}'", - "backup_ask_for_copying_if_needed": "Some files could not be prepared for bacup using the method that avoids temporarily wasting space on the system. To perform the backup, {size:s}MB will be temporarily. Do you agree?", + "backup_ask_for_copying_if_needed": "Some files could not be prepared for backup using the method that avoids temporarily wasting space on the system. To perform the backup, {size:s}MB will be temporarily. Do you agree?", "backup_borg_not_implemented": "The Borg backup method is not yet implemented", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive write protected", "backup_cleaning_failed": "Could not clean-up the temporary backup folder", From fa42efe557702a67db1b27bb87a9966760448623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 18 Sep 2019 02:08:01 +0200 Subject: [PATCH 140/299] The Dovecot Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 29c5e0885..77192e069 100644 --- a/locales/en.json +++ b/locales/en.json @@ -307,7 +307,7 @@ "mail_domain_unknown": "Unknown e-mail address for domain '{domain:s}'", "mail_forward_remove_failed": "Could not remove e-mail forwarding '{mail:s}'", "mailbox_disabled": "E-mail turned off for user {user:s}", - "mailbox_used_space_dovecot_down": "the Dovecot mailbox service needs to be up, if you want to fetch used mailbox space", + "mailbox_used_space_dovecot_down": "The Dovecot mailbox service needs to be up, if you want to fetch used mailbox space", "mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user", "maindomain_change_failed": "Could not change the main domain", "maindomain_changed": "The main domain now changed", From adf2245c204f38807b49e43550063683e394926a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 18 Sep 2019 02:08:45 +0200 Subject: [PATCH 141/299] have a Co-Authored-By: decentral1se --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 77192e069..e1cc95e5a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -424,7 +424,7 @@ "pattern_firstname": "Must be a valid first name", "pattern_lastname": "Must be a valid last name", "pattern_listname": "Must be alphanumeric and underscore characters only", - "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not havea quota", + "pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to not have a quota", "pattern_password": "Must be at least 3 characters long", "pattern_port": "Must be a valid port number (i.e. 0-65535)", "pattern_port_or_range": "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)", From 01fa0939fd65a88d6652e26f215b91645e1c50e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 18 Sep 2019 02:13:43 +0200 Subject: [PATCH 142/299] Spelling: exists, 'yunohost log list' --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index e1cc95e5a..dc411c054 100644 --- a/locales/en.json +++ b/locales/en.json @@ -232,7 +232,7 @@ "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters—though it is good practice to use longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", "group_already_allowed": "Group '{group:s}' already has permission '{permission:s}' turned on for the app '{app:s}'", "group_already_disallowed": "Group '{group:s}' already has permissions '{permission:s}' turned off for the app '{app:s}'", - "group_name_already_exist": "Group {name:s} already exist", + "group_name_already_exist": "Group {name:s} already exists", "group_created": "Group '{group}' created", "group_creation_failed": "Could not create the group '{group}'", "group_deleted": "Group '{group}' deleted", @@ -258,7 +258,7 @@ "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'", "log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by clicking here to get help", "log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log display {name} --share' to get help", - "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list to see all available operation logs'", + "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list' to see all available operation logs", "log_operation_unit_unclosed_properly": "Operation unit has not been closed properly", "log_app_addaccess": "Add access to '{}'", "log_app_removeaccess": "Remove access to '{}'", From 00795a7a0156d5d45aeabb63819ab3d6511270e5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Sep 2019 18:38:47 +0200 Subject: [PATCH 143/299] Make migration re-run even more robust --- src/yunohost/data_migrations/0011_setup_group_permission.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index c79d80e0c..a99dfb7c1 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -62,12 +62,14 @@ class MyMigration(Migration): try: self.remove_if_exists("cn=sftpusers,ou=groups") self.remove_if_exists("ou=permission") - self.remove_if_exists('cn=all_users,ou=groups') - self.remove_if_exists('cn=visitors,ou=groups') + self.remove_if_exists('ou=groups') attr_dict = ldap_map['parents']['ou=permission'] ldap.add('ou=permission', attr_dict) + attr_dict = ldap_map['parents']['ou=groups'] + ldap.add('ou=groups', attr_dict) + attr_dict = ldap_map['children']['cn=all_users,ou=groups'] ldap.add('cn=all_users,ou=groups', attr_dict) From 8d01a816f3cd0fd07f570cd2d9f290d01f100ed9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 18 Sep 2019 18:39:05 +0200 Subject: [PATCH 144/299] Typo fixes following tests --- src/yunohost/app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 537616e68..7938d6786 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1404,8 +1404,8 @@ def app_ssowatconf(): unprotected_regex += _get_setting(app_settings, 'protected_regex') # New permission system - this_app_perms = {name: info for name, info in all_permissions.items if name.startswith(app + ".")} - for perm_name, perm_info in this_app_perms: + this_app_perms = {name: info for name, info in all_permissions.items() if name.startswith(app['id'] + ".")} + for perm_name, perm_info in this_app_perms.items(): urls = [url.rstrip("/") for url in perm_info["urls"]] if "visitors" in perm_info["allowed"]: unprotected_urls += urls @@ -1414,6 +1414,7 @@ def app_ssowatconf(): protected_urls = [u for u in protected_urls if u not in urls] else: # TODO : small optimization to implement : we don't need to explictly add all the app roots + protected_urls += urls # Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier... From 6d0bf43aef152c6f5b4f4ff8f4d505aa37ef7819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 03:51:24 +0200 Subject: [PATCH 145/299] Spelling: add the files, as write protected, trying to use --- locales/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index dc411c054..54bf5eaba 100644 --- a/locales/en.json +++ b/locales/en.json @@ -87,10 +87,10 @@ "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", "backup_archive_open_failed": "Could not open the backup archive", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", - "backup_archive_writing_error": "Could not add the add files '{source:s}' (named in the archive: '{dest:s}') to be backed up into the compressed archive '{archive:s}'", + "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", "backup_ask_for_copying_if_needed": "Some files could not be prepared for backup using the method that avoids temporarily wasting space on the system. To perform the backup, {size:s}MB will be temporarily. Do you agree?", "backup_borg_not_implemented": "The Borg backup method is not yet implemented", - "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive write protected", + "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cleaning_failed": "Could not clean-up the temporary backup folder", "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", "backup_couldnt_bind": "Could not bind {src:s} to {dest:s}.", @@ -134,7 +134,7 @@ "certmanager_cert_install_success_selfsigned": "Self-signed certificate now installed for the domain '{domain:s}'", "certmanager_cert_renew_success": "Let's Encrypt certificate renewed for the domain '{domain:s}'", "certmanager_cert_signing_failed": "Could not sign the new certificate", - "certmanager_certificate_fetching_or_enabling_failed": "It seems actually using the new certificate for {domain:s} did not work…", + "certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work…", "certmanager_conflicting_nginx_file": "Could not prepare domain for ACME challenge: the NGINX configuration file {filepath:s} is conflicting and should be removed first", "certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted—please try again later.", "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use '--force' to do so.)", From 35df93bb865b5202deaa757678ae066a2d0b7c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 03:54:40 +0200 Subject: [PATCH 146/299] Spelling: user info --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 54bf5eaba..2f4e1aadc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -291,7 +291,7 @@ "log_user_group_add": "Add '{}' group", "log_user_group_delete": "Delete '{}' group", "log_user_group_update": "Update '{}' group", - "log_user_update": "Update info of '{}' user", + "log_user_update": "Update user info of '{}'", "log_user_permission_add": "Update '{}' permission", "log_user_permission_remove": "Update '{}' permission", "log_tools_maindomain": "Make '{}' the main domain", From f18cff9dbaa1f1a8449bae114a1577a9a299a5bf Mon Sep 17 00:00:00 2001 From: "J. Doe" Date: Thu, 19 Sep 2019 13:01:22 +0200 Subject: [PATCH 147/299] change maxretry of fail2ban from 6 to 10 --- data/templates/fail2ban/yunohost-jails.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/fail2ban/yunohost-jails.conf b/data/templates/fail2ban/yunohost-jails.conf index bf3bcb6e3..fdbd7990b 100644 --- a/data/templates/fail2ban/yunohost-jails.conf +++ b/data/templates/fail2ban/yunohost-jails.conf @@ -29,4 +29,4 @@ protocol = tcp filter = yunohost logpath = /var/log/nginx/*error.log /var/log/nginx/*access.log -maxretry = 6 +maxretry = 10 From 7903fb27f7ffcdd8dc368509fae899357f865a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:10:09 +0200 Subject: [PATCH 148/299] All applications are already Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 2f4e1aadc..7f68ce3d5 100644 --- a/locales/en.json +++ b/locales/en.json @@ -27,7 +27,7 @@ "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}", - "app_no_upgrade": "Everything is up-to-date", + "app_no_upgrade": "All applications are already up-to-date", "app_not_upgraded": "These apps were not upgraded: {apps}", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_installed": "The application '{app:s}' is not to be found among all installed apps: {all_apps}", From 98236ac839fe78d1596f3e4f7415cff4989d4f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:11:27 +0200 Subject: [PATCH 149/299] Some requirements are not met Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 7f68ce3d5..35656cfd9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -35,7 +35,7 @@ "app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes", "app_removed": "{app:s} removed", "app_requirements_checking": "Checking required packages for {app}…", - "app_requirements_failed": "Did not meet requirements for {app}: {error}", + "app_requirements_failed": "Some requirements are not met for {app}: {error}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", "app_start_install": "Installing application {app}…", From a8337f1bfd178d04c946428a3823af8b5886d19b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:12:33 +0200 Subject: [PATCH 150/299] Allows you to reach Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 35656cfd9..15c8f5e4b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -493,7 +493,7 @@ "service_already_started": "The service '{service:s}' has already been started", "service_already_stopped": "The service '{service:s}' has already been stopped", "service_cmd_exec_failed": "Could not execute the command '{command:s}'", - "service_description_avahi-daemon": "allows you to reach your server using 'yunohost.local' in your local network", + "service_description_avahi-daemon": "Allows you to reach your server using 'yunohost.local' in your local network", "service_description_dnsmasq": "Handles domain name resolution (DNS)", "service_description_dovecot": "Allows e-mail clients to access/fetch email (via IMAP and POP3)", "service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet", From 8ad01c6093eec38915b09c9362133cdac97d6e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:12:50 +0200 Subject: [PATCH 151/299] Runs applications Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 15c8f5e4b..e4c41fc5b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -502,7 +502,7 @@ "service_description_mysql": "Stores applications data (SQL database)", "service_description_nginx": "Serves or provides access to all the websites hosted on your server", "service_description_nslcd": "Handles YunoHost user shell connection", - "service_description_php7.0-fpm": "runs applications written in PHP with NGINX", + "service_description_php7.0-fpm": "Runs applications written in PHP with NGINX", "service_description_postfix": "Used to send and receive e-mails", "service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs", "service_description_rmilter": "Checks various parameters in e-mails", From 929421f81559deaacd861dfe0d60411816a884bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:13:30 +0200 Subject: [PATCH 152/299] Migration {id} completed Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index e4c41fc5b..03a750aa5 100644 --- a/locales/en.json +++ b/locales/en.json @@ -383,7 +383,7 @@ "migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}", "migrations_running_forward": "Running migration {id}…", "migrations_skip_migration": "Skipping migration {id}…", - "migrations_success_forward": "Ran migration {id}", + "migrations_success_forward": "Migration {id} completed", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", "monitor_disabled": "Server monitoring now turned off", "monitor_enabled": "Server monitoring now turned on", From 0015db83b9970012d5188bc22944358f1febb22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:14:17 +0200 Subject: [PATCH 153/299] create the backup archive Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 03a750aa5..5078e1026 100644 --- a/locales/en.json +++ b/locales/en.json @@ -96,7 +96,7 @@ "backup_couldnt_bind": "Could not bind {src:s} to {dest:s}.", "backup_created": "Backup created", "backup_creating_archive": "Creating the backup archive…", - "backup_creation_failed": "Could not create the backup creation", + "backup_creation_failed": "Could not create the backup archive", "backup_csv_addition_failed": "Could not add files to backup into the CSV file", "backup_csv_creation_failed": "Could not create the CSV file needed for restoration", "backup_custom_backup_error": "Custom backup method could not get past the 'backup' step", From 87050276b4a217acea21b7f45820bdfaead1194d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Sep 2019 19:26:41 +0200 Subject: [PATCH 154/299] Finish to implement first visitor test + fixes following test ... --- src/yunohost/app.py | 12 +++++------ src/yunohost/tests/test_permission.py | 29 +++++++++++++-------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 7938d6786..bba5fb104 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -429,8 +429,10 @@ def app_map(app=None, raw=False, user=None): continue if 'no_sso' in app_settings: # I don't think we need to check for the value here continue - if user and user not in permissions[app_id + ".main"]["corresponding_users"]: - continue + if user: + main_perm = permissions[app_id + ".main"] + if user not in main_perm["corresponding_users"] and "visitors" not in main_perm["allowed"]: + continue domain = app_settings['domain'] path = app_settings['path'] @@ -2613,10 +2615,8 @@ def _parse_args_in_yunohost_format(args, action_args): if arg_value not in domain_list()['domains']: raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('domain_unknown')) elif arg_type == 'user': - try: - user_info(arg_value) - except YunohostError as e: - raise YunohostError('app_argument_invalid', name=arg_name, error=e) + if not arg_value in user_list()["users"].keys(): + raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('user_unknown', user=arg_value)) elif arg_type == 'app': if not _is_installed(arg_value): raise YunohostError('app_argument_invalid', name=arg_name, error=m18n.n('app_unknown')) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 51bf6a4c6..bef042be1 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -19,7 +19,7 @@ def clean_user_groups_permission(): user_delete(u) for g in user_group_list()['groups']: - if g != "all_users": + if g not in ["all_users", "visitors"]: user_group_delete(g) for p in user_permission_list()['permissions']: @@ -162,8 +162,7 @@ def check_permission_for_apps(): def can_access_webpage(webpath, logged_as=None): webpath = webpath.rstrip("/") - webroot = webpath.rsplit("/", 1)[0] - sso_url = webroot+"/yunohost/sso" + sso_url = "https://"+maindomain+"/yunohost/sso/" # Anonymous access if not logged_as: @@ -177,6 +176,8 @@ def can_access_webpage(webpath, logged_as=None): headers={"Referer": sso_url, "Content-Type": "application/x-www-form-urlencoded"}, verify=False) + # We should have some cookies related to authentication now + assert session.cookies r = session.get(webpath, verify=False) # If we can't access it, we got redirected to the sso @@ -413,30 +414,28 @@ def test_permission_app_change_url(): def test_permission_app_propagation_on_ssowat(): - # TODO / FIXME : To be actually implemented later .... - raise NotImplementedError - app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&is_public=1&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) res = user_permission_list(full=True)['permissions'] - assert res['permissions_app.main']['allowed'] == ["all_users"] + assert res['permissions_app.main']['allowed'] == ["visitors"] - assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as=None) - assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as="alice") + app_webroot = "https://%s/urlpermissionapp" % maindomain + assert can_access_webpage(app_webroot, logged_as=None) + assert can_access_webpage(app_webroot, logged_as="alice") user_permission_update("permissions_app.main", remove="visitors", add="bob") res = user_permission_list(full=True)['permissions'] - assert not can_access_webpage(maindomain + "/urlpermissionapp", logged_as=None) - assert not can_access_webpage(maindomain + "/urlpermissionapp", logged_as="alice") - assert can_access_webpage(maindomain + "/urlpermissionapp", logged_as="bob") + assert not can_access_webpage(app_webroot, logged_as=None) + assert not can_access_webpage(app_webroot, logged_as="alice") + assert can_access_webpage(app_webroot, logged_as="bob") # Test admin access, as configured during install, only alice should be able to access it - assert not can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as=None) - assert not can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as="alice") - assert can_access_webpage(maindomain + "/urlpermissionapp/admin", logged_as="bob") + assert not can_access_webpage(app_webroot+"/admin", logged_as=None) + assert can_access_webpage(app_webroot+"/admin", logged_as="alice") + assert not can_access_webpage(app_webroot+"/admin", logged_as="bob") def test_permission_legacy_app_propagation_on_ssowat(): From 5a792fc6dbb72d834ccc8eee3a520339c9d1f9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:27:52 +0200 Subject: [PATCH 155/299] password got changed Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 5078e1026..c68141d97 100644 --- a/locales/en.json +++ b/locales/en.json @@ -3,7 +3,7 @@ "action_invalid": "Invalid action '{action:s}'", "admin_password": "Administration password", "admin_password_change_failed": "Cannot change password", - "admin_password_changed": "The administration password now changed", + "admin_password_changed": "The administration password got changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do. Everything is already up-to-date.", "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down): {services}", From d2e4a59a2506f56f463c1881afc02fe21d148312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Thu, 19 Sep 2019 19:31:03 +0200 Subject: [PATCH 156/299] The user, Could not find the, in the list of --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index c68141d97..5093d6e69 100644 --- a/locales/en.json +++ b/locales/en.json @@ -30,7 +30,7 @@ "app_no_upgrade": "All applications are already up-to-date", "app_not_upgraded": "These apps were not upgraded: {apps}", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", - "app_not_installed": "The application '{app:s}' is not to be found among all installed apps: {all_apps}", + "app_not_installed": "Could not find the application '{app:s}' in the list of installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", "app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes", "app_removed": "{app:s} removed", @@ -564,7 +564,7 @@ "upnp_disabled": "UPnP turned off", "upnp_enabled": "UPnP turned on", "upnp_port_open_failed": "Could not open port via UPnP", - "user_already_in_group": "The User '{user:}' already in the group {group:s}", + "user_already_in_group": "The user '{user:}' is already in the '{group:s}' group", "user_created": "User created", "user_creation_failed": "Could not create user", "user_deleted": "User deleted", From eb57a4ad9e982eea6a5dc0c7543c61a437265568 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 19 Sep 2019 19:51:27 +0200 Subject: [PATCH 157/299] Get rid of etckeeper --- data/hooks/conf_regen/01-yunohost | 3 -- data/templates/yunohost/etckeeper.conf | 43 -------------------------- debian/control | 2 +- 3 files changed, 1 insertion(+), 47 deletions(-) delete mode 100644 data/templates/yunohost/etckeeper.conf diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index faf041110..f22de7a53 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -53,9 +53,6 @@ do_pre_regen() { else sudo cp services.yml /etc/yunohost/services.yml fi - - mkdir -p "$pending_dir"/etc/etckeeper/ - cp etckeeper.conf "$pending_dir"/etc/etckeeper/ } _update_services() { diff --git a/data/templates/yunohost/etckeeper.conf b/data/templates/yunohost/etckeeper.conf deleted file mode 100644 index 2d11c3dc6..000000000 --- a/data/templates/yunohost/etckeeper.conf +++ /dev/null @@ -1,43 +0,0 @@ -# The VCS to use. -#VCS="hg" -VCS="git" -#VCS="bzr" -#VCS="darcs" - -# Options passed to git commit when run by etckeeper. -GIT_COMMIT_OPTIONS="--quiet" - -# Options passed to hg commit when run by etckeeper. -HG_COMMIT_OPTIONS="" - -# Options passed to bzr commit when run by etckeeper. -BZR_COMMIT_OPTIONS="" - -# Options passed to darcs record when run by etckeeper. -DARCS_COMMIT_OPTIONS="-a" - -# Uncomment to avoid etckeeper committing existing changes -# to /etc automatically once per day. -#AVOID_DAILY_AUTOCOMMITS=1 - -# Uncomment the following to avoid special file warning -# (the option is enabled automatically by cronjob regardless). -#AVOID_SPECIAL_FILE_WARNING=1 - -# Uncomment to avoid etckeeper committing existing changes to -# /etc before installation. It will cancel the installation, -# so you can commit the changes by hand. -#AVOID_COMMIT_BEFORE_INSTALL=1 - -# The high-level package manager that's being used. -# (apt, pacman-g2, yum, zypper etc) -HIGHLEVEL_PACKAGE_MANAGER=apt - -# The low-level package manager that's being used. -# (dpkg, rpm, pacman, pacman-g2, etc) -LOWLEVEL_PACKAGE_MANAGER=dpkg - -# To push each commit to a remote, put the name of the remote here. -# (eg, "origin" for git). Space-separated lists of multiple remotes -# also work (eg, "origin gitlab github" for git). -PUSH_REMOTE="" diff --git a/debian/control b/debian/control index 64c7cd31d..c0604d90e 100644 --- a/debian/control +++ b/debian/control @@ -31,7 +31,7 @@ Depends: ${python:Depends}, ${misc:Depends} , equivs, lsof Recommends: yunohost-admin , openssh-server, ntp, inetutils-ping | iputils-ping - , bash-completion, rsyslog, etckeeper + , bash-completion, rsyslog , php-gd, php-curl, php-gettext, php-mcrypt , python-pip , unattended-upgrades From 52b59ff2a41b04107433caa7e1523a03aee8f4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quent=C3=AD?= Date: Wed, 18 Sep 2019 16:30:18 +0000 Subject: [PATCH 158/299] Translated using Weblate (Occitan) Currently translated at 99.0% (578 of 584 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/oc/ --- locales/oc.json | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/locales/oc.json b/locales/oc.json index 2cb33f4bd..320a18341 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -198,7 +198,7 @@ "migrations_current_target": "La cibla de migracion est {}", "migrations_error_failed_to_load_migration": "ERROR : fracàs del cargament de la migracion {number} {name}", "migrations_list_conflict_pending_done": "Podètz pas utilizar --previous e --done a l’encòp.", - "migrations_loading_migration": "Cargament de la migracion{number} {name}…", + "migrations_loading_migration": "Cargament de la migracion {id}…", "migrations_no_migrations_to_run": "Cap de migracion de lançar", "migrations_show_currently_running_migration": "Realizacion de la migracion {number} {name}…", "migrations_show_last_migration": "La darrièra migracion realizada es {}", @@ -376,10 +376,10 @@ "migration_0003_general_warning": "Notatz qu’aquesta migracion es una operacion delicata. Encara que la còla YunoHost aguèsse fach çò melhor per la tornar legir e provar, la migracion poiriá copar de parts del sistèma o de las aplicacions.\n\nEn consequéncia, vos recomandam :\n· · · · - de lançar una salvagarda de vòstras donadas o aplicacions criticas. Mai d’informacions a https://yunohost.org/backup ;\n· · · · - d’èsser pacient aprèp aver lançat la migracion : segon vòstra connexion Internet e material, pòt trigar qualques oras per que tot siá mes al nivèl.\n\nEn mai, lo pòrt per SMTP, utilizat pels clients de corrièls extèrns (coma Thunderbird o K9-Mail per exemple) foguèt cambiat de 465 (SSL/TLS) per 587 (STARTTLS). L’ancian pòrt 465 serà automaticament tampat e lo nòu pòrt 587 serà dobèrt dins lo parafuòc. Vosautres e vòstres utilizaires *auretz* d’adaptar la configuracion de vòstre client de corrièl segon aqueles cambiaments !", "migration_0003_problematic_apps_warning": "Notatz que las aplicacions seguentas, saique problematicas, son estadas desactivadas. Semblan d’aver estadas installadas d’una lista d’aplicacions o que son pas marcadas coma «working ». En consequéncia, podèm pas assegurar que tendràn de foncionar aprèp la mesa a nivèl : {problematic_apps}", "migrations_bad_value_for_target": "Nombre invalid pel paramètre « target », los numèros de migracion son 0 o {}", - "migrations_migration_has_failed": "La migracion {number} {name} a pas capitat amb l’excepcion {exception}, anullacion", - "migrations_skip_migration": "Passatge de la migracion {number} {name}…", - "migrations_to_be_ran_manually": "La migracion {number} {name} deu èsser lançada manualament. Mercés d’anar a Aisinas > Migracion dins l’interfàcia admin, o lançar « yunohost tools migrations migrate ».", - "migrations_need_to_accept_disclaimer": "Per lançar la migracion {number} {name} , avètz d’acceptar aquesta clausa de non-responsabilitat :\n---\n{disclaimer}\n---\nS’acceptatz de lançar la migracion, mercés de tornar executar la comanda amb l’opcion accept-disclaimer.", + "migrations_migration_has_failed": "La migracion {id} a pas capitat, abandon. Error : {exception}", + "migrations_skip_migration": "Passatge de la migracion {id}…", + "migrations_to_be_ran_manually": "La migracion {id} deu èsser lançada manualament. Mercés d’anar a Aisinas > Migracion dins l’interfàcia admin, o lançar « yunohost tools migrations migrate ».", + "migrations_need_to_accept_disclaimer": "Per lançar la migracion {id} , avètz d’acceptar aquesta clausa de non-responsabilitat :\n---\n{disclaimer}\n---\nS’acceptatz de lançar la migracion, mercés de tornar executar la comanda amb l’opcion accept-disclaimer.", "monitor_disabled": "La supervision del servidor es desactivada", "monitor_enabled": "La supervision del servidor es activada", "mysql_db_initialized": "La basa de donadas MySQL es estada inicializada", @@ -453,7 +453,7 @@ "migration_0005_postgresql_94_not_installed": "Postgresql es pas installat sul sistèma. Pas res de far !", "migration_0005_postgresql_96_not_installed": "Avèm trobat que Postgresql 9.4 es installat, mas cap de version de Postgresql 9.6 pas trobada !? Quicòm d’estranh a degut arribar a vòstre sistèma :( …", "migration_0005_not_enough_space": "I a pas pro d’espaci disponible sus {path} per lançar la migracion d’aquela passa :(.", - "recommend_to_add_first_user": "La post installacion es acabada, mas YunoHost fa besonh d’almens un utilizaire per foncionar coma cal. Vos cal n’ajustar un en utilizant la comanda « yunohost user create $username » o ben l’interfàcia d’administracion.", + "recommend_to_add_first_user": "La post installacion es acabada, mas YunoHost fa besonh d’almens un utilizaire per foncionar coma cal. Vos cal n’ajustar un en utilizant la comanda « yunohost user create Date: Tue, 10 Sep 2019 14:08:19 +0200 Subject: [PATCH 159/299] Fix / remove stale i18n strings --- locales/en.json | 26 +------------------------- src/yunohost/app.py | 2 +- src/yunohost/tools.py | 2 +- tests/_test_m18nkeys.py | 39 +++++++++++++++++++++++++++++---------- 4 files changed, 32 insertions(+), 37 deletions(-) diff --git a/locales/en.json b/locales/en.json index 2d708279d..a4f5ec9da 100644 --- a/locales/en.json +++ b/locales/en.json @@ -14,7 +14,6 @@ "app_argument_choice_invalid": "Use one of these choices '{choices:s}' for the argument '{name:s}'", "app_argument_invalid": "Pick a valid value for the argument '{name:s}': {error:s}", "app_argument_required": "Argument '{name:s}' is required", - "app_change_no_change_url_script": "The application {app_name:s} doesn't support changing its URL yet, you might need to upgrade it.", "app_change_url_failed_nginx_reload": "Could not reload NGINX. Here is the output of 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain:s}{path:s}'), nothing to do.", "app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", @@ -75,7 +74,6 @@ "ask_password": "Password", "ask_path": "Path", "backup_abstract_method": "This backup method has yet to be implemented", - "backup_action_required": "You must specify something to save", "backup_actually_backuping": "Creating a backup archive from the collected files…", "backup_app_failed": "Could not back up the app '{app:s}'", "backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", @@ -84,7 +82,6 @@ "backup_applying_method_tar": "Creating the backup TAR archive…", "backup_archive_app_not_found": "Could not find the app '{app:s}' in the backup archive", "backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})", - "backup_archive_mount_failed": "Could not mount the backup archive", "backup_archive_name_exists": "A backup archive with this name already exists.", "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", "backup_archive_open_failed": "Could not open the backup archive", @@ -97,16 +94,13 @@ "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", "backup_couldnt_bind": "Could not bind {src:s} to {dest:s}.", "backup_created": "Backup created", - "backup_creating_archive": "Creating the backup archive…", "backup_creation_failed": "Could not create the backup archive", "backup_csv_addition_failed": "Could not add files to backup into the CSV file", "backup_csv_creation_failed": "Could not create the CSV file needed for restoration", "backup_custom_backup_error": "Custom backup method could not get past the 'backup' step", "backup_custom_mount_error": "Custom backup method could not get past the 'mount' step", - "backup_custom_need_mount_error": "Custom backup method could not get past 'need_mount' step", "backup_delete_error": "Could not delete '{path:s}'", "backup_deleted": "Backup deleted", - "backup_extracting_archive": "Extracting backup archive…", "backup_hook_unknown": "The backup hook '{hook:s}' is unknown", "backup_invalid_archive": "This is not a backup archive", "backup_method_borg_finished": "Backup into Borg finished", @@ -142,7 +136,6 @@ "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use '--force' to do so.)", "certmanager_domain_dns_ip_differs_from_public_ip": "The DNS 'A' record for the domain '{domain:s}' is different from this server IP. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_http_not_working": "It seems the domain {domain:s} cannot be accessed through HTTP. Check that your DNS and NGINX configuration is correct", - "certmanager_domain_not_resolved_locally": "The domain {domain:s} cannot be resolved from inside your YunoHost server. This might happen if you recently modified your DNS record. If so, please wait a few hours for it to propagate. If the issue persists, consider adding {domain:s} to /etc/hosts. (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_unknown": "Unknown domain '{domain:s}'", "certmanager_error_no_A_record": "No DNS 'A' record found for '{domain:s}'. You need to make your domain name point to your machine to be able to install a Let's Encrypt certificate. (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_hit_rate_limit": "Too many certificates already issued for this exact set of domains {domain:s} recently. Please try again later. See https://letsencrypt.org/docs/rate-limits/ for more details", @@ -158,12 +151,10 @@ "diagnosis_debian_version_error": "Could not retrieve the Debian version: {error}", "diagnosis_kernel_version_error": "Could not retrieve kernel version: {error}", "diagnosis_monitor_disk_error": "Could not monitor disks: {error}", - "diagnosis_monitor_network_error": "Could not monitor network: {error}", "diagnosis_monitor_system_error": "Could not monitor system: {error}", "diagnosis_no_apps": "No installed application", "dpkg_is_broken": "You cannot do this right now because dpkg/APT (the system package managers) seems to be in a broken state… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", "dpkg_lock_not_available": "This command can't be ran right now because another program seems to be using the lock of dpkg (the system package manager)", - "dnsmasq_isnt_installed": "Dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install it'", "domain_cannot_remove_main": "Cannot remove main domain. Set one first", "domain_cert_gen_failed": "Could not generate certificate", "domain_created": "Domain created", @@ -172,15 +163,11 @@ "domain_deletion_failed": "Could not delete domain", "domain_dns_conf_is_just_a_recommendation": "This command shows you the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.", "domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain", - "domain_dyndns_dynette_is_unreachable": "Could not reach YunoHost dynette, either your YunoHost is not correctly connected to the Internet, or the dynette server is down. Error: {error}", - "domain_dyndns_invalid": "This domain can not be used with DynDNS", "domain_dyndns_root_unknown": "Unknown DynDNS root domain", "domain_exists": "The domain already exists", "domain_hostname_failed": "Could not set new hostname. This might cause an issue later (it might be fine).", "domain_uninstall_app_first": "One or more apps are installed on this domain. Please uninstall them before proceeding to domain removal", "domain_unknown": "Unknown domain", - "domain_zone_exists": "DNS zone file already exists", - "domain_zone_not_found": "DNS zone file not found for domain {:s}", "domains_available": "Available domains:", "done": "Done", "downloading": "Downloading…", @@ -194,6 +181,7 @@ "dyndns_key_generating": "Generating DNS key… It may take a while.", "dyndns_key_not_found": "DNS key not found for the domain", "dyndns_no_domain_registered": "No domain registered with DynDNS", + "dyndns_provider_unreachable": "Unable to reach Dyndns provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", "dyndns_registered": "DynDNS domain registered", "dyndns_registration_failed": "Could not register DynDNS domain: {error:s}", "dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.", @@ -210,7 +198,6 @@ "firewall_reload_failed": "Could not reload the firewall", "firewall_reloaded": "Firewall reloaded", "firewall_rules_cmd_failed": "Some firewall rules commands have failed. More info in log.", - "format_datetime_short": "%m/%d/%Y %I:%M %p", "global_settings_bad_choice_for_enum": "Bad choice for setting {setting:s}, received '{choice:s}', but available choices are: {available_choices:s}", "global_settings_bad_type_for_setting": "Bad type for setting {setting:s}, received {received_type:s}, expected {expected_type:s}", "global_settings_cant_open_settings": "Could not open settings file, reason: {reason:s}", @@ -240,7 +227,6 @@ "group_deleted": "Group '{group}' deleted", "group_deletion_failed": "Could not delete the group '{group}'", "group_deletion_not_allowed": "The group {group:s} cannot be deleted manually.", - "group_info_failed": "Could not present group info", "group_unknown": "The group '{group:s}' is unknown", "group_updated": "Group '{group}' updated", "group_update_failed": "Could not update the group '{group}'", @@ -251,7 +237,6 @@ "hook_name_unknown": "Unknown hook name '{name:s}'", "installation_complete": "Installation complete", "installation_failed": "Something went wrong with the installation", - "invalid_url_format": "Something is wrong with the URL", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", @@ -403,24 +388,18 @@ "network_check_mx_ko": "DNS MX record is not set", "network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network", "network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked", - "new_domain_required": "You must provide the new main domain", - "no_appslist_found": "No app list found", "no_internet_connection": "Server not connected to the Internet", - "no_ipv6_connectivity": "IPv6 connectivity is not available", - "no_restore_script": "No restore script found for the app '{app:s}'", "not_enough_disk_space": "Not enough free space on '{path:s}'", "operation_interrupted": "The operation was manually interrupted?", "package_not_installed": "Package '{pkgname}' is not installed", "package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'", "package_unknown": "Unknown package '{pkgname}'", - "packages_upgrade_critical_later": "Critical packages ({packages:s}) will be upgraded later", "packages_upgrade_failed": "Could not upgrade all the packages", "password_listed": "This password is among the most used password in the world. Please choose something more unique.", "password_too_simple_1": "The password needs to be at least 8 characters long", "password_too_simple_2": "The password needs to be at least 8 characters long and contain a digit, upper and lower characters", "password_too_simple_3": "The password needs to be at least 8 characters long and contain a digit, upper, lower and special characters", "password_too_simple_4": "The password needs to be at least 12 characters long and contain a digit, upper, lower and special characters", - "path_removal_failed": "Could not remove path {:s}", "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, alphanumeric and -_. characters only", "pattern_domain": "Must be a valid domain name (e.g. my-domain.org)", "pattern_email": "Must be a valid email address (e.g. someone@domain.org)", @@ -468,7 +447,6 @@ "regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…", "regenconf_failed": "Could not regenerate the configuration for category(s): {categories}", "regenconf_pending_applying": "Applying pending configuration for category '{category}'…", - "restore_action_required": "You must pick something to restore", "restore_already_installed_app": "An app with the ID '{app:s}' is already installed", "restore_app_failed": "Could not restore the app '{app:s}'", "restore_cleaning_failed": "Could not clean up the temporary restoration directory", @@ -478,7 +456,6 @@ "restore_failed": "Could not restore system", "restore_hook_unavailable": "The restoration script for '{part:s}' not available on your system and not in the archive either", "restore_may_be_not_enough_disk_space": "Your system seems does not have enough space (free: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", - "restore_mounting_archive": "Mounting archive into '{path:s}'", "restore_not_enough_disk_space": "Not enough space (space: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)", "restore_nothings_done": "Nothing was restored", "restore_removing_tmp_dir_failed": "Could not remove an old temporary directory", @@ -530,7 +507,6 @@ "service_reloaded_or_restarted": "'{service:s}' service reloaded or restarted", "service_start_failed": "Could not start the service '{service:s}'\n\nRecent service logs:{logs:s}", "service_started": "'{service:s}' service started", - "service_status_failed": "Could not determine status of the service '{service:s}'", "service_stop_failed": "Could not stop the service '{service:s}'\n\nRecent service logs:{logs:s}", "service_stopped": "'{service:s}' service stopped", "service_unknown": "Unknown service '{service:s}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c93c7ba80..0d30a2da3 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -472,7 +472,7 @@ def app_change_url(operation_logger, app, domain, path): raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id()) if not os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")): - raise YunohostError("app_change_no_change_url_script", app_name=app) + raise YunohostError("app_change_url_no_script", app_name=app) old_domain = app_setting(app, "domain") old_path = app_setting(app, "path") diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 5ff5d64fc..eb2fc5cc6 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -657,7 +657,7 @@ def tools_upgrade(operation_logger, apps=None, system=False): ) returncode = call_async_output(dist_upgrade, callbacks, shell=True) if returncode != 0: - logger.warning('tools_upgrade_regular_packages_failed', + logger.warning(m18n.n('tools_upgrade_regular_packages_failed'), packages_list=', '.join(noncritical_packages_upgradable)) operation_logger.error(m18n.n('packages_upgrade_failed')) raise YunohostError(m18n.n('packages_upgrade_failed')) diff --git a/tests/_test_m18nkeys.py b/tests/_test_m18nkeys.py index ee8df0dc6..cc6202517 100644 --- a/tests/_test_m18nkeys.py +++ b/tests/_test_m18nkeys.py @@ -5,23 +5,37 @@ import glob import json import yaml +ignore = [ "service_description_", + "migration_description_", + "global_settings_setting_", + "password_too_simple_", + "password_listed", + "backup_method_", + "backup_applying_method_", + "confirm_app_install_", + "log_", + ] + ############################################################################### # Find used keys in python code # ############################################################################### # This regex matches « foo » in patterns like « m18n.n( "foo" » -p = re.compile(r'm18n\.n\(\s*[\"\']([a-zA-Z1-9_]+)[\"\']') +p1 = re.compile(r'm18n\.n\(\s*[\"\']([a-zA-Z0-9_]+)[\"\']') +p2 = re.compile(r'YunohostError\([\'\"]([a-zA-Z0-9_]+)[\'\"]') -python_files = glob.glob("/vagrant/yunohost/src/yunohost/*.py") -python_files.extend(glob.glob("/vagrant/yunohost/src/yunohost/utils/*.py")) -python_files.append("/vagrant/yunohost/bin/yunohost") +python_files = glob.glob("../src/yunohost/*.py") +python_files.extend(glob.glob("../src/yunohost/utils/*.py")) +python_files.extend(glob.glob("../src/yunohost/data_migrations/*.py")) +python_files.append("../bin/yunohost") python_keys = set() for python_file in python_files: - with open(python_file) as f: - keys_in_file = p.findall(f.read()) - for key in keys_in_file: - python_keys.add(key) + content = open(python_file).read() + for match in p1.findall(content): + python_keys.add(match) + for match in p2.findall(content): + python_keys.add(match) ############################################################################### # Find keys used in actionmap # @@ -42,19 +56,20 @@ for _, category in actionmap.items(): actionmap_keys.add(argument["extra"]["password"]) if "ask" in argument["extra"]: actionmap_keys.add(argument["extra"]["ask"]) + if "comment" in argument["extra"]: + actionmap_keys.add(argument["extra"]["comment"]) if "pattern" in argument["extra"]: actionmap_keys.add(argument["extra"]["pattern"][1]) if "help" in argument["extra"]: print argument["extra"]["help"] -# These keys are used but difficult to parse actionmap_keys.add("admin_password") ############################################################################### # Load en locale json keys # ############################################################################### -en_locale_file = "/vagrant/yunohost/locales/en.json" +en_locale_file = "../locales/en.json" with open(en_locale_file) as f: en_locale_json = json.loads(f.read()) @@ -72,11 +87,15 @@ keys_defined_but_not_used = en_locale_keys.difference(used_keys) if len(keys_used_but_not_defined) != 0: print "> Error ! Those keys are used in some files but not defined :" for key in sorted(keys_used_but_not_defined): + if any(key.startswith(i) for i in ignore): + continue print " - %s" % key if len(keys_defined_but_not_used) != 0: print "> Warning ! Those keys are defined but seems unused :" for key in sorted(keys_defined_but_not_used): + if any(key.startswith(i) for i in ignore): + continue print " - %s" % key From 6ed062b41bc106e9b84fbbc6c9828569926e6b89 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 14:49:31 +0200 Subject: [PATCH 160/299] app_no_upgrade -> apps_already_up_to_date --- locales/en.json | 2 +- src/yunohost/app.py | 4 ++-- src/yunohost/tools.py | 5 +---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/locales/en.json b/locales/en.json index a4f5ec9da..61fdcfa9b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -27,7 +27,6 @@ "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}", - "app_no_upgrade": "All applications are already up-to-date", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps upgrades have been cancelled: {apps}", "app_upgrade_stopped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", @@ -50,6 +49,7 @@ "app_upgrade_failed": "Could not upgrade {app:s}", "app_upgrade_some_app_failed": "Some applications could not be upgraded", "app_upgraded": "{app:s} upgraded", + "apps_already_up_to_date": "All applications are already up-to-date", "apps_permission_not_found": "No permission found for the installed apps", "apps_permission_restoration_failed": "Grant the permission permission '{permission:s}' to restore {app:s}", "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is damaged.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 0d30a2da3..5a51e57bb 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -590,7 +590,7 @@ def app_upgrade(app=[], url=None, file=None): try: app_list() except YunohostError: - raise YunohostError('app_no_upgrade') + raise YunohostError('apps_already_up_to_date') not_upgraded_apps = [] @@ -611,7 +611,7 @@ def app_upgrade(app=[], url=None, file=None): raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id()) if len(apps) == 0: - raise YunohostError('app_no_upgrade') + raise YunohostError('apps_already_up_to_date') if len(apps) > 1: logger.info(m18n.n("app_upgrade_several_apps", apps=", ".join(apps))) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index eb2fc5cc6..64689fe0c 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -584,10 +584,7 @@ def tools_upgrade(operation_logger, apps=None, system=False): upgradable_apps = [app["id"] for app in _list_upgradable_apps()] - if not upgradable_apps: - logger.info(m18n.n("app_no_upgrade")) - return - elif len(apps) and all(app not in upgradable_apps for app in apps): + if not upgradable_apps or (len(apps) and all(app not in upgradable_apps for app in apps)): logger.info(m18n.n("apps_already_up_to_date")) return From 379c28de909774bac44e10af34ecd6d91e281ab1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 15:00:31 +0200 Subject: [PATCH 161/299] Update src/yunohost/backup.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Allan Nordhøy --- src/yunohost/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 305152865..c28160342 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1365,7 +1365,7 @@ class RestoreManager(): permission_create(permission_name, urls=permission_infos.get("urls", [])) if "allowed" not in permission_infos: - logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s ... You might need to reconfigure permissions yourself!" % (permission_name, app_instance_name)) + logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name)) else: should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups] current_allowed = user_permission_list()["permissions"][permission_name]["allowed"] From ebf2fb9a141da65954d1855bc025f0cf362c6f99 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 20:13:51 +0200 Subject: [PATCH 162/299] Use relative urls by default for permissions while still supporting absolute urls ... --- src/yunohost/app.py | 18 +++++------- .../0011_setup_group_permission.py | 2 +- src/yunohost/permission.py | 29 ++++++++++--------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index bba5fb104..231568439 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -553,8 +553,6 @@ def app_change_url(operation_logger, app, domain, path): app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) - permission_urls(app+".main", add=[domain+path], remove=[old_domain+old_path], sync_perm=True) - # avoid common mistakes if _run_service_command("reload", "nginx") is False: # grab nginx errors @@ -868,10 +866,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu if os.path.exists(os.path.join(extracted_app_folder, file_to_copy)): os.system('cp -R %s/%s %s' % (extracted_app_folder, file_to_copy, app_setting_path)) - # Create permission before the install (useful if the install script redefine the permission) - # Note that sync_perm is disabled to avoid triggering a whole bunch of code and messages - # can't be sure that we don't have one case when it's needed - permission_create(app_instance_name+".main", sync_perm=False) + # Initialize the main permission for the app + # After the install, if apps don't have a domain and path defined, the default url '/' is removed from the permission + permission_create(app_instance_name+".main", urls=["/"]) # Execute the app install script install_retcode = 1 @@ -949,17 +946,16 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu os.system('chown -R root: %s' % app_setting_path) os.system('chown -R admin: %s/scripts' % app_setting_path) - # Add path in permission if it's defined in the app install script + # If an app doesn't have at least a domain and a path, assume it's not a webapp and remove the default "/" permission app_settings = _get_app_settings(app_instance_name) domain = app_settings.get('domain', None) path = app_settings.get('path', None) - if domain and path: - # FIXME : might want to move this to before running the install script because some app need to run install script during initialization etc (idk) ? - permission_urls(app_instance_name+".main", add=[domain+path], sync_perm=False) + if not (domain and path): + permission_urls(app_instance_name + ".main", remove=["/"], sync_perm=False) # Migrate classic public app still using the legacy unprotected_uris if app_settings.get("unprotected_uris", None) == "/": - user_permission_update(app_instance_name+".main", remove="all_users", add="visitors", sync_perm=False) + user_permission_update(app_instance_name + ".main", remove="all_users", add="visitors", sync_perm=False) permission_sync_to_user() diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index a99dfb7c1..dd5b3c274 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -108,7 +108,7 @@ class MyMigration(Migration): path = app_setting(app, 'path') domain = app_setting(app, 'domain') - urls = [domain + path] if domain and path else None + urls = "/" if domain and path else None permission_create(app+".main", urls=urls, sync_perm=False) if permission: allowed_group = permission.split(',') diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index dbfc6e6f5..5f9a88e11 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -268,7 +268,18 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - urls -- list of urls to specify for the permission + urls -- list of urls to specify for the permission. + + Urls are assumed to be relative to the app domain/path if they start with '/'. + For example: + / -> domain.tld/app + /admin -> domain.tld/app/admin + domain.tld/app/api -> domain.tld/app/api + + Urls can be later treated as regexes when they start with "re:". + For example: + re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ + re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ """ from yunohost.utils.ldap import _get_ldap_interface @@ -302,7 +313,7 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org'] if urls: - attr_dict['URL'] = [_normalize_url(url) for url in urls] + attr_dict['URL'] = urls operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() @@ -326,8 +337,8 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - add -- List of urls to add - remove -- List of urls to remove + add -- List of urls to add (c.f. permission_create for documentation about their format) + remove -- List of urls to remove (c.f. permission_create for documentation about their format) """ from yunohost.utils.ldap import _get_ldap_interface @@ -345,11 +356,9 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe if add: urls_to_add = [add] if not isinstance(add, list) else add - urls_to_add = [_normalize_url(url) for url in urls_to_add] new_urls += urls_to_add if remove: urls_to_remove = [remove] if not isinstance(remove, list) else remove - urls_to_remove = [_normalize_url(url) for url in urls_to_remove] new_urls = [u for u in new_urls if u not in urls_to_remove] if set(new_urls) == set(existing_permission["urls"]): @@ -457,11 +466,3 @@ def permission_sync_to_user(): # Reload unscd, otherwise the group ain't propagated to the LDAP database os.system('nscd --invalidate=passwd') os.system('nscd --invalidate=group') - - -def _normalize_url(url): - from yunohost.domain import _normalize_domain_path - domain = url[:url.index('/')] - path = url[url.index('/'):] - domain, path = _normalize_domain_path(domain, path) - return domain + path From 2b51d247fb5a1a66c152c13a6ad83e97fdaceee3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 20:14:14 +0200 Subject: [PATCH 163/299] Propagate changes on app helpers + tests --- data/helpers.d/setting | 22 +++++++++--- src/yunohost/tests/test_backuprestore.py | 12 +++---- src/yunohost/tests/test_permission.py | 45 ++++++++++++------------ 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index d083ed563..9deba2dc0 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -232,11 +232,24 @@ ynh_webpath_register () { # Create a new permission for the app # +# example: ynh_permission_create --permission admin --urls /admin +# # usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]] # | arg: permission - the name for the permission (by default a permission named "main" already exist) # | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) +# | arg: urls - (optional) a list of urls to specify for the permission. +# +# Urls are assumed to be relative to the app domain/path if they start with '/'. +# For example: +# / -> domain.tld/app +# /admin -> domain.tld/app/admin +# domain.tld/app/api -> domain.tld/app/api +# +# Urls can be treated as regexes when they start with "re:". +# For example: +# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ +# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ # -# example: ynh_permission_create --permission admin --urls domain.tld/blog/admin ynh_permission_create() { declare -Ar args_array=( [p]=permission= [u]=urls= ) local permission @@ -251,10 +264,11 @@ ynh_permission_create() { # Remove a permission for the app (note that when the app is removed all permission is automatically removed) # +# example: ynh_permission_delete --permission editors +# # usage: ynh_permission_delete --permission "permission" # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) # -# example: ynh_permission_delete --permission editors ynh_permission_delete() { declare -Ar args_array=( [p]=permission= ) local permission @@ -267,8 +281,8 @@ ynh_permission_delete() { # # usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...] # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) -# | arg: add - (optional) a list of FULL urls to add to the permission (e.g. domain.tld/apps/admin) -# | arg: remove - (optional) a list of FULL urls to remove from the permission (e.g. other.tld/apps/admin) +# | arg: add - (optional) a list of urls to add to the permission (see permission_create for details regarding their format) +# | arg: remove - (optional) a list of urls to remove from the permission (see permission_create for details regarding their format) # ynh_permission_urls() { declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index bdaf25299..cab98089b 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -529,9 +529,9 @@ def test_backup_and_restore_permission_app(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] - assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] - assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + assert res['permissions_app.main']['urls'] == ["/"] + assert res['permissions_app.admin']['urls'] == ["/admin"] + assert res['permissions_app.dev']['urls'] == ["/dev"] assert res['permissions_app.main']['allowed'] == ["all_users"] assert res['permissions_app.admin']['allowed'] == ["alice"] @@ -543,9 +543,9 @@ def test_backup_and_restore_permission_app(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] - assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] - assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + assert res['permissions_app.main']['urls'] == ["/"] + assert res['permissions_app.admin']['urls'] == ["/admin"] + assert res['permissions_app.dev']['urls'] == ["/dev"] assert res['permissions_app.main']['allowed'] == ["all_users"] assert res['permissions_app.admin']['allowed'] == ["alice"] diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index bef042be1..a8b6b8258 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -31,7 +31,7 @@ def setup_function(function): user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password) user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password) - permission_create("wiki.main", urls=[maindomain + "/wiki"], sync_perm=False) + permission_create("wiki.main", urls=["/"], sync_perm=False) permission_create("blog.main", sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") @@ -198,7 +198,7 @@ def test_permission_list(): assert res['blog.main']['allowed'] == ["alice"] assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"]) assert res['blog.main']['corresponding_users'] == ["alice"] - assert res['wiki.main']['urls'] == [maindomain + "/wiki"] + assert res['wiki.main']['urls'] == ["/"] # # Create - Remove functions @@ -322,37 +322,37 @@ def test_permission_update_permission_that_doesnt_exist(): # Permission url management def test_permission_add_url(): - permission_urls("blog.main", add=[maindomain + "/testA"]) + permission_urls("blog.main", add=["/testA"]) res = user_permission_list(full=True)['permissions'] - assert res["blog.main"]["urls"] == [maindomain + "/testA"] + assert res["blog.main"]["urls"] == ["/testA"] -def test_permission_add_second_url(): - permission_urls("wiki.main", add=[maindomain + "/testA"]) +def test_permission_add_another_url(): + permission_urls("wiki.main", add=["/testA"]) res = user_permission_list(full=True)['permissions'] - assert set(res["wiki.main"]["urls"]) == set([maindomain + "/testA", maindomain + "/wiki"]) + assert set(res["wiki.main"]["urls"]) == set(["/", "/testA"]) def test_permission_remove_url(): - permission_urls("wiki.main", remove=[maindomain + "/wiki"]) + permission_urls("wiki.main", remove=["/"]) res = user_permission_list(full=True)['permissions'] assert res["wiki.main"]["urls"] == [] def test_permission_add_url_already_added(): res = user_permission_list(full=True)['permissions'] - assert res["wiki.main"]["urls"] == [maindomain + "/wiki"] + assert res["wiki.main"]["urls"] == ["/"] - permission_urls("wiki.main", add=[maindomain + "/wiki"]) + permission_urls("wiki.main", add=["/"]) res = user_permission_list(full=True)['permissions'] - assert res["wiki.main"]["urls"] == [maindomain + "/wiki"] + assert res["wiki.main"]["urls"] == ["/"] def test_permission_remove_url_not_added(): - permission_urls("wiki.main", remove=[maindomain + "/doesnt_exist"]) + permission_urls("wiki.main", remove=["/doesnt_exist"]) res = user_permission_list(full=True)['permissions'] - assert res['wiki.main']['urls'] == [maindomain + "/wiki"] + assert res['wiki.main']['urls'] == ["/"] # # Application interaction @@ -366,9 +366,9 @@ def test_permission_app_install(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] - assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] - assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + assert res['permissions_app.main']['urls'] == ["/"] + assert res['permissions_app.admin']['urls'] == ["/admin"] + assert res['permissions_app.dev']['urls'] == ["/dev"] assert res['permissions_app.main']['allowed'] == ["all_users"] assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"]) @@ -399,17 +399,18 @@ def test_permission_app_change_url(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) + # FIXME : should rework this test to look for differences in the generated app map / app tiles ... res = user_permission_list(full=True)['permissions'] - assert res['permissions_app.main']['urls'] == [maindomain + "/urlpermissionapp"] - assert res['permissions_app.admin']['urls'] == [maindomain + "/urlpermissionapp/admin"] - assert res['permissions_app.dev']['urls'] == [maindomain + "/urlpermissionapp/dev"] + assert res['permissions_app.main']['urls'] == ["/"] + assert res['permissions_app.admin']['urls'] == ["/admin"] + assert res['permissions_app.dev']['urls'] == ["/dev"] app_change_url("permissions_app", maindomain, "/newchangeurl") res = user_permission_list(full=True)['permissions'] - assert res['permissions_app.main']['urls'] == [maindomain + "/newchangeurl"] - assert res['permissions_app.admin']['urls'] == [maindomain + "/newchangeurl/admin"] - assert res['permissions_app.dev']['urls'] == [maindomain + "/newchangeurl/dev"] + assert res['permissions_app.main']['urls'] == ["/"] + assert res['permissions_app.admin']['urls'] == ["/admin"] + assert res['permissions_app.dev']['urls'] == ["/dev"] def test_permission_app_propagation_on_ssowat(): From 7102c5d0ca1ce36a3ff90e3fcb688d4a5be0d221 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 20:14:44 +0200 Subject: [PATCH 164/299] Propagate the new relative url stuff to app_ssowatconf and actuall implement the whole permission system thing in app_map (related to ssowatconf) --- src/yunohost/app.py | 126 ++++++++++++++++++++++---- src/yunohost/tests/test_permission.py | 3 + 2 files changed, 113 insertions(+), 16 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 231568439..51030021f 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -427,25 +427,97 @@ def app_map(app=None, raw=False, user=None): if 'path' not in app_settings: # we assume that an app that doesn't have a path doesn't have an HTTP api continue + # This 'no_sso' settings sound redundant to not having $path defined .... + # At least from what I can see, all apps using it don't have a path defined ... if 'no_sso' in app_settings: # I don't think we need to check for the value here continue + # Users must at least have access to the main permission to have access to extra permissions if user: main_perm = permissions[app_id + ".main"] if user not in main_perm["corresponding_users"] and "visitors" not in main_perm["allowed"]: continue domain = app_settings['domain'] - path = app_settings['path'] + path = app_settings['path'].rstrip('/') + label = app_settings['label'] - if raw: - if domain not in result: - result[domain] = {} - result[domain][path] = { - 'label': app_settings['label'], - 'id': app_settings['id'] - } - else: - result[domain + path] = app_settings['label'] + def _sanitized_absolute_url(perm_url): + # Nominal case : url is relative to the app's path + if perm_url.startswith("/"): + perm_domain = domain + perm_path = path + perm_url.rstrip("/") + # Otherwise, the urls starts with a domain name, like domain.tld/foo/bar + # We want perm_domain = domain.tld and perm_path = "/foo/bar" + else: + perm_domain, perm_path = perm_url.split("/", 1) + perm_path = "/" + perm_path.rstrip("/") + + return perm_domain, perm_path + + this_app_perms = {p: i for p, i in permissions.items() if p.startswith(app_id + ".") and i["urls"]} + for perm_name, perm_info in this_app_perms.items(): + # If we're building the map for a specific user, check the user + # actually is allowed for this specific perm + if user and user not in perm_info["corresponding_users"] and "visitors" not in perm_info["allowed"]: + continue + if len(perm_info["urls"]) > 1 or perm_info["urls"][0].startswith("re:"): + # + # Here we have a big conceptual issue about the sso ... + # Let me take a sip of coffee and turn off the music... + # + # Let's say we have an app foo which created a permission + # 'foo.admin' and added as url "/admin" and "/api" This + # permission got defined somehow as only accessible for group + # "admins". So both "/admin" and "/api" are protected. Good! + # + # Now if we really want users in group "admins" to access those + # uris, then each users in group "admins" need to have these + # urls in the ssowat dict for this user. Which corresponds to a + # tile. To put it otherwise : in the current code of ssowat, a + # permission = a tile = a url ! + # + # We also have an issue if the url define is a regex, because + # the url we want to add to the dict is going to be turned into + # a clickable link (or analyzed by other parts of yunohost + # code...). To put it otherwise : in the current code of ssowat, + # you can't give access a user to a regex + # + # Instead, as drafted by Josue, we could rework the ssowat logic + # about how routes and their permissions are defined. So for example, + # have a dict of + # { "/route1": ["visitors", "user1", "user2", ...], # Public route + # "/route2_with_a_regex$": ["user1", "user2"], # Private route + # "/route3": None, # Skipped route idk + # } + # then each time a user try to request and url, we only keep the + # longest matching rule and check the user is allowed etc... + # + # The challenge with this is (beside actually implementing it) + # is that it creates a whole new mechanism that ultimately + # replace all the existing logic about + # protected/unprotected/skipped uris and regexes and we gotta + # handle / migrate all the legacy stuff somehow if we don't + # want to end up with a total mess in the future idk + logger.error("Permission %s can't be added to the SSOwat configuration because it uses multiple urls and/or uses a regex url" % perm_name) + continue + + perm_domain, perm_path = _sanitized_absolute_url(perm_info["urls"][0]) + + if perm_name.endswith(".main"): + perm_label = label + else: + # e.g. if perm_name is wordpress.admin, we want "Blog (Admin)" (where Blog is the label of this app) + perm_label = "%s (%s)" % (label, perm_name.rsplit(".")[-1].replace("_", " ").title()) + + if raw: + if domain not in result: + result[perm_domain] = {} + result[perm_domain][perm_path] = { + 'label': perm_label, + 'id': app_id + } + else: + result[perm_domain + perm_path] = perm_label return result @@ -1382,13 +1454,34 @@ def app_ssowatconf(): app_settings = read_yaml(APPS_SETTING_PATH + app['id'] + '/settings.yml') + if 'domain' not in app_settings: + continue + if 'path' not in app_settings: + continue + + # This 'no_sso' settings sound redundant to not having $path defined .... + # At least from what I can see, all apps using it don't have a path defined ... if 'no_sso' in app_settings: continue - app_root_webpath = app_settings['domain'] + app_settings['path'].rstrip('/') + domain = app_settings['domain'] + path = app_settings['path'].rstrip('/') + + def _sanitized_absolute_url(perm_url): + # Nominal case : url is relative to the app's path + if perm_url.startswith("/"): + perm_domain = domain + perm_path = path + perm_url.rstrip("/") + # Otherwise, the urls starts with a domain name, like domain.tld/foo/bar + # We want perm_domain = domain.tld and perm_path = "/foo/bar" + else: + perm_domain, perm_path = perm_url.split("/", 1) + perm_path = "/" + perm_path.rstrip("/") + + return perm_domain + perm_path # Skipped - skipped_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'skipped_uris')] + skipped_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'skipped_uris')] skipped_regex += _get_setting(app_settings, 'skipped_regex') # Redirected @@ -1396,15 +1489,16 @@ def app_ssowatconf(): redirected_regex.update(app_settings.get('redirected_regex', {})) # Legacy permission system using (un)protected_uris and _regex managed in app settings... - unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'unprotected_uris')] - unprotected_urls += [app_root_webpath + uri.rstrip("/") for uri in _get_setting(app_settings, 'protected_uris')] + unprotected_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'unprotected_uris')] + protected_urls += [_sanitized_absolute_url(uri) for uri in _get_setting(app_settings, 'protected_uris')] unprotected_regex += _get_setting(app_settings, 'unprotected_regex') - unprotected_regex += _get_setting(app_settings, 'protected_regex') + protected_regex += _get_setting(app_settings, 'protected_regex') # New permission system this_app_perms = {name: info for name, info in all_permissions.items() if name.startswith(app['id'] + ".")} for perm_name, perm_info in this_app_perms.items(): - urls = [url.rstrip("/") for url in perm_info["urls"]] + # FIXME : gotta handle regex-urls here... meh + urls = [_sanitized_absolute_url(url) for url in perm_info["urls"]] if "visitors" in perm_info["allowed"]: unprotected_urls += urls diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index a8b6b8258..4dc021496 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -434,6 +434,9 @@ def test_permission_app_propagation_on_ssowat(): # Test admin access, as configured during install, only alice should be able to access it + # alice gotta be allowed on the main permission to access the admin tho + user_permission_update("permissions_app.main", remove="bob", add="all_users") + assert not can_access_webpage(app_webroot+"/admin", logged_as=None) assert can_access_webpage(app_webroot+"/admin", logged_as="alice") assert not can_access_webpage(app_webroot+"/admin", logged_as="bob") From 545f697df2378d138ce7af73ef985df170f445e1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 20 Sep 2019 21:56:44 +0200 Subject: [PATCH 165/299] When using the legacy adduser function, remove all_users for backward compatibility --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index ab290cb4d..ae6d27b20 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1039,7 +1039,7 @@ def app_addaccess(apps, users=[]): output = {} for app in apps: - permission = user_permission_update(app+".main", add=users) + permission = user_permission_update(app+".main", add=users, remove="all_users") output[app] = permission["corresponding_users"] return {'allowed_users': output} From e2e960175b5a9839cb1c30587a16f7c1d4a44dd2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 21 Sep 2019 13:16:28 +0200 Subject: [PATCH 166/299] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update descriptions for action map Co-Authored-By: Allan Nordhøy --- data/actionsmap/yunohost.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e51f23a14..97489a841 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -270,11 +270,11 @@ user: ### user_group_info() info: - action_help: Get information for a specific group + action_help: Get information about a specific group api: GET /users/groups/ arguments: groupname: - help: Name of the group to get info about + help: Name of the group to fetch info about extra: pattern: *pattern_username @@ -289,11 +289,11 @@ user: arguments: -s: full: --short - help: List only the names of permissions + help: Only list permission names action: store_true -f: full: --full - help: Display all informations known about each permissions, including the full list of users corresponding to allowed groups. + help: Display all info known about each permission, including the full user list of each group it is granted to. action: store_true @@ -306,14 +306,14 @@ user: help: Permission to manage (e.g. mail or nextcloud or wordpress.editors) -a: full: --add - help: Group or user names to add to this permission + help: Group or usernames to grant this permission to nargs: "*" metavar: GROUP_OR_USER extra: pattern: *pattern_username -r: full: --remove - help: Group or user names to remove from this permission + help: Group or usernames revoke this permission from nargs: "*" metavar: GROUP_OR_USER extra: From 2a5053b66bfc7bd48cd8f7668db1ee93b1103fe7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 21 Sep 2019 13:32:40 +0200 Subject: [PATCH 167/299] Misc wording and orthotypography... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Allan Nordhøy --- data/helpers.d/setting | 8 ++++---- locales/en.json | 2 +- src/yunohost/permission.py | 12 ++++++------ src/yunohost/tests/test_permission.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index 9deba2dc0..b2a647993 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -177,7 +177,7 @@ else: if key in ['redirected_urls', 'redirected_regex']: value = yaml.load(value) if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: - logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage public/private access.") + logger.warning("/!\\ Packagers! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers 'ynh_permission_{create,urls,update,delete}' and the 'visitors' group to manage public/private access.") settings[key] = value else: raise ValueError("action should either be get, set or delete") @@ -237,15 +237,15 @@ ynh_webpath_register () { # usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]] # | arg: permission - the name for the permission (by default a permission named "main" already exist) # | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) -# | arg: urls - (optional) a list of urls to specify for the permission. +# | arg: urls - (optional) a list of URLs to specify for the permission. # -# Urls are assumed to be relative to the app domain/path if they start with '/'. +# URLs are assumed to be relative to the app domain/path if they start with '/'. # For example: # / -> domain.tld/app # /admin -> domain.tld/app/admin # domain.tld/app/api -> domain.tld/app/api # -# Urls can be treated as regexes when they start with "re:". +# URLs can be treated as regexes when they start with "re:". # For example: # re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ # re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ diff --git a/locales/en.json b/locales/en.json index 5df21b684..70b9fa626 100644 --- a/locales/en.json +++ b/locales/en.json @@ -230,7 +230,7 @@ "group_already_exist_on_system": "Group {group} already exists in the system group", "group_created": "Group '{group}' successfully created", "group_creation_failed": "Failed to create group {group}: {error}", - "group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in Yunohost", + "group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in YunoHost", "group_cannot_edit_visitors": "The group 'visitors' cannot be edited manually. It is a special group representing anonymous visitors", "group_cannot_edit_primary_group": "The group '{group}' cannot be edited manually. It is the primary group meant to contain only one specific user.", "group_cannot_be_edited": "The group {group} cannot be edited manually.", diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 5f9a88e11..75e3f6037 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -146,11 +146,11 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if "all_users" in new_allowed_groups: # FIXME : i18n # FIXME : write a better explanation ? - logger.warning("This permission is currently enabled for all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups currently allowed.") + logger.warning("This permission is currently granted to all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups it is currently granted to.") if "visitors" in new_allowed_groups: # FIXME : i18n # FIXME : write a better explanation ? - logger.warning("This permission is currently enabled for visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups currently allowed.") + logger.warning("This permission is currently granted to visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups it is currently granted to.") # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): @@ -268,7 +268,7 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - urls -- list of urls to specify for the permission. + urls -- list of URLs to specify for the permission. Urls are assumed to be relative to the app domain/path if they start with '/'. For example: @@ -276,7 +276,7 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): /admin -> domain.tld/app/admin domain.tld/app/api -> domain.tld/app/api - Urls can be later treated as regexes when they start with "re:". + URLs can be later treated as regexes when they start with "re:". For example: re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ @@ -337,8 +337,8 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - add -- List of urls to add (c.f. permission_create for documentation about their format) - remove -- List of urls to remove (c.f. permission_create for documentation about their format) + add -- List of URLs to add (c.f. permission_create for documentation about their format) + remove -- List of URLs to remove (c.f. permission_create for documentation about their format) """ from yunohost.utils.ldap import _get_ldap_interface diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 4dc021496..f17313fa1 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -180,7 +180,7 @@ def can_access_webpage(webpath, logged_as=None): assert session.cookies r = session.get(webpath, verify=False) - # If we can't access it, we got redirected to the sso + # If we can't access it, we got redirected to the SSO return not r.url.startswith(sso_url) # From adc92388ab9b73bc9fab2436632745d469e88923 Mon Sep 17 00:00:00 2001 From: nr 458 h Date: Sat, 21 Sep 2019 18:39:59 +0000 Subject: [PATCH 168/299] Translated using Weblate (German) Currently translated at 24.4% (143 of 586 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/locales/de.json b/locales/de.json index cef591411..5a3fa183e 100644 --- a/locales/de.json +++ b/locales/de.json @@ -2,22 +2,22 @@ "action_invalid": "Ungültige Aktion '{action:s}'", "admin_password": "Administrator-Passwort", "admin_password_change_failed": "Passwort kann nicht geändert werden", - "admin_password_changed": "Das Administrator-Kennwort wurde erfolgreich geändert", + "admin_password_changed": "Das Administrator-Kennwort wurde geändert", "app_already_installed": "{app:s} ist schon installiert", "app_argument_choice_invalid": "Ungültige Auswahl für Argument '{name:s}'. Es muss einer der folgenden Werte sein {choices:s}", "app_argument_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}", "app_argument_required": "Argument '{name:s}' wird benötigt", "app_extraction_failed": "Installationsdateien konnten nicht entpackt werden", "app_id_invalid": "Falsche App-ID", - "app_install_files_invalid": "Ungültige Installationsdateien", + "app_install_files_invalid": "Diese Dateien können nicht installiert werden", "app_location_already_used": "Eine andere App ({app}) ist bereits an diesem Ort ({path}) installiert", "app_location_install_failed": "Die App kann nicht an diesem Ort installiert werden, da es mit der App {other_app} die bereits in diesem Pfad ({other_path}) installiert ist Probleme geben würde", "app_manifest_invalid": "Ungültiges App-Manifest: {error}", "app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar", - "app_not_installed": "{app:s} ist nicht installiert", + "app_not_installed": "Die App {app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}", "app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette", - "app_removed": "{app:s} wurde erfolgreich entfernt", - "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden", + "app_removed": "{app:s} wurde entfernt", + "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?", "app_unknown": "Unbekannte App", "app_upgrade_failed": "{app:s} konnte nicht aktualisiert werden", "app_upgraded": "{app:s} wurde erfolgreich aktualisiert", @@ -42,7 +42,7 @@ "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", "backup_cleaning_failed": "Temporäres Sicherungsverzeichnis konnte nicht geleert werden", "backup_created": "Datensicherung komplett", - "backup_creating_archive": "Datensicherung wird erstellt...", + "backup_creating_archive": "Datensicherung wird erstellt…", "backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden", "backup_deleted": "Datensicherung wurde entfernt", "backup_extracting_archive": "Entpacke Sicherungsarchiv...", @@ -53,7 +53,7 @@ "backup_output_directory_not_empty": "Ausgabeordner ist nicht leer", "backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden", "backup_running_app_script": "Datensicherung für App '{app:s}' wurd durchgeführt...", - "backup_running_hooks": "Datensicherunghook wird ausgeführt...", + "backup_running_hooks": "Datensicherunghook wird ausgeführt…", "custom_app_url_required": "Es muss eine URL angegeben werden, um deine benutzerdefinierte App {app:s} zu aktualisieren", "custom_appslist_name_required": "Du musst einen Namen für deine benutzerdefinierte Appliste angeben", "dnsmasq_isnt_installed": "dnsmasq scheint nicht installiert zu sein. Bitte führe 'apt-get remove bind9 && apt-get install dnsmasq' aus", @@ -71,7 +71,7 @@ "domain_zone_exists": "DNS Zonen Datei existiert bereits", "domain_zone_not_found": "DNS Zonen Datei kann nicht für Domäne {:s} gefunden werden", "done": "Erledigt", - "downloading": "Wird heruntergeladen...", + "downloading": "Wird heruntergeladen…", "dyndns_cron_installed": "DynDNS Cronjob erfolgreich angelegt", "dyndns_cron_remove_failed": "Der DynDNS Cronjob konnte nicht entfernt werden", "dyndns_cron_removed": "Der DynDNS Cronjob wurde gelöscht", @@ -81,9 +81,9 @@ "dyndns_registered": "Deine DynDNS Domain wurde registriert", "dyndns_registration_failed": "DynDNS Domain konnte nicht registriert werden: {error:s}", "dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar", - "executing_command": "Führe den Behfehl '{command:s}' aus...", - "executing_script": "Skript '{script:s}' wird ausgeührt...", - "extracting": "Wird entpackt...", + "executing_command": "Führe den Behfehl '{command:s}' aus…", + "executing_script": "Skript '{script:s}' wird ausgeührt…", + "extracting": "Wird entpackt…", "field_invalid": "Feld '{:s}' ist unbekannt", "firewall_reload_failed": "Die Firewall konnte nicht neu geladen werden", "firewall_reloaded": "Die Firewall wurde neu geladen", @@ -156,7 +156,7 @@ "restore_hook_unavailable": "Das Wiederherstellungsskript für '{part:s}' steht weder in deinem System noch im Archiv zur Verfügung", "restore_nothings_done": "Es wurde nicht wiederhergestellt", "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", - "restore_running_hooks": "Wiederherstellung wird gestartet...", + "restore_running_hooks": "Wiederherstellung wird gestartet…", "service_add_configuration": "Füge Konfigurationsdatei {file:s} hinzu", "service_add_failed": "Der Dienst '{service:s}' kann nicht hinzugefügt werden", "service_added": "Der Service '{service:s}' wurde erfolgreich hinzugefügt", @@ -189,9 +189,9 @@ "unlimit": "Kein Kontingent", "unrestore_app": "App '{app:s}' kann nicht Wiederhergestellt werden", "update_cache_failed": "Konnte APT cache nicht aktualisieren", - "updating_apt_cache": "Die Liste der verfügbaren Pakete wird aktualisiert...", + "updating_apt_cache": "Die Liste der verfügbaren Pakete wird aktualisiert…", "upgrade_complete": "Upgrade vollständig", - "upgrading_packages": "Pakete werden aktualisiert...", + "upgrading_packages": "Pakete werden aktualisiert…", "upnp_dev_not_found": "Es konnten keine UPnP Geräte gefunden werden", "upnp_disabled": "UPnP wurde deaktiviert", "upnp_enabled": "UPnP wurde aktiviert", @@ -208,7 +208,7 @@ "yunohost_already_installed": "YunoHost ist bereits installiert", "yunohost_ca_creation_failed": "Zertifikatsstelle konnte nicht erstellt werden", "yunohost_configured": "YunoHost wurde konfiguriert", - "yunohost_installing": "YunoHost wird installiert...", + "yunohost_installing": "YunoHost wird installiert…", "yunohost_not_installed": "YunoHost ist nicht oder unvollständig installiert worden. Bitte 'yunohost tools postinstall' ausführen", "app_not_properly_removed": "{app:s} wurde nicht ordnungsgemäß entfernt", "service_regenconf_failed": "Konnte die Konfiguration für folgende Dienste nicht neu erzeugen: {services}", @@ -221,7 +221,7 @@ "package_unexpected_error": "Ein unerwarteter Fehler trat bei der Verarbeitung des Pakets '{pkgname}' auf", "app_incompatible": "Die Anwendung {app} ist nicht mit deiner YunoHost-Version kompatibel", "app_not_correctly_installed": "{app:s} scheint nicht korrekt installiert zu sein", - "app_requirements_checking": "Überprüfe notwendige Pakete für {app}...", + "app_requirements_checking": "Überprüfe notwendige Pakete für {app}…", "app_requirements_failed": "Anforderungen für {app} werden nicht erfüllt: {error}", "app_requirements_unmeet": "Anforderungen für {app} werden nicht erfüllt, das Paket {pkgname} ({version}) muss {spec} sein", "app_unsupported_remote_type": "Für die App wurde ein nicht unterstützer Steuerungstyp verwendet", @@ -281,7 +281,7 @@ "domain_hostname_failed": "Erstellen des neuen Hostnamens fehlgeschlagen", "appslist_name_already_tracked": "Es gibt bereits eine registrierte App-Liste mit Namen {name:s}.", "appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit dem URL {url:s}.", - "appslist_migrating": "Migriere Anwendungsliste {appslist:s} ...", + "appslist_migrating": "Migriere Anwendungsliste {appslist:s} …", "appslist_could_not_migrate": "Konnte Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.", "appslist_corrupted_json": "Konnte die Anwendungslisten. Es scheint, dass {filename:s} beschädigt ist.", "yunohost_ca_creation_success": "Die lokale Zertifizierungs-Authorität wurde angelegt.", @@ -292,15 +292,15 @@ "app_already_up_to_date": "{app:s} ist schon aktuell", "backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt", "backup_applying_method_tar": "Erstellen des Backup-tar Archives...", - "backup_applying_method_copy": "Kopiere alle Dateien ins Backup...", - "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung der Anwendung.", + "backup_applying_method_copy": "Kopiere alle Dateien ins Backup…", + "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung.", "app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}", - "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf...", + "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_mount_failed": "Das Einbinden des Backup-Archives ist fehlgeschlagen", "backup_archive_writing_error": "Die Dateien konnten nicht in der komprimierte Archiv-Backup hinzugefügt werden", - "app_change_url_success": "Erfolgreiche Änderung der URL von {app:s} zu {domain:s}{path:s}", - "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...", + "app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}", + "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository…", "invalid_url_format": "ungültiges URL Format", "global_settings_bad_type_for_setting": "Falscher Typ für Einstellung {setting: s}. Empfangen: {receive_type: s}, aber erwartet: {expected_type: s}", "global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting: s}. Habe '{choice: s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices: s}", @@ -343,7 +343,7 @@ "apps_permission_restoration_failed": "Die Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} ist fehlgeschlagen", "apps_permission_not_found": "Keine Berechtigung für die installierten Apps gefunden", "app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden", - "app_upgrade_app_name": "App {App} wird jetzt aktualisiert…", + "app_upgrade_app_name": "{App} wird jetzt aktualisiert…", "app_upgrade_several_apps": "Die folgenden Apps werden aktualisiert: {apps}", "app_start_restore": "Anwendung {app} wird wiederhergestellt…", "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen…", @@ -353,6 +353,6 @@ "app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der anderen App \"{other_app}\" verwendet", "aborting": "Breche ab.", "app_action_cannot_be_ran_because_required_services_down": "Diese App erfordert einige Dienste, die derzeit nicht verfügbar sind. Bevor Sie fortfahren, sollten Sie versuchen, die folgenden Dienste neu zu starten (und möglicherweise untersuchen, warum sie nicht verfügbar sind): {services}", - "already_up_to_date": "Nichts zu tun! Alles ist bereits auf dem neusten Stand!", - "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen." + "already_up_to_date": "Nichts zu tun. Alles ist bereits auf dem neusten Stand.", + "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen" } From 4bc59cf8cbde0f09f26390adfcd179b94a0ba1d5 Mon Sep 17 00:00:00 2001 From: Aksel Kiesling Date: Sun, 22 Sep 2019 05:34:02 +0000 Subject: [PATCH 169/299] Translated using Weblate (German) Currently translated at 24.4% (143 of 586 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/locales/de.json b/locales/de.json index 5a3fa183e..10087e12f 100644 --- a/locales/de.json +++ b/locales/de.json @@ -4,16 +4,16 @@ "admin_password_change_failed": "Passwort kann nicht geändert werden", "admin_password_changed": "Das Administrator-Kennwort wurde geändert", "app_already_installed": "{app:s} ist schon installiert", - "app_argument_choice_invalid": "Ungültige Auswahl für Argument '{name:s}'. Es muss einer der folgenden Werte sein {choices:s}", - "app_argument_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}", + "app_argument_choice_invalid": "Verwende einen der folgenden Werte {choices:s}", + "app_argument_invalid": "Wähle einen gültigen Wert für das Argument '{name: s}': {error: s}", "app_argument_required": "Argument '{name:s}' wird benötigt", "app_extraction_failed": "Installationsdateien konnten nicht entpackt werden", "app_id_invalid": "Falsche App-ID", "app_install_files_invalid": "Diese Dateien können nicht installiert werden", - "app_location_already_used": "Eine andere App ({app}) ist bereits an diesem Ort ({path}) installiert", - "app_location_install_failed": "Die App kann nicht an diesem Ort installiert werden, da es mit der App {other_app} die bereits in diesem Pfad ({other_path}) installiert ist Probleme geben würde", - "app_manifest_invalid": "Ungültiges App-Manifest: {error}", - "app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar", + "app_location_already_used": "Die App ({app}) ist bereits hier ({path}) installiert", + "app_location_install_failed": "Die App kann dort nicht installiert werden, da ein Konflikt mit der App '{other_app}' besteht, die bereits in '{other_path}' installiert ist", + "app_manifest_invalid": "Mit dem App-Manifest stimmt etwas nicht: {error}", + "app_no_upgrade": "Alle Apps sind bereits aktuell", "app_not_installed": "Die App {app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}", "app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette", "app_removed": "{app:s} wurde entfernt", @@ -294,7 +294,7 @@ "backup_applying_method_tar": "Erstellen des Backup-tar Archives...", "backup_applying_method_copy": "Kopiere alle Dateien ins Backup…", "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung.", - "app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}", + "app_location_unavailable": "Diese URL ist entweder nicht verfügbar oder steht in Konflikt mit den bereits installierten Apps:\n{apps: s}", "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_mount_failed": "Das Einbinden des Backup-Archives ist fehlgeschlagen", @@ -349,7 +349,7 @@ "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen…", "app_start_remove": "Anwendung {app} wird entfernt…", "app_start_install": "Anwendung {app} wird installiert…", - "app_not_upgraded": "Die folgenden Apps wurden nicht aktualisiert: {apps}", + "app_not_upgraded": "Die App '{failed_app}' konnte nicht aktualisiert werden. Infolgedessen wurden die folgenden App-Upgrades abgebrochen: {apps}", "app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der anderen App \"{other_app}\" verwendet", "aborting": "Breche ab.", "app_action_cannot_be_ran_because_required_services_down": "Diese App erfordert einige Dienste, die derzeit nicht verfügbar sind. Bevor Sie fortfahren, sollten Sie versuchen, die folgenden Dienste neu zu starten (und möglicherweise untersuchen, warum sie nicht verfügbar sind): {services}", From 0da83270e23d5796302244f53dd2486a04745902 Mon Sep 17 00:00:00 2001 From: advocatux Date: Fri, 20 Sep 2019 12:35:42 +0000 Subject: [PATCH 170/299] Translated using Weblate (Spanish) Currently translated at 100.0% (586 of 586 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 658 ++++++++++++++++++++++++------------------------ 1 file changed, 330 insertions(+), 328 deletions(-) diff --git a/locales/es.json b/locales/es.json index d028a7a8e..d219b36e1 100644 --- a/locales/es.json +++ b/locales/es.json @@ -2,36 +2,36 @@ "action_invalid": "Acción no válida '{action:s} 1'", "admin_password": "Contraseña administrativa", "admin_password_change_failed": "No se puede cambiar la contraseña", - "admin_password_changed": "La contraseña administrativa ha sido cambiada", - "app_already_installed": "{app:s} 2 ya está instalada", - "app_argument_choice_invalid": "Opción no válida para el argumento '{name:s} 3', deber una de {choices:s} 4", - "app_argument_invalid": "Valor no válido para el argumento '{name:s} 5': {error:s} 6", + "admin_password_changed": "La contraseña de administración ha sido cambiada", + "app_already_installed": "{app:s} ya está instalada", + "app_argument_choice_invalid": "Use una de estas opciones «{choices:s}» para el argumento «{name:s}»", + "app_argument_invalid": "Elija un valor válido para el argumento «{name:s}»: {error:s}", "app_argument_required": "Se requiere el argumento '{name:s} 7'", "app_extraction_failed": "No se pudieron extraer los archivos de instalación", - "app_id_invalid": "Id de la aplicación no válida", + "app_id_invalid": "ID de la aplicación no válida", "app_incompatible": "La aplicación {app} no es compatible con su versión de YunoHost", - "app_install_files_invalid": "Los archivos de instalación no son válidos", - "app_location_already_used": "La aplicación {app} ya está instalada en esta localización ({path})", - "app_location_install_failed": "No se puede instalar la aplicación en esta localización porque entra en conflicto con la aplicación '{other_app}' ya instalada en '{other_path}'", - "app_manifest_invalid": "El manifiesto de la aplicación no es válido: {error}", - "app_no_upgrade": "No hay aplicaciones para actualizar", + "app_install_files_invalid": "Estos archivos no se pueden instalar", + "app_location_already_used": "La aplicación «{app}» ya está instalada en ({path})", + "app_location_install_failed": "No se puede instalar la aplicación ahí porque entra en conflicto con la aplicación «{other_app}» ya instalada en «{other_path}»", + "app_manifest_invalid": "Algo va mal con el manifiesto de la aplicación: {error}", + "app_no_upgrade": "Todas las aplicaciones están ya actualizadas", "app_not_correctly_installed": "La aplicación {app:s} 8 parece estar incorrectamente instalada", - "app_not_installed": "La aplicación «{app:s}» no está instalada. Esta es la lista de todas las aplicaciones instaladas: {all_apps}", + "app_not_installed": "No se pudo encontrar la aplicación «{app:s}» en la lista de aplicaciones instaladas: {all_apps}", "app_not_properly_removed": "La {app:s} 0 no ha sido desinstalada correctamente", "app_package_need_update": "El paquete de la aplicación {app} necesita ser actualizada debido a los cambios en YunoHost", "app_recent_version_required": "{:s} requiere una versión más reciente de moulinette ", - "app_removed": "{app:s} ha sido eliminada", + "app_removed": "Eliminado {app:s}", "app_requirements_checking": "Comprobando los paquetes necesarios para {app}…", - "app_requirements_failed": "No se cumplen los requisitos para {app}: {error}", + "app_requirements_failed": "No se cumplen algunos requisitos para {app}: {error}", "app_requirements_unmeet": "No se cumplen los requisitos para {app}, el paquete {pkgname} ({version}) debe ser {spec}", - "app_sources_fetch_failed": "No se pudo obtener los archivos con el código fuente, ¿es la URL correcta?", + "app_sources_fetch_failed": "No se pudieron obtener los archivos con el código fuente, ¿es el URL correcto?", "app_unknown": "Aplicación desconocida", "app_unsupported_remote_type": "Tipo remoto no soportado por la aplicación", - "app_upgrade_failed": "No se pudo actualizar la aplicación {app:s}", - "app_upgraded": "{app:s} ha sido actualizada", - "appslist_fetched": "La lista de aplicaciones {appslist:s} ha sido descargada", - "appslist_removed": "La lista de aplicaciones {appslist:s} ha sido eliminada", - "appslist_retrieve_error": "No se pudo recuperar la lista remota de aplicaciones {appslist:s} : {error:s}", + "app_upgrade_failed": "No se pudo actualizar {app:s}", + "app_upgraded": "Actualizado {app:s}", + "appslist_fetched": "Obtenida lista de aplicaciones {appslist:s} actualizada", + "appslist_removed": "Eliminada la lista de aplicaciones {appslist:s}", + "appslist_retrieve_error": "No se puede recuperar la lista remota de aplicaciones {appslist:s}: {error:s}", "appslist_unknown": "Lista de aplicaciones {appslist:s} desconocida.", "ask_current_admin_password": "Contraseña administrativa actual", "ask_email": "Dirección de correo electrónico", @@ -42,43 +42,43 @@ "ask_new_admin_password": "Nueva contraseña administrativa", "ask_password": "Contraseña", "backup_action_required": "Debe especificar algo que guardar", - "backup_app_failed": "No es posible realizar la copia de seguridad de la aplicación '{app:s}'", - "backup_archive_app_not_found": "La aplicación '{app:s}' no ha sido encontrada en la copia de seguridad", + "backup_app_failed": "No se pudo respaldar la aplicación «{app:s}»", + "backup_archive_app_not_found": "No se pudo encontrar la aplicación «{app:s}» en el archivo de respaldo", "backup_archive_hook_not_exec": "El hook {hook:s} no ha sido ejecutado en esta copia de seguridad", - "backup_archive_name_exists": "Ya existe una copia de seguridad con ese nombre", + "backup_archive_name_exists": "Ya existe un archivo de respaldo con este nombre.", "backup_archive_name_unknown": "Copia de seguridad local desconocida '{name:s}'", - "backup_archive_open_failed": "No se pudo abrir la copia de seguridad", - "backup_cleaning_failed": "No se puede limpiar el directorio temporal de copias de seguridad", + "backup_archive_open_failed": "No se pudo abrir el archivo de respaldo", + "backup_cleaning_failed": "No se pudo limpiar la carpeta de respaldo temporal", "backup_created": "Se ha creado la copia de seguridad", "backup_creating_archive": "Creando el archivo de copia de seguridad…", - "backup_creation_failed": "No se pudo crear la copia de seguridad", - "backup_delete_error": "No se puede eliminar '{path:s}'", - "backup_deleted": "La copia de seguridad ha sido eliminada", - "backup_extracting_archive": "Extrayendo el archivo de la copia de seguridad…", - "backup_hook_unknown": "Hook de copia de seguridad desconocido '{hook:s}'", - "backup_invalid_archive": "La copia de seguridad no es válida", - "backup_nothings_done": "No hay nada que guardar", - "backup_output_directory_forbidden": "Directorio de salida no permitido. Las copias de seguridad no pueden ser creadas en /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var o en los subdirectorios de /home/yunohost.backup/archives", - "backup_output_directory_not_empty": "El directorio de salida no está vacío", + "backup_creation_failed": "No se pudo crear el archivo de respaldo", + "backup_delete_error": "No se pudo eliminar «{path:s}»", + "backup_deleted": "Eliminada la copia de seguridad", + "backup_extracting_archive": "Extrayendo el archivo de respaldo…", + "backup_hook_unknown": "El gancho «{hook:s}» de la copia de seguridad es desconocido", + "backup_invalid_archive": "Esto no es un archivo de respaldo", + "backup_nothings_done": "Nada que guardar", + "backup_output_directory_forbidden": "Elija un directorio de salida diferente. No se pueden crear copias de seguridad en las subcarpetas de /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var o /home/yunohost.backup/archives", + "backup_output_directory_not_empty": "Debe elegir un directorio de salida vacío", "backup_output_directory_required": "Debe proporcionar un directorio de salida para la copia de seguridad", "backup_running_app_script": "Ejecutando la script de copia de seguridad de la aplicación '{app:s}'...", "backup_running_hooks": "Ejecutando los hooks de copia de seguridad...", "custom_app_url_required": "Debe proporcionar una URL para actualizar su aplicación personalizada {app:s}", "custom_appslist_name_required": "Debe proporcionar un nombre para su lista de aplicaciones personalizadas", - "diagnosis_debian_version_error": "No se puede obtener la versión de Debian: {error}", - "diagnosis_kernel_version_error": "No se puede obtener la versión del kernel: {error}", - "diagnosis_monitor_disk_error": "No se pueden monitorizar los discos: {error}", - "diagnosis_monitor_network_error": "No se puede monitorizar la red: {error}", - "diagnosis_monitor_system_error": "No se puede monitorizar el sistema: {error}", + "diagnosis_debian_version_error": "No se pudo obtener la versión de Debian: {error}", + "diagnosis_kernel_version_error": "No se pudo obtener la versión del núcleo: {error}", + "diagnosis_monitor_disk_error": "No se pudieron monitorizar los discos: {error}", + "diagnosis_monitor_network_error": "No se pudo monitorizar la red: {error}", + "diagnosis_monitor_system_error": "No se pudo monitorizar el sistema: {error}", "diagnosis_no_apps": "Aplicación no instalada", - "dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, ejecute 'apt-get remove bind9 && apt-get install dnsmasq'", - "domain_cert_gen_failed": "No se pudo crear el certificado", - "domain_created": "El dominio ha sido creado", + "dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, ejecute «apt-get remove bind9 && apt-get install it»", + "domain_cert_gen_failed": "No se pudo generar el certificado", + "domain_created": "Dominio creado", "domain_creation_failed": "No se pudo crear el dominio", - "domain_deleted": "El dominio ha sido eliminado", - "domain_deletion_failed": "No se pudo borrar el dominio", - "domain_dyndns_already_subscribed": "Ya está suscrito a un dominio DynDNS", - "domain_dyndns_invalid": "Dominio no válido para usar con DynDNS", + "domain_deleted": "Dominio eliminado", + "domain_deletion_failed": "No se pudo eliminar el dominio", + "domain_dyndns_already_subscribed": "Ya se ha suscrito a un dominio de DynDNS", + "domain_dyndns_invalid": "Este dominio no se puede usar con DynDNS", "domain_dyndns_root_unknown": "Dominio raíz de DynDNS desconocido", "domain_exists": "El dominio ya existe", "domain_uninstall_app_first": "Una o más aplicaciones están instaladas en este dominio. Debe desinstalarlas antes de eliminar el dominio", @@ -87,63 +87,63 @@ "domain_zone_not_found": "No se ha encontrado el archivo de zona del DNS para el dominio [:s]", "done": "Hecho.", "downloading": "Descargando…", - "dyndns_cron_installed": "La tarea cron para DynDNS ha sido instalada", - "dyndns_cron_remove_failed": "No se pudo eliminar la tarea cron de DynDNS por: {error}", - "dyndns_cron_removed": "La tarea cron DynDNS ha sido eliminada", - "dyndns_ip_update_failed": "No se pudo actualizar la dirección IP en el DynDNS", - "dyndns_ip_updated": "Su dirección IP ha sido actualizada en el DynDNS", - "dyndns_key_generating": "Generando la clave del DNS. Esto podría tardar un rato…", + "dyndns_cron_installed": "Creado el trabajo de cron de DynDNS", + "dyndns_cron_remove_failed": "No se pudo eliminar el trabajo de cron de DynDNS por: {error}", + "dyndns_cron_removed": "Eliminado el trabajo de cron de DynDNS", + "dyndns_ip_update_failed": "No se pudo actualizar la dirección IP en DynDNS", + "dyndns_ip_updated": "Actualizada su IP en DynDNS", + "dyndns_key_generating": "Generando la clave del DNS. Esto podría tardar un rato.", "dyndns_key_not_found": "No se ha encontrado la clave DNS para el dominio", - "dyndns_no_domain_registered": "Ningún dominio ha sido registrado con DynDNS", - "dyndns_registered": "El dominio DynDNS ha sido registrado", - "dyndns_registration_failed": "No se pudo registrar el dominio DynDNS: {error:s}", - "dyndns_unavailable": "El dominio {domain:s} no está disponible.", + "dyndns_no_domain_registered": "Ningún dominio registrado con DynDNS", + "dyndns_registered": "Registrado dominio de DynDNS", + "dyndns_registration_failed": "No se pudo registrar el dominio de DynDNS: {error:s}", + "dyndns_unavailable": "El dominio «{domain:s}» no está disponible.", "executing_command": "Ejecutando la orden «{command:s}»…", "executing_script": "Ejecutando el guión «{script:s}»…", "extracting": "Extrayendo…", "field_invalid": "Campo no válido '{:s}'", "firewall_reload_failed": "No se pudo recargar el cortafuegos", - "firewall_reloaded": "El cortafuegos ha sido recargado", - "firewall_rules_cmd_failed": "No se pudieron aplicar algunas reglas del cortafuegos. Para más información consulte el registro.", + "firewall_reloaded": "Cortafuegos recargado", + "firewall_rules_cmd_failed": "Algunas órdenes de las reglas del cortafuegos han fallado. Más información en el registro.", "format_datetime_short": "%d/%m/%Y %I:%M %p", "hook_argument_missing": "Falta un parámetro '{:s}'", "hook_choice_invalid": "Selección inválida '{:s}'", - "hook_exec_failed": "No se puede ejecutar el script: {path:s}", - "hook_exec_not_terminated": "La ejecución del script no ha terminado: {path:s}", - "hook_list_by_invalid": "Enumerar los hooks por validez", + "hook_exec_failed": "No se pudo ejecutar el guión: {path:s}", + "hook_exec_not_terminated": "El guión no terminó correctamente:{path:s}", + "hook_list_by_invalid": "Esta propiedad no se puede usar para enumerar ganchos («hooks»)", "hook_name_unknown": "Nombre de hook desconocido '{name:s}'", "installation_complete": "Instalación finalizada", - "installation_failed": "No se pudo realizar la instalación", + "installation_failed": "Algo ha ido mal con la instalación", "ip6tables_unavailable": "No puede modificar ip6tables aquí. O bien está en un 'container' o su kernel no soporta esta opción", "iptables_unavailable": "No puede modificar iptables aquí. O bien está en un 'container' o su kernel no soporta esta opción", - "ldap_initialized": "Se ha inicializado LDAP", + "ldap_initialized": "Inicializado LDAP", "license_undefined": "indefinido", - "mail_alias_remove_failed": "No se pudo eliminar el alias de correo '{mail:s}'", - "mail_domain_unknown": "El dominio de correo '{domain:s}' es desconocido", - "mail_forward_remove_failed": "No se pudo eliminar el reenvío de correo '{mail:s}'", + "mail_alias_remove_failed": "No se pudo eliminar el alias de correo «{mail:s}»", + "mail_domain_unknown": "Dirección de correo desconocida para el dominio «{domain:s}»", + "mail_forward_remove_failed": "No se pudo eliminar el reenvío de correo «{mail:s}»", "maindomain_change_failed": "No se pudo cambiar el dominio principal", - "maindomain_changed": "Se ha cambiado el dominio principal", - "monitor_disabled": "La monitorización del sistema ha sido deshabilitada", - "monitor_enabled": "La monitorización del sistema ha sido habilitada", - "monitor_glances_con_failed": "No se pudo conectar al servidor Glances", - "monitor_not_enabled": "La monitorización del sistema no está habilitada", + "maindomain_changed": "El dominio principal ha cambiado", + "monitor_disabled": "Desactivada la monitorización del servidor", + "monitor_enabled": "Activada la monitorización del servidor", + "monitor_glances_con_failed": "No se pudo conectar al servidor de Glances", + "monitor_not_enabled": "La monitorización del servidor está apagada", "monitor_period_invalid": "Período de tiempo no válido", "monitor_stats_file_not_found": "No se pudo encontrar el archivo de estadísticas", "monitor_stats_no_update": "No hay estadísticas de monitorización para actualizar", "monitor_stats_period_unavailable": "No hay estadísticas para el período", "mountpoint_unknown": "Punto de montaje desconocido", - "mysql_db_creation_failed": "No se pudo crear la base de datos MySQL", - "mysql_db_init_failed": "No se pudo iniciar la base de datos MySQL", - "mysql_db_initialized": "La base de datos MySQL ha sido inicializada", + "mysql_db_creation_failed": "Error al crear la base de datos de MySQL", + "mysql_db_init_failed": "Error al iniciar la base de datos de MySQL", + "mysql_db_initialized": "Inicializada la base de datos MySQL", "network_check_mx_ko": "El registro DNS MX no está configurado", - "network_check_smtp_ko": "El puerto 25 (SMTP) para el correo saliente parece estar bloqueado por su red", - "network_check_smtp_ok": "El puerto de salida del correo electrónico (25, SMTP) no está bloqueado", + "network_check_smtp_ko": "El correo saliente (SMTP puerto 25) parece estar bloqueado por su red", + "network_check_smtp_ok": "El correo saliente (SMTP puerto 25) no está bloqueado", "new_domain_required": "Debe proporcionar el nuevo dominio principal", "no_appslist_found": "No se ha encontrado ninguna lista de aplicaciones", "no_internet_connection": "El servidor no está conectado a Internet", "no_ipv6_connectivity": "La conexión por IPv6 no está disponible", "no_restore_script": "No se ha encontrado un script de restauración para la aplicación '{app:s}'", - "not_enough_disk_space": "No hay suficiente espacio en '{path:s}'", + "not_enough_disk_space": "No hay espacio libre suficiente en «{path:s}»", "package_not_installed": "El paquete '{pkgname}' no está instalado", "package_unexpected_error": "Ha ocurrido un error inesperado procesando el paquete '{pkgname}'", "package_unknown": "Paquete desconocido '{pkgname}'", @@ -151,13 +151,13 @@ "packages_upgrade_critical_later": "Los paquetes críticos ({packages:s}) serán actualizados más tarde", "packages_upgrade_failed": "No se pudieron actualizar todos los paquetes", "path_removal_failed": "No se pudo eliminar la ruta {:s}", - "pattern_backup_archive_name": "Debe ser un nombre de archivo válido con un máximo de 30 caracteres, solo se admiten caracteres alfanuméricos, los guiones -_ y el punto", + "pattern_backup_archive_name": "Debe ser un nombre de archivo válido con un máximo de 30 caracteres, solo se admiten caracteres alfanuméricos y los caracteres -_. (guiones y punto)", "pattern_domain": "El nombre de dominio debe ser válido (por ejemplo mi-dominio.org)", "pattern_email": "Debe ser una dirección de correo electrónico válida (por ejemplo, alguien@dominio.org)", "pattern_firstname": "Debe ser un nombre válido", "pattern_lastname": "Debe ser un apellido válido", "pattern_listname": "Solo se pueden usar caracteres alfanuméricos y el guion bajo", - "pattern_mailbox_quota": "El tamaño de cuota debe tener uno de los sufijos b/k/M/G/T. Usar 0 para cuota ilimitada", + "pattern_mailbox_quota": "Debe ser un tamaño con el sufijo «b/k/M/G/T» o «0» para no tener una cuota", "pattern_password": "Debe contener al menos 3 caracteres", "pattern_port": "Debe ser un número de puerto válido (es decir, entre 0-65535)", "pattern_port_or_range": "Debe ser un número de puerto válido (es decir entre 0-65535) o un intervalo de puertos (por ejemplo 100:200)", @@ -167,22 +167,22 @@ "port_already_opened": "El puerto {port:d} ya está abierto para las conexiones {ip_version:s}", "port_available": "El puerto {port:d} está disponible", "port_unavailable": "El puerto {port:d} no está disponible", - "restore_action_required": "Debe especificar algo que restaurar", - "restore_already_installed_app": "Una aplicación con la id '{app:s}' ya está instalada", - "restore_app_failed": "No se puede restaurar la aplicación '{app:s}'", - "restore_cleaning_failed": "No se puede limpiar el directorio temporal de restauración", - "restore_complete": "Restauración finalizada", + "restore_action_required": "Debe escoger algo que restaurar", + "restore_already_installed_app": "Una aplicación con el ID «{app:s}» ya está instalada", + "restore_app_failed": "No se pudo restaurar la aplicación «{app:s}»", + "restore_cleaning_failed": "No se pudo limpiar el directorio temporal de restauración", + "restore_complete": "Restaurada", "restore_confirm_yunohost_installed": "¿Realmente desea restaurar un sistema ya instalado? [{answers:s}]", "restore_failed": "No se pudo restaurar el sistema", - "restore_hook_unavailable": "El script de restauración '{part:s}' no está disponible en su sistema y tampoco en el archivo", + "restore_hook_unavailable": "El guión de restauración para «{part:s}» no está disponible en su sistema y tampoco en el archivo", "restore_nothings_done": "No se ha restaurado nada", - "restore_running_app_script": "Ejecutando el guión de restauración de la aplicación «{app:s}»…", + "restore_running_app_script": "Restaurando la aplicación «{app:s}»…", "restore_running_hooks": "Ejecutando los ganchos de restauración…", - "service_add_failed": "No se pudo añadir el servicio '{service:s}'", - "service_added": "Servicio '{service:s}' ha sido añadido", - "service_already_started": "El servicio '{service:s}' ya ha sido inicializado", - "service_already_stopped": "El servicio '{service:s}' ya ha sido detenido", - "service_cmd_exec_failed": "No se pudo ejecutar el comando '{command:s}'", + "service_add_failed": "No se pudo añadir el servicio «{service:s}»", + "service_added": "Añadido el servicio «{service:s}»", + "service_already_started": "El servicio «{service:s}» ya ha sido iniciado", + "service_already_stopped": "El servicio «{service:s}» ya ha sido detenido", + "service_cmd_exec_failed": "No se pudo ejecutar la orden «{command:s}»", "service_conf_file_backed_up": "Se ha realizado una copia de seguridad del archivo de configuración '{conf}' en '{backup}'", "service_conf_file_copy_failed": "No se puede copiar el nuevo archivo de configuración '{new}' a {conf}", "service_conf_file_manually_modified": "El archivo de configuración '{conf}' ha sido modificado manualmente y no será actualizado", @@ -194,28 +194,28 @@ "service_conf_up_to_date": "La configuración del servicio '{service}' ya está actualizada", "service_conf_updated": "La configuración ha sido actualizada para el servicio '{service}'", "service_conf_would_be_updated": "La configuración podría haber sido actualizada para el servicio '{service} 1'", - "service_disable_failed": "No se pudo deshabilitar el servicio '{service:s}'", - "service_disabled": "El servicio '{service:s}' ha sido deshabilitado", - "service_enable_failed": "No se pudo habilitar el servicio '{service:s}'", - "service_enabled": "El servicio '{service:s}' ha sido habilitado", + "service_disable_failed": "No se pudo desactivar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_disabled": "Desactivado el servicio «{service:s}»", + "service_enable_failed": "No se pudo activar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_enabled": "Activado el servicio «{service:s}»", "service_no_log": "No hay ningún registro para el servicio '{service:s}'", "service_regenconf_dry_pending_applying": "Comprobando configuración pendiente que podría haber sido aplicada al servicio '{service}'...", "service_regenconf_failed": "No se puede regenerar la configuración para el servicio(s): {services}", "service_regenconf_pending_applying": "Aplicando la configuración pendiente para el servicio '{service}'...", - "service_remove_failed": "No se pudo desinstalar el servicio '{service:s}'", - "service_removed": "El servicio '{service:s}' ha sido desinstalado", - "service_start_failed": "No se pudo iniciar el servicio '{service:s}'\n\nRegistros de servicio recientes : {logs:s}", - "service_started": "El servicio '{service:s}' ha sido iniciado", - "service_status_failed": "No se pudo determinar el estado del servicio '{service:s}'", - "service_stop_failed": "No se pudo detener el servicio '{service:s}'", - "service_stopped": "El servicio '{service:s}' ha sido detenido", + "service_remove_failed": "No se pudo eliminar el servicio «{service:s}»", + "service_removed": "Eliminado el servicio «{service:s}»", + "service_start_failed": "No se pudo iniciar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_started": "Iniciado el servicio «{service:s}»", + "service_status_failed": "No se pudo determinar el estado del servicio «{service:s}»", + "service_stop_failed": "No se pudo detener el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_stopped": "Detenido el servicio «{service:s}»", "service_unknown": "Servicio desconocido '{service:s}'", - "ssowat_conf_generated": "Se ha generado la configuración de SSOwat", - "ssowat_conf_updated": "La configuración de SSOwat ha sido actualizada", - "system_upgraded": "El sistema ha sido actualizado", - "system_username_exists": "El nombre de usuario ya existe en el sistema", + "ssowat_conf_generated": "Generada la configuración de SSOwat", + "ssowat_conf_updated": "Actualizada la configuración de SSOwat", + "system_upgraded": "Sistema actualizado", + "system_username_exists": "El nombre de usuario ya existe en la lista de usuarios del sistema", "unbackup_app": "La aplicación '{app:s}' no se guardará", - "unexpected_error": "Ha ocurrido un error inesperado", + "unexpected_error": "Algo inesperado salió mal: {error}", "unit_unknown": "Unidad desconocida '{unit:s}'", "unlimit": "Sin cuota", "unrestore_app": "La aplicación '{app:s}' no será restaurada", @@ -224,278 +224,278 @@ "upgrade_complete": "Actualización finalizada", "upgrading_packages": "Actualizando paquetes…", "upnp_dev_not_found": "No se encontró ningún dispositivo UPnP", - "upnp_disabled": "UPnP ha sido deshabilitado", - "upnp_enabled": "UPnP ha sido habilitado", - "upnp_port_open_failed": "No se pudieron abrir puertos por UPnP", - "user_created": "El usuario ha sido creado", + "upnp_disabled": "UPnP desactivado", + "upnp_enabled": "UPnP activado", + "upnp_port_open_failed": "No se pudo abrir el puerto vía UPnP", + "user_created": "Usuario creado", "user_creation_failed": "No se pudo crear el usuario", - "user_deleted": "El usuario ha sido eliminado", + "user_deleted": "Usuario eliminado", "user_deletion_failed": "No se pudo eliminar el usuario", - "user_home_creation_failed": "No se puede crear el directorio de usuario 'home'", - "user_info_failed": "No se pudo extraer la información del usuario", + "user_home_creation_failed": "No se pudo crear la carpeta «home» para el usuario", + "user_info_failed": "No se pudo obtener la información del usuario", "user_unknown": "Usuario desconocido: {user:s}", - "user_update_failed": "No se pudo actualizar el usuario", - "user_updated": "El usuario ha sido actualizado", + "user_update_failed": "No se pudo cambiar la información del usuario", + "user_updated": "Cambiada la información de usuario", "yunohost_already_installed": "YunoHost ya está instalado", - "yunohost_ca_creation_failed": "No se pudo crear el certificado de autoridad", - "yunohost_configured": "YunoHost ha sido configurado", + "yunohost_ca_creation_failed": "No se pudo crear la autoridad de certificación", + "yunohost_configured": "YunoHost está configurado", "yunohost_installing": "Instalando YunoHost…", - "yunohost_not_installed": "YunoHost no está instalado o ha habido errores en la instalación. Ejecute 'yunohost tools postinstall'", - "ldap_init_failed_to_create_admin": "La inicialización de LDAP falló al crear el usuario administrador", - "mailbox_used_space_dovecot_down": "El servicio de e-mail Dovecot debe estar funcionando si desea obtener el espacio utilizado por el buzón de correo", - "ssowat_persistent_conf_read_error": "Error al leer la configuración persistente de SSOwat: {error:s}. Edite el archivo /etc/ssowat/conf.json.persistent para corregir la sintaxis de JSON", - "ssowat_persistent_conf_write_error": "Error al guardar la configuración persistente de SSOwat: {error:s}. Edite el archivo /etc/ssowat/conf.json.persistent para corregir la sintaxis de JSON", + "yunohost_not_installed": "YunoHost no está correctamente instalado. Ejecute «yunohost tools postinstall»", + "ldap_init_failed_to_create_admin": "La inicialización de LDAP no pudo crear el usuario «admin»", + "mailbox_used_space_dovecot_down": "El servicio de correo Dovecot debe estar funcionando si desea obtener el espacio usado por el buzón de correo", + "ssowat_persistent_conf_read_error": "No se pudo leer la configuración persistente de SSOwat: {error:s}. Edite el archivo /etc/ssowat/conf.json.persistent para corregir la sintaxis de JSON", + "ssowat_persistent_conf_write_error": "No se pudo guardar la configuración persistente de SSOwat: {error:s}. Edite el archivo /etc/ssowat/conf.json.persistent para corregir la sintaxis de JSON", "certmanager_attempt_to_replace_valid_cert": "Está intentando sobrescribir un certificado correcto y válido para el dominio {domain:s}! (Use --force para omitir este mensaje)", - "certmanager_domain_unknown": "Dominio desconocido {domain:s}", - "certmanager_domain_cert_not_selfsigned": "El certificado para el dominio {domain:s} no es un certificado autofirmado. ¿Está seguro de que quiere reemplazarlo? (Use --force para omitir este mensaje)", - "certmanager_certificate_fetching_or_enabling_failed": "Suena como que habilitar el nuevo certificado para {domain:s} fallara de algún modo…", - "certmanager_attempt_to_renew_nonLE_cert": "El certificado para el dominio {domain:s} no ha sido emitido por Let's Encrypt. ¡No se puede renovar automáticamente!", - "certmanager_attempt_to_renew_valid_cert": "¡El certificado para el dominio {domain:s} no está a punto de expirar! (Puede usar --force si sabe lo que está haciendo)", - "certmanager_domain_http_not_working": "Parece que no se puede acceder al dominio {domain:s} a través de HTTP. Compruebe que la configuración del DNS y de nginx es correcta", - "certmanager_error_no_A_record": "No se ha encontrado un registro DNS 'A' para el dominio {domain:s}. Debe hacer que su nombre de dominio apunte a su máquina para poder instalar un certificado Let's Encrypt. (Si sabe lo que está haciendo, use --no-checks para desactivar esas comprobaciones.)", - "certmanager_domain_dns_ip_differs_from_public_ip": "El registro DNS 'A' para el dominio {domain:s} es diferente de la IP de este servidor. Si recientemente modificó su registro A, espere a que se propague (existen algunos controladores de propagación DNS disponibles en línea). (Si sabe lo que está haciendo, use --no-checks para desactivar esas comprobaciones.)", + "certmanager_domain_unknown": "Dominio desconocido «{domain:s}»", + "certmanager_domain_cert_not_selfsigned": "El certificado para el dominio {domain:s} no es un certificado autofirmado. ¿Está seguro de que quiere reemplazarlo? (Use «--force» para hacerlo)", + "certmanager_certificate_fetching_or_enabling_failed": "El intento de usar el nuevo certificado para {domain:s} no ha funcionado…", + "certmanager_attempt_to_renew_nonLE_cert": "El certificado para el dominio «{domain:s}» no ha sido emitido por Let's Encrypt. ¡No se puede renovar automáticamente!", + "certmanager_attempt_to_renew_valid_cert": "¡El certificado para el dominio «{domain:s}» no está a punto de expirar! (Puede usar --force si sabe lo que está haciendo)", + "certmanager_domain_http_not_working": "Parece que no se puede acceder al dominio {domain:s} a través de HTTP. Compruebe que la configuración del DNS y de NGINX es correcta", + "certmanager_error_no_A_record": "No se ha encontrado un registro DNS «A» para el dominio {domain:s}. Debe hacer que su nombre de dominio apunte a su máquina para poder instalar un certificado de Let's Encrypt. (Si sabe lo que está haciendo, use «--no-checks» para desactivar esas comprobaciones.)", + "certmanager_domain_dns_ip_differs_from_public_ip": "El registro «A» del DNS para el dominio «{domain:s}» es diferente de la IP de este servidor. Si recientemente modificó su registro A, espere a que se propague (existen algunos verificadores de propagación de DNS disponibles en línea). (Si sabe lo que está haciendo, use «--no-checks» para desactivar esas comprobaciones.)", "certmanager_cannot_read_cert": "Se ha producido un error al intentar abrir el certificado actual para el dominio {domain:s} (archivo: {file:s}), razón: {reason:s}", - "certmanager_cert_install_success_selfsigned": "¡Se ha instalado correctamente un certificado autofirmado para el dominio {domain:s}!", - "certmanager_cert_install_success": "¡Se ha instalado correctamente un certificado Let's Encrypt para el dominio {domain:s}!", - "certmanager_cert_renew_success": "¡Se ha renovado correctamente el certificado Let's Encrypt para el dominio {domain:s}!", + "certmanager_cert_install_success_selfsigned": "Instalado correctamente un certificado autofirmado para el dominio «{domain:s}»", + "certmanager_cert_install_success": "Instalado correctamente un certificado de Let's Encrypt para el dominio «{domain:s}»", + "certmanager_cert_renew_success": "Renovado correctamente el certificado de Let's Encrypt para el dominio «{domain:s}»", "certmanager_old_letsencrypt_app_detected": "\nYunohost ha detectado que la aplicación 'letsencrypt' está instalada, esto produce conflictos con las nuevas funciones de administración de certificados integradas en Yunohost. Si desea utilizar las nuevas funciones integradas, ejecute los siguientes comandos para migrar su instalación:\n\n Yunohost app remove letsencrypt\n Yunohost domain cert-install\n\nP.D.: esto intentará reinstalar los certificados para todos los dominios con un certificado Let's Encrypt o con un certificado autofirmado", - "certmanager_hit_rate_limit": "Se han emitido demasiados certificados recientemente para el conjunto de dominios {domain:s}. Por favor, inténtelo de nuevo más tarde. Consulte https://letsencrypt.org/docs/rate-limits/ para obtener más detalles", + "certmanager_hit_rate_limit": "Se han emitido demasiados certificados recientemente para este conjunto exacto de dominios {domain:s}. Pruebe de nuevo más tarde. Vea para más detalles https://letsencrypt.org/docs/rate-limits/", "certmanager_cert_signing_failed": "No se pudo firmar el nuevo certificado", - "certmanager_no_cert_file": "No se puede leer el certificado para el dominio {domain:s} (archivo: {file:s})", - "certmanager_conflicting_nginx_file": "No se puede preparar el dominio para el desafío ACME: el archivo de configuración nginx {filepath:s} está en conflicto y debe ser eliminado primero", - "domain_cannot_remove_main": "No se puede eliminar el dominio principal. Primero debe establecer un nuevo dominio principal", - "certmanager_self_ca_conf_file_not_found": "No se ha encontrado el archivo de configuración para la autoridad de autofirma (file: {file:s})", - "certmanager_unable_to_parse_self_CA_name": "No se puede procesar el nombre de la autoridad de autofirma (file: {file:s} 1)", + "certmanager_no_cert_file": "No se pudo leer el certificado para el dominio {domain:s} (archivo: {file:s})", + "certmanager_conflicting_nginx_file": "No se pudo preparar el dominio para el desafío ACME: el archivo de configuración de NGINX {filepath:s} está en conflicto y debe ser eliminado primero", + "domain_cannot_remove_main": "No se puede eliminar el dominio principal. Configure uno primero", + "certmanager_self_ca_conf_file_not_found": "No se pudo encontrar el archivo de configuración para la autoridad de autofirma (archivo: {file:s})", + "certmanager_unable_to_parse_self_CA_name": "No se pudo procesar el nombre de la autoridad de autofirma (archivo: {file:s})", "domains_available": "Dominios disponibles:", - "backup_archive_broken_link": "Imposible acceder a la copia de seguridad (enlace roto {path:s})", - "certmanager_domain_not_resolved_locally": "Su servidor Yunohost no consigue resolver el dominio {domain:s}. Esto puede suceder si ha modificado su registro DNS. Si es el caso, espere unas horas hasta que se propague la modificación. Si el problema persiste, considere añadir {domain:s} a /etc/hosts. (Si sabe lo que está haciendo, use --no-checks para deshabilitar estas verificaciones.)", - "certmanager_acme_not_configured_for_domain": "El certificado para el dominio {domain:s} no parece instalado correctamente. Ejecute primero cert-install para este dominio.", - "certmanager_http_check_timeout": "Plazo expirado, el servidor no ha podido contactarse a si mismo a través de HTTP usando su dirección IP pública (dominio {domain:s} con ip {ip:s}). Puede ser debido a hairpinning o a una mala configuración del cortafuego/router al que está conectado su servidor.", - "certmanager_couldnt_fetch_intermediate_cert": "Plazo expirado, no se ha podido descargar el certificado intermedio de Let's Encrypt. La instalación/renovación del certificado ha sido cancelada - vuelva a intentarlo más tarde.", - "appslist_retrieve_bad_format": "El archivo obtenido para la lista de aplicaciones {appslist:s} no es válido", - "domain_hostname_failed": "Error al establecer un nuevo nombre de host («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).", - "yunohost_ca_creation_success": "Se ha creado la autoridad de certificación local.", - "app_already_installed_cant_change_url": "Esta aplicación ya está instalada. No se puede cambiar el URL únicamente mediante esta función. Compruebe si está disponible la opción 'app changeurl'.", + "backup_archive_broken_link": "No se pudo acceder al archivo de respaldo (enlace roto a {path:s})", + "certmanager_domain_not_resolved_locally": "El dominio {domain:s} no puede ser resuelto desde su servidor de YunoHost. Esto puede suceder si ha modificado su registro DNS recientemente. De ser así, espere unas horas para que se propague. Si el problema continúa, considere añadir {domain:s} a /etc/hosts. (Si sabe lo que está haciendo, use «--no-checks» para desactivar esas comprobaciones.)", + "certmanager_acme_not_configured_for_domain": "El certificado para el dominio «{domain:s}» no parece que esté instalado correctamente. Ejecute primero «cert-install» para este dominio.", + "certmanager_http_check_timeout": "Tiempo de espera agotado cuando el servidor intentaba conectarse consigo mismo a través de HTTP usando una dirección IP pública (dominio «{domain:s}» con IP «{ip:s}»). Puede que esté experimentando un problema de redirección («hairpinning»), o que el cortafuegos o el enrutador de su servidor esté mal configurado.", + "certmanager_couldnt_fetch_intermediate_cert": "Tiempo de espera agotado intentando obtener el certificado intermedio de Let's Encrypt. Cancelada la instalación o renovación del certificado. Vuelva a intentarlo más tarde.", + "appslist_retrieve_bad_format": "No se pudo leer la lista de aplicaciones obtenida {appslist:s}", + "domain_hostname_failed": "No se pudo establecer un nuevo nombre de anfitrión («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).", + "yunohost_ca_creation_success": "Creada la autoridad de certificación local.", + "app_already_installed_cant_change_url": "Esta aplicación ya está instalada. No se puede cambiar el URL únicamente mediante esta función. Compruebe si está disponible la opción `app changeurl`.", "app_change_no_change_url_script": "La aplicacion {app_name:s} aún no permite cambiar su URL, es posible que deba actualizarla.", - "app_change_url_failed_nginx_reload": "No se pudo recargar nginx. Compruebe la salida de 'nginx -t':\n{nginx_errors:s}", + "app_change_url_failed_nginx_reload": "No se pudo recargar NGINX. Esta es la salida de «nginx -t»:\n{nginx_errors:s}", "app_change_url_identical_domains": "El antiguo y nuevo dominio/url_path son idénticos ('{domain:s} {path:s}'), no se realizarán cambios.", - "app_change_url_no_script": "Esta aplicación '{app_name:s}' aún no permite modificar su URL. Quizás debería actualizar la aplicación.", - "app_change_url_success": "El URL de la aplicación {app:s} ha sido cambiado correctamente a {domain:s} {path:s}", - "app_location_unavailable": "Este URL no está disponible o está en conflicto con otra aplicación instalada:\n{apps:s}", + "app_change_url_no_script": "Esta aplicación «{app_name:s}» aún no permite la modificación de URLs. Quizás debería actualizarla.", + "app_change_url_success": "El URL de la aplicación {app:s} es ahora {domain:s} {path:s}", + "app_location_unavailable": "Este URL o no está disponible o está en conflicto con otra(s) aplicación(es) instalada(s):\n{apps:s}", "app_already_up_to_date": "La aplicación {app:s} ya está actualizada", - "appslist_name_already_tracked": "Ya existe una lista de aplicaciones registrada con el nombre {name:s}.", - "appslist_url_already_tracked": "Ya existe una lista de aplicaciones registrada con el URL {url:s}.", + "appslist_name_already_tracked": "Ya existe una lista de aplicaciones registradas con el nombre {name:s}.", + "appslist_url_already_tracked": "Ya existe una lista de aplicaciones registradas con el URL {url:s}.", "appslist_migrating": "Migrando la lista de aplicaciones {appslist:s}…", - "appslist_could_not_migrate": "No se pudo migrar la lista de aplicaciones {appslist:s}! No se pudo analizar el URL ... El antiguo cronjob se ha mantenido en {bkp_file:s}.", + "appslist_could_not_migrate": "¡No se pudo migrar la lista de aplicaciones {appslist:s}! No se pudo analizar el URL… El antiguo trabajo de cron se mantuvo en {bkp_file:s}.", "appslist_corrupted_json": "No se pudieron cargar las listas de aplicaciones. Parece que {filename:s} está dañado.", - "invalid_url_format": "Formato de URL no válido", + "invalid_url_format": "Algo va mal con el URL", "app_upgrade_some_app_failed": "No se pudieron actualizar algunas aplicaciones", - "app_make_default_location_already_used": "No puede hacer la aplicación '{app}' por defecto en el dominio {domain} dado que está siendo usado por otra aplicación '{other_app}'", - "app_upgrade_app_name": "Actualizando la aplicación {app}…", + "app_make_default_location_already_used": "No puede hacer que la aplicación «{app}» sea la predeterminada en el dominio, {domain} ya está siendo usado por otra aplicación «{other_app}»", + "app_upgrade_app_name": "Actualizando ahora {app}…", "ask_path": "Camino", - "backup_abstract_method": "Este método de backup no ha sido implementado aún", + "backup_abstract_method": "Este método de respaldo aún no se ha implementado", "backup_applying_method_borg": "Enviando todos los archivos para la copia de seguridad al repositorio de borg-backup…", "backup_applying_method_copy": "Copiando todos los archivos a la copia de seguridad…", "backup_applying_method_custom": "Llamando al método de copia de seguridad personalizado «{method:s}»…", - "backup_applying_method_tar": "Creando el archivo tar de la copia de seguridad…", - "backup_archive_mount_failed": "Fallo en el montado del archivo de backup", - "backup_archive_system_part_not_available": "La parte del sistema {part:s} no está disponible en este backup", - "backup_archive_writing_error": "No se pueden añadir archivos de backup en el archivo comprimido", - "backup_ask_for_copying_if_needed": "Algunos ficheros no pudieron ser preparados para hacer backup usando el método que evita el gasto de espacio temporal en el sistema. Para hacer el backup, {size:s} MB deberían ser usados temporalmente. ¿Está de acuerdo?", - "backup_borg_not_implemented": "Método de backup Borg no está implementado aún", - "backup_cant_mount_uncompress_archive": "No se puede montar en modo solo lectura el directorio del archivo descomprimido", + "backup_applying_method_tar": "Creando el archivo TAR de respaldo…", + "backup_archive_mount_failed": "No se pudo montar el archivo de respaldo", + "backup_archive_system_part_not_available": "La parte del sistema «{part:s}» no está disponible en esta copia de seguridad", + "backup_archive_writing_error": "No se pudieron añadir los archivos «{source:s}» (llamados en el archivo «{dest:s}») para ser respaldados en el archivo comprimido «{archive:s}»", + "backup_ask_for_copying_if_needed": "No se pudieron preparar algunos archivos para la copia de seguridad usando el método que evita desperdiciar espacio temporalmente en el sistema. Para hacer la copia de seguridad, {size:s}MB se usarán temporalmente. ¿Está de acuerdo?", + "backup_borg_not_implemented": "El método de respaldo de Borg aún no ha sido implementado", + "backup_cant_mount_uncompress_archive": "No se pudo montar el archivo descomprimido como protegido contra escritura", "backup_copying_to_organize_the_archive": "Copiando {size:s}MB para organizar el archivo", - "backup_couldnt_bind": "No puede enlazar {src:s} con {dest:s}.", - "backup_csv_addition_failed": "No puede añadir archivos al backup en el archivo CSV", - "backup_csv_creation_failed": "No se puede crear el archivo CSV necesario para futuras operaciones de restauración", - "backup_custom_mount_error": "Fracaso del método de copia de seguridad personalizada en la etapa \"mount\"", - "backup_custom_need_mount_error": "Fracaso del método de copia de seguridad personalizada en la étapa \"need_mount\"", - "backup_no_uncompress_archive_dir": "El directorio del archivo descomprimido no existe", - "backup_php5_to_php7_migration_may_fail": "No se ha podido convertir su archivo para soportar php7, la restauración de sus aplicaciones php puede fallar (razón : {error:s})", - "backup_system_part_failed": "No se puede hacer una copia de seguridad de la parte \"{part:s}\" del sistema", - "backup_with_no_backup_script_for_app": "La aplicación {app:s} no tiene script de respaldo. Se ha ignorado.", - "backup_with_no_restore_script_for_app": "La aplicación {app:s} no tiene script de restauración, no podrá restaurar automáticamente la copia de seguridad de esta aplicación.", + "backup_couldnt_bind": "No se pudo enlazar {src:s} con {dest:s}.", + "backup_csv_addition_failed": "No se pudo añadir archivos para respaldar en el archivo CSV", + "backup_csv_creation_failed": "No se pudo crear el archivo CSV necesario para la restauración", + "backup_custom_mount_error": "El método de respaldo personalizado no pudo superar el paso «mount»", + "backup_custom_need_mount_error": "El método de respaldo personalizado no pudo superar el paso «need_mount»", + "backup_no_uncompress_archive_dir": "No existe tal directorio de archivos sin comprimir", + "backup_php5_to_php7_migration_may_fail": "No se pudo convertir su archivo para que sea compatible con PHP 7, puede que no pueda restaurar sus aplicaciones de PHP (motivo: {error:s})", + "backup_system_part_failed": "No se pudo respaldar la parte del sistema «{part:s}»", + "backup_with_no_backup_script_for_app": "La aplicación «{app:s}» no tiene un guión de respaldo. Omitiendo.", + "backup_with_no_restore_script_for_app": "La aplicación «{app:s}» no tiene un guión de restauración, no podrá restaurar automáticamente la copia de seguridad de esta aplicación.", "dyndns_could_not_check_provide": "No se pudo verificar si {provider:s} puede ofrecer {domain:s}.", - "dyndns_domain_not_provided": "El proveedor Dyndns {provider:s} no puede proporcionar el dominio {domain:s}.", - "experimental_feature": "Cuidado : esta funcionalidad es experimental y no es considerada estable, no debería usarla excepto si sabe lo que hace.", - "good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más larga (es decir, una frase de paso) y/o usar varias clases de caracteres (mayúsculas, minúsculas, dígitos y caracteres especiales).", - "password_listed": "Esta contraseña es una de las más usadas en el mundo. Elija algo un poco más único.", - "password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud", - "password_too_simple_2": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas y minúsculas", - "password_too_simple_3": "La contraseña debe tener al menos 8 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales", - "password_too_simple_4": "La contraseña debe tener al menos 12 caracteres de longitud y contiene dígitos, mayúsculas, minúsculas y caracteres especiales", + "dyndns_domain_not_provided": "El proveedor de DynDNS {provider:s} no puede proporcionar el dominio {domain:s}.", + "experimental_feature": "Aviso : esta funcionalidad es experimental y no se considera estable, no debería usarla a menos que sepa lo que está haciendo.", + "good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más extensa (básicamente una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).", + "password_listed": "Esta contraseña es una de las más usadas en el mundo. Elija algo más único.", + "password_too_simple_1": "La contraseña tiene que ser de al menos 8 caracteres de longitud", + "password_too_simple_2": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número y caracteres en mayúsculas y minúsculas", + "password_too_simple_3": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales", + "password_too_simple_4": "La contraseña tiene que ser de al menos 12 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales", "users_available": "Usuarios disponibles:", - "user_not_in_group": "Usuario {user:s} no está en el grupo {group:s}", - "user_already_in_group": "Usuario {user:} ya está en el grupo {group:s}", + "user_not_in_group": "El usuario «{user:s}» no está en el grupo «{group:s}»", + "user_already_in_group": "El usuario «{user:}» ya está en el grupo «{group:s}»", "updating_app_lists": "Obteniendo actualizaciones disponibles para las aplicaciones…", - "update_apt_cache_warning": "Ocurrieron algunos errores durante la actualización de la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", - "update_apt_cache_failed": "No se puede actualizar la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", - "tools_upgrade_special_packages_completed": "¡Actualización de paquetes de YunoHost completada!\nPulse [Intro] para recuperar la línea de órdenes", - "tools_upgrade_special_packages_explanation": "Esta acción terminará pero la actualización especial real continuará en segundo plano. No inicie ninguna otra acción en su servidor en aproximadamente 10 minutos (dependiendo de la velocidad de su hardware). Una vez que esté hecho, podría tener que volver a iniciar sesión en la administración web. El registro de actualización estará disponible en Herramientas > Registro (en la administración web) o mediante «yunohost log list» (en la línea de órdenes).", - "tools_upgrade_special_packages": "Actualizando ahora paquetes «especiales» (relacionados con YunoHost)...", - "tools_upgrade_regular_packages_failed": "No se pueden actualizar los paquetes: {packages_list}", - "tools_upgrade_regular_packages": "Actualizando ahora paquetes «normales» (no relacionados con YunoHost)...", - "tools_upgrade_cant_unhold_critical_packages": "No se pueden liberar los paquetes críticos...", - "tools_upgrade_cant_hold_critical_packages": "No se pueden retener los paquetes críticos...", + "update_apt_cache_warning": "Algo fue mal durante la actualización de la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", + "update_apt_cache_failed": "No se pudo actualizar la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", + "tools_upgrade_special_packages_completed": "Actualización de paquetes de YunoHost completada.\nPulse [Intro] para regresar a la línea de órdenes", + "tools_upgrade_special_packages_explanation": "Esta acción terminará pero la actualización especial real continuará en segundo plano. No inicie ninguna otra acción en su servidor en aproximadamente 10 minutos (dependiendo de la velocidad de su hardware). Una vez que esté hecho, podría tener que volver a iniciar sesión en la administración web. El registro de actualización estará disponible en Herramientas → Registro (en la página de administración web) o mediante «yunohost log list» (desde la línea de órdenes).", + "tools_upgrade_special_packages": "Actualizando ahora paquetes «especiales» (relacionados con YunoHost)…", + "tools_upgrade_regular_packages_failed": "No se pudieron actualizar los paquetes: {packages_list}", + "tools_upgrade_regular_packages": "Actualizando ahora paquetes «normales» (no relacionados con YunoHost)…", + "tools_upgrade_cant_unhold_critical_packages": "No se pudieron liberar los paquetes críticos…", + "tools_upgrade_cant_hold_critical_packages": "No se pudieron retener los paquetes críticos…", "tools_upgrade_cant_both": "No se puede actualizar el sistema y las aplicaciones al mismo tiempo", - "tools_upgrade_at_least_one": "Especifique --apps O --system", - "tools_update_failed_to_app_fetchlist": "Error al actualizar la lista de aplicaciones de YunoHost porque: {error}", - "this_action_broke_dpkg": "Esta acción rompió dpkg/apt (los gestores de paquetes del sistema)... Puede tratar de solucionar este problema conectando mediante SSH y ejecutando `sudo dpkg --configure -a`.", + "tools_upgrade_at_least_one": "Especifique «--apps», o «--system»", + "tools_update_failed_to_app_fetchlist": "No se pudo actualizar la lista de aplicaciones de YunoHost porque: {error}", + "this_action_broke_dpkg": "Esta acción rompió dpkg/APT(los gestores de paquetes del sistema)… Puede tratar de solucionar este problema conectando mediante SSH y ejecutando `sudo dpkg --configure -a`.", "system_groupname_exists": "El nombre de grupo ya existe en el grupo del sistema", - "service_reloaded_or_restarted": "El servicio «{service:s}» ha sido recargado o reiniciado", - "service_reload_or_restart_failed": "No se puede recargar o reiniciar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", - "service_restarted": "El servicio «{service:s}» ha sido reiniciado", - "service_restart_failed": "No se puede reiniciar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", - "service_reloaded": "El servicio «{service:s}» ha sido recargado", - "service_reload_failed": "No se puede recargar el servicio «{service:s}»'\n\nRegistro de servicios reciente:{logs:s}", + "service_reloaded_or_restarted": "Recargado o reiniciado el servicio «{service:s}»", + "service_reload_or_restart_failed": "No se pudo recargar o reiniciar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_restarted": "Reiniciado el servicio «{service:s}»", + "service_restart_failed": "No se pudo reiniciar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", + "service_reloaded": "Recargado el servicio «{service:s}»", + "service_reload_failed": "No se pudo recargar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", "service_regen_conf_is_deprecated": "¡«yunohost service regen-conf» está obsoleto! Use «yunohost tools regen-conf» en su lugar.", - "service_description_yunohost-firewall": "gestiona los puertos de conexiones abiertos y cerrados a los servicios", - "service_description_yunohost-api": "gestiona las interacciones entre la interfaz web de YunoHost y el sistema", - "service_description_ssh": "le permite conectar a su servidor remotamente mediante un terminal (protocolo SSH)", - "service_description_slapd": "almacena usuarios, dominios e información relacionada", - "service_description_rspamd": "filtra correo no deseado y otras características relacionadas con el correo", - "service_description_rmilter": "comprueba varios parámetros en el correo", - "service_description_redis-server": "una base de datos especializada usada para el acceso rápido de datos, cola de tareas y comunicación entre programas", - "service_description_postfix": "usado para enviar y recibir correos", - "service_description_php7.0-fpm": "ejecuta aplicaciones escritas en PHP con nginx", - "service_description_nslcd": "maneja la conexión del intérprete («shell») de usuario de YunoHost", - "service_description_nginx": "sirve o proporciona acceso a todos los sitios web alojados en su servidor", - "service_description_mysql": "almacena los datos de las aplicaciones (base de datos SQL)", - "service_description_metronome": "gestionar las cuentas XMPP de mensajería instantánea", - "service_description_glances": "supervisa la información del sistema en su servidor", - "service_description_fail2ban": "protege contra ataques de fuerza bruta y otra clase de ataques desde Internet", - "service_description_dovecot": "permite al cliente de correo acceder/traer correo (vía IMAP y POP3)", - "service_description_dnsmasq": "maneja la resolución de nombres de dominio (DNS)", - "service_description_avahi-daemon": "permite acceder a su servidor usando yunohost.local en su red local", + "service_description_yunohost-firewall": "Gestiona los puertos de conexiones abiertos y cerrados a los servicios", + "service_description_yunohost-api": "Gestiona las interacciones entre la interfaz web de YunoHost y el sistema", + "service_description_ssh": "Permite conectar a su servidor remotamente mediante un terminal (protocolo SSH)", + "service_description_slapd": "Almacena usuarios, dominios e información relacionada", + "service_description_rspamd": "Filtra correo no deseado y otras características relacionadas con el correo", + "service_description_rmilter": "Comprueba varios parámetros en el correo", + "service_description_redis-server": "Una base de datos especializada usada para el acceso rápido de datos, cola de tareas y comunicación entre programas", + "service_description_postfix": "Usado para enviar y recibir correos", + "service_description_php7.0-fpm": "Ejecuta aplicaciones escritas en PHP con NGINX", + "service_description_nslcd": "Maneja la conexión del intérprete de órdenes («shell») de usuario de YunoHost", + "service_description_nginx": "Sirve o proporciona acceso a todos los sitios web alojados en su servidor", + "service_description_mysql": "Almacena los datos de las aplicaciones (base de datos SQL)", + "service_description_metronome": "Gestionar las cuentas XMPP de mensajería instantánea", + "service_description_glances": "Supervisa la información del sistema en su servidor", + "service_description_fail2ban": "Protege contra ataques de fuerza bruta y otras clases de ataques desde Internet", + "service_description_dovecot": "Permite a los clientes de correo acceder/obtener correo (vía IMAP y POP3)", + "service_description_dnsmasq": "Maneja la resolución de nombres de dominio (DNS)", + "service_description_avahi-daemon": "Permite acceder a su servidor usando «yunohost.local» en su red local", "server_reboot_confirm": "El servidor se reiniciará inmediatamente ¿está seguro? [{answers:s}]", "server_reboot": "El servidor se reiniciará", "server_shutdown_confirm": "El servidor se apagará inmediatamente ¿está seguro? [{answers:s}]", "server_shutdown": "El servidor se apagará", "root_password_replaced_by_admin_password": "Su contraseña de root ha sido sustituida por su contraseña de administración.", - "root_password_desynchronized": "La contraseña de administración ha sido cambiada pero ¡YunoHost no pudo propagar esto en la contraseña de root!", - "restore_system_part_failed": "No se puede restaurar la parte del sistema «{part:s}»", - "restore_removing_tmp_dir_failed": "No se puede eliminar un antiguo directorio temporal", - "restore_not_enough_disk_space": "Insuficiente espacio en disco (espacio libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", + "root_password_desynchronized": "La contraseña de administración ha sido cambiada pero ¡YunoHost no pudo propagar esto a la contraseña de root!", + "restore_system_part_failed": "No se pudo restaurar la parte del sistema «{part:s}»", + "restore_removing_tmp_dir_failed": "No se pudo eliminar un directorio temporal antiguo", + "restore_not_enough_disk_space": "Espacio insuficiente (espacio: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", "restore_mounting_archive": "Montando archivo en «{path:s}»", - "restore_may_be_not_enough_disk_space": "Parece que su sistema no tiene suficiente espacio de disco libre (espacio libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", + "restore_may_be_not_enough_disk_space": "Parece que su sistema no tiene suficiente espacio (libre: {free_space:d} B, espacio necesario: {needed_space:d} B, margen de seguridad: {margin:d} B)", "restore_extracting": "Extrayendo los archivos necesarios para el archivo…", "regenconf_pending_applying": "Aplicando la configuración pendiente para la categoría «{category}»…", - "regenconf_failed": "No se puede regenerar la configuración para la(s) categoría(s): {categories}", + "regenconf_failed": "No se pudo regenerar la configuración para la(s) categoría(s): {categories}", "regenconf_dry_pending_applying": "Comprobando la configuración pendiente que habría sido aplicada para la categoría «{category}»…", "regenconf_would_be_updated": "La configuración habría sido actualizada para la categoría «{category}»", - "regenconf_updated": "Ha sido actualizada la configuración para la categoría «{category}»", + "regenconf_updated": "Actualizada la configuración para la categoría «{category}»", "regenconf_up_to_date": "Ya está actualizada la configuración para la categoría «{category}»", "regenconf_now_managed_by_yunohost": "El archivo de configuración «{conf}» está gestionado ahora por YunoHost (categoría {category}).", - "regenconf_file_updated": "El archivo de configuración «{conf}» ha sido actualizado", - "regenconf_file_removed": "El archivo de configuración «{conf}» ha sido eliminado", - "regenconf_file_remove_failed": "No se puede eliminar el archivo de configuración «{conf}»", + "regenconf_file_updated": "Actualizado el archivo de configuración «{conf}»", + "regenconf_file_removed": "Eliminado el archivo de configuración «{conf}»", + "regenconf_file_remove_failed": "No se pudo eliminar el archivo de configuración «{conf}»", "regenconf_file_manually_removed": "El archivo de configuración «{conf}» ha sido eliminado manualmente y no se creará", "regenconf_file_manually_modified": "El archivo de configuración «{conf}» ha sido modificado manualmente y no será actualizado", "regenconf_file_kept_back": "Se espera que el archivo de configuración «{conf}» sea eliminado por regen-conf (categoría {category}) pero ha sido retenido.", - "regenconf_file_copy_failed": "No se puede copiar el nuevo archivo de configuración «{new}» a «{conf}»", - "regenconf_file_backed_up": "El archivo de configuración «{conf}» ha sido respaldado en «{backup}»", - "remove_user_of_group_not_allowed": "No tiene permiso para eliminar al usuario {user:s} en el grupo {group:s}", + "regenconf_file_copy_failed": "No se pudo copiar el nuevo archivo de configuración «{new}» a «{conf}»", + "regenconf_file_backed_up": "Archivo de configuración «{conf}» respaldado en «{backup}»", + "remove_user_of_group_not_allowed": "No tiene permiso para eliminar al usuario «{user:s}» en el grupo «{group:s}»", "remove_main_permission_not_allowed": "No se permite eliminar el permiso principal", "recommend_to_add_first_user": "La posinstalación ha terminado pero YunoHost necesita al menos un usuario para funcionar correctamente, debe añadir uno ejecutando «yunohost user create » o usando la interfaz de administración.", "permission_update_nothing_to_do": "No hay permisos para actualizar", - "permission_updated": "Permiso «{permission:s}» para la aplicación {app:s} actualizado", - "permission_generated": "La base de datos de permisos se ha actualizado", - "permission_update_failed": "Actualización de permiso fallida", - "permission_name_not_valid": "Nombre de permiso «{permission:s}» no válido", - "permission_not_found": "Permiso «{permission:s}» no encontrado para la aplicación {app:s}", - "permission_deletion_failed": "Permiso «{permission:s}» para eliminar la aplicación «{app:s}» fallido", + "permission_updated": "Actualizado el permiso «{permission:s}» para la aplicación «{app:s}»", + "permission_generated": "Actualizada la base de datos de permisos", + "permission_update_failed": "No se pudo actualizar el permiso", + "permission_name_not_valid": "Elija un nombre de permiso permitido para «{permission:s}", + "permission_not_found": "No se encontró el permiso «{permission:s}» para la aplicación «{app:s}»", + "permission_deletion_failed": "Falta el permiso «{permission:s}» para eliminar la aplicación «{app:s}»", "permission_deleted": "Eliminado el permiso «{permission:s}» para la aplicación {app:s}", - "permission_creation_failed": "Ha fallado la creación del permiso", + "permission_creation_failed": "No se pudo conceder el permiso", "permission_created": "Creado el permiso «{permission:s}» para la aplicación {app:s}", "permission_already_exist": "El permiso «{permission:s}» para la aplicación {app:s} ya existe", "permission_already_clear": "El permiso «{permission:s}» ya está definido para la aplicación {app:s}", - "pattern_password_app": "Las contraseñas no deben incluir los siguientes caracteres: {forbidden_chars}", - "need_define_permission_before": "Necesita redefinir los permisos ejecutando «yunohost user permission add -u USUARIO» antes de eliminar un grupo permitido", - "migrations_to_be_ran_manually": "La migración {id} hay que ejecutarla manualmente. Vaya a Herramientas > Migraciones en la web de administración o ejecute `yunohost tools migrations migrate`.", - "migrations_success_forward": "¡Migración {id} ejecutada correctamente!", + "pattern_password_app": "Las contraseñas no pueden incluir los siguientes caracteres: {forbidden_chars}", + "need_define_permission_before": "Redefina los permisos ejecutando «yunohost user permission add -u USUARIO» antes de eliminar un grupo permitido", + "migrations_to_be_ran_manually": "La migración {id} hay que ejecutarla manualmente. Vaya a Herramientas → Migraciones en la página web de administración o ejecute `yunohost tools migrations migrate`.", + "migrations_success_forward": "Migración {id} completada", "migrations_skip_migration": "Omitiendo migración {id}…", "migrations_running_forward": "Ejecutando migración {id}…", - "migrations_pending_cant_rerun": "Esas migraciones están aún pendientes así que no se pueden volver a ejecutar: {ids}", - "migrations_not_pending_cant_skip": "Esas migraciones no están pendientes así que no pueden ser omitidas: {ids}", - "migrations_no_such_migration": "No existe una migración llamada {id}", + "migrations_pending_cant_rerun": "Esas migraciones están aún pendientes, así que no se pueden volver a ejecutar: {ids}", + "migrations_not_pending_cant_skip": "Esas migraciones no están pendientes, así que no pueden ser omitidas: {ids}", + "migrations_no_such_migration": "No hay ninguna migración llamada {id}", "migrations_no_migrations_to_run": "No hay migraciones que ejecutar", - "migrations_need_to_accept_disclaimer": "Para ejecutar la migración {id} debe aceptar el siguiente descargo de responsabilidad:\n---\n{disclaimer}\n---\nSi acepta ejecutar la migración, vuelva a ejecutar la orden con la opción --accept-disclaimer.", - "migrations_must_provide_explicit_targets": "Necesita proporcionar objetivos explícitos al usar --skip o --force-rerun", - "migrations_migration_has_failed": "Migración {id} fallida, cancelando. Error: {exception}", + "migrations_need_to_accept_disclaimer": "Para ejecutar la migración {id} debe aceptar el siguiente descargo de responsabilidad:\n---\n{disclaimer}\n---\nSi acepta ejecutar la migración, vuelva a ejecutar la orden con la opción «--accept-disclaimer».", + "migrations_must_provide_explicit_targets": "Necesita proporcionar objetivos explícitos al usar «--skip» or «--force-rerun»", + "migrations_migration_has_failed": "La migración {id} no se ha completado, cancelando. Error: {exception}", "migrations_loading_migration": "Cargando migración {id}…", - "migrations_list_conflict_pending_done": "No puede usar --previous y --done al mismo tiempo.", - "migrations_exclusive_options": "--auto, --skip, and --force-rerun son opciones excluyentes.", - "migrations_failed_to_load_migration": "Error al cargar la migración {id} : {error}", + "migrations_list_conflict_pending_done": "No puede usar «--previous» y «--done» al mismo tiempo.", + "migrations_exclusive_options": "«--auto», «--skip», and «--force-rerun» son opciones mutuamente excluyentes.", + "migrations_failed_to_load_migration": "No se pudo cargar la migración {id}: {error}", "migrations_dependencies_not_satisfied": "No se puede ejecutar la migración {id} porque primero necesita ejecutar estas migraciones: {dependencies_id}", - "migrations_cant_reach_migration_file": "No se pueden acceder los archivos de migración en la ruta %s", - "migrations_already_ran": "Esas migraciones ya se han ejecutado: {ids}", - "migration_0011_update_LDAP_schema": "Actualizando el esquema de LDAP...", - "migration_0011_update_LDAP_database": "Actualizando la base de datos de LDAP...", - "migration_0011_rollback_success": "Revertido correctamente.", - "migration_0011_migration_failed_trying_to_rollback": "Migración fallida... intentando revertir el sistema.", - "migration_0011_migrate_permission": "Migrando permisos desde la configuración de las aplicaciones a LDAP...", - "migration_0011_LDAP_update_failed": "Actualización de LDAP fallida. Error: {error:s}", - "migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, restaurar la configuración original con la orden «yunohost tools regen-conf -f» y reintentar después la migración", - "migration_0011_done": "Migración exitosa. Ahora puede gestionar los grupos de usuarios.", - "migration_0011_create_group": "Creando un grupo para cada usuario...", - "migration_0011_can_not_backup_before_migration": "Falló la copia de seguridad del sistema antes de la migración. Migración fallida. Error: {error:s}", + "migrations_cant_reach_migration_file": "No se pudo acceder los archivos de migración en la ruta %s", + "migrations_already_ran": "Esas migraciones ya se han realizado: {ids}", + "migration_0011_update_LDAP_schema": "Actualizando el esquema de LDAP…", + "migration_0011_update_LDAP_database": "Actualizando la base de datos de LDAP…", + "migration_0011_rollback_success": "Sistema revertido.", + "migration_0011_migration_failed_trying_to_rollback": "Migración fallida… intentando revertir el sistema.", + "migration_0011_migrate_permission": "Migrando permisos desde la configuración de las aplicaciones a LDAP…", + "migration_0011_LDAP_update_failed": "No se pudo actualizar LDAP. Error: {error:s}", + "migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, reiniciar la configuración original ejecutando «yunohost tools regen-conf -f» y reintentar la migración", + "migration_0011_done": "Migración correcta. Ahora puede gestionar los grupos de usuarios.", + "migration_0011_create_group": "Creando un grupo para cada usuario…", + "migration_0011_can_not_backup_before_migration": "No se pudo respaldar el sistema antes de la migración. Error: {error:s}", "migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.", - "migration_0009_not_needed": "¿La migración ya ocurrió de algún modo? Omitiendo.", - "migration_0008_no_warning": "No se ha detectado ningún riesgo importante sobre la anulación de su configuración SSH ¡pero no existe una certeza absoluta ;)! Si permite a YunoHost anular su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", + "migration_0009_not_needed": "La migración ya ocurrió de algún modo… (?) Omitiendo.", + "migration_0008_no_warning": "No se ha detectado ningún riesgo importante con respecto a la anulación de su configuración SSH ¡sin embargo uno nunca puede estar absolutamente seguro ;)! Ejecute la migración para anularla. Por otra parte, puede omitir la migración aunque no esté recomendado.", "migration_0008_warning": "Si entiende esos avisos y permite a YunoHost anular su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", - "migration_0008_dsa": " - se desactivará la clave DSA. Así que podría tener que anular un aviso espeluznante de su cliente SSH y volver a comprobar la huella de su servidor;", - "migration_0008_root": " - no podrá conectarse como «root» a través de SSH. En su lugar debería usar el usuario de administración;", - "migration_0008_port": " - tendrá que conectarse usando el puerto 22 en vez de su actual puerto SSH personalizado. No dude en reconfigurarlo;", - "migration_0008_general_disclaimer": "Para mejorar la seguridad de su servidor, es recomendable permitir a YunoHost gestionar la configuración SSH. Su actual configuración SSH difiere de la configuración recomendada. Si permite a YunoHost reconfigurarla, la manera en la que conecta con su servidor a través de SSH cambiará en el siguiente modo:", + "migration_0008_dsa": "• Se desactivará la clave DSA. Así que podría tener que anular un aviso espeluznante de su cliente SSH y volver a comprobar la huella de su servidor;", + "migration_0008_root": "• No podrá conectarse como «root» a través de SSH. En su lugar debe usar el usuario «admin»;", + "migration_0008_port": "• Tendrá que conectarse usando el puerto 22 en vez de su actual puerto SSH personalizado. No dude en reconfigurarlo;", + "migration_0008_general_disclaimer": "Para mejorar la seguridad de su servidor, es recomendable permitir a YunoHost gestionar la configuración de SSH. Su actual configuración de SSH difiere de la recomendación. Si permite a YunoHost reconfigurarla, la manera en la que conecta con su servidor a través de SSH cambiará así:", "migration_0007_cannot_restart": "No se puede reiniciar SSH después de intentar cancelar la migración número 6.", "migration_0007_cancelled": "YunoHost no ha podido mejorar el modo en el que se gestiona su configuración de SSH.", "migration_0006_disclaimer": "YunoHost espera ahora que las contraseñas de «admin» y «root» estén sincronizadas. Al ejecutar esta migración, su contraseña de «root» será reemplazada por la contraseña de administración.", - "migration_0005_not_enough_space": "No hay suficiente espacio libre disponible en {path} para ejecutar la migración en este momento:(.", - "migration_0005_postgresql_96_not_installed": "¿¡Se encontró postgresql 9.4 para ser instalado pero no postgresql 9.6!? Algo raro podría haber ocurrido en su sistema:(…", - "migration_0005_postgresql_94_not_installed": "Postgresql no estaba instalado en su sistema. ¡Nada que hacer!", - "migration_0003_modified_files": "Tenga en cuenta que se encontró que los siguientes archivos fueron modificados manualmente y podrían ser sobrescritos al final de la actualización: {manually_modified_files}", + "migration_0005_not_enough_space": "Tenga suficiente espacio libre disponible en {path} para ejecutar la migración.", + "migration_0005_postgresql_96_not_installed": "⸘PostgreSQL 9.4 está instalado pero no PostgreSQL 9.6‽ Algo raro podría haber ocurrido en su sistema:(…", + "migration_0005_postgresql_94_not_installed": "PostgreSQL no estaba instalado en su sistema. Nada que hacer.", + "migration_0003_modified_files": "Tenga en cuenta que se encontró que los siguientes archivos fueron modificados manualmente y podrían ser sobrescritos después de la actualización: {manually_modified_files}", "migration_0003_problematic_apps_warning": "Tenga en cuenta que se detectaron las siguientes aplicaciones instaladas posiblemente problemáticas. Parece que no fueron instaladas desde una lista de aplicaciones o no estaban etiquetadas como «funciona». Así que no hay garantía de que aún funcionen después de la actualización: {problematic_apps}", - "migration_0003_general_warning": "Tenga en cuenta que esta migración es una operación delicada. Aunque el equipo de YunoHost hizo todo lo posible para revisarla y probarla, la migración aún podría romper parte del sistema o de las aplicaciones.\n\nPor lo tanto le recomendamos que:\n - Realice una copia de seguridad de cualquier dato crítico o aplicación. Más información en https://yunohost.org/backup;\n - Tenga paciencia tras iniciar la migración: dependiendo de su conexión a internet y de su hardware, podría tardar unas cuantas horas hasta que todo se actualice.\n\nAdemás, el puerto para SMTP usado por los clientes de correo externos (como Thunderbird o K9-Mail) cambió de 465 (SSL/TLS) a 587 (STARTTLS). El antiguo puerto 465 se cerrará automáticamente y el nuevo puerto 587 se abrirá en el cortafuegos. ¡Todos los usuarios *tendrán* que adaptar la configuración de sus clientes de correo por lo tanto!", - "migration_0003_still_on_jessie_after_main_upgrade": "Algo fue mal durante la actualización principal: ¿¡el sistema está aún en Jessie!? Para investigar el problema, vea {log}:s…", + "migration_0003_general_warning": "Tenga en cuenta que esta migración es una operación delicada. El equipo de YunoHost ha hecho todo lo posible para revisarla y probarla, pero la migración aún podría romper parte del sistema o de sus aplicaciones.\n\nPor lo tanto, se recomienda que:\n - Realice una copia de seguridad de cualquier dato crítico o aplicación. Más información en https://yunohost.org/backup;\n - Tenga paciencia tras iniciar la migración: dependiendo de su conexión a Internet y de su hardware, podría tardar unas cuantas horas hasta que todo se actualice.\n\nAdemás, el puerto para SMTP usado por los clientes de correo externos (como Thunderbird o K9-Mail) cambió de 465 (SSL/TLS) a 587 (STARTTLS). El antiguo puerto (465) se cerrará automáticamente y el nuevo puerto (587) se abrirá en el cortafuegos. Todos los usuarios *tendrán* que adaptar la configuración de sus clientes de correo por lo tanto.", + "migration_0003_still_on_jessie_after_main_upgrade": "Algo fue mal durante la actualización principal: ⸘el sistema está aún en Jessie‽ Para investigar el problema, vea {log}:s…", "migration_0003_system_not_fully_up_to_date": "Su sistema no está totalmente actualizado. Realice una actualización normal antes de ejecutar la migración a Stretch.", "migration_0003_not_jessie": "¡La distribución de Debian actual no es Jessie!", - "migration_0003_yunohost_upgrade": "Iniciando la actualización del paquete «yunohost»… La migración finalizará pero la actualización real ocurrirá justo después. Después de que la operación esté completada, podría tener que reiniciar sesión en la administración web.", + "migration_0003_yunohost_upgrade": "Iniciando la actualización del paquete YunoHost… La migración finalizará pero la actualización real ocurrirá inmediatamente después. Después de que la operación esté completada, podría tener que iniciar sesión en la página de administración de nuevo.", "migration_0003_restoring_origin_nginx_conf": "Su archivo /etc/nginx/nginx.conf ha sido editado de algún modo. La migración lo devolverá a su estado original primero… El archivo anterior estará disponible como {backup_dest}.", - "migration_0003_fail2ban_upgrade": "Iniciando la actualización de «fail2ban»…", + "migration_0003_fail2ban_upgrade": "Iniciando la actualización de Fail2Ban…", "migration_0003_main_upgrade": "Iniciando la actualización principal…", "migration_0003_patching_sources_list": "Corrigiendo «sources.lists»…", "migration_0003_start": "Iniciando migración a Stretch. El registro estará disponible en {logfile}.", - "migration_description_0012_postgresql_password_to_md5_authentication": "Forzar a la autentificación de postgresql a usar md5 para las conexiones locales", + "migration_description_0012_postgresql_password_to_md5_authentication": "Forzar a la autentificación de PostgreSQL a usar MD5 para las conexiones locales", "migration_description_0011_setup_group_permission": "Configurar grupo de usuario y configurar permisos para aplicaciones y servicios", - "migration_description_0010_migrate_to_apps_json": "Eliminar la obsoleta «appslists» y usar la nueva lista unificada «apps.json»", + "migration_description_0010_migrate_to_apps_json": "Eliminar las listas de aplicaciones («appslists») obsoletas y usar en su lugar la nueva lista unificada «apps.json»", "migration_description_0009_decouple_regenconf_from_services": "Separar el mecanismo «regen-conf» de los servicios", "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Permitir que la configuración de SSH la gestione YunoHost (paso 2, manual)", "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Permitir que la configuración de SSH la gestione YunoHost (paso 1, automático)", "migration_description_0006_sync_admin_and_root_passwords": "Sincronizar las contraseñas de «admin» y «root»", - "migration_description_0005_postgresql_9p4_to_9p6": "Migrar las bases de datos de postgresql 9.4 a 9.6", + "migration_description_0005_postgresql_9p4_to_9p6": "Migrar las bases de datos de PostgreSQL 9.4 a 9.6", "migration_description_0004_php5_to_php7_pools": "Reconfigurar los «pools» de PHP para usar PHP 7 en vez de 5", "migration_description_0003_migrate_to_stretch": "Actualizar el sistema a Debian Stretch y YunoHost 3.0", - "migration_description_0002_migrate_to_tsig_sha256": "Mejorar la seguridad de la TSIG de dyndns usando SHA512 en vez de MD5", + "migration_description_0002_migrate_to_tsig_sha256": "Mejore la seguridad de las actualizaciones de TSIG de DynDNS usando SHA-512 en vez de MD5", "migration_description_0001_change_cert_group_to_sslcert": "Cambiar los permisos de grupo de certificados de «metronome» a «ssl-cert»", - "migrate_tsig_not_needed": "Parece que no usa un dominio dyndns ¡así que no es necesario migrar!", + "migrate_tsig_not_needed": "Parece que no usa un dominio de DynDNS, así que no es necesario migrar.", "migrate_tsig_wait_4": "30 segundos…", "migrate_tsig_wait_3": "1 min. …", "migrate_tsig_wait_2": "2 min. …", - "migrate_tsig_wait": "Esperar 3 min. para que el servidor dyndns tenga en cuenta la nueva clave…", - "migrate_tsig_start": "Detectado algoritmo de clave insuficientemente seguro para la firma TSIG del dominio «{domain}», iniciando migración al más seguro hmac-sha512", - "migrate_tsig_failed": "Error al migrar el dominio de dyndns {domain} a hmac-sha512, revertiendo. Error: {error_code} - {error}", - "migrate_tsig_end": "Terminada la migración a hmac-sha512", + "migrate_tsig_wait": "Esperando tres minutos para que el servidor de DynDNS tenga en cuenta la nueva clave…", + "migrate_tsig_start": "Detectado algoritmo de clave insuficientemente seguro para la firma TSIG del dominio «{domain}», iniciando migración al más seguro HMAC-SHA-512", + "migrate_tsig_failed": "No se pudo migrar el dominio de DynDNS «{domain}» a HMAC-SHA-512, revertiendo. Error: {error_code}, {error}", + "migrate_tsig_end": "Terminada la migración a HMAC-SHA-512", "mail_unavailable": "Esta dirección de correo está reservada y será asignada automáticamente al primer usuario", - "mailbox_disabled": "Mailbox desactivado para usuario {user:s}", + "mailbox_disabled": "Correo desactivado para usuario {user:s}", "log_tools_reboot": "Reiniciar el servidor", "log_tools_shutdown": "Apagar el servidor", "log_tools_upgrade": "Actualizar paquetes del sistema", "log_tools_postinstall": "Posinstalación del servidor YunoHost", "log_tools_migrations_migrate_forward": "Migrar hacia adelante", - "log_tools_maindomain": "Convertir «{}» en dominio principal", + "log_tools_maindomain": "Convertir «{}» en el dominio principal", "log_user_permission_remove": "Actualizar permiso «{}»", "log_user_permission_add": "Actualizar permiso «{}»", - "log_user_update": "Actualizar información del usuario «{}»", + "log_user_update": "Actualizar la información de usuario de «{}»", "log_user_group_update": "Actualizar grupo «{}»", "log_user_group_delete": "Eliminar grupo «{}»", "log_user_group_add": "Añadir grupo «{}»", @@ -506,8 +506,8 @@ "log_selfsigned_cert_install": "Instalar certificado autofirmado en el dominio «{}»", "log_permission_update": "Actualizar permiso «{}» para la aplicación «{}»", "log_permission_remove": "Eliminar permiso «{}»", - "log_permission_add": "Añadir permiso «{}» para la aplicación «{}»", - "log_letsencrypt_cert_install": "Instalar certificado de Let's encrypt en el dominio «{}»", + "log_permission_add": "Añadir el permiso «{}» para la aplicación «{}»", + "log_letsencrypt_cert_install": "Instalar un certificado de Let's encrypt en el dominio «{}»", "log_dyndns_update": "Actualizar la IP asociada con su subdominio de YunoHost «{}»", "log_dyndns_subscribe": "Subscribirse a un subdomino de YunoHost «{}»", "log_domain_remove": "Eliminar el dominio «{}» de la configuración del sistema", @@ -521,7 +521,7 @@ "log_app_upgrade": "Actualizar la aplicación «{}»", "log_app_remove": "Eliminar la aplicación «{}»", "log_app_install": "Instalar la aplicación «{}»", - "log_app_change_url": "Cambiar la url de la aplicación «{}»", + "log_app_change_url": "Cambiar el URL de la aplicación «{}»", "log_app_removelist": "Eliminar una lista de aplicaciones", "log_app_fetchlist": "Añadir una lista de aplicaciones", "log_app_clearaccess": "Eliminar todos los accesos a «{}»", @@ -529,26 +529,26 @@ "log_app_addaccess": "Añadir acceso a «{}»", "log_operation_unit_unclosed_properly": "La unidad de operación no se ha cerrado correctamente", "log_does_exists": "No existe ningún registro de actividades con el nombre «{log}», ejecute «yunohost log list» para ver todos los registros de actividades disponibles", - "log_help_to_get_failed_log": "¡La operación «{desc}» ha fallado! Para obtener ayuda, comparta el registro completo de esta operación ejecutando la orden «yunohost log display {name} --share»", - "log_link_to_failed_log": "¡La operación «{desc}» ha fallado! Para obtener ayuda, proporcione el registro completo de esta operación pulsando aquí", + "log_help_to_get_failed_log": "No se pudo completar la operación «{desc}». Para obtener ayuda, comparta el registro completo de esta operación ejecutando la orden «yunohost log display {name} --share»", + "log_link_to_failed_log": "No se pudo completar la operación «{desc}». Para obtener ayuda, proporcione el registro completo de esta operación pulsando aquí", "log_help_to_get_log": "Para ver el registro de la operación «{desc}», ejecute la orden «yunohost log display {name}»", "log_link_to_log": "Registro completo de esta operación: «{desc}»", "log_category_404": "La categoría de registro «{category}» no existe", - "log_corrupted_md_file": "El archivo de metadatos yaml asociado con el registro está dañado: «{md_file}\nError: {error}»", - "hook_json_return_error": "Error al leer la respuesta del gancho {path:s}. Error: {msg:s}. Contenido sin procesar: {raw_content}", - "group_update_failed": "Error en la actualización del grupo «{group}»", + "log_corrupted_md_file": "El archivo de metadatos YAML asociado con el registro está dañado: «{md_file}\nError: {error}»", + "hook_json_return_error": "No se pudo leer la respuesta del gancho {path:s}. Error: {msg:s}. Contenido sin procesar: {raw_content}", + "group_update_failed": "No se pudo actualizar el grupo «{group}»", "group_updated": "Grupo «{group}» actualizado", - "group_unknown": "Grupo {group:s} desconocido", - "group_info_failed": "Error en la información del grupo", + "group_unknown": "El grupo «{group:s}» es desconocido", + "group_info_failed": "No se pudo mostrar la información del grupo", "group_deletion_not_allowed": "No se puede eliminar el grupo {group:s} manualmente.", - "group_deletion_failed": "Error al eliminar el grupo «{group}»", + "group_deletion_failed": "No se pudo eliminar el grupo «{group}»", "group_deleted": "Eliminado el grupo «{group}»", - "group_creation_failed": "Error al crear el grupo «{group}»", - "group_created": "Grupo «{group}» creado correctamente", + "group_creation_failed": "No se pudo crear el grupo «{group}»", + "group_created": "Creado el grupo «{group}»", "group_name_already_exist": "El grupo {name:s} ya existe", - "group_already_disallowed": "El grupo '{group:s}' ya tiene desactivado el permiso «{permission:s}» para la aplicación «{app:s}»", - "group_already_allowed": "El grupo '{group:s}' ya tiene activado el permiso «{permission:s}» para la aplicación «{app:s}»", - "good_practices_about_admin_password": "Va a determinar una nueva contraseña de administración. La contraseña debería tener al menos 8 caracteres, aunque es una buena práctica usar contraseñas más extensas (esto es, una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).", + "group_already_disallowed": "El grupo «{group:s}» ya tiene desactivado el permiso «{permission:s}» para la aplicación «{app:s}»", + "group_already_allowed": "El grupo «{group:s}» ya tiene activado el permiso «{permission:s}» para la aplicación «{app:s}»", + "good_practices_about_admin_password": "Va a establecer una nueva contraseña de administración. La contraseña debería tener al menos 8 caracteres, aunque es una buena práctica usar una contraseña más extensa (básicamente una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).", "global_settings_unknown_type": "Situación imprevista, la configuración {setting:s} parece tener el tipo {unknown_type:s} pero no es un tipo compatible con el sistema.", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permitir el uso de la llave (obsoleta) DSA para la configuración del demonio SSH", "global_settings_unknown_setting_from_settings_file": "Clave desconocida en la configuración: «{setting_key:s}», desechada y guardada en /etc/yunohost/settings-unknown.json", @@ -556,53 +556,55 @@ "global_settings_setting_security_ssh_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor SSH. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", "global_settings_setting_security_password_user_strength": "Seguridad de la contraseña de usuario", "global_settings_setting_security_password_admin_strength": "Seguridad de la contraseña del administrador", - "global_settings_setting_security_nginx_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor web nginx. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", + "global_settings_setting_security_nginx_compatibility": "Compromiso entre compatibilidad y seguridad para el servidor web NGINX. Afecta al cifrado (y otros aspectos relacionados con la seguridad)", "global_settings_setting_example_string": "Ejemplo de opción de cadena", "global_settings_setting_example_int": "Ejemplo de opción «int»", "global_settings_setting_example_enum": "Ejemplo de opción «enum»", "global_settings_setting_example_bool": "Ejemplo de opción booleana", - "global_settings_reset_success": "Éxito. Se ha respaldado su configuración previa en {path:s}", + "global_settings_reset_success": "Respaldada la configuración previa en {path:s}", "global_settings_key_doesnt_exists": "La clave «{settings_key:s}» no existe en la configuración global, puede ver todas las claves disponibles ejecutando «yunohost settings list»", - "global_settings_cant_write_settings": "Error al escribir el archivo de configuración, motivo: {reason:s}", - "global_settings_cant_serialize_settings": "Error al seriar los datos de configuración, motivo: {reason:s}", - "global_settings_cant_open_settings": "Error al abrir el archivo de configuración, motivo: {reason:s}", - "global_settings_bad_type_for_setting": "Tipo erróneo para la configuración {setting:s}, obtuvo {received_type:s}, excepto {expected_type:s}", - "global_settings_bad_choice_for_enum": "Mala elección para la configuración {setting:s}, obtuvo «{choice:s}» pero las opciones disponibles son: {available_choices:s}", + "global_settings_cant_write_settings": "No se pudo guardar el archivo de configuración, motivo: {reason:s}", + "global_settings_cant_serialize_settings": "No se pudo seriar los datos de configuración, motivo: {reason:s}", + "global_settings_cant_open_settings": "No se pudo abrir el archivo de configuración, motivo: {reason:s}", + "global_settings_bad_type_for_setting": "Tipo erróneo para la configuración {setting:s}, obtuvo {received_type:s}, esperado {expected_type:s}", + "global_settings_bad_choice_for_enum": "Opción errónea para la configuración {setting:s}, obtuvo «{choice:s}» pero las opciones disponibles son: {available_choices:s}", "file_does_not_exist": "El archivo {path:s} no existe.", - "error_when_removing_sftpuser_group": "Error al probar «remove sftpusers group»", + "error_when_removing_sftpuser_group": "No se pudo eliminar el grupo sftpusers", "edit_permission_with_group_all_users_not_allowed": "No puede editar el permiso para el grupo «all_users», utilice «yunohost user permission clear APLICACIÓN» o «yunohost user permission add APLICACIÓN -u USUARIO».", "edit_group_not_allowed": "No tiene permiso para editar el grupo {group:s}", "dyndns_could_not_check_available": "No se pudo comprobar si {domain:s} está disponible en {provider:s}.", - "domain_dyndns_dynette_is_unreachable": "No se pudo conectar al dynette de YunoHost, o su YunoHost no está correctamente conectado a internet o el servidor dynette está caído. Error: {error}", - "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra cuál es la configuración *recomendada*. No configura el DNS. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", + "domain_dyndns_dynette_is_unreachable": "No se pudo conectar a dynette de YunoHost. O su YunoHost no está correctamente conectado a Internet o el servidor dynette está caído. Error: {error}", + "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", "dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento porque otro programa parece que está usando el bloqueo de dpkg (el gestor de paquetes del sistema)", "dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.", "confirm_app_install_thirdparty": "¡AVISO! Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarlas salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", "confirm_app_install_danger": "¡AVISO! Esta aplicación es aún experimental (si no está funcionando expresamente) y ¡es probable que rompa su sistema! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", "confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ", - "backup_unable_to_organize_files": "No se pueden organizar los archivos en el archivo con el método rápido", + "backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo", "backup_permission": "Permiso de respaldo para la aplicación {app:s}", - "backup_output_symlink_dir_broken": "Tiene un enlace simbólico roto en vez del directorio «{path:s}» de sus archivos. Puede que tenga una configuración específica para respaldar sus datos en otro sistema de archivos, en este caso probablemente olvidó remontar o conectar su disco duro o clave usb.", + "backup_output_symlink_dir_broken": "Tiene un enlace simbólico roto en vez del directorio «{path:s}» de su archivo. Puede que tenga una configuración específica para respaldar sus datos en otro sistema de archivos, en este caso probablemente olvidó remontar o conectar su disco duro o clave USB.", "backup_mount_archive_for_restore": "Preparando el archivo para la restauración…", - "backup_method_tar_finished": "Creado el archivo de respaldo tar", + "backup_method_tar_finished": "Creado el archivo TAR de respaldo", "backup_method_custom_finished": "Terminado el método «{method:s}» de respaldo personalizado", "backup_method_copy_finished": "Terminada la copia de seguridad", - "backup_method_borg_finished": "Terminado el respaldo en borg", - "backup_custom_backup_error": "Fallo del método de respaldo personalizado en el paso «copia de seguridad»", + "backup_method_borg_finished": "Terminado el respaldo en Borg", + "backup_custom_backup_error": "El método de respaldo personalizado no pudo superar el paso de «copia de seguridad»", "backup_actually_backuping": "Creando un archivo de respaldo de los archivos obtenidos…", "ask_new_path": "Nueva ruta", "ask_new_domain": "Nuevo dominio", - "apps_permission_restoration_failed": "El permiso «{permission:s}» para la restauración de la aplicación {app:s} ha fallado", + "apps_permission_restoration_failed": "Otorgar el permiso «{permission:s}» para restaurar {app:s}", "apps_permission_not_found": "No se han encontrado permisos para las aplicaciones instaladas", "app_upgrade_several_apps": "Las siguientes aplicaciones se actualizarán: {apps}", "app_start_restore": "Restaurando aplicación {app}…", - "app_start_backup": "Obteniendo archivos de respaldo para {app}…", + "app_start_backup": "Obteniendo archivos para el respaldo de {app}…", "app_start_remove": "Eliminando aplicación {app}…", "app_start_install": "Instalando aplicación {app}…", "app_not_upgraded": "Error al actualizar la aplicación «{failed_app}» y como consecuencia se han cancelado las actualizaciones de las siguientes aplicaciones: {apps}", "app_action_cannot_be_ran_because_required_services_down": "Esta aplicación necesita algunos servicios que no están funcionando ahora. Antes de continuar, debería intentar reiniciar los siguientes servicios (y posiblemente investigar por qué no funcionan): {services}", - "already_up_to_date": "¡Nada que hacer! ¡Todo está actualizado!", + "already_up_to_date": "Nada que hacer. Todo está actualizado.", "admin_password_too_long": "Elija una contraseña de menos de 127 caracteres", "aborting": "Cancelando.", - "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar" + "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar", + "app_action_broke_system": "Esta acción parece que ha roto estos importantes servicios: {services}", + "operation_interrupted": "¿Ha sido interrumpida la operación manualmente?" } From 61fb0be7735dcc8369c03de4e066776aefad83e6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 23 Sep 2019 20:57:59 +0200 Subject: [PATCH 171/299] More accurate tests with explicit exception/message excepted to be triggered --- src/yunohost/tests/conftest.py | 23 +++ src/yunohost/tests/test_backuprestore.py | 184 ++++++++++------------- src/yunohost/tests/test_permission.py | 128 ++++++++++------ src/yunohost/tests/test_user-group.py | 146 +++++++++++------- src/yunohost/utils/error.py | 4 +- 5 files changed, 285 insertions(+), 200 deletions(-) diff --git a/src/yunohost/tests/conftest.py b/src/yunohost/tests/conftest.py index a2dc585bd..e23110d1a 100644 --- a/src/yunohost/tests/conftest.py +++ b/src/yunohost/tests/conftest.py @@ -1,9 +1,32 @@ +import pytest import sys import moulinette +from moulinette import m18n +from yunohost.utils.error import YunohostError +from contextlib import contextmanager + sys.path.append("..") + +@contextmanager +def message(mocker, key, **kwargs): + mocker.spy(m18n, "n") + yield + m18n.n.assert_any_call(key, **kwargs) + + +@contextmanager +def raiseYunohostError(mocker, key, **kwargs): + with pytest.raises(YunohostError) as e_info: + yield + assert e_info._excinfo[1].key == key + if kwargs: + assert e_info._excinfo[1].kwargs == kwargs + + + def pytest_addoption(parser): parser.addoption("--yunodebug", action="store_true", default=False) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index cab98089b..ce3e28401 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -2,14 +2,13 @@ import pytest import os import shutil import subprocess -from mock import ANY -from moulinette import m18n +from conftest import message, raiseYunohostError + from yunohost.app import app_install, app_remove, app_ssowatconf from yunohost.app import _is_installed from yunohost.backup import backup_create, backup_restore, backup_list, backup_info, backup_delete, _recursive_umount from yunohost.domain import _get_maindomain -from yunohost.utils.error import YunohostError from yunohost.user import user_permission_list, user_create, user_list, user_delete from yunohost.tests.test_permission import check_LDAP_db_integrity, check_permission_for_apps @@ -206,10 +205,11 @@ def add_archive_system_from_2p4(): # -def test_backup_only_ldap(): +def test_backup_only_ldap(mocker): # Create the backup - backup_create(system=["conf_ldap"], apps=None) + with message(mocker, "backup_created"): + backup_create(system=["conf_ldap"], apps=None) archives = backup_list()["archives"] assert len(archives) == 1 @@ -222,24 +222,22 @@ def test_backup_only_ldap(): def test_backup_system_part_that_does_not_exists(mocker): - mocker.spy(m18n, "n") - # Create the backup - with pytest.raises(YunohostError): - backup_create(system=["yolol"], apps=None) + with message(mocker, 'backup_hook_unknown', hook="doesnt_exist"): + with raiseYunohostError(mocker, "backup_nothings_done"): + backup_create(system=["doesnt_exist"], apps=None) - m18n.n.assert_any_call('backup_hook_unknown', hook="yolol") - m18n.n.assert_any_call('backup_nothings_done') # # System backup and restore # # -def test_backup_and_restore_all_sys(): +def test_backup_and_restore_all_sys(mocker): # Create the backup - backup_create(system=[], apps=None) + with message(mocker, "backup_created"): + backup_create(system=[], apps=None) archives = backup_list()["archives"] assert len(archives) == 1 @@ -255,8 +253,9 @@ def test_backup_and_restore_all_sys(): assert not os.path.exists("/etc/ssowat/conf.json") # Restore the backup - backup_restore(name=archives[0], force=True, - system=[], apps=None) + with message(mocker, "restore_complete"): + backup_restore(name=archives[0], force=True, + system=[], apps=None) # Check ssowat conf is back assert os.path.exists("/etc/ssowat/conf.json") @@ -270,16 +269,18 @@ def test_backup_and_restore_all_sys(): def test_restore_system_from_Ynh2p4(monkeypatch, mocker): # Backup current system - backup_create(system=[], apps=None) + with message(mocker, "backup_created"): + backup_create(system=[], apps=None) archives = backup_list()["archives"] assert len(archives) == 2 # Restore system archive from 2.4 try: - backup_restore(name=backup_list()["archives"][1], - system=[], - apps=None, - force=True) + with message(mocker, "restore_complete"): + backup_restore(name=backup_list()["archives"][1], + system=[], + apps=None, + force=True) finally: # Restore system as it was backup_restore(name=backup_list()["archives"][0], @@ -306,12 +307,10 @@ def test_backup_script_failure_handling(monkeypatch, mocker): # call with monkeypatch). We also patch m18n to check later it's been called # with the expected error message key monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) - mocker.spy(m18n, "n") - with pytest.raises(YunohostError): - backup_create(system=None, apps=["backup_recommended_app"]) - - m18n.n.assert_any_call('backup_app_failed', app='backup_recommended_app') + with message(mocker, 'backup_app_failed', app='backup_recommended_app'): + with raiseYunohostError(mocker, 'backup_nothings_done'): + backup_create(system=None, apps=["backup_recommended_app"]) @pytest.mark.with_backup_recommended_app_installed @@ -327,25 +326,17 @@ def test_backup_not_enough_free_space(monkeypatch, mocker): monkeypatch.setattr("yunohost.backup.free_space_in_directory", custom_free_space_in_directory) - mocker.spy(m18n, "n") - - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, 'not_enough_disk_space'): backup_create(system=None, apps=["backup_recommended_app"]) - m18n.n.assert_any_call('not_enough_disk_space', path=ANY) - def test_backup_app_not_installed(mocker): assert not _is_installed("wordpress") - mocker.spy(m18n, "n") - - with pytest.raises(YunohostError): - backup_create(system=None, apps=["wordpress"]) - - m18n.n.assert_any_call("unbackup_app", app="wordpress") - m18n.n.assert_any_call('backup_nothings_done') + with message(mocker, "unbackup_app", app="wordpress"): + with raiseYunohostError(mocker, 'backup_nothings_done'): + backup_create(system=None, apps=["wordpress"]) @pytest.mark.with_backup_recommended_app_installed @@ -355,13 +346,9 @@ def test_backup_app_with_no_backup_script(mocker): os.system("rm %s" % backup_script) assert not os.path.exists(backup_script) - mocker.spy(m18n, "n") - - with pytest.raises(YunohostError): - backup_create(system=None, apps=["backup_recommended_app"]) - - m18n.n.assert_any_call("backup_with_no_backup_script_for_app", app="backup_recommended_app") - m18n.n.assert_any_call('backup_nothings_done') + with message(mocker, "backup_with_no_backup_script_for_app", app="backup_recommended_app"): + with raiseYunohostError(mocker, 'backup_nothings_done'): + backup_create(system=None, apps=["backup_recommended_app"]) @pytest.mark.with_backup_recommended_app_installed @@ -371,23 +358,21 @@ def test_backup_app_with_no_restore_script(mocker): os.system("rm %s" % restore_script) assert not os.path.exists(restore_script) - mocker.spy(m18n, "n") - # Backuping an app with no restore script will only display a warning to the # user... - backup_create(system=None, apps=["backup_recommended_app"]) - - m18n.n.assert_any_call("backup_with_no_restore_script_for_app", app="backup_recommended_app") + with message(mocker, "backup_with_no_restore_script_for_app", app="backup_recommended_app"): + backup_create(system=None, apps=["backup_recommended_app"]) @pytest.mark.clean_opt_dir -def test_backup_with_different_output_directory(): +def test_backup_with_different_output_directory(mocker): # Create the backup - backup_create(system=["conf_ssh"], apps=None, - output_directory="/opt/test_backup_output_directory", - name="backup") + with message(mocker, "backup_created"): + backup_create(system=["conf_ssh"], apps=None, + output_directory="/opt/test_backup_output_directory", + name="backup") assert os.path.exists("/opt/test_backup_output_directory/backup.tar.gz") @@ -401,12 +386,14 @@ def test_backup_with_different_output_directory(): @pytest.mark.clean_opt_dir -def test_backup_with_no_compress(): +def test_backup_with_no_compress(mocker): + # Create the backup - backup_create(system=["conf_nginx"], apps=None, - output_directory="/opt/test_backup_output_directory", - no_compress=True, - name="backup") + with message(mocker, "backup_created"): + backup_create(system=["conf_nginx"], apps=None, + output_directory="/opt/test_backup_output_directory", + no_compress=True, + name="backup") assert os.path.exists("/opt/test_backup_output_directory/info.json") @@ -416,10 +403,11 @@ def test_backup_with_no_compress(): # @pytest.mark.with_wordpress_archive_from_2p4 -def test_restore_app_wordpress_from_Ynh2p4(): +def test_restore_app_wordpress_from_Ynh2p4(mocker): - backup_restore(system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + with message(mocker, "restore_complete"): + backup_restore(system=None, name=backup_list()["archives"][0], + apps=["wordpress"]) @pytest.mark.with_wordpress_archive_from_2p4 @@ -431,16 +419,14 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker): raise Exception monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec) - mocker.spy(m18n, "n") assert not _is_installed("wordpress") - with pytest.raises(YunohostError): - backup_restore(system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) + with message(mocker, 'restore_app_failed', app='wordpress'): + with raiseYunohostError(mocker, 'restore_nothings_done'): + backup_restore(system=None, name=backup_list()["archives"][0], + apps=["wordpress"]) - m18n.n.assert_any_call('restore_app_failed', app='wordpress') - m18n.n.assert_any_call('restore_nothings_done') assert not _is_installed("wordpress") @@ -452,18 +438,13 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker): monkeypatch.setattr("yunohost.backup.free_space_in_directory", custom_free_space_in_directory) - mocker.spy(m18n, "n") assert not _is_installed("wordpress") - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, 'restore_not_enough_disk_space'): backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) - m18n.n.assert_any_call('restore_not_enough_disk_space', - free_space=0, - margin=ANY, - needed_space=ANY) assert not _is_installed("wordpress") @@ -473,13 +454,11 @@ def test_restore_app_not_in_backup(mocker): assert not _is_installed("wordpress") assert not _is_installed("yoloswag") - mocker.spy(m18n, "n") + with message(mocker, 'backup_archive_app_not_found', app="yoloswag"): + with raiseYunohostError(mocker, 'restore_nothings_done'): + backup_restore(system=None, name=backup_list()["archives"][0], + apps=["yoloswag"]) - with pytest.raises(YunohostError): - backup_restore(system=None, name=backup_list()["archives"][0], - apps=["yoloswag"]) - - m18n.n.assert_any_call('backup_archive_app_not_found', app="yoloswag") assert not _is_installed("wordpress") assert not _is_installed("yoloswag") @@ -489,38 +468,36 @@ def test_restore_app_already_installed(mocker): assert not _is_installed("wordpress") - backup_restore(system=None, name=backup_list()["archives"][0], - apps=["wordpress"]) - - assert _is_installed("wordpress") - - mocker.spy(m18n, "n") - with pytest.raises(YunohostError): + with message(mocker, "restore_complete"): backup_restore(system=None, name=backup_list()["archives"][0], apps=["wordpress"]) - m18n.n.assert_any_call('restore_already_installed_app', app="wordpress") - m18n.n.assert_any_call('restore_nothings_done') + assert _is_installed("wordpress") + + with message(mocker, 'restore_already_installed_app', app="wordpress"): + with raiseYunohostError(mocker, 'restore_nothings_done'): + backup_restore(system=None, name=backup_list()["archives"][0], + apps=["wordpress"]) assert _is_installed("wordpress") @pytest.mark.with_legacy_app_installed -def test_backup_and_restore_legacy_app(): +def test_backup_and_restore_legacy_app(mocker): - _test_backup_and_restore_app("legacy_app") + _test_backup_and_restore_app(mocker, "legacy_app") @pytest.mark.with_backup_recommended_app_installed -def test_backup_and_restore_recommended_app(): +def test_backup_and_restore_recommended_app(mocker): - _test_backup_and_restore_app("backup_recommended_app") + _test_backup_and_restore_app(mocker, "backup_recommended_app") @pytest.mark.with_backup_recommended_app_installed_with_ynh_restore -def test_backup_and_restore_with_ynh_restore(): +def test_backup_and_restore_with_ynh_restore(mocker): - _test_backup_and_restore_app("backup_recommended_app") + _test_backup_and_restore_app(mocker, "backup_recommended_app") @pytest.mark.with_permission_app_installed def test_backup_and_restore_permission_app(): @@ -552,10 +529,11 @@ def test_backup_and_restore_permission_app(): assert res['permissions_app.dev']['allowed'] == [] -def _test_backup_and_restore_app(app): +def _test_backup_and_restore_app(mocker, app): # Create a backup of this app - backup_create(system=None, apps=[app]) + with message(mocker, "backup_created"): + backup_create(system=None, apps=[app]) archives = backup_list()["archives"] assert len(archives) == 1 @@ -571,8 +549,9 @@ def _test_backup_and_restore_app(app): assert app+".main" not in user_permission_list()['permissions'] # Restore the app - backup_restore(system=None, name=archives[0], - apps=[app]) + with message(mocker, "restore_complete"): + backup_restore(system=None, name=archives[0], + apps=[app]) assert app_is_installed(app) @@ -593,13 +572,11 @@ def test_restore_archive_with_no_json(mocker): assert "badbackup" in backup_list()["archives"] - mocker.spy(m18n, "n") - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, 'backup_invalid_archive'): backup_restore(name="badbackup", force=True) - m18n.n.assert_any_call('backup_invalid_archive') -def test_backup_binds_are_readonly(monkeypatch): +def test_backup_binds_are_readonly(mocker, monkeypatch): def custom_mount_and_backup(self, backup_manager): self.manager = backup_manager @@ -620,4 +597,5 @@ def test_backup_binds_are_readonly(monkeypatch): custom_mount_and_backup) # Create the backup - backup_create(system=[]) + with message(mocker, "backup_created"): + backup_create(system=[]) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index f17313fa1..0f3fb63e0 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -1,19 +1,20 @@ import requests import pytest -from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map +from conftest import message, raiseYunohostError -from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ - user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info +from yunohost.app import app_install, app_remove, app_change_url, app_list, app_map +from yunohost.user import user_list, user_create, user_delete, \ + user_group_list, user_group_delete from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \ permission_create, permission_urls, permission_delete from yunohost.domain import _get_maindomain -from yunohost.utils.error import YunohostError # Get main domain maindomain = _get_maindomain() dummy_password = "test123Ynh" + def clean_user_groups_permission(): for u in user_list()['users']: user_delete(u) @@ -26,6 +27,7 @@ def clean_user_groups_permission(): if any(p.startswith(name) for name in ["wiki", "blog", "site", "permissions_app"]): permission_delete(p, force=True, sync_perm=False) + def setup_function(function): clean_user_groups_permission() @@ -35,6 +37,7 @@ def setup_function(function): permission_create("blog.main", sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") + def teardown_function(function): clean_user_groups_permission() try: @@ -42,12 +45,14 @@ def teardown_function(function): except: pass + @pytest.fixture(autouse=True) def check_LDAP_db_integrity_call(): check_LDAP_db_integrity() yield check_LDAP_db_integrity() + def check_LDAP_db_integrity(): # Here we check that all attributes in all object are sychronized. # Here is the list of attributes per object: @@ -162,7 +167,7 @@ def check_permission_for_apps(): def can_access_webpage(webpath, logged_as=None): webpath = webpath.rstrip("/") - sso_url = "https://"+maindomain+"/yunohost/sso/" + sso_url = "https://" + maindomain + "/yunohost/sso/" # Anonymous access if not logged_as: @@ -183,6 +188,7 @@ def can_access_webpage(webpath, logged_as=None): # If we can't access it, we got redirected to the SSO return not r.url.startswith(sso_url) + # # List functions # @@ -204,8 +210,10 @@ def test_permission_list(): # Create - Remove functions # -def test_permission_create_main(): - permission_create("site.main") + +def test_permission_create_main(mocker): + with message(mocker, "permission_created", permission="site.main"): + permission_create("site.main") res = user_permission_list(full=True)['permissions'] assert "site.main" in res @@ -213,8 +221,9 @@ def test_permission_create_main(): assert set(res['site.main']['corresponding_users']) == set(["alice", "bob"]) -def test_permission_create_extra(): - permission_create("site.test") +def test_permission_create_extra(mocker): + with message(mocker, "permission_created", permission="site.test"): + permission_create("site.test") res = user_permission_list(full=True)['permissions'] assert "site.test" in res @@ -222,8 +231,10 @@ def test_permission_create_extra(): assert "all_users" not in res['site.test']['allowed'] assert res['site.test']['corresponding_users'] == [] -def test_permission_delete(): - permission_delete("wiki.main", force=True) + +def test_permission_delete(mocker): + with message(mocker, "permission_deleted", permission="wiki.main"): + permission_delete("wiki.main", force=True) res = user_permission_list()['permissions'] assert "wiki.main" not in res @@ -232,12 +243,14 @@ def test_permission_delete(): # Error on create - remove function # -def test_permission_create_already_existing(): - with pytest.raises(YunohostError): + +def test_permission_create_already_existing(mocker): + with raiseYunohostError(mocker, "permission_already_exist"): permission_create("wiki.main") -def test_permission_delete_doesnt_existing(): - with pytest.raises(YunohostError): + +def test_permission_delete_doesnt_existing(mocker): + with raiseYunohostError(mocker, "permission_not_found"): permission_delete("doesnt.exist", force=True) res = user_permission_list()['permissions'] @@ -246,8 +259,9 @@ def test_permission_delete_doesnt_existing(): assert "mail.main" in res assert "xmpp.main" in res -def test_permission_delete_main_without_force(): - with pytest.raises(YunohostError): + +def test_permission_delete_main_without_force(mocker): + with raiseYunohostError(mocker, "permission_cannot_remove_main"): permission_delete("blog.main") res = user_permission_list()['permissions'] @@ -259,44 +273,55 @@ def test_permission_delete_main_without_force(): # user side functions -def test_permission_add_group(): - user_permission_update("wiki.main", add="alice") + +def test_permission_add_group(mocker): + with message(mocker, "permission_updated", permission="wiki.main"): + user_permission_update("wiki.main", add="alice") res = user_permission_list(full=True)['permissions'] assert set(res['wiki.main']['allowed']) == set(["all_users", "alice"]) assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"]) -def test_permission_remove_group(): - user_permission_update("blog.main", remove="alice") + +def test_permission_remove_group(mocker): + with message(mocker, "permission_updated", permission="blog.main"): + user_permission_update("blog.main", remove="alice") res = user_permission_list(full=True)['permissions'] assert res['blog.main']['allowed'] == [] assert res['blog.main']['corresponding_users'] == [] -def test_permission_add_and_remove_group(): - user_permission_update("wiki.main", add="alice", remove="all_users") + +def test_permission_add_and_remove_group(mocker): + with message(mocker, "permission_updated", permission="wiki.main"): + user_permission_update("wiki.main", add="alice", remove="all_users") res = user_permission_list(full=True)['permissions'] assert res['wiki.main']['allowed'] == ["alice"] assert res['wiki.main']['corresponding_users'] == ["alice"] -def test_permission_add_group_already_allowed(): - user_permission_update("blog.main", add="alice") + +def test_permission_add_group_already_allowed(mocker): + with message(mocker, "permission_already_allowed", permission="blog.main", group="alice"): + user_permission_update("blog.main", add="alice") res = user_permission_list(full=True)['permissions'] assert res['blog.main']['allowed'] == ["alice"] assert res['blog.main']['corresponding_users'] == ["alice"] -def test_permission_remove_group_already_not_allowed(): - user_permission_update("blog.main", remove="bob") + +def test_permission_remove_group_already_not_allowed(mocker): + with message(mocker, "permission_already_disallowed", permission="blog.main", group="bob"): + user_permission_update("blog.main", remove="bob") res = user_permission_list(full=True)['permissions'] assert res['blog.main']['allowed'] == ["alice"] assert res['blog.main']['corresponding_users'] == ["alice"] -def test_permission_reset(): - # Reset permission - user_permission_reset("blog.main") + +def test_permission_reset(mocker): + with message(mocker, "permission_updated", permission="blog.main"): + user_permission_reset("blog.main") res = user_permission_list(full=True)['permissions'] assert res['blog.main']['allowed'] == ["all_users"] @@ -306,50 +331,62 @@ def test_permission_reset(): # Error on update function # -def test_permission_add_group_that_doesnt_exist(): - with pytest.raises(YunohostError): + +def test_permission_add_group_that_doesnt_exist(mocker): + with raiseYunohostError(mocker, "group_unknown"): user_permission_update("blog.main", add="doesnt_exist") res = user_permission_list(full=True)['permissions'] assert res['blog.main']['allowed'] == ["alice"] assert res['blog.main']['corresponding_users'] == ["alice"] -def test_permission_update_permission_that_doesnt_exist(): - with pytest.raises(YunohostError): + +def test_permission_update_permission_that_doesnt_exist(mocker): + with raiseYunohostError(mocker, "permission_not_found"): user_permission_update("doesnt.exist", add="alice") # Permission url management -def test_permission_add_url(): - permission_urls("blog.main", add=["/testA"]) + +def test_permission_add_url(mocker): + with message(mocker, "permission_updated", permission="blog.main"): + permission_urls("blog.main", add=["/testA"]) res = user_permission_list(full=True)['permissions'] assert res["blog.main"]["urls"] == ["/testA"] -def test_permission_add_another_url(): - permission_urls("wiki.main", add=["/testA"]) + +def test_permission_add_another_url(mocker): + with message(mocker, "permission_updated", permission="wiki.main"): + permission_urls("wiki.main", add=["/testA"]) res = user_permission_list(full=True)['permissions'] assert set(res["wiki.main"]["urls"]) == set(["/", "/testA"]) -def test_permission_remove_url(): - permission_urls("wiki.main", remove=["/"]) + +def test_permission_remove_url(mocker): + with message(mocker, "permission_updated", permission="wiki.main"): + permission_urls("wiki.main", remove=["/"]) res = user_permission_list(full=True)['permissions'] assert res["wiki.main"]["urls"] == [] -def test_permission_add_url_already_added(): + +def test_permission_add_url_already_added(mocker): res = user_permission_list(full=True)['permissions'] assert res["wiki.main"]["urls"] == ["/"] - permission_urls("wiki.main", add=["/"]) + with message(mocker, "permission_update_nothing_to_do"): + permission_urls("wiki.main", add=["/"]) res = user_permission_list(full=True)['permissions'] assert res["wiki.main"]["urls"] == ["/"] -def test_permission_remove_url_not_added(): - permission_urls("wiki.main", remove=["/doesnt_exist"]) + +def test_permission_remove_url_not_added(mocker): + with message(mocker, "permission_update_nothing_to_do"): + permission_urls("wiki.main", remove=["/doesnt_exist"]) res = user_permission_list(full=True)['permissions'] assert res['wiki.main']['urls'] == ["/"] @@ -358,6 +395,7 @@ def test_permission_remove_url_not_added(): # Application interaction # + def test_permission_app_install(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&is_public=0&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) @@ -395,6 +433,7 @@ def test_permission_app_remove(): res = user_permission_list(full=True)['permissions'] assert not any(p.startswith("permissions_app.") for p in res.keys()) + def test_permission_app_change_url(): app_install("./tests/apps/permissions_app_ynh", args="domain=%s&path=%s&admin=%s" % (maindomain, "/urlpermissionapp", "alice"), force=True) @@ -441,6 +480,7 @@ def test_permission_app_propagation_on_ssowat(): assert can_access_webpage(app_webroot+"/admin", logged_as="alice") assert not can_access_webpage(app_webroot+"/admin", logged_as="bob") + def test_permission_legacy_app_propagation_on_ssowat(): # TODO / FIXME : To be actually implemented later .... diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 30bdeb017..695f09477 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -1,22 +1,25 @@ import pytest +from conftest import message, raiseYunohostError + from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ - user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info + user_group_list, user_group_create, user_group_delete, user_group_update from yunohost.domain import _get_maindomain -from yunohost.utils.error import YunohostError from yunohost.tests.test_permission import check_LDAP_db_integrity # Get main domain maindomain = _get_maindomain() + def clean_user_groups(): for u in user_list()['users']: user_delete(u) for g in user_group_list()['groups']: - if g != "all_users": + if g not in ["all_users", "visitors"]: user_group_delete(g) + def setup_function(function): clean_user_groups() @@ -29,9 +32,11 @@ def setup_function(function): user_group_update("dev", add=["alice"]) user_group_update("apps", add=["bob"]) + def teardown_function(function): clean_user_groups() + @pytest.fixture(autouse=True) def check_LDAP_db_integrity_call(): check_LDAP_db_integrity() @@ -42,6 +47,7 @@ def check_LDAP_db_integrity_call(): # List functions # + def test_list_users(): res = user_list()['users'] @@ -49,6 +55,7 @@ def test_list_users(): assert "bob" in res assert "jack" in res + def test_list_groups(): res = user_group_list()['groups'] @@ -65,8 +72,11 @@ def test_list_groups(): # Create - Remove functions # -def test_create_user(): - user_create("albert", "Albert", "Good", "alber@" + maindomain, "test123Ynh") + +def test_create_user(mocker): + + with message(mocker, "user_created"): + user_create("albert", "Albert", "Good", "alber@" + maindomain, "test123Ynh") group_res = user_group_list()['groups'] assert "albert" in user_list()['users'] @@ -74,24 +84,33 @@ def test_create_user(): assert "albert" in group_res['albert']['members'] assert "albert" in group_res['all_users']['members'] -def test_del_user(): - user_delete("alice") + +def test_del_user(mocker): + + with message(mocker, "user_deleted"): + user_delete("alice") group_res = user_group_list()['groups'] assert "alice" not in user_list() assert "alice" not in group_res assert "alice" not in group_res['all_users']['members'] -def test_create_group(): - user_group_create("adminsys") + +def test_create_group(mocker): + + with message(mocker, "group_created", group="adminsys"): + user_group_create("adminsys") group_res = user_group_list()['groups'] assert "adminsys" in group_res assert "members" in group_res['adminsys'].keys() assert group_res["adminsys"]["members"] == [] -def test_del_group(): - user_group_delete("dev") + +def test_del_group(mocker): + + with message(mocker, "group_deleted", group="dev"): + user_group_delete("dev") group_res = user_group_list()['groups'] assert "dev" not in group_res @@ -100,75 +119,94 @@ def test_del_group(): # Error on create / remove function # -def test_create_user_with_mail_address_already_taken(): - with pytest.raises(YunohostError): + +def test_create_user_with_mail_address_already_taken(mocker): + with raiseYunohostError(mocker, "user_creation_failed"): user_create("alice2", "Alice", "White", "alice@" + maindomain, "test123Ynh") -def test_create_user_with_password_too_simple(): - with pytest.raises(YunohostError): + +def test_create_user_with_password_too_simple(mocker): + with raiseYunohostError(mocker, "password_listed"): user_create("other", "Alice", "White", "other@" + maindomain, "12") -def test_create_user_already_exists(): - with pytest.raises(YunohostError): + +def test_create_user_already_exists(mocker): + with raiseYunohostError(mocker, "user_already_exists"): user_create("alice", "Alice", "White", "other@" + maindomain, "test123Ynh") -def test_update_user_with_mail_address_already_taken(): - with pytest.raises(YunohostError): - user_update("bob", add_mailalias="alice@" + maindomain) -def test_del_user_that_does_not_exist(): - with pytest.raises(YunohostError): +def test_update_user_with_mail_address_already_taken(mocker): + with raiseYunohostError(mocker, "user_update_failed"): + user_update("bob", add_mailalias="alice@" + maindomain) + + +def test_del_user_that_does_not_exist(mocker): + with raiseYunohostError(mocker, "user_unknown"): user_delete("doesnt_exist") -def test_create_group_all_users(): + +def test_create_group_all_users(mocker): # Check groups already exist with special group "all_users" - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, "group_already_exist"): user_group_create("all_users") -def test_create_group_already_exists(): + +def test_create_group_already_exists(mocker): # Check groups already exist (regular groups) - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, "group_already_exist"): user_group_create("dev") -def test_del_group_all_users(): - with pytest.raises(YunohostError): + +def test_del_group_all_users(mocker): + with raiseYunohostError(mocker, "group_cannot_be_deleted"): user_group_delete("all_users") -def test_del_group_that_does_not_exist(): - with pytest.raises(YunohostError): + +def test_del_group_that_does_not_exist(mocker): + with raiseYunohostError(mocker, "group_unknown"): user_group_delete("doesnt_exist") # # Update function # -def test_update_user(): - user_update("alice", firstname="NewName", lastname="NewLast") + +def test_update_user(mocker): + with message(mocker, "user_updated"): + user_update("alice", firstname="NewName", lastname="NewLast") info = user_info("alice") assert info['firstname'] == "NewName" assert info['lastname'] == "NewLast" -def test_update_group_add_user(): - user_group_update("dev", add=["bob"]) + +def test_update_group_add_user(mocker): + with message(mocker, "group_updated", group="dev"): + user_group_update("dev", add=["bob"]) group_res = user_group_list()['groups'] assert set(group_res['dev']['members']) == set(["alice", "bob"]) -def test_update_group_add_user_already_in(): - user_group_update("apps", add=["bob"]) + +def test_update_group_add_user_already_in(mocker): + with message(mocker, "group_user_already_in_group", user="bob", group="apps"): + user_group_update("apps", add=["bob"]) group_res = user_group_list()['groups'] assert group_res['apps']['members'] == ["bob"] -def test_update_group_remove_user(): - user_group_update("apps", remove=["bob"]) + +def test_update_group_remove_user(mocker): + with message(mocker, "group_updated", group="apps"): + user_group_update("apps", remove=["bob"]) group_res = user_group_list()['groups'] assert group_res['apps']['members'] == [] -def test_update_group_remove_user_not_already_in(): - user_group_update("apps", remove=["jack"]) + +def test_update_group_remove_user_not_already_in(mocker): + with message(mocker, "group_user_not_in_group", user="jack", group="apps"): + user_group_update("apps", remove=["jack"]) group_res = user_group_list()['groups'] assert group_res['apps']['members'] == ["bob"] @@ -177,29 +215,33 @@ def test_update_group_remove_user_not_already_in(): # Error on update functions # -def test_update_user_that_doesnt_exist(): - with pytest.raises(YunohostError): + +def test_update_user_that_doesnt_exist(mocker): + with raiseYunohostError(mocker, "user_unknown"): user_update("doesnt_exist", firstname="NewName", lastname="NewLast") -def test_update_group_that_doesnt_exist(): - # Check groups not found - with pytest.raises(YunohostError): + +def test_update_group_that_doesnt_exist(mocker): + with raiseYunohostError(mocker, "group_unknown"): user_group_update("doesnt_exist", add=["alice"]) -def test_update_group_all_users_manually(): - with pytest.raises(YunohostError): + +def test_update_group_all_users_manually(mocker): + with raiseYunohostError(mocker, "group_cannot_edit_all_users"): user_group_update("all_users", remove=["alice"]) assert "alice" in user_group_list()["groups"]["all_users"]["members"] -def test_update_group_primary_manually(): - with pytest.raises(YunohostError): + +def test_update_group_primary_manually(mocker): + with raiseYunohostError(mocker, "group_cannot_edit_primary_group"): user_group_update("alice", remove=["alice"]) + assert "alice" in user_group_list()["groups"]["alice"]["members"] -def test_update_group_add_user_that_doesnt_exist(): - # Check add bad user in group - with pytest.raises(YunohostError): + +def test_update_group_add_user_that_doesnt_exist(mocker): + with raiseYunohostError(mocker, "user_unknown"): user_group_update("dev", add=["doesnt_exist"]) assert "doesnt_exist" not in user_group_list()["groups"]["dev"]["members"] diff --git a/src/yunohost/utils/error.py b/src/yunohost/utils/error.py index aeffabcf0..75cf35093 100644 --- a/src/yunohost/utils/error.py +++ b/src/yunohost/utils/error.py @@ -27,12 +27,14 @@ class YunohostError(MoulinetteError): """ Yunohost base exception - + The (only?) main difference with MoulinetteError being that keys are translated via m18n.n (namespace) instead of m18n.g (global?) """ def __init__(self, key, raw_msg=False, *args, **kwargs): + self.key = key # Saving the key is useful for unit testing + self.kwargs = kwargs # Saving the key is useful for unit testing if raw_msg: msg = key else: From 50fbfb0372f4478cf373ea67b883cbaa1ba9c6c7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 24 Sep 2019 23:18:05 +0200 Subject: [PATCH 172/299] Fucking ugly workaround for the goddamn dependency nighmare from sury djeezus kraiste --- data/helpers.d/apt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index b4bf60c1f..f5590b38d 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -218,6 +218,27 @@ ynh_install_app_dependencies () { fi local dep_app=${app//_/-} # Replace all '_' by '-' + # + # Epic ugly hack to fix the goddamn dependency nightmare of sury + # Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective + # https://github.com/YunoHost/issues/issues/1407 + # + # If we require to install php dependency + if echo $dependencies | grep -q 'php'; + then + # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33+1 on debian) + if dpkg --list | grep php | grep -q "7.0.33-10" + then + # And sury ain't already installed + if ! grep -nrq "sury" /etc/apt/sources.list* + then + # Re-add sury + echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury.list + wget -O /etc/apt/trusted.gpg.d/sury.gpg https://packages.sury.org/php/apt.gpg + fi + fi + fi + cat > /tmp/${dep_app}-ynh-deps.control << EOF # Make a control file for equivs-build Section: misc Priority: optional From ea9a93cec097a14f043b9ecc295ea23b7d14629a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 25 Sep 2019 00:07:50 +0200 Subject: [PATCH 173/299] Update data/actionsmap/yunohost.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Allan Nordhøy --- data/actionsmap/yunohost.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 97489a841..22037f05f 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -299,7 +299,7 @@ user: ### user_permission_update() update: - action_help: Grant / remove permissions to groups or users + action_help: Manage group or user permissions api: POST /users/permissions/ arguments: permission: From 35bfe97d50b1a6bb44f16ccddaf376723e304c57 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 25 Sep 2019 22:08:47 +0200 Subject: [PATCH 174/299] Copy pasta typo : all_users -> visitors Co-Authored-By: Josue-T --- src/yunohost/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index 581354f77..312b131b9 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -678,7 +678,7 @@ def user_group_update(operation_logger, groupname, add=None, remove=None, force= if not force: if groupname == "all_users": raise YunohostError('group_cannot_edit_all_users') - elif groupname == "all_users": + elif groupname == "visitors": raise YunohostError('group_cannot_edit_visitors') elif groupname in existing_users: raise YunohostError('group_cannot_edit_primary_group', group=groupname) From 24cc26d85a35ccd38c5af369e7bd9c739fca441c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 14:23:01 +0200 Subject: [PATCH 175/299] Support logfiles not ending with .log in logrotate ... --- data/helpers.d/logrotate | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/data/helpers.d/logrotate b/data/helpers.d/logrotate index 47ce46cf6..82cdee6a5 100644 --- a/data/helpers.d/logrotate +++ b/data/helpers.d/logrotate @@ -40,10 +40,13 @@ ynh_use_logrotate () { fi if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]; then - if [ "$(echo ${1##*.})" == "log" ]; then # Keep only the extension to check if it's a logfile - local logfile=$1 # In this case, focus logrotate on the logfile + # If the given logfile parameter already exists as a file, or if it ends up with ".log", + # we just want to manage a single file + if [ -f "$1" ] || [ "$(echo ${1##*.})" == "log" ]; then + local logfile=$1 + # Otherwise we assume we want to manage a directory and all its .log file inside else - local logfile=$1/*.log # Else, uses the directory and all logfile into it. + local logfile=$1/*.log fi fi # LEGACY CODE @@ -54,7 +57,7 @@ ynh_use_logrotate () { fi if [ -n "$logfile" ] then - if [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile + if [ ! -f "$1" ] && [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it. fi else From 274888c79fce9ee52a45a7a82db73fb118505a64 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 16:19:43 +0200 Subject: [PATCH 176/299] Better handling of remove failure (and in particular, catch manual interrupts to still perform the rest of the cleaning) --- src/yunohost/app.py | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5a51e57bb..05e1bdf4b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -944,10 +944,21 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu env=env_dict_remove) operation_logger_remove.start() - remove_retcode = hook_exec( - os.path.join(extracted_app_folder, 'scripts/remove'), - args=[app_instance_name], env=env_dict_remove - )[0] + # Try to remove the app + try: + remove_retcode = hook_exec( + os.path.join(extracted_app_folder, 'scripts/remove'), + args=[app_instance_name], env=env_dict_remove + )[0] + # Here again, calling hook_exec could failed miserably, or get + # manually interrupted (by mistake or because script was stuck) + # In that case we still want to proceed with the rest of the + # removal (permissions, /etc/yunohost/apps/{app} ...) + except (KeyboardInterrupt, EOFError, Exception): + remove_retcode = -1 + import traceback + logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + # Remove all permission in LDAP result = ldap.search(base='ou=permission,dc=yunohost,dc=org', filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) @@ -1057,11 +1068,24 @@ def app_remove(operation_logger, app): operation_logger.extra.update({'env': env_dict}) operation_logger.flush() - if hook_exec('/tmp/yunohost_remove/scripts/remove', args=args_list, - env=env_dict)[0] == 0: - logger.success(m18n.n('app_removed', app=app)) + try: + ret = hook_exec('/tmp/yunohost_remove/scripts/remove', + args=args_list, + env=env_dict)[0] + # Here again, calling hook_exec could failed miserably, or get + # manually interrupted (by mistake or because script was stuck) + # In that case we still want to proceed with the rest of the + # removal (permissions, /etc/yunohost/apps/{app} ...) + except (KeyboardInterrupt, EOFError, Exception): + ret = -1 + import traceback + logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + if ret == 0: + logger.success(m18n.n('app_removed', app=app)) hook_callback('post_app_remove', args=args_list, env=env_dict) + else: + logger.warning(m18n.n('app_not_properly_removed', app=app)) if os.path.exists(app_setting_path): shutil.rmtree(app_setting_path) From 47bdfd8654548d1ddde537fa1d8cf1594a860265 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 16:21:22 +0200 Subject: [PATCH 177/299] Clarify the handling of install script failures... --- locales/en.json | 1 + src/yunohost/app.py | 37 +++++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/locales/en.json b/locales/en.json index 61fdcfa9b..a182c7559 100644 --- a/locales/en.json +++ b/locales/en.json @@ -22,6 +22,7 @@ "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", + "app_install_failed": "Could not install {app}", "app_location_already_used": "The app '{app}' is already installed in ({path})", "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 05e1bdf4b..af6c5d522 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -908,29 +908,42 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu permission_add(app=app_instance_name, permission="main", sync_perm=False) # Execute the app install script - install_retcode = 1 + install_failed = True try: install_retcode = hook_exec( os.path.join(extracted_app_folder, 'scripts/install'), args=args_list, env=env_dict )[0] + # "Common" app install failure : the script failed and returned exit code != 0 + install_failed = (install_retcode != 0) + if install_failed: + error = m18n.n('unexpected_error', error='shell command return code: %s' % install_retcode) + logger.exception(error) + operation_logger.error(error) + # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): - install_retcode = -1 - except Exception: + error = m18n.n('operation_interrupted') + logger.exception(error) + operation_logger.error(error) + # Something wrong happened in Yunohost's code (most probably hook_exec) + except Exception as e : import traceback - logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + error = m18n.n('unexpected_error', error=u"\n" + traceback.format_exc()) + logger.exception(error) + operation_logger.error(error) finally: + # Whatever happened (install success or failure) we check if it broke the system + # and warn the user about it try: broke_the_system = False _assert_system_is_sane_for_app(manifest, "post") except Exception as e: broke_the_system = True - error_msg = operation_logger.error(str(e)) + logger.exception(str(e)) + operation_logger.error(str(e)) - if install_retcode != 0: - error_msg = operation_logger.error(m18n.n('unexpected_error', error='shell command return code: %s' % install_retcode)) - - if install_retcode != 0 or broke_the_system: + # If the install failed or broke the system, we remove it + if install_failed or broke_the_system: if not no_remove_on_failure: # Setup environment for remove script env_dict_remove = {} @@ -985,11 +998,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu app_ssowatconf() - if install_retcode == -1: - msg = m18n.n('operation_interrupted') + " " + error_msg - raise YunohostError(msg, raw_msg=True) - msg = error_msg - raise YunohostError(msg, raw_msg=True) + raise YunohostError("app_install_failed", app=app_id) # Clean hooks and add new ones hook_remove(app_instance_name) From 9331f44b343ae192bd86ec5ccf87f855bee7ba16 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 16:33:15 +0200 Subject: [PATCH 178/299] This message about shell command return code is too technical and uninformative. Let's explain what happen, which is that some error occured inside the install script (and details are in the debug log). --- locales/en.json | 1 + src/yunohost/app.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index a182c7559..ba0d7e3cd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -23,6 +23,7 @@ "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", "app_install_failed": "Could not install {app}", + "app_install_script_failed": "An error occured inside the app installation script.", "app_location_already_used": "The app '{app}' is already installed in ({path})", "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index af6c5d522..57df36713 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -917,7 +917,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # "Common" app install failure : the script failed and returned exit code != 0 install_failed = (install_retcode != 0) if install_failed: - error = m18n.n('unexpected_error', error='shell command return code: %s' % install_retcode) + error = m18n.n('app_install_script_failed') logger.exception(error) operation_logger.error(error) # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception From ccc49a2b2824d8aedfb642e4e9e273fa85431eac Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 16:34:14 +0200 Subject: [PATCH 179/299] Simplify that indentation madness --- src/yunohost/app.py | 90 +++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 57df36713..f2eea5646 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -944,53 +944,57 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # If the install failed or broke the system, we remove it if install_failed or broke_the_system: - if not no_remove_on_failure: - # Setup environment for remove script - env_dict_remove = {} - env_dict_remove["YNH_APP_ID"] = app_id - env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name - env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) - # Execute remove script - operation_logger_remove = OperationLogger('remove_on_failed_install', - [('app', app_instance_name)], - env=env_dict_remove) - operation_logger_remove.start() + # This option is meant for packagers to debug their apps more easily + if no_remove_on_failure: + raise YunohostError("The installation of %s failed, but was not cleaned up as requested by --no-remove-on-failure." % app_id, raw_msg=True) - # Try to remove the app + # Setup environment for remove script + env_dict_remove = {} + env_dict_remove["YNH_APP_ID"] = app_id + env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name + env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) + + # Execute remove script + operation_logger_remove = OperationLogger('remove_on_failed_install', + [('app', app_instance_name)], + env=env_dict_remove) + operation_logger_remove.start() + + # Try to remove the app + try: + remove_retcode = hook_exec( + os.path.join(extracted_app_folder, 'scripts/remove'), + args=[app_instance_name], env=env_dict_remove + )[0] + # Here again, calling hook_exec could failed miserably, or get + # manually interrupted (by mistake or because script was stuck) + # In that case we still want to proceed with the rest of the + # removal (permissions, /etc/yunohost/apps/{app} ...) + except (KeyboardInterrupt, EOFError, Exception): + remove_retcode = -1 + import traceback + logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) + + # Remove all permission in LDAP + result = ldap.search(base='ou=permission,dc=yunohost,dc=org', + filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) + permission_list = [p['cn'][0] for p in result] + for l in permission_list: + permission_remove(app_instance_name, l.split('.')[0], force=True) + + if remove_retcode != 0: + msg = m18n.n('app_not_properly_removed', + app=app_instance_name) + logger.warning(msg) + operation_logger_remove.error(msg) + else: try: - remove_retcode = hook_exec( - os.path.join(extracted_app_folder, 'scripts/remove'), - args=[app_instance_name], env=env_dict_remove - )[0] - # Here again, calling hook_exec could failed miserably, or get - # manually interrupted (by mistake or because script was stuck) - # In that case we still want to proceed with the rest of the - # removal (permissions, /etc/yunohost/apps/{app} ...) - except (KeyboardInterrupt, EOFError, Exception): - remove_retcode = -1 - import traceback - logger.exception(m18n.n('unexpected_error', error=u"\n" + traceback.format_exc())) - - # Remove all permission in LDAP - result = ldap.search(base='ou=permission,dc=yunohost,dc=org', - filter='(&(objectclass=permissionYnh)(cn=*.%s))' % app_instance_name, attrs=['cn']) - permission_list = [p['cn'][0] for p in result] - for l in permission_list: - permission_remove(app_instance_name, l.split('.')[0], force=True) - - if remove_retcode != 0: - msg = m18n.n('app_not_properly_removed', - app=app_instance_name) - logger.warning(msg) - operation_logger_remove.error(msg) + _assert_system_is_sane_for_app(manifest, "post") + except Exception as e: + operation_logger_remove.error(e) else: - try: - _assert_system_is_sane_for_app(manifest, "post") - except Exception as e: - operation_logger_remove.error(e) - else: - operation_logger_remove.success() + operation_logger_remove.success() # Clean tmp folders shutil.rmtree(app_setting_path) From c0e3d600b264eab6b5f817a9c83154edda892cd1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Sep 2019 17:17:03 +0200 Subject: [PATCH 180/299] If we got fed an app url, extract the name of the app to test if we do know it --- src/yunohost/app.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5a51e57bb..061113b4b 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -797,9 +797,19 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu raw_app_list = app_list(raw=True) if app in raw_app_list or ('@' in app) or ('http://' in app) or ('https://' in app): + + # If we got an app name directly (e.g. just "wordpress"), we gonna test this name if app in raw_app_list: - state = raw_app_list[app].get("state", "notworking") - level = raw_app_list[app].get("level", None) + app_name_to_test = app + # If we got an url like "https://github.com/foo/bar_ynh, we want to + # extract "bar" and test if we know this app + elif ('http://' in app) or ('https://' in app): + app_name_to_test = app.strip("/").split("/")[-1].replace("_ynh","") + + if app_name_to_test in raw_app_list: + + state = raw_app_list[app_name_to_test].get("state", "notworking") + level = raw_app_list[app_name_to_test].get("level", None) confirm = "danger" if state in ["working", "validated"]: if isinstance(level, int) and level >= 3: From a2ecbb9d8bc4e1bd8c08fc6ac4a8bad27e8efacc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Sep 2019 17:38:00 +0200 Subject: [PATCH 181/299] Make the warning spooky for notworking and thirdparty apps ... --- locales/en.json | 4 ++-- src/yunohost/app.py | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/locales/en.json b/locales/en.json index 61fdcfa9b..a4ee33abf 100644 --- a/locales/en.json +++ b/locales/en.json @@ -144,8 +144,8 @@ "certmanager_self_ca_conf_file_not_found": "Could not find configuration file for self-signing authority (file: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Could not parse name of self-signing authority (file: {file:s})", "confirm_app_install_warning": "Warning: This application may work, but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", - "confirm_app_install_danger": "WARNING! This application is still experimental (if not explicitly not working) and it is likely to break your system! You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", - "confirm_app_install_thirdparty": "WARNING! Installing third-party applications may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. Are you willing to take that risk? [{answers:s}] ", + "confirm_app_install_danger": "DANGER! This application 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 break your system... If you are willing to take that risk anyway, type '{answers:s}'", + "confirm_app_install_thirdparty": "DANGER! This application is not part of Yunohost's application catalog. Installing third-party applications may compromise the integrity and security of your system. 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 break your system... If you are willing to take that risk anyway, type '{answers:s}'", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", "diagnosis_debian_version_error": "Could not retrieve the Debian version: {error}", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 061113b4b..c85a017e4 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -789,10 +789,21 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu if confirm is None or force or msettings.get('interface') == 'api': return - answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm, - answers='Y/N')) - if answer.upper() != "Y": - raise YunohostError("aborting") + if confirm in ["danger", "thirdparty"]: + answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm, + answers='Yes, I understand'), + color="red") + if answer != "Yes, I understand": + raise YunohostError("aborting") + + else: + answer = msignals.prompt(m18n.n('confirm_app_install_' + confirm, + answers='Y/N'), + color="yellow") + if answer.upper() != "Y": + raise YunohostError("aborting") + + raw_app_list = app_list(raw=True) From babaf541b6df58143762b40177f7766f2e981776 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Sep 2019 17:42:56 +0200 Subject: [PATCH 182/299] Decent quality is now at least level 5 --- src/yunohost/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c85a017e4..1246b889e 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -823,7 +823,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu level = raw_app_list[app_name_to_test].get("level", None) confirm = "danger" if state in ["working", "validated"]: - if isinstance(level, int) and level >= 3: + if isinstance(level, int) and level >= 5: confirm = None elif isinstance(level, int) and level > 0: confirm = "warning" From 6aebec4a3450920956892b26152b20ee0cbc611a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 27 Sep 2019 20:37:13 +0200 Subject: [PATCH 183/299] Residual .migrate() -> .run() --- src/yunohost/dyndns.py | 2 +- src/yunohost/regenconf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index a35d39736..70817b3fe 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -212,7 +212,7 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, from yunohost.tools import _get_migration_by_name migration = _get_migration_by_name("migrate_to_tsig_sha256") try: - migration.migrate(dyn_host, domain, key) + migration.run(dyn_host, domain, key) except Exception as e: logger.error(m18n.n('migrations_migration_has_failed', exception=e, diff --git a/src/yunohost/regenconf.py b/src/yunohost/regenconf.py index 48129634a..b7a42dd9d 100644 --- a/src/yunohost/regenconf.py +++ b/src/yunohost/regenconf.py @@ -70,7 +70,7 @@ def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run or not os.path.exists(REGEN_CONF_FILE)): from yunohost.tools import _get_migration_by_name migration = _get_migration_by_name("decouple_regenconf_from_services") - migration.migrate() + migration.run() result = {} From 5ff9ff01cf04dd8886f00bbd77bfb532ab87e16f Mon Sep 17 00:00:00 2001 From: nr 458 h Date: Sun, 22 Sep 2019 12:33:25 +0000 Subject: [PATCH 184/299] Translated using Weblate (German) Currently translated at 29.4% (165 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/locales/de.json b/locales/de.json index 10087e12f..62dd97aaf 100644 --- a/locales/de.json +++ b/locales/de.json @@ -4,7 +4,7 @@ "admin_password_change_failed": "Passwort kann nicht geändert werden", "admin_password_changed": "Das Administrator-Kennwort wurde geändert", "app_already_installed": "{app:s} ist schon installiert", - "app_argument_choice_invalid": "Verwende einen der folgenden Werte {choices:s}", + "app_argument_choice_invalid": "Wähle einen der folgenden Werte '{choices:s}' für das Argument '{name:s}'", "app_argument_invalid": "Wähle einen gültigen Wert für das Argument '{name: s}': {error: s}", "app_argument_required": "Argument '{name:s}' wird benötigt", "app_extraction_failed": "Installationsdateien konnten nicht entpackt werden", @@ -20,9 +20,9 @@ "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?", "app_unknown": "Unbekannte App", "app_upgrade_failed": "{app:s} konnte nicht aktualisiert werden", - "app_upgraded": "{app:s} wurde erfolgreich aktualisiert", - "appslist_fetched": "Appliste {appslist:s} wurde erfolgreich heruntergelanden", - "appslist_removed": "Appliste {appslist:s} wurde erfolgreich entfernt", + "app_upgraded": "{app:s} aktualisiert", + "appslist_fetched": "Appliste {appslist:s} wurde erfolgreich gelanden", + "appslist_removed": "Appliste {appslist:s} wurde entfernt", "appslist_retrieve_error": "Entfernte Appliste {appslist:s} kann nicht empfangen werden: {error:s}", "appslist_unknown": "Appliste {appslist:s} ist unbekannt.", "ask_current_admin_password": "Derzeitiges Administrator-Kennwort", @@ -34,20 +34,20 @@ "ask_new_admin_password": "Neues Verwaltungskennwort", "ask_password": "Passwort", "backup_action_required": "Du musst etwas zum Speichern auswählen", - "backup_app_failed": "Konnte keine Sicherung für '{app:s}' erstellen", + "backup_app_failed": "Konnte keine Sicherung für die App '{app:s}' erstellen", "backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden", "backup_archive_hook_not_exec": "Hook '{hook:s}' konnte für diese Datensicherung nicht ausgeführt werden", - "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits", + "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits.", "backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden", "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", "backup_cleaning_failed": "Temporäres Sicherungsverzeichnis konnte nicht geleert werden", "backup_created": "Datensicherung komplett", "backup_creating_archive": "Datensicherung wird erstellt…", "backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden", - "backup_deleted": "Datensicherung wurde entfernt", + "backup_deleted": "Backup wurde entfernt", "backup_extracting_archive": "Entpacke Sicherungsarchiv...", "backup_hook_unknown": "Datensicherungshook '{hook:s}' unbekannt", - "backup_invalid_archive": "Ungültige Datensicherung", + "backup_invalid_archive": "Dies ist kein Backup-Archiv", "backup_nothings_done": "Es gibt keine Änderungen zur Speicherung", "backup_output_directory_forbidden": "Verbotenes Ausgabeverzeichnis. Datensicherung können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden", "backup_output_directory_not_empty": "Ausgabeordner ist nicht leer", @@ -213,7 +213,7 @@ "app_not_properly_removed": "{app:s} wurde nicht ordnungsgemäß entfernt", "service_regenconf_failed": "Konnte die Konfiguration für folgende Dienste nicht neu erzeugen: {services}", "not_enough_disk_space": "Zu wenig freier Speicherplatz unter '{path:s}' verfügbar", - "backup_creation_failed": "Erstellen des Backups fehlgeschlagen", + "backup_creation_failed": "Konnte Backup-Archiv nicht erstellen", "service_conf_up_to_date": "Die Konfiguration für den Dienst '{service}' ist bereits aktuell", "package_not_installed": "Das Paket '{pkgname}' ist nicht installiert", "pattern_positive_number": "Muss eine positive Zahl sein", @@ -277,28 +277,28 @@ "service_regenconf_pending_applying": "Überprüfe ausstehende Konfigurationen, die für den Server '{service}' notwendig sind...", "certmanager_http_check_timeout": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte sich selbst über HTTP mit der öffentlichen IP (Domain {domain:s} mit der IP {ip:s}) zu erreichen. Möglicherweise ist dafür hairpinning oder eine falsch konfigurierte Firewall/Router deines Servers dafür verantwortlich.", "certmanager_couldnt_fetch_intermediate_cert": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte die Teilzertifikate von Let's Encrypt zusammenzusetzen. Die Installation/Erneuerung des Zertifikats wurde abgebrochen - bitte versuche es später erneut.", - "appslist_retrieve_bad_format": "Die empfangene Datei der Appliste {appslist:s} ist ungültig", + "appslist_retrieve_bad_format": "Die geladene Appliste {appslist:s} ist ungültig", "domain_hostname_failed": "Erstellen des neuen Hostnamens fehlgeschlagen", "appslist_name_already_tracked": "Es gibt bereits eine registrierte App-Liste mit Namen {name:s}.", - "appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit dem URL {url:s}.", + "appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit der URL {url:s}.", "appslist_migrating": "Migriere Anwendungsliste {appslist:s} …", "appslist_could_not_migrate": "Konnte Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.", - "appslist_corrupted_json": "Konnte die Anwendungslisten. Es scheint, dass {filename:s} beschädigt ist.", + "appslist_corrupted_json": "Anwendungslisten konnte nicht geladen werden. Es scheint, dass {filename:s} beschädigt ist.", "yunohost_ca_creation_success": "Die lokale Zertifizierungs-Authorität wurde angelegt.", "app_already_installed_cant_change_url": "Diese Application ist bereits installiert. Die URL kann durch diese Funktion nicht modifiziert werden. Überprüfe ob `app changeurl` verfügbar ist.", "app_change_no_change_url_script": "Die Application {app_name:s} unterstützt das anpassen der URL noch nicht. Sie muss gegebenenfalls erweitert werden.", "app_change_url_failed_nginx_reload": "NGINX konnte nicht neu gestartet werden. Hier ist der Output von 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "Die alte und neue domain/url_path sind identisch: ('{domain:s} {path:s}'). Es gibt nichts zu tun.", - "app_already_up_to_date": "{app:s} ist schon aktuell", + "app_already_up_to_date": "{app:s} ist bereits aktuell", "backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt", - "backup_applying_method_tar": "Erstellen des Backup-tar Archives...", + "backup_applying_method_tar": "Erstellen des Backup-tar Archives…", "backup_applying_method_copy": "Kopiere alle Dateien ins Backup…", "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung.", "app_location_unavailable": "Diese URL ist entweder nicht verfügbar oder steht in Konflikt mit den bereits installierten Apps:\n{apps: s}", "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_mount_failed": "Das Einbinden des Backup-Archives ist fehlgeschlagen", - "backup_archive_writing_error": "Die Dateien konnten nicht in der komprimierte Archiv-Backup hinzugefügt werden", + "backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden", "app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}", "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository…", "invalid_url_format": "ungültiges URL Format", @@ -332,15 +332,15 @@ "backup_custom_need_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Braucht ein Einhängen/Verbinden\" (need_mount) ein Fehler aufgetreten", "backup_custom_mount_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Einhängen/Verbinden\" ein Fehler aufgetreten", "backup_custom_backup_error": "Bei der benutzerdefinierten Sicherungsmethode ist beim Arbeitsschritt \"Sicherung\" ein Fehler aufgetreten", - "backup_csv_creation_failed": "Die CSV-Datei, die für zukünftige Wiederherstellungsvorgänge erforderlich ist, kann nicht erstellt werden", + "backup_csv_creation_failed": "Die zur Wiederherstellung erforderliche CSV-Datei kann nicht erstellt werden", "backup_couldnt_bind": "{Src: s} konnte nicht an {dest: s} angebunden werden.", "backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert", "backup_ask_for_copying_if_needed": "Einige Dateien konnten mit der Methode, die es vermeidet vorübergehend Speicherplatz auf dem System zu verschwenden, nicht gesichert werden. Zur Durchführung der Sicherung sollten vorübergehend {size: s} MB verwendet werden. Sind Sie einverstanden?", - "backup_actually_backuping": "Erstelle nun ein Backup-Archiv aus den gesammelten Dateien …", + "backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien …", "ask_path": "Pfad", "ask_new_path": "Neuer Pfad", "ask_new_domain": "Neue Domain", - "apps_permission_restoration_failed": "Die Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} ist fehlgeschlagen", + "apps_permission_restoration_failed": "Erteilen der Berechtigung '{permission: s}' für die Wiederherstellung der App {app: s} erforderlich", "apps_permission_not_found": "Keine Berechtigung für die installierten Apps gefunden", "app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden", "app_upgrade_app_name": "{App} wird jetzt aktualisiert…", @@ -354,5 +354,7 @@ "aborting": "Breche ab.", "app_action_cannot_be_ran_because_required_services_down": "Diese App erfordert einige Dienste, die derzeit nicht verfügbar sind. Bevor Sie fortfahren, sollten Sie versuchen, die folgenden Dienste neu zu starten (und möglicherweise untersuchen, warum sie nicht verfügbar sind): {services}", "already_up_to_date": "Nichts zu tun. Alles ist bereits auf dem neusten Stand.", - "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen" + "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen", + "app_action_broke_system": "Diese Aktion hat anscheinend diese wichtigen Services gestört: {services}", + "apps_already_up_to_date": "Alle Apps sind bereits aktuell" } From b769111247f0b06fb99d17e86eb82eddc30c450b Mon Sep 17 00:00:00 2001 From: Aksel Kiesling Date: Sun, 22 Sep 2019 14:36:19 +0000 Subject: [PATCH 185/299] Translated using Weblate (German) Currently translated at 29.4% (165 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/de.json b/locales/de.json index 62dd97aaf..f9e9bbdbd 100644 --- a/locales/de.json +++ b/locales/de.json @@ -294,7 +294,7 @@ "backup_applying_method_tar": "Erstellen des Backup-tar Archives…", "backup_applying_method_copy": "Kopiere alle Dateien ins Backup…", "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modufikation. Vielleicht gibt es eine Aktualisierung.", - "app_location_unavailable": "Diese URL ist entweder nicht verfügbar oder steht in Konflikt mit den bereits installierten Apps:\n{apps: s}", + "app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}", "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf…", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_mount_failed": "Das Einbinden des Backup-Archives ist fehlgeschlagen", @@ -356,5 +356,6 @@ "already_up_to_date": "Nichts zu tun. Alles ist bereits auf dem neusten Stand.", "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen", "app_action_broke_system": "Diese Aktion hat anscheinend diese wichtigen Services gestört: {services}", - "apps_already_up_to_date": "Alle Apps sind bereits aktuell" + "apps_already_up_to_date": "Alle Apps sind bereits aktuell", + "backup_copying_to_organize_the_archive": "Kopieren von {size: s} MB, um das Archiv zu organisieren" } From bea512c7a6bd453b578ef9e7a244dc0e03a23b4b Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 25 Sep 2019 06:55:50 +0000 Subject: [PATCH 186/299] Translated using Weblate (French) Currently translated at 71.9% (404 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 81 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index c94406a07..5350f3073 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -17,11 +17,11 @@ "app_manifest_invalid": "Manifeste d’application incorrect : {error}", "app_no_upgrade": "Aucune application à mettre à jour", "app_not_correctly_installed": "{app:s} semble être mal installé", - "app_not_installed": "L'application « {app:s} » n’est pas installée. Voici la liste des applications installées: {all_apps}", + "app_not_installed": "Nous n’avons pas trouvé l’application « {app:s} » dans la liste des applications installées: {all_apps}", "app_not_properly_removed": "{app:s} n’a pas été supprimé correctement", "app_package_need_update": "Le paquet de l’application {app} doit être mis à jour pour être en adéquation avec les changements de YunoHost", "app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost", - "app_removed": "{app:s} a été supprimé", + "app_removed": "{app:s} supprimé", "app_requirements_checking": "Vérification des paquets requis pour {app} …", "app_requirements_failed": "Impossible de satisfaire les pré-requis pour {app} : {error}", "app_requirements_unmeet": "Les pré-requis de {app} ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}", @@ -29,8 +29,8 @@ "app_unknown": "Application inconnue", "app_unsupported_remote_type": "Ce type de commande à distance utilisé pour cette application n'est pas supporté", "app_upgrade_failed": "Impossible de mettre à jour {app:s}", - "app_upgraded": "{app:s} a été mis à jour", - "appslist_fetched": "La liste d’applications {appslist:s} a été récupérée", + "app_upgraded": "{app:s} mis à jour", + "appslist_fetched": "La liste d’applications {appslist:s} récupérée", "appslist_removed": "La liste d’applications {appslist:s} a été supprimée", "appslist_retrieve_error": "Impossible de récupérer la liste d’applications distante {appslist:s} : {error:s}", "appslist_unknown": "La liste d’applications {appslist:s} est inconnue.", @@ -93,7 +93,7 @@ "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS a été enlevée", "dyndns_ip_update_failed": "Impossible de mettre à jour l’adresse IP sur le domaine DynDNS", "dyndns_ip_updated": "Votre adresse IP a été mise à jour pour le domaine DynDNS", - "dyndns_key_generating": "La clé DNS est en cours de génération, cela peut prendre un certain temps …", + "dyndns_key_generating": "Génération de la clé DNS ... , cela peut prendre un certain temps.", "dyndns_key_not_found": "Clé DNS introuvable pour le domaine", "dyndns_no_domain_registered": "Aucun domaine n’a été enregistré avec DynDNS", "dyndns_registered": "Le domaine DynDNS a été enregistré", @@ -182,7 +182,7 @@ "restore_running_hooks": "Exécution des scripts de restauration …", "service_add_configuration": "Ajout du fichier de configuration {file:s}", "service_add_failed": "Impossible d’ajouter le service '{service:s}'", - "service_added": "Le service '{service:s}' a été ajouté", + "service_added": "Le service '{service:s}' ajouté", "service_already_started": "Le service '{service:s}' est déjà démarré", "service_already_stopped": "Le service '{service:s}' est déjà arrêté", "service_cmd_exec_failed": "Impossible d’exécuter la commande '{command:s}'", @@ -280,14 +280,14 @@ "certmanager_domain_not_resolved_locally": "Le domaine {domain:s} ne peut être résolu depuis votre serveur YunoHost. Cela peut se produire si vous avez récemment modifié votre enregistrement DNS. Si c'est le cas, merci d’attendre quelques heures qu’il se propage. Si le problème persiste, envisager d’ajouter {domain:s} au fichier /etc/hosts. (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces vérifications.)", "certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l'adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème d’hairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.", "certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Let’s Encrypt. L’installation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.", - "appslist_retrieve_bad_format": "Le fichier récupéré pour la liste d’applications {appslist:s} n’est pas valide", - "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (mais ce n’est pas sûr… peut-être que ça n’en causera pas).", + "appslist_retrieve_bad_format": "Impossible de lire la liste des applications extraites {appslist: s}", + "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (peut-être que ça n’en causera pas).", "yunohost_ca_creation_success": "L’autorité de certification locale a été créée.", - "appslist_name_already_tracked": "Il y a déjà une liste d’applications enregistrée avec le nom {name:s}.", + "appslist_name_already_tracked": "Il y a déjà une liste d’applications enregistrée avec le nom {name:s} existe déjà.", "appslist_url_already_tracked": "Il y a déjà une liste d’applications enregistrée avec l’URL {url:s}.", "appslist_migrating": "Migration de la liste d’applications {appslist:s} …", "appslist_could_not_migrate": "Impossible de migrer la liste {appslist:s} ! Impossible d’exploiter l’URL. L’ancienne tâche programmée a été conservée dans {bkp_file:s}.", - "appslist_corrupted_json": "Impossible de charger la liste d’applications. Il semble que {filename:s} soit corrompu.", + "appslist_corrupted_json": "Impossible de charger la liste d’applications. Il semble que {filename:s} soit endommager.", "app_already_installed_cant_change_url": "Cette application est déjà installée. L’URL ne peut pas être changé simplement par cette fonction. Regardez si cela est disponible avec `app changeurl`.", "app_change_no_change_url_script": "L’application {app_name:s} ne prend pas encore en charge le changement d’URL, vous pourriez avoir besoin de la mettre à jour.", "app_change_url_failed_nginx_reload": "Le redémarrage de Nginx a échoué. Voici la sortie de 'nginx -t' :\n{nginx_errors:s}", @@ -297,13 +297,13 @@ "app_location_unavailable": "Cette URL n’est pas disponible ou est en conflit avec une application existante :\n{apps:s}", "app_already_up_to_date": "{app:s} est déjà à jour", "invalid_url_format": "Format d’URL non valide", - "global_settings_bad_choice_for_enum": "Valeur du paramètre {setting:s} incorrecte. Reçu : {received_type:s}, mais les valeurs possibles sont : {expected_type:s}", + "global_settings_bad_choice_for_enum": "Valeur du paramètre {setting:s} incorrecte. Reçu : {choice:s}, mais les valeurs possibles sont : {available_choices:s}", "global_settings_bad_type_for_setting": "Le type du paramètre {setting:s} est incorrect. Reçu {received_type:s} alors que {expected_type:s} était attendu", "global_settings_cant_open_settings": "Échec de l’ouverture du ficher de configurations car : {reason:s}", "global_settings_cant_serialize_setings": "Échec de sérialisation des données de configurations, cause : {reason:s}", "global_settings_cant_write_settings": "Échec d’écriture du fichier de configurations car : {reason:s}", "global_settings_key_doesnt_exists": "La clef '{settings_key:s}' n’existe pas dans les configurations générales, vous pouvez voir toutes les clefs disponibles en saisissant 'yunohost settings list'", - "global_settings_reset_success": "Super ! Vos configurations précédentes ont été sauvegardées dans {path:s}", + "global_settings_reset_success": "Vos configurations précédentes ont été sauvegardées dans {path:s}", "global_settings_setting_example_bool": "Exemple d’option booléenne", "global_settings_setting_example_int": "Exemple d’option de type entier", "global_settings_setting_example_string": "Exemple d’option de type chaîne", @@ -313,7 +313,7 @@ "service_conf_new_managed_file": "Le fichier de configuration « {conf} » est désormais géré par le service {service}.", "service_conf_file_kept_back": "Le fichier de configuration '{conf}' devait être supprimé par le service {service} mais a été conservé.", "backup_abstract_method": "Cette méthode de sauvegarde n’a pas encore été implémentée", - "backup_applying_method_tar": "Création de l’archive tar de la sauvegarde …", + "backup_applying_method_tar": "Création de l’archive TAR de la sauvegarde …", "backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder …", "backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup…", "backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}' …", @@ -354,17 +354,17 @@ "migrations_current_target": "La cible de migration est {}", "migrations_error_failed_to_load_migration": "ERREUR : échec du chargement de migration {number} {name}", "migrations_forward": "Migration en avant", - "migrations_loading_migration": "Chargement de la migration {number} {name} …", - "migrations_migration_has_failed": "La migration {number} {name} a échoué avec l’exception {exception} : annulation", + "migrations_loading_migration": "Chargement de la migration {id} …", + "migrations_migration_has_failed": "La migration {id} a échoué avec l’exception {exception} : annulation", "migrations_no_migrations_to_run": "Aucune migration à lancer", "migrations_show_currently_running_migration": "Application de la migration {number} {name} …", "migrations_show_last_migration": "La dernière migration appliquée est {}", - "migrations_skip_migration": "Ignorer et passer la migration {number} {name}…", + "migrations_skip_migration": "Ignorer et passer la migration {id}…", "server_shutdown": "Le serveur va éteindre", "server_shutdown_confirm": "Le serveur va être éteint immédiatement, le voulez-vous vraiment ? [{answers:s}]", "server_reboot": "Le serveur va redémarrer", "server_reboot_confirm": "Le serveur va redémarrer immédiatement, le voulez-vous vraiment ? [{answers:s}]", - "app_upgrade_some_app_failed": "Impossible de mettre à jour certaines applications", + "app_upgrade_some_app_failed": "Certaines applications n’ont pas été mises à jour", "ask_path": "Chemin", "dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.", "dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.", @@ -373,12 +373,12 @@ "backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier d’archives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.", "migrate_tsig_end": "La migration à hmac-sha512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", - "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers hmac-sha512 qui est plus sécurisé", - "migrate_tsig_wait": "Attendre 3 minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …", + "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers HMAC-SHA-512 qui est plus sécurisé", + "migrate_tsig_wait": "Attendre trois minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …", "migrate_tsig_wait_2": "2 minutes …", "migrate_tsig_wait_3": "1 minute …", "migrate_tsig_wait_4": "30 secondes …", - "migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine DynDNS, donc aucune migration n’est nécessaire !", + "migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine DynDNS, donc aucune migration n’est nécessaire.", "app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' est obsolète ! Utilisez 'app register-url' en remplacement !", "migration_description_0001_change_cert_group_to_sslcert": "Changement des permissions de groupe des certificats de « metronome » à « ssl-cert »", "migration_description_0002_migrate_to_tsig_sha256": "Amélioration de la sécurité de DynDNS TSIG en utilisant SHA512 au lieu de MD5", @@ -397,10 +397,10 @@ "migration_0003_problematic_apps_warning": "Veuillez noter que des applications possiblement problématiques ont été détectées. Il semble qu’elles n’aient pas été installées depuis une liste d’application ou qu’elles ne soit pas marquées comme « fonctionnelles ». En conséquence, nous ne pouvons pas garantir qu’elles fonctionneront après la mise à niveau : {problematic_apps}", "migration_0003_modified_files": "Veuillez noter que les fichiers suivants ont été détectés comme modifiés manuellement et pourraient être écrasés à la fin de la mise à niveau : {manually_modified_files}", "migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.", - "migrations_to_be_ran_manually": "La migration {number} {name} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans l’interface admin, ou lancer `yunohost tools migrations migrate`.", + "migrations_to_be_ran_manually": "La migration {id} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans l’interface admin, ou lancer `yunohost tools migrations migrate`.", "migrations_need_to_accept_disclaimer": "Pour lancer la migration {number} {name}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer.", "service_description_avahi-daemon": "permet d’atteindre votre serveur via yunohost.local sur votre réseau local", - "service_description_dnsmasq": "gère la résolution des noms de domaine (DNS)", + "service_description_dnsmasq": "Gère la résolution des noms de domaine (DNS)", "service_description_dovecot": "permet aux clients de messagerie d’accéder/récupérer les courriels (via IMAP et POP3)", "service_description_fail2ban": "protège contre les attaques brute-force et autres types d’attaques venant d’Internet", "service_description_glances": "surveille les informations système de votre serveur", @@ -410,19 +410,19 @@ "service_description_nslcd": "gère la connexion en ligne de commande des utilisateurs YunoHost", "service_description_php5-fpm": "exécute des applications écrites en PHP avec nginx", "service_description_postfix": "utilisé pour envoyer et recevoir des courriels", - "service_description_redis-server": "une base de données spécialisée utilisée pour l’accès rapide aux données, les files d’attentes et la communication entre les programmes", + "service_description_redis-server": "Une base de données spécialisée utilisée pour l’accès rapide aux données, les files d’attentes et la communication entre les programmes", "service_description_rmilter": "vérifie divers paramètres dans les courriels", "service_description_rspamd": "filtre le pourriel, et d’autres fonctionnalités liées au courriel", "service_description_slapd": "stocke les utilisateurs, domaines et leurs informations liées", "service_description_ssh": "vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)", "service_description_yunohost-api": "permet les interactions entre l’interface web de YunoHost et le système", - "service_description_yunohost-firewall": "gère l'ouverture et la fermeture des ports de connexion aux services", + "service_description_yunohost-firewall": "Gère l'ouverture et la fermeture des ports de connexion aux services", "experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas l’utiliser à moins que vous ne sachiez ce que vous faites.", - "log_corrupted_md_file": "Le fichier yaml de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}", + "log_corrupted_md_file": "Le fichier YAML de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}", "log_category_404": "Le journal de la catégorie '{category}' n’existe pas", "log_link_to_log": "Journal historisé complet de cette opération : ' {desc} '", "log_help_to_get_log": "Pour voir le journal historisé de cette opération '{desc}', utilisez la commande 'yunohost log display {name}'", - "log_link_to_failed_log": "L’opération '{desc}' a échouée ! Pour avoir de l’aide, merci de fournir le journal historisé complet de l’opération en cliquant ici", + "log_link_to_failed_log": "L’opération '{desc}' a échouée ! Pour avoir de l’aide, merci cliqué ici pour avoir de l'aide", "backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge php7, vos applications php pourraient ne pas être restaurées (reason: {error:s})", "log_help_to_get_failed_log": "L’opération '{desc}' a échouée ! Pour avoir de l’aide, merci de partager le journal historisé de cette opération en utilisant la commande 'yunohost log display {name} --share'", "log_does_exists": "Il n’existe pas de journal historisé de l’opération ayant pour nom '{log}', utiliser 'yunohost log list pour voir tous les fichiers de journaux historisés disponibles'", @@ -468,7 +468,7 @@ "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système… :(", "migration_0005_not_enough_space": "Il n’y a pas assez d’espace libre de disponible sur {path} pour lancer maintenant la migration :(.", "recommend_to_add_first_user": "La post-installation est terminée mais YunoHost a besoin d’au moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant la commande 'yunohost user create $nomdutilisateur' ou bien via l’interface d’administration web.", - "service_description_php7.0-fpm": "exécute des applications écrites en PHP avec Nginx", + "service_description_php7.0-fpm": "Exécute des applications écrites en PHP avec NGINX", "users_available": "Liste des utilisateurs disponibles :", "good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe d’administration. Le mot de passe doit comporter au moins 8 caractères – bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).", "good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères tels que : majuscules, minuscules, chiffres et caractères spéciaux.", @@ -481,8 +481,8 @@ "password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux", "password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux", "root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost n’a pas pu le propager au mot de passe root !", - "aborting": "Opération annulée.", - "app_not_upgraded": "Les applications suivantes n'ont pas été mises à jour : {apps}", + "aborting": "Annulation.", + "app_not_upgraded": "L’application {failed_app} n’a pas été mise à jour et par conséquence les applications suivantes n’ont pas été mises à jour : {apps}", "app_start_install": "Installation de l'application {app} …", "app_start_remove": "Suppression de l'application {app} …", "app_start_backup": "Collecte des fichiers devant être sauvegardés pour {app} …", @@ -507,9 +507,9 @@ "migration_0007_cancelled": "YunoHost n'a pas réussi à améliorer la façon dont est gérée votre configuration SSH.", "migration_0007_cannot_restart": "SSH ne peut pas être redémarré après avoir essayé d'annuler la migration numéro 6.", "migration_0008_general_disclaimer": "Pour améliorer la sécurité de votre serveur, il est recommandé de laisser YunoHost gérer la configuration SSH. Votre configuration SSH actuelle diffère de la configuration recommandée. Si vous laissez YunoHost la reconfigurer, la façon dont vous vous connectez à votre serveur via SSH changera comme suit :", - "migration_0008_port": " - vous devrez vous connecter en utilisant le port 22 au lieu de votre actuel port SSH personnalisé. N'hésitez pas à le reconfigurer ;", - "migration_0008_root": " - vous ne pourrez pas vous connecter en tant que root via SSH. Au lieu de cela, vous devrez utiliser l'utilisateur admin ;", - "migration_0008_dsa": " - la clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;", + "migration_0008_port": "- Vous devrez vous connecter en utilisant le port 22 au lieu de votre actuel port SSH personnalisé. N'hésitez pas à le reconfigurer ;", + "migration_0008_root": "- Vous ne pourrez pas vous connecter en tant que root via SSH. Au lieu de cela, vous devrez utiliser l'utilisateur admin ;", + "migration_0008_dsa": "- La clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;", "migration_0008_warning": "Si vous comprenez ces avertissements et que vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.", "migration_0008_no_warning": "Aucun risque majeur n'a été identifié concernant l'écrasement de votre configuration SSH - mais nous ne pouvons pas en être absolument sûrs ;) ! Si vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.", "migrations_success": "Migration {number} {name} réussie !", @@ -549,16 +549,16 @@ "regenconf_failed": "Impossible de régénérer la configuration pour la ou les catégorie(s) : '{categories}'", "regenconf_pending_applying": "Applique la configuration en attente pour la catégorie '{category}' …", "service_regen_conf_is_deprecated": "'yunohost service regen-conf' est obsolète ! Veuillez plutôt utiliser 'yunohost tools regen-conf' à la place.", - "tools_upgrade_at_least_one": "Veuillez spécifier --apps OU --system", + "tools_upgrade_at_least_one": "Veuillez spécifier '--apps' OU '--system'", "tools_upgrade_cant_both": "Impossible de mettre à niveau le système et les applications en même temps", - "tools_upgrade_cant_hold_critical_packages": "Impossibilité de maintenir les paquets critiques...", - "tools_upgrade_regular_packages": "Mise à jour des paquets du système (non liés a YunoHost) ...", + "tools_upgrade_cant_hold_critical_packages": "Impossibilité de maintenir les paquets critiques…", + "tools_upgrade_regular_packages": "Mise à jour des paquets du système (non liés a YunoHost) …", "tools_upgrade_regular_packages_failed": "Impossible de mettre à jour les paquets suivants : {packages_list}", - "tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) ...", + "tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) …", "tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie!\nPressez [Entrée] pour revenir à la ligne de commande", "updating_app_lists": "Récupération des mises à jour des applications disponibles…", "dpkg_lock_not_available": "Cette commande ne peut être lancée maintenant car il semblerai qu'un autre programme utilise déjà le verrou dpkg du gestionnaire de paquets du système", - "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques ...", + "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques …", "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", @@ -566,8 +566,8 @@ "apps_permission_restoration_failed": "L'autorisation '{permission:s}' pour la restauration de l'application {app:s} a échoué", "backup_permission": "Autorisation de sauvegarde pour l'application {app:s}", "edit_group_not_allowed": "Vous n'êtes pas autorisé à modifier le groupe {group:s}", - "error_when_removing_sftpuser_group": "Erreur en essayant de supprimer le groupe sftpusers", - "group_created": "Le groupe '{group}' a créé avec succès", + "error_when_removing_sftpuser_group": "Vous ne pouvez pas supprimer le groupe sftpusers", + "group_created": "Le groupe '{group}' a été créé", "group_deleted": "Le groupe '{group}' a été supprimé", "group_deletion_not_allowed": "Le groupe {group:s} ne peut pas être supprimé manuellement.", "group_info_failed": "L'information sur le groupe a échoué", @@ -588,5 +588,6 @@ "log_user_group_update": "Mettre à jour '{}' pour le groupe", "log_user_permission_add": "Mettre à jour l'autorisation pour '{}'", "log_user_permission_remove": "Mettre à jour l'autorisation pour '{}'", - "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}" + "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}", + "app_action_broke_system": "Cette action semble avoir cassé des services important : {services}" } From 42b85356926f1908785615d3c43ce867d244b1c5 Mon Sep 17 00:00:00 2001 From: htsr Date: Wed, 25 Sep 2019 13:50:42 +0000 Subject: [PATCH 187/299] Translated using Weblate (French) Currently translated at 71.9% (404 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 5350f3073..87f806a21 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -524,7 +524,7 @@ "service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré", "this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`.", "app_action_cannot_be_ran_because_required_services_down": "Cette application requiert certains services qui sont actuellement arrêtés. Avant de continuer, vous devriez essayer de redémarrer les services suivants (et éventuellement rechercher pourquoi ils sont arrêtés) : {services}", - "admin_password_too_long": "Choisissez un mot de passe plus court que 127 caractères", + "admin_password_too_long": "Veuillez choisir un mot de passe de moins de 127 caractères", "log_regen_conf": "Régénérer les configurations du système '{}'", "migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On l'ignore.", "regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'", From 50e9ac0087b94efcb3f9f0fce267c572d3be4b12 Mon Sep 17 00:00:00 2001 From: advocatux Date: Sun, 22 Sep 2019 16:43:19 +0000 Subject: [PATCH 188/299] Translated using Weblate (Spanish) Currently translated at 100.0% (562 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index d219b36e1..ff4a230f9 100644 --- a/locales/es.json +++ b/locales/es.json @@ -606,5 +606,7 @@ "aborting": "Cancelando.", "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar", "app_action_broke_system": "Esta acción parece que ha roto estos importantes servicios: {services}", - "operation_interrupted": "¿Ha sido interrumpida la operación manualmente?" + "operation_interrupted": "¿Ha sido interrumpida la operación manualmente?", + "apps_already_up_to_date": "Todas las aplicaciones están ya actualizadas", + "dyndns_provider_unreachable": "No se puede conectar con el proveedor de Dyndns {provider}: o su YunoHost no está correctamente conectado a Internet o el servidor de dynette está caído." } From ab3c159318a4208f58a8f8a3cd39a4835e4024fc Mon Sep 17 00:00:00 2001 From: htsr Date: Wed, 25 Sep 2019 13:52:15 +0000 Subject: [PATCH 189/299] Translated using Weblate (French) Currently translated at 76.2% (428 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 87f806a21..cdb952387 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -462,7 +462,7 @@ "log_tools_shutdown": "Éteindre votre serveur", "log_tools_reboot": "Redémarrer votre serveur", "mail_unavailable": "Cette adresse de courriel est réservée et doit être automatiquement attribuée au tout premier utilisateur", - "migration_description_0004_php5_to_php7_pools": "Reconfiguration des groupes PHP pour utiliser PHP 7 au lieu de PHP 5", + "migration_description_0004_php5_to_php7_pools": "Reconfigurez les groupes PHP pour utiliser PHP 7 au lieu de PHP 5", "migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6", "migration_0005_postgresql_94_not_installed": "PostgreSQL n’a pas été installé sur votre système. Rien à faire !", "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système… :(", @@ -564,11 +564,11 @@ "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "apps_permission_not_found": "Aucune permission trouvée pour les applications installées", "apps_permission_restoration_failed": "L'autorisation '{permission:s}' pour la restauration de l'application {app:s} a échoué", - "backup_permission": "Autorisation de sauvegarde pour l'application {app:s}", + "backup_permission": "Permission de sauvegarde pour l'application {app:s}", "edit_group_not_allowed": "Vous n'êtes pas autorisé à modifier le groupe {group:s}", "error_when_removing_sftpuser_group": "Vous ne pouvez pas supprimer le groupe sftpusers", "group_created": "Le groupe '{group}' a été créé", - "group_deleted": "Le groupe '{group}' a été supprimé", + "group_deleted": "Suppression du groupe '{group}'", "group_deletion_not_allowed": "Le groupe {group:s} ne peut pas être supprimé manuellement.", "group_info_failed": "L'information sur le groupe a échoué", "group_unknown": "Le groupe {group:s} est inconnu", From d5999a122594bf0772c11ffd16d40405c6c8eff7 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 25 Sep 2019 13:53:22 +0000 Subject: [PATCH 190/299] Translated using Weblate (French) Currently translated at 76.2% (428 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index cdb952387..03c274f5a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -557,7 +557,7 @@ "tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) …", "tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie!\nPressez [Entrée] pour revenir à la ligne de commande", "updating_app_lists": "Récupération des mises à jour des applications disponibles…", - "dpkg_lock_not_available": "Cette commande ne peut être lancée maintenant car il semblerai qu'un autre programme utilise déjà le verrou dpkg du gestionnaire de paquets du système", + "dpkg_lock_not_available": "Cette commande ne peut être exécutée actuellement car un autre programme semble utiliser le verrou de dpkg (gestionnaire de paquets).", "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques …", "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", From d26fa71d97de9d4c92edc3aacaf3f826871d45d5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Holcroft Date: Wed, 25 Sep 2019 19:14:11 +0000 Subject: [PATCH 191/299] Translated using Weblate (French) Currently translated at 76.2% (428 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 03c274f5a..54ea1db46 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -46,13 +46,13 @@ "backup_app_failed": "Impossible de sauvegarder l’application '{app:s}'", "backup_archive_app_not_found": "L’application '{app:s}' n’a pas été trouvée dans l’archive de la sauvegarde", "backup_archive_hook_not_exec": "Le script « {hook:s} » n'a pas été exécuté dans cette sauvegarde", - "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà", + "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà.", "backup_archive_name_unknown": "L’archive locale de sauvegarde nommée '{name:s}' est inconnue", - "backup_archive_open_failed": "Impossible d’ouvrir l’archive de sauvegarde", + "backup_archive_open_failed": "Impossible d’ouvrir l’archive de la sauvegarde", "backup_cleaning_failed": "Impossible de nettoyer le dossier temporaire de sauvegarde", "backup_created": "Sauvegarde terminée", "backup_creating_archive": "Création de l’archive de sauvegarde …", - "backup_creation_failed": "Impossible de créer la sauvegarde", + "backup_creation_failed": "Impossible de créer l'archive de la sauvegarde", "backup_delete_error": "Impossible de supprimer '{path:s}'", "backup_deleted": "La sauvegarde a été supprimée", "backup_extracting_archive": "Extraction de l’archive de sauvegarde …", @@ -259,8 +259,8 @@ "certmanager_error_no_A_record": "Aucun enregistrement DNS 'A' n’a été trouvé pour {domain:s}. Vous devez faire pointer votre nom de domaine vers votre machine pour être en mesure d’installer un certificat Let’s Encrypt ! (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", "certmanager_domain_dns_ip_differs_from_public_ip": "L’enregistrement DNS 'A' du domaine {domain:s} est différent de l’adresse IP de ce serveur. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (quelques vérificateur de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", "certmanager_cannot_read_cert": "Quelque chose s’est mal passé lors de la tentative d’ouverture du certificat actuel pour le domaine {domain:s} (fichier : {file:s}), la cause est : {reason:s}", - "certmanager_cert_install_success_selfsigned": "Installation avec succès d’un certificat auto-signé pour le domaine {domain:s} !", - "certmanager_cert_install_success": "Installation avec succès d’un certificat Let’s Encrypt pour le domaine {domain:s} !", + "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »", + "certmanager_cert_install_success": "Le certificat Let’s Encrypt est maintenant installé pour le domaine « {domain:s} »", "certmanager_cert_renew_success": "Renouvellement avec succès d’un certificat Let’s Encrypt pour le domaine {domain:s} !", "certmanager_old_letsencrypt_app_detected": "\nYunoHost a détecté que l’application « letsencrypt » est installé, ce qui est en conflit avec les nouvelles fonctionnalités de gestion intégrée de certificats dans YunoHost. Si vous souhaitez utiliser ces nouvelles fonctionnalités intégrées, veuillez lancer les commandes suivantes pour migrer votre installation :\n\n yunohost app remove letsencrypt\n yunohost domain cert-install\n\nN.B. : cela tentera de réinstaller les certificats de tous les domaines avec un certificat Let's Encrypt ou ceux auto-signés", "certmanager_cert_signing_failed": "La signature du nouveau certificat a échoué", @@ -312,8 +312,8 @@ "global_settings_unknown_setting_from_settings_file": "Clé inconnue dans les paramètres : '{setting_key:s}', rejet de cette clé et sauvegarde de celle-ci dans /etc/yunohost/unkown_settings.json", "service_conf_new_managed_file": "Le fichier de configuration « {conf} » est désormais géré par le service {service}.", "service_conf_file_kept_back": "Le fichier de configuration '{conf}' devait être supprimé par le service {service} mais a été conservé.", - "backup_abstract_method": "Cette méthode de sauvegarde n’a pas encore été implémentée", - "backup_applying_method_tar": "Création de l’archive TAR de la sauvegarde …", + "backup_abstract_method": "Cette méthode de sauvegarde reste à implémenter", + "backup_applying_method_tar": "Création de l’archive TAR de la sauvegarde…", "backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder …", "backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup…", "backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}' …", @@ -324,20 +324,20 @@ "backup_borg_not_implemented": "La méthode de sauvegarde Borg n’est pas encore implémentée", "backup_cant_mount_uncompress_archive": "Impossible de monter en lecture seule le dossier de l’archive décompressée", "backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser l’archive", - "backup_csv_creation_failed": "Impossible de créer le fichier CSV nécessaire aux opérations futures de restauration", + "backup_csv_creation_failed": "Impossible de créer le fichier CSV nécessaire à la restauration", "backup_csv_addition_failed": "Impossible d’ajouter des fichiers à sauvegarder dans le fichier CSV", "backup_custom_need_mount_error": "Échec de la méthode de sauvegarde personnalisée à l’étape 'need_mount'", "backup_custom_backup_error": "Échec de la méthode de sauvegarde personnalisée à l’étape 'backup'", "backup_custom_mount_error": "Échec de la méthode de sauvegarde personnalisée à l’étape 'mount'", - "backup_no_uncompress_archive_dir": "Le dossier de l’archive décompressée n’existe pas", - "backup_method_tar_finished": "L’archive tar de la sauvegarde a été créée", + "backup_no_uncompress_archive_dir": "Ce dossier d’archive décompressée n’existe pas", + "backup_method_tar_finished": "L’archive TAR de la sauvegarde a été créée", "backup_method_copy_finished": "La copie de la sauvegarde est terminée", "backup_method_borg_finished": "La sauvegarde dans Borg est terminée", "backup_method_custom_finished": "La méthode de sauvegarde personnalisée '{method:s}' est terminée", "backup_system_part_failed": "Impossible de sauvegarder la partie '{part:s}' du système", - "backup_unable_to_organize_files": "Impossible d’organiser les fichiers dans l’archive avec la méthode rapide", + "backup_unable_to_organize_files": "Impossible d’utiliser la méthode rapide pour organiser les fichiers dans l’archive", "backup_with_no_backup_script_for_app": "L’application {app:s} n’a pas de script de sauvegarde. Ignorer.", - "backup_with_no_restore_script_for_app": "L’application {app:s} n’a pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.", + "backup_with_no_restore_script_for_app": "L’application « {app:s} » n’a pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.", "global_settings_cant_serialize_settings": "Échec de la sérialisation des données de paramétrage car : {reason:s}", "restore_removing_tmp_dir_failed": "Impossible de sauvegarder un ancien dossier temporaire", "restore_extracting": "Extraction des fichiers nécessaires depuis l’archive …", @@ -423,7 +423,7 @@ "log_link_to_log": "Journal historisé complet de cette opération : ' {desc} '", "log_help_to_get_log": "Pour voir le journal historisé de cette opération '{desc}', utilisez la commande 'yunohost log display {name}'", "log_link_to_failed_log": "L’opération '{desc}' a échouée ! Pour avoir de l’aide, merci cliqué ici pour avoir de l'aide", - "backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge php7, vos applications php pourraient ne pas être restaurées (reason: {error:s})", + "backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge PHP 7, vous pourriez ne plus pouvoir restaurer vos applications PHP (cause : {error:s})", "log_help_to_get_failed_log": "L’opération '{desc}' a échouée ! Pour avoir de l’aide, merci de partager le journal historisé de cette opération en utilisant la commande 'yunohost log display {name} --share'", "log_does_exists": "Il n’existe pas de journal historisé de l’opération ayant pour nom '{log}', utiliser 'yunohost log list pour voir tous les fichiers de journaux historisés disponibles'", "log_operation_unit_unclosed_properly": "L’opération ne s’est pas terminée correctement", @@ -589,5 +589,7 @@ "log_user_permission_add": "Mettre à jour l'autorisation pour '{}'", "log_user_permission_remove": "Mettre à jour l'autorisation pour '{}'", "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}", - "app_action_broke_system": "Cette action semble avoir cassé des services important : {services}" + "app_action_broke_system": "Cette action semble avoir cassé des services important : {services}", + "apps_already_up_to_date": "Toutes les applications sont déjà à jour", + "app_upgrade_stopped": "La mise à jour de toutes les applications a été arrêtée afin d’éviter d’éventuels dommages dus à l’échec de la mise à jour de l’application précédente" } From 81bb7bffd90994ae36f9c9795a0163bef5fe8b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20D=C3=B6ring?= Date: Mon, 23 Sep 2019 17:51:54 +0000 Subject: [PATCH 192/299] Translated using Weblate (German) Currently translated at 40.7% (229 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 58 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index f9e9bbdbd..2e3777207 100644 --- a/locales/de.json +++ b/locales/de.json @@ -357,5 +357,61 @@ "admin_password_too_long": "Bitte ein Passwort kürzer als 127 Zeichen wählen", "app_action_broke_system": "Diese Aktion hat anscheinend diese wichtigen Services gestört: {services}", "apps_already_up_to_date": "Alle Apps sind bereits aktuell", - "backup_copying_to_organize_the_archive": "Kopieren von {size: s} MB, um das Archiv zu organisieren" + "backup_copying_to_organize_the_archive": "Kopieren von {size: s} MB, um das Archiv zu organisieren", + "app_upgrade_stopped": "Das Upgrade aller Anwendungen wurde gestoppt, um mögliche Schäden zu vermeiden, da das Upgrade der vorherigen Anwendung fehlgeschlagen ist", + "group_already_disallowed": "Die Gruppe '{group:s}' hat bereits die Berechtigungen '{permission:s}' für die App '{app:s}' deaktiviert", + "global_settings_setting_security_ssh_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den SSH-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)", + "group_deleted": "Gruppe '{group}' gelöscht", + "group_deletion_failed": "Kann Gruppe '{group}' nicht löschen", + "group_deletion_not_allowed": "Die Gruppe {group:s} kann nicht manuell gelöscht werden.", + "dyndns_provider_unreachable": "Dyndns-Anbieter {provider} kann nicht erreicht werden: Entweder ist dein YunoHost nicht korrekt mit dem Internet verbunden oder der Dynette-Server ist ausgefallen.", + "group_already_allowed": "Gruppe '{group:s}' hat bereits die Berechtigung '{permission:s}' für die App '{app:s}' eingeschaltet", + "group_name_already_exist": "Gruppe {name:s} existiert bereits", + "group_created": "Gruppe '{group}' angelegt", + "group_creation_failed": "Kann Gruppe '{group}' nicht anlegen", + "group_unknown": "Die Gruppe '{group:s}' ist unbekannt", + "group_updated": "Gruppe '{group:s}' erneuert", + "group_update_failed": "Kann Gruppe '{group:s}' nicht anpassen", + "log_does_exists": "Es gibt kein Operationsprotokoll mit dem Namen'{log}', verwende'yunohost log list', um alle verfügbaren Operationsprotokolle anzuzeigen", + "log_app_removelist": "Entferne eine Applikationsliste", + "log_operation_unit_unclosed_properly": "Die Operationseinheit wurde nicht richtig geschlossen", + "log_app_removeaccess": "Entziehe Zugriff auf '{}'", + "global_settings_setting_security_postfix_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Postfix-Server. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)", + "log_category_404": "Die Log-Kategorie '{category}' existiert nicht", + "global_settings_unknown_type": "Unerwartete Situation, die Einstellung {setting:s} scheint den Typ {unknown_type:s} zu haben, ist aber kein vom System unterstützter Typ.", + "dpkg_is_broken": "Du kannst das gerade nicht tun, weil dpkg/APT (der Systempaketmanager) in einem defekten Zustand zu sein scheint.... Du kannst versuchen, dieses Problem zu lösen, indem du dich über SSH verbindest und `sudo dpkg --configure -a` ausführst.", + "global_settings_unknown_setting_from_settings_file": "Unbekannter Schlüssel in den Einstellungen: '{setting_key:s}', verwerfen und speichern in /etc/yunohost/settings-unknown.json", + "log_link_to_log": "Vollständiges Log dieser Operation: '{desc}'", + "global_settings_setting_example_bool": "Beispiel einer booleschen Option", + "log_app_fetchlist": "Füge eine Applikationsliste hinzu", + "log_help_to_get_log": "Um das Protokoll der Operation '{desc}' anzuzeigen, verwende den Befehl 'yunohost log display {name}'", + "global_settings_setting_security_nginx_compatibility": "Kompatibilität vs. Sicherheitskompromiss für den Webserver NGINX. Beeinflusst die Chiffren (und andere sicherheitsrelevante Aspekte)", + "backup_php5_to_php7_migration_may_fail": "Dein Archiv konnte nicht für PHP 7 konvertiert werden, Du kannst deine PHP-Anwendungen möglicherweise nicht wiederherstellen (Grund: {error:s})", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Erlaubt die Verwendung eines (veralteten) DSA-Hostkeys für die SSH-Daemon-Konfiguration", + "global_settings_setting_example_string": "Beispiel einer string Option", + "log_app_addaccess": "Füge Zugriff auf '{}' hinzu", + "log_app_remove": "Entferne die Anwendung '{}'", + "global_settings_setting_example_int": "Beispiel einer int Option", + "global_settings_cant_open_settings": "Einstellungsdatei konnte nicht geöffnet werden, Grund: {reason:s}", + "global_settings_cant_write_settings": "Einstellungsdatei konnte nicht gespeichert werden, Grund: {reason:s}", + "log_app_install": "Installiere die Anwendung '{}'", + "global_settings_reset_success": "Frühere Einstellungen werden nun auf {path:s} gesichert", + "log_app_upgrade": "Upgrade der Anwendung '{}'", + "good_practices_about_admin_password": "Sie sind nun dabei, ein neues Administrationspasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es sinnvoll ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.", + "log_corrupted_md_file": "Die mit Protokollen verknüpfte YAML-Metadatendatei ist beschädigt: '{md_file}\nFehler: {error}''", + "global_settings_cant_serialize_settings": "Einstellungsdaten konnten nicht serialisiert werden, Grund: {reason:s}", + "log_help_to_get_failed_log": "Der Vorgang'{desc}' konnte nicht abgeschlossen werden. Bitte teile das vollständige Protokoll dieser Operation mit dem Befehl 'yunohost log display {name} --share', um Hilfe zu erhalten", + "backup_no_uncompress_archive_dir": "Dieses unkomprimierte Archivverzeichnis gibt es nicht", + "log_app_change_url": "Ändere die URL der Anwendung '{}'", + "global_settings_setting_security_password_user_strength": "Stärke des Benutzerpassworts", + "good_practices_about_user_password": "Du bist nun dabei, ein neues Benutzerpasswort zu definieren. Das Passwort sollte mindestens 8 Zeichen lang sein - obwohl es ratsam ist, ein längeres Passwort (z.B. eine Passphrase) und/oder eine Variation von Zeichen (Groß- und Kleinschreibung, Ziffern und Sonderzeichen) zu verwenden.", + "global_settings_setting_example_enum": "Beispiel einer enum Option", + "log_link_to_failed_log": "Der Vorgang konnte nicht abgeschlossen werden '{desc}'. Bitte gib das vollständige Protokoll dieser Operation mit Klicken Sie hier an, um Hilfe zu erhalten", + "backup_cant_mount_uncompress_archive": "Das unkomprimierte Archiv konnte nicht als schreibgeschützt gemountet werden", + "backup_csv_addition_failed": "Es konnten keine Dateien zur Sicherung in die CSV-Datei hinzugefügt werden", + "log_app_clearaccess": "Entziehe alle Zugriffe auf '{}'", + "global_settings_setting_security_password_admin_strength": "Stärke des Admin-Passworts", + "global_settings_key_doesnt_exists": "Der Schlüssel'{settings_key:s}' existiert nicht in den globalen Einstellungen, du kannst alle verfügbaren Schlüssel sehen, indem du 'yunohost settings list' ausführst", + "log_app_makedefault": "Mache '{}' zur Standard-Anwendung", + "hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}" } From cb3a2a3adb2d5ccf6194a7aae06dbdf009d11043 Mon Sep 17 00:00:00 2001 From: nr 458 h Date: Tue, 24 Sep 2019 08:33:31 +0000 Subject: [PATCH 193/299] Translated using Weblate (German) Currently translated at 40.7% (229 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/locales/de.json b/locales/de.json index 2e3777207..d03226187 100644 --- a/locales/de.json +++ b/locales/de.json @@ -46,11 +46,11 @@ "backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden", "backup_deleted": "Backup wurde entfernt", "backup_extracting_archive": "Entpacke Sicherungsarchiv...", - "backup_hook_unknown": "Datensicherungshook '{hook:s}' unbekannt", + "backup_hook_unknown": "Der Datensicherungshook '{hook:s}' unbekannt", "backup_invalid_archive": "Dies ist kein Backup-Archiv", - "backup_nothings_done": "Es gibt keine Änderungen zur Speicherung", - "backup_output_directory_forbidden": "Verbotenes Ausgabeverzeichnis. Datensicherung können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden", - "backup_output_directory_not_empty": "Ausgabeordner ist nicht leer", + "backup_nothings_done": "Keine Änderungen zur Speicherung", + "backup_output_directory_forbidden": "Wähle ein anderes Ausgabeverzeichnis. Datensicherung können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden", + "backup_output_directory_not_empty": "Der gewählte Ausgabeordner sollte leer sein", "backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden", "backup_running_app_script": "Datensicherung für App '{app:s}' wurd durchgeführt...", "backup_running_hooks": "Datensicherunghook wird ausgeführt…", @@ -325,7 +325,7 @@ "backup_permission": "Sicherungsberechtigung für App {app: s}", "backup_output_symlink_dir_broken": "Sie haben einen fehlerhaften Symlink anstelle Ihres Archivverzeichnisses '{path: s}'. Möglicherweise haben Sie ein spezielles Setup, um Ihre Daten auf einem anderen Dateisystem zu sichern. In diesem Fall haben Sie wahrscheinlich vergessen, Ihre Festplatte oder Ihren USB-Schlüssel erneut einzuhängen oder anzuschließen.", "backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten…", - "backup_method_tar_finished": "Sicherungs-Tar-Archiv erstellt", + "backup_method_tar_finished": "Tar-Backup-Archiv erstellt", "backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method: s}' beendet", "backup_method_copy_finished": "Sicherungskopie beendet", "backup_method_borg_finished": "Backup in Borg beendet", From e1ca92234aeac72fe2758b76acfdf29361158310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Tue, 24 Sep 2019 00:14:38 +0000 Subject: [PATCH 194/299] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 24.9% (140 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/nb_NO/ --- locales/nb_NO.json | 170 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 0967ef424..4fe62eebb 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1 +1,169 @@ -{} +{ + "aborting": "Avbryter…", + "admin_password": "Administrasjonspassord", + "admin_password_change_failed": "Kan ikke endre passord", + "admin_password_changed": "Administrasjonspassord endret", + "admin_password_too_long": "Velg et passord kortere enn 127 tegn", + "app_already_installed": "{app:s} er allerede installert", + "app_already_up_to_date": "{app:s} er allerede oppdatert", + "app_argument_invalid": "Velg en gydlig verdi for argumentet '{name:s}': {error:s}", + "app_argument_required": "Argumentet '{name:s}' er påkrevd", + "diagnosis_no_apps": "Inget program installert", + "app_id_invalid": "Ugyldig program-ID", + "dyndns_cron_remove_failed": "Kunne ikke fjerne cron-jobb for DynDNS: {error}", + "dyndns_key_not_found": "Fant ikke DNS-nøkkel for domenet", + "app_not_correctly_installed": "{app:s} ser ikke ut til å ha blitt installert på riktig måte", + "dyndns_provider_unreachable": "Kunne ikke nå DynDNS-tilbyder {provider}: Enten har du ikke satt opp din YunoHost rett, dynette-tjeneren er nede, eller du mangler nett.", + "app_not_properly_removed": "{app:s} har ikke blitt fjernet på riktig måte", + "app_package_need_update": "Programmet {app}-pakken må oppdateres for å holde følge med YunoHost sine endringer", + "app_removed": "{app:s} fjernet", + "app_requirements_checking": "Sjekker påkrevde pakker for {app:s}…", + "app_requirements_failed": "Noen krav er ikke oppfylt for {app:s}: {error}", + "app_start_install": "Installerer programmet '{app}'…", + "action_invalid": "Ugyldig handling '{action:s}'", + "app_start_restore": "Gjenoppretter programmet '{app}'…", + "backup_created": "Sikkerhetskopi opprettet", + "backup_archive_name_exists": "En sikkerhetskopi med dette navnet finnes allerede.", + "backup_archive_name_unknown": "Ukjent lokalt sikkerhetskopiarkiv ved navn '{name:s}'", + "already_up_to_date": "Ingenting å gjøre. Alt er oppdatert.", + "backup_method_copy_finished": "Sikkerhetskopi fullført", + "backup_method_tar_finished": "TAR-sikkerhetskopiarkiv opprettet", + "app_action_cannot_be_ran_because_required_services_down": "Dette programmet krever noen tjenester som ikke kjører. Før du fortsetter, du bør prøve å starte følgende tjenester på ny (og antagelig undersøke hvorfor de er nede): {services}", + "app_already_installed_cant_change_url": "Dette programmet er allerede installert. Nettadressen kan ikke endres kun med denne funksjonen. Ta en titt på `app changeurl` hvis den er tilgjengelig.", + "diagnosis_monitor_disk_error": "Kunne ikke holde oppsyn med disker: {error}", + "diagnosis_monitor_system_error": "Kunne ikke holde oppsyn med systemet: {error}", + "domain_exists": "Domenet finnes allerede", + "app_change_url_failed_nginx_reload": "Kunne ikke gjeninnlaste NGINX. Her har du utdataen for 'nginx -t'\n{nginx_errors:s}", + "domains_available": "Tilgjengelige domener:", + "done": "Ferdig", + "downloading": "Laster ned…", + "dyndns_could_not_check_provide": "Kunne ikke sjekke om {provider:s} kan tilby {domain:s}.", + "dyndns_could_not_check_available": "Kunne ikke sjekke om {domain:s} er tilgjengelig på {provider:s}.", + "log_app_removeaccess": "Fjern tilgang til '{}'", + "license_undefined": "udefinert", + "mail_domain_unknown": "Ukjent e-postadresse for domenet '{domain:s}'", + "migrate_tsig_wait_2": "2 min…", + "log_remove_on_failed_restore": "Fjern '{}' etter mislykket gjenoppretting fra sikkerhetskopiarkiv", + "log_letsencrypt_cert_install": "Installer et Let's Encrypt-sertifikat på '{}'-domenet", + "log_permission_update": "Oppdater tilgang '{}' for programmet '{}'", + "log_letsencrypt_cert_renew": "Forny '{}'-Let's Encrypt-sertifikat", + "log_user_update": "Oppdater brukerinfo for '{}'", + "mail_alias_remove_failed": "Kunne ikke fjerne e-postaliaset '{mail:s}'", + "app_action_broke_system": "Denne handlingen ser ut til å ha knekt disse viktige tjenestene: {services}", + "app_argument_choice_invalid": "Bruk én av disse valgene '{choices:s}' for argumentet '{name:s}'", + "app_extraction_failed": "Kunne ikke pakke ut installasjonsfilene", + "app_incompatible": "Programmet {app} er ikke kompatibelt med din YunoHost-versjon", + "app_install_files_invalid": "Disse filene kan ikke installeres", + "app_location_already_used": "Programmet '{app}' er allerede installert i ({path})", + "ask_path": "Sti", + "backup_abstract_method": "Denne sikkerhetskopimetoden er ikke implementert enda", + "backup_actually_backuping": "Oppretter sikkerhetskopiarkiv fra innsamlede filer…", + "backup_app_failed": "Kunne ikke sikkerhetskopiere programmet '{app:s}'", + "backup_applying_method_tar": "Lager TAR-sikkerhetskopiarkiv…", + "backup_archive_app_not_found": "Fant ikke programmet '{app:s}' i sikkerhetskopiarkivet", + "backup_archive_open_failed": "Kunne ikke åpne sikkerhetskopiarkivet", + "app_start_remove": "Fjerner programmet '{app}'…", + "app_start_backup": "Samler inn filer for sikkerhetskopiering for {app}…", + "backup_applying_method_copy": "Kopier alle filer til sikkerhetskopi…", + "backup_borg_not_implemented": "Borg-sikkerhetskopimetoden er ikke implementert enda", + "backup_creation_failed": "Kunne ikke opprette sikkerhetskopiarkiv", + "backup_couldnt_bind": "Kunne ikke binde {src:s} til {dest:s}.", + "backup_csv_addition_failed": "Kunne ikke legge til filer for sikkerhetskopi inn i CSV-filen", + "backup_deleted": "Sikkerhetskopi slettet", + "backup_no_uncompress_archive_dir": "Det finnes ingen slik utpakket arkivmappe", + "backup_delete_error": "Kunne ikke slette '{path:s}'", + "certmanager_domain_unknown": "Ukjent domene '{domain:s}'", + "certmanager_cert_signing_failed": "Kunne ikke signere det nye sertifikatet", + "diagnosis_debian_version_error": "Kunne ikke hente Debian-versjon: {error}", + "diagnosis_kernel_version_error": "Kunne ikke hente kjerneversjon: {error}", + "error_when_removing_sftpuser_group": "Kunne ikke fjerne sftpusers-gruppen", + "executing_command": "Kjører kommendoen '{command:s}'…", + "executing_script": "Kjører skriptet '{script:s}'…", + "extracting": "Pakker ut…", + "edit_group_not_allowed": "Du tillates ikke å redigere gruppen {group:s}", + "log_domain_add": "Legg til '{}'-domenet i systemoppsett", + "log_domain_remove": "Fjern '{}'-domenet fra systemoppsett", + "log_dyndns_subscribe": "Abonner på YunoHost-underdomenet '{}'", + "log_dyndns_update": "Oppdater IP-adressen tilknyttet ditt YunoHost-underdomene '{}'", + "migrate_tsig_wait_3": "1 min…", + "migrate_tsig_wait_4": "30 sekunder…", + "apps_permission_restoration_failed": "Innvilg tilgangen '{permission:s}' for å gjenopprette {app:}", + "apps_permission_not_found": "Fant ingen tilgang for de installerte programmene", + "backup_invalid_archive": "Dette er ikke et sikkerhetskopiarkiv", + "backup_nothings_done": "Ingenting å lagre", + "backup_method_borg_finished": "Sikkerhetskopi inn i Borg fullført", + "field_invalid": "Ugyldig felt '{:s}'", + "firewall_reloaded": "Brannmur gjeninnlastet", + "log_app_removelist": "Fjern en programliste", + "log_app_change_url": "Endre nettadresse for '{}'-programmet", + "log_app_install": "Installer '{}'-programmet", + "log_app_remove": "Fjern '{}'-programmet", + "log_app_upgrade": "Oppgrader '{}'-programmet", + "log_app_makedefault": "Gjør '{}' til forvalgt program", + "log_available_on_yunopaste": "Denne loggen er nå tilgjengelig via {url}", + "log_tools_maindomain": "Gjør '{}' til hoveddomene", + "log_tools_shutdown": "Slå av tjeneren din", + "log_tools_reboot": "Utfør omstart av tjeneren din", + "apps_already_up_to_date": "Alle programmer allerede oppdatert", + "backup_mount_archive_for_restore": "Forbereder arkiv for gjenopprettelse…", + "backup_copying_to_organize_the_archive": "Kopierer {size:s} MB for å organisere arkivet", + "domain_cannot_remove_main": "Kan ikke fjerne hoveddomene. Sett et først", + "domain_cert_gen_failed": "Kunne ikke opprette sertifikat", + "domain_created": "Domene opprettet", + "domain_creation_failed": "Kunne ikke opprette domene", + "domain_dyndns_root_unknown": "Ukjent DynDNS-rotdomene", + "domain_unknown": "Ukjent domene", + "dyndns_cron_installed": "Opprettet cron-jobb for DynDNS", + "dyndns_cron_removed": "Fjernet cron-jobb for DynDNS", + "dyndns_ip_update_failed": "Kunne ikke oppdatere IP-adresse til DynDNS", + "dyndns_ip_updated": "Oppdaterte din IP på DynDNS", + "dyndns_key_generating": "Oppretter DNS-nøkkel… Dette kan ta en stund.", + "dyndns_no_domain_registered": "Inget domene registrert med DynDNS", + "dyndns_registered": "DynDNS-domene registrert", + "global_settings_setting_security_password_admin_strength": "Admin-passordets styrke", + "dyndns_registration_failed": "Kunne ikke registrere DynDNS-domene: {error:s}", + "global_settings_setting_security_password_user_strength": "Brukerpassordets styrke", + "log_app_fetchlist": "Legg til en programliste", + "log_backup_restore_app": "Gjenopprett '{}' fra sikkerhetskopiarkiv", + "log_remove_on_failed_install": "Fjern '{}' etter mislykket installasjon", + "log_permission_add": "Legg til '{}'-tilgangen for programmet '{}'", + "log_permission_remove": "Fjern tilgangen '{}'", + "log_selfsigned_cert_install": "Installer selvsignert sertifikat på '{}'-domenet", + "log_user_delete": "Slett '{}' bruker", + "log_user_group_add": "Legg til '{}' gruppe", + "log_user_group_delete": "Slett '{}' gruppe", + "log_user_group_update": "Oppdater '{}' gruppe", + "log_user_permission_add": "Oppdater '{}' tilgang", + "log_user_permission_remove": "Oppdater '{}' tilgang", + "ldap_init_failed_to_create_admin": "LDAP-igangsettelse kunne ikke opprette admin-bruker", + "ldap_initialized": "LDAP-igangsatt", + "maindomain_changed": "Hoveddomenet er nå endret", + "migration_description_0003_migrate_to_stretch": "Oppgrader systemet til Debian Stretch og YunoHost 3.0", + "app_unknown": "Ukjent program", + "app_upgrade_app_name": "Oppgraderer {app}…", + "app_upgrade_failed": "Kunne ikke oppgradere {app:s}", + "app_upgrade_some_app_failed": "Noen programmer kunne ikke oppgraderes", + "app_upgraded": "{app:s} oppgradert", + "ask_email": "E-postadresse", + "ask_firstname": "Fornavn", + "ask_lastname": "Etternavn", + "ask_list_to_remove": "Liste å fjerne", + "ask_main_domain": "Hoveddomene", + "ask_new_admin_password": "Nytt administrasjonspassord", + "app_upgrade_several_apps": "Følgende programmer vil oppgraderes: {apps}", + "appslist_removed": "{appslist:s}-programliste fjernet", + "appslist_url_already_tracked": "Dette er allerede en registrert programliste med nettadressen {url:s}.", + "ask_current_admin_password": "Nåværende administrasjonspassord", + "appslist_unknown": "Programlisten {appslist:s} er ukjent.", + "ask_new_domain": "Nytt domene", + "ask_new_path": "Ny sti", + "ask_password": "Passord", + "domain_deleted": "Domene slettet", + "domain_deletion_failed": "Kunne ikke slette domene", + "domain_dyndns_already_subscribed": "Du har allerede abonnement på et DynDNS-domene", + "log_category_404": "Loggkategorien '{category}' finnes ikke", + "log_link_to_log": "Full logg for denne operasjonen: '{desc}'", + "log_help_to_get_log": "For å vise loggen for operasjonen '{desc}', bruk kommandoen 'yunohost log display {name}'", + "log_app_clearaccess": "Fjern all tilgang til '{}'", + "log_user_create": "Legg til '{}' bruker" +} From d159f7ff07d20734d50594cf1e8eacde7f77f1ad Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 28 Sep 2019 16:11:44 +0200 Subject: [PATCH 195/299] Misc typo / wording / readability Co-Authored-By: decentral1se --- locales/en.json | 2 +- src/yunohost/app.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/locales/en.json b/locales/en.json index ba0d7e3cd..f9194bb42 100644 --- a/locales/en.json +++ b/locales/en.json @@ -23,7 +23,7 @@ "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", "app_install_failed": "Could not install {app}", - "app_install_script_failed": "An error occured inside the app installation script.", + "app_install_script_failed": "An error occured inside the app installation script", "app_location_already_used": "The app '{app}' is already installed in ({path})", "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f2eea5646..8c7d37f92 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -915,7 +915,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu args=args_list, env=env_dict )[0] # "Common" app install failure : the script failed and returned exit code != 0 - install_failed = (install_retcode != 0) + install_failed = True if install_retcode != 0 else False if install_failed: error = m18n.n('app_install_script_failed') logger.exception(error) @@ -967,7 +967,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu os.path.join(extracted_app_folder, 'scripts/remove'), args=[app_instance_name], env=env_dict_remove )[0] - # Here again, calling hook_exec could failed miserably, or get + # Here again, calling hook_exec could fail miserably, or get # manually interrupted (by mistake or because script was stuck) # In that case we still want to proceed with the rest of the # removal (permissions, /etc/yunohost/apps/{app} ...) @@ -1085,7 +1085,7 @@ def app_remove(operation_logger, app): ret = hook_exec('/tmp/yunohost_remove/scripts/remove', args=args_list, env=env_dict)[0] - # Here again, calling hook_exec could failed miserably, or get + # Here again, calling hook_exec could fail miserably, or get # manually interrupted (by mistake or because script was stuck) # In that case we still want to proceed with the rest of the # removal (permissions, /etc/yunohost/apps/{app} ...) From a98d7f4750da8e62fc1839ea86c5247ee023c440 Mon Sep 17 00:00:00 2001 From: advocatux Date: Thu, 3 Oct 2019 19:28:23 +0000 Subject: [PATCH 196/299] Translated using Weblate (Spanish) Currently translated at 100.0% (562 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index ff4a230f9..02f46652b 100644 --- a/locales/es.json +++ b/locales/es.json @@ -320,7 +320,7 @@ "experimental_feature": "Aviso : esta funcionalidad es experimental y no se considera estable, no debería usarla a menos que sepa lo que está haciendo.", "good_practices_about_user_password": "Está a punto de establecer una nueva contraseña de usuario. La contraseña debería de ser de al menos 8 caracteres, aunque es una buena práctica usar una contraseña más extensa (básicamente una frase) y/o usar caracteres de varias clases (mayúsculas, minúsculas, números y caracteres especiales).", "password_listed": "Esta contraseña es una de las más usadas en el mundo. Elija algo más único.", - "password_too_simple_1": "La contraseña tiene que ser de al menos 8 caracteres de longitud", + "password_too_simple_1": "La contraseña debe tener al menos 8 caracteres de longitud", "password_too_simple_2": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número y caracteres en mayúsculas y minúsculas", "password_too_simple_3": "La contraseña tiene que ser de al menos 8 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales", "password_too_simple_4": "La contraseña tiene que ser de al menos 12 caracteres de longitud e incluir un número, mayúsculas, minúsculas y caracteres especiales", From bd8a91fff7cdc29ed58fb5a649b8e93b6081e7f5 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 2 Oct 2019 05:49:03 +0000 Subject: [PATCH 197/299] Translated using Weblate (French) Currently translated at 100.0% (562 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 136 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 46 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 54ea1db46..8bffec8b2 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -88,15 +88,15 @@ "domain_zone_not_found": "Fichier de zone DNS introuvable pour le domaine {:s}", "done": "Terminé", "downloading": "Téléchargement en cours …", - "dyndns_cron_installed": "La tâche cron pour le domaine DynDNS a été installée", + "dyndns_cron_installed": "La tâche cron pour le domaine DynDNS a été créée", "dyndns_cron_remove_failed": "Impossible de supprimer la tâche cron DynDNS parce que: {error}", - "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS a été enlevée", + "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS enlevée", "dyndns_ip_update_failed": "Impossible de mettre à jour l’adresse IP sur le domaine DynDNS", - "dyndns_ip_updated": "Votre adresse IP a été mise à jour pour le domaine DynDNS", + "dyndns_ip_updated": "Mise à jour de votre IP pour le domaine DynDNS", "dyndns_key_generating": "Génération de la clé DNS ... , cela peut prendre un certain temps.", "dyndns_key_not_found": "Clé DNS introuvable pour le domaine", - "dyndns_no_domain_registered": "Aucun domaine n’a été enregistré avec DynDNS", - "dyndns_registered": "Le domaine DynDNS a été enregistré", + "dyndns_no_domain_registered": "Aucun domaine enregistré avec DynDNS", + "dyndns_registered": "Domaine DynDNS enregistré", "dyndns_registration_failed": "Impossible d’enregistrer le domaine DynDNS : {error:s}", "dyndns_unavailable": "Le domaine {domain:s} est indisponible.", "executing_command": "Exécution de la commande '{command:s}' …", @@ -104,8 +104,8 @@ "extracting": "Extraction en cours …", "field_invalid": "Champ incorrect : '{:s}'", "firewall_reload_failed": "Impossible de recharger le pare-feu", - "firewall_reloaded": "Le pare-feu a été rechargé", - "firewall_rules_cmd_failed": "Certaines règles du pare-feu n’ont pas pu être appliquées. Pour plus d’informations, consultez le journal.", + "firewall_reloaded": "Pare-feu rechargé", + "firewall_rules_cmd_failed": "Certaines règles du pare-feu n’ont pas pu être appliquées. Plus d'info dans le journal de log.", "format_datetime_short": "%d/%m/%Y %H:%M", "hook_argument_missing": "Argument manquant : '{:s}'", "hook_choice_invalid": "Choix incorrect : '{:s}'", @@ -114,16 +114,16 @@ "hook_list_by_invalid": "Propriété invalide pour lister les actions par celle-ci", "hook_name_unknown": "Nom de l'action '{name:s}' inconnu", "installation_complete": "Installation terminée", - "installation_failed": "Échec de l’installation", + "installation_failed": "Quelque chose s'est mal passé lors de l'installation", "ip6tables_unavailable": "Vous ne pouvez pas jouer avec ip6tables ici. Vous êtes soit dans un conteneur, soit votre noyau ne le prend pas en charge", "iptables_unavailable": "Vous ne pouvez pas jouer avec iptables ici. Vous êtes soit dans un conteneur, soit votre noyau ne le prend pas en charge", - "ldap_initialized": "L’annuaire LDAP a été initialisé", + "ldap_initialized": "L’annuaire LDAP initialisé", "license_undefined": "indéfinie", "mail_alias_remove_failed": "Impossible de supprimer l’alias de courriel '{mail:s}'", "mail_domain_unknown": "Le domaine '{domain:s}' pour l'adresse de courriel est inconnu", "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "maindomain_change_failed": "Impossible de modifier le domaine principal", - "maindomain_changed": "Le domaine principal a été modifié", + "maindomain_changed": "Le domaine principal modifié", "monitor_disabled": "La supervision du serveur a été désactivé", "monitor_enabled": "La supervision du serveur a été activé", "monitor_glances_con_failed": "Impossible de se connecter au serveur Glances", @@ -173,7 +173,7 @@ "restore_already_installed_app": "Une application est déjà installée avec l’identifiant '{app:s}'", "restore_app_failed": "Impossible de restaurer l’application '{app:s}'", "restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration", - "restore_complete": "Restauration terminée", + "restore_complete": "Restauré", "restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]", "restore_failed": "Impossible de restaurer le système", "restore_hook_unavailable": "Le script de restauration '{part:s}' n’est pas disponible sur votre système, et ne l'est pas non plus dans l’archive", @@ -218,9 +218,9 @@ "service_unknown": "Le service '{service:s}' est inconnu", "services_configured": "La configuration a été générée avec succès", "show_diff": "Voici les différences :\n{diff:s}", - "ssowat_conf_generated": "La configuration de SSOwat a été générée", - "ssowat_conf_updated": "La configuration de SSOwat a été mise à jour", - "system_upgraded": "Le système a été mis à jour", + "ssowat_conf_generated": "La configuration de SSOwat générée", + "ssowat_conf_updated": "La configuration de SSOwat mise à jour", + "system_upgraded": "Système mis à jour", "system_username_exists": "Ce nom d’utilisateur existe déjà dans les utilisateurs système", "unbackup_app": "L’application '{app:s}' ne sera pas sauvegardée", "unexpected_error": "Une erreur inattendue est survenue : {error}", @@ -232,12 +232,12 @@ "upgrade_complete": "Mise à jour terminée", "upgrading_packages": "Mise à jour des paquets en cours …", "upnp_dev_not_found": "Aucun périphérique compatible UPnP n’a été trouvé", - "upnp_disabled": "UPnP a été désactivé", - "upnp_enabled": "UPnP a été activé", + "upnp_disabled": "UPnP désactivé", + "upnp_enabled": "UPnP activé", "upnp_port_open_failed": "Impossible d’ouvrir les ports UPnP", - "user_created": "L’utilisateur a été créé", + "user_created": "L’utilisateur créé", "user_creation_failed": "Impossible de créer l’utilisateur", - "user_deleted": "L’utilisateur a été supprimé", + "user_deleted": "L’utilisateur supprimé", "user_deletion_failed": "Impossible de supprimer l’utilisateur", "user_home_creation_failed": "Impossible de créer le dossier personnel de l’utilisateur", "user_info_failed": "Impossible de récupérer les informations de l’utilisateur", @@ -246,7 +246,7 @@ "user_updated": "L’utilisateur a été modifié", "yunohost_already_installed": "YunoHost est déjà installé", "yunohost_ca_creation_failed": "Impossible de créer l’autorité de certification", - "yunohost_configured": "YunoHost a été configuré", + "yunohost_configured": "YunoHost maintenant configuré", "yunohost_installing": "L'installation de YunoHost est en cours …", "yunohost_not_installed": "YunoHost n’est pas ou pas correctement installé. Veuillez exécuter 'yunohost tools postinstall'", "certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)", @@ -261,15 +261,15 @@ "certmanager_cannot_read_cert": "Quelque chose s’est mal passé lors de la tentative d’ouverture du certificat actuel pour le domaine {domain:s} (fichier : {file:s}), la cause est : {reason:s}", "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »", "certmanager_cert_install_success": "Le certificat Let’s Encrypt est maintenant installé pour le domaine « {domain:s} »", - "certmanager_cert_renew_success": "Renouvellement avec succès d’un certificat Let’s Encrypt pour le domaine {domain:s} !", + "certmanager_cert_renew_success": "Certificat Let's Encrypt renouvelé pour le domaine '{domain: s}'", "certmanager_old_letsencrypt_app_detected": "\nYunoHost a détecté que l’application « letsencrypt » est installé, ce qui est en conflit avec les nouvelles fonctionnalités de gestion intégrée de certificats dans YunoHost. Si vous souhaitez utiliser ces nouvelles fonctionnalités intégrées, veuillez lancer les commandes suivantes pour migrer votre installation :\n\n yunohost app remove letsencrypt\n yunohost domain cert-install\n\nN.B. : cela tentera de réinstaller les certificats de tous les domaines avec un certificat Let's Encrypt ou ceux auto-signés", - "certmanager_cert_signing_failed": "La signature du nouveau certificat a échoué", + "certmanager_cert_signing_failed": "Impossible de signer le nouveau certificat", "certmanager_no_cert_file": "Impossible de lire le fichier du certificat pour le domaine {domain:s} (fichier : {file:s})", - "certmanager_conflicting_nginx_file": "Impossible de préparer le domaine pour le défi ACME : le fichier de configuration Nginx {filepath:s} est en conflit et doit être préalablement retiré", + "certmanager_conflicting_nginx_file": "Impossible de préparer le domaine pour le défi ACME : le fichier de configuration NGINX {filepath:s} est en conflit et doit être préalablement retiré", "certmanager_hit_rate_limit": "Trop de certificats ont déjà été émis récemment pour ce même ensemble de domaines {domain:s}. Veuillez réessayer plus tard. Lisez https://letsencrypt.org/docs/rate-limits/ pour obtenir plus de détails sur les ratios et limitations", "ldap_init_failed_to_create_admin": "L’initialisation de l'annuaire LDAP n’a pas réussi à créer l’utilisateur admin", - "ssowat_persistent_conf_read_error": "Erreur lors de la lecture de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON", - "ssowat_persistent_conf_write_error": "Erreur lors de la sauvegarde de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON", + "ssowat_persistent_conf_read_error": "Impossible de lire la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON", + "ssowat_persistent_conf_write_error": "Impossible de sauvegarder de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON", "domain_cannot_remove_main": "Impossible de supprimer le domaine principal. Définissez d'abord un nouveau domaine principal", "certmanager_self_ca_conf_file_not_found": "Le fichier de configuration pour l’autorité du certificat auto-signé est introuvable (fichier : {file:s})", "certmanager_unable_to_parse_self_CA_name": "Impossible d’analyser le nom de l’autorité du certificat auto-signé (fichier : {file:s})", @@ -282,7 +282,7 @@ "certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Let’s Encrypt. L’installation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.", "appslist_retrieve_bad_format": "Impossible de lire la liste des applications extraites {appslist: s}", "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (peut-être que ça n’en causera pas).", - "yunohost_ca_creation_success": "L’autorité de certification locale a été créée.", + "yunohost_ca_creation_success": "L’autorité de certification locale créée.", "appslist_name_already_tracked": "Il y a déjà une liste d’applications enregistrée avec le nom {name:s} existe déjà.", "appslist_url_already_tracked": "Il y a déjà une liste d’applications enregistrée avec l’URL {url:s}.", "appslist_migrating": "Migration de la liste d’applications {appslist:s} …", @@ -350,7 +350,7 @@ "domain_dyndns_dynette_is_unreachable": "Impossible de contacter la dynette YunoHost. Soit YunoHost n’est pas correctement connecté à internet, soit le serveur de dynette est en panne. Erreur : {error}", "migrations_backward": "Migration en arrière.", "migrations_bad_value_for_target": "Nombre invalide pour le paramètre target, les numéros de migration sont 0 ou {}", - "migrations_cant_reach_migration_file": "Impossible d’accéder aux fichiers de migrations avec le chemin %s", + "migrations_cant_reach_migration_file": "Impossible d'accéder aux fichiers de migration sur le chemin% s", "migrations_current_target": "La cible de migration est {}", "migrations_error_failed_to_load_migration": "ERREUR : échec du chargement de migration {number} {name}", "migrations_forward": "Migration en avant", @@ -371,7 +371,7 @@ "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine {domain} car il est déjà utilisé par l'application '{other_app}'", "app_upgrade_app_name": "Mise à jour de l’application {app} …", "backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier d’archives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.", - "migrate_tsig_end": "La migration à hmac-sha512 est terminée", + "migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers HMAC-SHA-512 qui est plus sécurisé", "migrate_tsig_wait": "Attendre trois minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …", @@ -393,29 +393,29 @@ "migration_0003_not_jessie": "La distribution Debian actuelle n’est pas Jessie !", "migration_0003_system_not_fully_up_to_date": "Votre système n’est pas complètement à jour. Veuillez mener une mise à jour classique avant de lancer à migration à Stretch.", "migration_0003_still_on_jessie_after_main_upgrade": "Quelque chose s’est mal passé pendant la mise à niveau principale : le système est toujours sur Debian Jessie !? Pour investiguer sur le problème, veuillez regarder les journaux {log}:s …", - "migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si l’équipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus d’informations sur https://yunohost.org/backup ;\n - d’être patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusqu’à quelques heures pour que tout soit à niveau.\n\nEn outre, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). L’ancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence !", + "migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si l’équipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus d’informations sur https://yunohost.org/backup ;\n - d’être patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusqu’à quelques heures pour que tout soit à niveau.\n\nEn outre, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). L’ancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence.", "migration_0003_problematic_apps_warning": "Veuillez noter que des applications possiblement problématiques ont été détectées. Il semble qu’elles n’aient pas été installées depuis une liste d’application ou qu’elles ne soit pas marquées comme « fonctionnelles ». En conséquence, nous ne pouvons pas garantir qu’elles fonctionneront après la mise à niveau : {problematic_apps}", "migration_0003_modified_files": "Veuillez noter que les fichiers suivants ont été détectés comme modifiés manuellement et pourraient être écrasés à la fin de la mise à niveau : {manually_modified_files}", "migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.", "migrations_to_be_ran_manually": "La migration {id} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans l’interface admin, ou lancer `yunohost tools migrations migrate`.", - "migrations_need_to_accept_disclaimer": "Pour lancer la migration {number} {name}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer.", - "service_description_avahi-daemon": "permet d’atteindre votre serveur via yunohost.local sur votre réseau local", + "migrations_need_to_accept_disclaimer": "Pour lancer la migration {id}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer.", + "service_description_avahi-daemon": "Vous permet d’atteindre votre serveur en utilisant «yunohost.local» sur votre réseau local", "service_description_dnsmasq": "Gère la résolution des noms de domaine (DNS)", - "service_description_dovecot": "permet aux clients de messagerie d’accéder/récupérer les courriels (via IMAP et POP3)", - "service_description_fail2ban": "protège contre les attaques brute-force et autres types d’attaques venant d’Internet", - "service_description_glances": "surveille les informations système de votre serveur", - "service_description_metronome": "gère les comptes de messagerie instantanée XMPP", - "service_description_mysql": "stocke les données des applications (bases de données SQL)", - "service_description_nginx": "sert ou permet l’accès à tous les sites web hébergés sur votre serveur", - "service_description_nslcd": "gère la connexion en ligne de commande des utilisateurs YunoHost", + "service_description_dovecot": "Permet aux clients de messagerie d’accéder/récupérer les courriels (via IMAP et POP3)", + "service_description_fail2ban": "Protège contre les attaques brute-force et autres types d’attaques venant d’Internet", + "service_description_glances": "Surveille les info système de votre serveur", + "service_description_metronome": "Gère les comptes de messagerie instantanée XMPP", + "service_description_mysql": "Stocke les données des applications (bases de données SQL)", + "service_description_nginx": "Sert ou permet l’accès à tous les sites web hébergés sur votre serveur", + "service_description_nslcd": "Gère la connexion en ligne de commande des utilisateurs YunoHost", "service_description_php5-fpm": "exécute des applications écrites en PHP avec nginx", - "service_description_postfix": "utilisé pour envoyer et recevoir des courriels", + "service_description_postfix": "Utilisé pour envoyer et recevoir des courriels", "service_description_redis-server": "Une base de données spécialisée utilisée pour l’accès rapide aux données, les files d’attentes et la communication entre les programmes", - "service_description_rmilter": "vérifie divers paramètres dans les courriels", - "service_description_rspamd": "filtre le pourriel, et d’autres fonctionnalités liées au courriel", - "service_description_slapd": "stocke les utilisateurs, domaines et leurs informations liées", - "service_description_ssh": "vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)", - "service_description_yunohost-api": "permet les interactions entre l’interface web de YunoHost et le système", + "service_description_rmilter": "Vérifie divers paramètres dans les courriels", + "service_description_rspamd": "Filtre le pourriel, et d’autres fonctionnalités liées au courriel", + "service_description_slapd": "Stocke les utilisateurs, domaines et leurs informations liées", + "service_description_ssh": "Vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)", + "service_description_yunohost-api": "Permet les interactions entre l’interface web de YunoHost et le système", "service_description_yunohost-firewall": "Gère l'ouverture et la fermeture des ports de connexion aux services", "experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas l’utiliser à moins que vous ne sachiez ce que vous faites.", "log_corrupted_md_file": "Le fichier YAML de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}", @@ -466,7 +466,7 @@ "migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6", "migration_0005_postgresql_94_not_installed": "PostgreSQL n’a pas été installé sur votre système. Rien à faire !", "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose d’étrange a dû arriver à votre système… :(", - "migration_0005_not_enough_space": "Il n’y a pas assez d’espace libre de disponible sur {path} pour lancer maintenant la migration :(.", + "migration_0005_not_enough_space": "Laissez suffisamment d'espace disponible dans {chemin} pour exécuter la migration.", "recommend_to_add_first_user": "La post-installation est terminée mais YunoHost a besoin d’au moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant la commande 'yunohost user create $nomdutilisateur' ou bien via l’interface d’administration web.", "service_description_php7.0-fpm": "Exécute des applications écrites en PHP avec NGINX", "users_available": "Liste des utilisateurs disponibles :", @@ -557,7 +557,7 @@ "tools_upgrade_special_packages": "Mise à jour des paquets 'spécifiques' (liés a YunoHost) …", "tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie!\nPressez [Entrée] pour revenir à la ligne de commande", "updating_app_lists": "Récupération des mises à jour des applications disponibles…", - "dpkg_lock_not_available": "Cette commande ne peut être exécutée actuellement car un autre programme semble utiliser le verrou de dpkg (gestionnaire de paquets).", + "dpkg_lock_not_available": "Cette commande ne peut être exécutée actuellement car un autre programme semble utiliser le verrou de dpkg (gestionnaire de paquets)", "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques …", "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", @@ -591,5 +591,49 @@ "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}", "app_action_broke_system": "Cette action semble avoir cassé des services important : {services}", "apps_already_up_to_date": "Toutes les applications sont déjà à jour", - "app_upgrade_stopped": "La mise à jour de toutes les applications a été arrêtée afin d’éviter d’éventuels dommages dus à l’échec de la mise à jour de l’application précédente" + "app_upgrade_stopped": "La mise à jour de toutes les applications a été arrêtée afin d’éviter d’éventuels dommages dus à l’échec de la mise à jour de l’application précédente", + "migration_0011_create_group": "Créer un groupe pour chaque utilisateur…", + "migration_0011_done": "Migration réussie. Vous êtes maintenant en mesure de gérer des groupes d'utilisateurs.", + "migrations_must_provide_explicit_targets": "Vous devez fournir des cibles explicites lorsque vous utilisez '--skip' ou '--force-rerun'", + "migrations_no_such_migration": "Il n'y a pas de migration appelée {id}", + "migrations_pending_cant_rerun": "Ces migrations étant toujours en attente, vous ne pouvez pas les exécuter à nouveau: {ids}", + "migration_description_0012_postgresql_password_to_md5_authentication": "Forcer l'authentification PostgreSQL à utiliser MD5 pour les connexions locales", + "migrations_exclusive_options": "'auto', '--skip' et '--force-rerun' sont des options mutuellement exclusives.", + "migrations_not_pending_cant_skip": "Ces migrations ne sont pas en attente et ne peuvent donc pas être ignorées: {ids}", + "migration_0011_can_not_backup_before_migration": "Impossible de sauvegarder le système avant la migration. Erreur: {error: s}", + "migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP…", + "migration_0011_migration_failed_trying_to_rollback": "La migration a échoué… essayait de restauration du système.", + "migration_0011_rollback_success": "Système restauré.", + "migration_0011_update_LDAP_database": "Mise à jour de la base de données LDAP…", + "system_groupname_exists": "Le nom de groupe existe déjà dans le groupe du systèmes", + "tools_update_failed_to_app_fetchlist": "Impossible de mettre à jour les applications de YunoHost car: {error}", + "user_already_in_group": "L'utilisateur '{user:}' est déjà dans le groupe '{group: s}'", + "user_not_in_group": "L'utilisateur '{user: s}' ne fait pas partie du groupe {group: s}", + "migration_0011_backup_before_migration": "Création d'une sauvegarde des paramètres de la base de données LDAP et des applications avant la migration.", + "permission_not_found": "Autorisation '{permission: s}' non trouvée pour l'application '{app: s}'", + "permission_name_not_valid": "Choisissez un nom d'autorisation autorisé pour '{permission: s}'", + "permission_update_failed": "Impossible de mettre à jour la permission", + "permission_generated": "Base de données des autorisations mise à jour", + "permission_updated": "Permission '{permission: s}' pour l'application '{app: s}' mise à jour", + "permission_update_nothing_to_do": "Aucune autorisation pour mettre à jour", + "remove_main_permission_not_allowed": "Supprimer l'autorisation principale n'est pas autorisé", + "dyndns_provider_unreachable": "Impossible d’atteindre le fournisseur Dyndns {provider}: votre YunoHost n’est pas correctement connecté à Internet ou le serveur Dynette est en panne.", + "migration_0011_update_LDAP_schema": "Mise à jour du schéma LDAP…", + "migrations_already_ran": "Ces migrations sont déjà effectuées: {ids}", + "migrations_dependencies_not_satisfied": "Impossible d'exécuter la migration {id} car vous devez d'abord exécuter ces migrations: {dependencies_id}", + "migrations_failed_to_load_migration": "Impossible de charger la migration {id}: {error}", + "migrations_running_forward": "Exécution de la migration {id}…", + "migrations_success_forward": "Migration {id} terminée", + "need_define_permission_before": "Redéfinissez l'autorisation à l'aide de 'yunohost user permission add -u USER' avant de supprimer un groupe autorisé", + "operation_interrupted": "L'opération a été interrompue manuellement", + "permission_already_clear": "L'autorisation '{permission: s}' est déjà vide pour l'application {app: s}", + "permission_already_exist": "L'autorisation '{permission: s}' pour l'application {app: s} existe déjà", + "permission_created": "Permission '{permission: s}' pour l'application {app: s} créée", + "permission_creation_failed": "Impossible d'accorder la permission", + "permission_deleted": "Permission '{permission: s}' pour app {app: s} supprimée", + "permission_deletion_failed": "Autorisation manquante '{permission: s}' pour supprimer l'application '{app: s}'", + "remove_user_of_group_not_allowed": "Vous n'êtes pas autorisé à supprimer l'utilisateur '{utilisateur: s}' dans le groupe '{groupe: s}'", + "migration_description_0011_setup_group_permission": "Configurer le groupe d'utilisateurs et configurer les autorisations pour les applications et les services", + "migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration", + "migration_0011_LDAP_update_failed": "Impossible de mettre à jour LDAP. Erreur: {error: s}" } From 7533dbd6500f9a28fc95905ea458b4da7a7045cf Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 2 Oct 2019 11:49:23 +0000 Subject: [PATCH 198/299] Translated using Weblate (Esperanto) Currently translated at 24.2% (136 of 562 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 157 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 31 deletions(-) diff --git a/locales/eo.json b/locales/eo.json index 1a6a2ea9a..1d367260d 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -1,43 +1,138 @@ { - "admin_password_change_failed": "Malebla ŝanĝi pasvorton", - "admin_password_changed": "Pasvorto de la estro estas ŝanĝita", + "admin_password_change_failed": "Ne eblas ŝanĝi pasvorton", + "admin_password_changed": "La pasvorto de administrado ŝanĝiĝis", "app_already_installed": "{app:s} estas jam instalita", - "app_already_up_to_date": "{app:s} estas ĝisdata", + "app_already_up_to_date": "{app:s} estas jam ĝisdata", "app_argument_required": "Parametro {name:s} estas bezonata", "app_change_url_identical_domains": "Malnovaj kaj novaj domajno/URL estas la sama ('{domain:s}{path:s}'), nenio fareblas.", - "app_change_url_success": "URL de appo {app:s} ŝanĝita al {domain:s}{path:s}", - "app_extraction_failed": "Malebla malkompaktigi instaldosierojn", - "app_id_invalid": "Nevalida apo id", + "app_change_url_success": "{app:s} URL nun estas {domain:s} {path:s}", + "app_extraction_failed": "Ne povis ĉerpi la instalajn dosierojn", + "app_id_invalid": "Nevalida apo ID", "app_incompatible": "Apo {app} ne estas kongrua kun via YunoHost versio", - "app_install_files_invalid": "Nevalidaj instaldosieroj", - "app_location_already_used": "Apo {app} jam estas instalita al tiu loco ({path})", - "user_updated": "Uzanto estas ĝisdatita", + "app_install_files_invalid": "Ĉi tiuj dosieroj ne povas esti instalitaj", + "app_location_already_used": "La app '{app}' jam estas instalita en ({path})", + "user_updated": "Uzantinformoj ŝanĝis", "users_available": "Uzantoj disponeblaj :", "yunohost_already_installed": "YunoHost estas jam instalita", - "yunohost_ca_creation_failed": "Ne eblas krei atestan aŭtoritaton", - "yunohost_ca_creation_success": "Loka atesta aŭtoritato estas kreita.", + "yunohost_ca_creation_failed": "Ne povis krei atestan aŭtoritaton", + "yunohost_ca_creation_success": "Loka atestila aŭtoritato kreiĝis.", "yunohost_installing": "Instalante YunoHost…", - "service_description_glances": "monitoras sisteminformojn de via servilo", - "service_description_metronome": "mastrumas XMPP tujmesaĝilon kontojn", - "service_description_mysql": "stokas aplikaĵojn datojn (SQL datumbazo)", - "service_description_nginx": "servas aŭ permesas atingi ĉiujn retejojn gastigita sur via servilo", - "service_description_nslcd": "mastrumas Yunohost uzantojn konektojn per komanda linio", - "service_description_php7.0-fpm": "rulas aplikaĵojn skibita en PHP kun nginx", - "service_description_postfix": "uzita por sendi kaj ricevi retpoŝtojn", - "service_description_redis-server": "specialita datumbazo uzita por rapida datumo atingo, atendovicoj kaj komunikadoj inter programoj", - "service_description_rmilter": "kontrolas diversajn parametrojn en retpoŝtoj", - "service_description_rspamd": "filtras trudmesaĝojn, kaj aliaj funkcioj rilate al retpoŝto", - "service_description_slapd": "stokas uzantojn, domajnojn kaj rilatajn informojn", - "service_description_ssh": "permesas al vi konekti al via servilo kun fora terminalo (SSH protokolo)", - "service_description_yunohost-api": "mastrumas interagojn inter la YunoHost retinterfaco kaj la sistemo", - "service_description_yunohost-firewall": "mastrumas malfermitajn kaj fermitajn konektejojn al servoj", - "service_disable_failed": "Neebla malaktivigi servon '{service:s}'\n\nFreŝaj protokoloj de la servo : {logs:s}", - "service_disabled": "Servo '{service:s}' estas malaktivigita", + "service_description_glances": "Monitoras sistemajn informojn en via servilo", + "service_description_metronome": "Mastrumas XMPP tujmesaĝilon kontojn", + "service_description_mysql": "Stokas aplikaĵojn datojn (SQL datumbazo)", + "service_description_nginx": "Servas aŭ permesas atingi ĉiujn retejojn gastigita sur via servilo", + "service_description_nslcd": "Mastrumas Yunohost uzantojn konektojn per komanda linio", + "service_description_php7.0-fpm": "Rulas aplikaĵojn skibita en PHP kun nginx", + "service_description_postfix": "Uzita por sendi kaj ricevi retpoŝtojn", + "service_description_redis-server": "Specialita datumbazo uzita por rapida datumo atingo, atendovicoj kaj komunikadoj inter programoj", + "service_description_rmilter": "Kontrolas diversajn parametrojn en retpoŝtoj", + "service_description_rspamd": "Filtras trudmesaĝojn, kaj aliaj funkcioj rilate al retpoŝto", + "service_description_slapd": "Stokas uzantojn, domajnojn kaj rilatajn informojn", + "service_description_ssh": "Permesas al vi konekti al via servilo kun fora terminalo (SSH protokolo)", + "service_description_yunohost-api": "Mastrumas interagojn inter la YunoHost retinterfaco kaj la sistemo", + "service_description_yunohost-firewall": "Mastrumas malfermitajn kaj fermitajn konektejojn al servoj", + "service_disable_failed": "Ne povis malŝalti la servon '{service:s}'\n\nFreŝaj protokoloj de la servo : {logs:s}", + "service_disabled": "'{service: s}' servo malŝaltita", "action_invalid": "Nevalida ago « {action:s} »", "admin_password": "Pasvorto de la estro", "admin_password_too_long": "Bonvolu elekti pasvorton pli mallonga ol 127 signoj", - "already_up_to_date": "Neniu estas farenda! Ĉiu jam estas ĝisdata!", - "app_argument_choice_invalid": "Nevalida elekto por argumento « {name:s} », ĝi devas esti unu el {choices:s}", - "app_argument_invalid": "Nevalida valoro por argumento « {name:s} » : {error:s}", - "app_change_url_failed_nginx_reload": "Reŝargi nginx malsuksesis. Jen la eligo de « nginx -t » :\n{nginx_errors:s}" + "already_up_to_date": "Nenio por fari. Ĉio estas jam ĝisdatigita.", + "app_argument_choice_invalid": "Uzu unu el ĉi tiuj elektoj '{choices:s}' por la argumento '{name:s}'", + "app_argument_invalid": "Elektu validan valoron por la argumento '{name:s}': {error:s}", + "app_change_url_failed_nginx_reload": "Ne eblis reŝarĝi NGINX. Jen la eligo de 'nginx -t':\n{nginx_errors:s}", + "appslist_url_already_tracked": "Estas jam registrita aplika listo kun la URL {url:s}.", + "ask_new_admin_password": "Nova administrada pasvorto", + "app_action_broke_system": "Ĉi tiu ago ŝajne rompis ĉi tiujn gravajn servojn: {services}", + "app_unsupported_remote_type": "Malkontrolita fora speco uzita por la apliko", + "backup_archive_system_part_not_available": "Sistemo parto '{part:s}' ne haveblas en ĉi tiu rezervo", + "apps_permission_not_found": "Neniu permeso trovita por la instalitaj programoj", + "apps_permission_restoration_failed": "Donu la rajtigan permeson '{permission:s}' por restarigi {app:s}", + "backup_abstract_method": "Ĉi tiu rezerva metodo ankoraŭ efektiviĝis", + "apps_already_up_to_date": "Ĉiuj aplikoj estas jam ĝisdatigitaj", + "backup_borg_not_implemented": "La kopia metodo de Borg ankoraŭ ne estas efektivigita", + "app_upgrade_stopped": "La ĝisdatigo de ĉiuj aplikoj estis ĉesigita por eviti eblajn damaĝojn ĉar la antaŭa apliko ne sukcesis ĝisdatigi", + "app_location_unavailable": "Ĉi tiu URL aŭ ne haveblas, aŭ konfliktas kun la jam instalita (j) apliko (j):\n{apps:s}", + "backup_archive_app_not_found": "Ne povis trovi la programon '{app:s}' en la rezerva ar archiveivo", + "backup_actually_backuping": "Krei rezervan ar archiveivon el la kolektitaj dosieroj …", + "backup_method_borg_finished": "Sekurkopio en Borg finiĝis", + "appslist_removed": "{appslist:s} aplika listo forigita", + "app_change_url_no_script": "Ĉi tiu apliko '{app_name:s}' ankoraŭ ne subtenas URL-modifon. Eble vi devus altgradigi ĝin.", + "app_start_install": "Instalanta aplikon {app} …", + "backup_created": "Sekurkopio kreita", + "app_make_default_location_already_used": "Ne povas igi la aplikon '{app}' defaŭlta sur la domajno, {domain} jam uziĝas de la alia app '{other_app}'", + "backup_method_copy_finished": "Rezerva kopio finis", + "app_not_properly_removed": "{app:s} ne estis ĝuste forigita", + "backup_archive_broken_link": "Ne povis aliri la rezervan ar archiveivon (rompita ligilo al {path:s})", + "app_requirements_checking": "Kontrolante postulatajn pakaĵojn por {app} …", + "app_not_installed": "Ne povis trovi la aplikon '{app:s}' en la listo de instalitaj programoj: {all_apps}", + "app_location_install_failed": "Ne eblas instali la aplikon tie ĉar ĝi konfliktas kun la '{other_app}' jam instalita en '{other_path}'", + "ask_new_path": "Nova vojo", + "backup_custom_mount_error": "Propra rezerva metodo ne povis preterpasi la paŝon 'monto'", + "app_upgrade_app_name": "Nun ĝisdatiganta {app} …", + "app_manifest_invalid": "Io misas pri la aplika manifesto: {error}", + "backup_cleaning_failed": "Ne povis purigi la provizoran rezervan dosierujon", + "backup_invalid_archive": "Ĉi tio ne estas rezerva ar archiveivo", + "ask_current_admin_password": "Pasvorto pri aktuala administrado", + "backup_creation_failed": "Ne povis krei la rezervan ar archiveivon", + "backup_hook_unknown": "La rezerva hoko '{hoko:s}' estas nekonata", + "backup_custom_backup_error": "Propra rezerva metodo ne povis preterpasi la paŝon \"sekurkopio\"", + "ask_main_domain": "Ĉefa domajno", + "backup_method_tar_finished": "TAR-rezerva ar archiveivo kreita", + "appslist_unknown": "Aplika listo {appslist:s} nekonata.", + "ask_list_to_remove": "Listo por forigi", + "backup_cant_mount_uncompress_archive": "Ne povis munti la nekompresitan ar archiveivon kiel protektita kontraŭ skribo", + "appslist_retrieve_bad_format": "Ne povis legi la elprenitan liston {appslist:s}", + "appslist_corrupted_json": "Ne povis ŝarĝi la aplikajn listojn. Ĝi aspektas kiel {filename:s} estas damaĝita.", + "app_action_cannot_be_ran_because_required_services_down": "Ĉi tiu app postulas iujn servojn, kiuj nuntempe malleviĝas. Antaŭ ol daŭrigi, vi provu rekomenci la jenajn servojn (kaj eventuale esploru kial ili malsukcesas): {services}", + "backup_copying_to_organize_the_archive": "Kopiante {size:s} MB por organizi la ar archiveivon", + "backup_output_directory_forbidden": "Elektu malsaman elirejan dosierujon. Sekurkopioj ne povas esti kreitaj en sub-dosierujoj / bin, / boot, / dev, / ktp, / lib, / root, / run, / sbin, / sys, / usr, / var aŭ /home/yunohost.backup/archives", + "appslist_could_not_migrate": "Ne povis migri la liston de aplikoj {appslist:s}! Ne eblis analizi la URL ... La malnova cron-laboro konserviĝis en {bkp_file:s}.", + "app_requirements_failed": "Certaines exigences ne sont pas remplies pour {app}: {error}", + "backup_no_uncompress_archive_dir": "Ne ekzistas tia nekompremita arkiva dosierujo", + "password_too_simple_1": "Pasvorto devas esti almenaŭ 8 signojn longa", + "app_upgrade_failed": "Ne povis ĝisdatigi {app:s}", + "app_upgrade_several_apps": "La sekvaj apliko estos altgradigitaj: {apps}", + "backup_archive_open_failed": "Ne povis malfermi la rezervan ar archiveivon", + "ask_lastname": "Familia nomo", + "app_start_backup": "Kolekti dosierojn por esti subtenata por {app} …", + "backup_archive_name_exists": "Rezerva arkivo kun ĉi tiu nomo jam ekzistas.", + "backup_applying_method_tar": "Krei la rezervan TAR-ar archiveivon …", + "backup_method_custom_finished": "Propra rezerva metodo '{metodo:s}' finiĝis", + "appslist_retrieve_error": "Ne eblas retrovi la forajn aplikajn listojn {appslist:s}: {eraro:s}", + "app_already_installed_cant_change_url": "Ĉi tiu app estas jam instalita. La URL ne povas esti ŝanĝita nur per ĉi tiu funkcio. Rigardu \"app changeurl\" se ĝi haveblas.", + "app_not_correctly_installed": "{app:s} ŝajnas esti malĝuste instalita", + "app_removed": "{app:s} forigita", + "backup_delete_error": "Ne povis forigi '{path: s}'", + "app_package_need_update": "La pakaĵo {app} devas esti ĝisdatigita por sekvi YunoHost-ŝanĝojn", + "backup_nothings_done": "Nenio por ŝpari", + "backup_applying_method_custom": "Nomante la kutiman rezervan metodon '{metodo:s}' …", + "appslist_fetched": "Ĝisdatigita aplika listo {appslist:s} elprenita", + "backup_app_failed": "Ne eblis rezervi la programon '{app:s}'", + "app_upgrade_some_app_failed": "Iuj aplikoj ne povis esti altgradigitaj", + "app_start_remove": "Forigo de apliko {app} …", + "backup_output_directory_not_empty": "Vi devas elekti malplenan eligitan dosierujon", + "backup_archive_writing_error": "Ne povis aldoni la dosierojn '{source:s}' (nomitaj en la ar theivo '{dest:s}') por esti rezervitaj en la kunpremita arkivo '{archive:s}'", + "ask_email": "Retpoŝta adreso", + "app_start_restore": "Restarigi aplikon {app} …", + "backup_applying_method_copy": "Kopiante ĉiujn dosierojn al sekurkopio …", + "backup_couldnt_bind": "Ne povis ligi {src:s} al {dest:s}.", + "ask_password": "Pasvorto", + "app_requirements_unmeet": "Postuloj ne estas renkontitaj por {app}, la pakaĵo {pkgname} ({version}) devas esti {spec}", + "ask_firstname": "Antaŭnomo", + "backup_ask_for_copying_if_needed": "Iuj dosieroj ne povus esti pretigitaj por sekurkopio uzante la metodon, kiu evitas portempe malŝpari spacon en la sistemo. Por plenumi la sekurkopion, {size:s} MB estos provizore. Ĉu vi konsentas?", + "backup_mount_archive_for_restore": "Preparante arkivon por restarigo …", + "appslist_migrating": "Migra aplika listo {appslist:s} …", + "backup_csv_creation_failed": "Ne povis krei la CSV-dosieron bezonatan por restarigo", + "backup_archive_name_unknown": "Nekonata loka rezerva ar archiveivo nomata '{name:s}'", + "backup_applying_method_borg": "Sendado de ĉiuj dosieroj al sekurkopio en borg-rezerva deponejo …", + "app_sources_fetch_failed": "Ne povis akiri fontajn dosierojn, ĉu la URL estas ĝusta?", + "appslist_name_already_tracked": "Registrita aplika listo kun nomo {name:s} jam ekzistas.", + "ask_new_domain": "Nova domajno", + "app_unknown": "Nekonata apliko", + "app_not_upgraded": "La aplikaĵo '{failed_app}' ne ĝisdatigis, kaj pro tio la sekvaj ĝisdatigoj de aplikoj estis nuligitaj: {apps}", + "aborting": "Aborti.", + "ask_path": "Pado", + "app_upgraded": "{app:s} altgradigita", + "backup_deleted": "Rezerva forigita", + "backup_csv_addition_failed": "Ne povis aldoni dosierojn al sekurkopio en la CSV-dosiero" } From f93e2b307904d1ac5c6dd86c4bf52cc22fda48d2 Mon Sep 17 00:00:00 2001 From: Julien Jershon Date: Sat, 5 Oct 2019 09:55:03 +0200 Subject: [PATCH 199/299] Better error message for invalid email domain. Fix https://github.com/YunoHost/issues/issues/1365. --- locales/en.json | 2 +- locales/fr.json | 2 +- locales/pt.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 98cbf22e9..2069ee6a6 100644 --- a/locales/en.json +++ b/locales/en.json @@ -286,7 +286,7 @@ "ldap_initialized": "LDAP initialized", "license_undefined": "undefined", "mail_alias_remove_failed": "Could not remove e-mail alias '{mail:s}'", - "mail_domain_unknown": "Unknown e-mail address for domain '{domain:s}'", + "mail_domain_unknown": "Invalid e-mail address for domain '{domain:s}'. Please, use a domain administrated by this server.", "mail_forward_remove_failed": "Could not remove e-mail forwarding '{mail:s}'", "mailbox_disabled": "E-mail turned off for user {user:s}", "mailbox_used_space_dovecot_down": "The Dovecot mailbox service needs to be up, if you want to fetch used mailbox space", diff --git a/locales/fr.json b/locales/fr.json index 8bffec8b2..7a2d299f8 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -120,7 +120,7 @@ "ldap_initialized": "L’annuaire LDAP initialisé", "license_undefined": "indéfinie", "mail_alias_remove_failed": "Impossible de supprimer l’alias de courriel '{mail:s}'", - "mail_domain_unknown": "Le domaine '{domain:s}' pour l'adresse de courriel est inconnu", + "mail_domain_unknown": "Le domaine '{domain:s}' de cette adress de courriel n'est pas valide. Merci d'utiliser un domain administré par ce serveur.", "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "maindomain_change_failed": "Impossible de modifier le domaine principal", "maindomain_changed": "Le domaine principal modifié", diff --git a/locales/pt.json b/locales/pt.json index 80a0d5ddd..b8c9d2eb3 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -72,7 +72,7 @@ "ldap_initialized": "LDAP inicializada com êxito", "license_undefined": "indefinido", "mail_alias_remove_failed": "Não foi possível remover a etiqueta de correio '{mail:s}'", - "mail_domain_unknown": "Domínio de endereço de correio desconhecido '{domain:s}'", + "mail_domain_unknown": "Domínio de endereço de correio '{domain:s}' inválido. Por favor, usa um domínio administrado per esse servidor.", "mail_forward_remove_failed": "Não foi possível remover o reencaminhamento de correio '{mail:s}'", "maindomain_change_failed": "Incapaz alterar o domínio raiz", "maindomain_changed": "Domínio raiz alterado com êxito", From a1822e2f42aa1a7ff516ff76ea5dee1def233a20 Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Sun, 6 Oct 2019 11:25:01 +0200 Subject: [PATCH 200/299] Use str instead of strerror (not present) See https://forum.yunohost.org/t/cant-create-a-user-after-post-intsallation/9190. --- src/yunohost/user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/user.py b/src/yunohost/user.py index c6413d7e1..fe27492f4 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -199,7 +199,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, with open('/etc/ssowat/conf.json.persistent') as json_conf: ssowat_conf = json.loads(str(json_conf.read())) except ValueError as e: - raise YunohostError('ssowat_persistent_conf_read_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_read_error', error=str(e)) except IOError: ssowat_conf = {} @@ -209,7 +209,7 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, with open('/etc/ssowat/conf.json.persistent', 'w+') as f: json.dump(ssowat_conf, f, sort_keys=True, indent=4) except IOError as e: - raise YunohostError('ssowat_persistent_conf_write_error', error=e.strerror) + raise YunohostError('ssowat_persistent_conf_write_error', error=str(e)) try: ldap.add('uid=%s,ou=users' % username, attr_dict) From 2642b64af5bccb6f93a8612a42365edd68e9b118 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 16 Sep 2019 23:17:46 +0200 Subject: [PATCH 201/299] Detect and warn early about unavailable full domain requirement... --- locales/en.json | 1 + src/yunohost/app.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/locales/en.json b/locales/en.json index 61fdcfa9b..4e9675cac 100644 --- a/locales/en.json +++ b/locales/en.json @@ -19,6 +19,7 @@ "app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", "app_change_url_success": "{app:s} URL is now {domain:s}{path:s}", "app_extraction_failed": "Could not extract the installation files", + "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on {domain}. One possible solution is to add a subdomain dedicated to this application.", "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5a51e57bb..6f5c1dabe 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -847,6 +847,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu args_list = [ value[0] for value in args_odict.values() ] args_list.append(app_instance_name) + # Validate domain / path availability for webapps + _validate_and_normalize_webpath(manifest, args_odict, extracted_app_folder) + # Prepare env. var. to pass to script env_dict = _make_environment_dict(args_odict) env_dict["YNH_APP_ID"] = app_id @@ -2547,8 +2550,7 @@ def _parse_args_for_action(action, args={}): def _parse_args_in_yunohost_format(args, action_args): """Parse arguments store in either manifest.json or actions.json """ - from yunohost.domain import (domain_list, _get_maindomain, - _get_conflicting_apps, _normalize_domain_path) + from yunohost.domain import domain_list, _get_maindomain from yunohost.user import user_info, user_list args_dict = OrderedDict() @@ -2666,13 +2668,18 @@ def _parse_args_in_yunohost_format(args, action_args): assert_password_is_strong_enough('user', arg_value) args_dict[arg_name] = (arg_value, arg_type) - # END loop over action_args... + return args_dict + + +def _validate_and_normalize_webpath(manifest, args_dict, app_folder): + + from yunohost.domain import _get_conflicting_apps, _normalize_domain_path # If there's only one "domain" and "path", validate that domain/path # is an available url and normalize the path. - domain_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "domain" ] - path_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "path" ] + domain_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "domain"] + path_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "path"] if len(domain_args) == 1 and len(path_args) == 1: @@ -2698,7 +2705,25 @@ def _parse_args_in_yunohost_format(args, action_args): # standard path format to deal with no matter what the user inputted) args_dict[path_args[0][0]] = (path, "path") - return args_dict + # This is likely to be a full-domain app... + elif len(domain_args) == 1 and len(path_args) == 0: + + # Confirm that this is a full-domain app This should cover most cases + # ... though anyway the proper solution is to implement some mechanism + # in the manifest for app to declare that they require a full domain + # (among other thing) so that we can dynamically check/display this + # requirement on the webadmin form and not miserably fail at submit time + + # Full-domain apps typically declare something like path_url="/" or path=/ + # and use ynh_webpath_register or yunohost_app_checkurl inside the install script + install_script_content = open(os.path.join(app_folder, 'scripts/install')).read() + if re.search(r"\npath(_url)?=[\"']?/[\"']?\n", install_script_content) \ + and re.search(r"(ynh_webpath_register|yunohost app checkurl)"): + + domain = domain_args[0][1] + conflicts = _get_conflicting_apps(domain, "/") + + raise YunohostError('app_full_domain_unavailable', domain) def _make_environment_dict(args_dict, prefix="APP_ARG_"): From 0d90133bb7a0181621824477a855f01256885ae6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:18:27 +0200 Subject: [PATCH 202/299] Improve message --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 4e9675cac..1be7d1151 100644 --- a/locales/en.json +++ b/locales/en.json @@ -19,7 +19,7 @@ "app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", "app_change_url_success": "{app:s} URL is now {domain:s}{path:s}", "app_extraction_failed": "Could not extract the installation files", - "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on {domain}. One possible solution is to add a subdomain dedicated to this application.", + "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on domain '{domain}'. One possible solution is to add and use a subdomain dedicated to this application instead.", "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", From 342fe2d4be0a1300dddf0e747906cb4e16b9b091 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:19:50 +0200 Subject: [PATCH 203/299] Add unit test for full-domain apps --- src/yunohost/tests/test_apps.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py index 9c85df1e9..fc44ef105 100644 --- a/src/yunohost/tests/test_apps.py +++ b/src/yunohost/tests/test_apps.py @@ -36,16 +36,22 @@ def clean(): if _is_installed("legacy_app"): app_remove("legacy_app") + if _is_installed("full_domain_app"): + app_remove("full_domain_app") + to_remove = [] to_remove += glob.glob("/etc/nginx/conf.d/*.d/*legacy*") + to_remove += glob.glob("/etc/nginx/conf.d/*.d/*full_domain*") to_remove += glob.glob("/etc/nginx/conf.d/*.d/*break_yo_system*") for filepath in to_remove: os.remove(filepath) to_remove = [] to_remove += glob.glob("/etc/yunohost/apps/*legacy_app*") + to_remove += glob.glob("/etc/yunohost/apps/*full_domain_app*") to_remove += glob.glob("/etc/yunohost/apps/*break_yo_system*") to_remove += glob.glob("/var/www/*legacy*") + to_remove += glob.glob("/var/www/*full_domain*") for folderpath in to_remove: shutil.rmtree(folderpath, ignore_errors=True) @@ -120,6 +126,13 @@ def install_legacy_app(domain, path): force=True) +def install_full_domain_app(domain): + + app_install("./tests/apps/full_domain_app_ynh", + args="domain=%s" % domain, + force=True) + + def install_break_yo_system(domain, breakwhat): app_install("./tests/apps/break_yo_system_ynh", @@ -272,6 +285,22 @@ def test_legacy_app_failed_remove(secondary_domain): assert app_is_not_installed(secondary_domain, "legacy") +def test_full_domain_app(secondary_domain): + + install_full_domain_app(secondary_domain) + + assert app_is_exposed_on_http(secondary_domain, "/", "This is a dummy app") + + +def test_full_domain_app_with_conflicts(secondary_domain): + + install_legacy_app(secondary_domain, "/legacy") + + # TODO : once #808 is merged, add test that the message raised is 'app_full_domain_unavailable' + with pytest.raises(YunohostError): + install_full_domain_app(secondary_domain) + + def test_systemfuckedup_during_app_install(secondary_domain): with pytest.raises(YunohostError): From c70418c4b25952f9308b69d88e47b8a2490781c1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:21:04 +0200 Subject: [PATCH 204/299] Fixes following tests --- src/yunohost/app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 6f5c1dabe..8596f7297 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2717,13 +2717,14 @@ def _validate_and_normalize_webpath(manifest, args_dict, app_folder): # Full-domain apps typically declare something like path_url="/" or path=/ # and use ynh_webpath_register or yunohost_app_checkurl inside the install script install_script_content = open(os.path.join(app_folder, 'scripts/install')).read() + if re.search(r"\npath(_url)?=[\"']?/[\"']?\n", install_script_content) \ - and re.search(r"(ynh_webpath_register|yunohost app checkurl)"): + and re.search(r"(ynh_webpath_register|yunohost app checkurl)", install_script_content): domain = domain_args[0][1] conflicts = _get_conflicting_apps(domain, "/") - raise YunohostError('app_full_domain_unavailable', domain) + raise YunohostError('app_full_domain_unavailable', domain=domain) def _make_environment_dict(args_dict, prefix="APP_ARG_"): From fc787009041069f7ffd83b1d7f8467a8d98e1c44 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:30:18 +0200 Subject: [PATCH 205/299] More accurate greps to identify that sury packages are installed --- data/helpers.d/apt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index f5590b38d..d84520daf 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -226,8 +226,8 @@ ynh_install_app_dependencies () { # If we require to install php dependency if echo $dependencies | grep -q 'php'; then - # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33+1 on debian) - if dpkg --list | grep php | grep -q "7.0.33-10" + # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian) + if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9u5" then # And sury ain't already installed if ! grep -nrq "sury" /etc/apt/sources.list* From 077e5c463c8c4d6befc28d9c0c79bdc21ed2b3cb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 24 Sep 2019 23:18:05 +0200 Subject: [PATCH 206/299] Fucking ugly workaround for the goddamn dependency nighmare from sury djeezus kraiste --- data/helpers.d/apt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index 9d5ad3ac2..fed585d95 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -218,6 +218,27 @@ ynh_install_app_dependencies () { fi local dep_app=${app//_/-} # Replace all '_' by '-' + # + # Epic ugly hack to fix the goddamn dependency nightmare of sury + # Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective + # https://github.com/YunoHost/issues/issues/1407 + # + # If we require to install php dependency + if echo $dependencies | grep -q 'php'; + then + # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33+1 on debian) + if dpkg --list | grep php | grep -q "7.0.33-10" + then + # And sury ain't already installed + if ! grep -nrq "sury" /etc/apt/sources.list* + then + # Re-add sury + echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury.list + wget -O /etc/apt/trusted.gpg.d/sury.gpg https://packages.sury.org/php/apt.gpg + fi + fi + fi + cat > /tmp/${dep_app}-ynh-deps.control << EOF # Make a control file for equivs-build Section: misc Priority: optional From 0e3a131095afe4893d10629271c1ce6c6b177624 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:30:18 +0200 Subject: [PATCH 207/299] More accurate greps to identify that sury packages are installed --- data/helpers.d/apt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index fed585d95..d772c6855 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -226,8 +226,8 @@ ynh_install_app_dependencies () { # If we require to install php dependency if echo $dependencies | grep -q 'php'; then - # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33+1 on debian) - if dpkg --list | grep php | grep -q "7.0.33-10" + # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian) + if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9u5" then # And sury ain't already installed if ! grep -nrq "sury" /etc/apt/sources.list* From 1c5220f7cbe029b4cf03011cb451426d755697f8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 26 Sep 2019 14:23:01 +0200 Subject: [PATCH 208/299] Support logfiles not ending with .log in logrotate ... --- data/helpers.d/logrotate | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/data/helpers.d/logrotate b/data/helpers.d/logrotate index 47ce46cf6..82cdee6a5 100644 --- a/data/helpers.d/logrotate +++ b/data/helpers.d/logrotate @@ -40,10 +40,13 @@ ynh_use_logrotate () { fi if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]; then - if [ "$(echo ${1##*.})" == "log" ]; then # Keep only the extension to check if it's a logfile - local logfile=$1 # In this case, focus logrotate on the logfile + # If the given logfile parameter already exists as a file, or if it ends up with ".log", + # we just want to manage a single file + if [ -f "$1" ] || [ "$(echo ${1##*.})" == "log" ]; then + local logfile=$1 + # Otherwise we assume we want to manage a directory and all its .log file inside else - local logfile=$1/*.log # Else, uses the directory and all logfile into it. + local logfile=$1/*.log fi fi # LEGACY CODE @@ -54,7 +57,7 @@ ynh_use_logrotate () { fi if [ -n "$logfile" ] then - if [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile + if [ ! -f "$1" ] && [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it. fi else From 5623689a2728a875880dd41b614fffd818a0597d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 16 Sep 2019 23:17:46 +0200 Subject: [PATCH 209/299] Detect and warn early about unavailable full domain requirement... --- locales/en.json | 1 + src/yunohost/app.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/locales/en.json b/locales/en.json index d1203c757..55735d760 100644 --- a/locales/en.json +++ b/locales/en.json @@ -19,6 +19,7 @@ "app_change_url_no_script": "This application '{app_name:s}' doesn't support url modification yet. Maybe you should upgrade the application.", "app_change_url_success": "Successfully changed {app:s} url to {domain:s}{path:s}", "app_extraction_failed": "Unable to extract installation files", + "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on {domain}. One possible solution is to add a subdomain dedicated to this application.", "app_id_invalid": "Invalid app id", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "Invalid installation files", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index d9a349579..c982c3418 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -802,6 +802,9 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu args_list = [ value[0] for value in args_odict.values() ] args_list.append(app_instance_name) + # Validate domain / path availability for webapps + _validate_and_normalize_webpath(manifest, args_odict, extracted_app_folder) + # Prepare env. var. to pass to script env_dict = _make_environment_dict(args_odict) env_dict["YNH_APP_ID"] = app_id @@ -2394,8 +2397,7 @@ def _parse_args_for_action(action, args={}): def _parse_args_in_yunohost_format(args, action_args): """Parse arguments store in either manifest.json or actions.json """ - from yunohost.domain import (domain_list, _get_maindomain, - _get_conflicting_apps, _normalize_domain_path) + from yunohost.domain import domain_list, _get_maindomain from yunohost.user import user_info, user_list args_dict = OrderedDict() @@ -2513,13 +2515,18 @@ def _parse_args_in_yunohost_format(args, action_args): assert_password_is_strong_enough('user', arg_value) args_dict[arg_name] = (arg_value, arg_type) - # END loop over action_args... + return args_dict + + +def _validate_and_normalize_webpath(manifest, args_dict, app_folder): + + from yunohost.domain import _get_conflicting_apps, _normalize_domain_path # If there's only one "domain" and "path", validate that domain/path # is an available url and normalize the path. - domain_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "domain" ] - path_args = [ (name, value[0]) for name, value in args_dict.items() if value[1] == "path" ] + domain_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "domain"] + path_args = [(name, value[0]) for name, value in args_dict.items() if value[1] == "path"] if len(domain_args) == 1 and len(path_args) == 1: @@ -2545,7 +2552,25 @@ def _parse_args_in_yunohost_format(args, action_args): # standard path format to deal with no matter what the user inputted) args_dict[path_args[0][0]] = (path, "path") - return args_dict + # This is likely to be a full-domain app... + elif len(domain_args) == 1 and len(path_args) == 0: + + # Confirm that this is a full-domain app This should cover most cases + # ... though anyway the proper solution is to implement some mechanism + # in the manifest for app to declare that they require a full domain + # (among other thing) so that we can dynamically check/display this + # requirement on the webadmin form and not miserably fail at submit time + + # Full-domain apps typically declare something like path_url="/" or path=/ + # and use ynh_webpath_register or yunohost_app_checkurl inside the install script + install_script_content = open(os.path.join(app_folder, 'scripts/install')).read() + if re.search(r"\npath(_url)?=[\"']?/[\"']?\n", install_script_content) \ + and re.search(r"(ynh_webpath_register|yunohost app checkurl)"): + + domain = domain_args[0][1] + conflicts = _get_conflicting_apps(domain, "/") + + raise YunohostError('app_full_domain_unavailable', domain) def _make_environment_dict(args_dict, prefix="APP_ARG_"): From 75742216ea93e45c6679310fa9a02724775dd838 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:18:27 +0200 Subject: [PATCH 210/299] Improve message --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 55735d760..4bb049db3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -19,7 +19,7 @@ "app_change_url_no_script": "This application '{app_name:s}' doesn't support url modification yet. Maybe you should upgrade the application.", "app_change_url_success": "Successfully changed {app:s} url to {domain:s}{path:s}", "app_extraction_failed": "Unable to extract installation files", - "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on {domain}. One possible solution is to add a subdomain dedicated to this application.", + "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on domain '{domain}'. One possible solution is to add and use a subdomain dedicated to this application instead.", "app_id_invalid": "Invalid app id", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "Invalid installation files", From 7ecefaf8dc78cc0d4ddb1f0fabc5eab6ff2bb176 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 18:21:04 +0200 Subject: [PATCH 211/299] Fixes following tests --- src/yunohost/app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c982c3418..421be9b60 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2564,13 +2564,14 @@ def _validate_and_normalize_webpath(manifest, args_dict, app_folder): # Full-domain apps typically declare something like path_url="/" or path=/ # and use ynh_webpath_register or yunohost_app_checkurl inside the install script install_script_content = open(os.path.join(app_folder, 'scripts/install')).read() + if re.search(r"\npath(_url)?=[\"']?/[\"']?\n", install_script_content) \ - and re.search(r"(ynh_webpath_register|yunohost app checkurl)"): + and re.search(r"(ynh_webpath_register|yunohost app checkurl)", install_script_content): domain = domain_args[0][1] conflicts = _get_conflicting_apps(domain, "/") - raise YunohostError('app_full_domain_unavailable', domain) + raise YunohostError('app_full_domain_unavailable', domain=domain) def _make_environment_dict(args_dict, prefix="APP_ARG_"): From bf1ad164dafc996c0bf9d1a3b19de7dc2d089003 Mon Sep 17 00:00:00 2001 From: "J. Doe" Date: Thu, 19 Sep 2019 13:01:22 +0200 Subject: [PATCH 212/299] change maxretry of fail2ban from 6 to 10 --- data/templates/fail2ban/yunohost-jails.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/fail2ban/yunohost-jails.conf b/data/templates/fail2ban/yunohost-jails.conf index bf3bcb6e3..fdbd7990b 100644 --- a/data/templates/fail2ban/yunohost-jails.conf +++ b/data/templates/fail2ban/yunohost-jails.conf @@ -29,4 +29,4 @@ protocol = tcp filter = yunohost logpath = /var/log/nginx/*error.log /var/log/nginx/*access.log -maxretry = 6 +maxretry = 10 From 115513c6503de63a7f970d1f2dae216839b7f46d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 19:03:32 +0200 Subject: [PATCH 213/299] Update changelog for 3.6.5 --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index 3eb347456..4b8c26471 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +yunohost (3.6.5) stable; urgency=low + + - [enh] Detect and warn early about unavailable full domains... (#798) + - [mod] Change maxretry of fail2ban from 6 to 10 (#802) + - [fix] Epicly ugly workaround for the goddamn dependency nighmare about sury fucking up php7.0 dependencies (#809) + - [fix] Support logfiles not ending with .log in logrotate ... (#810) + + -- Alexandre Aubin Mon, 08 Oct 2019 19:00:00 +0000 + yunohost (3.6.4.6) stable; urgency=low - [fix] Hopefully fix the issue about corrupted logs metadata files (d507d447, 1cec9d78) From fe8fd1b2c58993211e016fecb74d6fc026482bd1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 20:04:08 +0200 Subject: [PATCH 214/299] Change from #802 was only about the yunohost jail ... this should be global >.> --- data/templates/fail2ban/jail.conf | 2 +- data/templates/fail2ban/yunohost-jails.conf | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/data/templates/fail2ban/jail.conf b/data/templates/fail2ban/jail.conf index 9b4d39f17..bd522c4ba 100644 --- a/data/templates/fail2ban/jail.conf +++ b/data/templates/fail2ban/jail.conf @@ -63,7 +63,7 @@ bantime = 600 findtime = 600 # "maxretry" is the number of failures before a host get banned. -maxretry = 5 +maxretry = 10 # "backend" specifies the backend used to get files modification. # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". diff --git a/data/templates/fail2ban/yunohost-jails.conf b/data/templates/fail2ban/yunohost-jails.conf index fdbd7990b..e1e464b1a 100644 --- a/data/templates/fail2ban/yunohost-jails.conf +++ b/data/templates/fail2ban/yunohost-jails.conf @@ -29,4 +29,3 @@ protocol = tcp filter = yunohost logpath = /var/log/nginx/*error.log /var/log/nginx/*access.log -maxretry = 10 From 826429cf0b5c8dc72cb2c3898c42cf0f20971cb4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 20:04:08 +0200 Subject: [PATCH 215/299] Change from #802 was only about the yunohost jail ... this should be global >.> --- data/templates/fail2ban/jail.conf | 2 +- data/templates/fail2ban/yunohost-jails.conf | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/data/templates/fail2ban/jail.conf b/data/templates/fail2ban/jail.conf index 9b4d39f17..bd522c4ba 100644 --- a/data/templates/fail2ban/jail.conf +++ b/data/templates/fail2ban/jail.conf @@ -63,7 +63,7 @@ bantime = 600 findtime = 600 # "maxretry" is the number of failures before a host get banned. -maxretry = 5 +maxretry = 10 # "backend" specifies the backend used to get files modification. # Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". diff --git a/data/templates/fail2ban/yunohost-jails.conf b/data/templates/fail2ban/yunohost-jails.conf index fdbd7990b..e1e464b1a 100644 --- a/data/templates/fail2ban/yunohost-jails.conf +++ b/data/templates/fail2ban/yunohost-jails.conf @@ -29,4 +29,3 @@ protocol = tcp filter = yunohost logpath = /var/log/nginx/*error.log /var/log/nginx/*access.log -maxretry = 10 From c45b0edd39e47d66201c9b9223923abfd93f3a58 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 8 Oct 2019 20:21:11 +0200 Subject: [PATCH 216/299] Update changelog for 3.6.5.1 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 4b8c26471..6d5a16f2c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (3.6.5.1) stable; urgency=low + + - [mod] Change maxretry of fail2ban from 6 to 10 (fe8fd1b) + + -- Alexandre Aubin Mon, 08 Oct 2019 20:00:00 +0000 + yunohost (3.6.5) stable; urgency=low - [enh] Detect and warn early about unavailable full domains... (#798) From 4a14cbd6e0bc092c7bf3ed94f994d9330dc1c1a0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 18:42:17 +0200 Subject: [PATCH 217/299] Fix / implement remaining test --- src/yunohost/tests/test_permission.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index f17313fa1..a9e16cfc6 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -41,6 +41,10 @@ def teardown_function(function): app_remove("permissions_app") except: pass + try: + app_remove("legacy_app") + except: + pass @pytest.fixture(autouse=True) def check_LDAP_db_integrity_call(): @@ -443,23 +447,22 @@ def test_permission_app_propagation_on_ssowat(): def test_permission_legacy_app_propagation_on_ssowat(): - # TODO / FIXME : To be actually implemented later .... - raise NotImplementedError - app_install("./tests/apps/legacy_app_ynh", args="domain=%s&path=%s" % (maindomain, "/legacy"), force=True) # App is configured as public by default using the legacy unprotected_uri mechanics # It should automatically be migrated during the install - assert res['permissions_app.main']['allowed'] == ["visitors"] + res = user_permission_list(full=True)['permissions'] + assert res['legacy_app.main']['allowed'] == ["visitors"] - assert can_access_webpage(maindomain + "/legacy", logged_as=None) - assert can_access_webpage(maindomain + "/legacy", logged_as="alice") + app_webroot = "https://%s/legacy" % maindomain + + assert can_access_webpage(app_webroot, logged_as=None) + assert can_access_webpage(app_webroot, logged_as="alice") # Try to update the permission and check that permissions are still consistent user_permission_update("legacy_app.main", remove="visitors", add="bob") - res = user_permission_list(full=True)['permissions'] - assert not can_access_webpage(maindomain + "/legacy", logged_as=None) - assert not can_access_webpage(maindomain + "/legacy", logged_as="alice") - assert can_access_webpage(maindomain + "/legacy", logged_as="bob") + assert not can_access_webpage(app_webroot, logged_as=None) + assert not can_access_webpage(app_webroot, logged_as="alice") + assert can_access_webpage(app_webroot, logged_as="bob") From df49af0ad001b99086083798a2b6e888c9352a80 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 18:55:11 +0200 Subject: [PATCH 218/299] Redundant operation considering we're deleting all groups right after --- src/yunohost/data_migrations/0011_setup_group_permission.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index dd5b3c274..ae5a8bfb9 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -60,7 +60,6 @@ class MyMigration(Migration): ldap_map = read_yaml('/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml') try: - self.remove_if_exists("cn=sftpusers,ou=groups") self.remove_if_exists("ou=permission") self.remove_if_exists('ou=groups') From 96bc95656c6ad29fa59ae337a0f9f4f04c097261 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 19:22:31 +0200 Subject: [PATCH 219/299] Allow the migration to proceed if slapd config was manually modified, warn the user about where the conf will be backuped --- locales/en.json | 2 +- src/yunohost/data_migrations/0011_setup_group_permission.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/en.json b/locales/en.json index 5c3595782..3e1054069 100644 --- a/locales/en.json +++ b/locales/en.json @@ -346,7 +346,7 @@ "migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user…", "migration_0011_done": "Migration successful. You are now able to manage usergroups.", - "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your current configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration", + "migration_0011_slapd_config_will_be_overwritten": "It looks like you manually edited the slapd configuration. For this critical migration, YunoHost needs to force the update of the slapd configuration. The original files will be backuped in {conf_backup_folder}.", "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…", "migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index ae5a8bfb9..de28a3ad7 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -9,7 +9,7 @@ from moulinette.utils.filesystem import read_yaml from yunohost.tools import Migration from yunohost.user import user_group_create, user_group_update from yunohost.app import app_setting, app_list -from yunohost.regenconf import regen_conf +from yunohost.regenconf import regen_conf, BACKUP_CONF_DIR from yunohost.permission import permission_create, user_permission_update, permission_sync_to_user logger = getActionLogger('yunohost.migration') @@ -130,7 +130,7 @@ class MyMigration(Migration): ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) # By this we check if the have been customized if ldap_regen_conf_status and ldap_regen_conf_status['slapd']['pending']: - raise YunohostError("migration_0011_LDAP_config_dirty") + logger.warning("migration_0011_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR) # Backup LDAP and the apps settings before to do the migration logger.info(m18n.n("migration_0011_backup_before_migration")) From 9cecd71437d050696a5e98e676532c21e8396749 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 19:39:37 +0200 Subject: [PATCH 220/299] Fix permission_reset idempotency --- src/yunohost/permission.py | 4 ++++ src/yunohost/tests/test_permission.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 75e3f6037..97e5b4122 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -217,6 +217,10 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): if existing_permission is None: raise YunohostError('permission_not_found', permission=permission) + if existing_permission["allowed"] == ["all_users"]: + logger.warning("The permission was not updated all addition/removal requests already match the current state.") + return + # Update permission with default (all_users) operation_logger.related_to.append(('app', permission.split(".")[0])) diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index a9e16cfc6..0ddda4cec 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -306,6 +306,17 @@ def test_permission_reset(): assert res['blog.main']['allowed'] == ["all_users"] assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"]) + +def test_permission_reset_idempotency(): + # Reset permission + user_permission_reset("blog.main") + user_permission_reset("blog.main") + + res = user_permission_list(full=True)['permissions'] + assert res['blog.main']['allowed'] == ["all_users"] + assert set(res['blog.main']['corresponding_users']) == set(["alice", "bob"]) + + # # Error on update function # From 88794805eba3ececc5bc04aac2d41b4d09241bf7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 22:08:12 +0200 Subject: [PATCH 221/299] We probably don't need to have multiple urls per permissions ... --- data/helpers.d/setting | 62 ++++++++--------- locales/en.json | 2 +- src/yunohost/app.py | 68 ++++++++----------- src/yunohost/backup.py | 6 +- .../0011_setup_group_permission.py | 4 +- src/yunohost/permission.py | 38 ++++------- src/yunohost/tests/test_backuprestore.py | 16 ++--- src/yunohost/tests/test_permission.py | 56 +++++---------- 8 files changed, 105 insertions(+), 147 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index b2a647993..c911c5811 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -232,34 +232,36 @@ ynh_webpath_register () { # Create a new permission for the app # -# example: ynh_permission_create --permission admin --urls /admin +# example: ynh_permission_create --permission admin --url /admin # -# usage: ynh_permission_create --permission "permission" [--urls "url" ["url" ...]] +# usage: ynh_permission_create --permission "permission" [--url "url"] # | arg: permission - the name for the permission (by default a permission named "main" already exist) -# | arg: urls - (optional) a list of FULL urls for the permission (e.g. domain.tld/apps/admin) -# | arg: urls - (optional) a list of URLs to specify for the permission. +# | arg: url - (optional) URL for which access will be allowed/forbidden # -# URLs are assumed to be relative to the app domain/path if they start with '/'. -# For example: -# / -> domain.tld/app -# /admin -> domain.tld/app/admin -# domain.tld/app/api -> domain.tld/app/api +# If provided, 'url' is assumed to be relative to the app domain/path if they +# start with '/'. For example: +# / -> domain.tld/app +# /admin -> domain.tld/app/admin +# domain.tld/app/api -> domain.tld/app/api # -# URLs can be treated as regexes when they start with "re:". -# For example: -# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ -# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ +# 'url' can be later treated as a regex if it starts with "re:". +# For example: +# re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ +# re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ # ynh_permission_create() { - declare -Ar args_array=( [p]=permission= [u]=urls= ) + declare -Ar args_array=( [p]=permission= [u]=url= ) local permission local urls ynh_handle_getopts_args "$@" - if [[ -n ${urls:-} ]]; then - urls=",urls=['${urls//';'/"','"}']" + if [[ -n ${url:-} ]]; then + url="'$url'" + else + url="None" fi - yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission' ${urls:-}, sync_perm=False)" + + yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission', url=$url, sync_perm=False)" } # Remove a permission for the app (note that when the app is removed all permission is automatically removed) @@ -277,30 +279,28 @@ ynh_permission_delete() { yunohost tools shell -c "from yunohost.permission import permission_delete; permission_delete('$app.$permission', sync_perm=False)" } -# Manage urls related to a permission +# Redefine the url associated to a permission # -# usage: ynh_permission_urls --permission "permission" --add "url" ["url" ...] --remove "url" ["url" ...] +# usage: ynh_permission_url --permission "permission" --url "url" # | arg: permission - the name for the permission (by default a permission named "main" is removed automatically when the app is removed) -# | arg: add - (optional) a list of urls to add to the permission (see permission_create for details regarding their format) -# | arg: remove - (optional) a list of urls to remove from the permission (see permission_create for details regarding their format) +# | arg: url - (optional) URL for which access will be allowed/forbidden # -ynh_permission_urls() { - declare -Ar args_array=([p]=permission= [a]=add= [r]=remove=) +ynh_permission_url() { + declare -Ar args_array=([p]=permission= [u]=url=) local permission - local add - local remove + local url ynh_handle_getopts_args "$@" - if [[ -n ${add:-} ]]; then - add=",add=['${add//';'/"','"}']" - fi - if [[ -n ${remove:-} ]]; then - remove=",remove=['${remove//';'/"','"}']" + if [[ -n ${url:-} ]]; then + url="'$url'" + else + url="None" fi - yunohost tools shell -c "from yunohost.permission import permission_urls; permission_urls('$app.$permission' ${add:-} ${remove:-})" + yunohost tools shell -c "from yunohost.permission import permission_url; permission_url('$app.$permission', url=$url)" } + # Update a permission for the app # # usage: ynh_permission_update --permission "permission" --add "group" ["group" ...] --remove "group" ["group" ...] diff --git a/locales/en.json b/locales/en.json index 3e1054069..e3911a334 100644 --- a/locales/en.json +++ b/locales/en.json @@ -268,7 +268,7 @@ "log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain", "log_permission_create": "Create permission '{}'", "log_permission_delete": "Delete permission '{}'", - "log_permission_urls": "Update urls related to permission '{}'", + "log_permission_url": "Update url related to permission '{}'", "log_selfsigned_cert_install": "Install self signed certificate on '{}' domain", "log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate", "log_regen_conf": "Regenerate system configurations '{}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 4bda9ccf6..abb4387e5 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -454,33 +454,18 @@ def app_map(app=None, raw=False, user=None): return perm_domain, perm_path - this_app_perms = {p: i for p, i in permissions.items() if p.startswith(app_id + ".") and i["urls"]} + this_app_perms = {p: i for p, i in permissions.items() if p.startswith(app_id + ".") and i["url"]} for perm_name, perm_info in this_app_perms.items(): # If we're building the map for a specific user, check the user # actually is allowed for this specific perm if user and user not in perm_info["corresponding_users"] and "visitors" not in perm_info["allowed"]: continue - if len(perm_info["urls"]) > 1 or perm_info["urls"][0].startswith("re:"): - # - # Here we have a big conceptual issue about the sso ... - # Let me take a sip of coffee and turn off the music... - # - # Let's say we have an app foo which created a permission - # 'foo.admin' and added as url "/admin" and "/api" This - # permission got defined somehow as only accessible for group - # "admins". So both "/admin" and "/api" are protected. Good! - # - # Now if we really want users in group "admins" to access those - # uris, then each users in group "admins" need to have these - # urls in the ssowat dict for this user. Which corresponds to a - # tile. To put it otherwise : in the current code of ssowat, a - # permission = a tile = a url ! - # - # We also have an issue if the url define is a regex, because + if perm_info["url"].startswith("re:"): + # Here, we have an issue if the chosen url is a regex, because # the url we want to add to the dict is going to be turned into # a clickable link (or analyzed by other parts of yunohost # code...). To put it otherwise : in the current code of ssowat, - # you can't give access a user to a regex + # you can't give access a user to a regex. # # Instead, as drafted by Josue, we could rework the ssowat logic # about how routes and their permissions are defined. So for example, @@ -498,10 +483,10 @@ def app_map(app=None, raw=False, user=None): # protected/unprotected/skipped uris and regexes and we gotta # handle / migrate all the legacy stuff somehow if we don't # want to end up with a total mess in the future idk - logger.error("Permission %s can't be added to the SSOwat configuration because it uses multiple urls and/or uses a regex url" % perm_name) + logger.error("Permission %s can't be added to the SSOwat configuration because it doesn't support regexes so far..." % perm_name) continue - perm_domain, perm_path = _sanitized_absolute_url(perm_info["urls"][0]) + perm_domain, perm_path = _sanitized_absolute_url(perm_info["url"]) if perm_name.endswith(".main"): perm_label = label @@ -535,7 +520,6 @@ def app_change_url(operation_logger, app, domain, path): """ from yunohost.hook import hook_exec, hook_callback from yunohost.domain import _normalize_domain_path, _get_conflicting_apps - from yunohost.permission import permission_urls installed = _is_installed(app) if not installed: @@ -835,7 +819,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.log import OperationLogger - from yunohost.permission import user_permission_list, permission_create, permission_urls, permission_delete, permission_sync_to_user, user_permission_update + from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user, user_permission_update # Fetch or extract sources if not os.path.exists(INSTALL_TMP): @@ -994,7 +978,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Initialize the main permission for the app # After the install, if apps don't have a domain and path defined, the default url '/' is removed from the permission - permission_create(app_instance_name+".main", urls=["/"]) + permission_create(app_instance_name+".main", url="/") # Execute the app install script install_retcode = 1 @@ -1088,7 +1072,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu domain = app_settings.get('domain', None) path = app_settings.get('path', None) if not (domain and path): - permission_urls(app_instance_name + ".main", remove=["/"], sync_perm=False) + permission_url(app_instance_name + ".main", url=None, sync_perm=False) # Migrate classic public app still using the legacy unprotected_uris if app_settings.get("unprotected_uris", None) == "/": @@ -1178,7 +1162,7 @@ def app_addaccess(apps, users=[]): """ from yunohost.permission import user_permission_update - logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.") output = {} for app in apps: @@ -1199,7 +1183,7 @@ def app_removeaccess(apps, users=[]): """ from yunohost.permission import user_permission_update - logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.") output = {} for app in apps: @@ -1219,7 +1203,7 @@ def app_clearaccess(apps): """ from yunohost.permission import user_permission_reset - logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage permissions.") + logger.warning("/!\\ Packagers ! This app is using the legacy permission system. Please use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage permissions.") output = {} for app in apps: @@ -1329,7 +1313,7 @@ def app_setting(app, key, value=None, delete=False): if key in ['redirected_urls', 'redirected_regex']: value = yaml.load(value) if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: - logger.warning("/!\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,urls,update,delete} and the 'visitors' group to manage public/private access.") + logger.warning("/!\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage public/private access.") app_settings[key] = value _set_app_settings(app, app_settings) @@ -1562,20 +1546,24 @@ def app_ssowatconf(): # New permission system this_app_perms = {name: info for name, info in all_permissions.items() if name.startswith(app['id'] + ".")} for perm_name, perm_info in this_app_perms.items(): + + # Ignore permissions for which there's no url defined + if not perm_info["url"]: + continue + # FIXME : gotta handle regex-urls here... meh - urls = [_sanitized_absolute_url(url) for url in perm_info["urls"]] + url = _sanitized_absolute_url(perm_info["url"]) if "visitors" in perm_info["allowed"]: - unprotected_urls += urls + unprotected_urls.append(url) # Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier... - protected_urls = [u for u in protected_urls if u not in urls] + protected_urls = [u for u in protected_urls if u != url] else: # TODO : small optimization to implement : we don't need to explictly add all the app roots - - protected_urls += urls + protected_urls.append(url) # Legacy stuff : we remove now unprotected-urls that might have been declared as protected earlier... - unprotected_urls = [u for u in unprotected_urls if u not in urls] + unprotected_urls = [u for u in unprotected_urls if u != url] for domain in domains: skipped_urls.extend([domain + '/yunohost/admin', domain + '/yunohost/api']) @@ -1585,11 +1573,13 @@ def app_ssowatconf(): skipped_regex.append("^[^/]*/%.well%-known/autoconfig/mail/config%-v1%.1%.xml.*$") - permissions_per_url = {} - for permission_name, permission_infos in all_permissions.items(): - for url in permission_infos["urls"]: - permissions_per_url[url] = permission_infos['corresponding_users'] + for perm_name, perm_info in all_permissions.items(): + # Ignore permissions for which there's no url defined + if not perm_info["url"]: + continue + permissions_per_url[perm_info["url"]] = perm_info['corresponding_users'] + conf_dict = { 'portal_domain': main_domain, diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index c28160342..dcdb1adec 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1189,7 +1189,7 @@ class RestoreManager(): return from yunohost.user import user_group_list - from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list + from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list, permission_sync_to_user # Backup old permission for apps # We need to do that because in case of an app is installed we can't remove the permission for this app @@ -1251,7 +1251,7 @@ class RestoreManager(): for permission_name, permission_infos in old_apps_permission.items(): app_name = permission_name.split(".")[0] if _is_installed(app_name): - permission_create(permission_name, urls=permission_infos["urls"], sync_perm=False) + permission_create(permission_name, url=permission_infos["url"], sync_perm=False) user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"]) def _restore_apps(self): @@ -1362,7 +1362,7 @@ class RestoreManager(): for permission_name, permission_infos in permissions.items(): - permission_create(permission_name, urls=permission_infos.get("urls", [])) + permission_create(permission_name, url=permission_infos.get("url", None)) if "allowed" not in permission_infos: logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name)) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index de28a3ad7..880c5f54b 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -107,8 +107,8 @@ class MyMigration(Migration): path = app_setting(app, 'path') domain = app_setting(app, 'domain') - urls = "/" if domain and path else None - permission_create(app+".main", urls=urls, sync_perm=False) + url = "/" if domain and path else None + permission_create(app+".main", url=url, sync_perm=False) if permission: allowed_group = permission.split(',') user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 97e5b4122..6f9d63d69 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -73,7 +73,7 @@ def user_permission_list(short=False, full=False, ignore_system_perms=False): if full: permissions[name]["corresponding_users"] = [_ldap_path_extract(p, "uid") for p in infos.get('inheritPermission', [])] - permissions[name]["urls"] = infos.get("URL", []) + permissions[name]["url"] = infos.get("URL", [None])[0] if short: permissions = permissions.keys() @@ -260,27 +260,27 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): # # The followings methods are *not* directly exposed. # They are used to create/delete the permissions (e.g. during app install/remove) -# and by some app helpers to possibly add additional permissions and tweak the urls +# and by some app helpers to possibly add additional permissions # # @is_unit_operation() -def permission_create(operation_logger, permission, urls=None, sync_perm=True): +def permission_create(operation_logger, permission, url=None, sync_perm=True): """ Create a new permission for a specific application Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - urls -- list of URLs to specify for the permission. + url -- (optional) URL for which access will be allowed/forbidden - Urls are assumed to be relative to the app domain/path if they start with '/'. - For example: + If provided, 'url' is assumed to be relative to the app domain/path if they + start with '/'. For example: / -> domain.tld/app /admin -> domain.tld/app/admin domain.tld/app/api -> domain.tld/app/api - URLs can be later treated as regexes when they start with "re:". + 'url' can be later treated as a regex if it starts with "re:". For example: re:/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ @@ -316,8 +316,8 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): if permission.endswith(".main"): attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org'] - if urls: - attr_dict['URL'] = urls + if url: + attr_dict['URL'] = url operation_logger.related_to.append(('app', permission.split(".")[0])) operation_logger.start() @@ -335,15 +335,13 @@ def permission_create(operation_logger, permission, urls=None, sync_perm=True): @is_unit_operation() -def permission_urls(operation_logger, permission, add=None, remove=None, sync_perm=True): +def permission_url(operation_logger, permission, url=None, sync_perm=True): """ Update urls related to a permission for a specific application Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) - add -- List of URLs to add (c.f. permission_create for documentation about their format) - remove -- List of URLs to remove (c.f. permission_create for documentation about their format) - + url -- (optional) URL for which access will be allowed/forbidden """ from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -355,17 +353,9 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe raise YunohostError('permission_not_found', permission=permission) # Compute new url list + old_url = existing_permission["url"] - new_urls = copy.copy(existing_permission["urls"]) - - if add: - urls_to_add = [add] if not isinstance(add, list) else add - new_urls += urls_to_add - if remove: - urls_to_remove = [remove] if not isinstance(remove, list) else remove - new_urls = [u for u in new_urls if u not in urls_to_remove] - - if set(new_urls) == set(existing_permission["urls"]): + if old_url == url: logger.warning(m18n.n('permission_update_nothing_to_do')) return existing_permission @@ -375,7 +365,7 @@ def permission_urls(operation_logger, permission, add=None, remove=None, sync_pe operation_logger.start() try: - ldap.update('cn=%s,ou=permission' % permission, {'URL': new_urls}) + ldap.update('cn=%s,ou=permission' % permission, {'URL': [url]}) except Exception as e: raise YunohostError('permission_update_failed', permission=permission, error=e) diff --git a/src/yunohost/tests/test_backuprestore.py b/src/yunohost/tests/test_backuprestore.py index cab98089b..82d3da660 100644 --- a/src/yunohost/tests/test_backuprestore.py +++ b/src/yunohost/tests/test_backuprestore.py @@ -529,11 +529,11 @@ def test_backup_and_restore_permission_app(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == ["/"] - assert res['permissions_app.admin']['urls'] == ["/admin"] - assert res['permissions_app.dev']['urls'] == ["/dev"] + assert res['permissions_app.main']['url'] == "/" + assert res['permissions_app.admin']['url'] == "/admin" + assert res['permissions_app.dev']['url'] == "/dev" - assert res['permissions_app.main']['allowed'] == ["all_users"] + assert res['permissions_app.main']['allowed'] == ["visitors"] assert res['permissions_app.admin']['allowed'] == ["alice"] assert res['permissions_app.dev']['allowed'] == [] @@ -543,11 +543,11 @@ def test_backup_and_restore_permission_app(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == ["/"] - assert res['permissions_app.admin']['urls'] == ["/admin"] - assert res['permissions_app.dev']['urls'] == ["/dev"] + assert res['permissions_app.main']['url'] == "/" + assert res['permissions_app.admin']['url'] == "/admin" + assert res['permissions_app.dev']['url'] == "/dev" - assert res['permissions_app.main']['allowed'] == ["all_users"] + assert res['permissions_app.main']['allowed'] == ["visitors"] assert res['permissions_app.admin']['allowed'] == ["alice"] assert res['permissions_app.dev']['allowed'] == [] diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 0ddda4cec..8e536ec9e 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -6,7 +6,7 @@ from yunohost.app import app_install, app_remove, app_change_url, app_list, app_ from yunohost.user import user_list, user_info, user_create, user_delete, user_update, \ user_group_list, user_group_create, user_group_delete, user_group_update, user_group_info from yunohost.permission import user_permission_update, user_permission_list, user_permission_reset, \ - permission_create, permission_urls, permission_delete + permission_create, permission_delete, permission_url from yunohost.domain import _get_maindomain from yunohost.utils.error import YunohostError @@ -31,7 +31,7 @@ def setup_function(function): user_create("alice", "Alice", "White", "alice@" + maindomain, dummy_password) user_create("bob", "Bob", "Snow", "bob@" + maindomain, dummy_password) - permission_create("wiki.main", urls=["/"], sync_perm=False) + permission_create("wiki.main", url="/", sync_perm=False) permission_create("blog.main", sync_perm=False) user_permission_update("blog.main", remove="all_users", add="alice") @@ -202,7 +202,7 @@ def test_permission_list(): assert res['blog.main']['allowed'] == ["alice"] assert set(res['wiki.main']['corresponding_users']) == set(["alice", "bob"]) assert res['blog.main']['corresponding_users'] == ["alice"] - assert res['wiki.main']['urls'] == ["/"] + assert res['wiki.main']['url'] == "/" # # Create - Remove functions @@ -333,41 +333,19 @@ def test_permission_update_permission_that_doesnt_exist(): with pytest.raises(YunohostError): user_permission_update("doesnt.exist", add="alice") - # Permission url management -def test_permission_add_url(): - permission_urls("blog.main", add=["/testA"]) +def test_permission_redefine_url(): + permission_url("blog.main", url="/pwet") res = user_permission_list(full=True)['permissions'] - assert res["blog.main"]["urls"] == ["/testA"] - -def test_permission_add_another_url(): - permission_urls("wiki.main", add=["/testA"]) - - res = user_permission_list(full=True)['permissions'] - assert set(res["wiki.main"]["urls"]) == set(["/", "/testA"]) + assert res["blog.main"]["url"] == "/pwet" def test_permission_remove_url(): - permission_urls("wiki.main", remove=["/"]) + permission_url("blog.main", url=None) res = user_permission_list(full=True)['permissions'] - assert res["wiki.main"]["urls"] == [] - -def test_permission_add_url_already_added(): - res = user_permission_list(full=True)['permissions'] - assert res["wiki.main"]["urls"] == ["/"] - - permission_urls("wiki.main", add=["/"]) - - res = user_permission_list(full=True)['permissions'] - assert res["wiki.main"]["urls"] == ["/"] - -def test_permission_remove_url_not_added(): - permission_urls("wiki.main", remove=["/doesnt_exist"]) - - res = user_permission_list(full=True)['permissions'] - assert res['wiki.main']['urls'] == ["/"] + assert res["blog.main"]["url"] is None # # Application interaction @@ -381,9 +359,9 @@ def test_permission_app_install(): assert "permissions_app.main" in res assert "permissions_app.admin" in res assert "permissions_app.dev" in res - assert res['permissions_app.main']['urls'] == ["/"] - assert res['permissions_app.admin']['urls'] == ["/admin"] - assert res['permissions_app.dev']['urls'] == ["/dev"] + assert res['permissions_app.main']['url'] == "/" + assert res['permissions_app.admin']['url'] == "/admin" + assert res['permissions_app.dev']['url'] == "/dev" assert res['permissions_app.main']['allowed'] == ["all_users"] assert set(res['permissions_app.main']['corresponding_users']) == set(["alice", "bob"]) @@ -416,16 +394,16 @@ def test_permission_app_change_url(): # FIXME : should rework this test to look for differences in the generated app map / app tiles ... res = user_permission_list(full=True)['permissions'] - assert res['permissions_app.main']['urls'] == ["/"] - assert res['permissions_app.admin']['urls'] == ["/admin"] - assert res['permissions_app.dev']['urls'] == ["/dev"] + assert res['permissions_app.main']['url'] == "/" + assert res['permissions_app.admin']['url'] == "/admin" + assert res['permissions_app.dev']['url'] == "/dev" app_change_url("permissions_app", maindomain, "/newchangeurl") res = user_permission_list(full=True)['permissions'] - assert res['permissions_app.main']['urls'] == ["/"] - assert res['permissions_app.admin']['urls'] == ["/admin"] - assert res['permissions_app.dev']['urls'] == ["/dev"] + assert res['permissions_app.main']['url'] == "/" + assert res['permissions_app.admin']['url'] == "/admin" + assert res['permissions_app.dev']['url'] == "/dev" def test_permission_app_propagation_on_ssowat(): From 2617fd2487d8940309b7a19cb3011985eff3a327 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 22:11:19 +0200 Subject: [PATCH 222/299] Fix issues related to regerating ssowat conf while hacking permissions... --- src/yunohost/backup.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index dcdb1adec..90f795ea5 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1245,14 +1245,17 @@ class RestoreManager(): # Remove all permission for all app which is still in the LDAP for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys(): - permission_delete(permission_name, force=True) + permission_delete(permission_name, force=True, sync_perm=False) # Restore permission for the app which is installed for permission_name, permission_infos in old_apps_permission.items(): app_name = permission_name.split(".")[0] if _is_installed(app_name): permission_create(permission_name, url=permission_infos["url"], sync_perm=False) - user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"]) + user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"], sync_perm=False) + + permission_sync_to_user() + def _restore_apps(self): """Restore all apps targeted""" @@ -1290,7 +1293,7 @@ class RestoreManager(): restore_app_failed -- Raised if the restore bash script failed """ from yunohost.user import user_group_list - from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update + from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update, permission_sync_to_user def copytree(src, dst, symlinks=False, ignore=None): for item in os.listdir(src): @@ -1362,7 +1365,7 @@ class RestoreManager(): for permission_name, permission_infos in permissions.items(): - permission_create(permission_name, url=permission_infos.get("url", None)) + permission_create(permission_name, url=permission_infos.get("url", None), sync_perm=False) if "allowed" not in permission_infos: logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name)) @@ -1370,7 +1373,9 @@ class RestoreManager(): should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups] current_allowed = user_permission_list()["permissions"][permission_name]["allowed"] if should_be_allowed != current_allowed: - user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed) + user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed, sync_perm=False) + + permission_sync_to_user() os.remove('%s/permissions.yml' % app_settings_new_path) else: From c315df926999376fd79b81dd32072d8d3f06e4a9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 22:48:47 +0200 Subject: [PATCH 223/299] Wokay, getting tired of breaking the entire permission/group ecosystem because of bugs when developing. --- src/yunohost/app.py | 3 +++ src/yunohost/user.py | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index abb4387e5..75bf12f3d 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -433,6 +433,9 @@ def app_map(app=None, raw=False, user=None): continue # Users must at least have access to the main permission to have access to extra permissions if user: + if not app_id + ".main" in permissions: + logger.warning("Uhoh, no main permission was found for app %s ... sounds like an app was only partially removed due to another bug :/" % app_id) + continue main_perm = permissions[app_id + ".main"] if user not in main_perm["corresponding_users"] and "visitors" not in main_perm["allowed"]: continue diff --git a/src/yunohost/user.py b/src/yunohost/user.py index f4e550230..72aa36184 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -268,7 +268,12 @@ def user_delete(operation_logger, username, purge=False): # remove the member from the group if username != group and username in infos["members"]: user_group_update(group, remove=username, sync_perm=False) - user_group_delete(username, force=True, sync_perm=True) + + # Delete primary group if it exists (why wouldnt it exists ? because some + # epic bug happened somewhere else and only a partial removal was + # performed...) + if username in user_group_list()['groups'].keys(): + user_group_delete(username, force=True, sync_perm=True) ldap = _get_ldap_interface() try: From e7d1cc5f9449f77bf575c82f9faae5f2f2168b41 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 22:55:06 +0200 Subject: [PATCH 224/299] Allow to specify right away what groups to allow for a permission when creating it --- data/helpers.d/setting | 16 +++++++++++----- src/yunohost/app.py | 2 +- src/yunohost/backup.py | 11 ++++------- .../0011_setup_group_permission.py | 8 +++++--- src/yunohost/permission.py | 16 ++++++++++++++-- src/yunohost/tests/test_permission.py | 9 +++++++++ 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index c911c5811..a8d2919a4 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -232,11 +232,12 @@ ynh_webpath_register () { # Create a new permission for the app # -# example: ynh_permission_create --permission admin --url /admin +# example: ynh_permission_create --permission admin --url /admin --allowed alice bob # -# usage: ynh_permission_create --permission "permission" [--url "url"] +# usage: ynh_permission_create --permission "permission" [--url "url"] [--allowed group1 group2] # | arg: permission - the name for the permission (by default a permission named "main" already exist) # | arg: url - (optional) URL for which access will be allowed/forbidden +# | arg: allowed - (optional) A list of group/user to allow for the permission # # If provided, 'url' is assumed to be relative to the app domain/path if they # start with '/'. For example: @@ -250,9 +251,10 @@ ynh_webpath_register () { # re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ # ynh_permission_create() { - declare -Ar args_array=( [p]=permission= [u]=url= ) + declare -Ar args_array=( [p]=permission= [u]=url= [a]=allowed= ) local permission - local urls + local url + local allowed ynh_handle_getopts_args "$@" if [[ -n ${url:-} ]]; then @@ -261,7 +263,11 @@ ynh_permission_create() { url="None" fi - yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission', url=$url, sync_perm=False)" + if [[ -n ${allowed:-} ]]; then + allowed=",allowed=['${allowed//';'/"','"}']" + fi + + yunohost tools shell -c "from yunohost.permission import permission_create; permission_create('$app.$permission', url=$url ${allowed:-} , sync_perm=False)" } # Remove a permission for the app (note that when the app is removed all permission is automatically removed) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 75bf12f3d..7235535cd 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -981,7 +981,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Initialize the main permission for the app # After the install, if apps don't have a domain and path defined, the default url '/' is removed from the permission - permission_create(app_instance_name+".main", url="/") + permission_create(app_instance_name+".main", url="/", allowed=["all_users"]) # Execute the app install script install_retcode = 1 diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 90f795ea5..c57ab6685 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -1251,8 +1251,7 @@ class RestoreManager(): for permission_name, permission_infos in old_apps_permission.items(): app_name = permission_name.split(".")[0] if _is_installed(app_name): - permission_create(permission_name, url=permission_infos["url"], sync_perm=False) - user_permission_update(permission_name, remove="all_users", add=permission_infos["allowed"], sync_perm=False) + permission_create(permission_name, url=permission_infos["url"], allowed=permission_infos["allowed"], sync_perm=False) permission_sync_to_user() @@ -1365,15 +1364,13 @@ class RestoreManager(): for permission_name, permission_infos in permissions.items(): - permission_create(permission_name, url=permission_infos.get("url", None), sync_perm=False) - if "allowed" not in permission_infos: logger.warning("'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself." % (permission_name, app_instance_name)) + should_be_allowed = ["all_users"] else: should_be_allowed = [g for g in permission_infos["allowed"] if g in existing_groups] - current_allowed = user_permission_list()["permissions"][permission_name]["allowed"] - if should_be_allowed != current_allowed: - user_permission_update(permission_name, remove=current_allowed, add=should_be_allowed, sync_perm=False) + + permission_create(permission_name, url=permission_infos.get("url", None), allowed=should_be_allowed, sync_perm=False) permission_sync_to_user() diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 880c5f54b..3114817b9 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -108,10 +108,12 @@ class MyMigration(Migration): domain = app_setting(app, 'domain') url = "/" if domain and path else None - permission_create(app+".main", url=url, sync_perm=False) if permission: - allowed_group = permission.split(',') - user_permission_update(app+".main", remove="all_users", add=allowed_group, sync_perm=False) + allowed_groups = permission.split(',') + else: + allowed_groups = ["all_users"] + permission_create(app+".main", url=url, allowed=allowed_groups, sync_perm=False) + app_setting(app, 'allowed_users', delete=True) # Migrate classic public app still using the legacy unprotected_uris diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 6f9d63d69..426ecd10f 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -266,13 +266,14 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): @is_unit_operation() -def permission_create(operation_logger, permission, url=None, sync_perm=True): +def permission_create(operation_logger, permission, url=None, allowed=None, sync_perm=True): """ Create a new permission for a specific application Keyword argument: permission -- Name of the permission (e.g. mail or nextcloud or wordpress.editors) url -- (optional) URL for which access will be allowed/forbidden + allowed -- (optional) A list of group/user to allow for the permission If provided, 'url' is assumed to be relative to the app domain/path if they start with '/'. For example: @@ -286,6 +287,7 @@ def permission_create(operation_logger, permission, url=None, sync_perm=True): re:domain.tld/app/api/[A-Z]*$ -> domain.tld/app/api/[A-Z]*$ """ + from yunohost.user import user_group_list from yunohost.utils.ldap import _get_ldap_interface ldap = _get_ldap_interface() @@ -312,8 +314,18 @@ def permission_create(operation_logger, permission, url=None, sync_perm=True): 'gidNumber': gid, } + # If who should be allowed is explicitly provided, use this info + if allowed: + if not isinstance(allowed, list): + allowed = [allowed] + # (though first we validate that the targets actually exist) + all_existing_groups = user_group_list()['groups'].keys() + for g in allowed: + if g not in all_existing_groups: + raise YunohostError('group_unknown', group=g) + attr_dict['groupPermission'] = ['cn=%s,ou=groups,dc=yunohost,dc=org' % g for g in allowed] # For main permission, we add all users by default - if permission.endswith(".main"): + elif permission.endswith(".main"): attr_dict['groupPermission'] = ['cn=all_users,ou=groups,dc=yunohost,dc=org'] if url: diff --git a/src/yunohost/tests/test_permission.py b/src/yunohost/tests/test_permission.py index 8e536ec9e..5e1246793 100644 --- a/src/yunohost/tests/test_permission.py +++ b/src/yunohost/tests/test_permission.py @@ -226,6 +226,15 @@ def test_permission_create_extra(): assert "all_users" not in res['site.test']['allowed'] assert res['site.test']['corresponding_users'] == [] + +def test_permission_create_with_allowed(): + permission_create("site.test", allowed=["alice"]) + + res = user_permission_list(full=True)['permissions'] + assert "site.test" in res + assert res['site.test']['allowed'] == ["alice"] + + def test_permission_delete(): permission_delete("wiki.main", force=True) From 4bdcfb4373bfc0ce44931df209fb5d949a3d3768 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 23:16:07 +0200 Subject: [PATCH 225/299] Implement / fix i18n strings --- locales/en.json | 3 +++ .../data_migrations/0011_setup_group_permission.py | 2 +- src/yunohost/permission.py | 13 ++++--------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index e3911a334..c6a6a440e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -415,9 +415,12 @@ "permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'", "permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'", "permission_already_exist": "Permission '{permission}' already exists", + "permission_already_up_to_date": "The permission was not updated because the addition/removal requests already match the current state.", "permission_cannot_remove_main": "Removing a main permission is not allowed", "permission_created": "Permission '{permission:s}' created", "permission_creation_failed": "Could not create permission '{permission}': {error}", + "permission_currently_allowed_for_visitors": "This permission is currently granted to visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups it is currently granted to.", + "permission_currently_allowed_for_all_users": "This permission is currently granted to all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups it is currently granted to.", "permission_deleted": "Permission '{permission:s}' deleted", "permission_deletion_failed": "Could not delete permission '{permission}': {error}", "permission_not_found": "Permission '{permission:s}' not found", diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 3114817b9..9ba2268d9 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -132,7 +132,7 @@ class MyMigration(Migration): ldap_regen_conf_status = regen_conf(names=['slapd'], dry_run=True) # By this we check if the have been customized if ldap_regen_conf_status and ldap_regen_conf_status['slapd']['pending']: - logger.warning("migration_0011_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR) + logger.warning(m18n.n("migration_0011_slapd_config_will_be_overwritten", conf_backup_folder=BACKUP_CONF_DIR)) # Backup LDAP and the apps settings before to do the migration logger.info(m18n.n("migration_0011_backup_before_migration")) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 426ecd10f..4cfbc214f 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -144,18 +144,13 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if len(new_allowed_groups) > 1: if "all_users" in new_allowed_groups: - # FIXME : i18n - # FIXME : write a better explanation ? - logger.warning("This permission is currently granted to all users in addition to other groups. You probably want to either remove the 'all_users' permission or remove the other groups it is currently granted to.") + logger.warning(m18n.n("permission_currently_allowed_for_all_users")) if "visitors" in new_allowed_groups: - # FIXME : i18n - # FIXME : write a better explanation ? - logger.warning("This permission is currently granted to visitors in addition to other groups. You probably want to either remove the 'visitors' permission or remove the other groups it is currently granted to.") + logger.warning(m18n.n("permission_currently_allowed_for_visitors")) # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): - # FIXME : i18n - logger.warning("The permission was not updated all addition/removal requests already match the current state.") + logger.warning("permission_already_up_to_date") return # Commit the new allowed group list @@ -218,7 +213,7 @@ def user_permission_reset(operation_logger, permission, sync_perm=True): raise YunohostError('permission_not_found', permission=permission) if existing_permission["allowed"] == ["all_users"]: - logger.warning("The permission was not updated all addition/removal requests already match the current state.") + logger.warning(m18n.n("permission_already_up_to_date")) return # Update permission with default (all_users) From e4163136bbe6c7eff26102767400c0a99cb702c0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 9 Oct 2019 23:40:50 +0200 Subject: [PATCH 226/299] Don't attempt to delete the 'visitors' group during user/group tests --- src/yunohost/tests/test_user-group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/tests/test_user-group.py b/src/yunohost/tests/test_user-group.py index 30bdeb017..53fded94c 100644 --- a/src/yunohost/tests/test_user-group.py +++ b/src/yunohost/tests/test_user-group.py @@ -14,7 +14,7 @@ def clean_user_groups(): user_delete(u) for g in user_group_list()['groups']: - if g != "all_users": + if g not in ["all_users", "visitors"]: user_group_delete(g) def setup_function(function): From e48036a0829ed2754d2c39e033c78dd8b6d07c84 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 10 Oct 2019 00:05:20 +0200 Subject: [PATCH 227/299] Fix test about private app installs --- src/yunohost/tests/test_apps.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py index fc44ef105..fb2f13c3f 100644 --- a/src/yunohost/tests/test_apps.py +++ b/src/yunohost/tests/test_apps.py @@ -119,10 +119,10 @@ def app_is_exposed_on_http(domain, path, message_in_page): return False -def install_legacy_app(domain, path): +def install_legacy_app(domain, path, public=True): app_install("./tests/apps/legacy_app_ynh", - args="domain=%s&path=%s" % (domain, path), + args="domain=%s&path=%s&is_public=%s" % (domain, path, 1 if public else 0), force=True) @@ -180,13 +180,7 @@ def test_legacy_app_install_secondary_domain_on_root(secondary_domain): def test_legacy_app_install_private(secondary_domain): - install_legacy_app(secondary_domain, "/legacy") - - settings = open("/etc/yunohost/apps/legacy_app/settings.yml", "r").read() - new_settings = settings.replace("\nunprotected_uris: /", "") - assert new_settings != settings - open("/etc/yunohost/apps/legacy_app/settings.yml", "w").write(new_settings) - app_ssowatconf() + install_legacy_app(secondary_domain, "/legacy", public=False) assert app_is_installed(secondary_domain, "legacy_app") assert not app_is_exposed_on_http(secondary_domain, "/legacy", "This is a dummy app") From 2623d38567d18980265b8b30cdf1786f89355ad5 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 10 Oct 2019 00:06:36 +0200 Subject: [PATCH 228/299] Annnnnd Alex was drunk and released an epic stupid bug in stable --- src/yunohost/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 421be9b60..88204f3ff 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -2569,9 +2569,8 @@ def _validate_and_normalize_webpath(manifest, args_dict, app_folder): and re.search(r"(ynh_webpath_register|yunohost app checkurl)", install_script_content): domain = domain_args[0][1] - conflicts = _get_conflicting_apps(domain, "/") - - raise YunohostError('app_full_domain_unavailable', domain=domain) + if _get_conflicting_apps(domain, "/"): + raise YunohostError('app_full_domain_unavailable', domain=domain) def _make_environment_dict(args_dict, prefix="APP_ARG_"): From f2db3d34dd21fdf8d170fbd3eaea42ddb11283c9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 10 Oct 2019 00:08:47 +0200 Subject: [PATCH 229/299] Update changelog for 3.6.5.2 --- debian/changelog | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 6d5a16f2c..1d13b6290 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,14 @@ +yunohost (3.6.5.2) stable; urgency=low + + - [fix] Alex was drunk and released an epic stupid bug in stable (2623d385) + + -- Alexandre Aubin Thu, 10 Oct 2019 01:00:00 +0000 + yunohost (3.6.5.1) stable; urgency=low - [mod] Change maxretry of fail2ban from 6 to 10 (fe8fd1b) - -- Alexandre Aubin Mon, 08 Oct 2019 20:00:00 +0000 + -- Alexandre Aubin Tue, 08 Oct 2019 20:00:00 +0000 yunohost (3.6.5) stable; urgency=low @@ -11,7 +17,7 @@ yunohost (3.6.5) stable; urgency=low - [fix] Epicly ugly workaround for the goddamn dependency nighmare about sury fucking up php7.0 dependencies (#809) - [fix] Support logfiles not ending with .log in logrotate ... (#810) - -- Alexandre Aubin Mon, 08 Oct 2019 19:00:00 +0000 + -- Alexandre Aubin Tue, 08 Oct 2019 19:00:00 +0000 yunohost (3.6.4.6) stable; urgency=low From 38fd969f94c90a64e9119ee3b78c8f2c62c169e0 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Mon, 7 Oct 2019 05:19:53 +0000 Subject: [PATCH 230/299] Translated using Weblate (Esperanto) Currently translated at 100.0% (553 of 553 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 422 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 420 insertions(+), 2 deletions(-) diff --git a/locales/eo.json b/locales/eo.json index 1d367260d..d27c0171c 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -106,7 +106,7 @@ "app_package_need_update": "La pakaĵo {app} devas esti ĝisdatigita por sekvi YunoHost-ŝanĝojn", "backup_nothings_done": "Nenio por ŝpari", "backup_applying_method_custom": "Nomante la kutiman rezervan metodon '{metodo:s}' …", - "appslist_fetched": "Ĝisdatigita aplika listo {appslist:s} elprenita", + "appslist_fetched": "Ĝisdatigita aplika listo {appslist:s}", "backup_app_failed": "Ne eblis rezervi la programon '{app:s}'", "app_upgrade_some_app_failed": "Iuj aplikoj ne povis esti altgradigitaj", "app_start_remove": "Forigo de apliko {app} …", @@ -134,5 +134,423 @@ "ask_path": "Pado", "app_upgraded": "{app:s} altgradigita", "backup_deleted": "Rezerva forigita", - "backup_csv_addition_failed": "Ne povis aldoni dosierojn al sekurkopio en la CSV-dosiero" + "backup_csv_addition_failed": "Ne povis aldoni dosierojn al sekurkopio en la CSV-dosiero", + "dpkg_lock_not_available": "Ĉi tiu komando ne povas funkcii nun ĉar alia programo uzas la seruron de dpkg (la administrilo de paka sistemo)", + "migration_0003_yunohost_upgrade": "Komenci la ĝisdatigon de YunoHost-pako ... La migrado finiĝos, sed la efektiva ĝisdatigo okazos tuj poste. Post kiam la operacio finiĝos, vi eble devos ensaluti denove sur la retpaĝo.", + "domain_dyndns_root_unknown": "Nekonata radika domajno DynDNS", + "field_invalid": "Nevalida kampo '{:s}'", + "log_app_makedefault": "Faru '{}' la defaŭlta apliko", + "migration_0003_still_on_jessie_after_main_upgrade": "Io okazis malbone dum la ĉefa ĝisdatigo: Ĉu la sistemo ankoraŭ estas en Jessie‽ Por esplori la aferon, bonvolu rigardi {log}:s …", + "migration_0011_can_not_backup_before_migration": "La sekurkopio de la sistemo antaŭ la migrado malsukcesis. Migrado malsukcesis. Eraro: {error:s}", + "migration_0011_create_group": "Krei grupon por ĉiu uzanto…", + "backup_system_part_failed": "Ne eblis sekurkopi la sistemon de '{part:s}'", + "global_settings_setting_security_postfix_compatibility": "Kongruo vs sekureca kompromiso por la Postfix-servilo. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)", + "group_unknown": "La grupo '{group:s}' estas nekonata", + "mailbox_disabled": "Retpoŝto malŝaltita por uzanto {user:s}", + "migration_description_0011_setup_group_permission": "Agordu uzantogrupon kaj starigu permeson por programoj kaj servoj", + "migration_0011_backup_before_migration": "Krei sekurkopion de LDAP-datumbazo kaj agordojn antaŭ la efektiva migrado.", + "migration_0011_LDAP_config_dirty": "Similas ke vi agordis vian LDAP-agordon. Por ĉi tiu migrado la LDAP-agordo bezonas esti ĝisdatigita.\nVi devas konservi vian aktualan agordon, reintaligi la originalan agordon per funkciado de \"yunohost iloj regen-conf -f\" kaj reprovi la migradon", + "migration_0011_migrate_permission": "Migrado de permesoj de agordoj al aplikoj al LDAP…", + "migration_0011_migration_failed_trying_to_rollback": "Migrado malsukcesis ... provante reverti la sistemon.", + "migrations_dependencies_not_satisfied": "Ne eblas kuri migradon {id} ĉar unue vi devas ruli ĉi tiujn migradojn: {dependencies_id}", + "migrations_failed_to_load_migration": "Ne povis ŝarĝi migradon {id}: {error}", + "migrations_exclusive_options": "'--auto', '--skip' kaj '--force-rerun' estas reciproke ekskluzivaj ebloj.", + "migrations_must_provide_explicit_targets": "Vi devas provizi eksplicitajn celojn kiam vi uzas '--skip' aŭ '--force-rerun'", + "permission_update_failed": "Ne povis ĝisdatigi permeson '{permission}': {error}", + "permission_updated": "Ĝisdatigita \"{permission:s}\" rajtigita", + "permission_update_nothing_to_do": "Neniuj permesoj ĝisdatigi", + "tools_upgrade_cant_hold_critical_packages": "Ne povis teni kritikajn pakojn…", + "upnp_dev_not_found": "Neniu UPnP-aparato trovita", + "migration_description_0012_postgresql_password_to_md5_authentication": "Devigu PostgreSQL-aŭtentigon uzi MD5 por lokaj ligoj", + "migration_0011_done": "Migrado sukcesis. Vi nun kapablas administri uzantajn grupojn.", + "migration_0011_LDAP_update_failed": "Ne povis ĝisdatigi LDAP. Eraro: {error:s}", + "pattern_password": "Devas esti almenaŭ 3 signoj longaj", + "root_password_desynchronized": "La pasvorta administranto estis ŝanĝita, sed YunoHost ne povis propagandi ĉi tion al la radika pasvorto!", + "service_remove_failed": "Ne povis forigi la servon '{service:s}'", + "migration_0003_fail2ban_upgrade": "Komenci la ĝisdatigon Fail2Ban…", + "backup_permission": "Rezerva permeso por app {app:s}", + "log_user_group_delete": "Forigi grupon '{}'", + "log_user_group_update": "Ĝisdatigi grupon '{}'", + "migration_0005_postgresql_94_not_installed": "PostgreSQL ne estis instalita en via sistemo. Nenio por fari.", + "dyndns_provider_unreachable": "Ne povas atingi Dyndns-provizanton {provider}: ĉu via YunoHost ne estas ĝuste konektita al la interreto aŭ la dynette-servilo malŝaltiĝas.", + "good_practices_about_user_password": "Vi nun estas por difini novan uzantan pasvorton. La pasvorto devas esti almenaŭ 8 signoj - kvankam estas bone praktiki uzi pli longan pasvorton (t.e. pasfrazon) kaj / aŭ variaĵon de signoj (majuskloj, minuskloj, ciferoj kaj specialaj signoj).", + "group_updated": "Ĝisdatigita \"{group}\" grupo", + "group_already_exist": "Grupo {group} jam ekzistas", + "group_already_exist_on_system": "Grupo {group} jam ekzistas en la sistemaj grupoj", + "group_cannot_be_edited": "La grupo {group} ne povas esti redaktita permane.", + "group_cannot_be_deleted": "La grupo {group} ne povas esti forigita permane.", + "group_update_failed": "Ne povis ĝisdatigi la grupon '{group}': {error}", + "group_user_already_in_group": "Uzanto {user} jam estas en grupo {group}", + "group_user_not_in_group": "Uzanto {user} ne estas en grupo {group}", + "installation_complete": "Kompleta instalado", + "log_category_404": "La loga kategorio '{category}' ne ekzistas", + "log_permission_create": "Krei permeson '{}'", + "log_permission_delete": "Forigi permeson '{}'", + "log_permission_urls": "Ĝisdatigu URLojn rilatajn al permeso '{}'", + "log_user_group_create": "Krei grupon '{}'", + "log_user_permission_update": "Mise à jour des accès pour la permission '{}'", + "log_user_permission_reset": "Restarigi permeson '{}'", + "mail_forward_remove_failed": "Ne povis forigi retpoŝton plusendante '{mail:s}'", + "migration_0011_rollback_success": "Sistemo ruliĝis reen.", + "migration_0011_update_LDAP_database": "Ĝisdatigante LDAP-datumbazon…", + "migration_0011_update_LDAP_schema": "Ĝisdatigante LDAP-skemon…", + "migration_0011_failed_to_remove_stale_object": "Malsukcesis forigi neokazan objekton {dn}: {error}", + "migrations_already_ran": "Tiuj migradoj estas jam faritaj: {ids}", + "migrations_no_such_migration": "Estas neniu migrado nomata {id}", + "permission_already_allowed": "Grupo '{group}' jam havas permeson '{permission}' ebligita'", + "permission_already_disallowed": "Grupo '{group}' jam havas permeson '{permission}' malebligita'", + "permission_cannot_remove_main": "Forigo de ĉefa permeso ne rajtas", + "permission_creation_failed": "Ne povis krei permeson '{permission}': {error}", + "tools_update_failed_to_app_fetchlist": "Ne povis ĝisdatigi la aparatojn de YunoHost ĉar: {error}", + "user_already_exists": "Uzanto {uzanto} jam ekzistas", + "migrations_pending_cant_rerun": "Tiuj migradoj ankoraŭ estas pritraktataj, do ne plu rajtas esti ekzekutitaj: {ids}", + "migrations_running_forward": "Kuranta migrado {id}…", + "migrations_success_forward": "Migrado {id} kompletigita", + "operation_interrupted": "La operacio estis permane interrompita?", + "permission_created": "Permesita '{permission:s}' kreita", + "permission_deleted": "Permesita \"{permission:s}\" forigita", + "permission_deletion_failed": "Ne povis forigi permeson '{permission}': {error}", + "permission_not_found": "Permesita \"{permission:s}\" ne trovita", + "restore_not_enough_disk_space": "Ne sufiĉa spaco (spaco: {free_space:d} B, necesa spaco: {needed_space:d} B, sekureca marĝeno: {margin:d} B)", + "tools_upgrade_regular_packages": "Nun ĝisdatigi 'regulajn' (ne-yunohost-rilatajn) pakojn …", + "tools_upgrade_special_packages_explanation": "Ĉi tiu ago finiĝos, sed la fakta speciala ĝisdatigo daŭros en fono. Bonvolu ne komenci iun alian agon en via servilo en la sekvaj ~ 10 minutoj (depende de via aparata rapideco). Unufoje mi plenumis, vi eble devos ensaluti en la retpaĝo. La ĝisdatiga registro estos havebla en Iloj → Madero (sur la retpaĝo) aŭ tra 'yunohost-registro-listo' (el la komandlinio).", + "unrestore_app": "App '{app:s}' ne restarigos", + "group_created": "Grupo '{group}' kreita", + "group_creation_failed": "Ne povis krei la grupon '{group}': {error}", + "group_deleted": "Grupo '{group}' forigita", + "group_deletion_failed": "Ne povis forigi la grupon '{group}': {error}", + "migrations_not_pending_cant_skip": "Tiuj migradoj ankoraŭ ne estas pritraktataj, do ne eblas preterlasi: {ids}", + "permission_already_exist": "Permesita '{permission}' jam ekzistas", + "domain_created": "Domajno kreita", + "migrate_tsig_wait_2": "2 minutoj …", + "log_user_create": "Aldonu uzanton '{}'", + "ip6tables_unavailable": "Vi ne povas ludi kun ip6tabloj ĉi tie. Vi estas en ujo aŭ via kerno ne subtenas ĝin", + "mail_unavailable": "Ĉi tiu retpoŝta adreso estas rezervita kaj aŭtomate estos atribuita al la unua uzanto", + "certmanager_domain_dns_ip_differs_from_public_ip": "La DNS 'A' rekordo por la domajno '{domain:s}' diferencas de ĉi tiu IP-servilo. Se vi lastatempe modifis vian A-registron, bonvolu atendi ĝin propagandi (iuj DNS-disvastigaj kontroliloj estas disponeblaj interrete). (Se vi scias, kion vi faras, uzu '--no-checks' por malŝalti tiujn ĉekojn.)", + "tools_upgrade_special_packages_completed": "Plenumis la ĝisdatigon de pakaĵoj de YunoHost.\nPremu [Enter] por retrovi la komandlinion", + "log_remove_on_failed_install": "Forigu '{}' post malsukcesa instalado", + "regenconf_file_manually_modified": "La agorddosiero '{conf}' estis modifita permane kaj ne estos ĝisdatigita", + "regenconf_would_be_updated": "La agordo estus aktualigita por la kategorio '{category}'", + "certmanager_cert_install_success_selfsigned": "Mem-subskribita atestilo nun instalita por la domajno '{domain:s}'", + "global_settings_unknown_setting_from_settings_file": "Nekonata ŝlosilo en agordoj: '{setting_key:s}', forĵetu ĝin kaj konservu ĝin en /etc/yunohost/settings-unknown.json", + "regenconf_file_backed_up": "Agordodosiero '{conf}' estis rezervita al '{backup}'", + "migration_0007_cannot_restart": "SSH ne rekomencas post provi nuligi la migradan numeron 6.", + "migration_description_0006_sync_admin_and_root_passwords": "Sinkronigu admin kaj radikajn pasvortojn", + "updating_app_lists": "Akirante haveblajn ĝisdatigojn por aplikoj…", + "iptables_unavailable": "Vi ne povas ludi kun iptables ĉi tie. Vi estas en ujo aŭ via kerno ne subtenas ĝin", + "global_settings_cant_write_settings": "Ne eblis konservi agordojn, tial: {reason:s}", + "service_added": "La servo '{service:s}' aldonis", + "upnp_disabled": "UPnP malŝaltis", + "service_started": "Servo '{service:s}' komenciĝis", + "port_already_opened": "Haveno {port:d} estas jam malfermita por {ip_version:s} rilatoj", + "installation_failed": "Io okazis malbone kun la instalado", + "network_check_mx_ko": "DNS MX-rekordo ne estas agordita", + "migrate_tsig_wait_3": "1 minuto …", + "certmanager_conflicting_nginx_file": "Ne povis prepari domajnon por ACME-defio: la agordo de NGINX {filepath:s} konfliktas kaj unue devas esti forigita", + "upgrading_packages": "Ĝisdatigi pakojn…", + "custom_app_url_required": "Vi devas provizi URL por altgradigi vian kutimon app {app: s}", + "service_reload_failed": "Ne povis reŝargi la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "packages_upgrade_failed": "Ne povis ĝisdatigi ĉiujn pakojn", + "hook_json_return_error": "Ne povis legi revenon de hoko {path:s}. Eraro: {msg:s}. Kruda enhavo: {raw_content}", + "dyndns_cron_removed": "DynDNS cron-laboro forigita", + "dyndns_key_not_found": "DNS-ŝlosilo ne trovita por la domajno", + "custom_appslist_name_required": "Vi devas doni nomon por via kutima app-listo", + "tools_upgrade_regular_packages_failed": "Ne povis ĝisdatigi pakojn: {packages_list}", + "service_start_failed": "Ne povis komenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "service_reloaded": "Servo '{service:s}' reŝargita", + "system_upgraded": "Sistemo ĝisdatigita", + "domain_deleted": "Domajno forigita", + "certmanager_acme_not_configured_for_domain": "Atestilo por la domajno '{domain:s}' ne ŝajnas esti ĝuste instalita. Bonvolu ekzekuti 'cert-instali' por ĉi tiu regado unue.", + "migration_description_0009_decouple_regenconf_from_services": "Malkonstruu la regen-konf-mekanismon de servoj", + "user_update_failed": "Ne povis ĝisdatigi uzanton {user}: {error}", + "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Lasu la SSH-agordon estu administrata de YunoHost (paŝo 2, manlibro)", + "restore_confirm_yunohost_installed": "Ĉu vi vere volas restarigi jam instalitan sistemon? [{answers:s}]", + "pattern_positive_number": "Devas esti pozitiva nombro", + "monitor_stats_file_not_found": "Statistika dosiero ne trovita", + "certmanager_error_no_A_record": "Neniu DNS 'A' rekordo trovita por '{domain:s}'. Vi bezonas atentigi vian domajnan nomon al via maŝino por povi instali atestilon Lasu-Ĉifri. (Se vi scias, kion vi faras, uzu '--no-checks' por malŝalti tiujn ĉekojn.)", + "update_apt_cache_failed": "Ne eblis ĝisdatigi la kaŝmemoron de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourcelist}", + "migrations_no_migrations_to_run": "Neniuj migradoj por funkcii", + "executing_command": "Plenumanta komandon '{command:s}' …", + "diagnosis_no_apps": "Neniu instalita apliko", + "certmanager_attempt_to_renew_nonLE_cert": "La atestilo por la domajno '{domain:s}' ne estas elsendita de Let's Encrypt. Ne eblas renovigi ĝin aŭtomate!", + "global_settings_setting_example_bool": "Ekzemplo bulea elekto", + "domain_dyndns_already_subscribed": "Vi jam abonis DynDNS-domajnon", + "log_letsencrypt_cert_renew": "Renovigu '{}' Ni ĉifru atestilon", + "migrate_tsig_start": "Detektita ŝlosila algoritmo nesufiĉa por TSIG-subskribo de la domajno '{domain}', komencanta migradon al la pli sekura HMAC-SHA-512", + "ldap_init_failed_to_create_admin": "LDAP-iniciato ne povis krei administran uzanton", + "backup_output_directory_required": "Vi devas provizi elirejan dosierujon por la sekurkopio", + "tools_upgrade_cant_unhold_critical_packages": "Ne povis malhelpi kritikajn pakojn…", + "diagnosis_monitor_disk_error": "Ne povis monitori diskojn: {error}", + "log_link_to_log": "Plena ŝtipo de ĉi tiu operacio: ' {desc} '", + "service_no_log": "Neniu registro por montri por servo '{service:s}'", + "global_settings_cant_serialize_settings": "Ne eblis serialigi datumojn pri agordoj, motivo: {reason:s}", + "backup_running_hooks": "Kurado de apogaj hokoj …", + "package_not_installed": "Pako '{pkgname}' ne estas instalita", + "certmanager_domain_unknown": "Nekonata domajno '{domain:s}'", + "unexpected_error": "Io neatendita iris malbone: {error}", + "password_listed": "Ĉi tiu pasvorto estas inter la plej uzataj pasvortoj en la mondo. Bonvolu elekti ion pli unikan.", + "ssowat_persistent_conf_write_error": "Ne povis konservi konstantan SSOwat-agordon: {error:s}. Redakti /etc/ssowat/conf.json.persistent dosiero por ripari la Jaks-sintakson", + "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Lasu la SSH-agordon estu administrata de YunoHost (paŝo 1, aŭtomata)", + "migration_0009_not_needed": "Ĉi tiu migrado jam iel okazis ... (?) Saltado.", + "ssowat_conf_generated": "SSOwat-agordo generita", + "migrate_tsig_wait": "Atendante tri minutojn por ke la servilo DynDNS enkalkulu la novan ŝlosilon …", + "log_remove_on_failed_restore": "Forigu '{}' post malsukcesa restarigo de rezerva ar archiveivo", + "dpkg_is_broken": "Vi ne povas fari ĉi tion nun ĉar dpkg/APT (la administrantoj pri pakaĵaj sistemoj) ŝajnas esti rompita stato ... Vi povas provi solvi ĉi tiun problemon per konekto per SSH kaj funkcianta `sudo dpkg --configure -a`.", + "recommend_to_add_first_user": "La postinstalo finiĝis, sed YunoHost bezonas almenaŭ unu uzanton por funkcii ĝuste, vi devas aldoni unu uzante 'yunohost user create ' aŭ fari ĝin de la administra interfaco.", + "certmanager_cert_signing_failed": "Ne povis subskribi la novan atestilon", + "migration_description_0003_migrate_to_stretch": "Altgradigu la sistemon al Debian Stretch kaj YunoHost 3.0", + "log_tools_upgrade": "Ĝisdatigu sistemajn pakaĵojn", + "network_check_smtp_ko": "Ekstera retpoŝto (SMTP-haveno 25) ŝajnas esti blokita de via reto", + "log_available_on_yunopaste": "Ĉi tiu protokolo nun haveblas per {url}", + "certmanager_http_check_timeout": "Ekdifinita kiam servilo provis kontakti sin per HTTP per publika IP-adreso (domajno '{domain:s}' kun IP '{ip:s}'). Vi eble spertas haŭtoproblemon, aŭ la fajroŝirmilo / enkursigilo antaŭ via servilo miskonfiguras.", + "pattern_port_or_range": "Devas esti valida haveno-nombro (t.e. 0-65535) aŭ gamo da havenoj (t.e. 100:200)", + "migrations_loading_migration": "Ŝarĝante migradon {id}…", + "port_available": "Haveno {port:d} estas havebla", + "pattern_mailbox_quota": "Devas esti grandeco kun la sufikso b/k/M/G/T aŭ 0 por ne havi kvoton", + "migration_0008_general_disclaimer": "Por plibonigi la sekurecon de via servilo, rekomendas lasi YunoHost administri la SSH-agordon. Via nuna SSH-aranĝo diferencas de la rekomendo. Se vi lasas YunoHost agordi ĝin, la maniero per kiu vi konektas al via servilo per SSH ŝanĝiĝos tiel:", + "user_deletion_failed": "Ne povis forigi uzanton {user}: {error}", + "backup_with_no_backup_script_for_app": "La app '{app:s}' ne havas sekretan skripton. Ignorante.", + "service_regen_conf_is_deprecated": "'yunohost service regen-conf' malakceptas! Bonvolu uzi anstataŭe 'yunohost tools regen-conf'.", + "global_settings_key_doesnt_exists": "La ŝlosilo '{settings_key:s}' ne ekzistas en la tutmondaj agordoj, vi povas vidi ĉiujn disponeblajn klavojn per uzado de 'yunohost settings list'", + "dyndns_no_domain_registered": "Neniu domajno registrita ĉe DynDNS", + "dyndns_could_not_check_available": "Ne povis kontroli ĉu {domain:s} haveblas sur {provider:s}.", + "log_app_removelist": "Forigu aplikan liston", + "global_settings_setting_example_enum": "Ekzemplo enum elekto", + "hook_exec_not_terminated": "Skripto ne finiĝis ĝuste: {path:s}", + "service_stopped": "'{service:s}' servo ĉesis", + "restore_failed": "Ne povis restarigi sistemon", + "confirm_app_install_danger": "Danĝero! Ĉi tiu apliko estas konata ankoraŭ eksperimenta (se ne eksplicite ne funkcias)! Vi probable ne devas instali ĝin krom se vi scias kion vi faras. NENIU SUBTENO estos provizita se ĉi tiu app ne funkcias aŭ rompas vian sistemon ... Se vi pretas riski ĉiuokaze, tajpu '{answers: s}'", + "log_operation_unit_unclosed_properly": "Operaciumo ne estis fermita ĝuste", + "upgrade_complete": "Ĝisdatigo kompleta", + "upnp_enabled": "UPnP ŝaltis", + "mailbox_used_space_dovecot_down": "La retpoŝta servo de Dovecot devas funkcii, se vi volas akcepti uzitan poŝtan spacon", + "restore_system_part_failed": "Ne povis restarigi la sisteman parton '{part:s}'", + "diagnosis_monitor_system_error": "Ne povis monitori sistemon: {error}", + "service_stop_failed": "Ne povis maldaŭrigi la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "unbackup_app": "App '{app:s}' ne konserviĝos", + "updating_apt_cache": "Akirante haveblajn ĝisdatigojn por sistemaj pakoj…", + "tools_upgrade_at_least_one": "Bonvolu specifi '--apps' aŭ '--system'", + "service_already_stopped": "La servo '{service:s}' jam ĉesis", + "unit_unknown": "Nekonata unuo '{unit:s}'", + "migration_0003_modified_files": "Bonvolu noti, ke la jenaj dosieroj estis trovitaj mane kaj modifitaj kaj povus esti anstataŭigitaj sekve de la ĝisdatigo: {manual_modified_files}", + "tools_upgrade_cant_both": "Ne eblas ĝisdatigi ambaŭ sistemon kaj programojn samtempe", + "restore_extracting": "Eltirante bezonatajn dosierojn el la ar theivo…", + "upnp_port_open_failed": "Ne povis malfermi havenon per UPnP", + "log_app_upgrade": "Ĝisdatigu la aplikon '{}'", + "log_help_to_get_failed_log": "La operacio '{desc}' ne povis finiĝi. Bonvolu dividi la plenan ŝtipon de ĉi tiu operacio per la komando 'yunohost log display {name} --share' por akiri helpon", + "migration_description_0002_migrate_to_tsig_sha256": "Plibonigu sekurecon de DynDNS TSIG-ĝisdatigoj per SHA-512 anstataŭ MD5", + "monitor_disabled": "Servila monitorado nun malŝaltis", + "pattern_port": "Devas esti valida havena numero (t.e. 0-65535)", + "port_already_closed": "Haveno {port:d} estas jam fermita por {ip_version:s} rilatoj", + "hook_name_unknown": "Nekonata hoko-nomo '{name:s}'", + "migration_0003_system_not_fully_up_to_date": "Via sistemo ne estas plene ĝisdata. Bonvolu plenumi regulan ĝisdatigon antaŭ ol ruli la migradon al Stretch.", + "dyndns_could_not_check_provide": "Ne povis kontroli ĉu {provider:s} povas provizi {domain:s}.", + "dyndns_cron_remove_failed": "Ne povis forigi la cron-laboron DynDNS ĉar: {error}", + "pattern_listname": "Devas esti nur alfanumeraj kaj substrekaj signoj", + "restore_nothings_done": "Nenio estis restarigita", + "log_tools_postinstall": "Afiŝu vian servilon YunoHost", + "dyndns_unavailable": "La domajno '{domain:s}' ne haveblas.", + "experimental_feature": "Averto: Ĉi tiu funkcio estas eksperimenta kaj ne konsiderata stabila, vi ne uzu ĝin krom se vi scias kion vi faras.", + "root_password_replaced_by_admin_password": "Via radika pasvorto estis anstataŭigita per via administra pasvorto.", + "ssowat_persistent_conf_read_error": "Ne povis legi konstantan SSOwat-agordon: {error:s}. Redakti /etc/ssowat/conf.json.persistent dosiero por ripari la Jaks-sintakson", + "migration_description_0005_postgresql_9p4_to_9p6": "Migru datumbazojn de PostgreSQL 9.4 al 9.6", + "migration_0008_root": "• Vi ne povos konekti kiel radiko per SSH. Anstataŭe vi uzu la administran uzanton;", + "package_unknown": "Nekonata pako '{pkgname}'", + "domain_unknown": "Nekonata domajno", + "global_settings_setting_security_password_user_strength": "Uzanto pasvorta forto", + "restore_may_be_not_enough_disk_space": "Via sistemo ŝajnas ne havi sufiĉe da spaco (free:{free_space:d} B, necesa spaco: {needed_space:d} B, sekureca marĝeno: {margin:d} B)", + "log_corrupted_md_file": "La YAD-metadata dosiero asociita kun protokoloj estas damaĝita: '{md_file}\nEraro: {error} '", + "downloading": "Elŝutante …", + "user_deleted": "Uzanto forigita", + "service_enable_failed": "Ne eblis ŝalti la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "tools_upgrade_special_packages": "Nun ĝisdatigi 'specialajn' (rilatajn al yunohost)…", + "domains_available": "Haveblaj domajnoj:", + "dyndns_registered": "Registrita domajno DynDNS", + "service_description_fail2ban": "Protektas kontraŭ bruta forto kaj aliaj specoj de atakoj de la interreto", + "file_does_not_exist": "La dosiero {path:s} ne ekzistas.", + "yunohost_not_installed": "YunoHost estas malĝuste aŭ ne ĝuste instalita. Bonvolu prilabori 'yunohost tools postinstall'", + "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 estas instalita, sed ne postgresql 9.6‽ Io stranga eble okazis en via sistemo: (…", + "restore_removing_tmp_dir_failed": "Ne povis forigi malnovan provizoran dosierujon", + "certmanager_cannot_read_cert": "Io malbona okazis, kiam mi provis malfermi aktualan atestilon por domajno {domain:s} (dosiero: {file:s}), kialo: {reason:s}", + "service_removed": "'{service:s}' servo forigita", + "certmanager_hit_rate_limit": "Tro multaj atestiloj jam eldonitaj por ĉi tiu ĝusta aro de domajnoj {domain:s} antaŭ nelonge. Bonvolu reprovi poste. Vidu https://letsencrypt.org/docs/rate-limits/ por pliaj detaloj", + "migration_0005_not_enough_space": "Disponigu sufiĉan spacon en {path} por ruli la migradon.", + "pattern_firstname": "Devas esti valida antaŭnomo", + "migration_description_0010_migrate_to_apps_json": "Forigu malvalorigitajn aparatojn kaj uzu anstataŭe la novan unuigitan liston \"apps.json\"", + "domain_cert_gen_failed": "Ne povis generi atestilon", + "regenconf_file_kept_back": "La agorda dosiero '{conf}' estas atendita forigi per regen-conf (kategorio {category}), sed ĝi estis konservita.", + "migrate_tsig_wait_4": "30 sekundoj …", + "backup_with_no_restore_script_for_app": "La apliko \"{app:s}\" ne havas restarigan skripton, vi ne povos aŭtomate restarigi la sekurkopion de ĉi tiu apliko.", + "log_letsencrypt_cert_install": "Instalu atestilon Ni ĉifru sur '{}' regado", + "log_dyndns_update": "Ĝisdatigu la IP asociita kun via subdominio YunoHost '{}'", + "firewall_reload_failed": "Ne eblis reŝargi la firewall", + "confirm_app_install_warning": "Averto: Ĉi tiu aplikaĵo povas funkcii, sed ne bone integras en YunoHost. Iuj funkcioj kiel ekzemple aliĝilo kaj sekurkopio / restarigo eble ne haveblos. Instali ĉiuokaze? [{answers: s}] ", + "log_user_delete": "Forigi uzanton '{}'", + "dyndns_ip_updated": "Ĝisdatigis vian IP sur DynDNS", + "regenconf_up_to_date": "La agordo jam estas ĝisdatigita por kategorio '{category}'", + "migration_0003_patching_sources_list": "Patching the sources.lists …", + "global_settings_setting_security_ssh_compatibility": "Kongruo vs sekureca kompromiso por la SSH-servilo. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)", + "migrations_need_to_accept_disclaimer": "Por funkciigi la migradon {id}, via devas akcepti la sekvan malakcepton:\n---\n{malavantaĝo}\n---\nSe vi akceptas funkcii la migradon, bonvolu rekonduki la komandon kun la opcio '--accept-disclaimer'.", + "regenconf_file_remove_failed": "Ne povis forigi la agordodosieron '{conf}'", + "not_enough_disk_space": "Ne sufiĉe libera spaco sur '{path:s}'", + "migration_0006_disclaimer": "YunoHost nun atendas ke pasvortoj kaj administrantoj estu sinkronigitaj. Per ekzekuto de ĉi tiu migrado, via radika pasvorto estos anstataŭigita per administra pasvorto.", + "dyndns_ip_update_failed": "Ne povis ĝisdatigi IP-adreson al DynDNS", + "migration_description_0004_php5_to_php7_pools": "Rekonfigu la PHP-naĝejojn por uzi PHP 7 anstataŭ 5", + "monitor_glances_con_failed": "Ne povis konektiĝi al servilo de Glances", + "ssowat_conf_updated": "SSOwat-agordo ĝisdatigita", + "log_link_to_failed_log": "Ne povis plenumi la operacion '{desc}'. Bonvolu provizi la plenan protokolon de ĉi tiu operacio per alklakante ĉi tie por akiri helpon", + "log_app_fetchlist": "Aldonu liston de aplikoj", + "user_home_creation_failed": "Ne povis krei dosierujon \"home\" por uzanto", + "pattern_backup_archive_name": "Devas esti valida dosiernomo kun maksimume 30 signoj, alfanombraj kaj -_. signoj nur", + "restore_cleaning_failed": "Ne eblis purigi la adresaron de provizora restarigo", + "dyndns_registration_failed": "Ne povis registri DynDNS-domajnon: {error:s}", + "migration_0003_not_jessie": "La nuna Debian-distribuo ne estas Jessie!", + "user_unknown": "Nekonata uzanto: {user:s}", + "migrations_to_be_ran_manually": "Migrado {id} devas funkcii permane. Bonvolu iri al Iloj → Migradoj en la retpaĝa paĝo, aŭ kuri `yunohost tools migrations migrate`.", + "migration_0008_warning": "Se vi komprenas tiujn avertojn kaj konsentas lasi YunoHost pretervidi vian nunan agordon, faru la migradon. Alie, vi ankaŭ povas salti la migradon - kvankam ĝi ne rekomendas.", + "certmanager_cert_renew_success": "Ni Ĉifru atestilon renovigitan por la domajno '{domain:s}'", + "global_settings_reset_success": "Antaŭaj agordoj nun estas rezervitaj al {path:s}", + "pattern_domain": "Devas esti valida domajna nomo (t.e. mia-domino.org)", + "package_unexpected_error": "Neatendita eraro okazis prilaborante la pakon '{pkgname}'", + "dyndns_key_generating": "Generi DNS-ŝlosilon ... Eble daŭros iom da tempo.", + "restore_running_app_script": "Restarigi la programon '{app:s}'…", + "migrations_skip_migration": "Salti migradon {id}…", + "mysql_db_init_failed": "MysQL-datumbazo init malsukcesis", + "regenconf_file_removed": "Agordodosiero '{conf}' forigita", + "log_tools_shutdown": "Enŝaltu vian servilon", + "password_too_simple_3": "La pasvorto bezonas almenaŭ 8 signojn kaj enhavas ciferon, majusklon, pli malaltan kaj specialajn signojn", + "migration_0003_general_warning": "Bonvolu noti, ke ĉi tiu migrado estas delikata operacio. La teamo de YunoHost faris sian plej bonan revizii kaj testi ĝin, sed la migrado eble ankoraŭ rompos partojn de la sistemo aŭ ĝiaj programoj.\n\nTial oni rekomendas al:\n - Elfari kopion de iuj kritikaj datumoj aŭ app. Pliaj informoj pri https://yunohost.org/backup;\n - Paciencu post lanĉo de la migrado: Depende de via interreta konekto kaj aparataro, eble daŭros kelkaj horoj ĝis ĉio ĝisdatigi.\n\nAldone, la haveno por SMTP, uzata de eksteraj retpoŝtaj klientoj (kiel Thunderbird aŭ K9-Mail) estis ŝanĝita de 465 (SSL / TLS) al 587 (STARTTLS). La malnova haveno (465) aŭtomate fermiĝos, kaj la nova haveno (587) malfermiĝos en la fajrejo. Vi kaj viaj uzantoj * devos adapti la agordon de viaj retpoŝtaj klientoj laŭe.", + "diagnosis_kernel_version_error": "Ne povis akiri la kernan version: {error}", + "global_settings_setting_example_int": "Ekzemple int elekto", + "backup_output_symlink_dir_broken": "Vi havas rompitan simbolon anstataŭ via arkiva dosierujo '{path:s}'. Vi eble havas specifan agordon por sekurkopi viajn datumojn en alia dosiersistemo, ĉi-kaze vi probable forgesis remeti aŭ enŝovi vian malmolan disko aŭ ŝlosilon USB.", + "good_practices_about_admin_password": "Vi nun estas por difini novan administran pasvorton. La pasvorto devas esti almenaŭ 8 signoj - kvankam estas bone praktiki uzi pli longan pasvorton (t.e. pasfrazon) kaj / aŭ uzi variaĵon de signoj (majuskloj, minuskloj, ciferoj kaj specialaj signoj).", + "certmanager_attempt_to_renew_valid_cert": "La atestilo por la domajno '{domain:s}' ne finiĝos! (Vi eble uzos --force se vi scias kion vi faras)", + "restore_running_hooks": "Kurantaj restarigaj hokoj…", + "regenconf_pending_applying": "Aplikante pritraktata agordo por kategorio '{category}'…", + "service_description_dovecot": "Permesas al retpoŝtaj klientoj aliri / serĉi retpoŝton (per IMAP kaj POP3)", + "domain_dns_conf_is_just_a_recommendation": "Ĉi tiu komando montras al vi la *rekomenditan* agordon. Ĝi efektive ne agordas la DNS-agordon por vi. Via respondeco agordi vian DNS-zonon en via registristo laŭ ĉi tiu rekomendo.", + "backup_php5_to_php7_migration_may_fail": "Ne povis konverti vian ar archiveivon por subteni PHP 7, vi eble ne povas restarigi viajn PHP-programojn (kialo: {error:s})", + "log_backup_restore_system": "Restarigi sistemon de rezerva arkivo", + "log_app_change_url": "Ŝanĝu la URL de apliko '{}'", + "service_already_started": "La servo '{service:s}' estas jam komencita", + "license_undefined": "nedifinita", + "global_settings_setting_security_password_admin_strength": "Admin pasvorta forto", + "service_reload_or_restart_failed": "Ne povis reŝargi aŭ rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "migrations_list_conflict_pending_done": "Vi ne povas uzi ambaŭ '--previous' kaj '--done' samtempe.", + "maindomain_changed": "La ĉefa domajno nun ŝanĝiĝis", + "server_shutdown_confirm": "La servilo haltos tuj, ĉu vi certas? [{answers:s}]", + "monitor_period_invalid": "Nevalida tempoperiodo", + "log_backup_restore_app": "Restarigu '{}' de rezerva ar archiveivo", + "log_does_exists": "Ne estas operacio-registro kun la nomo '{log}', uzu 'yunohost loglist' por vidi ĉiujn disponeblajn operaciojn", + "service_add_failed": "Ne povis aldoni la servon '{service:s}'", + "pattern_password_app": "Bedaŭrinde, pasvortoj ne povas enhavi jenajn signojn: {forbidden_chars}", + "this_action_broke_dpkg": "Ĉi tiu ago rompis dpkg / APT (la administrantoj pri la paka sistemo) ... Vi povas provi solvi ĉi tiun problemon per konekto per SSH kaj funkcianta `sudo dpkg --configure -a`.", + "log_regen_conf": "Regeneri sistemajn agordojn '{}'", + "restore_hook_unavailable": "La restariga skripto por '{part:s}' ne haveblas en via sistemo kaj ankaŭ ne en la ar theivo", + "network_check_smtp_ok": "Eksteren retpoŝto (SMTP-haveno 25) ne estas blokita", + "log_dyndns_subscribe": "Aboni al YunoHost-subdominio '{}'", + "password_too_simple_4": "La pasvorto bezonas almenaŭ 12 signojn kaj enhavas ciferon, majuskle, pli malaltan kaj specialajn signojn", + "migration_0003_main_upgrade": "Komencanta ĉefa ĝisdatigo …", + "user_info_failed": "Ne povis akiri informojn pri uzanto", + "regenconf_file_updated": "Agordodosiero '{conf}' ĝisdatigita", + "log_help_to_get_log": "Por vidi la protokolon de la operacio '{desc}', uzu la komandon 'yunohost log display {name}'", + "global_settings_setting_security_nginx_compatibility": "Kongruo vs sekureca kompromiso por la TTT-servilo NGINX. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)", + "no_internet_connection": "Servilo ne konektita al la interreto", + "migration_0008_dsa": "• La DSA-ŝlosilo estos malŝaltita. Tial vi eble bezonos nuligi spuran averton de via SSH-kliento kaj revizii la fingrospuron de via servilo;", + "migration_0003_restoring_origin_nginx_conf": "Fileia dosiero /etc/nginx/nginx.conf estis iel redaktita. La migrado reaperos unue al sia originala stato ... La antaŭa dosiero estos havebla kiel {backup_dest}.", + "migrate_tsig_end": "Migrado al HMAC-SHA-512 finiĝis", + "restore_complete": "Restarigita", + "certmanager_couldnt_fetch_intermediate_cert": "Ekvilibrigita kiam vi provis ricevi interajn atestilojn de Let's Encrypt. Atestita instalado / renovigo nuligita - bonvolu reprovi poste.", + "hook_exec_failed": "Ne povis funkcii skripto: {path:s}", + "global_settings_cant_open_settings": "Ne eblis malfermi agordojn, tial: {reason:s}", + "user_created": "Uzanto kreita", + "service_description_avahi-daemon": "Permesas al vi atingi vian servilon uzante 'yunohost.local' en via loka reto", + "certmanager_attempt_to_replace_valid_cert": "Vi provas anstataŭigi bonan kaj validan atestilon por domajno {domajno:s}! (Uzu --forte pretervidi)", + "monitor_stats_period_unavailable": "Ne ekzistas disponeblaj statistikoj por la periodo", + "regenconf_updated": "Agordo por kategorio '{category}' ĝisdatigita", + "update_apt_cache_warning": "Io iris malbone dum la ĝisdatigo de la kaŝmemoro de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourcelist}", + "regenconf_dry_pending_applying": "Kontrolado de pritraktata agordo, kiu estus aplikita por kategorio '{category}'…", + "regenconf_file_copy_failed": "Ne povis kopii la novan agordodosieron '{new}' al '{conf}'", + "global_settings_setting_example_string": "Ekzemple korda elekto", + "restore_already_installed_app": "App kun la ID '{app:s}' estas jam instalita", + "mountpoint_unknown": "Nekonata montpunkto", + "log_tools_maindomain": "Faru de '{}' la ĉefa domajno", + "maindomain_change_failed": "Ne povis ŝanĝi la ĉefan domajnon", + "mail_domain_unknown": "Nekonata retpoŝtadreso por domajno '{domain:s}'", + "migrations_cant_reach_migration_file": "Ne povis aliri migrajn dosierojn ĉe la vojo% s", + "pattern_email": "Devas esti valida retpoŝtadreso (t.e.iu@domain.org)", + "mail_alias_remove_failed": "Ne povis forigi retpoŝton alias '{mail:s}'", + "regenconf_file_manually_removed": "La dosiero de agordo '{conf}' estis forigita permane, kaj ne estos kreita", + "monitor_enabled": "Servila monitorado nun ŝaltis", + "domain_exists": "La domajno jam ekzistas", + "migration_description_0001_change_cert_group_to_sslcert": "Ŝanĝu grupajn permesojn de 'metronomo' al 'ssl-cert'", + "mysql_db_creation_failed": "MySQL-datumbazkreado malsukcesis", + "ldap_initialized": "LDAP inicializis", + "migrate_tsig_not_needed": "Vi ne ŝajnas uzi DynDNS-domajnon, do neniu migrado necesas.", + "certmanager_domain_cert_not_selfsigned": "La atestilo por domajno {domajno:s} ne estas mem-subskribita. Ĉu vi certas, ke vi volas anstataŭigi ĝin? (Uzu '--force' por fari tion.)", + "certmanager_unable_to_parse_self_CA_name": "Ne povis trapasi nomon de mem-subskribinta aŭtoritato (dosiero: {file: s})", + "log_selfsigned_cert_install": "Instalu mem-subskribitan atestilon sur '{}' domajno", + "log_tools_reboot": "Reklamu vian servilon", + "certmanager_cert_install_success": "Ni Ĉifru atestilon nun instalitan por la domajno '{domain:s}'", + "global_settings_bad_choice_for_enum": "Malbona elekto por agordo {setting:s}, ricevita '{choice:s}', sed disponeblaj elektoj estas: {available_choices:s}", + "server_shutdown": "La servilo haltos", + "log_tools_migrations_migrate_forward": "Migri antaŭen", + "migration_0008_no_warning": "Neniu grava risko identigita pri superregado de via SSH-agordo, tamen oni ne povas esti absolute certa;)! Ekfunkciu la migradon por superregi ĝin. Alie, vi ankaŭ povas salti la migradon - kvankam ĝi ne rekomendas.", + "regenconf_now_managed_by_yunohost": "La agorda dosiero '{conf}' nun estas administrata de YunoHost (kategorio {category}).", + "server_reboot_confirm": "Ĉu la servilo rekomencos tuj, ĉu vi certas? [{answers:s}]", + "log_app_install": "Instalu la aplikon '{}'", + "service_description_dnsmasq": "Traktas rezolucion de domajna nomo (DNS)", + "global_settings_unknown_type": "Neatendita situacio, la agordo {setting:s} ŝajnas havi la tipon {unknown_type:s} sed ĝi ne estas tipo subtenata de la sistemo.", + "migration_0003_problematic_apps_warning": "Bonvolu noti, ke la sekvaj eventuale problemaj instalitaj apps estis detektitaj. Ĝi aspektas, ke tiuj ne estis instalitaj de aparato aŭ ne estas markitaj kiel \"funkciantaj\". Tial ne eblas garantii, ke ili ankoraŭ funkcios post la ĝisdatigo: {problematic_apps}", + "domain_hostname_failed": "Ne povis agordi novan gastigilon. Ĉi tio eble kaŭzos problemon poste (eble bone).", + "server_reboot": "La servilo rekomenciĝos", + "regenconf_failed": "Ne povis regeneri la agordon por kategorio(j): {categories}", + "domain_uninstall_app_first": "Unu aŭ pluraj programoj estas instalitaj en ĉi tiu domajno. Bonvolu malinstali ilin antaŭ ol daŭrigi la domajnan forigon", + "port_unavailable": "Haveno {port:d} ne haveblas", + "service_unknown": "Nekonata servo '{service:s}'", + "migration_0003_start": "Komencante migradon al Stretch. La protokoloj haveblos en {logfile}.", + "monitor_stats_no_update": "Neniuj monitoradaj statistikoj ĝisdatigi", + "domain_deletion_failed": "Ne povis forigi domajnon {domain}: {error}", + "log_user_update": "Ĝisdatigu uzantinformojn de '{}'", + "user_creation_failed": "Ne povis krei uzanton {user}: {error}", + "migrations_migration_has_failed": "Migrado {id} ne kompletigis, abolis. Eraro: {exception}", + "done": "Farita", + "log_domain_remove": "Forigi domon '{}' de agordo de sistemo", + "monitor_not_enabled": "Servila monitorado estas malŝaltita", + "diagnosis_debian_version_error": "Ne povis retrovi la Debianan version: {error}", + "hook_list_by_invalid": "Ĉi tiu posedaĵo ne povas esti uzata por listigi hokojn", + "confirm_app_install_thirdparty": "Danĝero! Ĉi tiu apliko ne estas parto de la aplika katalogo de Yunohost. Instali triajn aplikojn povas kompromiti la integrecon kaj sekurecon de via sistemo. Vi probable ne devas instali ĝin krom se vi scias kion vi faras. NENIU SUBTENO estos provizita se ĉi tiu app ne funkcias aŭ rompas vian sistemon ... Se vi pretas riski ĉiuokaze, tajpu '{answers: s}'", + "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permesu uzon de (malaktuala) DSA-hostkey por la agordo de daemon SSH", + "dyndns_domain_not_provided": "Provizanto DynDNS {provider:s} ne povas provizi domajnon {domain:s}.", + "backup_unable_to_organize_files": "Ne povis uzi la rapidan metodon por organizi dosierojn en la ar archiveivo", + "password_too_simple_2": "La pasvorto bezonas almenaŭ 8 signojn kaj enhavas ciferon, majusklojn kaj minusklojn", + "executing_script": "Plenumanta skripto '{script:s}' …", + "service_cmd_exec_failed": "Ne povis plenumi la komandon '{command:s}'", + "migration_0007_cancelled": "YunoHost ne plibonigis la administradon de via SSH-konf.", + "migrate_tsig_failed": "Ne povis migri la DynDNS-domajnon '{domain}' al HMAC-SHA-512, ruliĝante. Eraro: {error_code}, {error}", + "pattern_lastname": "Devas esti valida familinomo", + "service_enabled": "'{service:s}' servo malŝaltita", + "certmanager_no_cert_file": "Ne povis legi la atestan dosieron por la domajno {domain:s} (dosiero: {file:s})", + "migration_0008_port": "• Vi devos konekti uzante la havenon 22 anstataŭ via nuna kutimo SSH-haveno. Sentu vin libera reconfiguri ĝin;", + "domain_creation_failed": "Ne povis krei domajnon {domain}: {error}", + "certmanager_domain_http_not_working": "Ŝajnas ke la domajno {domain:s} ne atingeblas per HTTP. Kontrolu, ke via DNS kaj NGINX-agordo ĝustas", + "domain_cannot_remove_main": "Ne eblas forigi ĉefan domajnon. Fiksu unu unue", + "service_reloaded_or_restarted": "'{service:s}' servo reŝarĝis aŭ rekomencis", + "mysql_db_initialized": "La datumbazo MySQL jam estas pravalorizita", + "log_domain_add": "Aldonu '{}' domajnon en sisteman agordon", + "global_settings_bad_type_for_setting": "Malbona tipo por agordo {setting:s}, ricevita {received_type:s}, atendata {expected_type:s}", + "unlimit": "Neniu kvoto", + "dyndns_cron_installed": "Kreita laboro DynDNS cron", + "system_username_exists": "Uzantnomo jam ekzistas en la listo de uzantoj de sistemo", + "firewall_reloaded": "Fajroŝirmilo reŝarĝis", + "service_restarted": "'{service:s}' servo rekomencis", + "pattern_username": "Devas esti minuskulaj literoj kaj minuskloj nur", + "extracting": "Eltirante…", + "restore_app_failed": "Ne povis restarigi la programon '{app:s}'", + "yunohost_configured": "YunoHost nun agordis", + "certmanager_self_ca_conf_file_not_found": "Ne povis trovi agorddosieron por mem-subskriba aŭtoritato (dosiero: {file:s})", + "log_app_remove": "Forigu la aplikon '{}'", + "service_restart_failed": "Ne povis rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", + "firewall_rules_cmd_failed": "Iuj komandoj pri fajroŝirmilo malsukcesis. Pliaj informoj en ensaluto.", + "certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …" } From 8cdea18991b60e4489d93d2b861434ca3103bd53 Mon Sep 17 00:00:00 2001 From: advocatux Date: Fri, 4 Oct 2019 18:14:15 +0000 Subject: [PATCH 231/299] Translated using Weblate (Spanish) Currently translated at 100.0% (553 of 553 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 59 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/locales/es.json b/locales/es.json index 02f46652b..aeee0ff7a 100644 --- a/locales/es.json +++ b/locales/es.json @@ -29,7 +29,7 @@ "app_unsupported_remote_type": "Tipo remoto no soportado por la aplicación", "app_upgrade_failed": "No se pudo actualizar {app:s}", "app_upgraded": "Actualizado {app:s}", - "appslist_fetched": "Obtenida lista de aplicaciones {appslist:s} actualizada", + "appslist_fetched": "Lista de aplicaciones {appslist:s} actualizada", "appslist_removed": "Eliminada la lista de aplicaciones {appslist:s}", "appslist_retrieve_error": "No se puede recuperar la lista remota de aplicaciones {appslist:s}: {error:s}", "appslist_unknown": "Lista de aplicaciones {appslist:s} desconocida.", @@ -74,9 +74,9 @@ "dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, ejecute «apt-get remove bind9 && apt-get install it»", "domain_cert_gen_failed": "No se pudo generar el certificado", "domain_created": "Dominio creado", - "domain_creation_failed": "No se pudo crear el dominio", + "domain_creation_failed": "No se pudo crear el dominio {domain}: {error}", "domain_deleted": "Dominio eliminado", - "domain_deletion_failed": "No se pudo eliminar el dominio", + "domain_deletion_failed": "No se pudo eliminar el dominio {domain}: {error}", "domain_dyndns_already_subscribed": "Ya se ha suscrito a un dominio de DynDNS", "domain_dyndns_invalid": "Este dominio no se puede usar con DynDNS", "domain_dyndns_root_unknown": "Dominio raíz de DynDNS desconocido", @@ -228,13 +228,13 @@ "upnp_enabled": "UPnP activado", "upnp_port_open_failed": "No se pudo abrir el puerto vía UPnP", "user_created": "Usuario creado", - "user_creation_failed": "No se pudo crear el usuario", + "user_creation_failed": "No se pudo crear el usuario {user}: {error}", "user_deleted": "Usuario eliminado", - "user_deletion_failed": "No se pudo eliminar el usuario", + "user_deletion_failed": "No se pudo eliminar el usuario {user}: {error}", "user_home_creation_failed": "No se pudo crear la carpeta «home» para el usuario", "user_info_failed": "No se pudo obtener la información del usuario", "user_unknown": "Usuario desconocido: {user:s}", - "user_update_failed": "No se pudo cambiar la información del usuario", + "user_update_failed": "No se pudo actualizar el usuario {user}: {error}", "user_updated": "Cambiada la información de usuario", "yunohost_already_installed": "YunoHost ya está instalado", "yunohost_ca_creation_failed": "No se pudo crear la autoridad de certificación", @@ -398,16 +398,16 @@ "remove_main_permission_not_allowed": "No se permite eliminar el permiso principal", "recommend_to_add_first_user": "La posinstalación ha terminado pero YunoHost necesita al menos un usuario para funcionar correctamente, debe añadir uno ejecutando «yunohost user create » o usando la interfaz de administración.", "permission_update_nothing_to_do": "No hay permisos para actualizar", - "permission_updated": "Actualizado el permiso «{permission:s}» para la aplicación «{app:s}»", + "permission_updated": "Actualizado el permiso «{permission:s}»", "permission_generated": "Actualizada la base de datos de permisos", - "permission_update_failed": "No se pudo actualizar el permiso", + "permission_update_failed": "No se pudo actualizar el permiso «{permission}» : {error}", "permission_name_not_valid": "Elija un nombre de permiso permitido para «{permission:s}", - "permission_not_found": "No se encontró el permiso «{permission:s}» para la aplicación «{app:s}»", - "permission_deletion_failed": "Falta el permiso «{permission:s}» para eliminar la aplicación «{app:s}»", - "permission_deleted": "Eliminado el permiso «{permission:s}» para la aplicación {app:s}", - "permission_creation_failed": "No se pudo conceder el permiso", - "permission_created": "Creado el permiso «{permission:s}» para la aplicación {app:s}", - "permission_already_exist": "El permiso «{permission:s}» para la aplicación {app:s} ya existe", + "permission_not_found": "No se encontró el permiso «{permission:s}»", + "permission_deletion_failed": "No se pudo eliminar el permiso «{permission}»: {error}", + "permission_deleted": "Eliminado el permiso «{permission:s}»", + "permission_creation_failed": "No se pudo crear el permiso «{permission}»: {error}", + "permission_created": "Creado el permiso «{permission:s}»", + "permission_already_exist": "El permiso «{permission}» ya existe", "permission_already_clear": "El permiso «{permission:s}» ya está definido para la aplicación {app:s}", "pattern_password_app": "Las contraseñas no pueden incluir los siguientes caracteres: {forbidden_chars}", "need_define_permission_before": "Redefina los permisos ejecutando «yunohost user permission add -u USUARIO» antes de eliminar un grupo permitido", @@ -438,7 +438,7 @@ "migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, reiniciar la configuración original ejecutando «yunohost tools regen-conf -f» y reintentar la migración", "migration_0011_done": "Migración correcta. Ahora puede gestionar los grupos de usuarios.", "migration_0011_create_group": "Creando un grupo para cada usuario…", - "migration_0011_can_not_backup_before_migration": "No se pudo respaldar el sistema antes de la migración. Error: {error:s}", + "migration_0011_can_not_backup_before_migration": "Falló el respaldo del sistema antes de la migración. Fallo de migración. Error: {error:s}", "migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.", "migration_0009_not_needed": "La migración ya ocurrió de algún modo… (?) Omitiendo.", "migration_0008_no_warning": "No se ha detectado ningún riesgo importante con respecto a la anulación de su configuración SSH ¡sin embargo uno nunca puede estar absolutamente seguro ;)! Ejecute la migración para anularla. Por otra parte, puede omitir la migración aunque no esté recomendado.", @@ -536,14 +536,14 @@ "log_category_404": "La categoría de registro «{category}» no existe", "log_corrupted_md_file": "El archivo de metadatos YAML asociado con el registro está dañado: «{md_file}\nError: {error}»", "hook_json_return_error": "No se pudo leer la respuesta del gancho {path:s}. Error: {msg:s}. Contenido sin procesar: {raw_content}", - "group_update_failed": "No se pudo actualizar el grupo «{group}»", + "group_update_failed": "No se pudo actualizar el grupo «{group}»: {error}", "group_updated": "Grupo «{group}» actualizado", "group_unknown": "El grupo «{group:s}» es desconocido", "group_info_failed": "No se pudo mostrar la información del grupo", "group_deletion_not_allowed": "No se puede eliminar el grupo {group:s} manualmente.", - "group_deletion_failed": "No se pudo eliminar el grupo «{group}»", + "group_deletion_failed": "No se pudo eliminar el grupo «{group}»: {error}", "group_deleted": "Eliminado el grupo «{group}»", - "group_creation_failed": "No se pudo crear el grupo «{group}»", + "group_creation_failed": "No se pudo crear el grupo «{group}»: {error}", "group_created": "Creado el grupo «{group}»", "group_name_already_exist": "El grupo {name:s} ya existe", "group_already_disallowed": "El grupo «{group:s}» ya tiene desactivado el permiso «{permission:s}» para la aplicación «{app:s}»", @@ -577,8 +577,8 @@ "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", "dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento porque otro programa parece que está usando el bloqueo de dpkg (el gestor de paquetes del sistema)", "dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.", - "confirm_app_install_thirdparty": "¡AVISO! Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarlas salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", - "confirm_app_install_danger": "¡AVISO! Esta aplicación es aún experimental (si no está funcionando expresamente) y ¡es probable que rompa su sistema! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. ¿Está dispuesto a correr ese riesgo? [{answers:s}] ", + "confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de YunoHost. Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", + "confirm_app_install_danger": "¡PELIGRO! ¡Esta aplicación es conocida por ser aún experimental (o no funciona explícitamente)! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", "confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ", "backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo", "backup_permission": "Permiso de respaldo para la aplicación {app:s}", @@ -608,5 +608,22 @@ "app_action_broke_system": "Esta acción parece que ha roto estos importantes servicios: {services}", "operation_interrupted": "¿Ha sido interrumpida la operación manualmente?", "apps_already_up_to_date": "Todas las aplicaciones están ya actualizadas", - "dyndns_provider_unreachable": "No se puede conectar con el proveedor de Dyndns {provider}: o su YunoHost no está correctamente conectado a Internet o el servidor de dynette está caído." + "dyndns_provider_unreachable": "No se puede conectar con el proveedor de Dyndns {provider}: o su YunoHost no está correctamente conectado a Internet o el servidor de dynette está caído.", + "group_already_exist": "El grupo {group} ya existe", + "group_already_exist_on_system": "El grupo {group} ya existe en los grupos del sistema", + "group_cannot_be_edited": "El grupo {group} no se puede editar manualmente.", + "group_cannot_be_deleted": "El grupo {group} no se puede eliminar manualmente.", + "group_user_already_in_group": "El usuario {user} ya está en el grupo {group}", + "group_user_not_in_group": "El usuario {user} no está en el grupo {group}", + "log_permission_create": "Crear permiso «{}»", + "log_permission_delete": "Eliminar permiso «{}»", + "log_permission_urls": "Actualizar URLs relacionadas con el permiso «{}»", + "log_user_group_create": "Crear grupo «{}»", + "log_user_permission_update": "Actualizar los accesos para el permiso «{}»", + "log_user_permission_reset": "Restablecer permiso «{}»", + "migration_0011_failed_to_remove_stale_object": "No se pudo eliminar el objeto obsoleto {dn}: {error}", + "permission_already_allowed": "El grupo «{group}» ya tiene el permiso «{permission}» activado", + "permission_already_disallowed": "El grupo «{group}» ya tiene el permiso «{permission}» desactivado", + "permission_cannot_remove_main": "No está permitido eliminar un permiso principal", + "user_already_exists": "El usuario {user} ya existe" } From 3e6ae5fb6835246141575c140897e7fd7a4db6c9 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Sun, 6 Oct 2019 10:07:41 +0000 Subject: [PATCH 232/299] Translated using Weblate (French) Currently translated at 100.0% (553 of 553 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 61 +++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 8bffec8b2..6b5e2a790 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -30,7 +30,7 @@ "app_unsupported_remote_type": "Ce type de commande à distance utilisé pour cette application n'est pas supporté", "app_upgrade_failed": "Impossible de mettre à jour {app:s}", "app_upgraded": "{app:s} mis à jour", - "appslist_fetched": "La liste d’applications {appslist:s} récupérée", + "appslist_fetched": "La liste d’applications mise à jour {appslist:s}", "appslist_removed": "La liste d’applications {appslist:s} a été supprimée", "appslist_retrieve_error": "Impossible de récupérer la liste d’applications distante {appslist:s} : {error:s}", "appslist_unknown": "La liste d’applications {appslist:s} est inconnue.", @@ -75,9 +75,9 @@ "dnsmasq_isnt_installed": "dnsmasq ne semble pas être installé, veuillez lancer 'apt-get remove bind9 && apt-get install dnsmasq'", "domain_cert_gen_failed": "Impossible de générer le certificat", "domain_created": "Le domaine a été créé", - "domain_creation_failed": "Impossible de créer le domaine", + "domain_creation_failed": "Impossible de créer le domaine {domain}: {error}", "domain_deleted": "Le domaine a été supprimé", - "domain_deletion_failed": "Impossible de supprimer le domaine", + "domain_deletion_failed": "Impossible de supprimer le domaine {domain}: {error}", "domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS", "domain_dyndns_invalid": "Domaine incorrect pour un usage avec DynDNS", "domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu", @@ -236,13 +236,13 @@ "upnp_enabled": "UPnP activé", "upnp_port_open_failed": "Impossible d’ouvrir les ports UPnP", "user_created": "L’utilisateur créé", - "user_creation_failed": "Impossible de créer l’utilisateur", + "user_creation_failed": "Impossible de créer l’utilisateur {user}: {error}", "user_deleted": "L’utilisateur supprimé", - "user_deletion_failed": "Impossible de supprimer l’utilisateur", + "user_deletion_failed": "Impossible de supprimer l’utilisateur {user}: {error}", "user_home_creation_failed": "Impossible de créer le dossier personnel de l’utilisateur", "user_info_failed": "Impossible de récupérer les informations de l’utilisateur", "user_unknown": "L'utilisateur {user:s} est inconnu", - "user_update_failed": "Impossible de modifier l’utilisateur", + "user_update_failed": "Impossible de mettre à jour l'utilisateur {utilisateur}: {erreur}", "user_updated": "L’utilisateur a été modifié", "yunohost_already_installed": "YunoHost est déjà installé", "yunohost_ca_creation_failed": "Impossible de créer l’autorité de certification", @@ -493,8 +493,8 @@ "backup_actually_backuping": "Création d'une archive de sauvegarde à partir des fichiers collectés …", "backup_mount_archive_for_restore": "Préparation de l'archive pour restauration …", "confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais n'est pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que l'authentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. L'installer quand même ? [{answers:s}] ", - "confirm_app_install_danger": "AVERTISSEMENT ! Cette application est encore expérimentale (explicitement, elle ne fonctionne pas) et risque de casser votre système ! Vous ne devriez probablement PAS l'installer sans savoir ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ", - "confirm_app_install_thirdparty": "AVERTISSEMENT ! L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer si vous ne savez pas ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ", + "confirm_app_install_danger": "DANGER! Cette application est connue pour être encore expérimentale (si elle ne fonctionne pas explicitement)! Vous ne devriez probablement PAS l'installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers: s}'", + "confirm_app_install_thirdparty": "DANGER! Cette application ne fait pas partie du catalogue d'applications de Yunohost. L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'", "dpkg_is_broken": "Vous ne pouvez pas faire ça maintenant car dpkg/apt (le gestionnaire de paquets du système) semble avoir laissé des choses non configurées. Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a'.", "dyndns_could_not_check_available": "Impossible de vérifier si {domain:s} est disponible chez {provider:s}.", "file_does_not_exist": "Le fichier dont le chemin est {path:s} n'existe pas.", @@ -573,12 +573,12 @@ "group_info_failed": "L'information sur le groupe a échoué", "group_unknown": "Le groupe {group:s} est inconnu", "group_updated": "Le groupe '{group}' a été mis à jour", - "group_update_failed": "La mise à jour du groupe '{group}' a échoué", + "group_update_failed": "La mise à jour du groupe '{group}' a échoué : {error}", "group_already_allowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' activée pour l'application '{app:s}'", "group_already_disallowed": "Le groupe '{group:s}' a déjà la permission '{permission:s}' désactivée pour l'application '{app:s}'", "group_name_already_exist": "Le groupe {name:s} existe déjà", - "group_creation_failed": "Échec de la création du groupe '{group}'", - "group_deletion_failed": "Échec de la suppression du groupe '{group}'", + "group_creation_failed": "Échec de la création du groupe '{group}': {error}", + "group_deletion_failed": "Échec de la suppression du groupe '{group}': {error}", "edit_permission_with_group_all_users_not_allowed": "Vous n'êtes pas autorisé à modifier les permissions pour le groupe 'all_users', utilisez 'yunohost user permission clear APP' ou 'yunohost user permission add APP -u USER' à la place.", "log_permission_add": "Ajouter l'autorisation '{}' pour l'application '{}'", "log_permission_remove": "Supprimer l'autorisation '{}'", @@ -600,7 +600,7 @@ "migration_description_0012_postgresql_password_to_md5_authentication": "Forcer l'authentification PostgreSQL à utiliser MD5 pour les connexions locales", "migrations_exclusive_options": "'auto', '--skip' et '--force-rerun' sont des options mutuellement exclusives.", "migrations_not_pending_cant_skip": "Ces migrations ne sont pas en attente et ne peuvent donc pas être ignorées: {ids}", - "migration_0011_can_not_backup_before_migration": "Impossible de sauvegarder le système avant la migration. Erreur: {error: s}", + "migration_0011_can_not_backup_before_migration": "La sauvegarde du système avant la migration a échoué. La migration a échoué. Erreur: {error: s}", "migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP…", "migration_0011_migration_failed_trying_to_rollback": "La migration a échoué… essayait de restauration du système.", "migration_0011_rollback_success": "Système restauré.", @@ -610,11 +610,11 @@ "user_already_in_group": "L'utilisateur '{user:}' est déjà dans le groupe '{group: s}'", "user_not_in_group": "L'utilisateur '{user: s}' ne fait pas partie du groupe {group: s}", "migration_0011_backup_before_migration": "Création d'une sauvegarde des paramètres de la base de données LDAP et des applications avant la migration.", - "permission_not_found": "Autorisation '{permission: s}' non trouvée pour l'application '{app: s}'", + "permission_not_found": "Autorisation '{permission:s}' introuvable", "permission_name_not_valid": "Choisissez un nom d'autorisation autorisé pour '{permission: s}'", - "permission_update_failed": "Impossible de mettre à jour la permission", + "permission_update_failed": "Impossible de mettre à jour la permission '{permission}': {error}", "permission_generated": "Base de données des autorisations mise à jour", - "permission_updated": "Permission '{permission: s}' pour l'application '{app: s}' mise à jour", + "permission_updated": "Permission '{permission:s}' mise à jour", "permission_update_nothing_to_do": "Aucune autorisation pour mettre à jour", "remove_main_permission_not_allowed": "Supprimer l'autorisation principale n'est pas autorisé", "dyndns_provider_unreachable": "Impossible d’atteindre le fournisseur Dyndns {provider}: votre YunoHost n’est pas correctement connecté à Internet ou le serveur Dynette est en panne.", @@ -627,13 +627,30 @@ "need_define_permission_before": "Redéfinissez l'autorisation à l'aide de 'yunohost user permission add -u USER' avant de supprimer un groupe autorisé", "operation_interrupted": "L'opération a été interrompue manuellement", "permission_already_clear": "L'autorisation '{permission: s}' est déjà vide pour l'application {app: s}", - "permission_already_exist": "L'autorisation '{permission: s}' pour l'application {app: s} existe déjà", - "permission_created": "Permission '{permission: s}' pour l'application {app: s} créée", - "permission_creation_failed": "Impossible d'accorder la permission", - "permission_deleted": "Permission '{permission: s}' pour app {app: s} supprimée", - "permission_deletion_failed": "Autorisation manquante '{permission: s}' pour supprimer l'application '{app: s}'", + "permission_already_exist": "L'autorisation '{permission}' existe déjà", + "permission_created": "Permission '{permission:s}' créée", + "permission_creation_failed": "Impossible de créer l'autorisation '{permission}': {erreur}", + "permission_deleted": "Permission '{permission:s}' supprimée", + "permission_deletion_failed": "Impossible de supprimer la permission '{permission}': {error}", "remove_user_of_group_not_allowed": "Vous n'êtes pas autorisé à supprimer l'utilisateur '{utilisateur: s}' dans le groupe '{groupe: s}'", "migration_description_0011_setup_group_permission": "Configurer le groupe d'utilisateurs et configurer les autorisations pour les applications et les services", - "migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration", - "migration_0011_LDAP_update_failed": "Impossible de mettre à jour LDAP. Erreur: {error: s}" + "migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration.", + "migration_0011_LDAP_update_failed": "Impossible de mettre à jour LDAP. Erreur: {error: s}", + "group_already_exist": "Le groupe {group} existe déjà", + "group_already_exist_on_system": "Le groupe {group} existe déjà dans les groupes système", + "group_cannot_be_edited": "Le groupe {group} ne peut pas être édité manuellement.", + "group_cannot_be_deleted": "Le groupe {group} ne peut pas être supprimé manuellement.", + "group_user_already_in_group": "L'utilisateur {user} est déjà dans le groupe {group}", + "group_user_not_in_group": "L'utilisateur {user} n'est pas dans le groupe {group}", + "log_permission_create": "Créer permission '{}'", + "log_permission_delete": "supprimer permission '{}'", + "log_permission_urls": "Mettre à jour les URL liées à la permission '{}'", + "log_user_group_create": "Créer '{}' groupe", + "log_user_permission_update": "Mise à jour des accès pour la permission '{}'", + "log_user_permission_reset": "Réinitialiser la permission '{}'", + "migration_0011_failed_to_remove_stale_object": "Impossible de supprimer un objet obsolète {dn}: {error}", + "permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée '", + "permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '", + "permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé", + "user_already_exists": "L'utilisateur {user} existe déjà" } From d8468f77bc0d8554fcc17e61d228d3466007ee5c Mon Sep 17 00:00:00 2001 From: advocatux Date: Tue, 8 Oct 2019 17:07:51 +0000 Subject: [PATCH 233/299] Translated using Weblate (Spanish) Currently translated at 100.0% (554 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index aeee0ff7a..80b17673a 100644 --- a/locales/es.json +++ b/locales/es.json @@ -625,5 +625,6 @@ "permission_already_allowed": "El grupo «{group}» ya tiene el permiso «{permission}» activado", "permission_already_disallowed": "El grupo «{group}» ya tiene el permiso «{permission}» desactivado", "permission_cannot_remove_main": "No está permitido eliminar un permiso principal", - "user_already_exists": "El usuario {user} ya existe" + "user_already_exists": "El usuario {user} ya existe", + "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación." } From 5e477dd3f2d8df0ad74dcc9d7e7c96a8e1bb830f Mon Sep 17 00:00:00 2001 From: Lukas Dohn Date: Wed, 9 Oct 2019 08:31:31 +0000 Subject: [PATCH 234/299] Translated using Weblate (German) Currently translated at 39.2% (217 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index d03226187..1fe279d6b 100644 --- a/locales/de.json +++ b/locales/de.json @@ -282,7 +282,7 @@ "appslist_name_already_tracked": "Es gibt bereits eine registrierte App-Liste mit Namen {name:s}.", "appslist_url_already_tracked": "Es gibt bereits eine registrierte Anwendungsliste mit der URL {url:s}.", "appslist_migrating": "Migriere Anwendungsliste {appslist:s} …", - "appslist_could_not_migrate": "Konnte Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.", + "appslist_could_not_migrate": "Konnte die Anwendungsliste {appslist:s} nicht migrieren. Konnte die URL nicht verarbeiten... Der alte Cron-Job wurde unter {bkp_file:s} beibehalten.", "appslist_corrupted_json": "Anwendungslisten konnte nicht geladen werden. Es scheint, dass {filename:s} beschädigt ist.", "yunohost_ca_creation_success": "Die lokale Zertifizierungs-Authorität wurde angelegt.", "app_already_installed_cant_change_url": "Diese Application ist bereits installiert. Die URL kann durch diese Funktion nicht modifiziert werden. Überprüfe ob `app changeurl` verfügbar ist.", From a6a104677cb0ef723645734bd4b67c0c95dbc66a Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 9 Oct 2019 11:20:08 +0000 Subject: [PATCH 235/299] Translated using Weblate (French) Currently translated at 100.0% (554 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 6b5e2a790..62f6d6965 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -634,7 +634,7 @@ "permission_deletion_failed": "Impossible de supprimer la permission '{permission}': {error}", "remove_user_of_group_not_allowed": "Vous n'êtes pas autorisé à supprimer l'utilisateur '{utilisateur: s}' dans le groupe '{groupe: s}'", "migration_description_0011_setup_group_permission": "Configurer le groupe d'utilisateurs et configurer les autorisations pour les applications et les services", - "migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration.", + "migration_0011_LDAP_config_dirty": "Il semble que vous ayez personnalisé votre configuration LDAP. Pour cette migration, la configuration LDAP doit être mise à jour.\nVous devez enregistrer votre configuration actuelle, réintialiser la configuration d'origine en exécutant 'yunohost tools regen-conf -f', puis réessayer la migration", "migration_0011_LDAP_update_failed": "Impossible de mettre à jour LDAP. Erreur: {error: s}", "group_already_exist": "Le groupe {group} existe déjà", "group_already_exist_on_system": "Le groupe {group} existe déjà dans les groupes système", @@ -652,5 +652,6 @@ "permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée '", "permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '", "permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé", - "user_already_exists": "L'utilisateur {user} existe déjà" + "user_already_exists": "L'utilisateur {user} existe déjà", + "app_full_domain_unavailable": "Désolé, cette application nécessite l'installation d'un domaine complet, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Une solution possible consiste à ajouter et à utiliser un sous-domaine dédié à cette application." } From 5ac863c906f6858f3433fbc2082fc3f544faee1a Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 9 Oct 2019 11:21:54 +0000 Subject: [PATCH 236/299] Translated using Weblate (Esperanto) Currently translated at 100.0% (554 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/eo.json b/locales/eo.json index d27c0171c..0d8d13fe8 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -552,5 +552,6 @@ "log_app_remove": "Forigu la aplikon '{}'", "service_restart_failed": "Ne povis rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", "firewall_rules_cmd_failed": "Iuj komandoj pri fajroŝirmilo malsukcesis. Pliaj informoj en ensaluto.", - "certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …" + "certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …", + "app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu apliko postulas plenan domajnon esti instalita, sed iuj aliaj programoj jam estas instalitaj sur '{domain}'. Unu ebla solvo estas aldoni kaj uzi subdomajnon dediĉitan al ĉi tiu aplikaĵo anstataŭe." } From 08d97172369f0bb959398e539aba880a95dd7330 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Oct 2019 20:26:59 +0200 Subject: [PATCH 237/299] Improve test accuracy for apps --- src/yunohost/tests/test_apps.py | 78 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/yunohost/tests/test_apps.py b/src/yunohost/tests/test_apps.py index fb2f13c3f..5a6db43c9 100644 --- a/src/yunohost/tests/test_apps.py +++ b/src/yunohost/tests/test_apps.py @@ -4,6 +4,8 @@ import pytest import shutil import requests +from conftest import message, raiseYunohostError + from moulinette import m18n from moulinette.utils.filesystem import mkdir @@ -113,9 +115,9 @@ def app_is_not_installed(domain, app): def app_is_exposed_on_http(domain, path, message_in_page): try: - r = requests.get("http://127.0.0.1" + path + "/", headers={"Host": domain}, timeout=10) + r = requests.get("http://127.0.0.1" + path + "/", headers={"Host": domain}, timeout=10, verify=False) return r.status_code == 200 and message_in_page in r.text - except Exception: + except Exception as e: return False @@ -190,11 +192,11 @@ def test_legacy_app_install_private(secondary_domain): assert app_is_not_installed(secondary_domain, "legacy_app") -def test_legacy_app_install_unknown_domain(): +def test_legacy_app_install_unknown_domain(mocker): with pytest.raises(YunohostError): - install_legacy_app("whatever.nope", "/legacy") - # TODO check error message + with message(mocker, "app_argument_invalid"): + install_legacy_app("whatever.nope", "/legacy") assert app_is_not_installed("whatever.nope", "legacy_app") @@ -221,55 +223,51 @@ def test_legacy_app_install_multiple_instances(secondary_domain): assert app_is_not_installed(secondary_domain, "legacy_app__2") -def test_legacy_app_install_path_unavailable(secondary_domain): +def test_legacy_app_install_path_unavailable(mocker, secondary_domain): # These will be removed in teardown install_legacy_app(secondary_domain, "/legacy") with pytest.raises(YunohostError): - install_legacy_app(secondary_domain, "/") - # TODO check error message + with message(mocker, "app_location_unavailable"): + install_legacy_app(secondary_domain, "/") assert app_is_installed(secondary_domain, "legacy_app") assert app_is_not_installed(secondary_domain, "legacy_app__2") -def test_legacy_app_install_bad_args(): - - with pytest.raises(YunohostError): - install_legacy_app("this.domain.does.not.exists", "/legacy") - - -def test_legacy_app_install_with_nginx_down(secondary_domain): +def test_legacy_app_install_with_nginx_down(mocker, secondary_domain): os.system("systemctl stop nginx") - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, "app_action_cannot_be_ran_because_required_services_down"): install_legacy_app(secondary_domain, "/legacy") -def test_legacy_app_failed_install(secondary_domain): +def test_legacy_app_failed_install(mocker, secondary_domain): # This will conflict with the folder that the app # attempts to create, making the install fail mkdir("/var/www/legacy_app/", 0o750) with pytest.raises(YunohostError): - install_legacy_app(secondary_domain, "/legacy") - # TODO check error message + with message(mocker, 'app_install_script_failed'): + install_legacy_app(secondary_domain, "/legacy") assert app_is_not_installed(secondary_domain, "legacy_app") -def test_legacy_app_failed_remove(secondary_domain): +def test_legacy_app_failed_remove(mocker, secondary_domain): install_legacy_app(secondary_domain, "/legacy") # The remove script runs with set -eu and attempt to remove this # file without -f, so will fail if it's not there ;) os.remove("/etc/nginx/conf.d/%s.d/%s.conf" % (secondary_domain, "legacy_app")) - with pytest.raises(YunohostError): - app_remove("legacy") + + # TODO / FIXME : can't easily validate that 'app_not_properly_removed' + # is triggered for weird reasons ... + app_remove("legacy_app") # # Well here, we hit the classical issue where if an app removal script @@ -286,59 +284,61 @@ def test_full_domain_app(secondary_domain): assert app_is_exposed_on_http(secondary_domain, "/", "This is a dummy app") -def test_full_domain_app_with_conflicts(secondary_domain): +def test_full_domain_app_with_conflicts(mocker, secondary_domain): install_legacy_app(secondary_domain, "/legacy") - # TODO : once #808 is merged, add test that the message raised is 'app_full_domain_unavailable' - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, "app_full_domain_unavailable"): install_full_domain_app(secondary_domain) -def test_systemfuckedup_during_app_install(secondary_domain): +def test_systemfuckedup_during_app_install(mocker, secondary_domain): with pytest.raises(YunohostError): - install_break_yo_system(secondary_domain, breakwhat="install") - os.system("nginx -t") - os.system("systemctl status nginx") + with message(mocker, "app_install_failed"): + with message(mocker, 'app_action_broke_system'): + install_break_yo_system(secondary_domain, breakwhat="install") assert app_is_not_installed(secondary_domain, "break_yo_system") -def test_systemfuckedup_during_app_remove(secondary_domain): +def test_systemfuckedup_during_app_remove(mocker, secondary_domain): install_break_yo_system(secondary_domain, breakwhat="remove") with pytest.raises(YunohostError): - app_remove("break_yo_system") - os.system("nginx -t") - os.system("systemctl status nginx") + with message(mocker, 'app_action_broke_system'): + with message(mocker, 'app_removed'): + app_remove("break_yo_system") assert app_is_not_installed(secondary_domain, "break_yo_system") -def test_systemfuckedup_during_app_install_and_remove(secondary_domain): +def test_systemfuckedup_during_app_install_and_remove(mocker, secondary_domain): with pytest.raises(YunohostError): - install_break_yo_system(secondary_domain, breakwhat="everything") + with message(mocker, "app_install_failed"): + with message(mocker, 'app_action_broke_system'): + install_break_yo_system(secondary_domain, breakwhat="everything") assert app_is_not_installed(secondary_domain, "break_yo_system") -def test_systemfuckedup_during_app_upgrade(secondary_domain): +def test_systemfuckedup_during_app_upgrade(mocker, secondary_domain): install_break_yo_system(secondary_domain, breakwhat="upgrade") with pytest.raises(YunohostError): - app_upgrade("break_yo_system", file="./tests/apps/break_yo_system_ynh") + with message(mocker, 'app_action_broke_system'): + app_upgrade("break_yo_system", file="./tests/apps/break_yo_system_ynh") -def test_failed_multiple_app_upgrade(secondary_domain): +def test_failed_multiple_app_upgrade(mocker, secondary_domain): install_legacy_app(secondary_domain, "/legacy") install_break_yo_system(secondary_domain, breakwhat="upgrade") - with pytest.raises(YunohostError): + with raiseYunohostError(mocker, 'app_not_upgraded'): app_upgrade(["break_yo_system", "legacy_app"], file={"break_yo_system": "./tests/apps/break_yo_system_ynh", "legacy": "./tests/apps/legacy_app_ynh"}) From 61931f2c4b47f1e08479b3e46e09e8b3d7ab5f13 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 14 Oct 2019 20:28:16 +0200 Subject: [PATCH 238/299] We don't want this to call .error() for legit logs already completed --- src/yunohost/log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 8b0f893e8..0f5ff784c 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -502,7 +502,10 @@ class OperationLogger(object): The missing of the message below could help to see an electrical shortage. """ - self.error(m18n.n('log_operation_unit_unclosed_properly')) + if self.ended_at is not None or self.started_at is None: + return + else: + self.error(m18n.n('log_operation_unit_unclosed_properly')) def _get_description_from_name(name): From f12ff6ade8388917d6be6aae437023d3734bd610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 14 Oct 2019 20:04:59 +0000 Subject: [PATCH 239/299] Language reworked 2 --- locales/en.json | 160 ++++++++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/locales/en.json b/locales/en.json index a341a6b4f..54966135f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6,7 +6,7 @@ "admin_password_changed": "The administration password got changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do. Everything is already up-to-date.", - "app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down): {services}", + "app_action_cannot_be_ran_because_required_services_down": "This app requires more services to run. Before continuing, try to restart the following services (and possibly investigate why they are down): {services}", "app_action_broke_system": "This action seem to have broke these important services: {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Look into `app changeurl` if it's available.", @@ -16,24 +16,24 @@ "app_argument_required": "Argument '{name:s}' is required", "app_change_url_failed_nginx_reload": "Could not reload NGINX. Here is the output of 'nginx -t':\n{nginx_errors:s}", "app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain:s}{path:s}'), nothing to do.", - "app_change_url_no_script": "This application '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", + "app_change_url_no_script": "The app '{app_name:s}' doesn't support URL modification yet. Maybe you should upgrade it.", "app_change_url_success": "{app:s} URL is now {domain:s}{path:s}", "app_extraction_failed": "Could not extract the installation files", - "app_full_domain_unavailable": "Sorry, this application requires a full domain to be installed on, but some other apps are already installed on domain '{domain}'. One possible solution is to add and use a subdomain dedicated to this application instead.", + "app_full_domain_unavailable": "Sorry, this app must be installed on a domain of its own, but other apps are already installed on the domain '{domain}'. You could use a subdomain dedicated to this app instead.", "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", "app_install_failed": "Could not install {app}", - "app_install_script_failed": "An error occured inside the app installation script", + "app_install_script_failed": "An error occurred inside the app installation script", "app_location_already_used": "The app '{app}' is already installed in ({path})", - "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", + "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, '{domain}' is already in use by the other app '{other_app}'", "app_location_install_failed": "Cannot install the app there because it conflicts with the app '{other_app}' already installed in '{other_path}'", "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps upgrades have been cancelled: {apps}", - "app_upgrade_stopped": "The upgrade of all applications has been stopped to prevent possible damage because the previous application failed to upgrade", + "app_upgrade_stopped": "Upgrading all apps was stopped to prevent possible damage because one app could not be upgraded", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed", - "app_not_installed": "Could not find the application '{app:s}' in the list of installed apps: {all_apps}", + "app_not_installed": "Could not find the app '{app:s}' in the list of installed apps: {all_apps}", "app_not_properly_removed": "{app:s} has not been properly removed", "app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes", "app_removed": "{app:s} removed", @@ -41,29 +41,29 @@ "app_requirements_failed": "Some requirements are not met for {app}: {error}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", - "app_start_install": "Installing application {app}…", - "app_start_remove": "Removing application {app}…", - "app_start_backup": "Collecting files to be backed up for {app}…", - "app_start_restore": "Restoring application {app}…", + "app_start_install": "Installing the app '{app}'…", + "app_start_remove": "Removing the app '{app}'…", + "app_start_backup": "Collecting files to be backed up for the app '{app}'…", + "app_start_restore": "Restoring the app '{app}'…", "app_unknown": "Unknown app", "app_unsupported_remote_type": "Unsupported remote type used for the app", "app_upgrade_several_apps": "The following apps will be upgraded: {apps}", "app_upgrade_app_name": "Now upgrading {app}…", "app_upgrade_failed": "Could not upgrade {app:s}", - "app_upgrade_some_app_failed": "Some applications could not be upgraded", + "app_upgrade_some_app_failed": "Some apps could not be upgraded", "app_upgraded": "{app:s} upgraded", - "apps_already_up_to_date": "All applications are already up-to-date", + "apps_already_up_to_date": "All apps are already up-to-date", "apps_permission_not_found": "No permission found for the installed apps", - "appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is damaged.", - "appslist_could_not_migrate": "Could not migrate the app list {appslist:s}! Could not parse the URL… The old cron job was kept kept in {bkp_file:s}.", - "appslist_fetched": "Updated application list {appslist:s}", - "appslist_migrating": "Migrating application list {appslist:s}…", - "appslist_name_already_tracked": "A registered application list with name {name:s} already exists.", - "appslist_removed": "{appslist:s} application list removed", - "appslist_retrieve_bad_format": "Could not read the fetched application list {appslist:s}", - "appslist_retrieve_error": "Cannot retrieve the remote application list {appslist:s}: {error:s}", - "appslist_unknown": "Application list {appslist:s} unknown.", - "appslist_url_already_tracked": "There is already a registered application list with the URL {url:s}.", + "appslist_corrupted_json": "Could not load the app lists. It looks like {filename:s} is damaged.", + "appslist_could_not_migrate": "Could not migrate the app list '{appslist:s}'! Could not parse the URL… The old cron job was kept kept in {bkp_file:s}.", + "appslist_fetched": "Updated the app list '{appslist:s}'", + "appslist_migrating": "Migrating the app list '{appslist:s}'…", + "appslist_name_already_tracked": "A registered app list with the name {name:s} already exists.", + "appslist_removed": "The '{appslist:s}' app list was removed", + "appslist_retrieve_bad_format": "Could not read the fetched app list '{appslist:s}'", + "appslist_retrieve_error": "Cannot retrieve the remote app list '{appslist:s}': {error:s}", + "appslist_unknown": "The app list '{appslist:s}' is unknown.", + "appslist_url_already_tracked": "There is already a registered app list with the URL {url:s}.", "ask_current_admin_password": "Current administration password", "ask_email": "E-mail address", "ask_firstname": "First name", @@ -89,7 +89,7 @@ "backup_archive_open_failed": "Could not open the backup archive", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", - "backup_ask_for_copying_if_needed": "Some files could not be prepared for backup using the method that avoids temporarily wasting space on the system. To perform the backup, {size:s}MB will be temporarily. Do you agree?", + "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily due to inability to prepare some files for backup using a more efficient method?", "backup_borg_not_implemented": "The Borg backup method is not yet implemented", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cleaning_failed": "Could not clean-up the temporary backup folder", @@ -115,7 +115,7 @@ "backup_output_directory_forbidden": "Pick a different output directory. Backups can not be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", "backup_output_directory_not_empty": "You should pick an empty output directory", "backup_output_directory_required": "You must provide an output directory for the backup", - "backup_output_symlink_dir_broken": "You have a broken symlink in place of your archive directory '{path:s}'. You may have a specific setup to backup your data on another filesystem, in this case you probably forgot to remount or plug in your hard-drive or USB key.", + "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' only contains a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", "backup_permission": "Backup permission for app {app:s}", "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", "backup_running_hooks": "Running backup hooks…", @@ -145,16 +145,16 @@ "certmanager_no_cert_file": "Could not read the certificate file for the domain {domain:s} (file: {file:s})", "certmanager_self_ca_conf_file_not_found": "Could not find configuration file for self-signing authority (file: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Could not parse name of self-signing authority (file: {file:s})", - "confirm_app_install_warning": "Warning: This application may work, but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", - "confirm_app_install_danger": "DANGER! This application 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 break your system... If you are willing to take that risk anyway, type '{answers:s}'", - "confirm_app_install_thirdparty": "DANGER! This application is not part of Yunohost's application catalog. Installing third-party applications may compromise the integrity and security of your system. 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 break your system... If you are willing to take that risk anyway, type '{answers:s}'", + "confirm_app_install_warning": "Warning: This app may work, but is not well-integrated in YunoHost. Some features such as single sign-on and backup/restore might not be available. Install anyway? [{answers:s}] ", + "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 break your system… If you are willing to take that risk anyway, type '{answers:s}'", + "confirm_app_install_thirdparty": "DANGER! This app is not part of Yunohost's app catalog. Installing third-party apps may compromise the integrity and security of your system. 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 break your system… If you are willing to take that risk anyway, type '{answers:s}'", "custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}", "custom_appslist_name_required": "You must provide a name for your custom app list", "diagnosis_debian_version_error": "Could not retrieve the Debian version: {error}", "diagnosis_kernel_version_error": "Could not retrieve kernel version: {error}", "diagnosis_monitor_disk_error": "Could not monitor disks: {error}", "diagnosis_monitor_system_error": "Could not monitor system: {error}", - "diagnosis_no_apps": "No installed application", + "diagnosis_no_apps": "No such installed app", "dpkg_is_broken": "You cannot do this right now because dpkg/APT (the system package managers) seems to be in a broken state… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", "dpkg_lock_not_available": "This command can't be ran right now because another program seems to be using the lock of dpkg (the system package manager)", "domain_cannot_remove_main": "Cannot remove main domain. Set one first", @@ -236,7 +236,7 @@ "hook_json_return_error": "Could not read return from hook {path:s}. Error: {msg:s}. Raw content: {raw_content}", "hook_list_by_invalid": "This property can not be used to list hooks", "hook_name_unknown": "Unknown hook name '{name:s}'", - "installation_complete": "Installation complete", + "installation_complete": "Installation completed", "installation_failed": "Something went wrong with the installation", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", @@ -248,13 +248,13 @@ "log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log display {name} --share' to get help", "log_does_exists": "There is not operation log with the name '{log}', use 'yunohost log list' to see all available operation logs", "log_operation_unit_unclosed_properly": "Operation unit has not been closed properly", - "log_app_fetchlist": "Add an application list", - "log_app_removelist": "Remove an application list", - "log_app_change_url": "Change the URL of '{}' application", - "log_app_install": "Install the '{}' application", - "log_app_remove": "Remove the '{}' application", - "log_app_upgrade": "Upgrade the '{}' application", - "log_app_makedefault": "Make '{}' the default application", + "log_app_fetchlist": "Add an app list", + "log_app_removelist": "Remove an app list", + "log_app_change_url": "Change the URL of the '{}' app", + "log_app_install": "Install the '{}' app", + "log_app_remove": "Remove the '{}' app", + "log_app_upgrade": "Upgrade the '{}' app", + "log_app_makedefault": "Make '{}' the default app", "log_available_on_yunopaste": "This log is now available via {url}", "log_backup_restore_system": "Restore system from a backup archive", "log_backup_restore_app": "Restore '{}' from a backup archive", @@ -264,12 +264,12 @@ "log_domain_remove": "Remove '{}' domain from system configuration", "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", "log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'", - "log_letsencrypt_cert_install": "Install a Let's encrypt certificate on '{}' domain", + "log_letsencrypt_cert_install": "Install a Let's Encrypt certificate on '{}' domain", "log_permission_create": "Create permission '{}'", "log_permission_delete": "Delete permission '{}'", "log_permission_urls": "Update urls related to permission '{}'", "log_selfsigned_cert_install": "Install self signed certificate on '{}' domain", - "log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate", + "log_letsencrypt_cert_renew": "Renew '{}' Let's Encrypt certificate", "log_regen_conf": "Regenerate system configurations '{}'", "log_user_create": "Add '{}' user", "log_user_delete": "Delete '{}' user", @@ -331,31 +331,31 @@ "migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system:(…", "migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.", - "migration_0006_disclaimer": "YunoHost now expects admin and root passwords to be synchronized. By running this migration, your root password is going to be replaced by the admin password.", - "migration_0007_cancelled": "YunoHost has failed to improve the way your SSH conf is managed.", + "migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration, replaces your root password with the admin password.", + "migration_0007_cancelled": "Could not improve the way your SSH configuration is managed.", "migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", "migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:", "migration_0008_port": "• You will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;", "migration_0008_root": "• You will not be able to connect as root through SSH. Instead you should use the admin user;", "migration_0008_dsa": "• The DSA key will be turned off. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;", - "migration_0008_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.", - "migration_0008_no_warning": "No major risk indentified concerning overriding your SSH configuration—one can however not be absolutely sure ;)! Run the migration to override it. Otherwise, you can also skip the migration - though it is not recommended.", + "migration_0008_warning": "If you understand those warnings and want YunoHost to override your current configuration, run the migration. Otherwise, you can also skip the migration, though it is not recommended.", + "migration_0008_no_warning": "Overriding your SSH configuration should be safe, though this can not be promised! Run the migration to override it. Otherwise, you can also skip the migration, though it is not recommended.", "migration_0009_not_needed": "This migration already happened somehow… (?) Skipping.", "migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.", - "migration_0011_can_not_backup_before_migration": "The backup of the system before the migration failed. Migration failed. Error: {error:s}", + "migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}", "migration_0011_create_group": "Creating a group for each user…", - "migration_0011_done": "Migration successful. You are now able to manage usergroups.", - "migration_0011_LDAP_config_dirty": "It look like that you customized your LDAP configuration. For this migration the LDAP configuration needs to be updated.\nYou need to save your current configuration, reintialize the original configuration by running 'yunohost tools regen-conf -f' and retry the migration", + "migration_0011_done": "Migration completed. You are now able to manage usergroups.", + "migration_0011_LDAP_config_dirty": "Save your current custom LDAP conguration, and reintialize the original one by running 'yunohost tools regen-conf -f' and retry the migration.", "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…", - "migration_0011_migration_failed_trying_to_rollback": "Migration failed… trying to roll back the system.", + "migration_0011_migration_failed_trying_to_rollback": "Could not migrate… trying to roll back the system.", "migration_0011_rollback_success": "System rolled back.", "migration_0011_update_LDAP_database": "Updating LDAP database…", "migration_0011_update_LDAP_schema": "Updating LDAP schema…", - "migration_0011_failed_to_remove_stale_object": "Failed to remove stale object {dn}: {error}", + "migration_0011_failed_to_remove_stale_object": "Could not remove stale object {dn}: {error}", "migrations_already_ran": "Those migrations are already done: {ids}", - "migrations_cant_reach_migration_file": "Could not access migrations files at path %s", - "migrations_dependencies_not_satisfied": "Cannot run migration {id} because first you need to run these migrations: {dependencies_id}", + "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", + "migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.", "migrations_failed_to_load_migration": "Could not load migration {id}: {error}", "migrations_exclusive_options": "'--auto', '--skip', and '--force-rerun' are mutually exclusive options.", "migrations_list_conflict_pending_done": "You cannot use both '--previous' and '--done' at the same time.", @@ -364,32 +364,32 @@ "migrations_must_provide_explicit_targets": "You must provide explicit targets when using '--skip' or '--force-rerun'", "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option '--accept-disclaimer'.", "migrations_no_migrations_to_run": "No migrations to run", - "migrations_no_such_migration": "There is no migration called {id}", + "migrations_no_such_migration": "There is no migration called '{id}'", "migrations_not_pending_cant_skip": "Those migrations are not pending, so cannot be skipped: {ids}", "migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}", "migrations_running_forward": "Running migration {id}…", "migrations_skip_migration": "Skipping migration {id}…", "migrations_success_forward": "Migration {id} completed", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", - "monitor_disabled": "Server monitoring now turned off", - "monitor_enabled": "Server monitoring now turned on", + "monitor_disabled": "Server monitoring now off", + "monitor_enabled": "Server monitoring now on", "monitor_glances_con_failed": "Could not connect to Glances server", "monitor_not_enabled": "Server monitoring is off", "monitor_period_invalid": "Invalid time period", - "monitor_stats_file_not_found": "Statistics file not found", + "monitor_stats_file_not_found": "Could not find the statistics file", "monitor_stats_no_update": "No monitoring statistics to update", "monitor_stats_period_unavailable": "No available statistics for the period", "mountpoint_unknown": "Unknown mountpoint", - "mysql_db_creation_failed": "MySQL database creation failed", - "mysql_db_init_failed": "MySQL database init failed", - "mysql_db_initialized": "The MySQL database now initialized", + "mysql_db_creation_failed": "Could not create MySQL database", + "mysql_db_init_failed": "Could not initialize MySQL database", + "mysql_db_initialized": "The MySQL database is now initialized", "network_check_mx_ko": "DNS MX record is not set", "network_check_smtp_ko": "Outbound e-mail (SMTP port 25) seems to be blocked by your network", "network_check_smtp_ok": "Outbound e-mail (SMTP port 25) is not blocked", - "no_internet_connection": "Server not connected to the Internet", + "no_internet_connection": "The server is not connected to the Internet", "not_enough_disk_space": "Not enough free space on '{path:s}'", - "operation_interrupted": "The operation was manually interrupted?", - "package_not_installed": "Package '{pkgname}' is not installed", + "operation_interrupted": "Was the operation manually interrupted?", + "package_not_installed": "The package '{pkgname}' is not installed", "package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'", "package_unknown": "Unknown package '{pkgname}'", "packages_upgrade_failed": "Could not upgrade all the packages", @@ -400,7 +400,7 @@ "password_too_simple_4": "The password needs to be at least 12 characters long and contain a digit, upper, lower and special characters", "pattern_backup_archive_name": "Must be a valid filename with max 30 characters, alphanumeric and -_. characters only", "pattern_domain": "Must be a valid domain name (e.g. my-domain.org)", - "pattern_email": "Must be a valid email address (e.g. someone@domain.org)", + "pattern_email": "Must be a valid e-mail address (e.g. someone@example.com)", "pattern_firstname": "Must be a valid first name", "pattern_lastname": "Must be a valid last name", "pattern_listname": "Must be alphanumeric and underscore characters only", @@ -466,7 +466,7 @@ "server_reboot_confirm": "The server will reboot immediatly, are you sure? [{answers:s}]", "service_add_failed": "Could not add the service '{service:s}'", "service_added": "The service '{service:s}' added", - "service_already_started": "The service '{service:s}' has already been started", + "service_already_started": "The service '{service:s}' is running already", "service_already_stopped": "The service '{service:s}' has already been stopped", "service_cmd_exec_failed": "Could not execute the command '{command:s}'", "service_description_avahi-daemon": "Allows you to reach your server using 'yunohost.local' in your local network", @@ -475,10 +475,10 @@ "service_description_fail2ban": "Protects against brute-force and other kinds of attacks from the Internet", "service_description_glances": "Monitors system info on your server", "service_description_metronome": "Manage XMPP instant messaging accounts", - "service_description_mysql": "Stores applications data (SQL database)", + "service_description_mysql": "Stores app data (SQL database)", "service_description_nginx": "Serves or provides access to all the websites hosted on your server", "service_description_nslcd": "Handles YunoHost user shell connection", - "service_description_php7.0-fpm": "Runs applications written in PHP with NGINX", + "service_description_php7.0-fpm": "Runs apps written in PHP with NGINX", "service_description_postfix": "Used to send and receive e-mails", "service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs", "service_description_rmilter": "Checks various parameters in e-mails", @@ -486,42 +486,42 @@ "service_description_slapd": "Stores users, domains and related info", "service_description_ssh": "Allows you to connect remotely to your server via a terminal (SSH protocol)", "service_description_yunohost-api": "Manages interactions between the YunoHost web interface and the system", - "service_description_yunohost-firewall": "Manages open and close connexion ports to services", + "service_description_yunohost-firewall": "Manages open and close connection ports to services", "service_disable_failed": "Could not turn off the service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_disabled": "'{service:s}' service turned off", + "service_disabled": "The '{service:s}' service was turned off", "service_enable_failed": "Could not turn on the service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_enabled": "'{service:s}' service turned off", - "service_no_log": "No log to display for service '{service:s}'", + "service_enabled": "The '{service:s}' service was turned off", + "service_no_log": "No logs to display for the service '{service:s}'", "service_regen_conf_is_deprecated": "'yunohost service regen-conf' is deprecated! Please use 'yunohost tools regen-conf' instead.", "service_remove_failed": "Could not remove the service '{service:s}'", "service_removed": "'{service:s}' service removed", "service_reload_failed": "Could not reload the service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_reloaded": "'{service:s}' service reloaded", + "service_reloaded": "The '{service:s}' service was reloaded", "service_restart_failed": "Could not restart the service '{service:s}'\n\nRecent service logs:{logs:s}", "service_restarted": "'{service:s}' service restarted", "service_reload_or_restart_failed": "Could not reload or restart the service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_reloaded_or_restarted": "'{service:s}' service reloaded or restarted", + "service_reloaded_or_restarted": "The '{service:s}' service was reloaded or restarted", "service_start_failed": "Could not start the service '{service:s}'\n\nRecent service logs:{logs:s}", "service_started": "'{service:s}' service started", "service_stop_failed": "Could not stop the service '{service:s}'\n\nRecent service logs:{logs:s}", - "service_stopped": "'{service:s}' service stopped", + "service_stopped": "The '{service:s}' service stopped", "service_unknown": "Unknown service '{service:s}'", "ssowat_conf_generated": "SSOwat configuration generated", "ssowat_conf_updated": "SSOwat configuration updated", - "ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", - "ssowat_persistent_conf_write_error": "Could not save persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", + "ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit the /etc/ssowat/conf.json.persistent file to fix the JSON syntax", + "ssowat_persistent_conf_write_error": "Could not save persistent SSOwat configuration: {error:s}. Edit the /etc/ssowat/conf.json.persistent file to fix the JSON syntax", "system_upgraded": "System upgraded", "system_username_exists": "Username already exists in the list of system users", "this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers)… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", - "tools_update_failed_to_app_fetchlist": "Could not update YunoHost's applists because: {error}", + "tools_update_failed_to_app_fetchlist": "Could not update YunoHost's app lists because: {error}", "tools_upgrade_at_least_one": "Please specify '--apps', or '--system'", "tools_upgrade_cant_both": "Cannot upgrade both system and apps at the same time", "tools_upgrade_cant_hold_critical_packages": "Could not hold critical packages…", - "tools_upgrade_cant_unhold_critical_packages": "Could not to unhold critical packages…", + "tools_upgrade_cant_unhold_critical_packages": "Could not unhold critical packages…", "tools_upgrade_regular_packages": "Now upgrading 'regular' (non-yunohost-related) packages…", "tools_upgrade_regular_packages_failed": "Could not upgrade packages: {packages_list}", "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages…", - "tools_upgrade_special_packages_explanation": "This action will end, but the actual special upgrade will continue in background. Please don't start any other action on your server in the next ~10 minutes (depending on your hardware speed). Once it i done, you may have to log in on the webadmin page again. The upgrade log will be available in Tools → Log (on the webadmin page) or through 'yunohost log list' (from the command line).", + "tools_upgrade_special_packages_explanation": "This action will end, but the actual special upgrade will continue in background. Please don't start any other actions on your server the next ~10 minutes (depending on hardware speed). Once done, you may have to log in on the webadmin page again. The upgrade log will be available in Tools → Log (on the webadmin page) or through 'yunohost log list' (from the command-line).", "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed.\nPress [Enter] to get the command line back", "unbackup_app": "App '{app:s}' will not be saved", "unexpected_error": "Something unexpected went wrong: {error}", @@ -531,14 +531,14 @@ "update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "update_apt_cache_warning": "Something went wrong while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "updating_apt_cache": "Fetching available upgrades for system packages…", - "updating_app_lists": "Fetching available upgrades for applications…", + "updating_app_lists": "Fetching available upgrades for apps…", "upgrade_complete": "Upgrade complete", "upgrading_packages": "Upgrading packages…", "upnp_dev_not_found": "No UPnP device found", "upnp_disabled": "UPnP turned off", "upnp_enabled": "UPnP turned on", "upnp_port_open_failed": "Could not open port via UPnP", - "user_already_exists": "User {user} already exists", + "user_already_exists": "The user '{user}' already exists", "user_created": "User created", "user_creation_failed": "Could not create user {user}: {error}", "user_deleted": "User deleted", @@ -552,7 +552,7 @@ "yunohost_already_installed": "YunoHost is already installed", "yunohost_ca_creation_failed": "Could not create certificate authority", "yunohost_ca_creation_success": "Local certification authority created.", - "yunohost_configured": "YunoHost now configured", + "yunohost_configured": "YunoHost is now configured", "yunohost_installing": "Installing YunoHost…", - "yunohost_not_installed": "YunoHost is incorrectly or not correctly installed. Please run 'yunohost tools postinstall'" + "yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'" } From dcea6ae5fafcd9415ff8df34542286416797c859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 14 Oct 2019 21:08:55 +0000 Subject: [PATCH 240/299] This migration, Co-Authored-By: Alexandre Aubin --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 54966135f..5859dca6c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -331,7 +331,7 @@ "migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.", "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system:(…", "migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.", - "migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration, replaces your root password with the admin password.", + "migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration replaces your root password with the admin password.", "migration_0007_cancelled": "Could not improve the way your SSH configuration is managed.", "migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.", "migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:", From aef8a38071433e0b528bfe14e3319ae007a35541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 14 Oct 2019 21:10:46 +0000 Subject: [PATCH 241/299] is a broken symlink --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 5859dca6c..27b3bcaef 100644 --- a/locales/en.json +++ b/locales/en.json @@ -115,7 +115,7 @@ "backup_output_directory_forbidden": "Pick a different output directory. Backups can not be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", "backup_output_directory_not_empty": "You should pick an empty output directory", "backup_output_directory_required": "You must provide an output directory for the backup", - "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' only contains a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", + "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", "backup_permission": "Backup permission for app {app:s}", "backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", "backup_running_hooks": "Running backup hooks…", From af44f4c73e2d3689ec5355b132581ca871d358b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 14 Oct 2019 21:13:19 +0000 Subject: [PATCH 242/299] This way is used, could not be prepared --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 27b3bcaef..9f9cb05ba 100644 --- a/locales/en.json +++ b/locales/en.json @@ -89,7 +89,7 @@ "backup_archive_open_failed": "Could not open the backup archive", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", - "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily due to inability to prepare some files for backup using a more efficient method?", + "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s} MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", "backup_borg_not_implemented": "The Borg backup method is not yet implemented", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cleaning_failed": "Could not clean-up the temporary backup folder", From a8deabc36976947be492ea2857bbfd25a6b17e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Mon, 14 Oct 2019 21:17:11 +0000 Subject: [PATCH 243/299] Start all required --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 9f9cb05ba..ed7f08efd 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6,7 +6,7 @@ "admin_password_changed": "The administration password got changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do. Everything is already up-to-date.", - "app_action_cannot_be_ran_because_required_services_down": "This app requires more services to run. Before continuing, try to restart the following services (and possibly investigate why they are down): {services}", + "app_action_cannot_be_ran_because_required_services_down": "Start all required services to run this app. Try to restart the following ones (and possibly investigate why they are down): {services}", "app_action_broke_system": "This action seem to have broke these important services: {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Look into `app changeurl` if it's available.", From 7d0119ade48e9627be25e5857f9d0ff91bd65747 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 15 Oct 2019 01:06:04 +0200 Subject: [PATCH 244/299] Fix backup info.json format... --- src/yunohost/backup.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index c28160342..4bf8d8afc 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -602,10 +602,10 @@ class BackupManager(): env=env_dict, chdir=self.work_dir) - ret_succeed = {hook: {path:result["state"] for path, result in infos.items()} + ret_succeed = {hook: [path for path, result in infos.items() if result["state"] == "succeed"] for hook, infos in ret.items() if any(result["state"] == "succeed" for result in infos.values())} - ret_failed = {hook: {path:result["state"] for path, result in infos.items.items()} + ret_failed = {hook: [path for path, result in infos.items.items() if result["state"] == "failed"] for hook, infos in ret.items() if any(result["state"] == "failed" for result in infos.values())} @@ -2371,6 +2371,13 @@ def backup_info(name, with_details=False, human_readable=False): if "size_details" in info.keys(): for category in ["apps", "system"]: for name, key_info in info[category].items(): + + # Stupid legacy fix for weird format between 3.5 and 3.6 + if isinstance(key_info, dict): + key_info = key_info.keys() + + info[category][name] = key_info = {"paths": key_info} + if name in info["size_details"][category].keys(): key_info["size"] = info["size_details"][category][name] if human_readable: From 6dc720f3cfbf5945d89614587f6333038d8dffa6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 15 Oct 2019 02:36:12 +0200 Subject: [PATCH 245/299] [yolo] Use read_json / write_to_json helpers to read/write ssowat conf.json.persistent --- locales/en.json | 2 -- src/yunohost/app.py | 22 +++++++++------------- src/yunohost/tools.py | 16 ++++------------ src/yunohost/user.py | 21 +++++++++------------ 4 files changed, 22 insertions(+), 39 deletions(-) diff --git a/locales/en.json b/locales/en.json index a341a6b4f..cc73d658a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -508,8 +508,6 @@ "service_unknown": "Unknown service '{service:s}'", "ssowat_conf_generated": "SSOwat configuration generated", "ssowat_conf_updated": "SSOwat configuration updated", - "ssowat_persistent_conf_read_error": "Could not read persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", - "ssowat_persistent_conf_write_error": "Could not save persistent SSOwat configuration: {error:s}. Edit /etc/ssowat/conf.json.persistent file to fix the JSON syntax", "system_upgraded": "System upgraded", "system_username_exists": "Username already exists in the list of system users", "this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers)… You can try to solve this issue by connecting through SSH and running `sudo dpkg --configure -a`.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f0b4d3c25..8ac2bcb11 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -41,7 +41,7 @@ from datetime import datetime from moulinette import msignals, m18n, msettings from moulinette.utils.log import getActionLogger -from moulinette.utils.filesystem import read_json, read_toml +from moulinette.utils.filesystem import read_json, read_toml, write_to_json from yunohost.service import service_log, service_status, _run_service_command from yunohost.utils import packages @@ -1233,25 +1233,21 @@ def app_makedefault(operation_logger, app, domain=None): raise YunohostError('app_make_default_location_already_used', app=app, domain=app_domain, other_app=app_map(raw=True)[domain]["/"]["id"]) - try: - with open('/etc/ssowat/conf.json.persistent') as json_conf: - ssowat_conf = json.loads(str(json_conf.read())) - except ValueError as e: - raise YunohostError('ssowat_persistent_conf_read_error', error=e) - except IOError: + # TODO / FIXME : current trick is to add this to conf.json.persisten + # This is really not robust and should be improved + # e.g. have a flag in /etc/yunohost/apps/$app/ to say that this is the + # default app or idk... + if not os.path.exists('/etc/ssowat/conf.json.persistent'): ssowat_conf = {} + else: + ssowat_conf = read_json('/etc/ssowat/conf.json.persistent') if 'redirected_urls' not in ssowat_conf: ssowat_conf['redirected_urls'] = {} ssowat_conf['redirected_urls'][domain + '/'] = app_domain + app_path - try: - with open('/etc/ssowat/conf.json.persistent', 'w+') as f: - json.dump(ssowat_conf, f, sort_keys=True, indent=4) - except IOError as e: - raise YunohostError('ssowat_persistent_conf_write_error', error=e) - + write_to_json('/etc/ssowat/conf.json.persistent', ssowat_conf) os.system('chmod 644 /etc/ssowat/conf.json.persistent') logger.success(m18n.n('ssowat_conf_updated')) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 64689fe0c..679bf1190 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -350,25 +350,17 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False, os.system('hostname yunohost.yunohost.org') # Add a temporary SSOwat rule to redirect SSO to admin page - try: - with open('/etc/ssowat/conf.json.persistent') as json_conf: - ssowat_conf = json.loads(str(json_conf.read())) - except ValueError as e: - raise YunohostError('ssowat_persistent_conf_read_error', error=str(e)) - except IOError: + if not os.path.exists('/etc/ssowat/conf.json.persistent'): ssowat_conf = {} + else: + ssowat_conf = read_json('/etc/ssowat/conf.json.persistent') if 'redirected_urls' not in ssowat_conf: ssowat_conf['redirected_urls'] = {} ssowat_conf['redirected_urls']['/'] = domain + '/yunohost/admin' - try: - with open('/etc/ssowat/conf.json.persistent', 'w+') as f: - json.dump(ssowat_conf, f, sort_keys=True, indent=4) - except IOError as e: - raise YunohostError('ssowat_persistent_conf_write_error', error=str(e)) - + write_to_json('/etc/ssowat/conf.json.persistent', ssowat_conf) os.system('chmod 644 /etc/ssowat/conf.json.persistent') # Create SSL CA diff --git a/src/yunohost/user.py b/src/yunohost/user.py index fe27492f4..e12cccb9b 100644 --- a/src/yunohost/user.py +++ b/src/yunohost/user.py @@ -35,8 +35,10 @@ import subprocess import copy from moulinette import m18n -from yunohost.utils.error import YunohostError from moulinette.utils.log import getActionLogger +from moulinette.utils.filesystem import read_json, write_to_json, read_yaml, write_to_yaml + +from yunohost.utils.error import YunohostError from yunohost.service import service_status from yunohost.log import is_unit_operation @@ -195,21 +197,16 @@ def user_create(operation_logger, username, firstname, lastname, mail, password, attr_dict['mail'] = [attr_dict['mail']] + aliases # If exists, remove the redirection from the SSO - try: - with open('/etc/ssowat/conf.json.persistent') as json_conf: - ssowat_conf = json.loads(str(json_conf.read())) - except ValueError as e: - raise YunohostError('ssowat_persistent_conf_read_error', error=str(e)) - except IOError: + if not os.path.exists('/etc/ssowat/conf.json.persistent'): ssowat_conf = {} + else: + ssowat_conf = read_json('/etc/ssowat/conf.json.persistent') if 'redirected_urls' in ssowat_conf and '/' in ssowat_conf['redirected_urls']: del ssowat_conf['redirected_urls']['/'] - try: - with open('/etc/ssowat/conf.json.persistent', 'w+') as f: - json.dump(ssowat_conf, f, sort_keys=True, indent=4) - except IOError as e: - raise YunohostError('ssowat_persistent_conf_write_error', error=str(e)) + + write_to_json('/etc/ssowat/conf.json.persistent', ssowat_conf) + os.system('chmod 644 /etc/ssowat/conf.json.persistent') try: ldap.add('uid=%s,ou=users' % username, attr_dict) From 4def4dfa6a7cef4137498eaece55b4732f0559a8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 15 Oct 2019 14:54:52 +0200 Subject: [PATCH 246/299] [yolofix] Should have a list of string to be able to join() later --- src/yunohost/tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 679bf1190..28b507707 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -608,8 +608,8 @@ def tools_upgrade(operation_logger, apps=None, system=False): # randomly from yunohost itself... upgrading them is likely to critical_packages = ("moulinette", "yunohost", "yunohost-admin", "ssowat", "python") - critical_packages_upgradable = [p for p in upgradables if p["name"] in critical_packages] - noncritical_packages_upgradable = [p for p in upgradables if p["name"] not in critical_packages] + critical_packages_upgradable = [p["name"] for p in upgradables if p["name"] in critical_packages] + noncritical_packages_upgradable = [p["name"] for p in upgradables if p["name"] not in critical_packages] # Prepare dist-upgrade command dist_upgrade = "DEBIAN_FRONTEND=noninteractive" From f31c8c7475f054482852ac44d7b263fa5371d095 Mon Sep 17 00:00:00 2001 From: pitfd Date: Mon, 14 Oct 2019 09:14:40 +0000 Subject: [PATCH 247/299] Translated using Weblate (German) Currently translated at 39.4% (218 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/de/ --- locales/de.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/de.json b/locales/de.json index 1fe279d6b..6699f508c 100644 --- a/locales/de.json +++ b/locales/de.json @@ -413,5 +413,6 @@ "global_settings_setting_security_password_admin_strength": "Stärke des Admin-Passworts", "global_settings_key_doesnt_exists": "Der Schlüssel'{settings_key:s}' existiert nicht in den globalen Einstellungen, du kannst alle verfügbaren Schlüssel sehen, indem du 'yunohost settings list' ausführst", "log_app_makedefault": "Mache '{}' zur Standard-Anwendung", - "hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}" + "hook_json_return_error": "Konnte die Rückkehr vom Einsprungpunkt {path:s} nicht lesen. Fehler: {msg:s}. Unformatierter Inhalt: {raw_content}", + "app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation einer vollständigen Domäne, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist." } From 3aea7f6b04b26de1c634b8029fbe63da63a388ff Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Thu, 10 Oct 2019 08:24:02 +0000 Subject: [PATCH 248/299] Translated using Weblate (Catalan) Currently translated at 100.0% (554 of 554 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 385 ++++++++++++++++++++++++++---------------------- 1 file changed, 209 insertions(+), 176 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index f5c040670..1476d5fb5 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -1,31 +1,31 @@ { "action_invalid": "Acció '{action:s}' invàlida", "admin_password": "Contrasenya d'administració", - "admin_password_change_failed": "No s'ha pogut canviar la contrasenya", + "admin_password_change_failed": "No es pot canviar la contrasenya", "admin_password_changed": "S'ha canviat la contrasenya d'administració", "app_already_installed": "{app:s} ja està instal·lada", "app_already_installed_cant_change_url": "Aquesta aplicació ja està instal·lada. La URL no és pot canviar únicament amb aquesta funció. Mireu a \"app changeurl\" si està disponible.", "app_already_up_to_date": "{app:s} ja està actualitzada", - "app_argument_choice_invalid": "Aquesta opció no és vàlida per l'argument '{name:s}', ha de ser una de {choices:s}", - "app_argument_invalid": "Valor invàlid per l'argument '{name:s}':{error:s}", + "app_argument_choice_invalid": "Utilitzeu una de les opcions «{choices:s}» per l'argument «{name:s}»", + "app_argument_invalid": "Escolliu un valor vàlid per l'argument «{name:s}»: {error:s}", "app_argument_required": "Es necessita l'argument '{name:s}'", "app_change_no_change_url_script": "L'aplicació {app_name:s} encara no permet canviar la seva URL, es possible que s'hagi d'actualitzar.", - "app_change_url_failed_nginx_reload": "No s'ha pogut tornar a carregar nginx. Aquí teniu el resultat de \"nginx -t\":\n{nginx_errors:s}", + "app_change_url_failed_nginx_reload": "No s'ha pogut tornar a carregar NGINX. Aquí teniu el resultat de \"nginx -t\":\n{nginx_errors:s}", "app_change_url_identical_domains": "L'antic i el nou domini/camí són idèntics ('{domain:s}{path:s}'), no hi ha res per fer.", - "app_change_url_no_script": "Aquesta aplicació '{app_name:s}' encara no permet modificar la URL. Potser s'ha d'actualitzar l'aplicació.", - "app_change_url_success": "La URL de {app:s} s'ha canviat correctament a {domain:s}{path:s}", + "app_change_url_no_script": "Aquesta aplicació '{app_name:s}' encara no permet modificar la URL. Potser s'ha d'actualitzar.", + "app_change_url_success": "La URL de {app:s} ara és {domain:s}{path:s}", "app_extraction_failed": "No s'han pogut extreure els fitxers d'instal·lació", - "app_id_invalid": "Id de l'aplicació incorrecte", + "app_id_invalid": "ID de l'aplicació incorrecte", "app_incompatible": "L'aplicació {app} no és compatible amb la teva versió de YunoHost", - "app_install_files_invalid": "Fitxers d'instal·lació invàlids", - "app_location_already_used": "L'aplicació '{app}' ja està instal·lada en aquest camí ({path})", + "app_install_files_invalid": "Aquests fitxers no es poden instal·lar", + "app_location_already_used": "L'aplicació «{app}» ja està instal·lada en ({path})", "app_make_default_location_already_used": "No es pot fer l'aplicació '{app}' per defecte en el domini {domain} ja que ja és utilitzat per una altra aplicació '{other_app}'", - "app_location_install_failed": "No s'ha pogut instal·lar l'aplicació en aquest camí ja que entra en conflicte amb l'aplicació '{other_app}' ja instal·lada a '{other_path}'", - "app_location_unavailable": "Aquesta url no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}", - "app_manifest_invalid": "Manifest d'aplicació incorrecte: {error}", + "app_location_install_failed": "No s'ha pogut instal·lar l'aplicació aquí ja que entra en conflicte amb l'aplicació «{other_app}» ja instal·lada a «{other_path}»", + "app_location_unavailable": "Aquesta URL no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}", + "app_manifest_invalid": "Hi ha algun error amb el manifest de l'aplicació: {error}", "app_no_upgrade": "No hi ha cap aplicació per actualitzar", "app_not_correctly_installed": "{app:s} sembla estar mal instal·lada", - "app_not_installed": "L'aplicació «{app:s}» no està instal·lada. Aquí hi ha la llista d'aplicacions instal·lades: {all_apps}", + "app_not_installed": "No s'ha trobat l'aplicació «{app:s}» en la llista d'aplicacions instal·lades: {all_apps}", "app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament", "app_package_need_update": "El paquet de l'aplicació {app} ha de ser actualitzat per poder seguir els canvis de YunoHost", "app_removed": "{app:s} ha estat suprimida", @@ -35,22 +35,22 @@ "app_sources_fetch_failed": "No s'han pogut carregar els fitxers font, l'URL és correcta?", "app_unknown": "Aplicació desconeguda", "app_unsupported_remote_type": "El tipus remot utilitzat per l'aplicació no està suportat", - "app_upgrade_app_name": "Actualitzant l'aplicació {app}…", + "app_upgrade_app_name": "Actualitzant {app}…", "app_upgrade_failed": "No s'ha pogut actualitzar {app:s}", "app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions", - "app_upgraded": "{app:s} ha estat actualitzada", + "app_upgraded": "S'ha actualitzat {app:s}", "appslist_corrupted_json": "No s'han pogut carregar les llistes d'aplicacions. Sembla que {filename:s} està danyat.", "appslist_could_not_migrate": "No s'ha pogut migrar la llista d'aplicacions {appslist:s}! No s'ha pogut analitzar la URL... L'antic cronjob s'ha guardat a {bkp_file:s}.", - "appslist_fetched": "S'ha descarregat la llista d'aplicacions {appslist:s} correctament", + "appslist_fetched": "S'ha actualitzat la llista d'aplicacions {appslist:s}", "appslist_migrating": "Migrant la llista d'aplicacions {appslist:s}…", "appslist_name_already_tracked": "Ja hi ha una llista d'aplicacions registrada amb el nom {name:s}.", "appslist_removed": "S'ha eliminat la llista d'aplicacions {appslist:s}", - "appslist_retrieve_bad_format": "L'arxiu obtingut per la llista d'aplicacions {appslist:s} no és vàlid", + "appslist_retrieve_bad_format": "No s'ha pogut llegir la llista d'aplicacions obtinguda {appslist:s}", "appslist_retrieve_error": "No s'ha pogut obtenir la llista d'aplicacions remota {appslist:s}: {error:s}", "appslist_unknown": "La llista d'aplicacions {appslist:s} es desconeguda.", "appslist_url_already_tracked": "Ja hi ha una llista d'aplicacions registrada amb al URL {url:s}.", "ask_current_admin_password": "Contrasenya d'administrador actual", - "ask_email": "Correu electrònic", + "ask_email": "Adreça de correu electrònic", "ask_firstname": "Nom", "ask_lastname": "Cognom", "ask_list_to_remove": "Llista per a suprimir", @@ -58,31 +58,31 @@ "ask_new_admin_password": "Nova contrasenya d'administrador", "ask_password": "Contrasenya", "ask_path": "Camí", - "backup_abstract_method": "Encara no s'ha implementat aquest mètode de copia de seguretat", + "backup_abstract_method": "Encara està per implementar aquest mètode de còpia de seguretat", "backup_action_required": "S'ha d'especificar què s'ha de guardar", "backup_app_failed": "No s'ha pogut fer la còpia de seguretat de l'aplicació \"{app:s}\"", "backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup…", "backup_applying_method_copy": "Còpia de tots els fitxers a la còpia de seguretat…", "backup_applying_method_custom": "Crida del mètode de còpia de seguretat personalitzat \"{method:s}\"…", - "backup_applying_method_tar": "Creació de l'arxiu tar de la còpia de seguretat…", - "backup_archive_app_not_found": "L'aplicació \"{app:s}\" no es troba dins l'arxiu de la còpia de seguretat", + "backup_applying_method_tar": "Creació de l'arxiu TAR de la còpia de seguretat…", + "backup_archive_app_not_found": "No s'ha pogut trobar l'aplicació «{app:s}» dins l'arxiu de la còpia de seguretat", "backup_archive_broken_link": "No s'ha pogut accedir a l'arxiu de la còpia de seguretat (enllaç invàlid cap a {path:s})", "backup_archive_mount_failed": "No s'ha pogut carregar l'arxiu de la còpia de seguretat", - "backup_archive_name_exists": "Ja hi ha una còpia de seguretat amb aquest nom", + "backup_archive_name_exists": "Ja hi ha una còpia de seguretat amb aquest nom.", "backup_archive_name_unknown": "Còpia de seguretat local \"{name:s}\" desconeguda", "backup_archive_open_failed": "No s'ha pogut obrir l'arxiu de la còpia de seguretat", - "backup_archive_system_part_not_available": "La part \"{part:s}\" del sistema no està disponible en aquesta copia de seguretat", - "backup_archive_writing_error": "No es poden afegir arxius a l'arxiu comprimit de la còpia de seguretat", + "backup_archive_system_part_not_available": "La part «{part:s}» del sistema no està disponible en aquesta copia de seguretat", + "backup_archive_writing_error": "No es poden afegir els arxius «{source:s}» (anomenats en l'arxiu «{dest:s}») a l'arxiu comprimit de la còpia de seguretat «{archive:s}»", "backup_ask_for_copying_if_needed": "Alguns fitxers no s'han pogut preparar per la còpia de seguretat utilitzant el mètode que evita malgastar espai del sistema temporalment. Per fer la còpia de seguretat, s'han d'utilitzar {size:s}MB temporalment. Hi esteu d'acord?", "backup_borg_not_implemented": "El mètode de còpia de seguretat Borg encara no està implementat", - "backup_cant_mount_uncompress_archive": "No es pot carregar en mode de lectura només el directori de l'arxiu descomprimit", + "backup_cant_mount_uncompress_archive": "No es pot carregar l'arxiu descomprimit com a protegit contra escriptura", "backup_cleaning_failed": "No s'ha pogut netejar el directori temporal de la còpia de seguretat", "backup_copying_to_organize_the_archive": "Copiant {size:s}MB per organitzar l'arxiu", "backup_couldnt_bind": "No es pot lligar {src:s} amb {dest:s}.", "backup_created": "S'ha creat la còpia de seguretat", "backup_creating_archive": "Creant l'arxiu de la còpia de seguretat…", "aborting": "Avortant.", - "app_not_upgraded": "Les següents aplicacions no s'han actualitzat: {apps}", + "app_not_upgraded": "L'aplicació «{failed_app}» no s'ha pogut actualitzar, i com a conseqüència l'actualització de les següents aplicacions ha estat cancel·lada: {apps}", "app_start_install": "instal·lant l'aplicació {app}…", "app_start_remove": "Eliminant l'aplicació {app}…", "app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per {app}…", @@ -90,24 +90,24 @@ "app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}", "ask_new_domain": "Nou domini", "ask_new_path": "Nou camí", - "backup_actually_backuping": "S'està creant un arxiu de còpia de seguretat a partir dels fitxers recuperats…", - "backup_creation_failed": "Ha fallat la creació de la còpia de seguretat", + "backup_actually_backuping": "Creant un arxiu de còpia de seguretat a partir dels fitxers recuperats…", + "backup_creation_failed": "No s'ha pogut crear l'arxiu de la còpia de seguretat", "backup_csv_addition_failed": "No s'han pogut afegir fitxers per a fer-ne la còpia de seguretat al fitxer CSV", - "backup_csv_creation_failed": "No s'ha pogut crear el fitxer CSV necessari per a futures operacions de recuperació", - "backup_custom_backup_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"backup\"", - "backup_custom_mount_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"mount\"", + "backup_csv_creation_failed": "No s'ha pogut crear el fitxer CSV necessari per a la restauració", + "backup_custom_backup_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa «backup»", + "backup_custom_mount_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa «mount»", "backup_custom_need_mount_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"need_mount\"", - "backup_delete_error": "No s'ha pogut suprimir \"{path:s}\"", + "backup_delete_error": "No s'ha pogut suprimir «{path:s}»", "backup_deleted": "S'ha suprimit la còpia de seguretat", "backup_extracting_archive": "Extraient l'arxiu de la còpia de seguretat…", - "backup_hook_unknown": "Script de còpia de seguretat \"{hook:s}\" desconegut", - "backup_invalid_archive": "Arxiu de còpia de seguretat no vàlid", - "backup_method_borg_finished": "La còpia de seguretat a borg ha acabat", + "backup_hook_unknown": "Script de còpia de seguretat «{hook:s}» desconegut", + "backup_invalid_archive": "Aquest no és un arxiu de còpia de seguretat", + "backup_method_borg_finished": "La còpia de seguretat a Borg ha acabat", "backup_method_copy_finished": "La còpia de la còpia de seguretat ha acabat", "backup_method_custom_finished": "El mètode de còpia de seguretat personalitzat \"{method:s}\" ha acabat", - "backup_method_tar_finished": "S'ha creat l'arxiu de còpia de seguretat tar", + "backup_method_tar_finished": "S'ha creat l'arxiu de còpia de seguretat TAR", "backup_mount_archive_for_restore": "Preparant l'arxiu per la restauració…", - "good_practices_about_user_password": "Esteu a punt de definir una nova contrasenya d'usuari. La contrasenya ha de tenir un mínim de 8 caràcters ; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).", + "good_practices_about_user_password": "Esteu a punt de definir una nova contrasenya d'usuari. La contrasenya ha de tenir un mínim de 8 caràcters; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).", "password_listed": "Aquesta contrasenya és una de les més utilitzades en el món. Si us plau utilitzeu-ne una més única.", "password_too_simple_1": "La contrasenya ha de tenir un mínim de 8 caràcters", "password_too_simple_2": "La contrasenya ha de tenir un mínim de 8 caràcters i ha de contenir dígits, majúscules i minúscules", @@ -115,42 +115,42 @@ "password_too_simple_4": "La contrasenya ha de tenir un mínim de 12 caràcters i tenir dígits, majúscules, minúscules i caràcters especials", "backup_no_uncompress_archive_dir": "El directori de l'arxiu descomprimit no existeix", "backup_nothings_done": "No hi ha res a guardar", - "backup_output_directory_forbidden": "Directori de sortida no permès. Les còpies de seguretat no es poden crear ni dins els directoris /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ni dins els subdirectoris /home/yunohost.backup/archives", - "backup_output_directory_not_empty": "El directori de sortida no està buit", + "backup_output_directory_forbidden": "Escolliu un directori de sortida different. Les còpies de seguretat no es poden crear ni dins els directoris /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ni dins els subdirectoris /home/yunohost.backup/archives", + "backup_output_directory_not_empty": "Heu d'escollir un directori de sortida buit", "backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat", - "backup_output_symlink_dir_broken": "Teniu un enllaç simbòlic trencat en lloc del directori dels arxius '{path:s}'. Pot ser teniu una configuració per la còpia de seguretat específica en un altre sistema de fitxers, si és el cas segurament heu oblidat muntar o connectar el disc dur o la clau USB.", - "backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar php7, la restauració de les vostres aplicacions pot fallar (raó: {error:s})", + "backup_output_symlink_dir_broken": "Teniu un enllaç simbòlic trencat en lloc del directori del arxiu «{path:s}». Pot ser teniu una configuració per la còpia de seguretat específica en un altre sistema de fitxers, si és el cas segurament heu oblidat muntar o connectar el disc dur o la clau USB.", + "backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar PHP 7, pot ser que no es puguin restaurar les vostres aplicacions PHP (raó: {error:s})", "backup_running_hooks": "Executant els scripts de la còpia de seguretat…", "backup_system_part_failed": "No s'ha pogut fer la còpia de seguretat de la part \"{part:s}\" del sistema", - "backup_unable_to_organize_files": "No s'han pogut organitzar els fitxers dins de l'arxiu amb el mètode ràpid", - "backup_with_no_backup_script_for_app": "L'aplicació {app:s} no té un script de còpia de seguretat. Serà ignorat.", - "backup_with_no_restore_script_for_app": "L'aplicació {app:s} no té un script de restauració, no podreu restaurar automàticament la còpia de seguretat d'aquesta aplicació.", - "certmanager_acme_not_configured_for_domain": "El certificat pel domini {domain:s} sembla que no està instal·lat correctament. Si us plau executeu primer cert-install per aquest domini.", - "certmanager_attempt_to_renew_nonLE_cert": "El certificat pel domini {domain:s} no ha estat emès per Let's Encrypt. No es pot renovar automàticament!", - "certmanager_attempt_to_renew_valid_cert": "El certificat pel domini {domain:s} està a punt de caducar! (Utilitzeu --force si sabeu el que esteu fent)", + "backup_unable_to_organize_files": "No s'ha pogut utilitzar el mètode ràpid per organitzar els fitxers dins de l'arxiu", + "backup_with_no_backup_script_for_app": "L'aplicació «{app:s}» no té un script de còpia de seguretat. Serà ignorat.", + "backup_with_no_restore_script_for_app": "L'aplicació «{app:s}» no té un script de restauració, no podreu restaurar automàticament la còpia de seguretat d'aquesta aplicació.", + "certmanager_acme_not_configured_for_domain": "El certificat pel domini «{domain:s}» sembla que no està instal·lat correctament. Si us plau executeu primer «cert-install» per aquest domini.", + "certmanager_attempt_to_renew_nonLE_cert": "El certificat pel domini «{domain:s}» no ha estat emès per Let's Encrypt. No es pot renovar automàticament!", + "certmanager_attempt_to_renew_valid_cert": "El certificat pel domini «{domain:s}» està a punt de caducar! (Utilitzeu --force si sabeu el que esteu fent)", "certmanager_attempt_to_replace_valid_cert": "Esteu intentant sobreescriure un certificat correcte i vàlid pel domini {domain:s}! (Utilitzeu --force per ometre)", "certmanager_cannot_read_cert": "S'ha produït un error al intentar obrir el certificat actual pel domini {domain:s} (arxiu: {file:s}), raó: {reason:s}", - "certmanager_cert_install_success": "S'ha instal·lat correctament un certificat Let's Encrypt pel domini {domain:s}!", - "certmanager_cert_install_success_selfsigned": "S'ha instal·lat correctament un certificat auto-signat pel domini {domain:s}!", - "certmanager_cert_renew_success": "S'ha renovat correctament el certificat Let's Encrypt pel domini {domain:s}!", + "certmanager_cert_install_success": "S'ha instal·lat correctament un certificat Let's Encrypt pel domini «{domain:s}»", + "certmanager_cert_install_success_selfsigned": "S'ha instal·lat correctament un certificat auto-signat pel domini «{domain:s}»", + "certmanager_cert_renew_success": "S'ha renovat correctament el certificat Let's Encrypt pel domini «{domain:s}»", "certmanager_cert_signing_failed": "No s'ha pogut firmar el nou certificat", - "certmanager_certificate_fetching_or_enabling_failed": "Sembla que l'activació del nou certificat per {domain:s} ha fallat…", - "certmanager_conflicting_nginx_file": "No s'ha pogut preparar el domini per al desafiament ACME: l'arxiu de configuració nginx {filepath:s} entra en conflicte i s'ha d'eliminar primer", + "certmanager_certificate_fetching_or_enabling_failed": "Sembla que utilitzar el nou certificat per {domain:s} ha fallat…", + "certmanager_conflicting_nginx_file": "No s'ha pogut preparar el domini per al desafiament ACME: l'arxiu de configuració NGINX {filepath:s} entra en conflicte i s'ha d'eliminar primer", "certmanager_couldnt_fetch_intermediate_cert": "S'ha exhaurit el temps d'esperar al intentar recollir el certificat intermedi des de Let's Encrypt. La instal·lació/renovació del certificat s'ha cancel·lat - torneu a intentar-ho més tard.", - "certmanager_domain_cert_not_selfsigned": "El certificat pel domini {domain:s} no és auto-signat Esteu segur de voler canviar-lo? (Utilitzeu --force per fer-ho)", - "certmanager_domain_dns_ip_differs_from_public_ip": "El registre DNS \"A\" pel domini {domain:s} és diferent a l'adreça IP d'aquest servidor. Si heu modificat recentment el registre A, si us plau espereu a que es propagui (hi ha eines per verificar la propagació disponibles a internet). (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)", - "certmanager_domain_http_not_working": "Sembla que el domini {domain:s} no és accessible via HTTP. Si us plau verifiqueu que les configuracions DNS i nginx siguin correctes", + "certmanager_domain_cert_not_selfsigned": "El certificat pel domini {domain:s} no és auto-signat Esteu segur de voler canviar-lo? (Utilitzeu «--force» per fer-ho)", + "certmanager_domain_dns_ip_differs_from_public_ip": "El registre DNS \"A\" pel domini «{domain:s}» és diferent a l'adreça IP d'aquest servidor. Si heu modificat recentment el registre A, si us plau espereu a que es propagui (hi ha eines per verificar la propagació disponibles a internet). (Si sabeu el que esteu fent, podeu utilitzar «--no-checks» per desactivar aquestes comprovacions.)", + "certmanager_domain_http_not_working": "Sembla que el domini {domain:s} no és accessible via HTTP. Verifiqueu que les configuracions DNS i NGINX siguin correctes", "certmanager_domain_not_resolved_locally": "El domini {domain:s} no es pot resoldre dins del vostre servidor YunoHost. Això pot passar si heu modificat recentment el registre DNS. Si és així, si us plau espereu unes hores per a que es propagui. Si el problema continua, considereu afegir {domain:s} a /etc/hosts. (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)", - "certmanager_domain_unknown": "Domini desconegut {domain:s}", - "certmanager_error_no_A_record": "No s'ha trobat cap registre DNS \"A\" per {domain:s}. Heu de fer que el vostre nom de domini apunti cap a la vostra màquina per tal de poder instal·lar un certificat Let's Encrypt! (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)", + "certmanager_domain_unknown": "Domini desconegut «{domain:s}»", + "certmanager_error_no_A_record": "No s'ha trobat cap registre DNS «A» per «{domain:s}». Heu de fer que el vostre nom de domini apunti cap a la vostra màquina per tal de poder instal·lar un certificat Let's Encrypt. (Si sabeu el que esteu fent, podeu utilitzar «--no-checks» per desactivar aquestes comprovacions.)", "certmanager_hit_rate_limit": "S'han emès massa certificats recentment per aquest mateix conjunt de dominis {domain:s}. Si us plau torneu-ho a intentar més tard. Consulteu https://letsencrypt.org/docs/rate-limits/ per obtenir més detalls", - "certmanager_http_check_timeout": "S'ha exhaurit el temps d'espera quan el servidor ha intentat contactar amb ell mateix via HTTP utilitzant la seva adreça IP pública (domini domain:s} amb IP {ip:s}). Pot ser degut a hairpinning o a que el talla focs/router al que està connectat el servidor estan mal configurats.", + "certmanager_http_check_timeout": "S'ha exhaurit el temps d'espera quan el servidor ha intentat contactar amb ell mateix via HTTP utilitzant la seva adreça IP pública (domini «{domain:s}» amb IP «{ip:s}»). Pot ser degut a hairpinning o a que el talla focs/router al que està connectat el servidor estan mal configurats.", "certmanager_no_cert_file": "No s'ha pogut llegir l'arxiu del certificat pel domini {domain:s} (fitxer: {file:s})", "certmanager_self_ca_conf_file_not_found": "No s'ha trobat el fitxer de configuració per l'autoritat del certificat auto-signat (fitxer: {file:s})", "certmanager_unable_to_parse_self_CA_name": "No s'ha pogut analitzar el nom de l'autoritat del certificat auto-signat (fitxer: {file:s})", - "confirm_app_install_warning": "Atenció: aquesta aplicació funciona però no està ben integrada amb YunoHost. Algunes característiques com la autenticació única i la còpia de seguretat/restauració poden no estar disponibles. Voleu instal·lar-la de totes maneres? [{answers:s}] ", - "confirm_app_install_danger": "ATENCIÓ! Aquesta aplicació encara és experimental (si no és que no funciona directament) i és probable que trenqui el sistema! No hauríeu d'instal·lar-la a no ser que sapigueu el que feu. Esteu segurs de voler córrer aquest risc? [{answers:s}] ", - "confirm_app_install_thirdparty": "ATENCIÓ! La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. Faci-ho sota la seva responsabilitat.No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. Esteu segurs de voler córrer aquest risc? [{answers:s}] ", + "confirm_app_install_warning": "Atenció: Aquesta aplicació funciona, però no està ben integrada amb YunoHost. Algunes característiques com la autenticació única i la còpia de seguretat/restauració poden no estar disponibles. Voleu instal·lar-la de totes maneres? [{answers:s}] ", + "confirm_app_install_danger": "PERILL! Aquesta aplicació encara és experimental (si no és que no funciona directament)! No hauríeu d'instal·lar-la a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema... Si accepteu el risc, escriviu «{answers:s}»", + "confirm_app_install_thirdparty": "PERILL! Aquesta aplicació no es part del catàleg d'aplicacions de YunoHost. La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema… Si accepteu el risc, escriviu «{answers:s}»", "custom_app_url_required": "Heu de especificar una URL per actualitzar la vostra aplicació personalitzada {app:s}", "custom_appslist_name_required": "Heu d'especificar un nom per la vostra llista d'aplicacions personalitzada", "diagnosis_debian_version_error": "No s'ha pogut obtenir la versió Debian: {error}", @@ -160,22 +160,22 @@ "diagnosis_monitor_system_error": "No es pot monitorar el sistema: {error}", "diagnosis_no_apps": "No hi ha cap aplicació instal·lada", "admin_password_too_long": "Trieu una contrasenya de menys de 127 caràcters", - "dpkg_is_broken": "No es pot fer això en aquest instant perquè dpkg/apt (els gestors de paquets del sistema) sembla estar mal configurat... Podeu intentar solucionar-ho connectant-vos per ssh i executant \"sudo dpkg --configure -a\".", + "dpkg_is_broken": "No es pot fer això en aquest instant perquè dpkg/APT (els gestors de paquets del sistema) sembla estar mal configurat… Podeu intentar solucionar-ho connectant-vos per SSH i executant «sudo dpkg --configure -a».", "dnsmasq_isnt_installed": "sembla que dnsmasq no està instal·lat, executeu \"apt-get remove bind9 && apt-get install dnsmasq\"", "domain_cannot_remove_main": "No es pot eliminar el domini principal. S'ha d'establir un nou domini primer", "domain_cert_gen_failed": "No s'ha pogut generar el certificat", "domain_created": "S'ha creat el domini", - "domain_creation_failed": "No s'ha pogut crear el domini", + "domain_creation_failed": "No s'ha pogut crear el domini {domain}: {error}", "domain_deleted": "S'ha eliminat el domini", - "domain_deletion_failed": "No s'ha pogut eliminar el domini", + "domain_deletion_failed": "No s'ha pogut eliminar el domini {domini}: {error}", "domain_exists": "El domini ja existeix", - "app_action_cannot_be_ran_because_required_services_down": "Aquesta aplicació necessita serveis que estan aturats. Abans de continuar, hauríeu d'intentar arrancar de nou els serveis següents (i també investigar perquè estan aturats) : {services}", + "app_action_cannot_be_ran_because_required_services_down": "Aquesta aplicació necessita serveis que estan aturats. Abans de continuar, hauríeu d'intentar arrancar de nou els serveis següents (i també investigar perquè estan aturats): {services}", "domain_dns_conf_is_just_a_recommendation": "Aquesta ordre mostra la configuració *recomanada*. En cap cas fa la configuració del DNS. És la vostra responsabilitat configurar la zona DNS en el vostre registrar en acord amb aquesta recomanació.", "domain_dyndns_already_subscribed": "Ja us heu subscrit a un domini DynDNS", "domain_dyndns_dynette_is_unreachable": "No s'ha pogut abastar la dynette YunoHost, o bé YunoHost no està connectat a internet correctament o bé el servidor dynette està caigut. Error: {error}", "domain_dyndns_invalid": "Domini no vàlid per utilitzar amb DynDNS", "domain_dyndns_root_unknown": "Domini DynDNS principal desconegut", - "domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió. Això podria causar problemes més tard (no és segur ... podria no passar res).", + "domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió. Això podria causar problemes més tard (podria no passar res).", "domain_uninstall_app_first": "Hi ha una o més aplicacions instal·lades en aquest domini. Desinstal·leu les abans d'eliminar el domini", "domain_unknown": "Domini desconegut", "domain_zone_exists": "El fitxer de zona DNS ja existeix", @@ -187,61 +187,61 @@ "dyndns_could_not_check_available": "No s'ha pogut verificar la disponibilitat de {domain:s} a {provider:s}.", "dyndns_ip_update_failed": "No s'ha pogut actualitzar l'adreça IP al DynDNS", "dyndns_ip_updated": "S'ha actualitzat l'adreça IP al DynDNS", - "dyndns_key_generating": "S'està generant la clau DNS, això pot trigar una estona…", + "dyndns_key_generating": "S'està generant la clau DNS… això pot trigar una estona.", "dyndns_key_not_found": "No s'ha trobat la clau DNS pel domini", "dyndns_no_domain_registered": "No hi ha cap domini registrat amb DynDNS", "dyndns_registered": "S'ha registrat el domini DynDNS", "dyndns_registration_failed": "No s'ha pogut registrar el domini DynDNS: {error:s}", - "dyndns_domain_not_provided": "El proveïdor {provider:s} no pot oferir el domini {domain:s}.", + "dyndns_domain_not_provided": "El proveïdor de DynDNS {provider:s} no pot oferir el domini {domain:s}.", "dyndns_unavailable": "El domini {domain:s} no està disponible.", "executing_command": "Execució de l'ordre « {command:s} »…", "executing_script": "Execució de l'script « {script:s} »…", "extracting": "Extracció en curs…", - "dyndns_cron_installed": "S'ha instal·lat la tasca cron pel DynDNS", + "dyndns_cron_installed": "S'ha creat la tasca cron pel DynDNS", "dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron per a DynDNS: {error}", "dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS", - "experimental_feature": "Atenció: aquesta funcionalitat és experimental i no es considera estable, no s'ha d'utilitzar a excepció de saber el que esteu fent.", + "experimental_feature": "Atenció: Aquesta funcionalitat és experimental i no es considera estable, no s'ha d'utilitzar a excepció de saber el que esteu fent.", "field_invalid": "Camp incorrecte « {:s} »", "file_does_not_exist": "El camí {path:s} no existeix.", - "firewall_reload_failed": "No s'ha pogut tornar a carregar el tallafoc", - "firewall_reloaded": "S'ha tornat a carregar el tallafoc", - "firewall_rules_cmd_failed": "No s'han pogut aplicar algunes regles del tallafoc. Mireu el registre per a més informació.", + "firewall_reload_failed": "No s'ha pogut tornar a carregar el tallafocs", + "firewall_reloaded": "S'ha tornat a carregar el tallafocs", + "firewall_rules_cmd_failed": "No s'han pogut aplicar algunes regles del tallafocs. Més informació en el registre.", "format_datetime_short": "%d/%m/%Y %H:%M", - "global_settings_bad_choice_for_enum": "Opció pel paràmetre {setting:s} incorrecta, s'ha rebut «{choice:s}» però les opcions disponibles són: {available_choices:s}", + "global_settings_bad_choice_for_enum": "Opció pel paràmetre {setting:s} incorrecta, s'ha rebut «{choice:s}», però les opcions disponibles són: {available_choices:s}", "global_settings_bad_type_for_setting": "El tipus del paràmetre {setting:s} és incorrecte. S'ha rebut {received_type:s}, però s'esperava {expected_type:s}", "global_settings_cant_open_settings": "No s'ha pogut obrir el fitxer de configuració, raó: {reason:s}", "global_settings_cant_serialize_settings": "No s'ha pogut serialitzar les dades de configuració, raó: {reason:s}", "global_settings_cant_write_settings": "No s'ha pogut escriure el fitxer de configuració, raó: {reason:s}", "global_settings_key_doesnt_exists": "La clau « {settings_key:s} » no existeix en la configuració global, podeu veure totes les claus disponibles executant « yunohost settings list »", - "global_settings_reset_success": "Èxit. S'ha fet una còpia de seguretat de la configuració anterior a {path:s}", + "global_settings_reset_success": "S'ha fet una còpia de seguretat de la configuració anterior a {path:s}", "global_settings_setting_example_bool": "Exemple d'opció booleana", "global_settings_setting_example_enum": "Exemple d'opció de tipus enumeració", "global_settings_setting_example_int": "Exemple d'opció de tipus enter", "global_settings_setting_example_string": "Exemple d'opció de tipus cadena", - "global_settings_setting_security_nginx_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor web nginx. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", + "global_settings_setting_security_nginx_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor web NGINX. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", "global_settings_setting_security_password_admin_strength": "Robustesa de la contrasenya d'administrador", "global_settings_setting_security_password_user_strength": "Robustesa de la contrasenya de l'usuari", "global_settings_setting_security_ssh_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor SSH. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", - "global_settings_unknown_setting_from_settings_file": "Clau de configuració desconeguda: «{setting_key:s}», refusant-la i guardant-la a /etc/yunohost/settings-unknown.json", + "global_settings_unknown_setting_from_settings_file": "Clau de configuració desconeguda: «{setting_key:s}», refusada i guardada a /etc/yunohost/settings-unknown.json", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Permetre la clau d'hoste DSA (obsolet) per la configuració del servei SSH", "global_settings_unknown_type": "Situació inesperada, la configuració {setting:s} sembla tenir el tipus {unknown_type:s} però no és un tipus reconegut pel sistema.", - "good_practices_about_admin_password": "Esteu a punt de definir una nova contrasenya d'administrador. La contrasenya ha de tenir un mínim de 8 caràcters ; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).", - "hook_exec_failed": "No s'ha pogut executar l'script: {path:s}", - "hook_exec_not_terminated": "L'execució de l'script « {path:s} » no s'ha acabat correctament", - "hook_json_return_error": "No s'ha pogut llegir el retorn de l'script {path:s}. Error: {msg:s}. Contingut en brut: {raw_content}", - "hook_list_by_invalid": "Propietat per llistar les accions invàlida", + "good_practices_about_admin_password": "Esteu a punt de definir una nova contrasenya d'administrador. La contrasenya ha de tenir un mínim de 8 caràcters; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).", + "hook_exec_failed": "No s'ha pogut executar el script: {path:s}", + "hook_exec_not_terminated": "El script no s'ha acabat correctament: {path:s}", + "hook_json_return_error": "No s'ha pogut llegir el retorn del script {path:s}. Error: {msg:s}. Contingut en brut: {raw_content}", + "hook_list_by_invalid": "Aquesta propietat no es pot utilitzar per llistar els hooks", "hook_name_unknown": "Nom de script « {name:s} » desconegut", "installation_complete": "Instal·lació completada", - "installation_failed": "Ha fallat la instal·lació", + "installation_failed": "Ha fallat alguna cosa amb la instal·lació", "invalid_url_format": "Format d'URL invàlid", "ip6tables_unavailable": "No podeu modificar les ip6tables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", "iptables_unavailable": "No podeu modificar les iptables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", - "log_corrupted_md_file": "El fitxer de metadades yaml associat amb els registres està malmès: « {md_file} »\nError: {error}", + "log_corrupted_md_file": "El fitxer de metadades YAML associat amb els registres està malmès: « {md_file} »\nError: {error}", "log_category_404": "La categoria de registres « {category} » no existeix", "log_link_to_log": "El registre complet d'aquesta operació: «{desc}»", "log_help_to_get_log": "Per veure el registre de l'operació « {desc} », utilitzeu l'ordre « yunohost log display {name} »", - "log_link_to_failed_log": "L'operació « {dec} » ha fallat! Per obtenir ajuda, proveïu el registre complete de l'operació clicant aquí", - "log_help_to_get_failed_log": "L'operació « {dec} » ha fallat! Per obtenir ajuda, compartiu el registre complete de l'operació utilitzant l'ordre « yunohost log display {name} --share »", + "log_link_to_failed_log": "No s'ha pogut completar l'operació « {desc} ». Per obtenir ajuda, proveïu el registre complete de l'operació clicant aquí", + "log_help_to_get_failed_log": "No s'ha pogut completar l'operació « {desc} ». Per obtenir ajuda, compartiu el registre complete de l'operació utilitzant l'ordre « yunohost log display {name} --share »", "log_does_exists": "No hi ha cap registre per l'operació amb el nom« {log} », utilitzeu « yunohost log list » per veure tots els registre d'operació disponibles", "log_operation_unit_unclosed_properly": "L'operació no s'ha tancat de forma correcta", "log_app_addaccess": "Afegir accés a « {} »", @@ -263,7 +263,7 @@ "log_domain_remove": "Elimina el domini « {} » de la configuració del sistema", "log_dyndns_subscribe": "Subscriure's a un subdomini YunoHost « {} »", "log_dyndns_update": "Actualitza la IP associada al subdomini YunoHost « {} »", - "log_letsencrypt_cert_install": "Instal·la el certificat Let's Encrypt al domini « {} »", + "log_letsencrypt_cert_install": "Instal·la un certificat Let's Encrypt al domini « {} »", "log_selfsigned_cert_install": "Instal·la el certificat autosignat al domini « {} »", "log_letsencrypt_cert_renew": "Renova el certificat Let's Encrypt de « {} »", "log_service_enable": "Activa el servei « {} »", @@ -278,79 +278,79 @@ "log_tools_upgrade": "Actualitza els paquets del sistema", "log_tools_shutdown": "Apaga el servidor", "log_tools_reboot": "Reinicia el servidor", - "already_up_to_date": "No hi ha res a fer! Tot està al dia!", + "already_up_to_date": "No hi ha res a fer. Tot està actualitzat.", "dpkg_lock_not_available": "No es pot utilitzar aquesta comanda en aquest moment ja que sembla que un altre programa està utilitzant el lock de dpkg (el gestor de paquets del sistema)", "global_settings_setting_security_postfix_compatibility": "Solució de compromís entre compatibilitat i seguretat pel servidor Postfix. Afecta els criptògrafs (i altres aspectes relacionats amb la seguretat)", "ldap_init_failed_to_create_admin": "La inicialització de LDAP no ha pogut crear l'usuari admin", "ldap_initialized": "S'ha iniciat LDAP", "license_undefined": "indefinit", - "mail_alias_remove_failed": "No s'han pogut eliminar els alias del correu «{mail:s}»", - "mail_domain_unknown": "Domini d'adreça de correu «{domain:s}» desconegut", + "mail_alias_remove_failed": "No s'han pogut eliminar els àlies del correu «{mail:s}»", + "mail_domain_unknown": "Domini d'adreça de correu per «{domain:s}» desconegut", "mail_forward_remove_failed": "No s'han pogut eliminar el reenviament de correu «{mail:s}»", - "mailbox_used_space_dovecot_down": "S'ha d'engegar el servei de correu Dovecot per poder obtenir l'espai utilitzat per la bústia de correu", - "mail_unavailable": "Aquesta adreça de correu esta reservada i ha de ser atribuïda automàticament el primer usuari", + "mailbox_used_space_dovecot_down": "S'ha d'engegar el servei de correu Dovecot, per poder obtenir l'espai utilitzat per la bústia de correu", + "mail_unavailable": "Aquesta adreça de correu està reservada i ha de ser atribuïda automàticament el primer usuari", "maindomain_change_failed": "No s'ha pogut canviar el domini principal", "maindomain_changed": "S'ha canviat el domini principal", - "migrate_tsig_end": "La migració cap a hmac-sha512 s'ha acabat", - "migrate_tsig_failed": "Ha fallat la migració del domini dyndns {domain} cap a hmac-sha512, anul·lant les modificacions. Error: {error_code} - {error}", - "migrate_tsig_start": "L'algoritme de generació de claus no es prou segur per a la signatura TSIG del domini «{domain}», començant la migració cap a un de més segur hmac-sha512", - "migrate_tsig_wait": "Esperar 3 minuts per a que el servidor dyndns tingui en compte la nova clau…", + "migrate_tsig_end": "La migració cap a HMAC-SHA-512 s'ha acabat", + "migrate_tsig_failed": "Ha fallat la migració del domini DynDNS «{domain}» cap a HMAC-SHA-512, anul·lant les modificacions. Error: {error_code}, {error}", + "migrate_tsig_start": "L'algoritme de generació de claus no es prou segur per a la signatura TSIG del domini «{domain}», començant la migració cap a un de més segur HMAC-SHA-512", + "migrate_tsig_wait": "Esperant tres minuts per a que el servidor DynDNS tingui en compte la nova clau…", "migrate_tsig_wait_2": "2 minuts…", "migrate_tsig_wait_3": "1 minut…", "migrate_tsig_wait_4": "30 segons…", - "migrate_tsig_not_needed": "Sembla que no s'utilitza cap domini dyndns, no és necessari fer cap migració!", + "migrate_tsig_not_needed": "Sembla que no s'utilitza cap domini DynDNS, no és necessari fer cap migració.", "migration_description_0001_change_cert_group_to_sslcert": "Canvia els permisos del grup dels certificats de «metronome» a «ssl-cert»", - "migration_description_0002_migrate_to_tsig_sha256": "Millora la seguretat de dyndns TSIG utilitzant SHA512 en lloc de MD5", + "migration_description_0002_migrate_to_tsig_sha256": "Millora la seguretat de DynDNS TSIG utilitzant SHA-512 en lloc de MD5", "migration_description_0003_migrate_to_stretch": "Actualització del sistema a Debian Stretch i YunoHost 3.0", "migration_description_0004_php5_to_php7_pools": "Tornar a configurar els pools PHP per utilitzar PHP 7 en lloc de PHP 5", - "migration_description_0005_postgresql_9p4_to_9p6": "Migració de les bases de dades de postgresql 9.4 a 9.6", + "migration_description_0005_postgresql_9p4_to_9p6": "Migració de les bases de dades de PostgreSQL 9.4 a 9.6", "migration_description_0006_sync_admin_and_root_passwords": "Sincronitzar les contrasenyes admin i root", "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "La configuració SSH serà gestionada per YunoHost (pas 1, automàtic)", "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "La configuració SSH serà gestionada per YunoHost (pas 2, manual)", "migration_description_0009_decouple_regenconf_from_services": "Desvincula el mecanisme regen-conf dels serveis", - "migration_description_0010_migrate_to_apps_json": "Elimina la appslists (desfasat) i utilitza la nova llista unificada «apps.json» en el seu lloc", + "migration_description_0010_migrate_to_apps_json": "Elimina les llistes d'aplicacions obsoletes i utilitza la nova llista unificada «apps.json» en el seu lloc", "migration_0003_backward_impossible": "La migració Stretch no és reversible.", "migration_0003_start": "Ha començat la migració a Stretch. Els registres estaran disponibles a {logfile}.", "migration_0003_patching_sources_list": "Modificant el fitxer sources.lists…", "migration_0003_main_upgrade": "Començant l'actualització principal…", - "migration_0003_fail2ban_upgrade": "Començant l'actualització de fail2ban…", + "migration_0003_fail2ban_upgrade": "Començant l'actualització de Fail2Ban…", "migration_0003_restoring_origin_nginx_conf": "El fitxer /etc/nginx/nginx.conf ha estat editat. La migració el tornarà al seu estat original... El fitxer anterior estarà disponible com a {backup_dest}.", - "migration_0003_yunohost_upgrade": "Començant l'actualització del paquet yunohost... La migració acabarà, però l'actualització actual es farà just després. Després de completar aquesta operació, pot ser que us hagueu de tornar a connectar a la web d'administració.", + "migration_0003_yunohost_upgrade": "Començant l'actualització del paquet YunoHost... La migració acabarà, però l'actualització actual es farà just després. Després de completar aquesta operació, pot ser que us hagueu de tornar a connectar a la web d'administració.", "migration_0003_not_jessie": "La distribució Debian actual no és Jessie!", "migration_0003_system_not_fully_up_to_date": "El vostre sistema no està completament actualitzat. S'ha de fer una actualització normal abans de fer la migració a Stretch.", - "migration_0003_still_on_jessie_after_main_upgrade": "Hi ha hagut un problema durant l'actualització principal: el sistema encara està amb Jessie!? Per investigar el problema, mireu el registres a {log}:s…", - "migration_0003_general_warning": "Tingueu en compte que la migració és una operació delicada. Tot i que l'equip de YunoHost a fet els possibles per revisar-la i provar-la, la migració pot provocar errors en parts del sistema o aplicacions.\n\nPer tant, recomanem:\n - Fer una còpia de seguretat de les dades o aplicacions importants. Més informació a https://yunohost.org/backup;\n - Sigueu pacient un cop llençada la migració: en funció de la connexió a internet i el maquinari, pot trigar fins a unes hores per actualitzar-ho tot.\n\nD'altra banda, el port per SMTP, utilitzat per clients de correu externs (com Thunderbird o K9-Mail) ha canviat de 465 (SSL/TLS) a 587 (STARTTLS). L'antic port 465 serà tancat automàticament i el nou port 587 serà obert en el tallafocs. Tots els usuaris *hauran* d'adaptar la configuració dels clients de correu en acord amb aquests canvis!", - "migration_0003_problematic_apps_warning": "Tingueu en compte que s'han detectat les aplicacions, possiblement, problemàtiques següents. Sembla que aquestes no s'han instal·lat des d'una applist o que no estan marcades com a «working». Per conseqüent, no podem garantir que segueixin funcionant després de l'actualització: {problematic_apps}", + "migration_0003_still_on_jessie_after_main_upgrade": "Hi ha hagut un problema durant l'actualització principal: El sistema encara està amb Jessie? Per investigar el problema, mireu el registres a {log}:s…", + "migration_0003_general_warning": "Tingueu en compte que la migració és una operació delicada. L'equip de YunoHost a fet els possibles per revisar-la i provar-la, però la migració pot provocar errors en parts del sistema o aplicacions.\n\nPer tant, es recomana:\n - Fer una còpia de seguretat de les dades o aplicacions importants. Més informació a https://yunohost.org/backup;\n - Sigueu pacient un cop llençada la migració: en funció de la connexió a internet i el maquinari, pot trigar fins a unes hores per actualitzar-ho tot.\n\nD'altra banda, el port per SMTP, utilitzat per clients de correu externs (com Thunderbird o K9-Mail) ha canviat de 465 (SSL/TLS) a 587 (STARTTLS). L'antic port (465) serà tancat automàticament, i el nou port (587) serà obert en el tallafocs. Tots els usuaris *hauran* d'adaptar la configuració dels clients de correu en acord amb aquests canvis.", + "migration_0003_problematic_apps_warning": "Tingueu en compte que s'han detectat les aplicacions, possiblement, problemàtiques següents. Sembla que aquestes no s'han instal·lat des d'una applist, o que no estan marcades com a «working». Per conseqüent, no podem garantir que segueixin funcionant després de l'actualització: {problematic_apps}", "migration_0003_modified_files": "Tingueu en compte que s'han detectat els següents fitxers que han estat modificats manualment i podrien sobreescriure's al final de l'actualització: {manually_modified_files}", - "migration_0005_postgresql_94_not_installed": "Postgresql no està instal·lat en el sistema. No hi ha res per fer!", - "migration_0005_postgresql_96_not_installed": "S'ha trobat Postgresql 9.4 instal·lat, però no Postgresql 9.6!? Alguna cosa estranya a passat en el sistema :( …", - "migration_0005_not_enough_space": "No hi ha prou espai disponible en {path} per fer la migració en aquest moment :(.", + "migration_0005_postgresql_94_not_installed": "PostgreSQL no està instal·lat en el sistema. No hi ha res per fer.", + "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 està instal·lat, però no PostgreSQL 9.6? Alguna cosa estranya a passat en el sistema :( …", + "migration_0005_not_enough_space": "Creu espai disponible en {path} per executar la migració.", "migration_0006_disclaimer": "YunoHost esperar que les contrasenyes admin i root estiguin sincronitzades. Fent aquesta migració, la contrasenya root serà reemplaçada per la contrasenya admin.", "migration_0007_cancelled": "YunoHost no ha pogut millorar la gestió de la configuració SSH.", "migration_0007_cannot_restart": "No es pot reiniciar SSH després d'haver intentat cancel·lar la migració numero 6.", "migration_0008_general_disclaimer": "Per millorar la seguretat del servidor, es recomana que sigui YunoHost qui gestioni la configuració SSH. La configuració SSH actual és diferent a la configuració recomanada. Si deixeu que YunoHost ho reconfiguri, la manera de connectar-se al servidor mitjançant SSH canviarà de la següent manera:", - "migration_0008_port": " - la connexió es farà utilitzant el port 22 en lloc del port SSH personalitzat actual. Es pot reconfigurar;", - "migration_0008_root": " - no es podrà connectar com a root a través de SSH. S'haurà d'utilitzar l'usuari admin per fer-ho;", - "migration_0008_dsa": " - es desactivarà la clau DSA. Per tant, es podria haver d'invalidar un missatge esgarrifós del client SSH, i tornar a verificar l'empremta digital del servidor;", + "migration_0008_port": "• La connexió es farà utilitzant el port 22 en lloc del port SSH personalitzat actual. Es pot reconfigurar;", + "migration_0008_root": "• No es podrà connectar com a root a través de SSH. S'haurà d'utilitzar l'usuari admin per fer-ho;", + "migration_0008_dsa": "• Es desactivarà la clau DSA. Per tant, es podria haver d'invalidar un missatge esgarrifós del client SSH, i tornar a verificar l'empremta digital del servidor;", "migration_0008_warning": "Si heu entès els avisos i accepteu que YunoHost sobreescrigui la configuració actual, comenceu la migració. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", - "migration_0008_no_warning": "No s'han detectat riscs importants per sobreescriure la configuració SSH, però no es pot estar del tot segur ;)! Si accepteu que YunoHost sobreescrigui la configuració actual, comenceu la migració. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", - "migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració? Ometent.", + "migration_0008_no_warning": "No s'han identificat riscs importants per sobreescriure la configuració SSH, però no es pot estar del tot segur ;)! Executetu la migració per sobreescriure-la. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", + "migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració… (?) Ometent.", "migrations_backward": "Migració cap enrere.", "migrations_bad_value_for_target": "Nombre invàlid pel paràmetre target, els nombres de migració disponibles són 0 o {}", "migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí %s", "migrations_current_target": "La migració objectiu és {}", "migrations_error_failed_to_load_migration": "ERROR: no s'ha pogut carregar la migració {number} {name}", "migrations_forward": "Migració endavant", - "migrations_list_conflict_pending_done": "No es pot utilitzar --previous i --done al mateix temps.", - "migrations_loading_migration": "Carregant la migració {number} {name}…", - "migrations_migration_has_failed": "La migració {number} {name} ha fallat amb l'excepció {exception}, cancel·lant", + "migrations_list_conflict_pending_done": "No es pot utilitzar «--previous» i «--done» al mateix temps.", + "migrations_loading_migration": "Carregant la migració {id}…", + "migrations_migration_has_failed": "La migració {id} ha fallat, cancel·lant. Error: {exception}", "migrations_no_migrations_to_run": "No hi ha cap migració a fer", "migrations_show_currently_running_migration": "Fent la migració {number} {name}…", "migrations_show_last_migration": "L'última migració feta és {}", - "migrations_skip_migration": "Saltant migració {number} {name}…", + "migrations_skip_migration": "Saltant migració {id}…", "migrations_success": "S'ha completat la migració {number} {name} amb èxit!", - "migrations_to_be_ran_manually": "La migració {number} {name} s'ha de fer manualment. Aneu a Eines > Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».", - "migrations_need_to_accept_disclaimer": "Per fer la migració {number} {name}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció --accept-disclaimer.", + "migrations_to_be_ran_manually": "La migració {id} s'ha de fer manualment. Aneu a Eines → Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».", + "migrations_need_to_accept_disclaimer": "Per fer la migració {id}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció «--accept-disclaimer».", "monitor_disabled": "El monitoratge del servidor ha estat desactivat", "monitor_enabled": "El monitoratge del servidor ha estat activat", "monitor_glances_con_failed": "No s'ha pogut connectar al servidor Glances", @@ -384,18 +384,18 @@ "pattern_firstname": "Ha de ser un nom vàlid", "pattern_lastname": "Ha de ser un cognom vàlid", "pattern_listname": "Ha d'estar compost per caràcters alfanumèrics i guió baix exclusivament", - "pattern_mailbox_quota": "Ha de ser una mida amb el sufix b/k/M/G/T o 0 per desactivar la quota", + "pattern_mailbox_quota": "Ha de ser una mida amb el sufix b/k/M/G/T o 0 per no tenir quota", "pattern_password": "Ha de tenir un mínim de 3 caràcters", "pattern_port": "Ha de ser un número de port vàlid (i.e. 0-65535)", "pattern_port_or_range": "Ha de ser un número de port vàlid (i.e. 0-65535) o un interval de ports (ex. 100:200)", "pattern_positive_number": "Ha de ser un nombre positiu", "pattern_username": "Ha d'estar compost per caràcters alfanumèrics en minúscula i guió baix exclusivament", - "pattern_password_app": "Les contrasenyes no haurien de tenir els següents caràcters: {forbidden_chars}", + "pattern_password_app": "Les contrasenyes no poden de tenir els següents caràcters: {forbidden_chars}", "port_already_closed": "El port {port:d} ja està tancat per les connexions {ip_version:s}", "port_already_opened": "El port {port:d} ja està obert per les connexions {ip_version:s}", "port_available": "El port {port:d} està disponible", "port_unavailable": "El port {port:d} no està disponible", - "recommend_to_add_first_user": "La post instal·lació s'ha acabat, però YunoHost necessita com a mínim un usuari per funcionar correctament, hauríeu d'afegir un usuari executant «yunohost user create $username» o amb la interfície d'administració.", + "recommend_to_add_first_user": "La post instal·lació s'ha acabat, però YunoHost necessita com a mínim un usuari per funcionar correctament, hauríeu d'afegir un usuari executant «yunohost user create »; o fer-ho des de la interfície d'administració.", "regenconf_file_backed_up": "S'ha guardat una còpia de seguretat del fitxer de configuració «{conf}» a «{backup}»", "regenconf_file_copy_failed": "No s'ha pogut copiar el nou fitxer de configuració «{new}» a «{conf}»", "regenconf_file_kept_back": "S'espera que el fitxer de configuració «{conf}» sigui suprimit per regen-conf (categoria {category}) però s'ha mantingut.", @@ -406,26 +406,26 @@ "regenconf_file_updated": "El fitxer de configuració «{conf}» ha estat actualitzat", "regenconf_now_managed_by_yunohost": "El fitxer de configuració «{conf}» serà gestionat per YunoHost a partir d'ara (categoria {category}).", "regenconf_up_to_date": "La configuració ja està al dia per la categoria «{category}»", - "regenconf_updated": "La configuració ha estat actualitzada per la categoria «{category}»", + "regenconf_updated": "La configuració per la categoria «{category}» ha estat actualitzada", "regenconf_would_be_updated": "La configuració hagués estat actualitzada per la categoria «{category}»", "regenconf_dry_pending_applying": "Verificació de la configuració pendent que s'hauria d'haver aplicat per la categoria «{category}»…", "regenconf_failed": "No s'ha pogut regenerar la configuració per la/les categoria/es : {categories}", "regenconf_pending_applying": "Aplicació de la configuració pendent per la categoria «{category}»…", "restore_action_required": "S'ha d'especificar quelcom a restaurar", - "restore_already_installed_app": "Ja hi ha una aplicació instal·lada amb l'id «{app:s}»", + "restore_already_installed_app": "Una aplicació amb la ID «{app:s}» ja està instal·lada", "restore_app_failed": "No s'ha pogut restaurar l'aplicació «{app:s}»", "restore_cleaning_failed": "No s'ha pogut netejar el directori temporal de restauració", "restore_complete": "Restauració completada", "restore_confirm_yunohost_installed": "Esteu segur de voler restaurar un sistema ja instal·lat? [{answers:s}]", "restore_extracting": "Extracció dels fitxers necessaris de l'arxiu…", "restore_failed": "No s'ha pogut restaurar el sistema", - "restore_hook_unavailable": "L'script de restauració «{part:s}» no està disponible en el sistema i tampoc és en l'arxiu", - "restore_may_be_not_enough_disk_space": "Sembla que no hi ha prou espai disponible en el disc (espai lliure: {free_space:d} B, espai necessari: {needed_space:d} B, marge de seguretat: {margin:d} B)", + "restore_hook_unavailable": "El script de restauració «{part:s}» no està disponible en el sistema i tampoc és en l'arxiu", + "restore_may_be_not_enough_disk_space": "Sembla que no hi ha prou espai disponible en el sistema (lliure: {free_space:d} B, espai necessari: {needed_space:d} B, marge de seguretat: {margin:d} B)", "restore_mounting_archive": "Muntatge de l'arxiu a «{path:s}»", - "restore_not_enough_disk_space": "No hi ha prou espai disponible en el disc (espai lliure: {free_space:d} B, espai necessari: {needed_space:d} B, marge de seguretat: {margin:d} B)", + "restore_not_enough_disk_space": "No hi ha prou espai disponible (espai: {free_space:d} B, espai necessari: {needed_space:d} B, marge de seguretat: {margin:d} B)", "restore_nothings_done": "No s'ha restaurat res", "restore_removing_tmp_dir_failed": "No s'ha pogut eliminar un directori temporal antic", - "restore_running_app_script": "Execució de l'script de restauració de l'aplicació «{app:s}»…", + "restore_running_app_script": "Restaurant l'aplicació «{app:s}»…", "restore_running_hooks": "Execució dels hooks de restauració…", "restore_system_part_failed": "No s'ha pogut restaurar la part «{part:s}» del sistema", "root_password_desynchronized": "S'ha canviat la contrasenya d'administració, però YunoHost no ha pogut propagar-ho cap a la contrasenya root!", @@ -439,24 +439,24 @@ "service_already_started": "Ja s'ha iniciat el servei «{service:s}»", "service_already_stopped": "Ja s'ha aturat el servei «{service:s}»", "service_cmd_exec_failed": "No s'ha pogut executar l'ordre «{command:s}»", - "service_description_avahi-daemon": "permet accedir al servidor via yunohost.local en la xarxa local", - "service_description_dnsmasq": "gestiona la resolució del nom de domini (DNS)", - "service_description_dovecot": "permet als clients de correu accedir/recuperar correus (via IMAP i POP3)", - "service_description_fail2ban": "protegeix contra els atacs de força bruta i a altres atacs provinents d'Internet", - "service_description_glances": "monitora la informació del sistema en el servidor", - "service_description_metronome": "gestiona els comptes de missatgeria instantània XMPP", - "service_description_mysql": "guarda les dades de les aplicacions (base de dades SQL)", - "service_description_nginx": "serveix o permet l'accés a totes les pàgines web allotjades en el servidor", - "service_description_nslcd": "gestiona les connexions shell dels usuaris YunoHost", - "service_description_php7.0-fpm": "executa les aplicacions escrites en PHP amb nginx", - "service_description_postfix": "utilitzat per enviar i rebre correus", - "service_description_redis-server": "una base de dades especialitzada per l'accés ràpid a dades, files d'espera i comunicació entre programes", - "service_description_rmilter": "verifica diferents paràmetres en els correus", - "service_description_rspamd": "filtra el correu brossa, i altres funcionalitats relacionades al correu", - "service_description_slapd": "guarda el usuaris, dominis i informació relacionada", - "service_description_ssh": "permet la connexió remota al servidor via terminal (protocol SSH)", - "service_description_yunohost-api": "gestiona les interaccions entre la interfície web de YunoHost i el sistema", - "service_description_yunohost-firewall": "gestiona els ports de connexió oberts i tancats als serveis", + "service_description_avahi-daemon": "Permet accedir al servidor via «yunohost.local» en la xarxa local", + "service_description_dnsmasq": "Gestiona la resolució del nom de domini (DNS)", + "service_description_dovecot": "Permet als clients de correu accedir/recuperar correus (via IMAP i POP3)", + "service_description_fail2ban": "Protegeix contra els atacs de força bruta i a altres atacs provinents d'Internet", + "service_description_glances": "Monitora la informació del sistema en el servidor", + "service_description_metronome": "Gestiona els comptes de missatgeria instantània XMPP", + "service_description_mysql": "Guarda les dades de les aplicacions (base de dades SQL)", + "service_description_nginx": "Serveix o permet l'accés a totes les pàgines web allotjades en el servidor", + "service_description_nslcd": "Gestiona les connexions shell dels usuaris YunoHost", + "service_description_php7.0-fpm": "Executa les aplicacions escrites en PHP amb NGINX", + "service_description_postfix": "Utilitzat per enviar i rebre correus", + "service_description_redis-server": "Una base de dades especialitzada per l'accés ràpid a dades, files d'espera i comunicació entre programes", + "service_description_rmilter": "Verifica diferents paràmetres en els correus", + "service_description_rspamd": "Filtra el correu brossa, i altres funcionalitats relacionades amb el correu", + "service_description_slapd": "Guarda el usuaris, dominis i informació relacionada", + "service_description_ssh": "Permet la connexió remota al servidor via terminal (protocol SSH)", + "service_description_yunohost-api": "Gestiona les interaccions entre la interfície web de YunoHost i el sistema", + "service_description_yunohost-firewall": "Gestiona els ports de connexió oberts i tancats als serveis", "service_disable_failed": "No s'han pogut deshabilitar el servei «{service:s}»\n\nRegistres recents: {logs:s}", "service_disabled": "S'ha deshabilitat el servei {service:s}", "service_enable_failed": "No s'ha pogut activar el servei «{service:s}»\n\nRegistres recents: {log:s}", @@ -479,26 +479,26 @@ "service_unknown": "Servei «{service:s}» desconegut", "ssowat_conf_generated": "S'ha generat la configuració SSOwat", "ssowat_conf_updated": "S'ha actualitzat la configuració SSOwat", - "ssowat_persistent_conf_read_error": "Error en llegir la configuració persistent de SSOwat: {error:s}. Modifiqueu el fitxer /etc/ssowat/conf.json.persistent per arreglar la sintaxi JSON", - "ssowat_persistent_conf_write_error": "Error guardant la configuració persistent de SSOwat: {error:s}. Modifiqueu el fitxer /etc/ssowat/conf.json.persistent per arreglar la sintaxi JSON", + "ssowat_persistent_conf_read_error": "No s'ha pogut llegir la configuració persistent de SSOwat: {error:s}. Modifiqueu el fitxer /etc/ssowat/conf.json.persistent per arreglar la sintaxi JSON", + "ssowat_persistent_conf_write_error": "No s'ha pogut guardar la configuració persistent de SSOwat: {error:s}. Modifiqueu el fitxer /etc/ssowat/conf.json.persistent per arreglar la sintaxi JSON", "system_upgraded": "S'ha actualitzat el sistema", - "system_username_exists": "El nom d'usuari ja existeix en els usuaris de sistema", - "this_action_broke_dpkg": "Aquesta acció a trencat dpkg/apt (els gestors de paquets del sistema)… Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo dpkg --configure -a».", - "tools_upgrade_at_least_one": "Especifiqueu --apps O --system", + "system_username_exists": "El nom d'usuari ja existeix en la llista d'usuaris de sistema", + "this_action_broke_dpkg": "Aquesta acció a trencat dpkg/APT (els gestors de paquets del sistema)… Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo dpkg --configure -a».", + "tools_upgrade_at_least_one": "Especifiqueu «--apps», o «--system»", "tools_upgrade_cant_both": "No es poden actualitzar tant el sistema com les aplicacions al mateix temps", "tools_upgrade_cant_hold_critical_packages": "No es poden mantenir els paquets crítics…", "tools_upgrade_cant_unhold_critical_packages": "No es poden deixar de mantenir els paquets crítics…", "tools_upgrade_regular_packages": "Actualitzant els paquets «normals» (no relacionats amb YunoHost)…", "tools_upgrade_regular_packages_failed": "No s'han pogut actualitzar els paquets següents: {packages_list}", "tools_upgrade_special_packages": "Actualitzant els paquets «especials» (relacionats amb YunoHost)…", - "tools_upgrade_special_packages_explanation": "Aquesta acció s'acabarà, però l'actualització especial continuarà en segon pla. No comenceu cap altra acció al servidor en els pròxims ~10 minuts (depèn de la velocitat del maquinari). Un cop acabat, pot ser que us hagueu de tornar a connectar a la interfície d'administració. Els registres de l'actualització estaran disponibles a Eines > Registres (a la interfície d'administració) o amb «yunohost log list» (a la línia d'ordres).", - "tools_upgrade_special_packages_completed": "Actualització dels paquets YunoHost acabada!\nPremeu [Enter] per tornar a la línia d'ordres", + "tools_upgrade_special_packages_explanation": "Aquesta acció s'acabarà, però l'actualització especial continuarà en segon pla. No comenceu cap altra acció al servidor en els pròxims ~10 minuts (depèn de la velocitat del maquinari). Un cop acabat, pot ser que us hagueu de tornar a connectar a la interfície d'administració. Els registres de l'actualització estaran disponibles a Eines → Registres (a la interfície d'administració) o amb «yunohost log list» (des de la línia d'ordres).", + "tools_upgrade_special_packages_completed": "Actualització dels paquets YunoHost acabada.\nPremeu [Enter] per tornar a la línia d'ordres", "unbackup_app": "L'aplicació «{app:s}» no serà guardada", "unexpected_error": "Hi ha hagut un error inesperat: {error}", "unit_unknown": "Unitat desconeguda «{unit:s}»", "unlimit": "Sense quota", "unrestore_app": "L'aplicació «{app:s} no serà restaurada", - "update_apt_cache_failed": "No s'ha pogut actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", + "update_apt_cache_failed": "No s'ha pogut actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list, que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", "update_apt_cache_warning": "Hi ha hagut errors al actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", "updating_apt_cache": "Obtenció de les actualitzacions disponibles per als paquets del sistema…", "updating_app_lists": "Obtenció de les actualitzacions disponibles per a les aplicacions…", @@ -507,21 +507,21 @@ "upnp_dev_not_found": "No s'ha trobat cap dispositiu UPnP", "upnp_disabled": "S'ha desactivat UPnP", "upnp_enabled": "S'ha activat UPnP", - "upnp_port_open_failed": "No s'han pogut obrir els ports UPnP", + "upnp_port_open_failed": "No s'ha pogut obrir el port UPnP", "user_created": "S'ha creat l'usuari", - "user_creation_failed": "No s'ha pogut crear l'usuari", + "user_creation_failed": "No s'ha pogut crear l'usuari {user}: {error}", "user_deleted": "S'ha suprimit l'usuari", - "user_deletion_failed": "No s'ha pogut suprimir l'usuari", - "user_home_creation_failed": "No s'ha pogut crear la carpeta personal («home») de l'usuari", + "user_deletion_failed": "No s'ha pogut suprimir l'usuari {user}: {error}", + "user_home_creation_failed": "No s'ha pogut crear la carpeta personal «home» per l'usuari", "user_info_failed": "No s'ha pogut obtenir la informació de l'usuari", "user_unknown": "Usuari desconegut: {user:s}", - "user_update_failed": "No s'ha pogut actualitzar l'usuari", - "user_updated": "S'ha actualitzat l'usuari", + "user_update_failed": "No s'ha pogut actualitzar l'usuari {user}: {error}", + "user_updated": "S'ha canviat la informació de l'usuari", "users_available": "Usuaris disponibles:", "yunohost_already_installed": "YunoHost ja està instal·lat", "yunohost_ca_creation_failed": "No s'ha pogut crear l'autoritat de certificació", "yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.", - "yunohost_configured": "S'ha configurat YunoHost", + "yunohost_configured": "YunoHost està configurat", "yunohost_installing": "Instal·lació de YunoHost…", "yunohost_not_installed": "YunoHost no està instal·lat o no està instal·lat correctament. Executeu «yunohost tools postinstall»", "apps_permission_not_found": "No s'ha trobat cap permís per les aplicacions instal·lades", @@ -534,14 +534,14 @@ "group_already_disallowed": "El grup «{group:s}» ja té els permisos «{permission:s}» desactivats per l'aplicació «{app:s}»", "group_name_already_exist": "El grup {name:s} ja existeix", "group_created": "S'ha creat el grup «{group}»", - "group_creation_failed": "No s'ha pogut crear el grup «{group}»", + "group_creation_failed": "No s'ha pogut crear el grup «{group}»: {error}", "group_deleted": "S'ha eliminat el grup «{group}»", - "group_deletion_failed": "No s'ha pogut eliminar el grup «{group}»", + "group_deletion_failed": "No s'ha pogut eliminar el grup «{group}»: {error}", "group_deletion_not_allowed": "El grup {group:s} no es pot eliminar manualment.", "group_info_failed": "Ha fallat la informació del grup", "group_unknown": "Grup {group:s} desconegut", "group_updated": "S'ha actualitzat el grup «{group}»", - "group_update_failed": "No s'ha pogut actualitzat el grup «{group}»", + "group_update_failed": "No s'ha pogut actualitzat el grup «{group}»: {error}", "log_permission_add": "Afegir el permís «{}» per l'aplicació «{}»", "log_permission_remove": "Suprimir el permís «{}»", "log_permission_update": "Actualitzar el permís «{}» per l'aplicació «{}»", @@ -550,31 +550,31 @@ "log_user_group_update": "Actualitzar grup «{}»", "log_user_permission_add": "Actualitzar el permís «{}»", "log_user_permission_remove": "Actualitzar el permís «{}»", - "mailbox_disabled": "La bústia de correu està desactivada per als usuaris {user:s}", + "mailbox_disabled": "La bústia de correu està desactivada per al usuari {user:s}", "migration_description_0011_setup_group_permission": "Configurar el grup d'usuaris i els permisos per les aplicacions i els serveis", "migration_0011_backup_before_migration": "Creant una còpia de seguretat de la base de dades LDAP i la configuració de les aplicacions abans d'efectuar la migració.", "migration_0011_can_not_backup_before_migration": "No s'ha pogut fer la còpia de seguretat abans de la migració. No s'ha pogut fer la migració. Error: {error:s}", "migration_0011_create_group": "Creant un grup per a cada usuari…", "migration_0011_done": "Migració completa. Ja podeu gestionar grups d'usuaris.", - "migration_0011_LDAP_config_dirty": "Sembla que heu modificat manualment la configuració LDAP. Per fer aquesta migració s'ha d'actualitzar la configuració LDAP.\nGuardeu la configuració actual, reinicieu la configuració original amb l'ordre «yunohost tools regen-conf -f» i torneu a intentar la migració", + "migration_0011_LDAP_config_dirty": "Sembla que heu modificat manualment la configuració LDAP. Per fer aquesta migració s'ha d'actualitzar la configuració LDAP.\nGuardeu la configuració actual, reinicieu la configuració original executant l'ordre «yunohost tools regen-conf -f» i torneu a intentar la migració", "migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}", "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP…", - "migration_0011_migration_failed_trying_to_rollback": "La migració ha fallat … s'intenta tornar el sistema a l'estat anterior.", + "migration_0011_migration_failed_trying_to_rollback": "La migració ha fallat… s'intenta tornar el sistema a l'estat anterior.", "migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.", "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP…", "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP…", "need_define_permission_before": "Heu de tornar a redefinir els permisos utilitzant «yunohost user permission add -u USER» abans d'eliminar un grup permès", "permission_already_clear": "Ja s'ha donat el permís «{permission:s}» per l'aplicació {app:s}", - "permission_already_exist": "El permís «{permission:s}» ja existeix per l'aplicació {app:s}", - "permission_created": "S'ha creat el permís «{permission:s}» per l'aplicació {app:s}", - "permission_creation_failed": "La creació del permís ha fallat", - "permission_deleted": "S'ha eliminat el permís «{permission:s}» per l'aplicació {app:s}", - "permission_deletion_failed": "L'eliminació del permís «{permission:s}» per l'aplicació {app:s} ha fallat", - "permission_not_found": "No s'ha trobat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_already_exist": "El permís «{permission:s}» ja existeix", + "permission_created": "S'ha creat el permís «{permission:s}»", + "permission_creation_failed": "No s'ha pogut crear el permís «{permission}»: {error}", + "permission_deleted": "S'ha eliminat el permís «{permission:s}»", + "permission_deletion_failed": "No s'ha pogut eliminar el permís «{permission:s}»: {error}", + "permission_not_found": "No s'ha trobat el permís «{permission:s}»", "permission_name_not_valid": "El nom del permís «{permission:s}» no és vàlid", - "permission_update_failed": "L'actualització del permís ha fallat", + "permission_update_failed": "No s'ha pogut actualitzar el permís «{permission}»: {error}", "permission_generated": "S'ha actualitzat la base de dades del permís", - "permission_updated": "S'ha actualitzat el permís «{permission:s}» per l'aplicació {app:s}", + "permission_updated": "S'ha actualitzat el permís «{permission:s}»", "permission_update_nothing_to_do": "No hi ha cap permís per actualitzar", "remove_main_permission_not_allowed": "No es pot eliminar el permís principal", "remove_user_of_group_not_allowed": "No es pot eliminar l'usuari {user:s} del grup {group:s}", @@ -582,5 +582,38 @@ "tools_update_failed_to_app_fetchlist": "No s'ha pogut actualitzar la llista d'aplicacions de YunoHost a causa de: {error}", "user_already_in_group": "L'usuari {user:s} ja és en el grup {group:s}", "user_not_in_group": "L'usuari {user:s} no és en el grup {group:s}", - "migration_description_0012_postgresql_password_to_md5_authentication": "Força l'autenticació postgresql a fer servir md5 per a les connexions locals" + "migration_description_0012_postgresql_password_to_md5_authentication": "Força l'autenticació PostgreSQL a fer servir MD5 per a les connexions locals", + "app_full_domain_unavailable": "Aquesta aplicació requereix un domini sencer per ser instal·lada, però ja hi ha altres aplicacions instal·lades al domini «{domain}». Una possible solució és afegir i utilitzar un subdomini dedicat a aquesta aplicació.", + "migrations_not_pending_cant_skip": "Aquestes migracions no estan pendents, així que no poden ser omeses: {ids}", + "app_action_broke_system": "Aquesta acció sembla haver trencat els següents serveis importants: {services}", + "log_permission_urls": "Actualitzar les URLs relacionades amb el permís «{}»", + "log_user_group_create": "Crear grup «{}»", + "log_user_permission_update": "Actualitzar els accessos per al permís «{}»", + "log_user_permission_reset": "Restablir el permís «{}»", + "permission_already_disallowed": "El grup «{group}» ja té el permís «{permission}» desactivat", + "migrations_already_ran": "Aquestes migracions ja s'han fet: {ids}", + "migrations_dependencies_not_satisfied": "No s'ha pogut executar la migració {id} perquè s'han d'executar primer les següents migracions: {dependencies_id}", + "migrations_failed_to_load_migration": "No s'ha pogut carregar la migració {id}: {error}", + "migrations_exclusive_options": "«--auto», «--skip», i «--force-rerun» són opcions mútuament excloents.", + "migrations_must_provide_explicit_targets": "Heu de proporcionar objectius explícits al utilitzar «--skip» o «--force-rerun»", + "migrations_no_such_migration": "No hi ha cap migració anomenada {id}", + "migrations_pending_cant_rerun": "Aquestes migracions encara estan pendents, així que no es poden tornar a executar: {ids}", + "migrations_running_forward": "Executant la migració {id}…", + "migrations_success_forward": "Migració {id} completada", + "apps_already_up_to_date": "Ja estan actualitzades totes les aplicacions", + "dyndns_provider_unreachable": "No s'ha pogut connectar amb el proveïdor Dyndns {provider}: o el vostre YunoHost no està ben connectat a Internet o el servidor dynette està caigut.", + "operation_interrupted": "S'ha interromput manualment l'operació?", + "group_already_exist": "El grup {group} ja existeix", + "group_already_exist_on_system": "El grup {group} ja existeix en els grups del sistema", + "group_cannot_be_edited": "El grup {group} no es pot editar manualment.", + "group_cannot_be_deleted": "El grup {group} no es pot eliminar manualment.", + "group_user_already_in_group": "L'usuari {user} ja està en el grup {group}", + "group_user_not_in_group": "L'usuari {user} no està en el grup {group}", + "log_permission_create": "Crear el permís «{}»", + "log_permission_delete": "Eliminar el permís «{}»", + "migration_0011_failed_to_remove_stale_object": "No s'ha pogut eliminar l'objecte obsolet {dn}: {error}", + "permission_already_allowed": "El grup «{group}» ja té el permís «{permission}» activat", + "permission_cannot_remove_main": "No es permet eliminar un permís principal", + "user_already_exists": "L'usuari {user} ja existeix", + "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació" } From 3bd90c63b3ce9a352c74c295ef056cd650429741 Mon Sep 17 00:00:00 2001 From: advocatux Date: Mon, 14 Oct 2019 17:43:39 +0000 Subject: [PATCH 249/299] Translated using Weblate (Spanish) Currently translated at 100.0% (556 of 556 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index 80b17673a..4f136460c 100644 --- a/locales/es.json +++ b/locales/es.json @@ -626,5 +626,7 @@ "permission_already_disallowed": "El grupo «{group}» ya tiene el permiso «{permission}» desactivado", "permission_cannot_remove_main": "No está permitido eliminar un permiso principal", "user_already_exists": "El usuario {user} ya existe", - "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación." + "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación.", + "app_install_failed": "No se pudo instalar {app}", + "app_install_script_failed": "Ha ocurrido un error en el guión de instalación de la aplicación" } From 57af2c7c45a3c621a7e52359e5e8fb11a1e608f8 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Mon, 14 Oct 2019 20:51:25 +0000 Subject: [PATCH 250/299] Translated using Weblate (Catalan) Currently translated at 100.0% (556 of 556 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index 1476d5fb5..592bc4592 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -615,5 +615,7 @@ "permission_already_allowed": "El grup «{group}» ja té el permís «{permission}» activat", "permission_cannot_remove_main": "No es permet eliminar un permís principal", "user_already_exists": "L'usuari {user} ja existeix", - "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació" + "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació", + "app_install_failed": "No s'ha pogut instal·lar {app}", + "app_install_script_failed": "Hi ha hagut un error en el script d'instal·lació de l'aplicació" } From a74ba4f112a824e15fc9aaac49470ff4d532df8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 16 Oct 2019 00:03:50 +0000 Subject: [PATCH 251/299] services required, the action that failed --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 7fcaf0d04..c2aa0191b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6,7 +6,7 @@ "admin_password_changed": "The administration password got changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do. Everything is already up-to-date.", - "app_action_cannot_be_ran_because_required_services_down": "Start all required services to run this app. Try to restart the following ones (and possibly investigate why they are down): {services}", + "app_action_cannot_be_ran_because_required_services_down": "Start all services required to run the action that failed. Try restarting the following ones (and possibly investigate why they are down): {services}", "app_action_broke_system": "This action seem to have broke these important services: {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Look into `app changeurl` if it's available.", From 77960fd40535df3ab7e9f5f19e815742b5bcb502 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Oct 2019 16:40:26 +0200 Subject: [PATCH 252/299] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..c3b460087 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +custom: https://donate.yunohost.org +liberapay: YunoHost From 6be15a62880338c007bb426007bfba626112e5bf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Oct 2019 17:32:19 +0200 Subject: [PATCH 253/299] Simplify the README, add cool shiny logo, badges and screenshots --- README.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b860c27d7..5fb72e260 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,27 @@ +# YunoHost + [![Build status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) [![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) +![Mastodon Follow](https://img.shields.io/mastodon/follow/28084) -# YunoHost core +This repository corresponds to the core code of YunoHost, mainly written in Python and Bash. -This repository is the core of YunoHost code. +You can learn more about what's YunoHost and its features [here](https://yunohost.org/#/whatsyunohost)! - [Project website](https://yunohost.org) -- [Bugtracker](https://github.com/YunoHost/issues). +- [Installation documentation](https://yunohost.org/install) +- [Issue tracker](https://github.com/YunoHost/issues) + +# Screenshots + +Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single-sign on user portal ([SSOwat](https://github.com/YunoHost/ssowat)) +--- | --- +![](https://raw.githubusercontent.com/YunoHost/doc/master/images/webadmin.png) | ![](https://raw.githubusercontent.com/YunoHost/doc/master/images/user_panel.png) + ## Contributing -- You can develop on this repository using [ynh-dev](https://github.com/YunoHost/ynh-dev) with `use-git` sub-command. -- On this repository we are [following this workflow](https://yunohost.org/#/build_system_en): `stable ← testing ← unstable ← your_branch`. -- Note: If you modify Python scripts, you will have to modifiy the actions map. +- You can learn how to get started with developing on YunoHost by reading [this piece of documentation](https://yunohost.org/dev). - You can help translate YunoHost on our [translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) Translation status @@ -21,7 +30,7 @@ This repository is the core of YunoHost code. ## Repository content - [YunoHost core Python 2.7 scripts](./src/yunohost). -- [An actionsmap](./data/actionsmap/yunohost.yml) used by moulinette. +- [An actionsmap](./data/actionsmap/yunohost.yml) describing the CLI and API - [Services configuration templates](./data/templates). - [Hooks](./data/hooks). - [Locales](./locales) for translations of `yunohost` command. @@ -32,17 +41,10 @@ This repository is the core of YunoHost code. ## How does it work? - Python core scripts are accessible through two interfaces thanks to the [moulinette framework](https://github.com/YunoHost/moulinette): - - [CLI](https://en.wikipedia.org/wiki/Command-line_interface) for `yunohost` command. - - [API](https://en.wikipedia.org/wiki/Application_programming_interface) for [web administration module](https://github.com/YunoHost/yunohost-admin) (other modules could be implemented). + - the [CLI](https://en.wikipedia.org/wiki/Command-line_interface) corresponding to the `yunohost` command. + - the [API](https://en.wikipedia.org/wiki/Application_programming_interface) used by the [web administration interface](https://github.com/YunoHost/yunohost-admin) (other interfaces could be implemented). - You can find more details about how YunoHost works on this [documentation (in French)](https://yunohost.org/#/package_list_fr). -## Dependencies - -- [Python 2.7](https://www.python.org/download/releases/2.7) -- [Moulinette](https://github.com/YunoHost/moulinette) -- [Bash](https://www.gnu.org/software/bash/bash.html) -- [Debian Stretch](https://www.debian.org/releases/stretch) - ## License -As [other components of YunoHost core code](https://yunohost.org/#/faq_en), this repository is licensed GNU AGPL v3. +As [other components of YunoHost](https://yunohost.org/#/faq_en), this repository is licensed GNU AGPL v3. From a0febb0b2110041d58fdfdc61468fe62712ad496 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Oct 2019 17:35:52 +0200 Subject: [PATCH 254/299] Uuuh small fixes for the README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5fb72e260..588878261 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) [![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) -![Mastodon Follow](https://img.shields.io/mastodon/follow/28084) +[![Mastodon Follow](https://img.shields.io/mastodon/follow/28084)](https://mastodon.social/@yunohost) This repository corresponds to the core code of YunoHost, mainly written in Python and Bash. @@ -14,7 +14,7 @@ You can learn more about what's YunoHost and its features [here](https://yunohos # Screenshots -Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single-sign on user portal ([SSOwat](https://github.com/YunoHost/ssowat)) +Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single sign-on user portal ([SSOwat](https://github.com/YunoHost/ssowat)) --- | --- ![](https://raw.githubusercontent.com/YunoHost/doc/master/images/webadmin.png) | ![](https://raw.githubusercontent.com/YunoHost/doc/master/images/user_panel.png) From d9990cd818ad0c8056ecb1c0f966f2a180be683c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Oct 2019 18:59:23 +0200 Subject: [PATCH 255/299] Smarter regex to avoid redacting all --key=stuff when using setting helpers for example --- src/yunohost/log.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/yunohost/log.py b/src/yunohost/log.py index 0f5ff784c..72e497b5d 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -315,7 +315,8 @@ class RedactingFormatter(Formatter): try: # This matches stuff like db_pwd=the_secret or admin_password=other_secret # (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=") - match = re.search(r'(pwd|pass|password|secret|key|token)=(\S{3,})$', record.strip()) + # For 'key', we require to at least have one word char [a-zA-Z0-9_] before it to avoid catching "--key" used in many helpers + match = re.search(r'(pwd|pass|password|secret|\wkey|token)=(\S{3,})$', record.strip()) if match and match.group(2) not in self.data_to_redact: self.data_to_redact.append(match.group(2)) except Exception as e: From e2731955041b6cd37d4eeebf94b96edb66c0ce7a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Oct 2019 20:22:29 +0200 Subject: [PATCH 256/299] [fix] Bad copypasta: logger doesn't exists in that context... --- data/helpers.d/setting | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index a8d2919a4..fd2824997 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -159,7 +159,7 @@ ynh_add_protected_uris() { ynh_app_setting() { ACTION="$1" APP="$2" KEY="$3" VALUE="${4:-}" python - < Date: Sat, 19 Oct 2019 05:34:42 +0200 Subject: [PATCH 257/299] Try to make those debug messages sound less like an error --- src/yunohost/hook.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 05c5a6b6b..40d3d114f 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -196,7 +196,7 @@ def hook_list(action, list_by='name', show_info=False): else: _append_folder(result, HOOK_FOLDER) except OSError: - logger.debug("system hook folder not found for action '%s' in %s", + logger.debug("No default hook for action '%s' in %s", action, HOOK_FOLDER) try: @@ -207,7 +207,7 @@ def hook_list(action, list_by='name', show_info=False): else: _append_folder(result, CUSTOM_HOOK_FOLDER) except OSError: - logger.debug("custom hook folder not found for action '%s' in %s", + logger.debug("No custom hook for action '%s' in %s", action, CUSTOM_HOOK_FOLDER) return {'hooks': result} From 7c99aff94e3bbbf66af8a2a6cd3cfa20b523e05a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Oct 2019 18:04:55 +0200 Subject: [PATCH 258/299] Forgot the i18n --- src/yunohost/permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 4cfbc214f..67d115bc7 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -150,7 +150,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): - logger.warning("permission_already_up_to_date") + logger.warning(m18n.n("permission_already_up_to_date")) return # Commit the new allowed group list From 9362261d345b255eebf094b9adb14b271d851b23 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Oct 2019 18:05:32 +0200 Subject: [PATCH 259/299] Ugh some apps uses skipped_uris sometimes instead of unprotected_uris... --- src/yunohost/app.py | 2 +- src/yunohost/data_migrations/0011_setup_group_permission.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index f4c504505..9074c90fd 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1102,7 +1102,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu permission_url(app_instance_name + ".main", url=None, sync_perm=False) # Migrate classic public app still using the legacy unprotected_uris - if app_settings.get("unprotected_uris", None) == "/": + if app_settings.get("unprotected_uris", None) == "/" or app_settings.get("skipped_uris", None) == "/": user_permission_update(app_instance_name + ".main", remove="all_users", add="visitors", sync_perm=False) permission_sync_to_user() diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 9ba2268d9..7a987899c 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -117,7 +117,7 @@ class MyMigration(Migration): app_setting(app, 'allowed_users', delete=True) # Migrate classic public app still using the legacy unprotected_uris - if app_setting(app, "unprotected_uris") == "/": + if app_setting(app, "unprotected_uris") == "/" or app_setting(app, "skipped_uris") == "/": user_permission_update(app+".main", remove="all_users", add="visitors", sync_perm=False) permission_sync_to_user() From 98d60a888bf19987cbb956648e777dcbf79dd047 Mon Sep 17 00:00:00 2001 From: ljf Date: Thu, 17 Oct 2019 02:30:23 +0200 Subject: [PATCH 260/299] [fix] HTTP API for permissions --- data/actionsmap/yunohost.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 22037f05f..245b3615d 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -285,7 +285,7 @@ user: ### user_permission_list() list: action_help: List permissions and corresponding accesses - api: GET /users/permissions/ + api: GET /users/permissions arguments: -s: full: --short @@ -300,7 +300,7 @@ user: ### user_permission_update() update: action_help: Manage group or user permissions - api: POST /users/permissions/ + api: PUT /users/permissions/ arguments: permission: help: Permission to manage (e.g. mail or nextcloud or wordpress.editors) From e005d94f82c2ef85d47083fd3ee76ed4ff4ffd37 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Oct 2019 19:08:38 +0200 Subject: [PATCH 261/299] Messed up the message UX with a previous PR (did not have the message explaining how to debug at the very end) --- locales/en.json | 2 +- src/yunohost/app.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/locales/en.json b/locales/en.json index 03ecdbccd..1ad7361e9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -23,7 +23,7 @@ "app_id_invalid": "Invalid app ID", "app_incompatible": "The app {app} is incompatible with your YunoHost version", "app_install_files_invalid": "These files cannot be installed", - "app_install_failed": "Could not install {app}", + "app_install_failed": "Could not install {app}: {error}", "app_install_script_failed": "An error occured inside the app installation script", "app_location_already_used": "The app '{app}' is already installed in ({path})", "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, {domain} is already in use by the other app '{other_app}'", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 9074c90fd..5b7202362 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -994,19 +994,19 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu install_failed = True if install_retcode != 0 else False if install_failed: error = m18n.n('app_install_script_failed') - logger.exception(error) - operation_logger.error(error) + logger.exception(m18n.n("app_install_failed", app=app_id, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) # Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception except (KeyboardInterrupt, EOFError): error = m18n.n('operation_interrupted') - logger.exception(error) - operation_logger.error(error) + logger.exception(m18n.n("app_install_failed", app=app_id, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) # Something wrong happened in Yunohost's code (most probably hook_exec) - except Exception as e : + except Exception as e: import traceback error = m18n.n('unexpected_error', error=u"\n" + traceback.format_exc()) - logger.exception(error) - operation_logger.error(error) + logger.exception(m18n.n("app_install_failed", app=app_id, error=error)) + failure_message_with_debug_instructions = operation_logger.error(error) finally: # Whatever happened (install success or failure) we check if it broke the system # and warn the user about it @@ -1015,8 +1015,8 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu _assert_system_is_sane_for_app(manifest, "post") except Exception as e: broke_the_system = True - logger.exception(str(e)) - operation_logger.error(str(e)) + logger.exception(m18n.n("app_install_failed", app=app_id, error=str(e))) + failure_message_with_debug_instructions = operation_logger.error(str(e)) # If the install failed or broke the system, we remove it if install_failed or broke_the_system: @@ -1076,7 +1076,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu app_ssowatconf() - raise YunohostError("app_install_failed", app=app_id) + raise YunohostError(failure_message_with_debug_instructions, raw_msg=True) # Clean hooks and add new ones hook_remove(app_instance_name) From 9beeb16077277ba5c181baad94bcdf70510ed3de Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Oct 2019 19:18:19 +0200 Subject: [PATCH 262/299] Don't sync permission right away when deleting them, otherwise we get annoying warning because app_map thinks the app is still installed and expected a main permission --- src/yunohost/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5b7202362..5cf812871 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1055,7 +1055,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # Remove all permission in LDAP for permission_name in user_permission_list()["permissions"].keys(): if permission_name.startswith(app_instance_name+"."): - permission_delete(permission_name, force=True) + permission_delete(permission_name, force=True, sync_perm=False) if remove_retcode != 0: msg = m18n.n('app_not_properly_removed', @@ -1074,7 +1074,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu shutil.rmtree(app_setting_path) shutil.rmtree(extracted_app_folder) - app_ssowatconf() + permission_sync_to_user() raise YunohostError(failure_message_with_debug_instructions, raw_msg=True) From 25afdd4d4109faebf8e2aeef78eb2f53587f3dfb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 22 Oct 2019 15:50:41 +0200 Subject: [PATCH 263/299] Misc README improvements --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 588878261..5b7d04750 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,24 @@ -# YunoHost +

+ YunoHost +

+ +

YunoHost

+ +
[![Build status](https://travis-ci.org/YunoHost/yunohost.svg?branch=stretch-unstable)](https://travis-ci.org/YunoHost/yunohost) [![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/stretch-unstable/LICENSE) [![Mastodon Follow](https://img.shields.io/mastodon/follow/28084)](https://mastodon.social/@yunohost) +
+ +YunoHost is an operating system aiming to simplify as much as possible the administration of a server. + This repository corresponds to the core code of YunoHost, mainly written in Python and Bash. -You can learn more about what's YunoHost and its features [here](https://yunohost.org/#/whatsyunohost)! - +- [Project features](https://yunohost.org/#/whatsyunohost) - [Project website](https://yunohost.org) -- [Installation documentation](https://yunohost.org/install) +- [Install documentation](https://yunohost.org/install) - [Issue tracker](https://github.com/YunoHost/issues) # Screenshots @@ -22,6 +31,7 @@ Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single ## Contributing - You can learn how to get started with developing on YunoHost by reading [this piece of documentation](https://yunohost.org/dev). +- Come chat with us on the [dev chatroom](https://yunohost.org/#/chat_rooms) ! - You can help translate YunoHost on our [translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) Translation status From 73741f6cd937061329f0b9a2bbd54c3699a1a7e4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 22 Oct 2019 16:44:20 +0200 Subject: [PATCH 264/299] Simplify README, all those explanations are now in the dev documentation --- README.md | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 5b7d04750..aec5300e2 100644 --- a/README.md +++ b/README.md @@ -34,27 +34,10 @@ Webadmin ([Yunohost-Admin](https://github.com/YunoHost/yunohost-admin)) | Single - Come chat with us on the [dev chatroom](https://yunohost.org/#/chat_rooms) ! - You can help translate YunoHost on our [translation platform](https://translate.yunohost.org/engage/yunohost/?utm_source=widget) +

Translation status - - -## Repository content - -- [YunoHost core Python 2.7 scripts](./src/yunohost). -- [An actionsmap](./data/actionsmap/yunohost.yml) describing the CLI and API -- [Services configuration templates](./data/templates). -- [Hooks](./data/hooks). -- [Locales](./locales) for translations of `yunohost` command. -- [Shell helpers](./helpers.d) for [application packaging](https://yunohost.org/#/packaging_apps_helpers_en). -- [Modules for the XMPP server Metronome](./lib/metronome/modules). -- [Debian files](./debian) for package creation. - -## How does it work? - -- Python core scripts are accessible through two interfaces thanks to the [moulinette framework](https://github.com/YunoHost/moulinette): - - the [CLI](https://en.wikipedia.org/wiki/Command-line_interface) corresponding to the `yunohost` command. - - the [API](https://en.wikipedia.org/wiki/Application_programming_interface) used by the [web administration interface](https://github.com/YunoHost/yunohost-admin) (other interfaces could be implemented). -- You can find more details about how YunoHost works on this [documentation (in French)](https://yunohost.org/#/package_list_fr). +

## License -As [other components of YunoHost](https://yunohost.org/#/faq_en), this repository is licensed GNU AGPL v3. +As [other components of YunoHost](https://yunohost.org/#/faq_en), this repository is licensed under GNU AGPL v3. From ef65f82e4437b0c22249b8aebb88f8de9ef685b8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 22 Oct 2019 20:32:08 +0200 Subject: [PATCH 265/299] Improve string app_action_cannot_be_ran_because_required_services_down --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 34d5f1ab7..1d68fb334 100644 --- a/locales/en.json +++ b/locales/en.json @@ -6,7 +6,7 @@ "admin_password_changed": "The administration password got changed", "admin_password_too_long": "Please choose a password shorter than 127 characters", "already_up_to_date": "Nothing to do. Everything is already up-to-date.", - "app_action_cannot_be_ran_because_required_services_down": "Start all services required to run the action that failed. Try restarting the following ones (and possibly investigate why they are down): {services}", + "app_action_cannot_be_ran_because_required_services_down": "These required services should be running to run this action: {services}. Try restarting them to continue (and possibly investigate why they are down).", "app_action_broke_system": "This action seem to have broke these important services: {services}", "app_already_installed": "{app:s} is already installed", "app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Look into `app changeurl` if it's available.", From 4f3fac60057ed537fb86f7b0e8c9ee53663ef4d3 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 16 Oct 2019 06:21:31 +0000 Subject: [PATCH 266/299] Translated using Weblate (French) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 9abeb585c..9e64bcff5 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -653,5 +653,15 @@ "permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '", "permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé", "user_already_exists": "L'utilisateur {user} existe déjà", - "app_full_domain_unavailable": "Désolé, cette application nécessite l'installation d'un domaine complet, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Une solution possible consiste à ajouter et à utiliser un sous-domaine dédié à cette application." + "app_full_domain_unavailable": "Désolé, cette application nécessite l'installation d'un domaine complet, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Une solution possible consiste à ajouter et à utiliser un sous-domaine dédié à cette application.", + "group_cannot_edit_all_users": "Le groupe 'all_users' ne peut pas être édité manuellement. C'est un groupe spécial destiné à contenir tous les utilisateurs enregistrés dans YunoHost", + "group_cannot_edit_visitors": "Le groupe 'visiteurs' ne peut pas être édité manuellement. C'est un groupe spécial représentant les visiteurs anonymes", + "group_cannot_edit_primary_group": "Le groupe '{group}' ne peut pas être édité manuellement. C'est le groupe principal destiné à ne contenir qu'un utilisateur spécifique.", + "log_permission_url": "Mise à jour de l'URL associée à l'autorisation '{}'", + "migration_0011_slapd_config_will_be_overwritten": "Il semble que vous ayez modifié manuellement la configuration de slapd. Pour cette migration critique, YunoHost doit forcer la mise à jour de la configuration de slapd. Les fichiers originaux seront sauvegardés dans {conf_backup_folder}.", + "permission_already_up_to_date": "L'autorisation n'a pas été mise à jour car les demandes d'ajout/suppression correspondent déjà à l'état actuel.", + "permission_currently_allowed_for_visitors": "Cette autorisation est actuellement accordée aux visiteurs en plus d'autres groupes. Vous voudrez probablement supprimer l'autorisation \"visiteurs\" ou supprimer les autres groupes auxquels il est actuellement attribué.", + "permission_currently_allowed_for_all_users": "Cette autorisation est actuellement accordée à tous les utilisateurs en plus des autres groupes. Vous voudrez probablement soit supprimer l'autorisation 'all_users', soit supprimer les autres groupes auxquels il est actuellement autorisé.", + "app_install_failed": "Impossible d'installer {app}", + "app_install_script_failed": "Une erreur est survenue dans le script d'installation de l'application" } From 2a30a97142f78bcec645f4db21cb63effcb0bdd0 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Wed, 16 Oct 2019 07:47:01 +0000 Subject: [PATCH 267/299] Translated using Weblate (Catalan) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 592bc4592..b3831d929 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -285,7 +285,7 @@ "ldap_initialized": "S'ha iniciat LDAP", "license_undefined": "indefinit", "mail_alias_remove_failed": "No s'han pogut eliminar els àlies del correu «{mail:s}»", - "mail_domain_unknown": "Domini d'adreça de correu per «{domain:s}» desconegut", + "mail_domain_unknown": "El domini «{domain:s}» de l'adreça de correu no és vàlid. Utilitzeu un domini administrat per aquest servidor.", "mail_forward_remove_failed": "No s'han pogut eliminar el reenviament de correu «{mail:s}»", "mailbox_used_space_dovecot_down": "S'ha d'engegar el servei de correu Dovecot, per poder obtenir l'espai utilitzat per la bústia de correu", "mail_unavailable": "Aquesta adreça de correu està reservada i ha de ser atribuïda automàticament el primer usuari", @@ -617,5 +617,13 @@ "user_already_exists": "L'usuari {user} ja existeix", "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació", "app_install_failed": "No s'ha pogut instal·lar {app}", - "app_install_script_failed": "Hi ha hagut un error en el script d'instal·lació de l'aplicació" + "app_install_script_failed": "Hi ha hagut un error en el script d'instal·lació de l'aplicació", + "group_cannot_edit_all_users": "El grup «all_users» no es pot editar manualment. És un grup especial destinat a contenir els usuaris registrats a YunoHost", + "group_cannot_edit_visitors": "El grup «visitors» no es pot editar manualment. És un grup especial que representa els visitants anònims", + "group_cannot_edit_primary_group": "El grup «{group}» no es pot editar manualment. És el grup principal destinat a contenir un usuari específic.", + "log_permission_url": "Actualització de la URL associada al permís «{}»", + "migration_0011_slapd_config_will_be_overwritten": "Sembla que heu modificat manualment la configuració de sldap. Per aquesta migració crítica, YunoHost ha de forçar l'actualització de la configuració sldap. Es farà una còpia de seguretat a {conf_backup_folder}.", + "permission_already_up_to_date": "No s'ha actualitzat el permís perquè la petició d'afegir/eliminar ja corresponent a l'estat actual.", + "permission_currently_allowed_for_visitors": "El permís ja el tenen el grup de visitants a més d'altres grups. Segurament s'hauria de revocar el permís al grup dels visitants o eliminar els altres grups als que s'ha atribuït.", + "permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït." } From 889f4ef9ea7f35fe2ebf090decc9907e5f466c75 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Wed, 16 Oct 2019 06:28:28 +0000 Subject: [PATCH 268/299] Translated using Weblate (Esperanto) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/locales/eo.json b/locales/eo.json index 0d8d13fe8..237bdd1df 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -476,7 +476,7 @@ "mountpoint_unknown": "Nekonata montpunkto", "log_tools_maindomain": "Faru de '{}' la ĉefa domajno", "maindomain_change_failed": "Ne povis ŝanĝi la ĉefan domajnon", - "mail_domain_unknown": "Nekonata retpoŝtadreso por domajno '{domain:s}'", + "mail_domain_unknown": "Nevalida retadreso por domajno '{domain:s}'. Bonvolu uzi domajnon administritan de ĉi tiu servilo.", "migrations_cant_reach_migration_file": "Ne povis aliri migrajn dosierojn ĉe la vojo% s", "pattern_email": "Devas esti valida retpoŝtadreso (t.e.iu@domain.org)", "mail_alias_remove_failed": "Ne povis forigi retpoŝton alias '{mail:s}'", @@ -553,5 +553,15 @@ "service_restart_failed": "Ne povis rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", "firewall_rules_cmd_failed": "Iuj komandoj pri fajroŝirmilo malsukcesis. Pliaj informoj en ensaluto.", "certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …", - "app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu apliko postulas plenan domajnon esti instalita, sed iuj aliaj programoj jam estas instalitaj sur '{domain}'. Unu ebla solvo estas aldoni kaj uzi subdomajnon dediĉitan al ĉi tiu aplikaĵo anstataŭe." + "app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu apliko postulas plenan domajnon esti instalita, sed iuj aliaj programoj jam estas instalitaj sur '{domain}'. Unu ebla solvo estas aldoni kaj uzi subdomajnon dediĉitan al ĉi tiu aplikaĵo anstataŭe.", + "migration_0011_slapd_config_will_be_overwritten": "Ŝajnas ke vi permane redaktis la slapd-agordon. Por ĉi tiu kritika migrado, YunoHost bezonas devigi la ĝisdatigon de la slapd-agordo. La originalaj dosieroj estos rezervitaj en {conf_backup_folder}.", + "group_cannot_edit_all_users": "La grupo 'all_users' ne povas esti redaktita permane. Ĝi estas speciala grupo celita enhavi ĉiujn uzantojn registritajn en YunoHost", + "group_cannot_edit_visitors": "La grupo 'vizitantoj' ne povas esti redaktita permane. Ĝi estas speciala grupo reprezentanta anonimajn vizitantojn", + "group_cannot_edit_primary_group": "La grupo '{group}' ne povas esti redaktita permane. Ĝi estas la primara grupo celita enhavi nur unu specifan uzanton.", + "log_permission_url": "Ĝisdatigu url-rilataj al permeso '{}'", + "permission_already_up_to_date": "La permeso ne estis ĝisdatigita ĉar la petoj pri aldono/forigo jam kongruas kun la aktuala stato.", + "permission_currently_allowed_for_visitors": "Ĉi tiu permeso estas nuntempe donita al vizitantoj aldone al aliaj grupoj. Vi probable volas aŭ forigi la permeson de \"vizitantoj\" aŭ forigi la aliajn grupojn al kiuj ĝi nun estas koncedita.", + "permission_currently_allowed_for_all_users": "Ĉi tiu permeso estas nuntempe donita al ĉiuj uzantoj aldone al aliaj grupoj. Vi probable volas aŭ forigi la permeson \"all_users\" aŭ forigi la aliajn grupojn, kiujn ĝi nuntempe donas.", + "app_install_failed": "Ne povis instali {app}", + "app_install_script_failed": "Eraro okazis en la skripto de instalado de la app" } From 82662167fc55becbc766fe4ac00dedc96ca5f7f2 Mon Sep 17 00:00:00 2001 From: advocatux Date: Thu, 17 Oct 2019 16:16:02 +0000 Subject: [PATCH 269/299] Translated using Weblate (Spanish) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/locales/es.json b/locales/es.json index 4f136460c..17c2998fa 100644 --- a/locales/es.json +++ b/locales/es.json @@ -119,7 +119,7 @@ "ldap_initialized": "Inicializado LDAP", "license_undefined": "indefinido", "mail_alias_remove_failed": "No se pudo eliminar el alias de correo «{mail:s}»", - "mail_domain_unknown": "Dirección de correo desconocida para el dominio «{domain:s}»", + "mail_domain_unknown": "Dirección de correo no válida para el dominio «{domain:s}». Use un dominio administrado por este servidor.", "mail_forward_remove_failed": "No se pudo eliminar el reenvío de correo «{mail:s}»", "maindomain_change_failed": "No se pudo cambiar el dominio principal", "maindomain_changed": "El dominio principal ha cambiado", @@ -628,5 +628,13 @@ "user_already_exists": "El usuario {user} ya existe", "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación.", "app_install_failed": "No se pudo instalar {app}", - "app_install_script_failed": "Ha ocurrido un error en el guión de instalación de la aplicación" + "app_install_script_failed": "Ha ocurrido un error en el guión de instalación de la aplicación", + "group_cannot_edit_all_users": "El grupo «all_users» no se puede editar manualmente. Es un grupo especial destinado a contener todos los usuarios registrados en YunoHost", + "group_cannot_edit_visitors": "El grupo «visitors» no se puede editar manualmente. Es un grupo especial que representa a los visitantes anónimos", + "group_cannot_edit_primary_group": "El grupo «{group}» no se puede editar manualmente. Es el grupo primario destinado a contener solo un usuario específico.", + "log_permission_url": "Actualizar la URL relacionada con el permiso «{}»", + "migration_0011_slapd_config_will_be_overwritten": "Parece que ha editado manualmente la configuración de slapd. Para esta migración crítica, YunoHost necesita forzar la actualización de la configuración de slapd. Los archivos originales se respaldarán en {conf_backup_folder}.", + "permission_already_up_to_date": "El permiso no se ha actualizado porque las peticiones de incorporación o eliminación ya coinciden con el estado actual.", + "permission_currently_allowed_for_visitors": "Este permiso se concede actualmente a los visitantes además de otros grupos. Probablemente quiere o eliminar el permiso de «visitors» o eliminar los otros grupos a los que está otorgado actualmente.", + "permission_currently_allowed_for_all_users": "Este permiso se concede actualmente a todos los usuarios además de los otros grupos. Probablemente quiere o eliminar el permiso de «all_users» o eliminar los otros grupos a los que está otorgado actualmente." } From 88c8df2046997ddabbcf487c2a1dd5b622d29a25 Mon Sep 17 00:00:00 2001 From: advocatux Date: Mon, 21 Oct 2019 09:36:01 +0000 Subject: [PATCH 270/299] Translated using Weblate (Spanish) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index 17c2998fa..57e6de99d 100644 --- a/locales/es.json +++ b/locales/es.json @@ -627,7 +627,7 @@ "permission_cannot_remove_main": "No está permitido eliminar un permiso principal", "user_already_exists": "El usuario {user} ya existe", "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación.", - "app_install_failed": "No se pudo instalar {app}", + "app_install_failed": "No se pudo instalar {app}: {error}", "app_install_script_failed": "Ha ocurrido un error en el guión de instalación de la aplicación", "group_cannot_edit_all_users": "El grupo «all_users» no se puede editar manualmente. Es un grupo especial destinado a contener todos los usuarios registrados en YunoHost", "group_cannot_edit_visitors": "El grupo «visitors» no se puede editar manualmente. Es un grupo especial que representa a los visitantes anónimos", From fd4e89e22cbed23b24af132ede6413a90218d5e5 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Mon, 21 Oct 2019 07:44:05 +0000 Subject: [PATCH 271/299] Translated using Weblate (Esperanto) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/eo.json b/locales/eo.json index 237bdd1df..3931c27d0 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -562,6 +562,6 @@ "permission_already_up_to_date": "La permeso ne estis ĝisdatigita ĉar la petoj pri aldono/forigo jam kongruas kun la aktuala stato.", "permission_currently_allowed_for_visitors": "Ĉi tiu permeso estas nuntempe donita al vizitantoj aldone al aliaj grupoj. Vi probable volas aŭ forigi la permeson de \"vizitantoj\" aŭ forigi la aliajn grupojn al kiuj ĝi nun estas koncedita.", "permission_currently_allowed_for_all_users": "Ĉi tiu permeso estas nuntempe donita al ĉiuj uzantoj aldone al aliaj grupoj. Vi probable volas aŭ forigi la permeson \"all_users\" aŭ forigi la aliajn grupojn, kiujn ĝi nuntempe donas.", - "app_install_failed": "Ne povis instali {app}", + "app_install_failed": "Ne povis instali {app} : {error}", "app_install_script_failed": "Eraro okazis en la skripto de instalado de la app" } From d52cbd127b296fc2a64c135a47c0b73e25eacf35 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Mon, 21 Oct 2019 07:42:00 +0000 Subject: [PATCH 272/299] Translated using Weblate (French) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 9e64bcff5..90eed4bee 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -662,6 +662,6 @@ "permission_already_up_to_date": "L'autorisation n'a pas été mise à jour car les demandes d'ajout/suppression correspondent déjà à l'état actuel.", "permission_currently_allowed_for_visitors": "Cette autorisation est actuellement accordée aux visiteurs en plus d'autres groupes. Vous voudrez probablement supprimer l'autorisation \"visiteurs\" ou supprimer les autres groupes auxquels il est actuellement attribué.", "permission_currently_allowed_for_all_users": "Cette autorisation est actuellement accordée à tous les utilisateurs en plus des autres groupes. Vous voudrez probablement soit supprimer l'autorisation 'all_users', soit supprimer les autres groupes auxquels il est actuellement autorisé.", - "app_install_failed": "Impossible d'installer {app}", + "app_install_failed": "Impossible d'installer {app}: {error}", "app_install_script_failed": "Une erreur est survenue dans le script d'installation de l'application" } From 5695e4d25e1b4d9bbb5ae3a7d9e9f124128239d9 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Sun, 20 Oct 2019 14:13:21 +0000 Subject: [PATCH 273/299] Translated using Weblate (Catalan) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ca.json b/locales/ca.json index b3831d929..04ee413b9 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -616,7 +616,7 @@ "permission_cannot_remove_main": "No es permet eliminar un permís principal", "user_already_exists": "L'usuari {user} ja existeix", "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació", - "app_install_failed": "No s'ha pogut instal·lar {app}", + "app_install_failed": "No s'ha pogut instal·lar {app}: {error}", "app_install_script_failed": "Hi ha hagut un error en el script d'instal·lació de l'aplicació", "group_cannot_edit_all_users": "El grup «all_users» no es pot editar manualment. És un grup especial destinat a contenir els usuaris registrats a YunoHost", "group_cannot_edit_visitors": "El grup «visitors» no es pot editar manualment. És un grup especial que representa els visitants anònims", From bd02678275320e52a82082fb3286ba05ecb85386 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 24 Oct 2019 17:47:43 +0200 Subject: [PATCH 274/299] Refuse to add visitors to mail / xmpp / ... permission as it doesnt make sense --- locales/en.json | 1 + src/yunohost/permission.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/locales/en.json b/locales/en.json index 1d68fb334..73b5c46fb 100644 --- a/locales/en.json +++ b/locales/en.json @@ -429,6 +429,7 @@ "permission_update_failed": "Could not update permission '{permission}' : {error}", "permission_updated": "Permission '{permission:s}' updated", "permission_update_nothing_to_do": "No permissions to update", + "permission_require_account": "Permission {permission} only makes sense for users having an account, and therefore cannot be enabled for visitors.", "port_already_closed": "Port {port:d} is already closed for {ip_version:s} connections", "port_already_opened": "Port {port:d} is already opened for {ip_version:s} connections", "port_available": "Port {port:d} is available", diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 67d115bc7..226cc9050 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -100,6 +100,10 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, if "." not in permission: permission = permission + ".main" + # Refuse to add "visitors" to mail, xmpp ... they require an account to make sense. + if add and "visitors" in add and permission.split(".")[0] in SYSTEM_PERMS: + raise YunohostError('permission_require_account', permission=permission) + # Fetch currently allowed groups for this permission existing_permission = user_permission_list(full=True)["permissions"].get(permission, None) From a9317487b3fb4fd0020dfd35b73d43693f6f9338 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 24 Oct 2019 17:58:34 +0200 Subject: [PATCH 275/299] Typo in string --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 73b5c46fb..202650edb 100644 --- a/locales/en.json +++ b/locales/en.json @@ -414,7 +414,7 @@ "pattern_positive_number": "Must be a positive number", "pattern_username": "Must be lower-case alphanumeric and underscore characters only", "pattern_password_app": "Sorry, passwords can not contain the following characters: {forbidden_chars}", - "permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled'", + "permission_already_allowed": "Group '{group}' already has permission '{permission}' enabled", "permission_already_disallowed": "Group '{group}' already has permission '{permission}' disabled'", "permission_already_exist": "Permission '{permission}' already exists", "permission_already_up_to_date": "The permission was not updated because the addition/removal requests already match the current state.", From 936da7aa7948ddd9d27e1c7a5e520337b71a44e8 Mon Sep 17 00:00:00 2001 From: advocatux Date: Wed, 23 Oct 2019 09:03:22 +0000 Subject: [PATCH 276/299] Translated using Weblate (Spanish) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 104 ++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/locales/es.json b/locales/es.json index 57e6de99d..afa323269 100644 --- a/locales/es.json +++ b/locales/es.json @@ -29,10 +29,10 @@ "app_unsupported_remote_type": "Tipo remoto no soportado por la aplicación", "app_upgrade_failed": "No se pudo actualizar {app:s}", "app_upgraded": "Actualizado {app:s}", - "appslist_fetched": "Lista de aplicaciones {appslist:s} actualizada", - "appslist_removed": "Eliminada la lista de aplicaciones {appslist:s}", - "appslist_retrieve_error": "No se puede recuperar la lista remota de aplicaciones {appslist:s}: {error:s}", - "appslist_unknown": "Lista de aplicaciones {appslist:s} desconocida.", + "appslist_fetched": "Lista de aplicaciones «{appslist:s}» actualizada", + "appslist_removed": "Eliminada la lista de aplicaciones «{appslist:s}»", + "appslist_retrieve_error": "No se puede recuperar la lista remota de aplicaciones «{appslist:s}»: {error:s}", + "appslist_unknown": "Lista de aplicaciones «{appslist:s}» desconocida.", "ask_current_admin_password": "Contraseña administrativa actual", "ask_email": "Dirección de correo electrónico", "ask_firstname": "Nombre", @@ -70,7 +70,7 @@ "diagnosis_monitor_disk_error": "No se pudieron monitorizar los discos: {error}", "diagnosis_monitor_network_error": "No se pudo monitorizar la red: {error}", "diagnosis_monitor_system_error": "No se pudo monitorizar el sistema: {error}", - "diagnosis_no_apps": "Aplicación no instalada", + "diagnosis_no_apps": "No hay tal aplicación instalada", "dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, ejecute «apt-get remove bind9 && apt-get install it»", "domain_cert_gen_failed": "No se pudo generar el certificado", "domain_created": "Dominio creado", @@ -123,8 +123,8 @@ "mail_forward_remove_failed": "No se pudo eliminar el reenvío de correo «{mail:s}»", "maindomain_change_failed": "No se pudo cambiar el dominio principal", "maindomain_changed": "El dominio principal ha cambiado", - "monitor_disabled": "Desactivada la monitorización del servidor", - "monitor_enabled": "Activada la monitorización del servidor", + "monitor_disabled": "La monitorización del servidor está ahora desactivada", + "monitor_enabled": "La monitorización del servidor está ahora activada", "monitor_glances_con_failed": "No se pudo conectar al servidor de Glances", "monitor_not_enabled": "La monitorización del servidor está apagada", "monitor_period_invalid": "Período de tiempo no válido", @@ -132,9 +132,9 @@ "monitor_stats_no_update": "No hay estadísticas de monitorización para actualizar", "monitor_stats_period_unavailable": "No hay estadísticas para el período", "mountpoint_unknown": "Punto de montaje desconocido", - "mysql_db_creation_failed": "Error al crear la base de datos de MySQL", - "mysql_db_init_failed": "Error al iniciar la base de datos de MySQL", - "mysql_db_initialized": "Inicializada la base de datos MySQL", + "mysql_db_creation_failed": "No se pudo crear la base de datos MySQL", + "mysql_db_init_failed": "No se pudo inicializar la base de datos MySQL", + "mysql_db_initialized": "La base de datos MySQL está ahora inicializada", "network_check_mx_ko": "El registro DNS MX no está configurado", "network_check_smtp_ko": "El correo saliente (SMTP puerto 25) parece estar bloqueado por su red", "network_check_smtp_ok": "El correo saliente (SMTP puerto 25) no está bloqueado", @@ -144,7 +144,7 @@ "no_ipv6_connectivity": "La conexión por IPv6 no está disponible", "no_restore_script": "No se ha encontrado un script de restauración para la aplicación '{app:s}'", "not_enough_disk_space": "No hay espacio libre suficiente en «{path:s}»", - "package_not_installed": "El paquete '{pkgname}' no está instalado", + "package_not_installed": "El paquete «{pkgname}» no está instalado", "package_unexpected_error": "Ha ocurrido un error inesperado procesando el paquete '{pkgname}'", "package_unknown": "Paquete desconocido '{pkgname}'", "packages_no_upgrade": "No hay paquetes para actualizar", @@ -153,7 +153,7 @@ "path_removal_failed": "No se pudo eliminar la ruta {:s}", "pattern_backup_archive_name": "Debe ser un nombre de archivo válido con un máximo de 30 caracteres, solo se admiten caracteres alfanuméricos y los caracteres -_. (guiones y punto)", "pattern_domain": "El nombre de dominio debe ser válido (por ejemplo mi-dominio.org)", - "pattern_email": "Debe ser una dirección de correo electrónico válida (por ejemplo, alguien@dominio.org)", + "pattern_email": "Debe ser una dirección de correo electrónico válida (p.ej. alguien@example.com)", "pattern_firstname": "Debe ser un nombre válido", "pattern_lastname": "Debe ser un apellido válido", "pattern_listname": "Solo se pueden usar caracteres alfanuméricos y el guion bajo", @@ -180,7 +180,7 @@ "restore_running_hooks": "Ejecutando los ganchos de restauración…", "service_add_failed": "No se pudo añadir el servicio «{service:s}»", "service_added": "Añadido el servicio «{service:s}»", - "service_already_started": "El servicio «{service:s}» ya ha sido iniciado", + "service_already_started": "El servicio «{service:s}» ya está funcionando", "service_already_stopped": "El servicio «{service:s}» ya ha sido detenido", "service_cmd_exec_failed": "No se pudo ejecutar la orden «{command:s}»", "service_conf_file_backed_up": "Se ha realizado una copia de seguridad del archivo de configuración '{conf}' en '{backup}'", @@ -195,10 +195,10 @@ "service_conf_updated": "La configuración ha sido actualizada para el servicio '{service}'", "service_conf_would_be_updated": "La configuración podría haber sido actualizada para el servicio '{service} 1'", "service_disable_failed": "No se pudo desactivar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", - "service_disabled": "Desactivado el servicio «{service:s}»", + "service_disabled": "El servicio «{service:s}» ha sido desactivado", "service_enable_failed": "No se pudo activar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", - "service_enabled": "Activado el servicio «{service:s}»", - "service_no_log": "No hay ningún registro para el servicio '{service:s}'", + "service_enabled": "El servicio «{service:s}» ha sido desactivado", + "service_no_log": "No hay ningún registro que mostrar para el servicio «{service:s}»", "service_regenconf_dry_pending_applying": "Comprobando configuración pendiente que podría haber sido aplicada al servicio '{service}'...", "service_regenconf_failed": "No se puede regenerar la configuración para el servicio(s): {services}", "service_regenconf_pending_applying": "Aplicando la configuración pendiente para el servicio '{service}'...", @@ -208,7 +208,7 @@ "service_started": "Iniciado el servicio «{service:s}»", "service_status_failed": "No se pudo determinar el estado del servicio «{service:s}»", "service_stop_failed": "No se pudo detener el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", - "service_stopped": "Detenido el servicio «{service:s}»", + "service_stopped": "El servicio «{service:s}» se detuvo", "service_unknown": "Servicio desconocido '{service:s}'", "ssowat_conf_generated": "Generada la configuración de SSOwat", "ssowat_conf_updated": "Actualizada la configuración de SSOwat", @@ -238,7 +238,7 @@ "user_updated": "Cambiada la información de usuario", "yunohost_already_installed": "YunoHost ya está instalado", "yunohost_ca_creation_failed": "No se pudo crear la autoridad de certificación", - "yunohost_configured": "YunoHost está configurado", + "yunohost_configured": "YunoHost está ahora configurado", "yunohost_installing": "Instalando YunoHost…", "yunohost_not_installed": "YunoHost no está correctamente instalado. Ejecute «yunohost tools postinstall»", "ldap_init_failed_to_create_admin": "La inicialización de LDAP no pudo crear el usuario «admin»", @@ -272,25 +272,25 @@ "certmanager_acme_not_configured_for_domain": "El certificado para el dominio «{domain:s}» no parece que esté instalado correctamente. Ejecute primero «cert-install» para este dominio.", "certmanager_http_check_timeout": "Tiempo de espera agotado cuando el servidor intentaba conectarse consigo mismo a través de HTTP usando una dirección IP pública (dominio «{domain:s}» con IP «{ip:s}»). Puede que esté experimentando un problema de redirección («hairpinning»), o que el cortafuegos o el enrutador de su servidor esté mal configurado.", "certmanager_couldnt_fetch_intermediate_cert": "Tiempo de espera agotado intentando obtener el certificado intermedio de Let's Encrypt. Cancelada la instalación o renovación del certificado. Vuelva a intentarlo más tarde.", - "appslist_retrieve_bad_format": "No se pudo leer la lista de aplicaciones obtenida {appslist:s}", + "appslist_retrieve_bad_format": "No se pudo leer la lista de aplicaciones obtenida «{appslist:s}»", "domain_hostname_failed": "No se pudo establecer un nuevo nombre de anfitrión («hostname»). Esto podría causar problemas más tarde (no es seguro... podría ir bien).", "yunohost_ca_creation_success": "Creada la autoridad de certificación local.", "app_already_installed_cant_change_url": "Esta aplicación ya está instalada. No se puede cambiar el URL únicamente mediante esta función. Compruebe si está disponible la opción `app changeurl`.", "app_change_no_change_url_script": "La aplicacion {app_name:s} aún no permite cambiar su URL, es posible que deba actualizarla.", "app_change_url_failed_nginx_reload": "No se pudo recargar NGINX. Esta es la salida de «nginx -t»:\n{nginx_errors:s}", "app_change_url_identical_domains": "El antiguo y nuevo dominio/url_path son idénticos ('{domain:s} {path:s}'), no se realizarán cambios.", - "app_change_url_no_script": "Esta aplicación «{app_name:s}» aún no permite la modificación de URLs. Quizás debería actualizarla.", + "app_change_url_no_script": "La aplicación «{app_name:s}» aún no permite la modificación de URLs. Quizás debería actualizarla.", "app_change_url_success": "El URL de la aplicación {app:s} es ahora {domain:s} {path:s}", "app_location_unavailable": "Este URL o no está disponible o está en conflicto con otra(s) aplicación(es) instalada(s):\n{apps:s}", "app_already_up_to_date": "La aplicación {app:s} ya está actualizada", "appslist_name_already_tracked": "Ya existe una lista de aplicaciones registradas con el nombre {name:s}.", "appslist_url_already_tracked": "Ya existe una lista de aplicaciones registradas con el URL {url:s}.", - "appslist_migrating": "Migrando la lista de aplicaciones {appslist:s}…", - "appslist_could_not_migrate": "¡No se pudo migrar la lista de aplicaciones {appslist:s}! No se pudo analizar el URL… El antiguo trabajo de cron se mantuvo en {bkp_file:s}.", + "appslist_migrating": "Migrando la lista de aplicaciones «{appslist:s}»…", + "appslist_could_not_migrate": "¡No se pudo migrar la lista de aplicaciones «{appslist:s}»! No se pudo analizar el URL… El antiguo trabajo de cron se mantuvo en {bkp_file:s}.", "appslist_corrupted_json": "No se pudieron cargar las listas de aplicaciones. Parece que {filename:s} está dañado.", "invalid_url_format": "Algo va mal con el URL", "app_upgrade_some_app_failed": "No se pudieron actualizar algunas aplicaciones", - "app_make_default_location_already_used": "No puede hacer que la aplicación «{app}» sea la predeterminada en el dominio, {domain} ya está siendo usado por otra aplicación «{other_app}»", + "app_make_default_location_already_used": "No puede hacer que la aplicación «{app}» sea la predeterminada en el dominio, «{domain}» ya está siendo usado por otra aplicación «{other_app}»", "app_upgrade_app_name": "Actualizando ahora {app}…", "ask_path": "Camino", "backup_abstract_method": "Este método de respaldo aún no se ha implementado", @@ -301,7 +301,7 @@ "backup_archive_mount_failed": "No se pudo montar el archivo de respaldo", "backup_archive_system_part_not_available": "La parte del sistema «{part:s}» no está disponible en esta copia de seguridad", "backup_archive_writing_error": "No se pudieron añadir los archivos «{source:s}» (llamados en el archivo «{dest:s}») para ser respaldados en el archivo comprimido «{archive:s}»", - "backup_ask_for_copying_if_needed": "No se pudieron preparar algunos archivos para la copia de seguridad usando el método que evita desperdiciar espacio temporalmente en el sistema. Para hacer la copia de seguridad, {size:s}MB se usarán temporalmente. ¿Está de acuerdo?", + "backup_ask_for_copying_if_needed": "¿Quiere realizar la copia de seguridad usando {size:s} MB temporalmente? (Se usa este modo ya que algunos archivos no se pudieron preparar usando un método más eficiente.)", "backup_borg_not_implemented": "El método de respaldo de Borg aún no ha sido implementado", "backup_cant_mount_uncompress_archive": "No se pudo montar el archivo descomprimido como protegido contra escritura", "backup_copying_to_organize_the_archive": "Copiando {size:s}MB para organizar el archivo", @@ -331,22 +331,22 @@ "update_apt_cache_warning": "Algo fue mal durante la actualización de la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", "update_apt_cache_failed": "No se pudo actualizar la caché de APT (gestor de paquetes de Debian). Aquí tiene un volcado de las líneas de sources.list que podría ayudarle a identificar las líneas problemáticas:\n{sourceslist}", "tools_upgrade_special_packages_completed": "Actualización de paquetes de YunoHost completada.\nPulse [Intro] para regresar a la línea de órdenes", - "tools_upgrade_special_packages_explanation": "Esta acción terminará pero la actualización especial real continuará en segundo plano. No inicie ninguna otra acción en su servidor en aproximadamente 10 minutos (dependiendo de la velocidad de su hardware). Una vez que esté hecho, podría tener que volver a iniciar sesión en la administración web. El registro de actualización estará disponible en Herramientas → Registro (en la página de administración web) o mediante «yunohost log list» (desde la línea de órdenes).", + "tools_upgrade_special_packages_explanation": "Esta acción terminará pero la actualización especial real continuará en segundo plano. No inicie ninguna otra acción en su servidor en aproximadamente 10 minutos (dependiendo de la velocidad de su hardware). Una vez hecho, podría tener que volver a iniciar sesión en la administración web. El registro de actualización estará disponible en Herramientas → Registro (en la página de administración web) o mediante «yunohost log list» (desde la línea de órdenes).", "tools_upgrade_special_packages": "Actualizando ahora paquetes «especiales» (relacionados con YunoHost)…", "tools_upgrade_regular_packages_failed": "No se pudieron actualizar los paquetes: {packages_list}", "tools_upgrade_regular_packages": "Actualizando ahora paquetes «normales» (no relacionados con YunoHost)…", - "tools_upgrade_cant_unhold_critical_packages": "No se pudieron liberar los paquetes críticos…", + "tools_upgrade_cant_unhold_critical_packages": "No se pudo liberar los paquetes críticos…", "tools_upgrade_cant_hold_critical_packages": "No se pudieron retener los paquetes críticos…", "tools_upgrade_cant_both": "No se puede actualizar el sistema y las aplicaciones al mismo tiempo", "tools_upgrade_at_least_one": "Especifique «--apps», o «--system»", "tools_update_failed_to_app_fetchlist": "No se pudo actualizar la lista de aplicaciones de YunoHost porque: {error}", "this_action_broke_dpkg": "Esta acción rompió dpkg/APT(los gestores de paquetes del sistema)… Puede tratar de solucionar este problema conectando mediante SSH y ejecutando `sudo dpkg --configure -a`.", "system_groupname_exists": "El nombre de grupo ya existe en el grupo del sistema", - "service_reloaded_or_restarted": "Recargado o reiniciado el servicio «{service:s}»", + "service_reloaded_or_restarted": "El servicio «{service:s}» ha sido recargado o reiniciado", "service_reload_or_restart_failed": "No se pudo recargar o reiniciar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", "service_restarted": "Reiniciado el servicio «{service:s}»", "service_restart_failed": "No se pudo reiniciar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", - "service_reloaded": "Recargado el servicio «{service:s}»", + "service_reloaded": "El servicio «{service:s}» ha sido recargado", "service_reload_failed": "No se pudo recargar el servicio «{service:s}»\n\nRegistro de servicios recientes:{logs:s}", "service_regen_conf_is_deprecated": "¡«yunohost service regen-conf» está obsoleto! Use «yunohost tools regen-conf» en su lugar.", "service_description_yunohost-firewall": "Gestiona los puertos de conexiones abiertos y cerrados a los servicios", @@ -360,7 +360,7 @@ "service_description_php7.0-fpm": "Ejecuta aplicaciones escritas en PHP con NGINX", "service_description_nslcd": "Maneja la conexión del intérprete de órdenes («shell») de usuario de YunoHost", "service_description_nginx": "Sirve o proporciona acceso a todos los sitios web alojados en su servidor", - "service_description_mysql": "Almacena los datos de las aplicaciones (base de datos SQL)", + "service_description_mysql": "Almacena los datos de la aplicación (base de datos SQL)", "service_description_metronome": "Gestionar las cuentas XMPP de mensajería instantánea", "service_description_glances": "Supervisa la información del sistema en su servidor", "service_description_fail2ban": "Protege contra ataques de fuerza bruta y otras clases de ataques desde Internet", @@ -417,7 +417,7 @@ "migrations_running_forward": "Ejecutando migración {id}…", "migrations_pending_cant_rerun": "Esas migraciones están aún pendientes, así que no se pueden volver a ejecutar: {ids}", "migrations_not_pending_cant_skip": "Esas migraciones no están pendientes, así que no pueden ser omitidas: {ids}", - "migrations_no_such_migration": "No hay ninguna migración llamada {id}", + "migrations_no_such_migration": "No hay ninguna migración llamada «{id}»", "migrations_no_migrations_to_run": "No hay migraciones que ejecutar", "migrations_need_to_accept_disclaimer": "Para ejecutar la migración {id} debe aceptar el siguiente descargo de responsabilidad:\n---\n{disclaimer}\n---\nSi acepta ejecutar la migración, vuelva a ejecutar la orden con la opción «--accept-disclaimer».", "migrations_must_provide_explicit_targets": "Necesita proporcionar objetivos explícitos al usar «--skip» or «--force-rerun»", @@ -426,30 +426,30 @@ "migrations_list_conflict_pending_done": "No puede usar «--previous» y «--done» al mismo tiempo.", "migrations_exclusive_options": "«--auto», «--skip», and «--force-rerun» son opciones mutuamente excluyentes.", "migrations_failed_to_load_migration": "No se pudo cargar la migración {id}: {error}", - "migrations_dependencies_not_satisfied": "No se puede ejecutar la migración {id} porque primero necesita ejecutar estas migraciones: {dependencies_id}", - "migrations_cant_reach_migration_file": "No se pudo acceder los archivos de migración en la ruta %s", + "migrations_dependencies_not_satisfied": "Ejecutar estas migraciones: «{dependencies_id}» antes de migrar {id}.", + "migrations_cant_reach_migration_file": "No se pudo acceder a los archivos de migración en la ruta «%s»", "migrations_already_ran": "Esas migraciones ya se han realizado: {ids}", "migration_0011_update_LDAP_schema": "Actualizando el esquema de LDAP…", "migration_0011_update_LDAP_database": "Actualizando la base de datos de LDAP…", "migration_0011_rollback_success": "Sistema revertido.", - "migration_0011_migration_failed_trying_to_rollback": "Migración fallida… intentando revertir el sistema.", + "migration_0011_migration_failed_trying_to_rollback": "No se pudo migrar… intentando revertir el sistema.", "migration_0011_migrate_permission": "Migrando permisos desde la configuración de las aplicaciones a LDAP…", "migration_0011_LDAP_update_failed": "No se pudo actualizar LDAP. Error: {error:s}", "migration_0011_LDAP_config_dirty": "Parece que ha personalizado la configuración de LDAP. Para esta migración se necesita actualizar la configuración de LDAP.\nNecesita guardar su configuración actual, reiniciar la configuración original ejecutando «yunohost tools regen-conf -f» y reintentar la migración", - "migration_0011_done": "Migración correcta. Ahora puede gestionar los grupos de usuarios.", + "migration_0011_done": "Migración finalizada. Ahora puede gestionar los grupos de usuarios.", "migration_0011_create_group": "Creando un grupo para cada usuario…", - "migration_0011_can_not_backup_before_migration": "Falló el respaldo del sistema antes de la migración. Fallo de migración. Error: {error:s}", + "migration_0011_can_not_backup_before_migration": "El respaldo del sistema no se pudo completar antes de que la migración fallase. Error: {error:s}", "migration_0011_backup_before_migration": "Creando un respaldo de la base de datos de LDAP y de la configuración de las aplicaciones antes de la migración real.", "migration_0009_not_needed": "La migración ya ocurrió de algún modo… (?) Omitiendo.", - "migration_0008_no_warning": "No se ha detectado ningún riesgo importante con respecto a la anulación de su configuración SSH ¡sin embargo uno nunca puede estar absolutamente seguro ;)! Ejecute la migración para anularla. Por otra parte, puede omitir la migración aunque no esté recomendado.", - "migration_0008_warning": "Si entiende esos avisos y permite a YunoHost anular su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", + "migration_0008_no_warning": "Ignorar su configuración SSH debería ser seguro ¡aunque esto no se puede prometer! Ejecute la migración para ignorarla. Por otra parte puede omitir la migración, aunque no se recomienda.", + "migration_0008_warning": "Si entiende esos avisos y quiere que YunoHost ignore su configuración actual, ejecute la migración. Por otra parte puede omitir la migración, aunque no se recomienda.", "migration_0008_dsa": "• Se desactivará la clave DSA. Así que podría tener que anular un aviso espeluznante de su cliente SSH y volver a comprobar la huella de su servidor;", "migration_0008_root": "• No podrá conectarse como «root» a través de SSH. En su lugar debe usar el usuario «admin»;", "migration_0008_port": "• Tendrá que conectarse usando el puerto 22 en vez de su actual puerto SSH personalizado. No dude en reconfigurarlo;", "migration_0008_general_disclaimer": "Para mejorar la seguridad de su servidor, es recomendable permitir a YunoHost gestionar la configuración de SSH. Su actual configuración de SSH difiere de la recomendación. Si permite a YunoHost reconfigurarla, la manera en la que conecta con su servidor a través de SSH cambiará así:", "migration_0007_cannot_restart": "No se puede reiniciar SSH después de intentar cancelar la migración número 6.", - "migration_0007_cancelled": "YunoHost no ha podido mejorar el modo en el que se gestiona su configuración de SSH.", - "migration_0006_disclaimer": "YunoHost espera ahora que las contraseñas de «admin» y «root» estén sincronizadas. Al ejecutar esta migración, su contraseña de «root» será reemplazada por la contraseña de administración.", + "migration_0007_cancelled": "No se pudo mejorar el modo en el que se gestiona su configuración de SSH.", + "migration_0006_disclaimer": "YunoHost espera ahora que las contraseñas de «admin» y «root» estén sincronizadas. Esta migración reemplaza su contraseña de «root» por la contraseña de «admin».", "migration_0005_not_enough_space": "Tenga suficiente espacio libre disponible en {path} para ejecutar la migración.", "migration_0005_postgresql_96_not_installed": "⸘PostgreSQL 9.4 está instalado pero no PostgreSQL 9.6‽ Algo raro podría haber ocurrido en su sistema:(…", "migration_0005_postgresql_94_not_installed": "PostgreSQL no estaba instalado en su sistema. Nada que hacer.", @@ -502,12 +502,12 @@ "log_user_delete": "Eliminar usuario «{}»", "log_user_create": "Añadir usuario «{}»", "log_regen_conf": "Regenerar la configuración del sistema «{}»", - "log_letsencrypt_cert_renew": "Renovar el certificado «{}» de Let's encrypt", + "log_letsencrypt_cert_renew": "Renovar el certificado «{}» de Let's Encrypt", "log_selfsigned_cert_install": "Instalar certificado autofirmado en el dominio «{}»", "log_permission_update": "Actualizar permiso «{}» para la aplicación «{}»", "log_permission_remove": "Eliminar permiso «{}»", "log_permission_add": "Añadir el permiso «{}» para la aplicación «{}»", - "log_letsencrypt_cert_install": "Instalar un certificado de Let's encrypt en el dominio «{}»", + "log_letsencrypt_cert_install": "Instalar un certificado de Let's Encrypt en el dominio «{}»", "log_dyndns_update": "Actualizar la IP asociada con su subdominio de YunoHost «{}»", "log_dyndns_subscribe": "Subscribirse a un subdomino de YunoHost «{}»", "log_domain_remove": "Eliminar el dominio «{}» de la configuración del sistema", @@ -577,12 +577,12 @@ "domain_dns_conf_is_just_a_recommendation": "Esta orden muestra la configuración *recomendada*. No configura el DNS en realidad. Es su responsabilidad configurar la zona de DNS en su registrador según esta recomendación.", "dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento porque otro programa parece que está usando el bloqueo de dpkg (el gestor de paquetes del sistema)", "dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/apt (los gestores de paquetes del sistema) parecen estar en un estado roto... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo dpkg --configure -a`.", - "confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de YunoHost. Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", - "confirm_app_install_danger": "¡PELIGRO! ¡Esta aplicación es conocida por ser aún experimental (o no funciona explícitamente)! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema... Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", + "confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de YunoHost. Instalar aplicaciones de terceros podría comprometer la integridad y seguridad de su sistema. Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema… Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", + "confirm_app_install_danger": "¡PELIGRO! ¡Esta aplicación es conocida por ser aún experimental (o no funciona explícitamente)! Probablemente NO debería instalarla salvo que sepa lo que está haciendo. No tendrá NINGUNA AYUDA si esta aplicación no funciona o rompe su sistema… Si está dispuesto a aceptar ese riesgo de todas formas, escriba «{answers:s}»", "confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers:s}] ", "backup_unable_to_organize_files": "No se pudo usar el método rápido de organización de los archivos en el archivo", "backup_permission": "Permiso de respaldo para la aplicación {app:s}", - "backup_output_symlink_dir_broken": "Tiene un enlace simbólico roto en vez del directorio «{path:s}» de su archivo. Puede que tenga una configuración específica para respaldar sus datos en otro sistema de archivos, en este caso probablemente olvidó remontar o conectar su disco duro o clave USB.", + "backup_output_symlink_dir_broken": "El directorio de su archivo «{path:s}» es un enlace simbólico roto. Tal vez olvidó (re)montarlo o conectarlo al medio de almacenamiento al que apunta.", "backup_mount_archive_for_restore": "Preparando el archivo para la restauración…", "backup_method_tar_finished": "Creado el archivo TAR de respaldo", "backup_method_custom_finished": "Terminado el método «{method:s}» de respaldo personalizado", @@ -595,16 +595,16 @@ "apps_permission_restoration_failed": "Otorgar el permiso «{permission:s}» para restaurar {app:s}", "apps_permission_not_found": "No se han encontrado permisos para las aplicaciones instaladas", "app_upgrade_several_apps": "Las siguientes aplicaciones se actualizarán: {apps}", - "app_start_restore": "Restaurando aplicación {app}…", - "app_start_backup": "Obteniendo archivos para el respaldo de {app}…", - "app_start_remove": "Eliminando aplicación {app}…", - "app_start_install": "Instalando aplicación {app}…", + "app_start_restore": "Restaurando aplicación «{app}»…", + "app_start_backup": "Obteniendo archivos para el respaldo de «{app}»…", + "app_start_remove": "Eliminando aplicación «{app}»…", + "app_start_install": "Instalando aplicación «{app}»…", "app_not_upgraded": "Error al actualizar la aplicación «{failed_app}» y como consecuencia se han cancelado las actualizaciones de las siguientes aplicaciones: {apps}", - "app_action_cannot_be_ran_because_required_services_down": "Esta aplicación necesita algunos servicios que no están funcionando ahora. Antes de continuar, debería intentar reiniciar los siguientes servicios (y posiblemente investigar por qué no funcionan): {services}", + "app_action_cannot_be_ran_because_required_services_down": "Estos servicios necesarios deberían estar funcionando para ejecutar esta acción: {services}. Pruebe a reiniciarlos para continuar (y posiblemente investigar por qué están caídos).", "already_up_to_date": "Nada que hacer. Todo está actualizado.", "admin_password_too_long": "Elija una contraseña de menos de 127 caracteres", "aborting": "Cancelando.", - "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque la aplicación anterior no se pudo actualizar", + "app_upgrade_stopped": "Se ha detenido la actualización de todas las aplicaciones para prevenir un posible daño porque una aplicación no se pudo actualizar", "app_action_broke_system": "Esta acción parece que ha roto estos importantes servicios: {services}", "operation_interrupted": "¿Ha sido interrumpida la operación manualmente?", "apps_already_up_to_date": "Todas las aplicaciones están ya actualizadas", @@ -625,8 +625,8 @@ "permission_already_allowed": "El grupo «{group}» ya tiene el permiso «{permission}» activado", "permission_already_disallowed": "El grupo «{group}» ya tiene el permiso «{permission}» desactivado", "permission_cannot_remove_main": "No está permitido eliminar un permiso principal", - "user_already_exists": "El usuario {user} ya existe", - "app_full_domain_unavailable": "Lamentablemente esta aplicación necesita un dominio completo para ser instalada pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Una solución posible es añadir y usar un subdominio dedicado a esta aplicación.", + "user_already_exists": "El usuario «{user}» ya existe", + "app_full_domain_unavailable": "Lamentablemente esta aplicación tiene que instalarse en un dominio propio pero ya hay otras aplicaciones instaladas en el dominio «{domain}». Podría usar un subdomino dedicado a esta aplicación en su lugar.", "app_install_failed": "No se pudo instalar {app}: {error}", "app_install_script_failed": "Ha ocurrido un error en el guión de instalación de la aplicación", "group_cannot_edit_all_users": "El grupo «all_users» no se puede editar manualmente. Es un grupo especial destinado a contener todos los usuarios registrados en YunoHost", From 345747d2af3fd54599e6592b2a27820bfa3b3526 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Thu, 24 Oct 2019 12:27:11 +0000 Subject: [PATCH 277/299] Translated using Weblate (Esperanto) Currently translated at 86.6% (485 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/eo.json b/locales/eo.json index 3931c27d0..c78bf6269 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -56,7 +56,7 @@ "backup_actually_backuping": "Krei rezervan ar archiveivon el la kolektitaj dosieroj …", "backup_method_borg_finished": "Sekurkopio en Borg finiĝis", "appslist_removed": "{appslist:s} aplika listo forigita", - "app_change_url_no_script": "Ĉi tiu apliko '{app_name:s}' ankoraŭ ne subtenas URL-modifon. Eble vi devus altgradigi ĝin.", + "app_change_url_no_script": "La app '{app_name:s}' ankoraŭ ne subtenas URL-modifon. Eble vi devus altgradigi ĝin.", "app_start_install": "Instalanta aplikon {app} …", "backup_created": "Sekurkopio kreita", "app_make_default_location_already_used": "Ne povas igi la aplikon '{app}' defaŭlta sur la domajno, {domain} jam uziĝas de la alia app '{other_app}'", @@ -83,7 +83,7 @@ "backup_cant_mount_uncompress_archive": "Ne povis munti la nekompresitan ar archiveivon kiel protektita kontraŭ skribo", "appslist_retrieve_bad_format": "Ne povis legi la elprenitan liston {appslist:s}", "appslist_corrupted_json": "Ne povis ŝarĝi la aplikajn listojn. Ĝi aspektas kiel {filename:s} estas damaĝita.", - "app_action_cannot_be_ran_because_required_services_down": "Ĉi tiu app postulas iujn servojn, kiuj nuntempe malleviĝas. Antaŭ ol daŭrigi, vi provu rekomenci la jenajn servojn (kaj eventuale esploru kial ili malsukcesas): {services}", + "app_action_cannot_be_ran_because_required_services_down": "Ĉi tiuj postulataj servoj devas funkcii por funkciigi ĉi tiun agon: {services}. Provu rekomenci ilin por daŭrigi (kaj eble esploru, kial ili malsupreniras).", "backup_copying_to_organize_the_archive": "Kopiante {size:s} MB por organizi la ar archiveivon", "backup_output_directory_forbidden": "Elektu malsaman elirejan dosierujon. Sekurkopioj ne povas esti kreitaj en sub-dosierujoj / bin, / boot, / dev, / ktp, / lib, / root, / run, / sbin, / sys, / usr, / var aŭ /home/yunohost.backup/archives", "appslist_could_not_migrate": "Ne povis migri la liston de aplikoj {appslist:s}! Ne eblis analizi la URL ... La malnova cron-laboro konserviĝis en {bkp_file:s}.", From 258209bcd08406fcf55d0d8361e2d2cd6db0691e Mon Sep 17 00:00:00 2001 From: amirale qt Date: Thu, 24 Oct 2019 06:33:17 +0000 Subject: [PATCH 278/299] Translated using Weblate (French) Currently translated at 100.0% (560 of 560 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 82 ++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 90eed4bee..37e5f95c1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -30,10 +30,10 @@ "app_unsupported_remote_type": "Ce type de commande à distance utilisé pour cette application n'est pas supporté", "app_upgrade_failed": "Impossible de mettre à jour {app:s}", "app_upgraded": "{app:s} mis à jour", - "appslist_fetched": "La liste d’applications mise à jour {appslist:s}", - "appslist_removed": "La liste d’applications {appslist:s} a été supprimée", - "appslist_retrieve_error": "Impossible de récupérer la liste d’applications distante {appslist:s} : {error:s}", - "appslist_unknown": "La liste d’applications {appslist:s} est inconnue.", + "appslist_fetched": "La liste d’applications mise à jour '{appslist:s}'", + "appslist_removed": "La liste d'applications '{appslist:s}' a été supprimée", + "appslist_retrieve_error": "Impossible de récupérer la liste d’applications distante '{appslist:s}' : {error:s}", + "appslist_unknown": "La liste d’applications '{appslist:s}' est inconnue.", "ask_current_admin_password": "Mot de passe d’administration actuel", "ask_email": "Adresse de courriel", "ask_firstname": "Prénom", @@ -124,18 +124,18 @@ "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "maindomain_change_failed": "Impossible de modifier le domaine principal", "maindomain_changed": "Le domaine principal modifié", - "monitor_disabled": "La supervision du serveur a été désactivé", - "monitor_enabled": "La supervision du serveur a été activé", + "monitor_disabled": "Surveillance du serveur est maintenant arrêté", + "monitor_enabled": "La supervision du serveur est maintenant allumée", "monitor_glances_con_failed": "Impossible de se connecter au serveur Glances", "monitor_not_enabled": "Le suivi de l’état du serveur n’est pas activé", "monitor_period_invalid": "Période de temps incorrecte", - "monitor_stats_file_not_found": "Le fichier de statistiques est introuvable", + "monitor_stats_file_not_found": "Impossible de trouver le fichier de statistiques", "monitor_stats_no_update": "Aucune donnée de l’état du serveur à mettre à jour", "monitor_stats_period_unavailable": "Aucune statistique n’est disponible pour la période", "mountpoint_unknown": "Point de montage inconnu", "mysql_db_creation_failed": "Impossible de créer la base de données MySQL", - "mysql_db_init_failed": "Impossible d’initialiser la base de données MySQL", - "mysql_db_initialized": "La base de données MySQL a été initialisée", + "mysql_db_init_failed": "Impossible d'initialiser la base de données MySQL", + "mysql_db_initialized": "La base de données MySQL est maintenant initialisée", "network_check_mx_ko": "L’enregistrement DNS MX n’est pas défini", "network_check_smtp_ko": "Le trafic courriel sortant (port 25 SMTP) semble bloqué par votre réseau", "network_check_smtp_ok": "Le trafic courriel sortant (port 25 SMTP) n’est pas bloqué", @@ -155,7 +155,7 @@ "path_removal_failed": "Impossible de supprimer le chemin {:s}", "pattern_backup_archive_name": "Doit être un nom de fichier valide avec un maximum de 30 caractères, et composé de caractères alphanumériques et -_. uniquement", "pattern_domain": "Doit être un nom de domaine valide (ex : mon-domaine.fr)", - "pattern_email": "Doit être une adresse de courriel valide (ex. : pseudo@domaine.fr)", + "pattern_email": "Doit être une adresse de courriel valide (ex. : pseudo@example.com)", "pattern_firstname": "Doit être un prénom valide", "pattern_lastname": "Doit être un nom valide", "pattern_listname": "Doit être composé uniquement de caractères alphanumériques et de tirets bas (aussi appelé tiret du 8 ou underscore)", @@ -183,7 +183,7 @@ "service_add_configuration": "Ajout du fichier de configuration {file:s}", "service_add_failed": "Impossible d’ajouter le service '{service:s}'", "service_added": "Le service '{service:s}' ajouté", - "service_already_started": "Le service '{service:s}' est déjà démarré", + "service_already_started": "Le service '{service:s}' est déjà en cours d'exécution", "service_already_stopped": "Le service '{service:s}' est déjà arrêté", "service_cmd_exec_failed": "Impossible d’exécuter la commande '{command:s}'", "service_conf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé dans '{backup}'", @@ -204,7 +204,7 @@ "service_disabled": "Le service '{service:s}' a été désactivé", "service_enable_failed": "Impossible d’activer le service '{service:s}'\n\nJournaux historisés récents : {logs:s}", "service_enabled": "Le service '{service:s}' a été activé", - "service_no_log": "Aucun journal historisé à afficher pour le service '{service:s}'", + "service_no_log": "Aucun journal à afficher pour le service '{service:s}'", "service_regenconf_dry_pending_applying": "Vérification des configurations en attentes qui pourraient être appliquées au le service '{service}' …", "service_regenconf_failed": "Impossible de régénérer la configuration pour les services : {services}", "service_regenconf_pending_applying": "Application des configurations en attentes pour le service '{service}' …", @@ -246,9 +246,9 @@ "user_updated": "L’utilisateur a été modifié", "yunohost_already_installed": "YunoHost est déjà installé", "yunohost_ca_creation_failed": "Impossible de créer l’autorité de certification", - "yunohost_configured": "YunoHost maintenant configuré", + "yunohost_configured": "YunoHost est maintenant configuré", "yunohost_installing": "L'installation de YunoHost est en cours …", - "yunohost_not_installed": "YunoHost n’est pas ou pas correctement installé. Veuillez exécuter 'yunohost tools postinstall'", + "yunohost_not_installed": "YunoHost n'est pas correctement installé. S'il vous plaît exécuter 'yunohost tools postinstall'", "certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)", "certmanager_domain_unknown": "Domaine {domain:s} inconnu", "certmanager_domain_cert_not_selfsigned": "Le certificat du domaine {domain:s} n’est pas auto-signé. Voulez-vous vraiment le remplacer ? (Utilisez --force pour cela)", @@ -280,13 +280,13 @@ "certmanager_domain_not_resolved_locally": "Le domaine {domain:s} ne peut être résolu depuis votre serveur YunoHost. Cela peut se produire si vous avez récemment modifié votre enregistrement DNS. Si c'est le cas, merci d’attendre quelques heures qu’il se propage. Si le problème persiste, envisager d’ajouter {domain:s} au fichier /etc/hosts. (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces vérifications.)", "certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l'adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème d’hairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.", "certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Let’s Encrypt. L’installation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.", - "appslist_retrieve_bad_format": "Impossible de lire la liste des applications extraites {appslist: s}", + "appslist_retrieve_bad_format": "Impossible de lire la liste des applications extraites '{appslist: s}'", "domain_hostname_failed": "Échec de l’utilisation d’un nouveau nom d’hôte. Cela pourrait causer des soucis plus tard (peut-être que ça n’en causera pas).", "yunohost_ca_creation_success": "L’autorité de certification locale créée.", - "appslist_name_already_tracked": "Il y a déjà une liste d’applications enregistrée avec le nom {name:s} existe déjà.", + "appslist_name_already_tracked": "Une liste d'applications enregistrées portant le nom {name:s} existe déjà.", "appslist_url_already_tracked": "Il y a déjà une liste d’applications enregistrée avec l’URL {url:s}.", - "appslist_migrating": "Migration de la liste d’applications {appslist:s} …", - "appslist_could_not_migrate": "Impossible de migrer la liste {appslist:s} ! Impossible d’exploiter l’URL. L’ancienne tâche programmée a été conservée dans {bkp_file:s}.", + "appslist_migrating": "Migration de la liste d’applications '{appslist:s}' …", + "appslist_could_not_migrate": "Impossible de migrer la liste '{appslist:s}' ! Impossible d’exploiter l’URL. L’ancienne tâche programmée a été conservée dans {bkp_file:s}.", "appslist_corrupted_json": "Impossible de charger la liste d’applications. Il semble que {filename:s} soit endommager.", "app_already_installed_cant_change_url": "Cette application est déjà installée. L’URL ne peut pas être changé simplement par cette fonction. Regardez si cela est disponible avec `app changeurl`.", "app_change_no_change_url_script": "L’application {app_name:s} ne prend pas encore en charge le changement d’URL, vous pourriez avoir besoin de la mettre à jour.", @@ -320,7 +320,7 @@ "backup_archive_system_part_not_available": "La partie '{part:s}' du système n’est pas disponible dans cette sauvegarde", "backup_archive_mount_failed": "Le montage de l’archive de sauvegarde a échoué", "backup_archive_writing_error": "Impossible d'ajouter des fichiers '{source:s}' (nommés dans l'archive : '{dest:s}') à sauvegarder dans l'archive compressée '{archive:s}'", - "backup_ask_for_copying_if_needed": "Certains fichiers n’ont pas pu être préparés pour être sauvegardés en utilisant la méthode qui évite temporairement de gaspiller de l’espace sur le système. Pour réaliser la sauvegarde, {size:s} Mo doivent être temporairement utilisés. Acceptez-vous ?", + "backup_ask_for_copying_if_needed": "Voulez-vous effectuer la sauvegarde en utilisant {taille:s} temporairement? (Cette méthode est utilisée car certains fichiers n'ont pas pu être préparés avec une méthode plus efficace.)", "backup_borg_not_implemented": "La méthode de sauvegarde Borg n’est pas encore implémentée", "backup_cant_mount_uncompress_archive": "Impossible de monter en lecture seule le dossier de l’archive décompressée", "backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser l’archive", @@ -350,7 +350,7 @@ "domain_dyndns_dynette_is_unreachable": "Impossible de contacter la dynette YunoHost. Soit YunoHost n’est pas correctement connecté à internet, soit le serveur de dynette est en panne. Erreur : {error}", "migrations_backward": "Migration en arrière.", "migrations_bad_value_for_target": "Nombre invalide pour le paramètre target, les numéros de migration sont 0 ou {}", - "migrations_cant_reach_migration_file": "Impossible d'accéder aux fichiers de migration sur le chemin% s", + "migrations_cant_reach_migration_file": "Impossible d'accéder aux fichiers de migration via le chemin '%s'", "migrations_current_target": "La cible de migration est {}", "migrations_error_failed_to_load_migration": "ERREUR : échec du chargement de migration {number} {name}", "migrations_forward": "Migration en avant", @@ -368,9 +368,9 @@ "ask_path": "Chemin", "dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.", "dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.", - "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine {domain} car il est déjà utilisé par l'application '{other_app}'", + "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine '{domain}' car il est déjà utilisé par l'application '{other_app}'", "app_upgrade_app_name": "Mise à jour de l’application {app} …", - "backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier d’archives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.", + "backup_output_symlink_dir_broken": "Votre répertoire d'archivage '{chemin:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.", "migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers HMAC-SHA-512 qui est plus sécurisé", @@ -473,7 +473,7 @@ "good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe d’administration. Le mot de passe doit comporter au moins 8 caractères – bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).", "good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien qu’il soit recommandé d’utiliser un mot de passe plus long (c’est-à-dire une phrase secrète) et/ou d’utiliser différents types de caractères tels que : majuscules, minuscules, chiffres et caractères spéciaux.", "migration_description_0006_sync_admin_and_root_passwords": "Synchroniser les mots de passe admin et root", - "migration_0006_disclaimer": "YunoHost s’attendra désormais à ce que les mots de passe admin et root soient synchronisés. En exécutant cette migration, votre mot de passe root sera remplacé par le mot de passe administrateur.", + "migration_0006_disclaimer": "YunoHost s'attend maintenant à ce que les mots de passe administrateur et racine soient synchronisés. Cette migration remplace votre mot de passe root par le mot de passe administrateur.", "migration_0006_done": "Votre mot de passe root a été remplacé par celui de votre adminitrateur.", "password_listed": "Ce mot de passe est l'un des mots de passe les plus utilisés dans le monde. Veuillez choisir quelque chose d'un peu plus singulier.", "password_too_simple_1": "Le mot de passe doit comporter au moins 8 caractères", @@ -485,7 +485,7 @@ "app_not_upgraded": "L’application {failed_app} n’a pas été mise à jour et par conséquence les applications suivantes n’ont pas été mises à jour : {apps}", "app_start_install": "Installation de l'application {app} …", "app_start_remove": "Suppression de l'application {app} …", - "app_start_backup": "Collecte des fichiers devant être sauvegardés pour {app} …", + "app_start_backup": "Collecte des fichiers devant être sauvegardés pour l'application {app} …", "app_start_restore": "Restauration de l'application {app} …", "app_upgrade_several_apps": "Les applications suivantes seront mises à jour : {apps}", "ask_new_domain": "Nouveau domaine", @@ -504,14 +504,14 @@ "hook_json_return_error": "Échec de la lecture au retour du script {path:s}. Erreur : {msg:s}. Contenu brut : {raw_content}", "migration_description_0007_ssh_conf_managed_by_yunohost_step1": "La configuration SSH sera gérée par YunoHost (étape 1, automatique)", "migration_description_0008_ssh_conf_managed_by_yunohost_step2": "La configuration SSH sera gérée par YunoHost (étape 2, manuelle)", - "migration_0007_cancelled": "YunoHost n'a pas réussi à améliorer la façon dont est gérée votre configuration SSH.", + "migration_0007_cancelled": "Impossible d'améliorer la gestion de votre configuration SSH.", "migration_0007_cannot_restart": "SSH ne peut pas être redémarré après avoir essayé d'annuler la migration numéro 6.", "migration_0008_general_disclaimer": "Pour améliorer la sécurité de votre serveur, il est recommandé de laisser YunoHost gérer la configuration SSH. Votre configuration SSH actuelle diffère de la configuration recommandée. Si vous laissez YunoHost la reconfigurer, la façon dont vous vous connectez à votre serveur via SSH changera comme suit :", "migration_0008_port": "- Vous devrez vous connecter en utilisant le port 22 au lieu de votre actuel port SSH personnalisé. N'hésitez pas à le reconfigurer ;", "migration_0008_root": "- Vous ne pourrez pas vous connecter en tant que root via SSH. Au lieu de cela, vous devrez utiliser l'utilisateur admin ;", "migration_0008_dsa": "- La clé DSA sera désactivée. Par conséquent, il se peut que vous ayez besoin d'invalider un avertissement effrayant de votre client SSH afin de revérifier l'empreinte de votre serveur ;", - "migration_0008_warning": "Si vous comprenez ces avertissements et que vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.", - "migration_0008_no_warning": "Aucun risque majeur n'a été identifié concernant l'écrasement de votre configuration SSH - mais nous ne pouvons pas en être absolument sûrs ;) ! Si vous acceptez de laisser YunoHost remplacer votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également passer la migration, bien que cela ne soit pas recommandé.", + "migration_0008_warning": "Si vous comprenez ces avertissements et souhaitez que YunoHost écrase votre configuration actuelle, exécutez la migration. Sinon, vous pouvez également ignorer la migration, bien que cela ne soit pas recommandé.", + "migration_0008_no_warning": "Remplacer votre configuration SSH devrait être sûr, bien que cela ne puisse être promis! Exécutez la migration pour la remplacer. Sinon, vous pouvez également ignorer la migration, bien que cela ne soit pas recommandé.", "migrations_success": "Migration {number} {name} réussie !", "pattern_password_app": "Désolé, les mots de passe ne doivent pas contenir les caractères suivants : {forbidden_chars}", "root_password_replaced_by_admin_password": "Votre mot de passe root a été remplacé par votre mot de passe administrateur.", @@ -523,7 +523,7 @@ "service_reload_or_restart_failed": "Impossible de recharger ou de redémarrer le service '{service:s}'\n\nJournaux historisés récents de ce service : {logs:s}", "service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré", "this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`.", - "app_action_cannot_be_ran_because_required_services_down": "Cette application requiert certains services qui sont actuellement arrêtés. Avant de continuer, vous devriez essayer de redémarrer les services suivants (et éventuellement rechercher pourquoi ils sont arrêtés) : {services}", + "app_action_cannot_be_ran_because_required_services_down": "Ces services requis doivent être en cours d'exécution pour exécuter cette action: {services}. Essayez de les redémarrer pour continuer (et éventuellement rechercher pourquoi ils sont en panne).", "admin_password_too_long": "Veuillez choisir un mot de passe de moins de 127 caractères", "log_regen_conf": "Régénérer les configurations du système '{}'", "migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On l'ignore.", @@ -558,8 +558,8 @@ "tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie!\nPressez [Entrée] pour revenir à la ligne de commande", "updating_app_lists": "Récupération des mises à jour des applications disponibles…", "dpkg_lock_not_available": "Cette commande ne peut être exécutée actuellement car un autre programme semble utiliser le verrou de dpkg (gestionnaire de paquets)", - "tools_upgrade_cant_unhold_critical_packages": "Impossible de dé-marquer les paquets critiques …", - "tools_upgrade_special_packages_explanation": "Cette opération prendra fin mais la mise à jour spécifique continuera en arrière-plan. Veuillez ne pas lancer d'autre action sur votre serveur dans les 10 prochaines minutes (en fonction de la vitesse de votre matériel). Une fois que c'est fait, vous devrez peut-être vous reconnecter sur le panel d'administration web. Le journal de la mise à jour sera disponible dans Outils > Log (dans le panel d'administration web) ou dans la liste des journaux YunoHost (en ligne de commande).", + "tools_upgrade_cant_unhold_critical_packages": "Impossible de conserver les paquets critiques…", + "tools_upgrade_special_packages_explanation": "Cette action se terminera, mais la mise à niveau spéciale réelle continuera en arrière-plan. Veuillez ne pas lancer d’autres actions sur votre serveur au cours des 10 prochaines minutes (en fonction de la vitesse du matériel). Une fois cela fait, vous devrez peut-être vous reconnecter à la page Webadmin. Le journal de mise à niveau sera disponible dans Outils → Journal (sur la page Webadmin) ou dans la \"liste des journaux yunohost\" (à partir de la ligne de commande).", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "apps_permission_not_found": "Aucune permission trouvée pour les applications installées", @@ -591,22 +591,22 @@ "mailbox_disabled": "La boîte aux lettres est désactivée pour l'utilisateur {user:s}", "app_action_broke_system": "Cette action semble avoir cassé des services important : {services}", "apps_already_up_to_date": "Toutes les applications sont déjà à jour", - "app_upgrade_stopped": "La mise à jour de toutes les applications a été arrêtée afin d’éviter d’éventuels dommages dus à l’échec de la mise à jour de l’application précédente", + "app_upgrade_stopped": "La mise à niveau de toutes les applications s'est arrêtée pour éviter tout dommage, car une application n'a pas pu être mise à niveau.", "migration_0011_create_group": "Créer un groupe pour chaque utilisateur…", - "migration_0011_done": "Migration réussie. Vous êtes maintenant en mesure de gérer des groupes d'utilisateurs.", + "migration_0011_done": "Migration terminée. Vous êtes maintenant en mesure de gérer des groupes d'utilisateurs.", "migrations_must_provide_explicit_targets": "Vous devez fournir des cibles explicites lorsque vous utilisez '--skip' ou '--force-rerun'", - "migrations_no_such_migration": "Il n'y a pas de migration appelée {id}", + "migrations_no_such_migration": "Il n'y a pas de migration appelée '{id}'", "migrations_pending_cant_rerun": "Ces migrations étant toujours en attente, vous ne pouvez pas les exécuter à nouveau: {ids}", "migration_description_0012_postgresql_password_to_md5_authentication": "Forcer l'authentification PostgreSQL à utiliser MD5 pour les connexions locales", "migrations_exclusive_options": "'auto', '--skip' et '--force-rerun' sont des options mutuellement exclusives.", "migrations_not_pending_cant_skip": "Ces migrations ne sont pas en attente et ne peuvent donc pas être ignorées: {ids}", - "migration_0011_can_not_backup_before_migration": "La sauvegarde du système avant la migration a échoué. La migration a échoué. Erreur: {error: s}", + "migration_0011_can_not_backup_before_migration": "La sauvegarde du système n'a pas pu être terminée avant l'échec de la migration. Erreur: {erreur:s}", "migration_0011_migrate_permission": "Migration des autorisations des paramètres des applications vers LDAP…", - "migration_0011_migration_failed_trying_to_rollback": "La migration a échoué… essayait de restauration du système.", + "migration_0011_migration_failed_trying_to_rollback": "Impossible de migrer… en essayant de restaurer le système.", "migration_0011_rollback_success": "Système restauré.", "migration_0011_update_LDAP_database": "Mise à jour de la base de données LDAP…", "system_groupname_exists": "Le nom de groupe existe déjà dans le groupe du systèmes", - "tools_update_failed_to_app_fetchlist": "Impossible de mettre à jour les applications de YunoHost car: {error}", + "tools_update_failed_to_app_fetchlist": "Impossible de mettre à jour les listes d'applications de YunoHost car: {error}", "user_already_in_group": "L'utilisateur '{user:}' est déjà dans le groupe '{group: s}'", "user_not_in_group": "L'utilisateur '{user: s}' ne fait pas partie du groupe {group: s}", "migration_0011_backup_before_migration": "Création d'une sauvegarde des paramètres de la base de données LDAP et des applications avant la migration.", @@ -620,12 +620,12 @@ "dyndns_provider_unreachable": "Impossible d’atteindre le fournisseur Dyndns {provider}: votre YunoHost n’est pas correctement connecté à Internet ou le serveur Dynette est en panne.", "migration_0011_update_LDAP_schema": "Mise à jour du schéma LDAP…", "migrations_already_ran": "Ces migrations sont déjà effectuées: {ids}", - "migrations_dependencies_not_satisfied": "Impossible d'exécuter la migration {id} car vous devez d'abord exécuter ces migrations: {dependencies_id}", + "migrations_dependencies_not_satisfied": "Exécutez ces migrations: '{dependencies_id}', avant migration {id}.", "migrations_failed_to_load_migration": "Impossible de charger la migration {id}: {error}", "migrations_running_forward": "Exécution de la migration {id}…", "migrations_success_forward": "Migration {id} terminée", "need_define_permission_before": "Redéfinissez l'autorisation à l'aide de 'yunohost user permission add -u USER' avant de supprimer un groupe autorisé", - "operation_interrupted": "L'opération a été interrompue manuellement", + "operation_interrupted": "L'opération a-t-elle été interrompue manuellement ?", "permission_already_clear": "L'autorisation '{permission: s}' est déjà vide pour l'application {app: s}", "permission_already_exist": "L'autorisation '{permission}' existe déjà", "permission_created": "Permission '{permission:s}' créée", @@ -648,12 +648,12 @@ "log_user_group_create": "Créer '{}' groupe", "log_user_permission_update": "Mise à jour des accès pour la permission '{}'", "log_user_permission_reset": "Réinitialiser la permission '{}'", - "migration_0011_failed_to_remove_stale_object": "Impossible de supprimer un objet obsolète {dn}: {error}", + "migration_0011_failed_to_remove_stale_object": "Impossible de supprimer un objet périmé {dn}: {error}", "permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée '", "permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '", "permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé", - "user_already_exists": "L'utilisateur {user} existe déjà", - "app_full_domain_unavailable": "Désolé, cette application nécessite l'installation d'un domaine complet, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Une solution possible consiste à ajouter et à utiliser un sous-domaine dédié à cette application.", + "user_already_exists": "L'utilisateur '{user}' existe déjà", + "app_full_domain_unavailable": "Désolé, cette application doit être installée sur un domaine qui lui est propre, mais d'autres applications sont déjà installées sur le domaine '{domain}'. Vous pouvez utiliser un sous-domaine dédié à cette application à la place.", "group_cannot_edit_all_users": "Le groupe 'all_users' ne peut pas être édité manuellement. C'est un groupe spécial destiné à contenir tous les utilisateurs enregistrés dans YunoHost", "group_cannot_edit_visitors": "Le groupe 'visiteurs' ne peut pas être édité manuellement. C'est un groupe spécial représentant les visiteurs anonymes", "group_cannot_edit_primary_group": "Le groupe '{group}' ne peut pas être édité manuellement. C'est le groupe principal destiné à ne contenir qu'un utilisateur spécifique.", From db7d68cf926455d22219aee65a561c6f6c5edd6f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 26 Oct 2019 14:43:54 +0200 Subject: [PATCH 279/299] Fix weird fr string.. --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 37e5f95c1..53a3f051e 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -248,7 +248,7 @@ "yunohost_ca_creation_failed": "Impossible de créer l’autorité de certification", "yunohost_configured": "YunoHost est maintenant configuré", "yunohost_installing": "L'installation de YunoHost est en cours …", - "yunohost_not_installed": "YunoHost n'est pas correctement installé. S'il vous plaît exécuter 'yunohost tools postinstall'", + "yunohost_not_installed": "YunoHost n'est pas correctement installé. Veuillez exécuter 'yunohost tools postinstall'", "certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)", "certmanager_domain_unknown": "Domaine {domain:s} inconnu", "certmanager_domain_cert_not_selfsigned": "Le certificat du domaine {domain:s} n’est pas auto-signé. Voulez-vous vraiment le remplacer ? (Utilisez --force pour cela)", From c2e5412a995fcbac02abc997314bcb501e124706 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Fri, 25 Oct 2019 09:59:46 +0000 Subject: [PATCH 280/299] Translated using Weblate (French) Currently translated at 100.0% (561 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 53a3f051e..3cb3c8fa9 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -649,7 +649,7 @@ "log_user_permission_update": "Mise à jour des accès pour la permission '{}'", "log_user_permission_reset": "Réinitialiser la permission '{}'", "migration_0011_failed_to_remove_stale_object": "Impossible de supprimer un objet périmé {dn}: {error}", - "permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée '", + "permission_already_allowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' activée", "permission_already_disallowed": "Le groupe '{group}' a déjà l'autorisation '{permission}' désactivé '", "permission_cannot_remove_main": "Supprimer une autorisation principale n'est pas autorisé", "user_already_exists": "L'utilisateur '{user}' existe déjà", @@ -663,5 +663,6 @@ "permission_currently_allowed_for_visitors": "Cette autorisation est actuellement accordée aux visiteurs en plus d'autres groupes. Vous voudrez probablement supprimer l'autorisation \"visiteurs\" ou supprimer les autres groupes auxquels il est actuellement attribué.", "permission_currently_allowed_for_all_users": "Cette autorisation est actuellement accordée à tous les utilisateurs en plus des autres groupes. Vous voudrez probablement soit supprimer l'autorisation 'all_users', soit supprimer les autres groupes auxquels il est actuellement autorisé.", "app_install_failed": "Impossible d'installer {app}: {error}", - "app_install_script_failed": "Une erreur est survenue dans le script d'installation de l'application" + "app_install_script_failed": "Une erreur est survenue dans le script d'installation de l'application", + "permission_require_account": "Permission {permission} n'a de sens que pour les utilisateurs ayant un compte et ne peut donc pas être activé pour les visiteurs." } From af703bce47dce5d4925e553798504e9e37a361a7 Mon Sep 17 00:00:00 2001 From: advocatux Date: Thu, 24 Oct 2019 16:53:15 +0000 Subject: [PATCH 281/299] Translated using Weblate (Spanish) Currently translated at 100.0% (561 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/es/ --- locales/es.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/es.json b/locales/es.json index afa323269..d216b8a9a 100644 --- a/locales/es.json +++ b/locales/es.json @@ -636,5 +636,6 @@ "migration_0011_slapd_config_will_be_overwritten": "Parece que ha editado manualmente la configuración de slapd. Para esta migración crítica, YunoHost necesita forzar la actualización de la configuración de slapd. Los archivos originales se respaldarán en {conf_backup_folder}.", "permission_already_up_to_date": "El permiso no se ha actualizado porque las peticiones de incorporación o eliminación ya coinciden con el estado actual.", "permission_currently_allowed_for_visitors": "Este permiso se concede actualmente a los visitantes además de otros grupos. Probablemente quiere o eliminar el permiso de «visitors» o eliminar los otros grupos a los que está otorgado actualmente.", - "permission_currently_allowed_for_all_users": "Este permiso se concede actualmente a todos los usuarios además de los otros grupos. Probablemente quiere o eliminar el permiso de «all_users» o eliminar los otros grupos a los que está otorgado actualmente." + "permission_currently_allowed_for_all_users": "Este permiso se concede actualmente a todos los usuarios además de los otros grupos. Probablemente quiere o eliminar el permiso de «all_users» o eliminar los otros grupos a los que está otorgado actualmente.", + "permission_require_account": "El permiso {permission} solo tiene sentido para usuarios con una cuenta y, por lo tanto, no se puede activar para visitantes." } From 6930fa82e71b07865c9549b3b7a11a46322e27e2 Mon Sep 17 00:00:00 2001 From: Filip Bengtsson Date: Fri, 25 Oct 2019 20:49:31 +0000 Subject: [PATCH 282/299] Translated using Weblate (Swedish) Currently translated at 0.2% (1 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/sv/ --- locales/sv.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/locales/sv.json b/locales/sv.json index 0967ef424..4960d43aa 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -1 +1,3 @@ -{} +{ + "password_too_simple_1": "Lösenordet måste bestå av minst åtta tecken" +} From 1b5c1c39bc5216dfb08173f0794aef94d5dac5c9 Mon Sep 17 00:00:00 2001 From: xaloc33 Date: Fri, 25 Oct 2019 17:27:26 +0000 Subject: [PATCH 283/299] Translated using Weblate (Catalan) Currently translated at 100.0% (561 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/ca/ --- locales/ca.json | 77 +++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 04ee413b9..32bbfb50f 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -12,14 +12,14 @@ "app_change_no_change_url_script": "L'aplicació {app_name:s} encara no permet canviar la seva URL, es possible que s'hagi d'actualitzar.", "app_change_url_failed_nginx_reload": "No s'ha pogut tornar a carregar NGINX. Aquí teniu el resultat de \"nginx -t\":\n{nginx_errors:s}", "app_change_url_identical_domains": "L'antic i el nou domini/camí són idèntics ('{domain:s}{path:s}'), no hi ha res per fer.", - "app_change_url_no_script": "Aquesta aplicació '{app_name:s}' encara no permet modificar la URL. Potser s'ha d'actualitzar.", + "app_change_url_no_script": "L'aplicació '{app_name:s}' encara no permet modificar la URL. Potser s'ha d'actualitzar.", "app_change_url_success": "La URL de {app:s} ara és {domain:s}{path:s}", "app_extraction_failed": "No s'han pogut extreure els fitxers d'instal·lació", "app_id_invalid": "ID de l'aplicació incorrecte", "app_incompatible": "L'aplicació {app} no és compatible amb la teva versió de YunoHost", "app_install_files_invalid": "Aquests fitxers no es poden instal·lar", "app_location_already_used": "L'aplicació «{app}» ja està instal·lada en ({path})", - "app_make_default_location_already_used": "No es pot fer l'aplicació '{app}' per defecte en el domini {domain} ja que ja és utilitzat per una altra aplicació '{other_app}'", + "app_make_default_location_already_used": "No es pot fer l'aplicació '{app}' per defecte en el domini «{domain}» ja que ja és utilitzat per una altra aplicació '{other_app}'", "app_location_install_failed": "No s'ha pogut instal·lar l'aplicació aquí ja que entra en conflicte amb l'aplicació «{other_app}» ja instal·lada a «{other_path}»", "app_location_unavailable": "Aquesta URL no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}", "app_manifest_invalid": "Hi ha algun error amb el manifest de l'aplicació: {error}", @@ -40,14 +40,14 @@ "app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions", "app_upgraded": "S'ha actualitzat {app:s}", "appslist_corrupted_json": "No s'han pogut carregar les llistes d'aplicacions. Sembla que {filename:s} està danyat.", - "appslist_could_not_migrate": "No s'ha pogut migrar la llista d'aplicacions {appslist:s}! No s'ha pogut analitzar la URL... L'antic cronjob s'ha guardat a {bkp_file:s}.", - "appslist_fetched": "S'ha actualitzat la llista d'aplicacions {appslist:s}", - "appslist_migrating": "Migrant la llista d'aplicacions {appslist:s}…", + "appslist_could_not_migrate": "No s'ha pogut migrar la llista d'aplicacions «{appslist:s}»! No s'ha pogut analitzar la URL... L'antic cronjob s'ha guardat a {bkp_file:s}.", + "appslist_fetched": "S'ha actualitzat la llista d'aplicacions «{appslist:s}»", + "appslist_migrating": "Migrant la llista d'aplicacions «{appslist:s}»…", "appslist_name_already_tracked": "Ja hi ha una llista d'aplicacions registrada amb el nom {name:s}.", - "appslist_removed": "S'ha eliminat la llista d'aplicacions {appslist:s}", - "appslist_retrieve_bad_format": "No s'ha pogut llegir la llista d'aplicacions obtinguda {appslist:s}", - "appslist_retrieve_error": "No s'ha pogut obtenir la llista d'aplicacions remota {appslist:s}: {error:s}", - "appslist_unknown": "La llista d'aplicacions {appslist:s} es desconeguda.", + "appslist_removed": "S'ha eliminat la llista d'aplicacions «{appslist:s}»", + "appslist_retrieve_bad_format": "No s'ha pogut llegir la llista d'aplicacions obtinguda «{appslist:s}»", + "appslist_retrieve_error": "No s'ha pogut obtenir la llista d'aplicacions remota «{appslist:s}»: {error:s}", + "appslist_unknown": "La llista d'aplicacions «{appslist:s}» es desconeguda.", "appslist_url_already_tracked": "Ja hi ha una llista d'aplicacions registrada amb al URL {url:s}.", "ask_current_admin_password": "Contrasenya d'administrador actual", "ask_email": "Adreça de correu electrònic", @@ -73,7 +73,7 @@ "backup_archive_open_failed": "No s'ha pogut obrir l'arxiu de la còpia de seguretat", "backup_archive_system_part_not_available": "La part «{part:s}» del sistema no està disponible en aquesta copia de seguretat", "backup_archive_writing_error": "No es poden afegir els arxius «{source:s}» (anomenats en l'arxiu «{dest:s}») a l'arxiu comprimit de la còpia de seguretat «{archive:s}»", - "backup_ask_for_copying_if_needed": "Alguns fitxers no s'han pogut preparar per la còpia de seguretat utilitzant el mètode que evita malgastar espai del sistema temporalment. Per fer la còpia de seguretat, s'han d'utilitzar {size:s}MB temporalment. Hi esteu d'acord?", + "backup_ask_for_copying_if_needed": "Voleu fer la còpia de seguretat utilitzant {size:s} MB temporalment? (S'utilitza aquest mètode ja que alguns dels fitxers no s'han pogut preparar utilitzar un mètode més eficient.)", "backup_borg_not_implemented": "El mètode de còpia de seguretat Borg encara no està implementat", "backup_cant_mount_uncompress_archive": "No es pot carregar l'arxiu descomprimit com a protegit contra escriptura", "backup_cleaning_failed": "No s'ha pogut netejar el directori temporal de la còpia de seguretat", @@ -83,10 +83,10 @@ "backup_creating_archive": "Creant l'arxiu de la còpia de seguretat…", "aborting": "Avortant.", "app_not_upgraded": "L'aplicació «{failed_app}» no s'ha pogut actualitzar, i com a conseqüència l'actualització de les següents aplicacions ha estat cancel·lada: {apps}", - "app_start_install": "instal·lant l'aplicació {app}…", - "app_start_remove": "Eliminant l'aplicació {app}…", - "app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per {app}…", - "app_start_restore": "Recuperant l'aplicació {app}…", + "app_start_install": "instal·lant l'aplicació «{app}»…", + "app_start_remove": "Eliminant l'aplicació «{app}»…", + "app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per «{app}»…", + "app_start_restore": "Recuperant l'aplicació «{app}»…", "app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}", "ask_new_domain": "Nou domini", "ask_new_path": "Nou camí", @@ -118,7 +118,7 @@ "backup_output_directory_forbidden": "Escolliu un directori de sortida different. Les còpies de seguretat no es poden crear ni dins els directoris /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ni dins els subdirectoris /home/yunohost.backup/archives", "backup_output_directory_not_empty": "Heu d'escollir un directori de sortida buit", "backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat", - "backup_output_symlink_dir_broken": "Teniu un enllaç simbòlic trencat en lloc del directori del arxiu «{path:s}». Pot ser teniu una configuració per la còpia de seguretat específica en un altre sistema de fitxers, si és el cas segurament heu oblidat muntar o connectar el disc dur o la clau USB.", + "backup_output_symlink_dir_broken": "El directori del arxiu «{path:s}» es un enllaç simbòlic trencat. Pot ser heu oblidat muntar, tornar a muntar o connectar el mitja d'emmagatzematge al que apunta.", "backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar PHP 7, pot ser que no es puguin restaurar les vostres aplicacions PHP (raó: {error:s})", "backup_running_hooks": "Executant els scripts de la còpia de seguretat…", "backup_system_part_failed": "No s'ha pogut fer la còpia de seguretat de la part \"{part:s}\" del sistema", @@ -149,7 +149,7 @@ "certmanager_self_ca_conf_file_not_found": "No s'ha trobat el fitxer de configuració per l'autoritat del certificat auto-signat (fitxer: {file:s})", "certmanager_unable_to_parse_self_CA_name": "No s'ha pogut analitzar el nom de l'autoritat del certificat auto-signat (fitxer: {file:s})", "confirm_app_install_warning": "Atenció: Aquesta aplicació funciona, però no està ben integrada amb YunoHost. Algunes característiques com la autenticació única i la còpia de seguretat/restauració poden no estar disponibles. Voleu instal·lar-la de totes maneres? [{answers:s}] ", - "confirm_app_install_danger": "PERILL! Aquesta aplicació encara és experimental (si no és que no funciona directament)! No hauríeu d'instal·lar-la a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema... Si accepteu el risc, escriviu «{answers:s}»", + "confirm_app_install_danger": "PERILL! Aquesta aplicació encara és experimental (si no és que no funciona directament)! No hauríeu d'instal·lar-la a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema… Si accepteu el risc, escriviu «{answers:s}»", "confirm_app_install_thirdparty": "PERILL! Aquesta aplicació no es part del catàleg d'aplicacions de YunoHost. La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema… Si accepteu el risc, escriviu «{answers:s}»", "custom_app_url_required": "Heu de especificar una URL per actualitzar la vostra aplicació personalitzada {app:s}", "custom_appslist_name_required": "Heu d'especificar un nom per la vostra llista d'aplicacions personalitzada", @@ -158,7 +158,7 @@ "diagnosis_monitor_disk_error": "No es poden monitorar els discs: {error}", "diagnosis_monitor_network_error": "No es pot monitorar la xarxa: {error}", "diagnosis_monitor_system_error": "No es pot monitorar el sistema: {error}", - "diagnosis_no_apps": "No hi ha cap aplicació instal·lada", + "diagnosis_no_apps": "Aquesta aplicació no està instal·lada", "admin_password_too_long": "Trieu una contrasenya de menys de 127 caràcters", "dpkg_is_broken": "No es pot fer això en aquest instant perquè dpkg/APT (els gestors de paquets del sistema) sembla estar mal configurat… Podeu intentar solucionar-ho connectant-vos per SSH i executant «sudo dpkg --configure -a».", "dnsmasq_isnt_installed": "sembla que dnsmasq no està instal·lat, executeu \"apt-get remove bind9 && apt-get install dnsmasq\"", @@ -169,7 +169,7 @@ "domain_deleted": "S'ha eliminat el domini", "domain_deletion_failed": "No s'ha pogut eliminar el domini {domini}: {error}", "domain_exists": "El domini ja existeix", - "app_action_cannot_be_ran_because_required_services_down": "Aquesta aplicació necessita serveis que estan aturats. Abans de continuar, hauríeu d'intentar arrancar de nou els serveis següents (i també investigar perquè estan aturats): {services}", + "app_action_cannot_be_ran_because_required_services_down": "Aquests serveis necessaris haurien d'estar funcionant per poder executar aquesta acció: {services} Intenteu reiniciar-los per continuar (i possiblement investigar perquè estan aturats).", "domain_dns_conf_is_just_a_recommendation": "Aquesta ordre mostra la configuració *recomanada*. En cap cas fa la configuració del DNS. És la vostra responsabilitat configurar la zona DNS en el vostre registrar en acord amb aquesta recomanació.", "domain_dyndns_already_subscribed": "Ja us heu subscrit a un domini DynDNS", "domain_dyndns_dynette_is_unreachable": "No s'ha pogut abastar la dynette YunoHost, o bé YunoHost no està connectat a internet correctament o bé el servidor dynette està caigut. Error: {error}", @@ -325,19 +325,19 @@ "migration_0005_postgresql_94_not_installed": "PostgreSQL no està instal·lat en el sistema. No hi ha res per fer.", "migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 està instal·lat, però no PostgreSQL 9.6? Alguna cosa estranya a passat en el sistema :( …", "migration_0005_not_enough_space": "Creu espai disponible en {path} per executar la migració.", - "migration_0006_disclaimer": "YunoHost esperar que les contrasenyes admin i root estiguin sincronitzades. Fent aquesta migració, la contrasenya root serà reemplaçada per la contrasenya admin.", - "migration_0007_cancelled": "YunoHost no ha pogut millorar la gestió de la configuració SSH.", + "migration_0006_disclaimer": "YunoHost esperar que les contrasenyes de admin i root estiguin sincronitzades. Aquesta migració canvia la contrasenya root per la contrasenya admin.", + "migration_0007_cancelled": "No s'ha pogut millorar la gestió de la configuració SSH.", "migration_0007_cannot_restart": "No es pot reiniciar SSH després d'haver intentat cancel·lar la migració numero 6.", "migration_0008_general_disclaimer": "Per millorar la seguretat del servidor, es recomana que sigui YunoHost qui gestioni la configuració SSH. La configuració SSH actual és diferent a la configuració recomanada. Si deixeu que YunoHost ho reconfiguri, la manera de connectar-se al servidor mitjançant SSH canviarà de la següent manera:", "migration_0008_port": "• La connexió es farà utilitzant el port 22 en lloc del port SSH personalitzat actual. Es pot reconfigurar;", "migration_0008_root": "• No es podrà connectar com a root a través de SSH. S'haurà d'utilitzar l'usuari admin per fer-ho;", "migration_0008_dsa": "• Es desactivarà la clau DSA. Per tant, es podria haver d'invalidar un missatge esgarrifós del client SSH, i tornar a verificar l'empremta digital del servidor;", - "migration_0008_warning": "Si heu entès els avisos i accepteu que YunoHost sobreescrigui la configuració actual, comenceu la migració. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", - "migration_0008_no_warning": "No s'han identificat riscs importants per sobreescriure la configuració SSH, però no es pot estar del tot segur ;)! Executetu la migració per sobreescriure-la. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", + "migration_0008_warning": "Si heu entès els avisos i voleu que YunoHost sobreescrigui la configuració actual, comenceu la migració. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", + "migration_0008_no_warning": "Hauria de ser segur sobreescriure la configuració SSH, però no es pot estar del tot segur! Executetu la migració per sobreescriure-la. Sinó, podeu saltar-vos la migració, tot i que no està recomanat.", "migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració… (?) Ometent.", "migrations_backward": "Migració cap enrere.", "migrations_bad_value_for_target": "Nombre invàlid pel paràmetre target, els nombres de migració disponibles són 0 o {}", - "migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí %s", + "migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí «%s»", "migrations_current_target": "La migració objectiu és {}", "migrations_error_failed_to_load_migration": "ERROR: no s'ha pogut carregar la migració {number} {name}", "migrations_forward": "Migració endavant", @@ -351,8 +351,8 @@ "migrations_success": "S'ha completat la migració {number} {name} amb èxit!", "migrations_to_be_ran_manually": "La migració {id} s'ha de fer manualment. Aneu a Eines → Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».", "migrations_need_to_accept_disclaimer": "Per fer la migració {id}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció «--accept-disclaimer».", - "monitor_disabled": "El monitoratge del servidor ha estat desactivat", - "monitor_enabled": "El monitoratge del servidor ha estat activat", + "monitor_disabled": "S'ha desactivat el monitoratge del servidor", + "monitor_enabled": "S'ha activat el monitoratge del sistema", "monitor_glances_con_failed": "No s'ha pogut connectar al servidor Glances", "monitor_not_enabled": "El monitoratge del servidor no està activat", "monitor_period_invalid": "Període de temps invàlid", @@ -436,7 +436,7 @@ "server_reboot_confirm": "Es reiniciarà el servidor immediatament, n'esteu segur? [{answers:s}]", "service_add_failed": "No s'ha pogut afegir el servei «{service:s}»", "service_added": "S'ha afegit el servei «{service:s}»", - "service_already_started": "Ja s'ha iniciat el servei «{service:s}»", + "service_already_started": "El servei «{service:s}» ja està funcionant", "service_already_stopped": "Ja s'ha aturat el servei «{service:s}»", "service_cmd_exec_failed": "No s'ha pogut executar l'ordre «{command:s}»", "service_description_avahi-daemon": "Permet accedir al servidor via «yunohost.local» en la xarxa local", @@ -458,9 +458,9 @@ "service_description_yunohost-api": "Gestiona les interaccions entre la interfície web de YunoHost i el sistema", "service_description_yunohost-firewall": "Gestiona els ports de connexió oberts i tancats als serveis", "service_disable_failed": "No s'han pogut deshabilitar el servei «{service:s}»\n\nRegistres recents: {logs:s}", - "service_disabled": "S'ha deshabilitat el servei {service:s}", + "service_disabled": "S'ha deshabilitat el servei «{service:s}»", "service_enable_failed": "No s'ha pogut activar el servei «{service:s}»\n\nRegistres recents: {log:s}", - "service_enabled": "S'ha activat el servei {service:s}", + "service_enabled": "S'ha activat el servei «{service:s}»", "service_no_log": "No hi ha cap registre pel servei «{service:s}»", "service_regen_conf_is_deprecated": "«yunohost service regen-conf» està desfasat! Utilitzeu «yunohost tools regen-conf» en el seu lloc.", "service_remove_failed": "No s'ha pogut eliminar el servei «{service:s}»", @@ -523,7 +523,7 @@ "yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.", "yunohost_configured": "YunoHost està configurat", "yunohost_installing": "Instal·lació de YunoHost…", - "yunohost_not_installed": "YunoHost no està instal·lat o no està instal·lat correctament. Executeu «yunohost tools postinstall»", + "yunohost_not_installed": "YunoHost no està instal·lat correctament. Executeu «yunohost tools postinstall»", "apps_permission_not_found": "No s'ha trobat cap permís per les aplicacions instal·lades", "apps_permission_restoration_failed": "Ha fallat el permís «{permission:s}» per la restauració de l'aplicació {app:s}", "backup_permission": "Permís de còpia de seguretat per l'aplicació {app:s}", @@ -553,13 +553,13 @@ "mailbox_disabled": "La bústia de correu està desactivada per al usuari {user:s}", "migration_description_0011_setup_group_permission": "Configurar el grup d'usuaris i els permisos per les aplicacions i els serveis", "migration_0011_backup_before_migration": "Creant una còpia de seguretat de la base de dades LDAP i la configuració de les aplicacions abans d'efectuar la migració.", - "migration_0011_can_not_backup_before_migration": "No s'ha pogut fer la còpia de seguretat abans de la migració. No s'ha pogut fer la migració. Error: {error:s}", + "migration_0011_can_not_backup_before_migration": "No s'ha pogut completar la còpia de seguretat abans de que la migració fallés. Error: {error:s}", "migration_0011_create_group": "Creant un grup per a cada usuari…", - "migration_0011_done": "Migració completa. Ja podeu gestionar grups d'usuaris.", + "migration_0011_done": "Migració completada. Ja podeu gestionar grups d'usuaris.", "migration_0011_LDAP_config_dirty": "Sembla que heu modificat manualment la configuració LDAP. Per fer aquesta migració s'ha d'actualitzar la configuració LDAP.\nGuardeu la configuració actual, reinicieu la configuració original executant l'ordre «yunohost tools regen-conf -f» i torneu a intentar la migració", "migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}", "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP…", - "migration_0011_migration_failed_trying_to_rollback": "La migració ha fallat… s'intenta tornar el sistema a l'estat anterior.", + "migration_0011_migration_failed_trying_to_rollback": "No s'ha pogut fer la migració… s'intenta tornar el sistema a l'estat anterior.", "migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.", "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP…", "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP…", @@ -583,7 +583,7 @@ "user_already_in_group": "L'usuari {user:s} ja és en el grup {group:s}", "user_not_in_group": "L'usuari {user:s} no és en el grup {group:s}", "migration_description_0012_postgresql_password_to_md5_authentication": "Força l'autenticació PostgreSQL a fer servir MD5 per a les connexions locals", - "app_full_domain_unavailable": "Aquesta aplicació requereix un domini sencer per ser instal·lada, però ja hi ha altres aplicacions instal·lades al domini «{domain}». Una possible solució és afegir i utilitzar un subdomini dedicat a aquesta aplicació.", + "app_full_domain_unavailable": "Aquesta aplicació ha de ser instal·lada en el seu propi domini, però ja hi ha altres aplicacions instal·lades en el domini «{domain}». Podeu utilitzar un subdomini dedicat a aquesta aplicació.", "migrations_not_pending_cant_skip": "Aquestes migracions no estan pendents, així que no poden ser omeses: {ids}", "app_action_broke_system": "Aquesta acció sembla haver trencat els següents serveis importants: {services}", "log_permission_urls": "Actualitzar les URLs relacionades amb el permís «{}»", @@ -592,11 +592,11 @@ "log_user_permission_reset": "Restablir el permís «{}»", "permission_already_disallowed": "El grup «{group}» ja té el permís «{permission}» desactivat", "migrations_already_ran": "Aquestes migracions ja s'han fet: {ids}", - "migrations_dependencies_not_satisfied": "No s'ha pogut executar la migració {id} perquè s'han d'executar primer les següents migracions: {dependencies_id}", + "migrations_dependencies_not_satisfied": "Executeu aquestes migracions: «{dependencies_id}», abans la migració {id}.", "migrations_failed_to_load_migration": "No s'ha pogut carregar la migració {id}: {error}", "migrations_exclusive_options": "«--auto», «--skip», i «--force-rerun» són opcions mútuament excloents.", "migrations_must_provide_explicit_targets": "Heu de proporcionar objectius explícits al utilitzar «--skip» o «--force-rerun»", - "migrations_no_such_migration": "No hi ha cap migració anomenada {id}", + "migrations_no_such_migration": "No hi ha cap migració anomenada «{id}»", "migrations_pending_cant_rerun": "Aquestes migracions encara estan pendents, així que no es poden tornar a executar: {ids}", "migrations_running_forward": "Executant la migració {id}…", "migrations_success_forward": "Migració {id} completada", @@ -614,8 +614,8 @@ "migration_0011_failed_to_remove_stale_object": "No s'ha pogut eliminar l'objecte obsolet {dn}: {error}", "permission_already_allowed": "El grup «{group}» ja té el permís «{permission}» activat", "permission_cannot_remove_main": "No es permet eliminar un permís principal", - "user_already_exists": "L'usuari {user} ja existeix", - "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir els possibles danys ja que hi ha hagut un error en l'actualització de l'anterior aplicació", + "user_already_exists": "L'usuari «{user}» ja existeix", + "app_upgrade_stopped": "S'ha aturat l'actualització de totes les aplicacions per prevenir possibles danys ja que no s'ha pogut actualitzar una aplicació", "app_install_failed": "No s'ha pogut instal·lar {app}: {error}", "app_install_script_failed": "Hi ha hagut un error en el script d'instal·lació de l'aplicació", "group_cannot_edit_all_users": "El grup «all_users» no es pot editar manualment. És un grup especial destinat a contenir els usuaris registrats a YunoHost", @@ -625,5 +625,6 @@ "migration_0011_slapd_config_will_be_overwritten": "Sembla que heu modificat manualment la configuració de sldap. Per aquesta migració crítica, YunoHost ha de forçar l'actualització de la configuració sldap. Es farà una còpia de seguretat a {conf_backup_folder}.", "permission_already_up_to_date": "No s'ha actualitzat el permís perquè la petició d'afegir/eliminar ja corresponent a l'estat actual.", "permission_currently_allowed_for_visitors": "El permís ja el tenen el grup de visitants a més d'altres grups. Segurament s'hauria de revocar el permís al grup dels visitants o eliminar els altres grups als que s'ha atribuït.", - "permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït." + "permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït.", + "permission_require_account": "El permís {permission} només té sentit per als usuaris que tenen un compte, i per tant no es pot activar per als visitants." } From ec1fa46c9f3986e34e7695d9e691ac74d685dfb3 Mon Sep 17 00:00:00 2001 From: Kayou Date: Sat, 26 Oct 2019 23:12:11 +0900 Subject: [PATCH 284/299] iproute2 instead of iproute --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index c0604d90e..b0de9032b 100644 --- a/debian/control +++ b/debian/control @@ -17,7 +17,7 @@ Depends: ${python:Depends}, ${misc:Depends} , python-toml , glances, apt-transport-https , dnsutils, bind9utils, unzip, git, curl, cron, wget, jq - , ca-certificates, netcat-openbsd, iproute + , ca-certificates, netcat-openbsd, iproute2 , mariadb-server, php-mysql | php-mysqlnd , slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd , postfix-ldap, postfix-policyd-spf-perl, postfix-pcre, procmail, mailutils, postsrsd From 17ce7bd95c097f8fceeee6f35873c89cd4b4b91c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 26 Oct 2019 17:35:00 +0200 Subject: [PATCH 285/299] Rework depreciation warning about legacy permission stuff --- data/helpers.d/setting | 4 ++-- src/yunohost/app.py | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index fd2824997..d905b61dd 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -176,8 +176,8 @@ else: elif action == "set": if key in ['redirected_urls', 'redirected_regex']: value = yaml.load(value) - if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: - sys.stderr.write("/!\\ Packagers! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers 'ynh_permission_{create,urls,update,delete}' and the 'visitors' group to manage public/private access.\n") + if any(key.startswith(word+"_") for word in ["unprotected", "protected", "skipped"]): + sys.stderr.write("/!\\ Packagers! This app is still using the skipped/protected/unprotected_uris/regex settings which are now obsolete and deprecated... Instead, you should use the new helpers 'ynh_permission_{create,urls,update,delete}' and the 'visitors' group to initialize the public/private access. Check out the documentation at the bottom of yunohost.org/groups_and_permissions to learn how to use the new permission mechanism.\n") settings[key] = value else: raise ValueError("action should either be get, set or delete") diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 5cf812871..b1ad0f40c 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1341,17 +1341,18 @@ def app_setting(app, key, value=None, delete=False): except Exception as e: logger.debug("cannot get app setting '%s' for '%s' (%s)", key, app, e) return None + + if delete and key in app_settings: + del app_settings[key] else: - if delete and key in app_settings: - del app_settings[key] - else: - # FIXME: Allow multiple values for some keys? - if key in ['redirected_urls', 'redirected_regex']: - value = yaml.load(value) - if key in ["unprotected_uris", "unprotected_regex", "protected_uris", "protected_regex"]: - logger.warning("/!\ Packagers ! This app is using the legacy permission system. Please delete these legacy settings and use the new helpers ynh_permission_{create,url,update,delete} and the 'visitors' group to manage public/private access.") - app_settings[key] = value - _set_app_settings(app, app_settings) + # FIXME: Allow multiple values for some keys? + if key in ['redirected_urls', 'redirected_regex']: + value = yaml.load(value) + if any(key.startswith(word+"_") for word in ["unprotected", "protected", "skipped"]): + logger.warning("/!\\ Packagers! This app is still using the skipped/protected/unprotected_uris/regex settings which are now obsolete and deprecated... Instead, you should use the new helpers 'ynh_permission_{create,urls,update,delete}' and the 'visitors' group to initialize the public/private access. Check out the documentation at the bottom of yunohost.org/groups_and_permissions to learn how to use the new permission mechanism.") + + app_settings[key] = value + _set_app_settings(app, app_settings) def app_checkport(port): From 9294664d6c60269a5c2dc3048d6994bd2944cb3d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 26 Oct 2019 17:39:09 +0200 Subject: [PATCH 286/299] Fix permission backward compatibility for the case where an app needs to make the app temporarily public during install script... --- data/helpers.d/setting | 7 +++++++ src/yunohost/app.py | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/data/helpers.d/setting b/data/helpers.d/setting index d905b61dd..185e6111f 100644 --- a/data/helpers.d/setting +++ b/data/helpers.d/setting @@ -184,6 +184,13 @@ else: with open(setting_file, "w") as f: yaml.safe_dump(settings, f, default_flow_style=False) EOF + + # Fucking legacy permission management. + # We need this because app temporarily set the app as unprotected to configure it with curl... + if [[ "$3" =~ ^(unprotected|skipped)_ ]] && [[ "${4:-}" == "/" ]] + then + ynh_permission_update --permission "main" --remove "all_users" --add "visitors" + fi } # Check availability of a web path diff --git a/src/yunohost/app.py b/src/yunohost/app.py index b1ad0f40c..6d3da405f 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1101,9 +1101,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu if not (domain and path): permission_url(app_instance_name + ".main", url=None, sync_perm=False) - # Migrate classic public app still using the legacy unprotected_uris - if app_settings.get("unprotected_uris", None) == "/" or app_settings.get("skipped_uris", None) == "/": - user_permission_update(app_instance_name + ".main", remove="all_users", add="visitors", sync_perm=False) + _migrate_legacy_permissions(app_instance_name) permission_sync_to_user() @@ -1112,6 +1110,34 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu hook_callback('post_app_install', args=args_list, env=env_dict) +def _migrate_legacy_permissions(app): + + from yunohost.permission import user_permission_list, user_permission_update + + # Check if app is apparently using the legacy permission management, defined by the presence of something like + # ynh_app_setting_set on unprotected_uris (or yunohost app setting) + install_script_path = os.path.join(APPS_SETTING_PATH, app, 'scripts/install') + install_script_content = open(install_script_path, "r").read() + if not re.search(r"(yunohost app setting|ynh_app_setting_set) .*(unprotected|skipped)_uris", install_script_content): + return + + app_settings = _get_app_settings(app) + app_perm_currently_allowed = user_permission_list()["permissions"][app + ".main"]["allowed"] + + # If the current permission says app is protected, but there are legacy rules saying it should be public... + if app_perm_currently_allowed == ["all_users"] \ + and (app_settings.get("unprotected_uris", None) == "/" + or app_settings.get("skipped_uris", None) == "/"): + # Make it public + user_permission_update(app + ".main", remove="all_users", add="visitors", sync_perm=False) + # If the current permission says app is public, but there are no setting saying it should be public... + if app_perm_currently_allowed == ["visitors"] \ + and (app_settings.get("unprotected_uris", None) is None + and app_settings.get("skipped_uris", None) is None): + # Make is private + user_permission_update(app + ".main", remove="visitors", add="all_users", sync_perm=False) + + @is_unit_operation() def app_remove(operation_logger, app): """ @@ -1354,6 +1380,12 @@ def app_setting(app, key, value=None, delete=False): app_settings[key] = value _set_app_settings(app, app_settings) + # Fucking legacy permission management. + # We need this because app temporarily set the app as unprotected to configure it with curl... + if key.startswith("unprotected_") or key.startswith("skipped_") and value == "/": + from permission import user_permission_update + user_permission_update(app + ".main", remove="all_users", add="visitors") + def app_checkport(port): """ From 854e52c21e0d5a6e10d122f8954b34c182c4de44 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 15:46:22 +0100 Subject: [PATCH 287/299] More inclusive rule for this php/sury hack because php version got updated to deb9u6 --- data/helpers.d/apt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/apt b/data/helpers.d/apt index d772c6855..cbf4e3e59 100644 --- a/data/helpers.d/apt +++ b/data/helpers.d/apt @@ -227,7 +227,7 @@ ynh_install_app_dependencies () { if echo $dependencies | grep -q 'php'; then # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian) - if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9u5" + if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9" then # And sury ain't already installed if ! grep -nrq "sury" /etc/apt/sources.list* From 5a599f2ebf08245b9673487ce949d15543bd5f0f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 15:49:31 +0100 Subject: [PATCH 288/299] Update changelog for 3.6.5.3 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 1d13b6290..45dbadef5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (3.6.5.3) stable; urgency=low + + - [fix] More general grep for the php/sury dependency nightmare fix (followup of #809) + + -- Alexandre Aubin Tue, 29 Oct 2019 03:48:00 +0000 + yunohost (3.6.5.2) stable; urgency=low - [fix] Alex was drunk and released an epic stupid bug in stable (2623d385) From 79627d79ccfbc4e5a23691e3fd7da80620a707cd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 16:18:31 +0100 Subject: [PATCH 289/299] [yolo] Cosmetic improvement for logs during system package upgrades --- src/yunohost/tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 28b507707..f4bb83c15 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -640,8 +640,11 @@ def tools_upgrade(operation_logger, apps=None, system=False): logger.debug("Running apt command :\n{}".format(dist_upgrade)) + def is_relevant(l): + return "Reading database ..." not in l.rstrip() + callbacks = ( - lambda l: logger.info("+" + l.rstrip() + "\r"), + lambda l: logger.info("+ " + l.rstrip() + "\r") if is_relevant(l) else logger.debug(l.rstrip() + "\r"), lambda l: logger.warning(l.rstrip()), ) returncode = call_async_output(dist_upgrade, callbacks, shell=True) From 71bc6a0fafce76e03de00133a4df0e570512f138 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 16:38:15 +0100 Subject: [PATCH 290/299] We gotta regen the ssowat conf to propagate the change --- src/yunohost/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 6d3da405f..1eeb38924 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -612,6 +612,8 @@ def app_change_url(operation_logger, app, domain, path): app_setting(app, 'domain', value=domain) app_setting(app, 'path', value=path) + app_ssowatconf() + # avoid common mistakes if _run_service_command("reload", "nginx") is False: # grab nginx errors From 572b003e299428238546813f956ebbb23f800946 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 17:17:43 +0100 Subject: [PATCH 291/299] We gotta return a permission structure here in all case, otherwise stuff like app_addaccess will miserably fail in these case --- src/yunohost/permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py index 226cc9050..c53804d49 100644 --- a/src/yunohost/permission.py +++ b/src/yunohost/permission.py @@ -155,7 +155,7 @@ def user_permission_update(operation_logger, permission, add=None, remove=None, # Don't update LDAP if we update exactly the same values if set(new_allowed_groups) == set(current_allowed_groups): logger.warning(m18n.n("permission_already_up_to_date")) - return + return existing_permission # Commit the new allowed group list From 83b45d7894f49878d608071e7f27b3981ae896cc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 17:38:05 +0100 Subject: [PATCH 292/299] Fix the legacy permission fix after app install, sometimes the setting ain't None --- src/yunohost/app.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1eeb38924..1e82b78ba 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1126,16 +1126,16 @@ def _migrate_legacy_permissions(app): app_settings = _get_app_settings(app) app_perm_currently_allowed = user_permission_list()["permissions"][app + ".main"]["allowed"] + settings_say_it_should_be_public = (app_settings.get("unprotected_uris", None) == "/" + or app_settings.get("skipped_uris", None) == "/") + # If the current permission says app is protected, but there are legacy rules saying it should be public... - if app_perm_currently_allowed == ["all_users"] \ - and (app_settings.get("unprotected_uris", None) == "/" - or app_settings.get("skipped_uris", None) == "/"): + if app_perm_currently_allowed == ["all_users"] and settings_say_it_should_be_public: # Make it public user_permission_update(app + ".main", remove="all_users", add="visitors", sync_perm=False) + # If the current permission says app is public, but there are no setting saying it should be public... - if app_perm_currently_allowed == ["visitors"] \ - and (app_settings.get("unprotected_uris", None) is None - and app_settings.get("skipped_uris", None) is None): + if app_perm_currently_allowed == ["visitors"] and not settings_say_it_should_be_public: # Make is private user_permission_update(app + ".main", remove="visitors", add="all_users", sync_perm=False) From 9ee3d234e8db1080dd5d87f3bec0b802a1035943 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 19:19:04 +0100 Subject: [PATCH 293/299] [ux] Add a message to explain that the app is being removed if the install fails --- locales/en.json | 1 + src/yunohost/app.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/locales/en.json b/locales/en.json index 202650edb..27f25e095 100644 --- a/locales/en.json +++ b/locales/en.json @@ -40,6 +40,7 @@ "app_requirements_checking": "Checking required packages for {app}…", "app_requirements_failed": "Some requirements are not met for {app}: {error}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", + "app_remove_after_failed_install": "Removing the app following the installation failure…", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", "app_start_install": "Installing the app '{app}'…", "app_start_remove": "Removing the app '{app}'…", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1e82b78ba..a4185b880 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1026,6 +1026,8 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu # This option is meant for packagers to debug their apps more easily if no_remove_on_failure: raise YunohostError("The installation of %s failed, but was not cleaned up as requested by --no-remove-on-failure." % app_id, raw_msg=True) + else: + logger.warning(m18n.n("app_remove_after_failed_install")) # Setup environment for remove script env_dict_remove = {} From 4dadb9eb906eaef566b1cd08aa6755d6e8d9637c Mon Sep 17 00:00:00 2001 From: amirale qt Date: Mon, 28 Oct 2019 08:27:21 +0000 Subject: [PATCH 294/299] Translated using Weblate (French) Currently translated at 100.0% (561 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/fr/ --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 3cb3c8fa9..15f82baf1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -370,7 +370,7 @@ "dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.", "app_make_default_location_already_used": "Impossible de configurer l’application '{app}' par défaut pour le domaine '{domain}' car il est déjà utilisé par l'application '{other_app}'", "app_upgrade_app_name": "Mise à jour de l’application {app} …", - "backup_output_symlink_dir_broken": "Votre répertoire d'archivage '{chemin:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.", + "backup_output_symlink_dir_broken": "Votre répertoire d'archivage '{path:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.", "migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", "migrate_tsig_start": "L’algorithme de génération des clefs n’est pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers HMAC-SHA-512 qui est plus sécurisé", From dd9f1944af994ec2a44081c4d22a6798f5bd2047 Mon Sep 17 00:00:00 2001 From: amirale qt Date: Mon, 28 Oct 2019 08:04:16 +0000 Subject: [PATCH 295/299] Translated using Weblate (Esperanto) Currently translated at 93.0% (522 of 561 strings) Translation: YunoHost/core Translate-URL: https://translate.yunohost.org/projects/yunohost/core/eo/ --- locales/eo.json | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/locales/eo.json b/locales/eo.json index c78bf6269..720485ba6 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -40,7 +40,7 @@ "app_argument_choice_invalid": "Uzu unu el ĉi tiuj elektoj '{choices:s}' por la argumento '{name:s}'", "app_argument_invalid": "Elektu validan valoron por la argumento '{name:s}': {error:s}", "app_change_url_failed_nginx_reload": "Ne eblis reŝarĝi NGINX. Jen la eligo de 'nginx -t':\n{nginx_errors:s}", - "appslist_url_already_tracked": "Estas jam registrita aplika listo kun la URL {url:s}.", + "appslist_url_already_tracked": "Jam ekzistas registrita app-listo kun la URL {url:s}.", "ask_new_admin_password": "Nova administrada pasvorto", "app_action_broke_system": "Ĉi tiu ago ŝajne rompis ĉi tiujn gravajn servojn: {services}", "app_unsupported_remote_type": "Malkontrolita fora speco uzita por la apliko", @@ -50,16 +50,16 @@ "backup_abstract_method": "Ĉi tiu rezerva metodo ankoraŭ efektiviĝis", "apps_already_up_to_date": "Ĉiuj aplikoj estas jam ĝisdatigitaj", "backup_borg_not_implemented": "La kopia metodo de Borg ankoraŭ ne estas efektivigita", - "app_upgrade_stopped": "La ĝisdatigo de ĉiuj aplikoj estis ĉesigita por eviti eblajn damaĝojn ĉar la antaŭa apliko ne sukcesis ĝisdatigi", + "app_upgrade_stopped": "Ĝisdatigi ĉiujn aplikaĵojn estis ĉesigita por eviti eblajn damaĝojn ĉar unu app ne povis esti altgradigita", "app_location_unavailable": "Ĉi tiu URL aŭ ne haveblas, aŭ konfliktas kun la jam instalita (j) apliko (j):\n{apps:s}", "backup_archive_app_not_found": "Ne povis trovi la programon '{app:s}' en la rezerva ar archiveivo", "backup_actually_backuping": "Krei rezervan ar archiveivon el la kolektitaj dosieroj …", "backup_method_borg_finished": "Sekurkopio en Borg finiĝis", - "appslist_removed": "{appslist:s} aplika listo forigita", + "appslist_removed": "La listo de '{appslist:s}' estis forigita", "app_change_url_no_script": "La app '{app_name:s}' ankoraŭ ne subtenas URL-modifon. Eble vi devus altgradigi ĝin.", - "app_start_install": "Instalanta aplikon {app} …", + "app_start_install": "Instali la programon '{app}' …", "backup_created": "Sekurkopio kreita", - "app_make_default_location_already_used": "Ne povas igi la aplikon '{app}' defaŭlta sur la domajno, {domain} jam uziĝas de la alia app '{other_app}'", + "app_make_default_location_already_used": "Ne povas igi la aplikon '{app}' defaŭlta sur la domajno, '{domain}' jam uziĝas de la alia app '{other_app}'", "backup_method_copy_finished": "Rezerva kopio finis", "app_not_properly_removed": "{app:s} ne estis ĝuste forigita", "backup_archive_broken_link": "Ne povis aliri la rezervan ar archiveivon (rompita ligilo al {path:s})", @@ -78,15 +78,15 @@ "backup_custom_backup_error": "Propra rezerva metodo ne povis preterpasi la paŝon \"sekurkopio\"", "ask_main_domain": "Ĉefa domajno", "backup_method_tar_finished": "TAR-rezerva ar archiveivo kreita", - "appslist_unknown": "Aplika listo {appslist:s} nekonata.", + "appslist_unknown": "La app-listo '{appslist:s}' estas nekonata.", "ask_list_to_remove": "Listo por forigi", "backup_cant_mount_uncompress_archive": "Ne povis munti la nekompresitan ar archiveivon kiel protektita kontraŭ skribo", - "appslist_retrieve_bad_format": "Ne povis legi la elprenitan liston {appslist:s}", + "appslist_retrieve_bad_format": "Ne povis legi la elprenitan liston '{appslist:s}'", "appslist_corrupted_json": "Ne povis ŝarĝi la aplikajn listojn. Ĝi aspektas kiel {filename:s} estas damaĝita.", "app_action_cannot_be_ran_because_required_services_down": "Ĉi tiuj postulataj servoj devas funkcii por funkciigi ĉi tiun agon: {services}. Provu rekomenci ilin por daŭrigi (kaj eble esploru, kial ili malsupreniras).", "backup_copying_to_organize_the_archive": "Kopiante {size:s} MB por organizi la ar archiveivon", "backup_output_directory_forbidden": "Elektu malsaman elirejan dosierujon. Sekurkopioj ne povas esti kreitaj en sub-dosierujoj / bin, / boot, / dev, / ktp, / lib, / root, / run, / sbin, / sys, / usr, / var aŭ /home/yunohost.backup/archives", - "appslist_could_not_migrate": "Ne povis migri la liston de aplikoj {appslist:s}! Ne eblis analizi la URL ... La malnova cron-laboro konserviĝis en {bkp_file:s}.", + "appslist_could_not_migrate": "Ne povis migri la liston de aplikoj '{appslist:s}'! Ne eblis analizi la URL ... La malnova cron-laboro konserviĝis en {bkp_file:s}.", "app_requirements_failed": "Certaines exigences ne sont pas remplies pour {app}: {error}", "backup_no_uncompress_archive_dir": "Ne ekzistas tia nekompremita arkiva dosierujo", "password_too_simple_1": "Pasvorto devas esti almenaŭ 8 signojn longa", @@ -94,11 +94,11 @@ "app_upgrade_several_apps": "La sekvaj apliko estos altgradigitaj: {apps}", "backup_archive_open_failed": "Ne povis malfermi la rezervan ar archiveivon", "ask_lastname": "Familia nomo", - "app_start_backup": "Kolekti dosierojn por esti subtenata por {app} …", + "app_start_backup": "Kolekti dosierojn por esti subtenata por la '{app}' …", "backup_archive_name_exists": "Rezerva arkivo kun ĉi tiu nomo jam ekzistas.", "backup_applying_method_tar": "Krei la rezervan TAR-ar archiveivon …", "backup_method_custom_finished": "Propra rezerva metodo '{metodo:s}' finiĝis", - "appslist_retrieve_error": "Ne eblas retrovi la forajn aplikajn listojn {appslist:s}: {eraro:s}", + "appslist_retrieve_error": "Ne eblas akiri la forajn listojn '{appslist:s}': {eraro:s}", "app_already_installed_cant_change_url": "Ĉi tiu app estas jam instalita. La URL ne povas esti ŝanĝita nur per ĉi tiu funkcio. Rigardu \"app changeurl\" se ĝi haveblas.", "app_not_correctly_installed": "{app:s} ŝajnas esti malĝuste instalita", "app_removed": "{app:s} forigita", @@ -106,27 +106,27 @@ "app_package_need_update": "La pakaĵo {app} devas esti ĝisdatigita por sekvi YunoHost-ŝanĝojn", "backup_nothings_done": "Nenio por ŝpari", "backup_applying_method_custom": "Nomante la kutiman rezervan metodon '{metodo:s}' …", - "appslist_fetched": "Ĝisdatigita aplika listo {appslist:s}", + "appslist_fetched": "Ĝisdatigis la liston de aplikoj '{appslist:s}'", "backup_app_failed": "Ne eblis rezervi la programon '{app:s}'", "app_upgrade_some_app_failed": "Iuj aplikoj ne povis esti altgradigitaj", - "app_start_remove": "Forigo de apliko {app} …", + "app_start_remove": "Forigo de la apliko '{app}' …", "backup_output_directory_not_empty": "Vi devas elekti malplenan eligitan dosierujon", "backup_archive_writing_error": "Ne povis aldoni la dosierojn '{source:s}' (nomitaj en la ar theivo '{dest:s}') por esti rezervitaj en la kunpremita arkivo '{archive:s}'", "ask_email": "Retpoŝta adreso", - "app_start_restore": "Restarigi aplikon {app} …", + "app_start_restore": "Restarigi la programon '{app}' …", "backup_applying_method_copy": "Kopiante ĉiujn dosierojn al sekurkopio …", "backup_couldnt_bind": "Ne povis ligi {src:s} al {dest:s}.", "ask_password": "Pasvorto", "app_requirements_unmeet": "Postuloj ne estas renkontitaj por {app}, la pakaĵo {pkgname} ({version}) devas esti {spec}", "ask_firstname": "Antaŭnomo", - "backup_ask_for_copying_if_needed": "Iuj dosieroj ne povus esti pretigitaj por sekurkopio uzante la metodon, kiu evitas portempe malŝpari spacon en la sistemo. Por plenumi la sekurkopion, {size:s} MB estos provizore. Ĉu vi konsentas?", + "backup_ask_for_copying_if_needed": "Ĉu vi volas realigi la sekurkopion uzante {size:s} MB provizore? (Ĉi tiu maniero estas uzata ĉar iuj dosieroj ne povus esti pretigitaj per pli efika metodo.)", "backup_mount_archive_for_restore": "Preparante arkivon por restarigo …", - "appslist_migrating": "Migra aplika listo {appslist:s} …", + "appslist_migrating": "Migrado de la aplika listo '{appslist:s}' …", "backup_csv_creation_failed": "Ne povis krei la CSV-dosieron bezonatan por restarigo", "backup_archive_name_unknown": "Nekonata loka rezerva ar archiveivo nomata '{name:s}'", "backup_applying_method_borg": "Sendado de ĉiuj dosieroj al sekurkopio en borg-rezerva deponejo …", "app_sources_fetch_failed": "Ne povis akiri fontajn dosierojn, ĉu la URL estas ĝusta?", - "appslist_name_already_tracked": "Registrita aplika listo kun nomo {name:s} jam ekzistas.", + "appslist_name_already_tracked": "Registrita aplika listo kun la nomo {name:s} jam ekzistas.", "ask_new_domain": "Nova domajno", "app_unknown": "Nekonata apliko", "app_not_upgraded": "La aplikaĵo '{failed_app}' ne ĝisdatigis, kaj pro tio la sekvaj ĝisdatigoj de aplikoj estis nuligitaj: {apps}", @@ -271,11 +271,11 @@ "update_apt_cache_failed": "Ne eblis ĝisdatigi la kaŝmemoron de APT (paka administranto de Debian). Jen rubujo de la sources.list-linioj, kiuj povus helpi identigi problemajn liniojn:\n{sourcelist}", "migrations_no_migrations_to_run": "Neniuj migradoj por funkcii", "executing_command": "Plenumanta komandon '{command:s}' …", - "diagnosis_no_apps": "Neniu instalita apliko", + "diagnosis_no_apps": "Neniu tia instalita app", "certmanager_attempt_to_renew_nonLE_cert": "La atestilo por la domajno '{domain:s}' ne estas elsendita de Let's Encrypt. Ne eblas renovigi ĝin aŭtomate!", "global_settings_setting_example_bool": "Ekzemplo bulea elekto", "domain_dyndns_already_subscribed": "Vi jam abonis DynDNS-domajnon", - "log_letsencrypt_cert_renew": "Renovigu '{}' Ni ĉifru atestilon", + "log_letsencrypt_cert_renew": "Renovigu '{}' Let's Encrypt atestilon", "migrate_tsig_start": "Detektita ŝlosila algoritmo nesufiĉa por TSIG-subskribo de la domajno '{domain}', komencanta migradon al la pli sekura HMAC-SHA-512", "ldap_init_failed_to_create_admin": "LDAP-iniciato ne povis krei administran uzanton", "backup_output_directory_required": "Vi devas provizi elirejan dosierujon por la sekurkopio", @@ -381,7 +381,7 @@ "regenconf_file_kept_back": "La agorda dosiero '{conf}' estas atendita forigi per regen-conf (kategorio {category}), sed ĝi estis konservita.", "migrate_tsig_wait_4": "30 sekundoj …", "backup_with_no_restore_script_for_app": "La apliko \"{app:s}\" ne havas restarigan skripton, vi ne povos aŭtomate restarigi la sekurkopion de ĉi tiu apliko.", - "log_letsencrypt_cert_install": "Instalu atestilon Ni ĉifru sur '{}' regado", + "log_letsencrypt_cert_install": "Instalu atestilon Let's Encrypt sur '{}' regado", "log_dyndns_update": "Ĝisdatigu la IP asociita kun via subdominio YunoHost '{}'", "firewall_reload_failed": "Ne eblis reŝargi la firewall", "confirm_app_install_warning": "Averto: Ĉi tiu aplikaĵo povas funkcii, sed ne bone integras en YunoHost. Iuj funkcioj kiel ekzemple aliĝilo kaj sekurkopio / restarigo eble ne haveblos. Instali ĉiuokaze? [{answers: s}] ", @@ -393,7 +393,7 @@ "migrations_need_to_accept_disclaimer": "Por funkciigi la migradon {id}, via devas akcepti la sekvan malakcepton:\n---\n{malavantaĝo}\n---\nSe vi akceptas funkcii la migradon, bonvolu rekonduki la komandon kun la opcio '--accept-disclaimer'.", "regenconf_file_remove_failed": "Ne povis forigi la agordodosieron '{conf}'", "not_enough_disk_space": "Ne sufiĉe libera spaco sur '{path:s}'", - "migration_0006_disclaimer": "YunoHost nun atendas ke pasvortoj kaj administrantoj estu sinkronigitaj. Per ekzekuto de ĉi tiu migrado, via radika pasvorto estos anstataŭigita per administra pasvorto.", + "migration_0006_disclaimer": "YunoHost nun atendas, ke la pasvortoj de admin kaj radiko estos sinkronigitaj. Ĉi tiu migrado anstataŭigas vian radikan pasvorton kun la administran pasvorton.", "dyndns_ip_update_failed": "Ne povis ĝisdatigi IP-adreson al DynDNS", "migration_description_0004_php5_to_php7_pools": "Rekonfigu la PHP-naĝejojn por uzi PHP 7 anstataŭ 5", "monitor_glances_con_failed": "Ne povis konektiĝi al servilo de Glances", @@ -422,7 +422,7 @@ "migration_0003_general_warning": "Bonvolu noti, ke ĉi tiu migrado estas delikata operacio. La teamo de YunoHost faris sian plej bonan revizii kaj testi ĝin, sed la migrado eble ankoraŭ rompos partojn de la sistemo aŭ ĝiaj programoj.\n\nTial oni rekomendas al:\n - Elfari kopion de iuj kritikaj datumoj aŭ app. Pliaj informoj pri https://yunohost.org/backup;\n - Paciencu post lanĉo de la migrado: Depende de via interreta konekto kaj aparataro, eble daŭros kelkaj horoj ĝis ĉio ĝisdatigi.\n\nAldone, la haveno por SMTP, uzata de eksteraj retpoŝtaj klientoj (kiel Thunderbird aŭ K9-Mail) estis ŝanĝita de 465 (SSL / TLS) al 587 (STARTTLS). La malnova haveno (465) aŭtomate fermiĝos, kaj la nova haveno (587) malfermiĝos en la fajrejo. Vi kaj viaj uzantoj * devos adapti la agordon de viaj retpoŝtaj klientoj laŭe.", "diagnosis_kernel_version_error": "Ne povis akiri la kernan version: {error}", "global_settings_setting_example_int": "Ekzemple int elekto", - "backup_output_symlink_dir_broken": "Vi havas rompitan simbolon anstataŭ via arkiva dosierujo '{path:s}'. Vi eble havas specifan agordon por sekurkopi viajn datumojn en alia dosiersistemo, ĉi-kaze vi probable forgesis remeti aŭ enŝovi vian malmolan disko aŭ ŝlosilon USB.", + "backup_output_symlink_dir_broken": "Via arkiva dosierujo '{path:s}' estas rompita ligilo. Eble vi forgesis restarigi aŭ munti aŭ enŝovi la stokadon, al kiu ĝi notas.", "good_practices_about_admin_password": "Vi nun estas por difini novan administran pasvorton. La pasvorto devas esti almenaŭ 8 signoj - kvankam estas bone praktiki uzi pli longan pasvorton (t.e. pasfrazon) kaj / aŭ uzi variaĵon de signoj (majuskloj, minuskloj, ciferoj kaj specialaj signoj).", "certmanager_attempt_to_renew_valid_cert": "La atestilo por la domajno '{domain:s}' ne finiĝos! (Vi eble uzos --force se vi scias kion vi faras)", "restore_running_hooks": "Kurantaj restarigaj hokoj…", @@ -431,7 +431,7 @@ "domain_dns_conf_is_just_a_recommendation": "Ĉi tiu komando montras al vi la *rekomenditan* agordon. Ĝi efektive ne agordas la DNS-agordon por vi. Via respondeco agordi vian DNS-zonon en via registristo laŭ ĉi tiu rekomendo.", "backup_php5_to_php7_migration_may_fail": "Ne povis konverti vian ar archiveivon por subteni PHP 7, vi eble ne povas restarigi viajn PHP-programojn (kialo: {error:s})", "log_backup_restore_system": "Restarigi sistemon de rezerva arkivo", - "log_app_change_url": "Ŝanĝu la URL de apliko '{}'", + "log_app_change_url": "Ŝanĝu la URL de la apliko '{}'", "service_already_started": "La servo '{service:s}' estas jam komencita", "license_undefined": "nedifinita", "global_settings_setting_security_password_admin_strength": "Admin pasvorta forto", @@ -526,7 +526,7 @@ "password_too_simple_2": "La pasvorto bezonas almenaŭ 8 signojn kaj enhavas ciferon, majusklojn kaj minusklojn", "executing_script": "Plenumanta skripto '{script:s}' …", "service_cmd_exec_failed": "Ne povis plenumi la komandon '{command:s}'", - "migration_0007_cancelled": "YunoHost ne plibonigis la administradon de via SSH-konf.", + "migration_0007_cancelled": "Ne povis plibonigi la manieron kiel via SSH-agordo estas administrita.", "migrate_tsig_failed": "Ne povis migri la DynDNS-domajnon '{domain}' al HMAC-SHA-512, ruliĝante. Eraro: {error_code}, {error}", "pattern_lastname": "Devas esti valida familinomo", "service_enabled": "'{service:s}' servo malŝaltita", @@ -553,7 +553,7 @@ "service_restart_failed": "Ne povis rekomenci la servon '{service:s}'\n\nLastatempaj servaj protokoloj: {logs:s}", "firewall_rules_cmd_failed": "Iuj komandoj pri fajroŝirmilo malsukcesis. Pliaj informoj en ensaluto.", "certmanager_certificate_fetching_or_enabling_failed": "Provante uzi la novan atestilon por {domain:s} ne funkciis …", - "app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu apliko postulas plenan domajnon esti instalita, sed iuj aliaj programoj jam estas instalitaj sur '{domain}'. Unu ebla solvo estas aldoni kaj uzi subdomajnon dediĉitan al ĉi tiu aplikaĵo anstataŭe.", + "app_full_domain_unavailable": "Bedaŭrinde, ĉi tiu app devas esti instalita sur propra domajno, sed aliaj programoj jam estas instalitaj sur la domajno '{domain}'. Vi povus uzi subdominon dediĉitan al ĉi tiu app anstataŭe.", "migration_0011_slapd_config_will_be_overwritten": "Ŝajnas ke vi permane redaktis la slapd-agordon. Por ĉi tiu kritika migrado, YunoHost bezonas devigi la ĝisdatigon de la slapd-agordo. La originalaj dosieroj estos rezervitaj en {conf_backup_folder}.", "group_cannot_edit_all_users": "La grupo 'all_users' ne povas esti redaktita permane. Ĝi estas speciala grupo celita enhavi ĉiujn uzantojn registritajn en YunoHost", "group_cannot_edit_visitors": "La grupo 'vizitantoj' ne povas esti redaktita permane. Ĝi estas speciala grupo reprezentanta anonimajn vizitantojn", From 40f48ff25ce1b76294d2d830b7237b898ed83d50 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 29 Oct 2019 21:38:43 +0100 Subject: [PATCH 296/299] zzzzz previous commit broke the info for apps, only system infos were affected --- src/yunohost/backup.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 3f253c7ff..213f2cec1 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -2374,11 +2374,13 @@ def backup_info(name, with_details=False, human_readable=False): for category in ["apps", "system"]: for name, key_info in info[category].items(): - # Stupid legacy fix for weird format between 3.5 and 3.6 - if isinstance(key_info, dict): - key_info = key_info.keys() - - info[category][name] = key_info = {"paths": key_info} + if category == "system": + # Stupid legacy fix for weird format between 3.5 and 3.6 + if isinstance(key_info, dict): + key_info = key_info.keys() + info[category][name] = key_info = {"paths": key_info} + else: + info[category][name] = key_info if name in info["size_details"][category].keys(): key_info["size"] = info["size_details"][category][name] From 3f80fe4e932708820ccd180f861e99f52f331592 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 31 Oct 2019 18:14:20 +0100 Subject: [PATCH 297/299] Update changelog for 3.7.0 testing --- debian/changelog | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/debian/changelog b/debian/changelog index 45dbadef5..15b3c049c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,46 @@ +yunohost (3.7.0) testing; urgency=low + + # ~ Major stuff + + - [enh] Add group and permission mechanism (YunoHost#585, YunoHost#763, YunoHost#789, YunoHost#790, YunoHost#795, YunoHost#797, SSOwat#147, Moulinette#189, YunoHost-admin#257) + - [mod] Rework migration system to have independent migrations (YunoHost#768, YunoHost#774, YunoHost-admin#258) + - [enh] Many improvements in the way app action failures are handled (YunoHost#769, YunoHost#811) + - [enh] Improve checks for system anomalies after app operations (YunoHost#785) + - [mod] Spookier warnings for dangerous app installs (YunoHost#814, Moulinette/808f620) + - [enh] Support app manifests in toml (YunoHost#748, Moulinette#204, Moulinette/55515cb) + - [mod] Get rid of etckeeper (YunoHost#803) + - [enh] Quite a lot of messages improvements, string cleaning, language rework... (YunoHost#793, YunoHost#799, YunoHost#823, SSOwat#143, YunoHost#766, YunoHost#767, YunoHost/fd99ef0, YunoHost/92a6315, YunoHost-admin/10ea04a, Moulinette/599bec3, Moulinette#208, Moulinette#213, Moulinette/b7d415d, Moulinette/a8966b8, Moulinette/fdf9a71, Moulinette/d895ae3, Moulinette/bdf0a1c, YunoHost#817, YunoHost#823, YunoHost/79627d7, YunoHost/9ee3d23, YunoHost-admin#265) + - [i18n] Improved translations for Catalan, Occitan, French, Esperanto, Arabic, German, Spanish, Norwegian Bokmål, Portuguese + + # Smaller or pretty technical fix/enh + + - [enh] Add unit/functional tests for apps + improve other tests (YunoHost#779, YunoHost#808) + - [enh] Preparations for moulinette Python3 migration (Tox, Pytest and unit tests) (Moulinette#203, Moulinette#206, Moulinette#207, Moulinette#210, Moulinette#211 Moulinette#212, Moulinette/2403ee1, Moulinette/69b0d49, Moulinette/49c749c, Moulinette/2c84ee1, Moulinette/cef72f7, YunoHost/6365a26) + - [enh] Support python hooks (YunoHost#747) + - [enh] Upgrade n version + compatibility with arm64 (YunoHost#753) + - [enh] Add OpenLDAP TLS support (YunoHost#755, YunoHost/0a2d1c7, YunoHost/2dc8095) + - [enh] Improve PostgreSQL password security (YunoHost#762) + - [enh] Integrate actions/config-panel into operation logs (YunoHost#764) + - [mod] Assume that apps without any 'path' setting defined aren't webapps (YunoHost#765) + - [fix] Set dpkg vendor to YunoHost (YunoHost#749, YunoHost#772) + - [enh] Adding variable 'token' to data to redact from logs (YunoHost#783) + - [enh] Add --force and --dry-run options to 'yunohost dyndns update' (YunoHost#786) + - [fix] Don't throw a fatal error if we can't change the hostname (YunoHost/fe3ecd7) + - [enh] Dynamically evaluate proper mariadb-server- (YunoHost/f0440fb) + - [fix] Bad format for backup info.json ... (YunoHost/7d0119a) + - [fix] Inline buttons responsiveness on migration screen (YunoHost-admin#259) + - [enh] Add debug logs to SSOwat (SSOwat#145) + - [enh] Add a write_to_yaml utility similar to write_to_json (Moulinette/2e2e627) + - [enh] Warn the user about long locks (Moulinette#205) + - [mod] Tweak stuff about setuptools and moulinette deps? (Moulinette/b739f27, Moulinette/da00fc9, Moulinette/d8cbbb0) + - [fix] Misc micro bugfixes or improvements (YunoHost#743, YunoHost#792, YunoHost/6f48d1d, YunoHost/d516cf8, YunoHost#819, Moulinette/83d9e77, YunoHost/63d364e, YunoHost/68e9724, YunoHost/0849adb, YunoHost/19dbe87, YunoHost/61931f2, YunoHost/6dc720f, YunoHost/4def4df, SSOwat#140, SSOwat#141, YunoHost#829) + - [doc] Fix doc building + add doc build tests with Tox (Moulinette/f1ac5b8, Moulinette/df7d478, Moulinette/74c8f79, Moulinette/bcf92c7, Moulinette/af2c80c, Moulinette/d52a574, Moulinette/307f660, Moulinette/dced104, Moulinette/ed3823b) + - [enh] READMEs improvements (YunoHost/b3398e7, SSOwat/ee67b6f, Moulinette/1541b74, Moulinette/ad1eeef, YunoHost/25afdd4, YunoHost/73741f6) + + Thanks to all contributors <3 ! (accross all repo: Yunohost, Moulinette, SSOwat, Yunohost-admin) : advocatux, Aksel K., Aleks, Allan N., amirale qt, Armin P., Bram, ButterflyOfFire, Carles S. A., chema o. r., decentral1se, Emmanuel V., Etienne M., Filip B., Geoff M., htsr, Jibec, Josué, Julien J., Kayou, liberodark, ljf, lucaskev, Lukas D., madtibo, Martin D., Mélanie C., nr 458 h, pitfd, ppr, Quentí, sidddy, troll, tufek yamero, xaloc33, yalh76 + + -- Alexandre Aubin Thu, 31 Oct 2019 18:00:00 +0000 + yunohost (3.6.5.3) stable; urgency=low - [fix] More general grep for the php/sury dependency nightmare fix (followup of #809) From 089b3cf9739bbce8dfdf38a0a7fe7b88de4d31fc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 31 Oct 2019 20:36:25 +0100 Subject: [PATCH 298/299] [fix] Sync permissions only after we're done migrating them, otherwise this triggers a shitload of warnings --- src/yunohost/data_migrations/0011_setup_group_permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/data_migrations/0011_setup_group_permission.py b/src/yunohost/data_migrations/0011_setup_group_permission.py index 7a987899c..e9ca32294 100644 --- a/src/yunohost/data_migrations/0011_setup_group_permission.py +++ b/src/yunohost/data_migrations/0011_setup_group_permission.py @@ -120,7 +120,7 @@ class MyMigration(Migration): if app_setting(app, "unprotected_uris") == "/" or app_setting(app, "skipped_uris") == "/": user_permission_update(app+".main", remove="all_users", add="visitors", sync_perm=False) - permission_sync_to_user() + permission_sync_to_user() def run(self): From 67a11b5b79689fb4c3ca784f6f0691988d48d523 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 31 Oct 2019 20:37:51 +0100 Subject: [PATCH 299/299] Update changelog for 3.7.0.1 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 15b3c049c..578a8e314 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +yunohost (3.7.0.1) testing; urgency=low + + - Hotfix to avoid having a shitload of warnings displayed during the permission migration + + -- Alexandre Aubin Thu, 31 Oct 2019 20:35:00 +0000 + yunohost (3.7.0) testing; urgency=low # ~ Major stuff