Merge remote-tracking branch 'origin/dev' into enh-dns-autoconf

This commit is contained in:
Alexandre Aubin 2021-09-13 02:58:06 +02:00
commit ee2a7347a0
15 changed files with 599 additions and 314 deletions

View file

@ -4,7 +4,7 @@
_ynh_app_config_get() {
# From settings
local lines
lines=`python3 << EOL
lines=$(python3 << EOL
import toml
from collections import OrderedDict
with open("../config_panel.toml", "r") as f:
@ -24,7 +24,7 @@ for panel_name, panel in loaded_toml.items():
param.get('bind', 'settings' if param.get('type', 'string') != 'file' else 'null')
]))
EOL
`
)
for line in $lines
do
# Split line into short_setting, type and bind

View file

@ -55,7 +55,7 @@
"global_settings_setting_smtp_relay_password": "SMTP relay heslo uživatele/hostitele",
"global_settings_setting_smtp_relay_user": "SMTP relay uživatelské jméno/účet",
"global_settings_setting_smtp_relay_port": "SMTP relay port",
"global_settings_setting_smtp_relay_host": "Použít SMTP relay hostitele pro odesílání emailů místo této Yunohost instance. Užitečné v různých situacích: port 25 je blokován vaším ISP nebo VPS poskytovatelem, IP adresa je na blacklistu (např. DUHL), nemůžete nastavit reverzní DNS záznam nebo tento server není přímo připojen do internetu a vy chcete použít jiný server k odesílání emailů.",
"global_settings_setting_smtp_relay_host": "Použít SMTP relay hostitele pro odesílání emailů místo této YunoHost instance. Užitečné v různých situacích: port 25 je blokován vaším ISP nebo VPS poskytovatelem, IP adresa je na blacklistu (např. DUHL), nemůžete nastavit reverzní DNS záznam nebo tento server není přímo připojen do internetu a vy chcete použít jiný server k odesílání emailů.",
"global_settings_setting_smtp_allow_ipv6": "Povolit použití IPv6 pro příjem a odesílání emailů",
"global_settings_setting_ssowat_panel_overlay_enabled": "Povolit SSOwat překryvný panel",
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Povolit použití (zastaralého) DSA klíče hostitele pro konfiguraci SSH služby",
@ -65,4 +65,4 @@
"global_settings_setting_security_ssh_compatibility": "Kompromis mezi kompatibilitou a bezpečností SSH serveru. Ovlivní šifry a další související bezpečnostní nastavení",
"global_settings_setting_security_password_user_strength": "Síla uživatelského hesla",
"global_settings_setting_security_password_admin_strength": "Síla administračního hesla"
}
}

View file

@ -432,9 +432,9 @@
"additional_urls_already_removed": "Zusätzliche URL '{url}' bereits entfernt in der zusätzlichen URL für Berechtigung '{permission}'",
"app_label_deprecated": "Dieser Befehl ist veraltet! Bitte nutzen Sie den neuen Befehl 'yunohost user permission update' um das Applabel zu verwalten.",
"diagnosis_http_hairpinning_issue_details": "Das ist wahrscheinlich aufgrund Ihrer ISP Box / Router. Als Konsequenz können Personen von ausserhalb Ihres Netzwerkes aber nicht von innerhalb Ihres lokalen Netzwerkes (wie wahrscheinlich Sie selber?) wie gewohnt auf Ihren Server zugreifen, wenn Sie ihre Domäne oder Ihre öffentliche IP verwenden. Sie können die Situation wahrscheinlich verbessern, indem Sie ein einen Blick in <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a> werfen",
"diagnosis_http_nginx_conf_not_up_to_date": "Jemand hat anscheinend die Konfiguration von Nginx manuell geändert. Diese Änderung verhindert, dass Yunohost eine Diagnose durchführen kann, wenn er via HTTP erreichbar ist.",
"diagnosis_http_nginx_conf_not_up_to_date": "Jemand hat anscheinend die Konfiguration von Nginx manuell geändert. Diese Änderung verhindert, dass YunoHost eine Diagnose durchführen kann, wenn er via HTTP erreichbar ist.",
"diagnosis_http_bad_status_code": "Anscheinend beantwortet ein anderes Gerät als Ihr Server die Anfrage (Vielleicht ihr Internetrouter).<br>1. Die häufigste Ursache ist, dass Port 80 (und 443) <a href='https://yunohost.org/isp_box_config'>nicht richtig auf Ihren Server weitergeleitet wird</a>.<br> 2. Bei komplexeren Setups: Vergewissern Sie sich, dass keine Firewall und keine Reverse-Proxy interferieren.",
"diagnosis_never_ran_yet": "Sie haben kürzlich einen neuen Yunohost-Server installiert aber es gibt davon noch keinen Diagnosereport. Sie sollten eine Diagnose anstossen. Sie können das entweder vom Webadmin aus oder in der Kommandozeile machen. In der Kommandozeile verwenden Sie dafür den Befehl 'yunohost diagnosis run'.",
"diagnosis_never_ran_yet": "Sie haben kürzlich einen neuen YunoHost-Server installiert aber es gibt davon noch keinen Diagnosereport. Sie sollten eine Diagnose anstossen. Sie können das entweder vom Webadmin aus oder in der Kommandozeile machen. In der Kommandozeile verwenden Sie dafür den Befehl 'yunohost diagnosis run'.",
"diagnosis_http_nginx_conf_not_up_to_date_details": "Um dieses Problem zu beheben, geben Sie in der Kommandozeile <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> ein. Dieses Tool zeigt ihnen den Unterschied an. Wenn Sie damit einverstanden sind, können Sie mit <cmd>yunohost tools regen-conf nginx --force</cmd> die Änderungen übernehmen.",
"diagnosis_backports_in_sources_list": "Sie haben anscheinend apt (den Paketmanager) für das Backports-Repository konfiguriert. Wir raten strikte davon ab, Pakete aus dem Backports-Repository zu installieren. Diese würden wahrscheinlich zu Instabilitäten und Konflikten führen. Es sei denn, Sie wissen was Sie tun.",
"diagnosis_basesystem_hardware_model": "Das Servermodell ist {model}",
@ -442,8 +442,8 @@
"group_user_not_in_group": "Der Benutzer {user} ist nicht in der Gruppe {group}",
"group_user_already_in_group": "Der Benutzer {user} ist bereits in der Gruppe {group}",
"group_cannot_edit_visitors": "Die Gruppe \"Besucher\" kann nicht manuell editiert werden. Sie ist eine Sondergruppe und repräsentiert anonyme Besucher",
"group_cannot_edit_all_users": "Die Gruppe \"all_users\" kann nicht manuell editiert werden. Sie ist eine Sondergruppe die dafür gedacht ist alle Benutzer in Yunohost zu halten",
"group_already_exist_on_system_but_removing_it": "Die Gruppe {group} existiert bereits in den Systemgruppen, aber Yunohost wird sie entfernen...",
"group_cannot_edit_all_users": "Die Gruppe \"all_users\" kann nicht manuell editiert werden. Sie ist eine Sondergruppe die dafür gedacht ist alle Benutzer in YunoHost zu halten",
"group_already_exist_on_system_but_removing_it": "Die Gruppe {group} existiert bereits in den Systemgruppen, aber YunoHost wird sie entfernen...",
"group_already_exist_on_system": "Die Gruppe {group} existiert bereits in den Systemgruppen",
"group_already_exist": "Die Gruppe {group} existiert bereits",
"global_settings_setting_smtp_relay_password": "SMTP Relay Host Passwort",

View file

@ -142,8 +142,14 @@
"certmanager_warning_subdomain_dns_record": "Subdomain '{subdomain}' does not resolve to the same IP address as '{domain}'. Some features will not be available until you fix this and regenerate the certificate.",
"config_apply_failed": "Applying the new configuration failed: {error}",
"config_cant_set_value_on_section": "You can't set a single value on an entire config section.",
"config_forbidden_keyword": "The keyword '{keyword}' is reserved, you can't create or use a config panel with a question with this id.",
"config_no_panel": "No config panel found.",
"config_unknown_filter_key": "The filter key '{filter_key}' is incorrect.",
"config_validate_color": "Should be a valid RGB hexadecimal color",
"config_validate_date": "Should be a valid date like in the format YYYY-MM-DD",
"config_validate_email": "Should be a valid email",
"config_validate_time": "Should be a valid time like XX:YY",
"config_validate_url": "Should be a valid web URL",
"config_version_not_supported": "Config panel versions '{version}' are not supported.",
"confirm_app_install_danger": "DANGER! This app is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or breaks your system... If you are willing to take that risk anyway, type '{answers}'",
"confirm_app_install_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 breaks your system... If you are willing to take that risk anyway, type '{answers}'",
@ -389,6 +395,8 @@
"hook_name_unknown": "Unknown hook name '{name}'",
"installation_complete": "Installation completed",
"invalid_number": "Must be a number",
"invalid_number_min": "Must be greater than {min}",
"invalid_number_max": "Must be lesser than {max}",
"invalid_password": "Invalid password",
"invalid_regex": "Invalid regex:'{regex}'",
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",

