Merge branch 'stretch-unstable' into stdinfo

This commit is contained in:
Alexandre Aubin 2018-08-22 17:09:57 +00:00
commit efa9c9bd69
19 changed files with 366 additions and 55 deletions

View file

@ -11,7 +11,7 @@ prototype interfaces for your application.
Issues Issues
------ ------
- [Please report issues on YunoHost bugtracker](https://dev.yunohost.org/projects/yunohost/issues) (no registration needed). - [Please report issues on YunoHost bugtracker](https://github.com/YunoHost/issues).
Overview Overview
-------- --------

92
debian/changelog vendored
View file

@ -1,3 +1,95 @@
moulinette (3.1.0) stable; urgency=low
* Fix datetime output formats (#163)
* Add the missing Info: prefix for info messages
* Rework a bit the way we handle async outputs (#166)
* Don't crash the moulinette if we fail to format a string (#168)
Thanks to all contributors : ljf, Bram, Aleks !
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 15 Aug 2018 21:44:00 +0000
moulinette (3.0.0) stable; urgency=low
Merging with Jessie's branches
Releasing as stable
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 17 Jun 2018 03:46:00 +0000
moulinette (3.0.0~beta1.1) testing; urgency=low
Merging with Jessie's branches
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 28 May 2018 02:55:00 +0000
moulinette (3.0.0~beta1) testing; urgency=low
Beta release for Stretch
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 03 May 2018 03:04:45 +0000
moulinette (2.7.14) stable; urgency=low
* Improve French, Occitan, Portuguese, Arabic translations
* Release as stable
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 17 Jun 2018 01:42:12 +0000
moulinette (2.7.13) testing; urgency=low
* [i18n] Improve translations for Portugueuse, Occitan
* [enh] Add read_yaml util (#161)
Contributors : Bram, by0ne, Quent-in
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 28 May 2018 02:55:00 +0000
moulinette (2.7.12) stable; urgency=low
* Bumping version number for stable release
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 06 May 2018 16:57:18 +0000
moulinette (2.7.11) testing; urgency=low
* [i18n] Improve translations for Arabic, Dutch, French, Occitan, Spanish
* [enh] Improve performances by lazy-loading some imports
* [enh] Log time needed to load a python module for an action
* [fix] Avoid cases where get_cookie returns None
* [mod] Improve exception logging in ldap stuff
Thanks to all contributors (pitchum, Bram, ButteflyOfFire, J. Keerl, Matthieu, Jibec, David B, Quenti, bjarkan) <3 !
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 01 May 2018 23:33:59 +0000
moulinette (2.7.7) stable; urgency=low
(Bumping version for stable release)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 18 Jan 2018 17:41:43 -0500
moulinette (2.7.6) testing; urgency=low
* [fix] Indicate where those damn logs files are
* [i18n] Improve Spanish and French translations
Thanks to all contributors (Bram, Jibec, David B.) ! <3
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 16 Jan 2018 17:12:19 -0500
moulinette (2.7.5) stable; urgency=low
(Bumping version for stable release)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 02 Dec 2017 12:26:43 -0500
moulinette (2.7.3) testing; urgency=low
* Optional expected status code for download_text/json (#153)
* Allow to give lock to multiple processes (#154)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 12 Oct 2017 16:09:27 -0400
moulinette (2.7.2) stable; urgency=low moulinette (2.7.2) stable; urgency=low
* Revert hack for buildchain, found a proper solution * Revert hack for buildchain, found a proper solution

View file

@ -3,6 +3,7 @@ File system operation utils
.. autofunction:: moulinette.utils.filesystem.read_file .. autofunction:: moulinette.utils.filesystem.read_file
.. autofunction:: moulinette.utils.filesystem.read_json .. autofunction:: moulinette.utils.filesystem.read_json
.. autofunction:: moulinette.utils.filesystem.read_yaml
.. autofunction:: moulinette.utils.filesystem.write_to_file .. autofunction:: moulinette.utils.filesystem.write_to_file
.. autofunction:: moulinette.utils.filesystem.append_to_file .. autofunction:: moulinette.utils.filesystem.append_to_file
.. autofunction:: moulinette.utils.filesystem.write_to_json .. autofunction:: moulinette.utils.filesystem.write_to_json

54
locales/ar.json Normal file
View file

@ -0,0 +1,54 @@
{
"argument_required": "المُعامِل '{argument}' مطلوب",
"authentication_profile_required": "المصادقة مع الملف الشخصي '{profile}' مطلوبة",
"authentication_required": "المصادقة مطلوبة",
"authentication_required_long": "المصادقة مطلوبة قبل القيام بهذا الإجراء",
"colon": "{}: ",
"confirm": "تأكيد {prompt}",
"deprecated_command": "'{prog} {command}' تم التخلي عنه و سوف تتم إزالته مستقبلا",
"deprecated_command_alias": "'{prog} {old}' تم التخلي عنه و سوف يتم إزالته مستقبلا، إستخدم '{prog} {new}' بدلا من ذلك",
"error": "خطأ :",
"error_see_log": "طرأ هناك خطأ. يرجى الإطلاع على السجلات للمزيد مِن التفاصيل على المسار /var/log/yunohost/.",
"file_exists": "إنّ الملف موجود من قبل : '{path}'",
"file_not_exist": "الملف غير موجود : '{path}'",
"folder_exists": "إنّ المجلد موجود من قبل : '{path}'",
"folder_not_exist": "المجلد غير موجود",
"instance_already_running": "هناك نسخة خادوم تشتغل مِن قبل",
"invalid_argument": "المُعامِل غير صالح '{argument}': {error}",
"invalid_password": "كلمة السر خاطئة",
"invalid_usage": "إستعمال غير صالح، إستخدم --help لعرض المساعدة",
"ldap_attribute_already_exists": "الخاصية '{attribute}' موجودة مسبقا و تحمل القيمة '{value}'",
"ldap_operation_error": "طرأ هناك خطأ أثناء عملية في LDAP",
"ldap_server_down": "لا يمكن الإتصال بخادم LDAP",
"logged_in": "مُتّصل",
"logged_out": "تم تسجيل خروجك",
"not_logged_in": "لم تقم بعدُ بتسجيل دخولك",
"operation_interrupted": "تم توقيف العملية",
"password": "كلمة السر",
"pattern_not_match": "لا يتطابق مع النموذج",
"permission_denied": "رُفض التصريح",
"root_required": "يتوجب عليك أن تكون مدير الجذر root للقيام بهذا الإجراء",
"server_already_running": "هناك خادم يشتغل على ذاك المنفذ",
"success": "تم بنجاح !",
"unable_authenticate": "تعذرت المصادقة",
"unable_retrieve_session": "تعذرت مواصلة الجلسة",
"unknown_group": "الفريق '{group}' مجهول",
"unknown_user": "المستخدم '{user}' مجهول",
"values_mismatch": "القيمتين غير متطابقتين",
"warning": "تحذير :",
"websocket_request_expected": "كان ينتظر طلبًا عبر الويب سوكت WebSocket",
"cannot_open_file": "ليس بالإمكان فتح الملف {file:s} (السبب : {error:s})",
"cannot_write_file": "لا يمكن الكتابة في الملف {file:s} (السبب : {error:s})",
"unknown_error_reading_file": "طرأ هناك خطأ ما أثناء عملية قراءة الملف {file:s}",
"corrupted_json": "قراءة json مُشوّهة مِن {ressource:s} (السبب : {error:s})",
"error_writing_file": "طرأ هناك خطأ أثناء الكتابة في الملف {file:s}: {error:s}",
"error_removing": "خطأ أثناء عملية حذف {path:s}: {error:s}",
"error_changing_file_permissions": "خطأ أثناء عملية تعديل التصريحات لـ {path:s}: {error:s}",
"invalid_url": "خطأ في عنوان الرابط {url:s} (هل هذا الموقع موجود حقًا ؟)",
"download_ssl_error": "خطأ في الإتصال الآمن عبر الـ SSL أثناء محاولة الإتصال بـ {url:s}",
"download_timeout": "{url:s} استغرق مدة طويلة جدا للإستجابة، فتوقّف.",
"download_unknown_error": "خطأ أثناء عملية تنزيل البيانات مِن {url:s} : {error:s}",
"download_bad_status_code": "{url:s} أعاد رمز الحالة {code:s}",
"command_unknown": "الأمر '{command:s}' غير معروف ؟",
"corrupted_yaml": "قراءة مُشوّهة لنسق yaml مِن {ressource:s} (السبب : {error:s})"
}

View file

@ -8,11 +8,12 @@
"deprecated_command": "'{prog} {command}' is deprecated and will be removed in the future", "deprecated_command": "'{prog} {command}' is deprecated and will be removed in the future",
"deprecated_command_alias": "'{prog} {old}' is deprecated and will be removed in the future, use '{prog} {new}' instead", "deprecated_command_alias": "'{prog} {old}' is deprecated and will be removed in the future, use '{prog} {new}' instead",
"error": "Error:", "error": "Error:",
"error_see_log": "An error occurred. Please see the log for details.", "error_see_log": "An error occurred. Please see the logs for details, they are located in /var/log/yunohost/.",
"file_exists": "File already exists: '{path}'", "file_exists": "File already exists: '{path}'",
"file_not_exist": "File does not exist: '{path}'", "file_not_exist": "File does not exist: '{path}'",
"folder_exists": "Folder already exists: '{path}'", "folder_exists": "Folder already exists: '{path}'",
"folder_not_exist": "Folder does not exist", "folder_not_exist": "Folder does not exist",
"info": "Info:",
"instance_already_running": "An instance is already running", "instance_already_running": "An instance is already running",
"invalid_argument": "Invalid argument '{argument}': {error}", "invalid_argument": "Invalid argument '{argument}': {error}",
"invalid_password": "Invalid password", "invalid_password": "Invalid password",
@ -42,6 +43,7 @@
"cannot_write_file": "Could not write file {file:s} (reason: {error:s})", "cannot_write_file": "Could not write file {file:s} (reason: {error:s})",
"unknown_error_reading_file": "Unknown error while trying to read file {file:s}", "unknown_error_reading_file": "Unknown error while trying to read file {file:s}",
"corrupted_json": "Corrupted json read from {ressource:s} (reason: {error:s})", "corrupted_json": "Corrupted json read from {ressource:s} (reason: {error:s})",
"corrupted_yaml": "Corrupted yaml read from {ressource:s} (reason: {error:s})",
"error_writing_file": "Error when writing file {file:s}: {error:s}", "error_writing_file": "Error when writing file {file:s}: {error:s}",
"error_removing": "Error when removing {path:s}: {error:s}", "error_removing": "Error when removing {path:s}: {error:s}",
"error_changing_file_permissions": "Error when changing permissions for {path:s}: {error:s}", "error_changing_file_permissions": "Error when changing permissions for {path:s}: {error:s}",

View file

@ -8,12 +8,12 @@
"deprecated_command": "'{prog} {command}' está obsoleto y será eliminado en el futuro", "deprecated_command": "'{prog} {command}' está obsoleto y será eliminado en el futuro",
"deprecated_command_alias": "'{prog} {old}' está obsoleto y se eliminará en el futuro, use '{prog} {new}' en su lugar", "deprecated_command_alias": "'{prog} {old}' está obsoleto y se eliminará en el futuro, use '{prog} {new}' en su lugar",
"error": "Error:", "error": "Error:",
"error_see_log": "Ha ocurrido un error. Consulte el registro para obtener más información.", "error_see_log": "Ha ocurrido un error. Consulte el registro para obtener más información, localizado en /var/log/yunohost/.",
"file_exists": "El archivo ya existe: '{path}'", "file_exists": "El archivo ya existe: '{path}'",
"file_not_exist": "El archivo no existe: '{path}'", "file_not_exist": "El archivo no existe: '{path}'",
"folder_exists": "El directorio ya existe: '{path}'", "folder_exists": "El directorio ya existe: '{path}'",
"folder_not_exist": "El directorio no existe", "folder_not_exist": "El directorio no existe",
"instance_already_running": "El programa ya se está ejecutando", "instance_already_running": "Una instancia ya se está ejecutando",
"invalid_argument": "Argumento no válido '{argument}': {error}", "invalid_argument": "Argumento no válido '{argument}': {error}",
"invalid_password": "Contraseña no válida", "invalid_password": "Contraseña no válida",
"invalid_usage": "Uso no válido, utilice --help para ver la ayuda", "invalid_usage": "Uso no válido, utilice --help para ver la ayuda",
@ -36,5 +36,18 @@
"unknown_user": "Usuario '{user}' desconocido", "unknown_user": "Usuario '{user}' desconocido",
"values_mismatch": "Los valores no coinciden", "values_mismatch": "Los valores no coinciden",
"warning": "Advertencia:", "warning": "Advertencia:",
"websocket_request_expected": "Una petición de WebSocket prevista" "websocket_request_expected": "Se esperaba una petición WebSocket",
"cannot_open_file": "No se pudo abrir el fichero{file:s} (motivo:{error:s})",
"cannot_write_file": "No se pudo escribir el fichero {file:s} (motivo: {error:s})",
"unknown_error_reading_file": "Error desconocido al intentar leer el fichero {file:s}",
"corrupted_json": "Json corrupto leido desde {ressource:s} (motivo: {error:s})",
"error_writing_file": "Error al escribir el fichero {file:s}: {error:s}",
"error_removing": "Error al eliminar {path:s}: {error:s}",
"error_changing_file_permissions": "Error al cambiar los permisos para {path:s}: {error:s}",
"invalid_url": "Url no válida {url:s} (¿existe este sitio web?)",
"download_ssl_error": "Error SSL al conectar con {url:s}",
"download_timeout": "{url:s} tardó demasiado en responder, me rindo.",
"download_unknown_error": "Error al descargar datos desde {url:s} : {error:s}",
"download_bad_status_code": "{url:s} devolvió el código de estado {code:s}",
"command_unknown": "Comando '{command:s}' desconocido ?"
} }

View file

@ -1,14 +1,14 @@
{ {
"argument_required": "L'argument « {argument} » est requis", "argument_required": "L'argument « {argument} » est requis",
"authentication_profile_required": "Authentification au profil « {profile} » requise", "authentication_profile_required": "Lauthentification au profil « {profile} » requise",
"authentication_required": "Authentification requise", "authentication_required": "Authentification requise",
"authentication_required_long": "L'authentification est requise pour exécuter cette action", "authentication_required_long": "Lauthentification est requise pour exécuter cette action",
"colon": "{} : ", "colon": "{} : ",
"confirm": "Confirmez : {prompt}", "confirm": "Confirmez : {prompt}",
"deprecated_command": "« {prog} {command} » est déprécié et sera bientôt supprimé", "deprecated_command": "« {prog} {command} » est déprécié et sera bientôt supprimé",
"deprecated_command_alias": "« {prog} {old} » est déprécié et sera bientôt supprimé, utilisez « {prog} {new} » à la place", "deprecated_command_alias": "« {prog} {old} » est déprécié et sera bientôt supprimé, utilisez « {prog} {new} » à la place",
"error": "Erreur :", "error": "Erreur :",
"error_see_log": "Une erreur est survenue. Veuillez consulter les journaux pour plus de détails.", "error_see_log": "Une erreur est survenue. Veuillez consulter les journaux pour plus de détails, ils sont situés en /var/log/yunohost/.",
"file_exists": "Le fichier existe déjà : « {path} »", "file_exists": "Le fichier existe déjà : « {path} »",
"file_not_exist": "Le fichier « {path} » n'existe pas", "file_not_exist": "Le fichier « {path} » n'existe pas",
"folder_exists": "Le dossier existe déjà : « {path} »", "folder_exists": "Le dossier existe déjà : « {path} »",
@ -27,7 +27,7 @@
"password": "Mot de passe", "password": "Mot de passe",
"pattern_not_match": "Ne correspond pas au motif", "pattern_not_match": "Ne correspond pas au motif",
"permission_denied": "Permission refusée", "permission_denied": "Permission refusée",
"root_required": "Vous devez avoir les droits super-utilisateur pour exécuter cette action", "root_required": "Vous devez être super-utilisateur pour exécuter cette action",
"server_already_running": "Un serveur est déjà en cours d'exécution sur ce port", "server_already_running": "Un serveur est déjà en cours d'exécution sur ce port",
"success": "Succès !", "success": "Succès !",
"unable_authenticate": "Impossible de vous authentifier", "unable_authenticate": "Impossible de vous authentifier",
@ -49,5 +49,6 @@
"download_timeout": "{url:s} a pris trop de temps pour répondre, abandon.", "download_timeout": "{url:s} a pris trop de temps pour répondre, abandon.",
"download_unknown_error": "Erreur lors du téléchargement des données à partir de {url:s}:{error:s}", "download_unknown_error": "Erreur lors du téléchargement des données à partir de {url:s}:{error:s}",
"download_bad_status_code": "{url:s} code de statut renvoyé {code:s}", "download_bad_status_code": "{url:s} code de statut renvoyé {code:s}",
"command_unknown": "Commande {command:s} inconnue ?" "command_unknown": "Commande '{command:s}' inconnue ?",
"corrupted_yaml": "YAML corrompu lu {ressource:s} depuis (cause : {error:s})"
} }

View file

@ -1,21 +1,21 @@
{ {
"argument_required": "Argument {argument} is vereist", "argument_required": "Argument {argument} is vereist",
"authentication_profile_required": "Authenticatie tot profiel '{profile}' is vereist", "authentication_profile_required": "Authenticatie tot profiel '{profile}' is vereist",
"authentication_required": "Authenticatie vereist", "authentication_required": "Aanmelding vereist",
"authentication_required_long": "Authenticatie is vereist om deze actie uit te voeren", "authentication_required_long": "Aanmelding is vereist om deze actie uit te voeren",
"colon": "{}: ", "colon": "{}: ",
"confirm": "Bevestig {prompt}", "confirm": "Bevestig {prompt}",
"error": "Fout:", "error": "Fout:",
"error_see_log": "Er is een fout opgetreden, zie logboek voor meer informatie.", "error_see_log": "Er is een fout opgetreden, zie logboek voor meer informatie. Je kan deze vinden in /var/log/yunohost/.",
"file_exists": "Kan '{path}' niet aanmaken: Bestand bestaat al", "file_exists": "Kan '{path}' niet aanmaken: bestand bestaat al",
"file_not_exist": "Bestand bestaat niet: '{path}'", "file_not_exist": "Bestand bestaat niet: '{path}'",
"folder_exists": "kan map {path} niet aanmaken: Bestand bestaat al", "folder_exists": "Deze map bestaat al: '{path}'",
"folder_not_exist": "Map bestaat niet", "folder_not_exist": "Map bestaat niet",
"instance_already_running": "Er is al een instantie aan het draaien", "instance_already_running": "Er is al een instantie actief",
"invalid_argument": "Onjuist argument '{argument}': {error}", "invalid_argument": "Ongeldig argument '{argument}': {error}",
"invalid_password": "Ongeldig wachtwoord", "invalid_password": "Ongeldig wachtwoord",
"invalid_usage": "Geef 'pass --help' in voor meer informatie", "invalid_usage": "Ongeldig gebruik, doe --help om de hulptekst te lezen",
"ldap_attribute_already_exists": "Attribuut bestaat al: '{attribute}={value}'", "ldap_attribute_already_exists": "Attribuut '{attribute}' bestaat al met waarde '{value}'",
"ldap_operation_error": "Er is een fout opgetreden bij het uitvoeren van LDAP operatie", "ldap_operation_error": "Er is een fout opgetreden bij het uitvoeren van LDAP operatie",
"ldap_server_down": "Kan LDAP server niet bereiken", "ldap_server_down": "Kan LDAP server niet bereiken",
"logged_in": "Ingelogd", "logged_in": "Ingelogd",
@ -26,7 +26,7 @@
"pattern_not_match": "Past niet in het patroon", "pattern_not_match": "Past niet in het patroon",
"permission_denied": "Toegang geweigerd", "permission_denied": "Toegang geweigerd",
"root_required": "Je moet root zijn om deze actie uit te voeren", "root_required": "Je moet root zijn om deze actie uit te voeren",
"server_already_running": "Er is al een server aktief op die poort", "server_already_running": "Er is al een server actief op die poort",
"success": "Succes!", "success": "Succes!",
"unable_authenticate": "Aanmelding niet mogelijk", "unable_authenticate": "Aanmelding niet mogelijk",
"unable_retrieve_session": "Kan de sessie niet ophalen", "unable_retrieve_session": "Kan de sessie niet ophalen",
@ -34,7 +34,20 @@
"warning": "Waarschuwing:", "warning": "Waarschuwing:",
"websocket_request_expected": "Verwachtte een WebSocket request", "websocket_request_expected": "Verwachtte een WebSocket request",
"deprecated_command": "'{prog} {command}' is verouderd en wordt binnenkort verwijderd", "deprecated_command": "'{prog} {command}' is verouderd en wordt binnenkort verwijderd",
"deprecated_command_alias": "'{prog} {old}' is verouderd en wordt binnenkort verwijderd, gebruik '{prog} {new}' in plaats daarvan", "deprecated_command_alias": "'{prog} {old}' is verouderd en wordt binnenkort verwijderd, gebruik in de plaats '{prog} {new}'",
"unknown_group": "Groep '{group}' is onbekend", "unknown_group": "Groep '{group}' is onbekend",
"unknown_user": "Gebruiker '{user}' is onbekend" "unknown_user": "Gebruiker '{user}' is onbekend",
"cannot_open_file": "Niet mogelijk om bestand {file:s} te openen (reden: {error:s})",
"cannot_write_file": "Niet gelukt om bestand {file:s} te schrijven (reden: {error:s})",
"unknown_error_reading_file": "Ongekende fout tijdens het lezen van bestand {file:s}",
"corrupted_json": "Corrupte json gelezen van {ressource:s} (reden: {error:s})",
"error_writing_file": "Fout tijdens het schrijven van bestand {file:s}: {error:s}",
"error_removing": "Fout tijdens het verwijderen van {path:s}: {error:s}",
"error_changing_file_permissions": "Fout tijdens het veranderen van machtiging voor {path:s}: {error:s}",
"invalid_url": "Ongeldige URL {url:s} (bestaat deze website?)",
"download_ssl_error": "SSL fout gedurende verbinding met {url:s}",
"download_timeout": "{url:s} neemt te veel tijd om te antwoorden, we geven het op.",
"download_unknown_error": "Fout tijdens het downloaden van data van {url:s}: {error:s}",
"download_bad_status_code": "{url:s} stuurt status code {code:s}",
"command_unknown": "Opdracht '{command:s}' ongekend ?"
} }

54
locales/oc.json Normal file
View file

@ -0,0 +1,54 @@
{
"argument_required": "Largument {argument} es requesit",
"authentication_profile_required": "Lidentificacion del perfil {profile} es requesida",
"authentication_required": "Autentificacion requesida",
"authentication_required_long": "Una autentificacion es requesida per acomplir aquesta accion",
"logged_in": "Connectat",
"logged_out": "Desconnectat",
"password": "Senhal",
"colon": "{}: ",
"confirm": "Confirmatz: {prompt}",
"deprecated_command": "« {prog} {command} » es despreciat e serà lèu suprimit",
"deprecated_command_alias": "« {prog} {old} » es despreciat e serà lèu suprimit, utilizatz « {prog} {new} » allòc",
"error": "Error:",
"error_see_log": "Una error ses producha. Mercés de consultar los jornals per mai detalhs, son plaçats dins /var/log/yunohost/.",
"file_exists": "Lo fichièr existís ja: « {path} »",
"file_not_exist": "Lo fichièr « {path} » existís pas",
"folder_exists": "Lo repertòri existís ja: « {path} »",
"folder_not_exist": "Lo repertòri existís pas",
"instance_already_running": "Una instància es ja en execucion",
"invalid_argument": "Argument « {argument} » incorrècte: {error}",
"invalid_password": "Senhal incorrècte",
"ldap_server_down": "Impossible daténher lo servidor LDAP",
"not_logged_in": "Cap de session començada",
"pattern_not_match": "Correspond pas al patron",
"permission_denied": "Permission refusada",
"root_required": "Cal èsser root per realizar aquesta accion",
"unable_retrieve_session": "Impossible de recuperar la session",
"unknown_group": "Grop « {group} » desconegut",
"unknown_user": "Utilizaire « {user} » desconegut",
"values_mismatch": "Las valors correspondon pas",
"warning": "Atencion:",
"invalid_usage": "Usatge invalid, utilizatz --help per accedir a lajuda",
"ldap_attribute_already_exists": "Latribut « {attribute} » existís ja amb la valor: {value}",
"ldap_operation_error": "Una error ses producha pendent loperacion LDAP",
"operation_interrupted": "Operacion interrompuda",
"server_already_running": "Un servidor es ja en execucion sus aqueste pòrt",
"success": "Capitada!",
"unable_authenticate": "Impossible de vos autentificar",
"websocket_request_expected": "Una requèsta WebSocket èra esperada",
"cannot_open_file": "Impossible de dobrir lo fichièr {file:s} (rason: {error:s})",
"cannot_write_file": "Escritura impossibla del fichièr {file:s} (rason: {error:s})",
"unknown_error_reading_file": "Error desconeguda en ensajar de legir lo fichièr {file:s}",
"error_writing_file": "Error en escriure lo fichièr {file:s}: {error:s}",
"error_removing": "Error en suprimir {path:s}: {error:s}",
"error_changing_file_permissions": "Error en modificar las permissions per {path:s}: {error:s}",
"invalid_url": "Url invalida {url:s} (existís aqueste site?)",
"download_ssl_error": "Error SSL en se connectant a {url:s}",
"download_timeout": "{url:s} a trigat per respondre, avèm quitat desperar.",
"download_unknown_error": "Error en telecargar de donadas de {url:s}: {error:s}",
"download_bad_status_code": "{url:s} tòrna lo còdi destat {code:s}",
"command_unknown": "Comanda {command:s} desconeguda?",
"corrupted_json": "Fichièr Json corromput legit de {ressource:s} (rason: {error:s})",
"corrupted_yaml": "Fichièr YAML corromput legit de {ressource:s} (rason: {error:s})"
}

View file

@ -34,7 +34,21 @@
"websocket_request_expected": "Esperado um pedido a WebSocket", "websocket_request_expected": "Esperado um pedido a WebSocket",
"deprecated_command": "'{prog} {command}' está obsoleto e será removido no futuro", "deprecated_command": "'{prog} {command}' está obsoleto e será removido no futuro",
"deprecated_command_alias": "'{prog} {old}' está obsoleto e será removido no futuro, em vez disso, usa '{prog} {new}'", "deprecated_command_alias": "'{prog} {old}' está obsoleto e será removido no futuro, em vez disso, usa '{prog} {new}'",
"error_see_log": "Um erro ocorreu. Por favor, veja mais detalhes no log.", "error_see_log": "Ocorreu um erro . Por favor, veja os logs para maiores detalhes, eles estão localizados em /var/log/yunohost/.",
"unknown_group": "Grupo '{group}' desconhecido", "unknown_group": "Grupo '{group}' desconhecido",
"unknown_user": "Nome de utilizador '{user}' desconhecido" "unknown_user": "Nome de utilizador '{user}' desconhecido",
"cannot_open_file": "Não foi possível abrir o arquivo {file:s} (reason: {error:s})",
"cannot_write_file": "Não foi possível abrir o arquivo {file:s} (reason: {error:s})",
"unknown_error_reading_file": "Erro desconhecido ao tentar ler o arquivo {file:s}",
"error_writing_file": "Erro ao gravar arquivo {file:s}: {error:s}",
"error_removing": "Erro ao remover {path:s}: {error:s}",
"error_changing_file_permissions": "Erro ao alterar as permissões para {path:s}: {error:s}",
"invalid_url": "URL inválida {url:s} (does this site exists ?)",
"download_ssl_error": "Erro de SSL ao conectar-se a {url:s}",
"download_timeout": "{url:s} demorou muito para responder, desistiu.",
"download_unknown_error": "Erro quando baixando os dados de {url:s} : {error:s}",
"download_bad_status_code": "{url:s} retornou o código de status {code:s}",
"command_unknown": "Comando '{command:s}' desconhecido ?",
"corrupted_json": "Json corrompido lido do {ressource:s} (motivo: {error:s})",
"corrupted_yaml": "Yaml corrompido lido do {ressource:s} (motivo: {error:s})"
} }

View file

@ -391,7 +391,7 @@ class ActionsMap(object):
with open(actionsmap_pkl) as f: with open(actionsmap_pkl) as f:
actionsmaps[n] = pickle.load(f) actionsmaps[n] = pickle.load(f)
# TODO: Switch to python3 and catch proper exception # TODO: Switch to python3 and catch proper exception
except IOError: except (IOError, EOFError):
self.use_cache = False self.use_cache = False
actionsmaps = self.generate_cache(namespaces) actionsmaps = self.generate_cache(namespaces)
elif use_cache: # cached file doesn't exists elif use_cache: # cached file doesn't exists
@ -469,10 +469,13 @@ class ActionsMap(object):
# Lock the moulinette for the namespace # Lock the moulinette for the namespace
with MoulinetteLock(namespace, timeout): with MoulinetteLock(namespace, timeout):
start = time()
try: try:
mod = __import__('%s.%s' % (namespace, category), mod = __import__('%s.%s' % (namespace, category),
globals=globals(), level=0, globals=globals(), level=0,
fromlist=[func_name]) fromlist=[func_name])
logger.debug('loading python module %s took %.3fs',
'%s.%s' % (namespace, category), time() - start)
func = getattr(mod, func_name) func = getattr(mod, func_name)
except (AttributeError, ImportError): except (AttributeError, ImportError):
logger.exception("unable to load function %s.%s", logger.exception("unable to load function %s.%s",
@ -495,7 +498,7 @@ class ActionsMap(object):
return func(**arguments) return func(**arguments)
finally: finally:
stop = time() stop = time()
logger.debug('action [%s] ended after %.3fs', logger.debug('action [%s] executed in %.3fs',
log_id, stop - start) log_id, stop - start)
@staticmethod @staticmethod

View file

@ -62,7 +62,8 @@ class Authenticator(BaseAuthenticator):
try: try:
# Retrieve identity # Retrieve identity
who = self.con.whoami_s() who = self.con.whoami_s()
except: except Exception as e:
logger.warning("Error during ldap authentication process: %s", e)
return False return False
else: else:
if who[3:] == self.userdn: if who[3:] == self.userdn:
@ -131,9 +132,9 @@ class Authenticator(BaseAuthenticator):
try: try:
result = self.con.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs) result = self.con.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs)
except: except Exception as e:
logger.exception("error during LDAP search operation with: base='%s', " logger.exception("error during LDAP search operation with: base='%s', "
"filter='%s', attrs=%s", base, filter, attrs) "filter='%s', attrs=%s and exception %s", base, filter, attrs, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error')) raise MoulinetteError(169, m18n.g('ldap_operation_error'))
result_list = [] result_list = []
@ -162,9 +163,9 @@ class Authenticator(BaseAuthenticator):
try: try:
self.con.add_s(dn, ldif) self.con.add_s(dn, ldif)
except: except Exception as e:
logger.exception("error during LDAP add operation with: rdn='%s', " logger.exception("error during LDAP add operation with: rdn='%s', "
"attr_dict=%s", rdn, attr_dict) "attr_dict=%s and exception %s", rdn, attr_dict, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error')) raise MoulinetteError(169, m18n.g('ldap_operation_error'))
else: else:
return True return True
@ -183,8 +184,8 @@ class Authenticator(BaseAuthenticator):
dn = rdn + ',' + self.basedn dn = rdn + ',' + self.basedn
try: try:
self.con.delete_s(dn) self.con.delete_s(dn)
except: except Exception as e:
logger.exception("error during LDAP delete operation with: rdn='%s'", rdn) logger.exception("error during LDAP delete operation with: rdn='%s' and exception %s", rdn, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error')) raise MoulinetteError(169, m18n.g('ldap_operation_error'))
else: else:
return True return True
@ -212,9 +213,10 @@ class Authenticator(BaseAuthenticator):
dn = new_rdn + ',' + self.basedn dn = new_rdn + ',' + self.basedn
self.con.modify_ext_s(dn, ldif) self.con.modify_ext_s(dn, ldif)
except: except Exception as e:
logger.exception("error during LDAP update operation with: rdn='%s', " logger.exception("error during LDAP update operation with: rdn='%s', "
"attr_dict=%s, new_rdn=%s", rdn, attr_dict, new_rdn) "attr_dict=%s, new_rdn=%s and exception: %s", rdn, attr_dict,
new_rdn, e)
raise MoulinetteError(169, m18n.g('ldap_operation_error')) raise MoulinetteError(169, m18n.g('ldap_operation_error'))
else: else:
return True return True

View file

@ -88,14 +88,23 @@ class Translator(object):
- key -- The key to translate - key -- The key to translate
""" """
failed_to_format = False
if key in self._translations.get(self.locale, {}): if key in self._translations.get(self.locale, {}):
return self._translations[self.locale][key].encode('utf-8').format(*args, **kwargs) try:
return self._translations[self.locale][key].encode('utf-8').format(*args, **kwargs)
except KeyError as e:
logger.exception("Failed to format translated string '%s' with error: %s" % (key, e))
failed_to_format = True
if self.default_locale != self.locale and key in self._translations.get(self.default_locale, {}): if failed_to_format or (self.default_locale != self.locale and key in self._translations.get(self.default_locale, {})):
logger.info("untranslated key '%s' for locale '%s'", logger.info("untranslated key '%s' for locale '%s'",
key, self.locale) key, self.locale)
return self._translations[self.default_locale][key].encode('utf-8').format(*args, **kwargs) try:
return self._translations[self.default_locale][key].encode('utf-8').format(*args, **kwargs)
except KeyError as e:
logger.exception("Failed to format translatable string '%s' with error: %s" % (key, e))
return self._translations[self.locale][key].encode('utf-8')
logger.exception("unable to retrieve key '%s' for default locale '%s'", logger.exception("unable to retrieve key '%s' for default locale '%s'",
key, self.default_locale) key, self.default_locale)
@ -500,7 +509,7 @@ class MoulinetteLock(object):
lock_pids = f.read().strip().split('\n') lock_pids = f.read().strip().split('\n')
# Make sure to convert those pids to integers # Make sure to convert those pids to integers
lock_pids = [ int(pid) for pid in lock_pids ] lock_pids = [int(pid) for pid in lock_pids if pid.strip() != '']
return lock_pids return lock_pids

View file

@ -438,7 +438,7 @@ class _ActionsMapPlugin(object):
try: try:
s_secret = self.secrets[s_id] s_secret = self.secrets[s_id]
s_hash = request.get_cookie('session.hashes', s_hash = request.get_cookie('session.hashes',
secret=s_secret)[authenticator.name] secret=s_secret, default={})[authenticator.name]
except KeyError: except KeyError:
if authenticator.name == 'default': if authenticator.name == 'default':
msg = m18n.g('authentication_required') msg = m18n.g('authentication_required')

View file

@ -1,4 +1,5 @@
import os import os
import yaml
import errno import errno
import shutil import shutil
import json import json
@ -64,6 +65,28 @@ def read_json(file_path):
return loaded_json return loaded_json
def read_yaml(file_path):
"""
Safely read a yaml file
Keyword argument:
file_path -- Path to the yaml file
"""
# Read file
file_content = read_file(file_path)
# Try to load yaml to check if it's syntaxically correct
try:
loaded_yaml = yaml.safe_load(file_content)
except ValueError as e:
raise MoulinetteError(errno.EINVAL,
m18n.g('corrupted_yaml',
ressource=file_path, error=str(e)))
return loaded_yaml
def write_to_file(file_path, data, file_mode="w"): def write_to_file(file_path, data, file_mode="w"):
""" """
Write a single string or a list of string to a text file. Write a single string or a list of string to a text file.

View file

@ -1,5 +1,4 @@
import errno import errno
import requests
import json import json
from moulinette import m18n from moulinette import m18n
@ -17,6 +16,7 @@ def download_text(url, timeout=30, expected_status_code=200):
expected_status_code -- Status code expected from the request. Can be expected_status_code -- Status code expected from the request. Can be
None to ignore the status code. None to ignore the status code.
""" """
import requests # lazy loading this module for performance reasons
# Assumptions # Assumptions
assert isinstance(url, str) assert isinstance(url, str)

View file

@ -95,22 +95,33 @@ def call_async_output(args, callback, **kwargs):
if stdinfo: if stdinfo:
stdinfo_reader, stdinfo_consum = async_file_reading(stdinfo_f, callback[2]) stdinfo_reader, stdinfo_consum = async_file_reading(stdinfo_f, callback[2])
while not stdout_reader.eof() or not stderr_reader.eof(): while not stdout_reader.eof() and not stderr_reader.eof():
while not stdout_consum.empty() or not stderr_consum.empty():
# alternate between the 2 consumers to avoid desynchronisation
# this way is not 100% perfect but should do it
stdout_consum.process_next_line()
stderr_consum.process_next_line()
stdinfo_consum.process_next_line()
time.sleep(.1) time.sleep(.1)
stderr_reader.join() stderr_reader.join()
stderr_consum.join() # clear the queues
stdout_consum.process_current_queue()
stderr_consum.process_current_queue()
stdinfo_consum.process_current_queue()
else: else:
while not stdout_reader.eof(): while not stdout_reader.eof():
stdout_consum.process_current_queue()
time.sleep(.1) time.sleep(.1)
stdout_reader.join() stdout_reader.join()
stdout_consum.join() # clear the queue
stdout_consum.process_current_queue()
if stdinfo: if stdinfo:
# Remove the stdinfo pipe # Remove the stdinfo pipe
os.remove(stdinfo) os.remove(stdinfo)
os.rmdir(os.path.dirname(stdinfo)) os.rmdir(os.path.dirname(stdinfo))
stdinfo_reader.join() stdinfo_reader.join()
stdinfo_consum.join() stdinfo_consum.process_current_queue()
# on slow hardware, in very edgy situations it is possible that the process # on slow hardware, in very edgy situations it is possible that the process
# isn't finished just after having closed stdout and stderr, so we wait a # isn't finished just after having closed stdout and stderr, so we wait a

View file

@ -1,5 +1,6 @@
import logging import logging
from json.encoder import JSONEncoder from json.encoder import JSONEncoder
import datetime
logger = logging.getLogger('moulinette.utils.serialize') logger = logging.getLogger('moulinette.utils.serialize')
@ -24,6 +25,10 @@ class JSONExtendedEncoder(JSONEncoder):
hasattr(o, '__iter__') and hasattr(o, 'next')): hasattr(o, '__iter__') and hasattr(o, 'next')):
return list(o) return list(o)
# Display the date in its iso format ISO-8601 Internet Profile (RFC 3339)
if isinstance(o, datetime.datetime) or isinstance(o, datetime.date):
return o.isoformat()
# Return the repr for object that json can't encode # Return the repr for object that json can't encode
logger.warning('cannot properly encode in JSON the object %s, ' logger.warning('cannot properly encode in JSON the object %s, '
'returned repr is: %r', type(o), o) 'returned repr is: %r', type(o), o)

View file

@ -73,14 +73,29 @@ class AsynchronousFileReader(Process):
Process.join(self, timeout) Process.join(self, timeout)
def consume_queue(queue, callback): class Consummer(object):
"""Consume the queue and give content to the callback.""" def __init__(self, queue, callback):
while True: self.queue = queue
line = queue.get() self.callback = callback
if line:
if line == StopIteration: def empty(self):
break return self.queue.empty()
callback(line)
def process_next_line(self):
if not self.empty():
line = self.queue.get()
if line:
if line == StopIteration:
return
self.callback(line)
def process_current_queue(self):
while not self.empty():
line = self.queue.get()
if line:
if line == StopIteration:
break
self.callback(line)
def async_file_reading(fd, callback): def async_file_reading(fd, callback):
@ -88,6 +103,5 @@ def async_file_reading(fd, callback):
queue = SimpleQueue() queue = SimpleQueue()
reader = AsynchronousFileReader(fd, queue) reader = AsynchronousFileReader(fd, queue)
reader.start() reader.start()
consummer = Process(target=consume_queue, args=(queue, callback)) consummer = Consummer(queue, callback)
consummer.start()
return (reader, consummer) return (reader, consummer)