View file

@ -657,5 +657,7 @@
"user_import_missing_columns": "Les colonnes suivantes sont manquantes : {columns}",
"user_import_bad_file": "Votre fichier CSV n'est pas correctement formaté, il sera ignoré afin d'éviter une potentielle perte de données",
"user_import_bad_line": "Ligne incorrecte {line} : {details}",
"log_user_import": "Importer des utilisateurs"
"log_user_import": "Importer des utilisateurs",
"diagnosis_high_number_auth_failures": "Il y a eu récemment un grand nombre d'échecs d'authentification. Assurez-vous que Fail2Ban est en cours d'exécution et est correctement configuré, ou utilisez un port personnalisé pour SSH comme expliqué dans https://yunohost.org/security.",
"global_settings_setting_security_nginx_redirect_to_https": "Rediriger les requêtes HTTP vers HTTPS par défaut (NE PAS DÉSACTIVER à moins de savoir vraiment ce que vous faites !)"
}

View file

@ -45,7 +45,7 @@
"apps_catalog_update_success": "O catálogo de aplicacións foi actualizado!",
"apps_catalog_obsolete_cache": "A caché do catálogo de apps está baleiro ou obsoleto.",
"apps_catalog_failed_to_download": "Non se puido descargar o catálogo de apps {apps_catalog}: {error}",
"apps_catalog_updating": "Actualizando o catálogo de aplicacións",
"apps_catalog_updating": "Actualizando o catálogo de aplicacións...",
"apps_catalog_init_success": "Sistema do catálogo de apps iniciado!",
"apps_already_up_to_date": "Xa tes tódalas apps ao día",
"app_packaging_format_not_supported": "Esta app non se pode instalar porque o formato de empaquetado non está soportado pola túa versión de YunoHost. Deberías considerar actualizar o teu sistema.",
@ -292,7 +292,7 @@
"dyndns_could_not_check_provide": "Non se comprobou se {provider} pode proporcionar {domain}.",
"dpkg_lock_not_available": "Non se pode executar agora mesmo este comando porque semella que outro programa está a utilizar dpkg (o xestos de paquetes do sistema)",
"dpkg_is_broken": "Non podes facer isto agora mesmo porque dpkg/APT (o xestor de paquetes do sistema) semella que non está a funcionar... Podes intentar solucionalo conectándote a través de SSH e executando `sudo apt install --fix-broken`e/ou `sudo dpkg --configure -a`.",
"downloading": "Descargando",
"downloading": "Descargando...",
"done": "Feito",
"domains_available": "Dominios dispoñibles:",
"domain_unknown": "Dominio descoñecido",
@ -458,7 +458,7 @@
"migration_0015_specific_upgrade": "Iniciando a actualización dos paquetes do sistema que precisan ser actualizados de xeito independente...",
"migration_0015_modified_files": "Ten en conta que os seguintes ficheiros semella que foron modificados manualmente e poderían ser sobrescritos na actualización: {manually_modified_files}",
"migration_0015_problematic_apps_warning": "Ten en conta que se detectaron as seguintes apps que poderían ser problemáticas. Semella que non foron instaladas usando o catálogo de YunoHost, ou non están marcadas como 'funcionais'. En consecuencia, non se pode garantir que seguirán funcionando após a actualización: {problematic_apps}",
"diagnosis_http_localdomain": "O dominio {domain}, cun TLD .local, non é de agardar que sexa accesible desde o exterior da rede local.",
"diagnosis_http_localdomain": "O dominio {domain}, cun TLD .local, non é de agardar que esté exposto ao exterior da rede local.",
"diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) polo que non é de agardar que realmente teña rexistros DNS.",
"upnp_enabled": "UPnP activado",
"upnp_disabled": "UPnP desactivado",
@ -524,7 +524,7 @@
"permission_cant_add_to_all_users": "O permiso {permission} non pode ser concecido a tódalas usuarias.",
"permission_currently_allowed_for_all_users": "Este permiso está concedido actualmente a tódalas usuarias ademáis de a outros grupos. Probablemente queiras ben eliminar o permiso 'all_users' ou ben eliminar os outros grupos que teñen permiso.",
"restore_failed": "Non se puido restablecer o sistema",
"restore_extracting": "Extraendo os ficheiros necesarios desde o arquivo",
"restore_extracting": "Extraendo os ficheiros necesarios desde o arquivo...",
"restore_confirm_yunohost_installed": "Tes a certeza de querer restablecer un sistema xa instalado? [{answers}]",
"restore_complete": "Restablecemento completado",
"restore_cleaning_failed": "Non se puido despexar o directorio temporal de restablecemento",
@ -536,7 +536,7 @@
"regenconf_need_to_explicitly_specify_ssh": "A configuración ssh foi modificada manualmente, pero tes que indicar explícitamente a categoría 'ssh' con --force para realmente aplicar os cambios.",
"regenconf_pending_applying": "Aplicando a configuración pendente para categoría '{category}'...",
"regenconf_failed": "Non se rexenerou a configuración para a categoría(s): {categories}",
"regenconf_dry_pending_applying": "Comprobando as configuracións pendentes que deberían aplicarse á categoría '{category}'",
"regenconf_dry_pending_applying": "Comprobando as configuracións pendentes que deberían aplicarse á categoría '{category}'...",
"regenconf_would_be_updated": "A configuración debería ser actualizada para a categoría '{category}'",
"regenconf_updated": "Configuración actualizada para '{category}'",
"regenconf_up_to_date": "A configuración xa está ao día para a categoría '{category}'",
@ -574,8 +574,8 @@
"root_password_replaced_by_admin_password": "O contrasinal root foi substituído polo teu contrasinal de administración.",
"root_password_desynchronized": "Mudou o contrasinal de administración, pero YunoHost non puido transferir este cambio ao contrasinal root!",
"restore_system_part_failed": "Non se restableceu a parte do sistema '{part}'",
"restore_running_hooks": "Executando os ganchos do restablecemento",
"restore_running_app_script": "Restablecendo a app '{app}'",
"restore_running_hooks": "Executando os ganchos do restablecemento...",
"restore_running_app_script": "Restablecendo a app '{app}'...",
"restore_removing_tmp_dir_failed": "Non se puido eliminar o directorio temporal antigo",
"restore_nothings_done": "Nada foi restablecido",
"restore_not_enough_disk_space": "Non hai espazo abondo (espazo: {free_space.d} B, espazo necesario: {needed_space} B, marxe de seguridade: {margin} B)",
@ -593,7 +593,7 @@
"user_updated": "Cambiada a info da usuaria",
"user_update_failed": "Non se actualizou usuaria {user}: {error}",
"user_unknown": "Usuaria descoñecida: {user}",
"user_home_creation_failed": "Non se puido crear cartafol 'home' para a usuaria",
"user_home_creation_failed": "Non se puido crear cartafol home '{home}' para a usuaria",
"user_deletion_failed": "Non se puido eliminar a usuaria {user}: {error}",
"user_deleted": "Usuaria eliminada",
"user_creation_failed": "Non se puido crear a usuaria {user}: {error}",
@ -613,11 +613,11 @@
"unbackup_app": "{app} non vai ser gardada",
"tools_upgrade_special_packages_completed": "Completada a actualización dos paquetes YunoHost.\nPreme [Enter] para recuperar a liña de comandos",
"tools_upgrade_special_packages_explanation": "A actualización especial continuará en segundo plano. Non inicies outras tarefas no servidor nos seguintes ~10 minutos (depende do hardware). Após isto, podes volver a conectar na webadmin. O rexistro da actualización estará dispoñible en Ferramentas → Rexistro (na webadmin) ou con 'yunohost log list' (na liña de comandos).",
"tools_upgrade_special_packages": "Actualizando paquetes 'special' (yunohost-related)",
"tools_upgrade_special_packages": "Actualizando paquetes 'special' (yunohost-related)...",
"tools_upgrade_regular_packages_failed": "Non se actualizaron os paquetes: {packages_list}",
"tools_upgrade_regular_packages": "Actualizando os paquetes 'regular' (non-yunohost-related)",
"tools_upgrade_cant_unhold_critical_packages": "Non se desbloquearon os paquetes críticos",
"tools_upgrade_cant_hold_critical_packages": "Non se puideron bloquear os paquetes críticos",
"tools_upgrade_regular_packages": "Actualizando os paquetes 'regular' (non-yunohost-related)...",
"tools_upgrade_cant_unhold_critical_packages": "Non se desbloquearon os paquetes críticos...",
"tools_upgrade_cant_hold_critical_packages": "Non se puideron bloquear os paquetes críticos...",
"tools_upgrade_cant_both": "Non se pode actualizar o sistema e as apps ao mesmo tempo",
"tools_upgrade_at_least_one": "Por favor indica 'apps', ou 'system'",
"this_action_broke_dpkg": "Esta acción rachou dpkg/APT (xestores de paquetes do sistema)... Podes intentar resolver o problema conectando a través de SSH e executando `sudo apt install --fix-broken`e/ou `sudo dpkg --configure -a`.",
@ -641,5 +641,23 @@
"service_removed": "Eliminado o servizo '{service}'",
"service_remove_failed": "Non se eliminou o servizo '{service}'",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' xa non se utiliza! Executa 'yunohost tools regen-conf' no seu lugar.",
"service_enabled": "O servizo '{service}' vai ser iniciado automáticamente no inicio do sistema."
"service_enabled": "O servizo '{service}' vai ser iniciado automáticamente no inicio do sistema.",
"diagnosis_apps_allgood": "Tódalas apps instaladas respectan as prácticas básicas de empaquetado",
"diagnosis_apps_bad_quality": "Esta aplicación está actualmente marcada como estragada no catálogo de aplicacións de YunoHost. Podería ser un problema temporal mentras as mantedoras intentan arranxar o problema. Ata ese momento a actualización desta app está desactivada.",
"global_settings_setting_security_nginx_redirect_to_https": "Redirixir peticións HTTP a HTTPs por defecto (NON DESACTIVAR ISTO a non ser que realmente saibas o que fas!)",
"log_user_import": "Importar usuarias",
"user_import_failed": "A operación de importación de usuarias fracasou",
"user_import_missing_columns": "Faltan as seguintes columnas: {columns}",
"user_import_nothing_to_do": "Ningunha usuaria precisa ser importada",
"user_import_partial_failed": "A operación de importación de usuarias fallou parcialmente",
"diagnosis_apps_deprecated_practices": "A versión instalada desta app aínda utiliza algunha das antigas prácticas de empaquetado xa abandonadas. Deberías considerar actualizala.",
"diagnosis_apps_outdated_ynh_requirement": "A versión instalada desta app só require yunohost >= 2.x, que normalmente indica que non está ao día coas prácticas recomendadas de empaquetado e asistentes. Deberías considerar actualizala.",
"user_import_success": "Usuarias importadas correctamente",
"diagnosis_high_number_auth_failures": "Hai un alto número sospeitoso de intentos fallidos de autenticación. Deberías comprobar que fail2ban está a executarse e que está correctamente configurado, ou utiliza un porto personalizado para SSH tal como se explica en https://yunohost.org/security.",
"user_import_bad_file": "O ficheiro CSV non ten o formato correcto e será ignorado para evitar unha potencial perda de datos",
"user_import_bad_line": "Liña incorrecta {line}: {details}",
"diagnosis_description_apps": "Aplicacións",
"diagnosis_apps_broken": "Actualmente esta aplicación está marcada como estragada no catálogo de aplicacións de YunoHost. Podería tratarse dun problema temporal mentras as mantedoras intentan arraxala. Entanto así a actualización da app está desactivada.",
"diagnosis_apps_issue": "Atopouse un problema na app {app}",
"diagnosis_apps_not_in_app_catalog": "Esta aplicación non está no catálgo de aplicacións de YunoHost. Se estivo no pasado e foi eliminada, deberías considerar desinstalala porque non recibirá actualizacións, e podería comprometer a integridade e seguridade do teu sistema."
}

1
locales/id.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -20,13 +20,13 @@
"ask_new_admin_password": "Nova senha de administração",
"ask_password": "Senha",
"backup_created": "Backup completo",
"backup_output_directory_not_empty": "A pasta de destino não se encontra vazia",
"backup_output_directory_not_empty": "Você deve escolher um diretório de saída que esteja vazio",
"custom_app_url_required": "Deve fornecer um link para atualizar a sua aplicação personalizada {app}",
"domain_cert_gen_failed": "Não foi possível gerar o certificado",
"domain_created": "Domínio criado com êxito",
"domain_creation_failed": "Não foi possível criar o domínio",
"domain_creation_failed": "Não foi possível criar o domínio {domain}: {error}",
"domain_deleted": "Domínio removido com êxito",
"domain_deletion_failed": "Não foi possível eliminar o domínio",
"domain_deletion_failed": "Não foi possível eliminar o domínio {domain}: {error}",
"domain_dyndns_already_subscribed": "Já subscreveu um domínio DynDNS",
"domain_dyndns_root_unknown": "Domínio root (administrador) DynDNS desconhecido",
"domain_exists": "O domínio já existe",
@ -34,12 +34,12 @@
"domain_unknown": "Domínio desconhecido",
"done": "Concluído.",
"downloading": "Transferência em curso...",
"dyndns_ip_update_failed": "Não foi possível atualizar o endereço IP a partir de DynDNS",
"dyndns_ip_updated": "Endereço IP atualizado com êxito a partir de DynDNS",
"dyndns_ip_update_failed": "Não foi possível atualizar o endereço IP para DynDNS",
"dyndns_ip_updated": "Endereço IP atualizado com êxito para DynDNS",
"dyndns_key_generating": "A chave DNS está a ser gerada, isto pode demorar um pouco...",
"dyndns_registered": "Dom+inio DynDNS registado com êxito",
"dyndns_registration_failed": "Não foi possível registar o domínio DynDNS: {error}",
"dyndns_unavailable": "Subdomínio DynDNS indisponível",
"dyndns_unavailable": "O domínio '{domain}' não está disponível.",
"extracting": "Extração em curso...",
"field_invalid": "Campo inválido '{}'",
"firewall_reloaded": "Firewall recarregada com êxito",
@ -97,17 +97,17 @@
"app_not_properly_removed": "{app} não foi corretamente removido",
"app_requirements_checking": "Verificando os pacotes necessários para {app}...",
"app_unsupported_remote_type": "A aplicação não possui suporte ao tipo remoto utilizado",
"backup_archive_app_not_found": "A aplicação '{app}' não foi encontrada no arquivo de backup",
"backup_archive_broken_link": "Impossível acessar o arquivo de backup (link quebrado ao {path})",
"backup_archive_name_exists": "O nome do arquivo de backup já existe",
"backup_archive_open_failed": "Não é possível abrir o arquivo de backup",
"backup_cleaning_failed": "Não é possível limpar a pasta temporária de backups",
"backup_creation_failed": "A criação do backup falhou",
"backup_delete_error": "Impossível apagar '{path}'",
"backup_deleted": "O backup foi suprimido",
"backup_hook_unknown": "Gancho de backup '{hook}' desconhecido",
"backup_nothings_done": "Não há nada para guardar",
"backup_output_directory_forbidden": "Diretório de saída proibido. Os backups não podem ser criados em /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives subpastas",
"backup_archive_app_not_found": "Não foi possível encontrar {app} no arquivo de backup",
"backup_archive_broken_link": "Não foi possível acessar o arquivo de backup (link quebrado ao {path})",
"backup_archive_name_exists": "Já existe um arquivo de backup com esse nome.",
"backup_archive_open_failed": "Não foi possível abrir o arquivo de backup",
"backup_cleaning_failed": "Não foi possível limpar o diretório temporário de backup",
"backup_creation_failed": "Não foi possível criar o arquivo de backup",
"backup_delete_error": "Não foi possível remover '{path}'",
"backup_deleted": "Backup removido",
"backup_hook_unknown": "O gancho de backup '{hook}' é desconhecido",
"backup_nothings_done": "Nada há se salvar",
"backup_output_directory_forbidden": "Escolha um diretório de saída diferente. Backups não podem ser criados nos subdiretórios /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives",
"app_already_installed_cant_change_url": "Este aplicativo já está instalado. A URL não pode ser alterada apenas por esta função. Confira em `app changeurl` se está disponível.",
"app_already_up_to_date": "{app} já está atualizado",
"app_argument_choice_invalid": "Use uma das opções '{choices}' para o argumento '{name}'",
@ -117,14 +117,14 @@
"app_location_unavailable": "Esta url ou não está disponível ou está em conflito com outra(s) aplicação(ões) já instalada(s):\n{apps}",
"app_upgrade_app_name": "Atualizando {app}…",
"app_upgrade_some_app_failed": "Não foi possível atualizar algumas aplicações",
"backup_abstract_method": "Este metodo de backup ainda não foi implementado",
"backup_app_failed": "Não foi possível fazer o backup dos aplicativos '{app}'",
"backup_applying_method_custom": "Chamando o metodo personalizado de backup '{method}'…",
"backup_applying_method_tar": "Criando o arquivo tar de backup…",
"backup_abstract_method": "Este método de backup ainda não foi implementado",
"backup_app_failed": "Não foi possível fazer o backup de '{app}'",
"backup_applying_method_custom": "Chamando o método personalizado de backup '{method}'…",
"backup_applying_method_tar": "Criando o arquivo TAR de backup…",
"backup_archive_name_unknown": "Desconhece-se o arquivo local de backup de nome '{name}'",
"backup_archive_system_part_not_available": "A seção do sistema '{part}' está indisponivel neste backup",
"backup_ask_for_copying_if_needed": "Alguns arquivos não consiguiram ser preparados para backup utilizando o metodo que não gasta espaço de disco temporariamente. Para realizar o backup {size}MB precisam ser usados temporariamente. Você concorda?",
"backup_cant_mount_uncompress_archive": "Não foi possível montar em modo leitura o diretorio de arquivos não comprimido",
"backup_archive_system_part_not_available": "A seção do sistema '{part}' está indisponível neste backup",
"backup_ask_for_copying_if_needed": "Você quer efetuar o backup usando {size}MB temporariamente? (E necessário fazer dessa forma porque alguns arquivos não puderam ser preparados usando um método mais eficiente)",
"backup_cant_mount_uncompress_archive": "Não foi possível montar o arquivo descomprimido como protegido contra escrita",
"backup_copying_to_organize_the_archive": "Copiando {size}MB para organizar o arquivo",
"app_change_url_identical_domains": "O antigo e o novo domínio / url_path são idênticos ('{domain}{path}'), nada para fazer.",
"password_too_simple_1": "A senha precisa ter pelo menos 8 caracteres",
@ -143,7 +143,7 @@
"app_change_url_success": "A URL agora é {domain}{path}",
"apps_catalog_obsolete_cache": "O cache do catálogo de aplicações está vazio ou obsoleto.",
"apps_catalog_failed_to_download": "Não foi possível fazer o download do catálogo de aplicações {apps_catalog}: {error}",
"apps_catalog_updating": "Atualizado o catálogo de aplicações…",
"apps_catalog_updating": "Atualizando o catálogo de aplicações...",
"apps_catalog_init_success": "Catálogo de aplicações do sistema inicializado!",
"apps_already_up_to_date": "Todas as aplicações já estão atualizadas",
"app_packaging_format_not_supported": "Essa aplicação não pode ser instalada porque o formato dela não é suportado pela sua versão do YunoHost. Considere atualizar seu sistema.",
@ -164,5 +164,34 @@
"app_manifest_install_ask_path": "Escolha o caminho da url (depois do domínio) em que essa aplicação deve ser instalada",
"app_manifest_install_ask_domain": "Escolha o domínio em que esta aplicação deve ser instalada",
"app_label_deprecated": "Este comando está deprecado! Por favor use o novo comando 'yunohost user permission update' para gerenciar a etiqueta da aplicação.",
"app_make_default_location_already_used": "Não foi passível fazer a aplicação '{app}' ser a padrão no domínio, '{domain}' já está sendo usado por '{other_app}'"
"app_make_default_location_already_used": "Não foi passível fazer a aplicação '{app}' ser a padrão no domínio, '{domain}' já está sendo usado por '{other_app}'",
"backup_archive_writing_error": "Não foi possível adicionar os arquivos '{source}' (nomeados dentro do arquivo '{dest}') ao backup no arquivo comprimido '{archive}'",
"backup_archive_corrupted": "Parece que o arquivo de backup '{archive}' está corrompido: {error}",
"backup_archive_cant_retrieve_info_json": "Não foi possível carregar informações para o arquivo '{archive}'... Não foi possível carregar info.json (ou não é um JSON válido).",
"backup_applying_method_copy": "Copiando todos os arquivos para o backup...",
"backup_actually_backuping": "Criando cópia de backup dos arquivos obtidos...",
"ask_user_domain": "Domínio para usar para o endereço de email e conta XMPP do usuário",
"ask_new_path": "Novo caminho",
"ask_new_domain": "Novo domínio",
"apps_catalog_update_success": "O catálogo de aplicações foi atualizado!",
"backup_no_uncompress_archive_dir": "Não existe tal diretório de arquivo descomprimido",
"backup_mount_archive_for_restore": "Preparando o arquivo para restauração...",
"backup_method_tar_finished": "Arquivo de backup TAR criado",
"backup_method_custom_finished": "Método de backup personalizado '{method}' finalizado",
"backup_method_copy_finished": "Cópia de backup finalizada",
"backup_custom_mount_error": "O método personalizado de backup não pôde passar do passo de 'mount'",
"backup_custom_backup_error": "O método personalizado de backup não pôde passar do passo de 'backup'",
"backup_csv_creation_failed": "Não foi possível criar o arquivo CSV necessário para a restauração",
"backup_csv_addition_failed": "Não foi possível adicionar os arquivos que estarão no backup ao arquivo CSV",
"backup_create_size_estimation": "O arquivo irá conter cerca de {size} de dados.",
"backup_couldnt_bind": "Não foi possível vincular {src} ao {dest}",
"certmanager_attempt_to_replace_valid_cert": "Você está tentando sobrescrever um certificado bom e válido para o domínio {domain}! (Use --force para prosseguir mesmo assim)",
"backup_with_no_restore_script_for_app": "A aplicação {app} não tem um script de restauração, você não será capaz de automaticamente restaurar o backup dessa aplicação.",
"backup_with_no_backup_script_for_app": "A aplicação '{app}' não tem um script de backup. Ignorando.",
"backup_unable_to_organize_files": "Não foi possível usar o método rápido de organizar os arquivos no arquivo de backup",
"backup_system_part_failed": "Não foi possível fazer o backup da parte do sistema '{part}'",
"backup_running_hooks": "Executando os hooks de backup...",
"backup_permission": "Permissão de backup para {app}",
"backup_output_symlink_dir_broken": "O diretório de seu arquivo '{path}' é um link simbólico quebrado. Talvez você tenha esquecido de re/montar ou conectar o dispositivo de armazenamento para onde o link aponta.",
"backup_output_directory_required": "Você deve especificar um diretório de saída para o backup"
}

View file

@ -87,7 +87,7 @@
"restore_cleaning_failed": "Не вдалося очистити тимчасовий каталог відновлення",
"restore_backup_too_old": "Цей архів резервних копій не може бути відновлений, бо він отриманий з дуже старої версії YunoHost.",
"restore_already_installed_apps": "Наступні програми не можуть бути відновлені, тому що вони вже встановлені: {apps}",
"restore_already_installed_app": "Застосунок з ID \"{app} 'вже встановлено",
"restore_already_installed_app": "Застосунок з ID «{app}» вже встановлено",
"regex_with_only_domain": "Ви не можете використовувати regex для домену, тільки для шляху",
"regex_incompatible_with_tile": "/! \\ Packagers! Дозвіл '{permission}' має значення show_tile 'true', тому ви не можете визначити regex URL в якості основної URL",
"regenconf_need_to_explicitly_specify_ssh": "Конфігурація ssh була змінена вручну, але вам потрібно явно вказати категорію 'ssh' з --force, щоб застосувати зміни.",
@ -175,7 +175,7 @@
"migration_0015_cleaning_up": "Очищення кеш-пам'яті і пакетів, які більше не потрібні...",
"migration_0015_specific_upgrade": "Початок оновлення системних пакетів, які повинні бути оновлені незалежно...",
"migration_0015_modified_files": "Зверніть увагу, що такі файли були змінені вручну і можуть бути перезаписані після оновлення: {manually_modified_files}",
"migration_0015_problematic_apps_warning": "Зверніть увагу, що були виявлені наступні, можливо, проблемні встановлені застосунки. Схоже, що вони не були встановлені з каталогу застосунків YunoHost або не зазначені як \"робочі\". Отже, не можна гарантувати, що вони будуть працювати після оновлення: {problematic_apps}",
"migration_0015_problematic_apps_warning": "Зверніть увагу, що були виявлені наступні, можливо, проблемні встановлені застосунки. Схоже, що вони не були встановлені з каталогу застосунків YunoHost або не зазначені як «робочі». Отже, не можна гарантувати, що вони будуть працювати після оновлення: {problematic_apps}",
"migration_0015_general_warning": "Будь ласка, зверніть увагу, що ця міграція є делікатною операцією. Команда YunoHost зробила все можливе, щоб перевірити і протестувати її, але міграція все ще може порушити частина системи або її застосунків.\n\nТому рекомендовано:\n - Виконати резервне копіювання всіх важливих даних або застосунків. Подробиці на сайті https://yunohost.org/backup; \n - Наберіться терпіння після запуску міграції: В залежності від вашого з'єднання з Інтернетом і апаратного забезпечення, оновлення може зайняти до декількох годин.",
"migration_0015_system_not_fully_up_to_date": "Ваша система не повністю оновлена. Будь ласка, виконайте регулярне оновлення перед запуском міграції на Buster.",
"migration_0015_not_enough_free_space": "Вільного місця в /var/ досить мало! У вас повинно бути не менше 1 ГБ вільного місця, щоб запустити цю міграцію.",
@ -211,11 +211,11 @@
"log_tools_postinstall": "Післявстановлення сервера YunoHost",
"log_tools_migrations_migrate_forward": "Запущено міграції",
"log_domain_main_domain": "Зроблено '{}' основним доменом",
"log_user_permission_reset": "Скинуто дозвіл \"{} '",
"log_user_permission_reset": "Скинуто дозвіл «{}»",
"log_user_permission_update": "Оновлено доступи для дозволу '{}'",
"log_user_update": "Оновлено відомості для користувача '{}'",
"log_user_group_update": "Оновлено групу '{}'",
"log_user_group_delete": "Видалено групу \"{} '",
"log_user_group_delete": "Видалено групу «{}»",
"log_user_group_create": "Створено групу '{}'",
"log_user_delete": "Видалення користувача '{}'",
"log_user_create": "Додавання користувача '{}'",
@ -238,12 +238,12 @@
"log_available_on_yunopaste": "Цей журнал тепер доступний за посиланням {url}",
"log_app_config_apply": "Застосування конфігурації до застосунку '{}'",
"log_app_config_show_panel": "Показ панелі конфігурації застосунку '{}'",
"log_app_action_run": "Запуск дії застосунку \"{} '",
"log_app_action_run": "Запуск дії застосунку «{}»",
"log_app_makedefault": "Застосунок '{}' зроблено типовим",
"log_app_upgrade": "Оновлення застосунку '{}'",
"log_app_remove": "Вилучення застосунку '{}'",
"log_app_install": "Установлення застосунку '{}'",
"log_app_change_url": "Змінення URL-адреси застосунку \"{} '",
"log_app_change_url": "Змінення URL-адреси застосунку «{}»",
"log_operation_unit_unclosed_properly": "Блок операцій не був закритий належним чином",
"log_does_exists": "Немає журналу операцій з назвою '{log}', використовуйте 'yunohost log list', щоб подивитися всі доступні журнали операцій",
"log_help_to_get_failed_log": "Операція '{desc}' не може бути завершена. Будь ласка, поділіться повним журналом цієї операції, використовуючи команду 'yunohost log share {name}', щоб отримати допомогу",
@ -507,7 +507,7 @@
"diagnosis_ip_connected_ipv6": "Сервер під'єднаний до Інтернету через IPv6!",
"diagnosis_ip_no_ipv4": "Сервер не має робочого IPv4.",
"diagnosis_ip_connected_ipv4": "Сервер під'єднаний до Інтернету через IPv4!",
"diagnosis_no_cache": "Для категорії \"{category} 'ще немає кеша діагностики",
"diagnosis_no_cache": "Для категорії «{category}» ще немає кеша діагностики",
"diagnosis_failed": "Не вдалося отримати результат діагностики для категорії '{category}': {error}",
"diagnosis_everything_ok": "Усе виглядає добре для {category}!",
"diagnosis_found_warnings": "Знайдено {warnings} пунктів, які можна поліпшити для {category}.",
@ -657,5 +657,7 @@
"diagnosis_apps_broken": "Цей застосунок наразі позначено як зламаний у каталозі застосунків YunoHost. Це може бути тимчасовою проблемою, поки організатори намагаються вирішити цю проблему. Тим часом оновлення цього застосунку вимкнено.",
"diagnosis_apps_not_in_app_catalog": "Цей застосунок не міститься у каталозі застосунків YunoHost. Якщо він був у минулому і був видалений, вам слід подумати про видалення цього застосунку, оскільки він не отримає оновлення, і це може поставити під загрозу цілісність та безпеку вашої системи.",
"diagnosis_apps_issue": "Виявлено проблему із застосунком {app}",
"diagnosis_apps_allgood": "Усі встановлені застосунки дотримуються основних способів упакування"
"diagnosis_apps_allgood": "Усі встановлені застосунки дотримуються основних способів упакування",
"diagnosis_high_number_auth_failures": "Останнім часом сталася підозріло велика кількість помилок автентифікації. Ви можете переконатися, що fail2ban працює і правильно налаштований, або скористатися власним портом для SSH, як описано в https://yunohost.org/security.",
"global_settings_setting_security_nginx_redirect_to_https": "Типово переспрямовувати HTTP-запити до HTTP (НЕ ВИМИКАЙТЕ, якщо ви дійсно не знаєте, що робите!)"
}

View file

@ -511,7 +511,12 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
"""
from packaging import version
from yunohost.hook import hook_add, hook_remove, hook_callback, hook_exec_with_script_debug_if_failure
from yunohost.hook import (
hook_add,
hook_remove,
hook_callback,
hook_exec_with_script_debug_if_failure,
)
from yunohost.permission import permission_sync_to_user
from yunohost.regenconf import manually_modified_files
@ -633,12 +638,17 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
# Execute the app upgrade script
upgrade_failed = True
try:
upgrade_failed, failure_message_with_debug_instructions = hook_exec_with_script_debug_if_failure(
(
upgrade_failed,
failure_message_with_debug_instructions,
) = hook_exec_with_script_debug_if_failure(
extracted_app_folder + "/scripts/upgrade",
env=env_dict,
operation_logger=operation_logger,
error_message_if_script_failed=m18n.n("app_upgrade_script_failed"),
error_message_if_failed=lambda e: m18n.n("app_upgrade_failed", app=app_instance_name, error=e)
error_message_if_failed=lambda e: m18n.n(
"app_upgrade_failed", app=app_instance_name, error=e
),
)
finally:
# Whatever happened (install success or failure) we check if it broke the system
@ -669,7 +679,7 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
if upgrade_failed or broke_the_system:
# display this if there are remaining apps
if apps[number + 1:]:
if apps[number + 1 :]:
not_upgraded_apps = apps[number:]
logger.error(
m18n.n(
@ -785,7 +795,13 @@ def app_install(
force -- Do not ask for confirmation when installing experimental / low-quality apps
"""
from yunohost.hook import hook_add, hook_remove, hook_callback, hook_exec, hook_exec_with_script_debug_if_failure
from yunohost.hook import (
hook_add,
hook_remove,
hook_callback,
hook_exec,
hook_exec_with_script_debug_if_failure,
)
from yunohost.log import OperationLogger
from yunohost.permission import (
user_permission_list,
@ -976,12 +992,17 @@ def app_install(
# Execute the app install script
install_failed = True
try:
install_failed, failure_message_with_debug_instructions = hook_exec_with_script_debug_if_failure(
(
install_failed,
failure_message_with_debug_instructions,
) = hook_exec_with_script_debug_if_failure(
os.path.join(extracted_app_folder, "scripts/install"),
env=env_dict,
operation_logger=operation_logger,
error_message_if_script_failed=m18n.n("app_install_script_failed"),
error_message_if_failed=lambda e: m18n.n("app_install_failed", app=app_id, error=e)
error_message_if_failed=lambda e: m18n.n(
"app_install_failed", app=app_id, error=e
),
)
finally:
# If success so far, validate that app didn't break important stuff
@ -1671,7 +1692,9 @@ def app_config_get(app, key="", full=False, export=False):
Display an app configuration in classic, full or export mode
"""
if full and export:
raise YunohostValidationError("You can't use --full and --export together.", raw_msg=True)
raise YunohostValidationError(
"You can't use --full and --export together.", raw_msg=True
)
if full:
mode = "full"

View file

@ -1496,13 +1496,18 @@ class RestoreManager:
# Execute the app install script
restore_failed = True
try:
restore_failed, failure_message_with_debug_instructions = hook_exec_with_script_debug_if_failure(
(
restore_failed,
failure_message_with_debug_instructions,
) = hook_exec_with_script_debug_if_failure(
restore_script,
chdir=app_backup_in_archive,
env=env_dict,
operation_logger=operation_logger,
error_message_if_script_failed=m18n.n("app_restore_script_failed"),
error_message_if_failed=lambda e: m18n.n("app_restore_failed", app=app_instance_name, error=e)
error_message_if_failed=lambda e: m18n.n(
"app_restore_failed", app=app_instance_name, error=e
),
)
finally:
# Cleaning temporary scripts directory

View file

@ -288,7 +288,11 @@ def service_reload_or_restart(names, test_conf=True):
if p.returncode != 0:
errors = out.decode().strip().split("\n")
logger.error(
m18n.n("service_not_reloading_because_conf_broken", name=name, errors=errors)
m18n.n(
"service_not_reloading_because_conf_broken",
name=name,
errors=errors,
)
)
continue

View file

@ -80,7 +80,6 @@ def legacy_app(request):
return "legacy_app"
@pytest.fixture()
def config_app(request):
@ -105,9 +104,7 @@ def test_app_config_get(config_app):
assert isinstance(app_config_get(config_app, export=True), dict)
assert isinstance(app_config_get(config_app, "main"), dict)
assert isinstance(app_config_get(config_app, "main.components"), dict)
# Is it expected that we return None if no value defined yet ?
# c.f. the whole discussion about "should we have defaults" etc.
assert app_config_get(config_app, "main.components.boolean") is None
assert app_config_get(config_app, "main.components.boolean") == "0"
def test_app_config_nopanel(legacy_app):
@ -130,10 +127,14 @@ def test_app_config_get_nonexistentstuff(config_app):
with pytest.raises(YunohostValidationError):
app_config_get(config_app, "main.components.nonexistent")
app_setting(config_app, "boolean", delete=True)
with pytest.raises(YunohostValidationError):
app_config_get(config_app, "main.components.boolean")
def test_app_config_regular_setting(config_app):
assert app_config_get(config_app, "main.components.boolean") is None
assert app_config_get(config_app, "main.components.boolean") == "0"
app_config_set(config_app, "main.components.boolean", "no")
@ -166,7 +167,10 @@ def test_app_config_bind_on_file(config_app):
def test_app_config_custom_get(config_app):
assert app_setting(config_app, "arg9") is None
assert "Files in /var/www" in app_config_get(config_app, "bind.function.arg9")["ask"]["en"]
assert (
"Files in /var/www"
in app_config_get(config_app, "bind.function.arg9")["ask"]["en"]
)
assert app_setting(config_app, "arg9") is None

View file

@ -12,7 +12,7 @@ from yunohost import domain, user
from yunohost.utils.config import (
parse_args_in_yunohost_format,
PasswordQuestion,
Question
Question,
)
from yunohost.utils.error import YunohostError
@ -75,8 +75,7 @@ def test_question_string_no_input():
]
answers = {}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -90,8 +89,9 @@ def test_question_string_input():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -104,8 +104,9 @@ def test_question_string_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -133,8 +134,9 @@ def test_question_string_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -149,8 +151,9 @@ def test_question_string_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_string": ("", "string")})
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -164,8 +167,9 @@ def test_question_string_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -198,8 +202,11 @@ def test_question_string_input_test_ask():
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text, is_password=False, confirm=False,
prefill='', is_multiline=False
message=ask_text,
is_password=False,
confirm=False,
prefill="",
is_multiline=False,
)
@ -221,8 +228,10 @@ def test_question_string_input_test_ask_with_default():
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=default_text, is_multiline=False
is_password=False,
confirm=False,
prefill=default_text,
is_multiline=False,
)
@ -243,8 +252,8 @@ def test_question_string_input_test_ask_with_example():
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert example_text in prompt.call_args[1]["message"]
@pytest.mark.skip # we should do something with this help
@ -264,8 +273,8 @@ def test_question_string_input_test_ask_with_help():
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert help_text in prompt.call_args[1]["message"]
def test_question_string_with_choice():
@ -279,8 +288,9 @@ def test_question_string_with_choice_prompt():
questions = [{"name": "some_string", "type": "string", "choices": ["fr", "en"]}]
answers = {"some_string": "fr"}
expected_result = OrderedDict({"some_string": ("fr", "string")})
with patch.object(Moulinette, "prompt", return_value="fr"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="fr"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -288,8 +298,7 @@ def test_question_string_with_choice_bad():
questions = [{"name": "some_string", "type": "string", "choices": ["fr", "en"]}]
answers = {"some_string": "bad"}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions)
@ -305,13 +314,14 @@ def test_question_string_with_choice_ask():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value="ru") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="ru") as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
for choice in choices:
assert choice in prompt.call_args[1]['message']
assert choice in prompt.call_args[1]["message"]
def test_question_string_with_choice_default():
@ -352,8 +362,7 @@ def test_question_password_no_input():
]
answers = {}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -366,14 +375,16 @@ def test_question_password_input():
}
]
answers = {}
Question.operation_logger = { 'data_to_redact': [] }
Question.operation_logger = {"data_to_redact": []}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
def test_question_password_input_no_ask():
@ -387,9 +398,11 @@ def test_question_password_input_no_ask():
expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -405,8 +418,9 @@ def test_question_password_no_input_optional():
expected_result = OrderedDict({"some_password": ("", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(os, "isatty", return_value=False):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
questions = [
@ -414,8 +428,9 @@ def test_question_password_no_input_optional():
]
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(os, "isatty", return_value=False):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -432,9 +447,11 @@ def test_question_password_optional_with_input():
expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -451,9 +468,11 @@ def test_question_password_optional_with_empty_input():
expected_result = OrderedDict({"some_password": ("", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -469,9 +488,11 @@ def test_question_password_optional_with_input_without_ask():
expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -487,8 +508,7 @@ def test_question_password_no_input_default():
answers = {}
# no default for password!
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -505,8 +525,7 @@ def test_question_password_no_input_example():
answers = {"some_password": "some_value"}
# no example for password!
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -522,14 +541,20 @@ def test_question_password_input_test_ask():
answers = {}
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=True, confirm=True,
prefill='', is_multiline=False
is_password=True,
confirm=True,
prefill="",
is_multiline=False,
)
@ -548,12 +573,16 @@ def test_question_password_input_test_ask_with_example():
answers = {}
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert example_text in prompt.call_args[1]["message"]
@pytest.mark.skip # we should do something with this help
@ -571,12 +600,16 @@ def test_question_password_input_test_ask_with_help():
answers = {}
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Question.operation_logger, "data_to_redact", create=True
), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert help_text in prompt.call_args[1]["message"]
def test_question_password_bad_chars():
@ -590,8 +623,9 @@ def test_question_password_bad_chars():
]
for i in PasswordQuestion.forbidden_chars:
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(
os, "isatty", return_value=False
):
parse_args_in_yunohost_format({"some_password": i * 8}, questions)
@ -605,13 +639,11 @@ def test_question_password_strong_enough():
}
]
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
# too short
parse_args_in_yunohost_format({"some_password": "a"}, questions)
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format({"some_password": "password"}, questions)
@ -625,13 +657,11 @@ def test_question_password_optional_strong_enough():
}
]
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
# too short
parse_args_in_yunohost_format({"some_password": "a"}, questions)
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format({"some_password": "password"}, questions)
@ -656,8 +686,7 @@ def test_question_path_no_input():
]
answers = {}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -672,8 +701,9 @@ def test_question_path_input():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -687,8 +717,9 @@ def test_question_path_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -718,8 +749,9 @@ def test_question_path_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -735,8 +767,9 @@ def test_question_path_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_path": ("", "path")})
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -751,8 +784,9 @@ def test_question_path_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -788,8 +822,10 @@ def test_question_path_input_test_ask():
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill='', is_multiline=False
is_password=False,
confirm=False,
prefill="",
is_multiline=False,
)
@ -812,8 +848,10 @@ def test_question_path_input_test_ask_with_default():
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=default_text, is_multiline=False
is_password=False,
confirm=False,
prefill=default_text,
is_multiline=False,
)
@ -835,8 +873,8 @@ def test_question_path_input_test_ask_with_example():
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert example_text in prompt.call_args[1]["message"]
@pytest.mark.skip # we should do something with this help
@ -857,8 +895,8 @@ def test_question_path_input_test_ask_with_help():
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert help_text in prompt.call_args[1]["message"]
def test_question_boolean():
@ -906,8 +944,7 @@ def test_question_boolean_all_yes():
== expected_result
)
assert (
parse_args_in_yunohost_format({"some_boolean": 1}, questions)
== expected_result
parse_args_in_yunohost_format({"some_boolean": 1}, questions) == expected_result
)
assert (
parse_args_in_yunohost_format({"some_boolean": True}, questions)
@ -960,8 +997,7 @@ def test_question_boolean_all_no():
== expected_result
)
assert (
parse_args_in_yunohost_format({"some_boolean": 0}, questions)
== expected_result
parse_args_in_yunohost_format({"some_boolean": 0}, questions) == expected_result
)
assert (
parse_args_in_yunohost_format({"some_boolean": False}, questions)
@ -1005,8 +1041,7 @@ def test_question_boolean_bad_input():
]
answers = {"some_boolean": "stuff"}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1021,13 +1056,15 @@ def test_question_boolean_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="y"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(Moulinette, "prompt", return_value="n"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="n"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1041,8 +1078,9 @@ def test_question_boolean_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="y"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1072,8 +1110,9 @@ def test_question_boolean_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="y"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1089,8 +1128,9 @@ def test_question_boolean_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")}) # default to false
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1105,8 +1145,9 @@ def test_question_boolean_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(Moulinette, "prompt", return_value="n"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="n"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1150,13 +1191,16 @@ def test_question_boolean_input_test_ask():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value=0) as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=0) as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text + " [yes | no]",
is_password=False, confirm=False,
prefill='no', is_multiline=False
is_password=False,
confirm=False,
prefill="no",
is_multiline=False,
)
@ -1173,13 +1217,16 @@ def test_question_boolean_input_test_ask_with_default():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value=1) as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=1) as prompt, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text + " [yes | no]",
is_password=False, confirm=False,
prefill='yes', is_multiline=False
is_password=False,
confirm=False,
prefill="yes",
is_multiline=False,
)
@ -1194,9 +1241,13 @@ def test_question_domain_empty():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
answers = {}
with patch.object(domain, "_get_maindomain", return_value="my_main_domain.com"),\
patch.object(domain, "domain_list", return_value={"domains": [main_domain]}), \
patch.object(os, "isatty", return_value=False):
with patch.object(
domain, "_get_maindomain", return_value="my_main_domain.com"
), patch.object(
domain, "domain_list", return_value={"domains": [main_domain]}
), patch.object(
os, "isatty", return_value=False
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1214,7 +1265,7 @@ def test_question_domain():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(
domain, "_get_maindomain", return_value=main_domain
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1234,7 +1285,7 @@ def test_question_domain_two_domains():
expected_result = OrderedDict({"some_domain": (other_domain, "domain")})
with patch.object(
domain, "_get_maindomain", return_value=main_domain
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1242,7 +1293,7 @@ def test_question_domain_two_domains():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(
domain, "_get_maindomain", return_value=main_domain
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1261,10 +1312,11 @@ def test_question_domain_two_domains_wrong_answer():
answers = {"some_domain": "doesnt_exist.pouet"}
with patch.object(
domain, "_get_maindomain", return_value=main_domain
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(
os, "isatty", return_value=False
):
parse_args_in_yunohost_format(answers, questions)
@ -1283,9 +1335,12 @@ def test_question_domain_two_domains_default_no_ask():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=False):
domain, "_get_maindomain", return_value=main_domain
), patch.object(
domain, "domain_list", return_value={"domains": domains}
), patch.object(
os, "isatty", return_value=False
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1299,9 +1354,12 @@ def test_question_domain_two_domains_default():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=False):
domain, "_get_maindomain", return_value=main_domain
), patch.object(
domain, "domain_list", return_value={"domains": domains}
), patch.object(
os, "isatty", return_value=False
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1314,9 +1372,12 @@ def test_question_domain_two_domains_default_input():
answers = {}
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=True):
domain, "_get_maindomain", return_value=main_domain
), patch.object(
domain, "domain_list", return_value={"domains": domains}
), patch.object(
os, "isatty", return_value=True
):
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(Moulinette, "prompt", return_value=main_domain):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1346,8 +1407,9 @@ def test_question_user_empty():
answers = {}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(
os, "isatty", return_value=False
):
parse_args_in_yunohost_format(answers, questions)
@ -1373,9 +1435,10 @@ def test_question_user():
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(user, "user_list", return_value={"users": users}), patch.object(
user, "user_info", return_value={}
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
def test_question_user_two_users():
@ -1407,16 +1470,18 @@ def test_question_user_two_users():
answers = {"some_user": other_user}
expected_result = OrderedDict({"some_user": (other_user, "user")})
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(user, "user_list", return_value={"users": users}), patch.object(
user, "user_info", return_value={}
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
answers = {"some_user": username}
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(user, "user_list", return_value={"users": users}), patch.object(
user, "user_info", return_value={}
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
def test_question_user_two_users_wrong_answer():
@ -1448,8 +1513,9 @@ def test_question_user_two_users_wrong_answer():
answers = {"some_user": "doesnt_exist.pouet"}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(
os, "isatty", return_value=False
):
parse_args_in_yunohost_format(answers, questions)
@ -1477,8 +1543,9 @@ def test_question_user_two_users_no_default():
answers = {}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(
os, "isatty", return_value=False
):
parse_args_in_yunohost_format(answers, questions)
@ -1505,21 +1572,20 @@ def test_question_user_two_users_default_input():
questions = [{"name": "some_user", "type": "user", "ask": "choose a user"}]
answers = {}
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(os, "isatty", return_value=True):
with patch.object(user, "user_list", return_value={"users": users}), patch.object(
os, "isatty", return_value=True
):
with patch.object(user, "user_info", return_value={}):
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(Moulinette, "prompt", return_value=username):
assert (
parse_args_in_yunohost_format(answers, questions)
== expected_result
parse_args_in_yunohost_format(answers, questions) == expected_result
)
expected_result = OrderedDict({"some_user": (other_user, "user")})
with patch.object(Moulinette, "prompt", return_value=other_user):
assert (
parse_args_in_yunohost_format(answers, questions)
== expected_result
parse_args_in_yunohost_format(answers, questions) == expected_result
)
@ -1544,8 +1610,7 @@ def test_question_number_no_input():
]
answers = {}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1558,13 +1623,11 @@ def test_question_number_bad_input():
]
answers = {"some_number": "stuff"}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
answers = {"some_number": 1.5}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1579,17 +1642,20 @@ def test_question_number_input():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="1337"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(Moulinette, "prompt", return_value=1337), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value=1337), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
expected_result = OrderedDict({"some_number": (0, "number")})
with patch.object(Moulinette, "prompt", return_value="0"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="0"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1603,8 +1669,9 @@ def test_question_number_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="1337"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1634,8 +1701,9 @@ def test_question_number_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="1337"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1650,8 +1718,9 @@ def test_question_number_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_number": (0, "number")})
with patch.object(Moulinette, "prompt", return_value="0"), \
patch.object(os, "isatty", return_value=True):
with patch.object(Moulinette, "prompt", return_value="0"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1680,8 +1749,7 @@ def test_question_number_bad_default():
}
]
answers = {}
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
with pytest.raises(YunohostError), patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1696,13 +1764,16 @@ def test_question_number_input_test_ask():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Moulinette, "prompt", return_value="1111"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill='', is_multiline=False
is_password=False,
confirm=False,
prefill="",
is_multiline=False,
)
@ -1719,13 +1790,16 @@ def test_question_number_input_test_ask_with_default():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Moulinette, "prompt", return_value="1111"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=str(default_value), is_multiline=False
is_password=False,
confirm=False,
prefill=str(default_value),
is_multiline=False,
)
@ -1743,11 +1817,12 @@ def test_question_number_input_test_ask_with_example():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Moulinette, "prompt", return_value="1111"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert example_value in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert example_value in prompt.call_args[1]["message"]
@pytest.mark.skip # we should do something with this help
@ -1764,18 +1839,20 @@ def test_question_number_input_test_ask_with_help():
]
answers = {}
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
with patch.object(
Moulinette, "prompt", return_value="1111"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[1]['message']
assert help_value in prompt.call_args[1]['message']
assert ask_text in prompt.call_args[1]["message"]
assert help_value in prompt.call_args[1]["message"]
def test_question_display_text():
questions = [{"name": "some_app", "type": "display_text", "ask": "foobar"}]
answers = {}
with patch.object(sys, "stdout", new_callable=StringIO) as stdout, \
patch.object(os, "isatty", return_value=True):
with patch.object(sys, "stdout", new_callable=StringIO) as stdout, patch.object(
os, "isatty", return_value=True
):
parse_args_in_yunohost_format(answers, questions)
assert "foobar" in stdout.getvalue()

View file

@ -98,7 +98,9 @@ class ConfigPanel:
return result
def set(self, key=None, value=None, args=None, args_file=None, operation_logger=None):
def set(
self, key=None, value=None, args=None, args_file=None, operation_logger=None
):
self.filter_key = key or ""
# Read config panel toml
@ -108,7 +110,10 @@ class ConfigPanel:
raise YunohostValidationError("config_no_panel")
if (args is not None or args_file is not None) and value is not None:
raise YunohostValidationError("You should either provide a value, or a serie of args/args_file, but not both at the same time", raw_msg=True)
raise YunohostValidationError(
"You should either provide a value, or a serie of args/args_file, but not both at the same time",
raw_msg=True,
)
if self.filter_key.count(".") != 2 and value is not None:
raise YunohostValidationError("config_cant_set_value_on_section")
@ -167,7 +172,10 @@ class ConfigPanel:
# Split filter_key
filter_key = self.filter_key.split(".") if self.filter_key != "" else []
if len(filter_key) > 3:
raise YunohostError(f"The filter key {filter_key} has too many sub-levels, the max is 3.", raw_msg=True)
raise YunohostError(
f"The filter key {filter_key} has too many sub-levels, the max is 3.",
raw_msg=True,
)
if not os.path.exists(self.config_path):
logger.debug(f"Config panel {self.config_path} doesn't exists")
@ -192,7 +200,7 @@ class ConfigPanel:
"default": {
"name": "",
"services": [],
"actions": {"apply": {"en": "Apply"}}
"actions": {"apply": {"en": "Apply"}},
},
},
"sections": {
@ -201,15 +209,34 @@ class ConfigPanel:
"name": "",
"services": [],
"optional": True,
}
},
},
"options": {
"properties": ["ask", "type", "bind", "help", "example",
"style", "icon", "placeholder", "visible",
"optional", "choices", "yes", "no", "pattern",
"limit", "min", "max", "step", "accept", "redact"],
"default": {}
}
"properties": [
"ask",
"type",
"bind",
"help",
"example",
"default",
"style",
"icon",
"placeholder",
"visible",
"optional",
"choices",
"yes",
"no",
"pattern",
"limit",
"min",
"max",
"step",
"accept",
"redact",
],
"default": {},
},
}
def convert(toml_node, node_type):
@ -221,14 +248,16 @@ class ConfigPanel:
This function detects all children nodes and put them in a list
"""
# Prefill the node default keys if needed
default = format_description[node_type]['default']
default = format_description[node_type]["default"]
node = {key: toml_node.get(key, value) for key, value in default.items()}
properties = format_description[node_type]['properties']
properties = format_description[node_type]["properties"]
# Define the filter_key part to use and the children type
i = list(format_description).index(node_type)
subnode_type = list(format_description)[i + 1] if node_type != "options" else None
subnode_type = (
list(format_description)[i + 1] if node_type != "options" else None
)
search_key = filter_key[i] if len(filter_key) > i else False
for key, value in toml_node.items():
@ -266,14 +295,47 @@ class ConfigPanel:
"config_unknown_filter_key", filter_key=self.filter_key
)
# List forbidden keywords from helpers and sections toml (to avoid conflict)
forbidden_keywords = [
"old",
"app",
"changed",
"file_hash",
"binds",
"types",
"formats",
"getter",
"setter",
"short_setting",
"type",
"bind",
"nothing_changed",
"changes_validated",
"result",
"max_progression",
]
forbidden_keywords += format_description["sections"]
for _, _, option in self._iterate():
if option["id"] in forbidden_keywords:
raise YunohostError("config_forbidden_keyword", keyword=option["id"])
return self.config
def _hydrate(self):
# Hydrating config panel with current value
logger.debug("Hydrating config with current values")
for _, _, option in self._iterate():
if option["name"] not in self.values:
continue
if option["id"] not in self.values:
allowed_empty_types = ["alert", "display_text", "markdown", "file"]
if (
option["type"] in allowed_empty_types
or option.get("bind") == "null"
):
continue
else:
raise YunohostError(
f"Config panel question '{option['id']}' should be initialized with a value during install or upgrade.", raw_msg=True
)
value = self.values[option["name"]]
# In general, the value is just a simple value.
# Sometimes it could be a dict used to overwrite the option itself
@ -378,6 +440,7 @@ class ConfigPanel:
class Question(object):
hide_user_input_in_prompt = False
operation_logger = None
pattern = None
def __init__(self, question, user_answers):
self.name = question["name"]
@ -386,7 +449,7 @@ class Question(object):
self.current_value = question.get("current_value")
self.optional = question.get("optional", False)
self.choices = question.get("choices", [])
self.pattern = question.get("pattern")
self.pattern = question.get("pattern", self.pattern)
self.ask = question.get("ask", {"en": self.name})
self.help = question.get("help")
self.value = user_answers.get(self.name)
@ -427,14 +490,11 @@ class Question(object):
)
# Apply default value
class_default= getattr(self, "default_value", None)
if self.value in [None, ""] and \
(self.default is not None or class_default is not None):
self.value = (
class_default
if self.default is None
else self.default
)
class_default = getattr(self, "default_value", None)
if self.value in [None, ""] and (
self.default is not None or class_default is not None
):
self.value = class_default if self.default is None else self.default
# Normalization
# This is done to enforce a certain formating like for boolean
@ -527,6 +587,52 @@ class StringQuestion(Question):
default_value = ""
class EmailQuestion(StringQuestion):
pattern = {
"regexp": r"^.+@.+",
"error": "config_validate_email", # i18n: config_validate_email
}
class URLQuestion(StringQuestion):
pattern = {
"regexp": r"^https?://.*$",
"error": "config_validate_url", # i18n: config_validate_url
}
class DateQuestion(StringQuestion):
pattern = {
"regexp": r"^\d{4}-\d\d-\d\d$",
"error": "config_validate_date", # i18n: config_validate_date
}
def _prevalidate(self):
from datetime import datetime
super()._prevalidate()
if self.value not in [None, ""]:
try:
datetime.strptime(self.value, "%Y-%m-%d")
except ValueError:
raise YunohostValidationError("config_validate_date")
class TimeQuestion(StringQuestion):
pattern = {
"regexp": r"^(1[12]|0?\d):[0-5]\d$",
"error": "config_validate_time", # i18n: config_validate_time
}
class ColorQuestion(StringQuestion):
pattern = {
"regexp": r"^#[ABCDEFabcdef\d]{3,6}$",
"error": "config_validate_color", # i18n: config_validate_color
}
class TagsQuestion(Question):
argument_type = "tags"
@ -675,7 +781,9 @@ class DomainQuestion(Question):
def _raise_invalid_answer(self):
raise YunohostValidationError(
"app_argument_invalid", name=self.name, error=m18n.n("domain_name_unknown", domain=self.value)
"app_argument_invalid",
name=self.name,
error=m18n.n("domain_name_unknown", domain=self.value),
)
@ -737,14 +845,14 @@ class NumberQuestion(Question):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("invalid_number"),
error=m18n.n("invalid_number_min", min=self.min),
)
if self.max is not None and int(self.value) > self.max:
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("invalid_number"),
error=m18n.n("invalid_number_max", max=self.max),
)
@ -756,7 +864,9 @@ class DisplayTextQuestion(Question):
super().__init__(question, user_answers)
self.optional = True
self.style = question.get("style", "info" if question['type'] == 'alert' else '')
self.style = question.get(
"style", "info" if question["type"] == "alert" else ""
)
def _format_text_for_user_input_in_cli(self):
text = _value_for_locale(self.ask)
@ -789,9 +899,9 @@ class FileQuestion(Question):
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
if question.get("accept"):
self.accept = question.get("accept").replace(" ", "").split(",")
self.accept = question.get("accept")
else:
self.accept = []
self.accept = ""
if Moulinette.interface.type == "api":
if user_answers.get(f"{self.name}[name]"):
self.value = {
@ -818,7 +928,9 @@ class FileQuestion(Question):
return
filename = self.value if isinstance(self.value, str) else self.value["filename"]
if "." not in filename or "." + filename.split(".")[-1] not in self.accept:
if "." not in filename or "." + filename.split(".")[
-1
] not in self.accept.replace(" ", "").split(","):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
@ -871,11 +983,11 @@ ARGUMENTS_TYPE_PARSERS = {
"text": StringQuestion,
"select": StringQuestion,
"tags": TagsQuestion,
"email": StringQuestion,
"url": StringQuestion,
"date": StringQuestion,
"time": StringQuestion,
"color": StringQuestion,
"email": EmailQuestion,
"url": URLQuestion,
"date": DateQuestion,
"time": TimeQuestion,
"color": ColorQuestion,
"password": PasswordQuestion,
"path": PathQuestion,
"boolean": BooleanQuestion,