Initial QSfera import
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[o:qsfera-eu:p:qsfera-eu:r:qsfera-settings]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/settings.po
|
||||
minimum_perc = 75
|
||||
resource_name = qsfera-settings
|
||||
source_file = settings.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Ivan Fustero, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Ivan Fustero, 2025\n"
|
||||
"Language-Team: Catalan (https://app.transifex.com/qsfera-eu/teams/204053/ca/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "S'ha afegit com a membre de l'espai"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Diàriament"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Interval d'enviament de correu"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "S'ha rebutjat el fitxer"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Immediat"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Mai"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Notifica'm quan m'hagin afegit com a membre a un espai"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Notifica'm quan m'hagin tret com a membre d'un espai"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Notifica quan rebi una compartició"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Notifica'm quan un fitxer que he pujat ha estat rebutjat per una infecció "
|
||||
"per virus o una violació de la política"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Notifica'm quan s'hagi eliminat una compartició rebuda"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Notifica'm quan hagi caducat una compartició rebuda"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Notifica'm quan s'hagi suprimit un espai del qual soc membre"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Notifica'm quan s'hagi desactivat un espai del qual soc membre"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Notifica'm quan hagi caducat la pertinença a un espai"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Eliminat com a membre de l'espai"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valor seleccionat:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Compartició caducada"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Compartició rebuda"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Compartició eliminada"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Espai eliminat"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Espai desactivat"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "La pertinença a l'espai ha expirat"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Setmanalment"
|
||||
@@ -0,0 +1,151 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Jörn Friedrich Dreyer <jfd@butonic.de>, 2025
|
||||
# Jannick Kuhr, 2026
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-20 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Jannick Kuhr, 2026\n"
|
||||
"Language-Team: German (https://app.transifex.com/qsfera-eu/teams/204053/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Als Space-Mitglied hinzugefügt"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Täglich"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "E-Mail-Versandintervall"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Datei abgelehnt"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Sofort"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nie"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr ""
|
||||
"Benachrichtigung, wenn ich zu einem Space als Mitglied hinzugefügt wurde"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr ""
|
||||
"Benachrichtigung, wenn ich als Mitglied aus einem Space entfernt wurde"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Benachrichtigung, wenn ich eine Freigabe empfangen habe"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Benachrichtigung, wenn eine hochgeladene Datei aufgrund einer Vireninfektion"
|
||||
" oder eines Verstoßes gegen die Richtlinien abgelehnt wurde"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Benachrichtigung, wenn eine empfangene Freigabe entfernt wurde"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Benachrichtigung, wenn eine empfangene Freigabe abgelaufen ist"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr ""
|
||||
"Benachrichtigung, wenn ein Space, in dem ich Mitglied bin, gelöscht wurde"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Benachrichtigung, wenn ein Space, in dem ich Mitglied bin, deaktiviert wurde"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Benachrichtigung, wenn eine Space-Mitgliedschaft abgelaufen ist"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Space-Mitgliedschaft entfernt"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Eingestellter Wert:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Freigabe abgelaufen"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Freigabe empfangen"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Freigabe entfernt"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Space gelöscht"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Space deaktiviert"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Space-Mitgliedschaft abgelaufen"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Wöchentlich"
|
||||
@@ -0,0 +1,147 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Efstathios Iosifidis <eiosifidis@gmail.com>, 2026
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-23 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Efstathios Iosifidis <eiosifidis@gmail.com>, 2026\n"
|
||||
"Language-Team: Greek (https://app.transifex.com/qsfera-eu/teams/204053/el/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Προσθήκη ως μέλος χώρου"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Καθημερινά"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Συχνότητα αποστολής email"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Απόρριψη αρχείου"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Άμεσα"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Ποτέ"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Ειδοποίηση όταν προστίθεμαι ως μέλος σε έναν χώρο"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Ειδοποίηση όταν αφαιρούμαι από μέλος ενός χώρου"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Ειδοποίηση όταν λαμβάνω έναν διαμοιρασμό"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Ειδοποίηση όταν ένα αρχείο που ανέβασα απορρίφθηκε λόγω μόλυνσης από ιό ή "
|
||||
"παραβίασης πολιτικής"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Ειδοποίηση όταν ένας ληφθείς διαμοιρασμός έχει αφαιρεθεί"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Ειδοποίηση όταν ένας ληφθείς διαμοιρασμός έχει λήξει"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Ειδοποίηση όταν ένας χώρος στον οποίο είμαι μέλος έχει διαγραφεί"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Ειδοποίηση όταν ένας χώρος στον οποίο είμαι μέλος έχει απενεργοποιηθεί"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Ειδοποίηση όταν η ιδιότητα μέλους σε έναν χώρο έχει λήξει"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Αφαίρεση από μέλος χώρου"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Επιλεγμένη τιμή:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Λήξη διαμοιρασμού"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Λήψη διαμοιρασμού"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Αφαίρεση διαμοιρασμού"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Διαγραφή χώρου"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Απενεργοποίηση χώρου"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Λήξη ιδιότητας μέλους χώρου"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Εβδομαδιαία"
|
||||
@@ -0,0 +1,147 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Elías Martín, 2025
|
||||
# Alejandro Robles, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Alejandro Robles, 2025\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/qsfera-eu/teams/204053/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Incluido como miembro del espacio"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Diario"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Intervalo de envío de correos"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Archivo rechazado"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instantaneo"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nunca"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Notificar cuando me han agregado como miembro a un espacio"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Notificar cuando he sido eliminado como miembro de un espacio"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Notificarme cuando he recibido un compartido"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Notificar cuando un archivo que he subido ha sido rechazado por contener "
|
||||
"virus o violar alguna política"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Notificar cuando un compartido que he recibido ha sido eliminado"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Notificar cuando un compartido recibido ha expirado."
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Notificar cuando un espacio del que soy miembro ha sido eliminado"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Notificar cuando un espacio del que soy miembro ha sido deshabilitado"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Notificar cuando la membresía a un espacio ha caducado"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Eliminado como miembro del espacio"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valor seleccionado:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Compartido expirado"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Compartido recibido"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Compartido eliminado"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Espacio eliminado"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Espacio inhabilitado"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Membresia al espacio expirada"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Semanal"
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Jiri Grönroos <jiri.gronroos@iki.fi>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-07 00:02+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>, 2025\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/qsfera-eu/teams/204053/fi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fi\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Lisätty avaruuden jäseneksi"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Päivittäin"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Sähköpostin lähetyksen aikaväli"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Tiedosto hylätty"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Välittömästi"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Ei koskaan"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Ilmoita kun minut lisätty jäseneksi avaruuteen"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Ilmoita kun jäsenyyteni avaruudesta on poistettu"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Ilmoita kun olen vastaanottanut jaon"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Ilmoita, kun lähettämäni tiedosto hylättiin joko virustartunnan tai "
|
||||
"käytäntöloukkauksen vuoksi"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Ilmoita kun vastaanotettu jako on poistettu"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Ilmoita kun vastaanotettu jako on vanhentunut"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Ilmoita, kun avaruus, jonka jäsen olen, on poistettu"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Ilmoita, kun avaruus, jonka jäsen olen, on poistettu käytöstä"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Ilmoita, kun avaruuden jäsenyys on vanhentunut"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Poistettu avaruuden jäsenyydestä"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valittu arvo:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Jako vanheni"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Jako vastaanotettu"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Jako poistettu"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Avaruus poistettu"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Avaruus poistettu käytöstä"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Avaruuden jäsenyys vanhentunut"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Viikottain"
|
||||
@@ -0,0 +1,148 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# eric_G <junk.eg@free.fr>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: eric_G <junk.eg@free.fr>, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/qsfera-eu/teams/204053/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Ajouté comme membre de l'Espace"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Quotidiennement"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Intervalle d'envoi des e-mails"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Fichier rejeté"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instantané"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Jamais"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr ""
|
||||
"Recevoir une notification lorsque j'ai été ajouté en tant que membre à un "
|
||||
"espace"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "M'avertir lorsque j'ai été retiré d'un Espace en tant que membre"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Recevoir une notification lorsque j'ai reçu un partage"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Notifier lorsqu'un fichier que j'ai téléchargé a été rejeté en raison d'une "
|
||||
"infection virale ou d'une violation de politique."
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Notifier la suppression d'un partage reçu"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Notifier l'expiration d'un partage reçue"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Notifier lorsqu'un espace dont je suis membre a été supprimé"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Notifier lorsqu'un Espace dont je suis membre a été désactivé"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Notifier l'expiration d'une adhésion à un Espace"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Supprimé en tant que membre de l'Espace"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valeur sélectionnée :"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Partage expiré"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Partage reçue"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Partage supprimée"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Espace supprimé"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Espace désactivé"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "L'adhésion à l'espace a expiré"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Hebdomadaire"
|
||||
@@ -0,0 +1,147 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# mitibor, 2026
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-04 07:46+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: mitibor, 2026\n"
|
||||
"Language-Team: Hungarian (https://app.transifex.com/qsfera-eu/teams/204053/hu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: hu\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Hozzáadva tárhelytagként"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Naponta"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "E-mail küldési időköz"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Fájl elutasítva"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Azonnal"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Soha"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Értesítést kérek, ha hozzáadtak egy tárhelyhez tagként"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Értesítést kérek, ha eltávolítottak egy tárhely tagjai közül"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Értesítést kérek, ha megosztást kaptam"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Értesítést kérek, ha egy általam feltöltött fájl elutasításra került "
|
||||
"vírusfertőzés vagy szabályszegés miatt"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Értesítést kérek, ha egy kapott megosztást eltávolítottak"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Értesítést kérek, ha egy kapott megosztás lejárt"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Értesítést kérek, ha egy tárhelyet, amelynek tagja vagyok, töröltek"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Értesítést kérek, ha egy tárhelyet, amelynek tagja vagyok, letiltottak"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Értesítést kérek, ha egy tárhelytagság lejárt"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Eltávolítva a tárhelytagok közül"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Kiválasztott érték:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Megosztás lejárt"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Megosztás érkezett"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Megosztás eltávolítva"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Tárhely törölve"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Tárhely letiltva"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Tárhelytagság lejárt"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Hetente"
|
||||
@@ -0,0 +1,150 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# idoet <idoet@protonmail.ch>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-06 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: idoet <idoet@protonmail.ch>, 2025\n"
|
||||
"Language-Team: Indonesian (https://app.transifex.com/qsfera-eu/teams/204053/id/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: id\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Ditambahkan menjadi anggota ruang penyimpanan"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Harian"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Interval pengiriman email"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Berkas ditolak"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instan"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Tidak pernah"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr ""
|
||||
"Beri tahu ketika saya telah ditambahkan menjadi anggota dari suatu ruang "
|
||||
"penyimpanan"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr ""
|
||||
"Beri tahu ketika saya telah dihapus dari keanggotaan suatu ruang penyimpanan"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Beri tahu ketika saya telah menerima berbagi"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Beri tahu jika file yang saya unggah ditolak karena infeksi virus atau "
|
||||
"pelanggaran kebijakan"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Beri tahu saat berbagi yang diterima telah dihapus"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Beri tahu ketika berbagi yang diterima telah berakhir"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Beri tahu ketika ruang penyimpanan yang saya ikuti telah dihapus"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Beri tahu ketika ruang penyimpanan yang saya ikuti telah dinonaktifkan"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Beri tahu saat keanggotaan ruang penyimpanan telah berakhir"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Dihapus dari anggota ruang penyimpanan"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Yang dipilih:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Berbagi Berakhir"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Berbagi Diterima"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Berbagi Dihapus"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Ruang penyimpanan dihapus"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Ruang penyimpanan dinonaktifkan"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Keanggotaan ruang penyimpanan telah berakhir"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Mingguan"
|
||||
@@ -0,0 +1,148 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Simone Pagano, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Simone Pagano, 2025\n"
|
||||
"Language-Team: Italian (https://app.transifex.com/qsfera-eu/teams/204053/it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Aggiunto come membro di uno spazio"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Giornaliero"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Intervallo di invio delle e-mail"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "File rifiutato"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Istantaneo"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Mai"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Inviami una notifica quando vengo aggiunto come membro di uno spazio"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Inviami una notifica quando vengo rimosso come membro di uno spazio"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Inviami una notifica quando ricevo una condivisione"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Inviami una notifica quando un file che ho caricato viene rifiutato a causa "
|
||||
"di una violazione delle policy o a causa della presenza di virus."
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Inviami una notifica quando una condivisione viene rimossa"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Inviami una notifica quando una condivisione ricevuta scade"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr ""
|
||||
"Inviami una notifica quando uno spazio di cui sono membro viene eliminato"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Inviami una notifica quando uno spazio di cui sono membro viene disattivato"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Inviami una notifica quando uno spazio di cui sono membro scade"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Non più un membro dello spazio"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valore selezionato: "
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Condivisione scaduta"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Condivisione ricevuta"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Condivisione rimossa"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Spazio eliminato"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Spazio disattivato"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Partecipazione ad uno spazio scaduta"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Settimanale"
|
||||
@@ -0,0 +1,144 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# iikaka88, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-28 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: iikaka88, 2025\n"
|
||||
"Language-Team: Japanese (https://app.transifex.com/qsfera-eu/teams/204053/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ja\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "スペースメンバーへの追加"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "毎日"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "メール送信間隔"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "ファイルの拒否"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "即時"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "しない"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "スペースのメンバーに追加された時に通知する"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "スペースのメンバーから削除された時に通知する"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "共有を受け取った時に通知する"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr "アップロードしたファイルがウイルスやポリシー違反で拒否された時に通知する"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "受け取った共有が削除された時に通知する"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "受け取った共有の期限が切れた時に通知する"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "参加しているスペースが削除された時に通知する"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "参加しているスペースが無効化された時に通知する"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "スペースのメンバーシップ期限が切れた時に通知する"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "スペースメンバーからの削除"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "選択された値:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "共有期限切れ"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "共有受け取った時"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "共有削除"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "スペース削除"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "スペース無効化"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "スペース期限切れ"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "毎週"
|
||||
@@ -0,0 +1,144 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# gapho shin, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: gapho shin, 2025\n"
|
||||
"Language-Team: Korean (https://app.transifex.com/qsfera-eu/teams/204053/ko/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ko\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "스페이스 멤버로 추가됨"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "일간"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "이메일 전송 간격"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "파일 거부"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "즉시"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "지속"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "내가 공간에 멤버로 추가되었을 때 알림"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "내가 공간에서 멤버로 제거되었을 때 알림"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "공유 받으면 알림"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr "바이러스 감염 또는 정책 위반으로 인해 업로드한 파일이 거부되었을 때 알림"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "허가된 공유가 제거되면 알림"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "허가된 공유가 만료되면 알림"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "내가 속한 공간이 삭제되었을 때 알림"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "내가 속한 공간이 비활성화되었을 때 알림"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "스페이스 멤버십이 만료되면 알림"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "스페이스 멤버가 제거됨"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "선택한 값:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "만료된 공유"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "공유됨"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "공유제거"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "스페이스 삭제"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "스페이스 비활성화"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "스페이스 멤버십 만료"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "주간"
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# BoneNI, 2026
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-30 00:02+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: BoneNI, 2026\n"
|
||||
"Language-Team: Lao (https://app.transifex.com/qsfera-eu/teams/204053/lo/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: lo\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "ຖືກເພີ່ມເປັນສະມາຊິກພື້ນທີ່"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "ປະຈຳວັນ"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "ໄລຍະຫ່າງການສົ່ງອີເມລ"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "ໄຟລ໌ຖືກປະຕິເສດ"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "ທັນທີ"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "ບໍ່ເຄີຍ"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອຂ້ອຍຖືກເພີ່ມເປັນສະມາຊິກໃນພື້ນທີ່"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອຂ້ອຍຖືກຖອນອອກຈາກການເປັນສະມາຊິກໃນພື້ນທີ່"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອຂ້ອຍໄດ້ຮັບການແບ່ງປັນ"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"ແຈ້ງເຕືອນເມື່ອໄຟລ໌ທີ່ຂ້ອຍອັບໂຫລດຖືກປະຕິເສດ ເນື່ອງຈາກກວດພົບໄວຣັສ ຫຼື "
|
||||
"ລະເມີດນະໂຍບາຍ"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອການແບ່ງປັນທີ່ໄດ້ຮັບຖືກຖອນອອກ"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອການແບ່ງປັນທີ່ໄດ້ຮັບໝົດອາຍຸ"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອພື້ນທີ່ທີ່ຂ້ອຍເປັນສະມາຊິກຖືກລຶບ"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອພື້ນທີ່ທີ່ຂ້ອຍເປັນສະມາຊິກຖືກປິດການໃຊ້ງານ"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "ແຈ້ງເຕືອນເມື່ອການເປັນສະມາຊິກພື້ນທີ່ໝົດອາຍຸ"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "ຖືກຖອນອອກຈາກການເປັນສະມາຊິກພື້ນທີ່"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "ຄ່າທີ່ເລືອກ:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "ການແບ່ງປັນໝົດອາຍຸ"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "ໄດ້ຮັບການແບ່ງປັນ"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "ການແບ່ງປັນຖືກຖອນອອກ"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "ພື້ນທີ່ຖືກລຶບ"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "ພື້ນທີ່ຖືກປິດການໃຊ້ງານ"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "ການເປັນສະມາຊິກພື້ນທີ່ໝົດອາຍຸ"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "ປະຈຳອາທິດ"
|
||||
@@ -0,0 +1,147 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Stephan Paternotte <stephan@paternottes.net>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-26 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/qsfera-eu/teams/204053/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Toegevoegd als lid aan ruimte"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Dagelijks"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Interval van e-mails"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Bestand afgewezen"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instantaan"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nooit"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Stuur een melding wanneer ik als lid aan een ruimte ben toegevoegd"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Meld me wanneer ik ben verwijderd als lid van een ruimte"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Stuur een melding wanneer iemand iets met mij deelt"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Stuur een melding wanneer een bestand dat ik heb geüpload is afgewezen "
|
||||
"vanwege een virusinfectie of schending van het beleid."
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Stuur een melding wanneer een share met mij is ingetrokken"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Stuur een melding wanneer een share met mij is verlopen"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Stuur een melding wanneer een ruimte waarvan ik lid ben is verwijderd"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr ""
|
||||
"Stuur een melding wanner een ruimte waarvan ik lid ben is uitgeschakeld"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Stuur een melding waneer het lidmaatschap aan een ruimte is verlopen"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Verwijderd als lid van een ruimte"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Geselecteerde waarde:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Share verlopen"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Share ontvangen"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Share ingetrokken"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Ruimte verwijderd"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Ruimte uitgeschakeld"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Lidmaatschap van ruimte verlopen"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Wekelijks"
|
||||
@@ -0,0 +1,148 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# skrzat <skrzacikus@gmail.com>, 2025
|
||||
# Xiaomi Box, 2025
|
||||
# Radoslaw Posim, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-06 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Radoslaw Posim, 2025\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/qsfera-eu/teams/204053/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pl\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Dodano jako członek Przestrzeni"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Dziennie"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Interwał wysyłania email"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Plik odrzucony"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Natychmiast"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nigdy"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Powiadom, jeśli dodano mnie jako członka przestrzeni"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Powiadom, jeśli usunięto mnie jako członka z przestrzeni."
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Powiadom o otrzymanym udostępnieniu."
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Poinformuj, jeśli przesłany plik został odrzucony z powodu wirusa lub "
|
||||
"naruszenia zasad"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Powiadom, jeśli otrzymane udostępnienie zostało usunięte"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Powiadom, jeśli otrzymane udostępnienie wygasło"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Powiadom, jeśli Przestrzeń gdzie jestem członkiem została usunięta"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Poinformuj, jeśli przestrzeń gdzie jestem członkiem została wyłączona"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Powiadom, jeśli członkostwo w przestrzeni wygasło"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Usunięto z przestrzeni"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Wybrana wartość:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Udostępnienie wygasło"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Otrzymano udostępnienie"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Usunięto udostępnienie"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Przestrzeń usunięta"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Przestrzeń wyłączona"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Członkostwo w przestrzeni wygasło"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Co tydzień"
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Mário Machado, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-06 00:01+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/qsfera-eu/teams/204053/pt/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Adicionado como membro do espaço"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Diário"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Intervalo de envio de e-mail"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Ficheiro rejeitado"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Instantâneo"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Nunca"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Notificar quando for adicionado como membro a um espaço"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Notificar quando for removido como membro de um espaço"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Notificar quando receber uma partilha"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Notificar quando um ficheiro que carreguei for rejeitado devido a uma "
|
||||
"infeção por vírus ou violação de política"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Notificar quando uma partilha recebida for removida"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Notificar quando uma partilha recebida expirar"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Notificar quando um espaço do qual sou membro for eliminado"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Notificar quando um espaço do qual sou membro for desativado"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Notificar quando uma subscrição de espaço expirar"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Removido como membro do espaço"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valor selecionado:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Partilha expirada"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Partilha recebida"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Partilha removida"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Espaço eliminado"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Espaço desativado"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Subscrição de espaço expirada"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Semanal"
|
||||
@@ -0,0 +1,151 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# yellow sky, 2025
|
||||
# Lulufox, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Lulufox, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/qsfera-eu/teams/204053/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Добавлен как член пространства"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Ежедневно"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Интервал отправки электронных писем"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Файл отклонен"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Немедленно"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Никогда"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Уведомлять, когда меня добавляют в пространства"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Уведомлять, когда меня исключают из пространств"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr ""
|
||||
"Уведомлять, когда мне предоставили доступ для совместного использования "
|
||||
"ресура"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Уведомлять, когда в загруженный мной файл был отклонен из-за проверки на "
|
||||
"вирусы"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr ""
|
||||
"Уведомлять, когда у меня забрали доступ для совместного использования "
|
||||
"ресурса"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Уведомлять, когда доступ для совместного использования ресурса истек"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Уведомлять, когда пространство, в котором я состою, было удалено"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Уведомлять, когда пространство, в котором я состою, было отключено"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Уведомлять, когда мое членство в пространстве истекло"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Удален как участник пространства"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Выбранное значение:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Доступ для совместного использования истек"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Получен доступ для совместного использования ресурса"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Доступ для совместного использования ресурса отозван"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Пространство удалено"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Пространство отключено"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Членство в пространстве истекло"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Еженедельно"
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# Davis Kaza, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Davis Kaza, 2025\n"
|
||||
"Language-Team: Swedish (https://app.transifex.com/qsfera-eu/teams/204053/sv/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sv\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Tillagd som medlem i arbetsytan"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Dagligen"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Sändningsintervall för e-post"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Fil nekad"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Omedelbart"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Aldrig"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Meddela mig när jag blivit tillagd som medlem i en arbetsyta"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Meddela mig när jag tagits bort som medlem i en arbetsyta"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Meddela mig när jag tagit emot en delning"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Meddela mig när en fil jag laddat upp har nekats på grund av virus eller "
|
||||
"policybrott"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Meddela när en mottagen delning har raderats"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Meddela när en mottagen delning har löpt ut"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Meddela när en arbetsyta jag är medlem i har raderats"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Meddela när en arbetsyta jag är medlem i har avaktiverats"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Meddela när ett medlemskap i en arbetsyta har löpt ut"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Borttagen som medlem i arbetsyta"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Valt värde:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Delning har löpt ut"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Delning mottagen"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Delning raderad"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Arbetsyta raderad"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Arbetsyta avaktiverad"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Medlemskap i arbetsyta har löpt ut"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Veckovis"
|
||||
@@ -0,0 +1,146 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# LinkinWires <darkinsonic13@gmail.com>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-05-08 00:02+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: LinkinWires <darkinsonic13@gmail.com>, 2025\n"
|
||||
"Language-Team: Ukrainian (https://app.transifex.com/qsfera-eu/teams/204053/uk/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: uk\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "Додавання до простору у якості учасника (-ці)"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "Щоденно"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "Інтервал надсилання електронних листів"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "Відхилення файлу"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "Миттєво"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "Ніколи"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "Сповіщати, коли мене додають як учасника (-цю) до простору"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "Сповіщати, коли мене видаляють з учасників простору"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "Сповіщати, коли зі мною поділилися файлом або папкою"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr ""
|
||||
"Сповіщати, коли завантажений мною файл було відхилено через знайдений вірус "
|
||||
"або порушення політик"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "Сповіщати, коли зі мною більше не діляться файлом або папкою"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "Сповіщати, коли термін доступу до файлу або папки сплинув"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "Сповіщати, коли простір, учасником (-цею) якого я є, був видалений"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "Сповіщати, коли простір, учасником (-цею) якого я є, був вимкнений"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "Сповіщати, коли термін доступу до простору сплинув"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "Видалення з простору"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "Обране значення:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "Сплинення доступу до файлу / папки"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "Надання доступу"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "Видалення доступу"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "Видалення простору"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "Вимкнення простору"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "Сплинення доступу до простору"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "Щотижнево"
|
||||
@@ -0,0 +1,144 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
# Translators:
|
||||
# YQS Yang, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-04-22 00:03+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: YQS Yang, 2025\n"
|
||||
"Language-Team: Chinese (https://app.transifex.com/qsfera-eu/teams/204053/zh/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: zh\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#. name of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:20
|
||||
msgid "Added as space member"
|
||||
msgstr "添加为空间成员"
|
||||
|
||||
#. translation for the 'daily' email interval option
|
||||
#: pkg/store/defaults/templates.go:50
|
||||
msgid "Daily"
|
||||
msgstr "每日"
|
||||
|
||||
#. name of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:44
|
||||
msgid "Email sending interval"
|
||||
msgstr "邮件发送频率"
|
||||
|
||||
#. name of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:40
|
||||
msgid "File rejected"
|
||||
msgstr "文件被拒绝"
|
||||
|
||||
#. translation for the 'instant' email interval option
|
||||
#: pkg/store/defaults/templates.go:48
|
||||
msgid "Instant"
|
||||
msgstr "实时"
|
||||
|
||||
#. translation for the 'never' email interval option
|
||||
#: pkg/store/defaults/templates.go:54
|
||||
msgid "Never"
|
||||
msgstr "从不"
|
||||
|
||||
#. description of the notification option 'Space Shared'
|
||||
#: pkg/store/defaults/templates.go:22
|
||||
msgid "Notify when I have been added as a member to a space"
|
||||
msgstr "当我被添加为空间成员时通知我"
|
||||
|
||||
#. description of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:26
|
||||
msgid "Notify when I have been removed as member from a space"
|
||||
msgstr "当我被从空间中移除成员资格时通知我"
|
||||
|
||||
#. description of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:10
|
||||
msgid "Notify when I have received a share"
|
||||
msgstr "当我收到共享时通知我"
|
||||
|
||||
#. description of the notification option 'File Rejected'
|
||||
#: pkg/store/defaults/templates.go:42
|
||||
msgid ""
|
||||
"Notify when a file I uploaded was rejected because of a virus infection or "
|
||||
"policy violation"
|
||||
msgstr "当我上传的文件因病毒感染或违反策略而被拒绝时通知我"
|
||||
|
||||
#. description of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:14
|
||||
msgid "Notify when a received share has been removed"
|
||||
msgstr "当收到的共享被移除时通知我"
|
||||
|
||||
#. description of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:18
|
||||
msgid "Notify when a received share has expired"
|
||||
msgstr "当收到的共享已过期时通知我"
|
||||
|
||||
#. description of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:38
|
||||
msgid "Notify when a space I am member of has been deleted"
|
||||
msgstr "当我所在的空间被删除时通知我"
|
||||
|
||||
#. description of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:34
|
||||
msgid "Notify when a space I am member of has been disabled"
|
||||
msgstr "当我所在的空间被禁用时通知我"
|
||||
|
||||
#. description of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:30
|
||||
msgid "Notify when a space membership has expired"
|
||||
msgstr "当空间成员资格已过期时通知我"
|
||||
|
||||
#. name of the notification option 'Space Unshared'
|
||||
#: pkg/store/defaults/templates.go:24
|
||||
msgid "Removed as space member"
|
||||
msgstr "移除空间成员资格"
|
||||
|
||||
#. description of the notification option 'Email Interval'
|
||||
#: pkg/store/defaults/templates.go:46
|
||||
msgid "Selected value:"
|
||||
msgstr "已选值:"
|
||||
|
||||
#. name of the notification option 'Share Expired'
|
||||
#: pkg/store/defaults/templates.go:16
|
||||
msgid "Share Expired"
|
||||
msgstr "共享已过期"
|
||||
|
||||
#. name of the notification option 'Share Received'
|
||||
#: pkg/store/defaults/templates.go:8
|
||||
msgid "Share Received"
|
||||
msgstr "收到共享"
|
||||
|
||||
#. name of the notification option 'Share Removed'
|
||||
#: pkg/store/defaults/templates.go:12
|
||||
msgid "Share Removed"
|
||||
msgstr "共享已移除"
|
||||
|
||||
#. name of the notification option 'Space Deleted'
|
||||
#: pkg/store/defaults/templates.go:36
|
||||
msgid "Space deleted"
|
||||
msgstr "空间已删除"
|
||||
|
||||
#. name of the notification option 'Space Disabled'
|
||||
#: pkg/store/defaults/templates.go:32
|
||||
msgid "Space disabled"
|
||||
msgstr "空间已禁用"
|
||||
|
||||
#. name of the notification option 'Space Membership Expired'
|
||||
#: pkg/store/defaults/templates.go:28
|
||||
msgid "Space membership expired"
|
||||
msgstr "空间成员资格已过期"
|
||||
|
||||
#. translation for the 'weekly' email interval option
|
||||
#: pkg/store/defaults/templates.go:52
|
||||
msgid "Weekly"
|
||||
msgstr "每周"
|
||||
@@ -0,0 +1,39 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/services/settings/pkg/config"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
// Logger provides a function to set the logger option.
|
||||
func Logger(val log.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = val
|
||||
}
|
||||
}
|
||||
|
||||
// Config provides a function to set the config option.
|
||||
func Config(val *config.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.Config = val
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware provides a function to set the middleware option.
|
||||
func Middleware(val ...func(http.Handler) http.Handler) Option {
|
||||
return func(o *Options) {
|
||||
o.Middleware = val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
)
|
||||
|
||||
func (g Service) hasPermission(
|
||||
roleIDs []string,
|
||||
resource *settingsmsg.Resource,
|
||||
operations []settingsmsg.Permission_Operation,
|
||||
constraint settingsmsg.Permission_Constraint,
|
||||
) bool {
|
||||
permissions, err := g.manager.ListPermissionsByResource(resource, roleIDs)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).
|
||||
Str("resource-type", resource.Type.String()).
|
||||
Str("resource-id", resource.Id).
|
||||
Msg("permissions could not be loaded for resource")
|
||||
return false
|
||||
}
|
||||
permissions = getFilteredPermissionsByOperations(permissions, operations)
|
||||
return isConstraintFulfilled(permissions, constraint)
|
||||
}
|
||||
|
||||
// filterPermissionsByOperations returns the subset of the given permissions, where at least one of the given operations is fulfilled.
|
||||
func getFilteredPermissionsByOperations(permissions []*settingsmsg.Permission, operations []settingsmsg.Permission_Operation) []*settingsmsg.Permission {
|
||||
var filteredPermissions []*settingsmsg.Permission
|
||||
for _, permission := range permissions {
|
||||
if isAnyOperationFulfilled(permission, operations) {
|
||||
filteredPermissions = append(filteredPermissions, permission)
|
||||
}
|
||||
}
|
||||
return filteredPermissions
|
||||
}
|
||||
|
||||
// isAnyOperationFulfilled checks if the permissions is about any of the operations
|
||||
func isAnyOperationFulfilled(permission *settingsmsg.Permission, operations []settingsmsg.Permission_Operation) bool {
|
||||
for _, operation := range operations {
|
||||
if operation == permission.Operation {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isConstraintFulfilled checks if one of the permissions has the same or a parent of the constraint.
|
||||
// this is only a comparison on ENUM level. More sophisticated checks cannot happen here...
|
||||
func isConstraintFulfilled(permissions []*settingsmsg.Permission, constraint settingsmsg.Permission_Constraint) bool {
|
||||
for _, permission := range permissions {
|
||||
// comparing enum by order is not a feasible solution, because `SHARED` is not a superset of `OWN`.
|
||||
if permission.Constraint == settingsmsg.Permission_CONSTRAINT_ALL {
|
||||
return true
|
||||
}
|
||||
if permission.Constraint != settingsmsg.Permission_CONSTRAINT_UNKNOWN && permission.Constraint == constraint {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,720 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cs3permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
|
||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
"github.com/qsfera/server/pkg/l10n"
|
||||
"github.com/qsfera/server/pkg/log"
|
||||
"github.com/qsfera/server/pkg/middleware"
|
||||
"github.com/qsfera/server/pkg/roles"
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
settingssvc "github.com/qsfera/server/protogen/gen/qsfera/services/settings/v0"
|
||||
"github.com/qsfera/server/services/settings/pkg/config"
|
||||
"github.com/qsfera/server/services/settings/pkg/settings"
|
||||
"github.com/qsfera/server/services/settings/pkg/store/defaults"
|
||||
metastore "github.com/qsfera/server/services/settings/pkg/store/metadata"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
"go-micro.dev/v4/metadata"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
//go:embed l10n/locale
|
||||
var _translationFS embed.FS
|
||||
|
||||
var _domain = "settings"
|
||||
|
||||
// Service represents a service.
|
||||
type Service struct {
|
||||
id string
|
||||
config *config.Config
|
||||
logger log.Logger
|
||||
manager settings.Manager
|
||||
}
|
||||
|
||||
// NewService returns a service implementation for Service.
|
||||
func NewService(cfg *config.Config, logger log.Logger) settings.ServiceHandler {
|
||||
service := Service{
|
||||
id: "qsfera-settings",
|
||||
config: cfg,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
service.manager = metastore.New(cfg)
|
||||
return service
|
||||
}
|
||||
|
||||
// CheckPermission implements the CS3 API Permssions service.
|
||||
// It's used to check if a subject (user or group) has a permission.
|
||||
func (g Service) CheckPermission(ctx context.Context, req *cs3permissions.CheckPermissionRequest) (*cs3permissions.CheckPermissionResponse, error) {
|
||||
spec := req.GetSubjectRef().GetSpec()
|
||||
|
||||
var accountID string
|
||||
switch ref := spec.(type) {
|
||||
case *cs3permissions.SubjectReference_UserId:
|
||||
accountID = ref.UserId.GetOpaqueId()
|
||||
case *cs3permissions.SubjectReference_GroupId:
|
||||
accountID = ref.GroupId.GetOpaqueId()
|
||||
}
|
||||
|
||||
assignments, err := g.manager.ListRoleAssignments(accountID)
|
||||
if err != nil {
|
||||
return &cs3permissions.CheckPermissionResponse{
|
||||
Status: status.NewInternal(ctx, err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
roleIDs := make([]string, 0, len(assignments))
|
||||
for _, a := range assignments {
|
||||
roleIDs = append(roleIDs, a.GetRoleId())
|
||||
}
|
||||
|
||||
permission, err := g.manager.ReadPermissionByName(req.GetPermission(), roleIDs)
|
||||
if err != nil {
|
||||
if !errors.Is(err, settings.ErrNotFound) {
|
||||
return &cs3permissions.CheckPermissionResponse{
|
||||
Status: status.NewInternal(ctx, err.Error()),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if permission == nil {
|
||||
return &cs3permissions.CheckPermissionResponse{
|
||||
Status: &rpcv1beta1.Status{
|
||||
Code: rpcv1beta1.Code_CODE_PERMISSION_DENIED,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &cs3permissions.CheckPermissionResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO: check permissions on every request
|
||||
|
||||
// SaveBundle implements the BundleServiceHandler interface
|
||||
func (g Service) SaveBundle(ctx context.Context, req *settingssvc.SaveBundleRequest, res *settingssvc.SaveBundleResponse) error {
|
||||
cleanUpResource(ctx, req.GetBundle().GetResource())
|
||||
if err := g.checkStaticPermissionsByBundleType(ctx, req.GetBundle().GetType()); err != nil {
|
||||
return err
|
||||
}
|
||||
if validationError := validateSaveBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
|
||||
r, err := g.manager.WriteBundle(req.GetBundle())
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Bundle = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBundle implements the BundleServiceHandler interface
|
||||
func (g Service) GetBundle(ctx context.Context, req *settingssvc.GetBundleRequest, res *settingssvc.GetBundleResponse) error {
|
||||
if validationError := validateGetBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
bundle, err := g.manager.ReadBundle(req.GetBundleId())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
filteredBundle := g.getFilteredBundle(g.getRoleIDs(ctx), bundle)
|
||||
if len(filteredBundle.GetSettings()) == 0 {
|
||||
err = fmt.Errorf("could not read bundle: %s", req.GetBundleId())
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Bundle = filteredBundle
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListBundles implements the BundleServiceHandler interface
|
||||
func (g Service) ListBundles(ctx context.Context, req *settingssvc.ListBundlesRequest, res *settingssvc.ListBundlesResponse) error {
|
||||
// fetch all bundles
|
||||
if validationError := validateListBundles(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
bundles, err := g.manager.ListBundles(settingsmsg.Bundle_TYPE_DEFAULT, req.GetBundleIds())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
roleIDs := g.getRoleIDs(ctx)
|
||||
|
||||
// find user locale
|
||||
var locale string
|
||||
if u, ok := ctxpkg.ContextGetUser(ctx); ok {
|
||||
var err error
|
||||
locale, err = g.getUserLocale(ctx, u.GetId().GetOpaqueId())
|
||||
if err != nil {
|
||||
g.logger.Error().Err(err).Str("userid", u.GetId().GetOpaqueId()).Msg("failed to get user locale")
|
||||
}
|
||||
}
|
||||
|
||||
// filter settings in bundles that are allowed according to roles
|
||||
var filteredBundles []*settingsmsg.Bundle
|
||||
for _, bundle := range bundles {
|
||||
filteredBundle := g.getFilteredBundle(roleIDs, bundle)
|
||||
if len(filteredBundle.GetSettings()) > 0 {
|
||||
t := l10n.NewTranslatorFromCommonConfig(g.config.DefaultLanguage, _domain, g.config.TranslationPath, _translationFS, "l10n/locale").Locale(locale)
|
||||
filteredBundles = append(filteredBundles, translateBundle(filteredBundle, t))
|
||||
}
|
||||
}
|
||||
|
||||
res.Bundles = filteredBundles
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Service) getFilteredBundle(roleIDs []string, bundle *settingsmsg.Bundle) *settingsmsg.Bundle {
|
||||
// check if full bundle is whitelisted
|
||||
bundleResource := &settingsmsg.Resource{
|
||||
Type: settingsmsg.Resource_TYPE_BUNDLE,
|
||||
Id: bundle.GetId(),
|
||||
}
|
||||
if g.hasPermission(
|
||||
roleIDs,
|
||||
bundleResource,
|
||||
[]settingsmsg.Permission_Operation{settingsmsg.Permission_OPERATION_READ, settingsmsg.Permission_OPERATION_READWRITE},
|
||||
settingsmsg.Permission_CONSTRAINT_OWN,
|
||||
) {
|
||||
return bundle
|
||||
}
|
||||
|
||||
// filter settings based on permissions
|
||||
var filteredSettings []*settingsmsg.Setting
|
||||
for _, setting := range bundle.GetSettings() {
|
||||
settingResource := &settingsmsg.Resource{
|
||||
Type: settingsmsg.Resource_TYPE_SETTING,
|
||||
Id: setting.GetId(),
|
||||
}
|
||||
if g.hasPermission(
|
||||
roleIDs,
|
||||
settingResource,
|
||||
[]settingsmsg.Permission_Operation{settingsmsg.Permission_OPERATION_READ, settingsmsg.Permission_OPERATION_READWRITE},
|
||||
settingsmsg.Permission_CONSTRAINT_OWN,
|
||||
) {
|
||||
filteredSettings = append(filteredSettings, setting)
|
||||
}
|
||||
}
|
||||
bundle.Settings = filteredSettings
|
||||
return bundle
|
||||
}
|
||||
|
||||
// AddSettingToBundle implements the BundleServiceHandler interface
|
||||
func (g Service) AddSettingToBundle(ctx context.Context, req *settingssvc.AddSettingToBundleRequest, res *settingssvc.AddSettingToBundleResponse) error {
|
||||
cleanUpResource(ctx, req.GetSetting().GetResource())
|
||||
if err := g.checkStaticPermissionsByBundleID(ctx, req.GetBundleId()); err != nil {
|
||||
return err
|
||||
}
|
||||
if validationError := validateAddSettingToBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
|
||||
r, err := g.manager.AddSettingToBundle(req.GetBundleId(), req.GetSetting())
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Setting = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSettingFromBundle implements the BundleServiceHandler interface
|
||||
func (g Service) RemoveSettingFromBundle(ctx context.Context, req *settingssvc.RemoveSettingFromBundleRequest, _ *emptypb.Empty) error {
|
||||
if err := g.checkStaticPermissionsByBundleID(ctx, req.GetBundleId()); err != nil {
|
||||
return err
|
||||
}
|
||||
if validationError := validateRemoveSettingFromBundle(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
|
||||
if err := g.manager.RemoveSettingFromBundle(req.GetBundleId(), req.GetSettingId()); err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveValue implements the ValueServiceHandler interface
|
||||
func (g Service) SaveValue(ctx context.Context, req *settingssvc.SaveValueRequest, res *settingssvc.SaveValueResponse) error {
|
||||
req.Value.AccountUuid = getValidatedAccountUUID(ctx, req.GetValue().GetAccountUuid())
|
||||
if !g.isCurrentUser(ctx, req.GetValue().GetAccountUuid()) {
|
||||
return merrors.Forbidden(g.id, "can't save value for another user")
|
||||
}
|
||||
|
||||
cleanUpResource(ctx, req.GetValue().GetResource())
|
||||
// TODO: we need to check, if the authenticated user has permission to write the value for the specified resource (e.g. global, file with id xy, ...)
|
||||
if validationError := validateSaveValue(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError.Error())
|
||||
}
|
||||
r, err := g.manager.WriteValue(req.GetValue())
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(r)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Value = valueWithIdentifier
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValue implements the ValueServiceHandler interface
|
||||
func (g Service) GetValue(_ context.Context, req *settingssvc.GetValueRequest, res *settingssvc.GetValueResponse) error {
|
||||
if validationError := validateGetValue(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ReadValue(req.GetId())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(r)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Value = valueWithIdentifier
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetValueByUniqueIdentifiers implements the ValueService interface
|
||||
func (g Service) GetValueByUniqueIdentifiers(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, res *settingssvc.GetValueResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(ctx, req.GetAccountUuid())
|
||||
if !g.isCurrentUser(ctx, req.GetAccountUuid()) {
|
||||
return merrors.Forbidden(g.id, "can't get value of another user")
|
||||
}
|
||||
if validationError := validateGetValueByUniqueIdentifiers(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
v, err := g.manager.ReadValueByUniqueIdentifiers(req.GetAccountUuid(), req.GetSettingId())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
|
||||
if v.GetBundleId() != "" {
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(v)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
|
||||
res.Value = valueWithIdentifier
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListValues implements the ValueServiceHandler interface
|
||||
func (g Service) ListValues(ctx context.Context, req *settingssvc.ListValuesRequest, res *settingssvc.ListValuesResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(ctx, req.GetAccountUuid())
|
||||
if !g.isCurrentUser(ctx, req.GetAccountUuid()) {
|
||||
return merrors.Forbidden(g.id, "can't list values of another user")
|
||||
}
|
||||
|
||||
if validationError := validateListValues(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
values, err := g.manager.ListValues(req.GetBundleId(), req.GetAccountUuid())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
result := make([]*settingsmsg.ValueWithIdentifier, 0, len(values))
|
||||
for _, value := range values {
|
||||
valueWithIdentifier, err := g.getValueWithIdentifier(value)
|
||||
if err == nil {
|
||||
result = append(result, valueWithIdentifier)
|
||||
}
|
||||
}
|
||||
res.Values = result
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRoles implements the RoleServiceHandler interface
|
||||
func (g Service) ListRoles(_ context.Context, req *settingssvc.ListBundlesRequest, res *settingssvc.ListBundlesResponse) error {
|
||||
//accountUUID := getValidatedAccountUUID(c, "me")
|
||||
if validationError := validateListRoles(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ListBundles(settingsmsg.Bundle_TYPE_ROLE, req.GetBundleIds())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
// TODO: only allow listing roles when user has account/role/... management permissions
|
||||
res.Bundles = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRoleAssignments implements the RoleServiceHandler interface
|
||||
func (g Service) ListRoleAssignments(ctx context.Context, req *settingssvc.ListRoleAssignmentsRequest, res *settingssvc.ListRoleAssignmentsResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(ctx, req.GetAccountUuid())
|
||||
if validationError := validateListRoleAssignments(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
r, err := g.manager.ListRoleAssignments(req.GetAccountUuid())
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Assignments = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListRoleAssignmentsFiltered implements the RoleServiceHandler interface. Who made this up? And why is everyone copying it? So this methods lists role assignments filtered by account or role.
|
||||
func (g Service) ListRoleAssignmentsFiltered(ctx context.Context, req *settingssvc.ListRoleAssignmentsFilteredRequest, res *settingssvc.ListRoleAssignmentsResponse) error {
|
||||
if validationError := validateListRoleAssignmentsFiltered(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
filters := req.GetFilters()
|
||||
|
||||
var r []*settingsmsg.UserRoleAssignment
|
||||
var err error
|
||||
switch filters[0].GetType() {
|
||||
case settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT:
|
||||
accountUUID := getValidatedAccountUUID(ctx, filters[0].GetAccountUuid())
|
||||
r, err = g.manager.ListRoleAssignments(accountUUID)
|
||||
case settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE:
|
||||
roleID := filters[0].GetRoleId()
|
||||
r, err = g.manager.ListRoleAssignmentsByRole(roleID)
|
||||
}
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "%s", err)
|
||||
}
|
||||
res.Assignments = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// AssignRoleToUser implements the RoleServiceHandler interface
|
||||
func (g Service) AssignRoleToUser(ctx context.Context, req *settingssvc.AssignRoleToUserRequest, res *settingssvc.AssignRoleToUserResponse) error {
|
||||
req.AccountUuid = getValidatedAccountUUID(ctx, req.GetAccountUuid())
|
||||
if validationError := validateAssignRoleToUser(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
|
||||
ownAccountUUID, ok := metadata.Get(ctx, middleware.AccountID)
|
||||
if !ok {
|
||||
g.logger.Debug().Str("id", g.id).Msg("user not in context")
|
||||
return merrors.InternalServerError(g.id, "user not in context")
|
||||
}
|
||||
|
||||
switch {
|
||||
case ownAccountUUID == req.GetAccountUuid():
|
||||
// Allow users to assign themself to the user or user light role
|
||||
// deny any other attempt to change the user's own assignment
|
||||
if r, err := g.manager.ListRoleAssignments(req.GetAccountUuid()); err == nil && len(r) > 0 {
|
||||
return merrors.Forbidden(g.id, "Changing own role assignment forbidden")
|
||||
}
|
||||
if req.GetRoleId() != defaults.BundleUUIDRoleUser && req.GetRoleId() != defaults.BundleUUIDRoleUserLight {
|
||||
return merrors.Forbidden(g.id, "Changing own role assignment forbidden")
|
||||
}
|
||||
g.logger.Debug().Str("userid", ownAccountUUID).Msg("Self-assignment for default 'user' role permitted")
|
||||
case g.canManageRoles(ctx):
|
||||
default:
|
||||
return merrors.Forbidden(g.id, "user has no role management permission")
|
||||
}
|
||||
|
||||
r, err := g.manager.WriteRoleAssignment(req.GetAccountUuid(), req.GetRoleId())
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Assignment = r
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRoleFromUser implements the RoleServiceHandler interface
|
||||
func (g Service) RemoveRoleFromUser(ctx context.Context, req *settingssvc.RemoveRoleFromUserRequest, _ *emptypb.Empty) error {
|
||||
if !g.canManageRoles(ctx) {
|
||||
return merrors.Forbidden(g.id, "user has no role management permission")
|
||||
}
|
||||
|
||||
if validationError := validateRemoveRoleFromUser(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
|
||||
ownAccountUUID, ok := metadata.Get(ctx, middleware.AccountID)
|
||||
if !ok {
|
||||
g.logger.Debug().Str("id", g.id).Msg("user not in context")
|
||||
return merrors.InternalServerError(g.id, "user not in context")
|
||||
}
|
||||
|
||||
al, err := g.manager.ListRoleAssignments(ownAccountUUID)
|
||||
if err != nil {
|
||||
g.logger.Debug().Err(err).Str("id", g.id).Msg("ListRoleAssignments failed")
|
||||
return merrors.InternalServerError(g.id, "%s", err)
|
||||
}
|
||||
|
||||
for _, a := range al {
|
||||
if a.GetId() == req.GetId() {
|
||||
g.logger.Debug().Str("id", g.id).Msg("Removing own role assignment forbidden")
|
||||
return merrors.Forbidden(g.id, "Removing own role assignment forbidden")
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.manager.RemoveRoleAssignment(req.GetId()); err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPermissions implements the PermissionServiceHandler interface
|
||||
func (g Service) ListPermissions(ctx context.Context, req *settingssvc.ListPermissionsRequest, res *settingssvc.ListPermissionsResponse) error {
|
||||
ownAccountUUID, ok := metadata.Get(ctx, middleware.AccountID)
|
||||
if !ok {
|
||||
g.logger.Debug().Str("id", g.id).Msg("user not in context")
|
||||
return merrors.InternalServerError(g.id, "user not in context")
|
||||
}
|
||||
|
||||
if ownAccountUUID != req.GetAccountUuid() {
|
||||
return merrors.NotFound(g.id, "user not found: %s", req.GetAccountUuid())
|
||||
}
|
||||
|
||||
assignments, err := g.manager.ListRoleAssignments(req.GetAccountUuid())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// deduplicate role ids
|
||||
roleIDs := map[string]struct{}{}
|
||||
for _, a := range assignments {
|
||||
roleIDs[a.GetRoleId()] = struct{}{}
|
||||
}
|
||||
|
||||
// deduplicate permission names
|
||||
permissionNames := map[string]struct{}{}
|
||||
for roleID := range roleIDs {
|
||||
bundle, err := g.manager.ReadBundle(roleID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, settings.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if bundle != nil {
|
||||
for _, setting := range bundle.GetSettings() {
|
||||
permissionNames[formatPermissionName(setting)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.Permissions = make([]string, 0, len(permissionNames))
|
||||
for p := range permissionNames {
|
||||
res.Permissions = append(res.Permissions, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPermissionsByResource implements the PermissionServiceHandler interface
|
||||
func (g Service) ListPermissionsByResource(ctx context.Context, req *settingssvc.ListPermissionsByResourceRequest, res *settingssvc.ListPermissionsByResourceResponse) error {
|
||||
if validationError := validateListPermissionsByResource(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
permissions, err := g.manager.ListPermissionsByResource(req.GetResource(), g.getRoleIDs(ctx))
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
res.Permissions = permissions
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPermissionByID implements the PermissionServiceHandler interface
|
||||
func (g Service) GetPermissionByID(ctx context.Context, req *settingssvc.GetPermissionByIDRequest, res *settingssvc.GetPermissionByIDResponse) error {
|
||||
if validationError := validateGetPermissionByID(req); validationError != nil {
|
||||
return merrors.BadRequest(g.id, "%s", validationError)
|
||||
}
|
||||
permission, err := g.manager.ReadPermissionByID(req.GetPermissionId(), g.getRoleIDs(ctx))
|
||||
if err != nil {
|
||||
return merrors.BadRequest(g.id, "%s", err)
|
||||
}
|
||||
if permission == nil {
|
||||
return merrors.NotFound(g.id, "%s", fmt.Errorf("permission %s not found in roles", req.GetPermissionId()))
|
||||
}
|
||||
res.Permission = permission
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanUpResource makes sure that the account uuid of the authenticated user is injected if needed.
|
||||
func cleanUpResource(ctx context.Context, resource *settingsmsg.Resource) {
|
||||
if resource != nil && resource.GetType() == settingsmsg.Resource_TYPE_USER {
|
||||
resource.Id = getValidatedAccountUUID(ctx, resource.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
// getValidatedAccountUUID converts `me` into an actual account uuid from the context, if possible.
|
||||
// the result of this function will always be a valid lower-case UUID or an empty string.
|
||||
func getValidatedAccountUUID(ctx context.Context, accountUUID string) string {
|
||||
if accountUUID == "me" {
|
||||
if ownAccountUUID, ok := metadata.Get(ctx, middleware.AccountID); ok {
|
||||
accountUUID = ownAccountUUID
|
||||
}
|
||||
}
|
||||
if accountUUID == "me" {
|
||||
// no matter what happens above, an accountUUID of `me` must not be passed on. Clear it instead.
|
||||
accountUUID = ""
|
||||
}
|
||||
return accountUUID
|
||||
}
|
||||
|
||||
// getRoleIDs extracts the roleIDs of the authenticated user from the context.
|
||||
func (g Service) getRoleIDs(ctx context.Context) []string {
|
||||
if ownRoleIDs, ok := roles.ReadRoleIDsFromContext(ctx); ok {
|
||||
return ownRoleIDs
|
||||
}
|
||||
if accountID, ok := metadata.Get(ctx, middleware.AccountID); ok {
|
||||
assignments, err := g.manager.ListRoleAssignments(accountID)
|
||||
if err != nil {
|
||||
g.logger.Info().Err(err).Str("userid", accountID).Msg("failed to get roles for user")
|
||||
return nil
|
||||
}
|
||||
|
||||
ownRoleIDs := make([]string, 0, len(assignments))
|
||||
for _, a := range assignments {
|
||||
ownRoleIDs = append(ownRoleIDs, a.GetRoleId())
|
||||
}
|
||||
return ownRoleIDs
|
||||
}
|
||||
g.logger.Info().Msg("failed to get accountID from context")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Service) getValueWithIdentifier(value *settingsmsg.Value) (*settingsmsg.ValueWithIdentifier, error) {
|
||||
bundle, err := g.manager.ReadBundle(value.GetBundleId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setting, err := g.manager.ReadSetting(value.GetSettingId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settingsmsg.ValueWithIdentifier{
|
||||
Identifier: &settingsmsg.Identifier{
|
||||
Extension: bundle.GetExtension(),
|
||||
Bundle: bundle.GetName(),
|
||||
Setting: setting.GetName(),
|
||||
},
|
||||
Value: value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g Service) hasStaticPermission(ctx context.Context, permissionID string) bool {
|
||||
roleIDs, ok := roles.ReadRoleIDsFromContext(ctx)
|
||||
if !ok {
|
||||
// TODO add system role for internal requests.
|
||||
// - at least the proxy needs to look up account info
|
||||
// - glauth needs to make bind requests
|
||||
// tracked as OCIS-454
|
||||
|
||||
accountID, ok := metadata.Get(ctx, middleware.AccountID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
assignments, err := g.manager.ListRoleAssignments(accountID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// deduplicate roleids
|
||||
uniqueRoleIDs := make(map[string]struct{})
|
||||
for _, a := range assignments {
|
||||
uniqueRoleIDs[a.GetRoleId()] = struct{}{}
|
||||
}
|
||||
roleIDs = make([]string, 0, len(uniqueRoleIDs))
|
||||
for a := range uniqueRoleIDs {
|
||||
roleIDs = append(roleIDs, a)
|
||||
}
|
||||
}
|
||||
p, err := g.manager.ReadPermissionByID(permissionID, roleIDs)
|
||||
return err == nil && p != nil
|
||||
}
|
||||
|
||||
func (g Service) checkStaticPermissionsByBundleID(ctx context.Context, bundleID string) error {
|
||||
bundle, err := g.manager.ReadBundle(bundleID)
|
||||
if err != nil {
|
||||
return merrors.NotFound(g.id, "bundle not found: %s", err)
|
||||
}
|
||||
return g.checkStaticPermissionsByBundleType(ctx, bundle.GetType())
|
||||
}
|
||||
|
||||
func (g Service) checkStaticPermissionsByBundleType(ctx context.Context, bundleType settingsmsg.Bundle_Type) error {
|
||||
if bundleType == settingsmsg.Bundle_TYPE_ROLE {
|
||||
if !g.hasStaticPermission(ctx, RoleManagementPermissionID) {
|
||||
return merrors.Forbidden(g.id, "user has no role management permission")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !g.hasStaticPermission(ctx, SettingsManagementPermissionID) {
|
||||
return merrors.Forbidden(g.id, "user has no settings management permission")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g Service) isCurrentUser(ctx context.Context, accountID string) bool {
|
||||
ownAccountID, ok := metadata.Get(ctx, middleware.AccountID)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return accountID == ownAccountID
|
||||
}
|
||||
|
||||
func (g Service) canManageRoles(ctx context.Context) bool {
|
||||
return g.hasStaticPermission(ctx, RoleManagementPermissionID)
|
||||
}
|
||||
|
||||
func (g Service) getUserLocale(ctx context.Context, userID string) (string, error) {
|
||||
var resp settingssvc.GetValueResponse
|
||||
err := g.GetValueByUniqueIdentifiers(
|
||||
ctx,
|
||||
&settingssvc.GetValueByUniqueIdentifiersRequest{
|
||||
AccountUuid: userID,
|
||||
SettingId: defaults.SettingUUIDProfileLanguage,
|
||||
},
|
||||
&resp,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
val := resp.GetValue().GetValue().GetListValue().GetValues()
|
||||
if len(val) == 0 {
|
||||
return "", errors.New("no language setting found")
|
||||
}
|
||||
return val[0].GetStringValue(), nil
|
||||
}
|
||||
|
||||
func formatPermissionName(setting *settingsmsg.Setting) string {
|
||||
constraint := strings.TrimPrefix(setting.GetPermissionValue().GetConstraint().String(), "CONSTRAINT_")
|
||||
return setting.GetName() + "." + strings.ToLower(constraint)
|
||||
}
|
||||
|
||||
func translateBundle(bundle *settingsmsg.Bundle, t *gotext.Locale) *settingsmsg.Bundle {
|
||||
for i, set := range bundle.GetSettings() {
|
||||
switch set.GetId() {
|
||||
default:
|
||||
continue
|
||||
case defaults.SettingUUIDProfileEmailSendingInterval:
|
||||
// translate interval names ('Instant', 'Daily', 'Weekly', 'Never')
|
||||
value := set.GetSingleChoiceValue()
|
||||
for i, v := range value.GetOptions() {
|
||||
value.Options[i].DisplayValue = t.Get(v.GetDisplayValue(), []any{}...)
|
||||
}
|
||||
set.Value = &settingsmsg.Setting_SingleChoiceValue{SingleChoiceValue: value}
|
||||
fallthrough
|
||||
case defaults.SettingUUIDProfileEventShareCreated,
|
||||
defaults.SettingUUIDProfileEventShareRemoved,
|
||||
defaults.SettingUUIDProfileEventShareExpired,
|
||||
defaults.SettingUUIDProfileEventSpaceShared,
|
||||
defaults.SettingUUIDProfileEventSpaceUnshared,
|
||||
defaults.SettingUUIDProfileEventSpaceMembershipExpired,
|
||||
defaults.SettingUUIDProfileEventSpaceDisabled,
|
||||
defaults.SettingUUIDProfileEventSpaceDeleted:
|
||||
// translate event names ('Share Received', 'Share Removed', ...)
|
||||
set.DisplayName = t.Get(set.GetDisplayName(), []any{}...)
|
||||
// translate event descriptions ('Notify me when I receive a share', ...)
|
||||
set.Description = t.Get(set.GetDescription(), []any{}...)
|
||||
bundle.Settings[i] = set
|
||||
}
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/qsfera/server/pkg/middleware"
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
v0 "github.com/qsfera/server/protogen/gen/qsfera/services/settings/v0"
|
||||
"github.com/qsfera/server/services/settings/pkg/settings/mocks"
|
||||
"github.com/qsfera/server/services/settings/pkg/store/defaults"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
merrors "go-micro.dev/v4/errors"
|
||||
"go-micro.dev/v4/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
ctxWithUUID = metadata.Set(context.Background(), middleware.AccountID, "61445573-4dbe-4d56-88dc-88ab47aceba7")
|
||||
ctxWithEmptyUUID = metadata.Set(context.Background(), middleware.AccountID, "")
|
||||
emptyCtx = context.Background()
|
||||
|
||||
scenarios = []struct {
|
||||
name string
|
||||
accountUUID string
|
||||
ctx context.Context
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "context with UUID; identifier = 'me'",
|
||||
ctx: ctxWithUUID,
|
||||
accountUUID: "me",
|
||||
expect: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
},
|
||||
{
|
||||
name: "context with empty UUID; identifier = 'me'",
|
||||
ctx: ctxWithEmptyUUID,
|
||||
accountUUID: "me",
|
||||
expect: "",
|
||||
},
|
||||
{
|
||||
name: "context without UUID; identifier = 'me'",
|
||||
ctx: emptyCtx,
|
||||
accountUUID: "me",
|
||||
expect: "",
|
||||
},
|
||||
{
|
||||
name: "context with UUID; identifier not 'me'",
|
||||
ctx: ctxWithUUID,
|
||||
accountUUID: "",
|
||||
expect: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetValidatedAccountUUID(t *testing.T) {
|
||||
for _, s := range scenarios {
|
||||
scenario := s
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
got := getValidatedAccountUUID(scenario.ctx, scenario.accountUUID)
|
||||
assert.NotPanics(t, func() {
|
||||
getValidatedAccountUUID(emptyCtx, scenario.accountUUID)
|
||||
})
|
||||
assert.Equal(t, scenario.expect, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditOwnRoleAssignment(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
a := []*settingsmsg.UserRoleAssignment{}
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(a, nil)
|
||||
manager.On("WriteRoleAssignment", mock.Anything, mock.Anything).Return(nil, nil)
|
||||
// Creating an initial self assignment is expected to succeed for UserRole when no assignment exists yet
|
||||
req := v0.AssignRoleToUserRequest{
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: defaults.BundleUUIDRoleUser,
|
||||
}
|
||||
res := v0.AssignRoleToUserResponse{}
|
||||
err := svc.AssignRoleToUser(ctxWithUUID, &req, &res)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Creating an initial self assignment is expected to succeed for UserLightRole when no assignment exists yet
|
||||
req = v0.AssignRoleToUserRequest{
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: defaults.BundleUUIDRoleUserLight,
|
||||
}
|
||||
res = v0.AssignRoleToUserResponse{}
|
||||
err = svc.AssignRoleToUser(ctxWithUUID, &req, &res)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Creating an initial self assignment is expected to fail for non UserRole when no assignment exists yet
|
||||
req = v0.AssignRoleToUserRequest{
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: defaults.BundleUUIDRoleAdmin,
|
||||
}
|
||||
res = v0.AssignRoleToUserResponse{}
|
||||
err = svc.AssignRoleToUser(ctxWithUUID, &req, &res)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
manager = &mocks.Manager{}
|
||||
svc = Service{
|
||||
manager: manager,
|
||||
}
|
||||
a = []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
},
|
||||
}
|
||||
editRolePermission := &settingsmsg.Permission{
|
||||
Operation: settingsmsg.Permission_OPERATION_READWRITE,
|
||||
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
|
||||
}
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(a, nil)
|
||||
manager.On("ReadPermissionByID", mock.Anything, mock.Anything).Return(editRolePermission, nil)
|
||||
|
||||
// Creating an self assignment is expect to fail if there is already an assingment
|
||||
req = v0.AssignRoleToUserRequest{
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: defaults.BundleUUIDRoleUser,
|
||||
}
|
||||
res = v0.AssignRoleToUserResponse{}
|
||||
err = svc.AssignRoleToUser(ctxWithUUID, &req, &res)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
manager.On("WriteRoleAssignment", mock.Anything, mock.Anything).Return(nil, nil)
|
||||
// Creating an assignment for somebody else is expected to succeed, give the right permissions
|
||||
req = v0.AssignRoleToUserRequest{
|
||||
AccountUuid: "00000000-0000-0000-0000-000000000000",
|
||||
RoleId: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
}
|
||||
res = v0.AssignRoleToUserResponse{}
|
||||
err = svc.AssignRoleToUser(ctxWithUUID, &req, &res)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestRemoveOwnRoleAssignment(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
a := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
},
|
||||
}
|
||||
editRolePermission := &settingsmsg.Permission{
|
||||
Operation: settingsmsg.Permission_OPERATION_READWRITE,
|
||||
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
|
||||
}
|
||||
manager.On("ReadPermissionByID", mock.Anything, mock.Anything).Return(editRolePermission, nil)
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(a, nil)
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
// Removing a role for oneself is expected to fail
|
||||
req := v0.RemoveRoleFromUserRequest{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
}
|
||||
err := svc.RemoveRoleFromUser(ctxWithUUID, &req, nil)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
manager = &mocks.Manager{}
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(nil, nil)
|
||||
manager.On("RemoveRoleAssignment", mock.Anything).Return(nil)
|
||||
manager.On("ReadPermissionByID", mock.Anything, mock.Anything).Return(editRolePermission, nil)
|
||||
svc = Service{
|
||||
manager: manager,
|
||||
}
|
||||
// Removing a role for someone else is expected to fail
|
||||
req = v0.RemoveRoleFromUserRequest{
|
||||
Id: "00000000-0000-0000-0000-000000000002",
|
||||
}
|
||||
err = svc.RemoveRoleFromUser(ctxWithUUID, &req, nil)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestListPermissionsOfCurrentUser(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
a := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
RoleId: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
},
|
||||
}
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(a, nil)
|
||||
b := &settingsmsg.Bundle{
|
||||
Id: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
Settings: []*settingsmsg.Setting{
|
||||
{
|
||||
Name: "some-permission",
|
||||
},
|
||||
{
|
||||
Name: "other-permission",
|
||||
},
|
||||
{
|
||||
Name: "duplicate-permission",
|
||||
},
|
||||
{
|
||||
Name: "duplicate-permission",
|
||||
},
|
||||
},
|
||||
}
|
||||
manager.On("ReadBundle", mock.Anything).Return(b, nil)
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
// Listing permissions for yourself
|
||||
req := v0.ListPermissionsRequest{
|
||||
AccountUuid: "61445573-4dbe-4d56-88dc-88ab47aceba7",
|
||||
}
|
||||
res := v0.ListPermissionsResponse{}
|
||||
err := svc.ListPermissions(ctxWithUUID, &req, &res)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res.Permissions, 3)
|
||||
}
|
||||
|
||||
func TestListPermissionsOfOtherUser(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
// Listing permissions for another user produces a not found error
|
||||
req := v0.ListPermissionsRequest{
|
||||
AccountUuid: "66666666-4444-4444-8888-88ab47aceba7",
|
||||
}
|
||||
res := v0.ListPermissionsResponse{}
|
||||
err := svc.ListPermissions(ctxWithUUID, &req, &res)
|
||||
assert.Error(t, err)
|
||||
|
||||
// assert the requested account uuid was not found
|
||||
merr, ok := merrors.As(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, int32(http.StatusNotFound), merr.Code)
|
||||
assert.Contains(t, err.Error(), req.AccountUuid)
|
||||
}
|
||||
|
||||
func TestListRoleAssignmentsFilteredValidation(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
req *v0.ListRoleAssignmentsFilteredRequest
|
||||
statusCode int32
|
||||
}{
|
||||
"no filters": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"multiple filters": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_AccountUuid{
|
||||
AccountUuid: "uid",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_RoleId{
|
||||
RoleId: "rid",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"bad filtertype": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_UNKNOWN,
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"account filter without term": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT,
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"account filter with invalid term": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_AccountUuid{
|
||||
AccountUuid: "invalid-&*&^%$#",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"role filter without term": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE,
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
"role filter with invalid uuid": {
|
||||
req: &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_RoleId{
|
||||
RoleId: "this is no uuid",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
statusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
res := v0.ListRoleAssignmentsResponse{}
|
||||
err := svc.ListRoleAssignmentsFiltered(ctxWithUUID, test.req, &res)
|
||||
merr, ok := merrors.As(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, int32(test.statusCode), merr.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRoleAssignmentsFilteredByAccount(t *testing.T) {
|
||||
accountUUID := "61445573-4dbe-4d56-88dc-88ab47aceba7"
|
||||
|
||||
tests := map[string]struct {
|
||||
result []*settingsmsg.UserRoleAssignment
|
||||
err error
|
||||
status int32
|
||||
}{
|
||||
"handles manager error": {
|
||||
result: nil,
|
||||
err: assert.AnError,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
"succeeds with results": {
|
||||
result: []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
AccountUuid: accountUUID,
|
||||
RoleId: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
status: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
manager.On("ListRoleAssignments", mock.Anything).Return(test.result, test.err)
|
||||
req := &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_AccountUuid{
|
||||
AccountUuid: accountUUID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
res := v0.ListRoleAssignmentsResponse{}
|
||||
err := svc.ListRoleAssignmentsFiltered(ctxWithUUID, req, &res)
|
||||
switch test.err {
|
||||
case nil:
|
||||
assert.Nil(t, err)
|
||||
default:
|
||||
merr, ok := merrors.As(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, int32(test.status), merr.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRoleAssignmentsFilteredByRole(t *testing.T) {
|
||||
roleID := "61445573-4dbe-4d56-88dc-88ab47aceba7"
|
||||
|
||||
tests := map[string]struct {
|
||||
result []*settingsmsg.UserRoleAssignment
|
||||
err error
|
||||
status int32
|
||||
}{
|
||||
"handles manager error": {
|
||||
result: nil,
|
||||
err: assert.AnError,
|
||||
status: http.StatusNotFound,
|
||||
},
|
||||
"succeeds with results": {
|
||||
result: []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "00000000-0000-0000-0000-000000000001",
|
||||
AccountUuid: "aceb15b8-7486-479f-ae32-c91118e07a39",
|
||||
RoleId: roleID,
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
status: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
manager := &mocks.Manager{}
|
||||
svc := Service{
|
||||
manager: manager,
|
||||
}
|
||||
manager.On("ListRoleAssignmentsByRole", mock.Anything).Return(test.result, test.err)
|
||||
req := &v0.ListRoleAssignmentsFilteredRequest{
|
||||
Filters: []*settingsmsg.UserRoleAssignmentFilter{
|
||||
{
|
||||
Type: settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE,
|
||||
Term: &settingsmsg.UserRoleAssignmentFilter_RoleId{
|
||||
RoleId: roleID,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
res := v0.ListRoleAssignmentsResponse{}
|
||||
err := svc.ListRoleAssignmentsFiltered(ctxWithUUID, req, &res)
|
||||
switch test.err {
|
||||
case nil:
|
||||
assert.Nil(t, err)
|
||||
default:
|
||||
merr, ok := merrors.As(err)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, int32(test.status), merr.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
settingssvc "github.com/qsfera/server/protogen/gen/qsfera/services/settings/v0"
|
||||
"github.com/qsfera/server/services/settings/pkg/config"
|
||||
"github.com/qsfera/server/services/settings/pkg/settings"
|
||||
"github.com/qsfera/server/services/settings/pkg/store/defaults"
|
||||
)
|
||||
|
||||
var _defaultLanguage = "en"
|
||||
|
||||
// NewDefaultLanguageService returns a default language decorator for ServiceHandler.
|
||||
func NewDefaultLanguageService(cfg *config.Config, serviceHandler settings.ServiceHandler) settings.ServiceHandler {
|
||||
defaultLanguage := cfg.DefaultLanguage
|
||||
if defaultLanguage == "" {
|
||||
defaultLanguage = _defaultLanguage
|
||||
}
|
||||
return &defaultLanguageDecorator{defaultLanguage: defaultLanguage, ServiceHandler: serviceHandler}
|
||||
}
|
||||
|
||||
type defaultLanguageDecorator struct {
|
||||
defaultLanguage string
|
||||
settings.ServiceHandler
|
||||
}
|
||||
|
||||
// GetValueByUniqueIdentifiers implements the ValueService interface
|
||||
func (s *defaultLanguageDecorator) GetValueByUniqueIdentifiers(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, res *settingssvc.GetValueResponse) error {
|
||||
err := s.ServiceHandler.GetValueByUniqueIdentifiers(ctx, req, res)
|
||||
if err != nil {
|
||||
if strings.Contains(strings.ToLower(err.Error()), "not found") && res.GetValue() == nil {
|
||||
defaultValueList := getDefaultValueList()
|
||||
// Ensure the default values for profile settings
|
||||
if _, ok := defaultValueList[req.GetSettingId()]; ok {
|
||||
res.Value = s.withDefaultProfileValue(ctx, req.AccountUuid, req.GetSettingId())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListValues implements the ValueServiceHandler interface
|
||||
func (s *defaultLanguageDecorator) ListValues(ctx context.Context, req *settingssvc.ListValuesRequest, res *settingssvc.ListValuesResponse) error {
|
||||
err := s.ServiceHandler.ListValues(ctx, req, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defaultValueList := getDefaultValueList()
|
||||
for _, v := range res.Values {
|
||||
delete(defaultValueList, v.GetValue().GetSettingId())
|
||||
}
|
||||
|
||||
// Ensure the default values for profile settings
|
||||
defaultValueList = s.withDefaultProfileValueList(ctx, req.AccountUuid, defaultValueList)
|
||||
if len(defaultValueList) > 0 {
|
||||
for _, v := range defaultValueList {
|
||||
if v != nil {
|
||||
res.Values = append(res.Values, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *defaultLanguageDecorator) withDefaultLanguageSetting(accountUUID string) *settingsmsg.ValueWithIdentifier {
|
||||
return &settingsmsg.ValueWithIdentifier{
|
||||
Identifier: &settingsmsg.Identifier{
|
||||
Extension: "qsfera-accounts",
|
||||
Bundle: "profile",
|
||||
Setting: "language",
|
||||
},
|
||||
Value: &settingsmsg.Value{
|
||||
BundleId: defaults.BundleUUIDProfile,
|
||||
SettingId: defaults.SettingUUIDProfileLanguage,
|
||||
AccountUuid: accountUUID,
|
||||
Resource: &settingsmsg.Resource{
|
||||
Type: settingsmsg.Resource_TYPE_USER,
|
||||
},
|
||||
Value: &settingsmsg.Value_ListValue{
|
||||
ListValue: &settingsmsg.ListValue{Values: []*settingsmsg.ListOptionValue{
|
||||
{
|
||||
Option: &settingsmsg.ListOptionValue_StringValue{
|
||||
StringValue: s.defaultLanguage,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *defaultLanguageDecorator) withDefaultProfileValue(ctx context.Context, accountUUID string, settingId string) *settingsmsg.ValueWithIdentifier {
|
||||
if settingId == defaults.SettingUUIDProfileLanguage {
|
||||
return s.withDefaultLanguageSetting(accountUUID)
|
||||
}
|
||||
res := s.withDefaultProfileValueList(ctx, accountUUID, map[string]*settingsmsg.ValueWithIdentifier{settingId: nil})
|
||||
if v, ok := res[settingId]; ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *defaultLanguageDecorator) withDefaultProfileValueList(ctx context.Context,
|
||||
accountUUID string, requested map[string]*settingsmsg.ValueWithIdentifier) map[string]*settingsmsg.ValueWithIdentifier {
|
||||
|
||||
// we use the default profile bundle instead of s.GetBundle(ctx, req, resp)
|
||||
bundle := defaults.GenerateDefaultProfileBundle()
|
||||
|
||||
for _, setting := range bundle.GetSettings() {
|
||||
if v, ok := requested[setting.GetId()]; !ok || v != nil {
|
||||
continue
|
||||
}
|
||||
if setting.GetId() == defaults.SettingUUIDProfileLanguage {
|
||||
requested[setting.GetId()] = s.withDefaultLanguageSetting(accountUUID)
|
||||
continue
|
||||
}
|
||||
|
||||
newVal := &settingsmsg.ValueWithIdentifier{
|
||||
Identifier: &settingsmsg.Identifier{
|
||||
Extension: bundle.GetExtension(),
|
||||
Bundle: bundle.GetName(),
|
||||
Setting: setting.GetName(),
|
||||
},
|
||||
Value: &settingsmsg.Value{
|
||||
BundleId: bundle.GetId(),
|
||||
SettingId: setting.GetId(),
|
||||
AccountUuid: accountUUID,
|
||||
Resource: setting.GetResource(),
|
||||
},
|
||||
}
|
||||
|
||||
switch val := setting.GetValue().(type) {
|
||||
case *settingsmsg.Setting_MultiChoiceCollectionValue:
|
||||
newVal.Value.Value = multiChoiceCollectionToValue(val.MultiChoiceCollectionValue)
|
||||
requested[setting.GetId()] = newVal
|
||||
case *settingsmsg.Setting_SingleChoiceValue:
|
||||
sv := &settingsmsg.Value_StringValue{}
|
||||
for _, option := range val.SingleChoiceValue.Options {
|
||||
if option.GetDefault() {
|
||||
sv.StringValue = option.Value.GetStringValue()
|
||||
break
|
||||
}
|
||||
}
|
||||
newVal.Value.Value = sv
|
||||
requested[setting.GetId()] = newVal
|
||||
}
|
||||
}
|
||||
|
||||
return requested
|
||||
}
|
||||
|
||||
func multiChoiceCollectionToValue(collection *settingsmsg.MultiChoiceCollection) *settingsmsg.Value_CollectionValue {
|
||||
values := make([]*settingsmsg.CollectionOption, 0, len(collection.GetOptions()))
|
||||
for _, option := range collection.GetOptions() {
|
||||
switch o := option.GetValue().GetOption().(type) {
|
||||
case *settingsmsg.MultiChoiceCollectionOptionValue_BoolValue:
|
||||
if o != nil {
|
||||
values = append(values, &settingsmsg.CollectionOption{
|
||||
Key: option.GetKey(),
|
||||
Option: &settingsmsg.CollectionOption_BoolValue{
|
||||
BoolValue: o.BoolValue.GetDefault(),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &settingsmsg.Value_CollectionValue{
|
||||
CollectionValue: &settingsmsg.CollectionValue{
|
||||
Values: values,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultValueList() map[string]*settingsmsg.ValueWithIdentifier {
|
||||
return map[string]*settingsmsg.ValueWithIdentifier{
|
||||
// specific profile settings should be handled individually
|
||||
defaults.SettingUUIDProfileLanguage: nil,
|
||||
// all other profile settings that populated from the bundle based on type
|
||||
defaults.SettingUUIDProfileEventShareCreated: nil,
|
||||
defaults.SettingUUIDProfileEventShareRemoved: nil,
|
||||
defaults.SettingUUIDProfileEventShareExpired: nil,
|
||||
defaults.SettingUUIDProfileEventSpaceShared: nil,
|
||||
defaults.SettingUUIDProfileEventSpaceUnshared: nil,
|
||||
defaults.SettingUUIDProfileEventSpaceMembershipExpired: nil,
|
||||
defaults.SettingUUIDProfileEventSpaceDisabled: nil,
|
||||
defaults.SettingUUIDProfileEventSpaceDeleted: nil,
|
||||
defaults.SettingUUIDProfileEventPostprocessingStepFinished: nil,
|
||||
defaults.SettingUUIDProfileEmailSendingInterval: nil,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package svc
|
||||
|
||||
const (
|
||||
|
||||
// RoleManagementPermissionID is the hardcoded setting UUID for the role management permission
|
||||
RoleManagementPermissionID string = "a53e601e-571f-4f86-8fec-d4576ef49c62"
|
||||
|
||||
// SettingsManagementPermissionID is the hardcoded setting UUID for the settings management permission
|
||||
SettingsManagementPermissionID string = "3d58f441-4a05-42f8-9411-ef5874528ae1"
|
||||
|
||||
// AccountManagementPermissionID is the hardcoded setting UUID for the account management permission
|
||||
AccountManagementPermissionID string = "8e587774-d929-4215-910b-a317b1e80f73"
|
||||
)
|
||||
@@ -0,0 +1,225 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
validation "github.com/invopop/validation"
|
||||
"github.com/invopop/validation/is"
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
settingssvc "github.com/qsfera/server/protogen/gen/qsfera/services/settings/v0"
|
||||
)
|
||||
|
||||
var (
|
||||
regexForAccountUUID = regexp.MustCompile(`^[A-Za-z0-9\-_.+@:]+$`)
|
||||
requireAccountID = []validation.Rule{
|
||||
// use rule for validation error message consistency (".. must not be blank" on empty strings)
|
||||
validation.Required,
|
||||
validation.Match(regexForAccountUUID),
|
||||
}
|
||||
regexForKeys = regexp.MustCompile(`^[A-Za-z0-9\-_]*$`)
|
||||
requireAlphanumeric = []validation.Rule{
|
||||
validation.Required,
|
||||
validation.Match(regexForKeys),
|
||||
}
|
||||
)
|
||||
|
||||
func validateSaveBundle(req *settingssvc.SaveBundleRequest) error {
|
||||
if err := validation.ValidateStruct(
|
||||
req.Bundle,
|
||||
validation.Field(&req.Bundle.Id, validation.When(req.Bundle.Id != "", is.UUID)),
|
||||
validation.Field(&req.Bundle.Name, requireAlphanumeric...),
|
||||
validation.Field(&req.Bundle.Type, validation.NotIn(settingsmsg.Bundle_TYPE_UNKNOWN)),
|
||||
validation.Field(&req.Bundle.Extension, requireAlphanumeric...),
|
||||
validation.Field(&req.Bundle.DisplayName, validation.Required),
|
||||
validation.Field(&req.Bundle.Settings, validation.Required),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateResource(req.Bundle.Resource); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range req.Bundle.Settings {
|
||||
if err := validateSetting(req.Bundle.Settings[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGetBundle(req *settingssvc.GetBundleRequest) error {
|
||||
return validation.Validate(&req.BundleId, is.UUID)
|
||||
}
|
||||
|
||||
func validateListBundles(req *settingssvc.ListBundlesRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAddSettingToBundle(req *settingssvc.AddSettingToBundleRequest) error {
|
||||
if err := validation.ValidateStruct(req, validation.Field(&req.BundleId, is.UUID)); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateSetting(req.Setting)
|
||||
}
|
||||
|
||||
func validateRemoveSettingFromBundle(req *settingssvc.RemoveSettingFromBundleRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.BundleId, is.UUID),
|
||||
validation.Field(&req.SettingId, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateSaveValue(req *settingssvc.SaveValueRequest) error {
|
||||
if err := validation.ValidateStruct(
|
||||
req.Value,
|
||||
validation.Field(&req.Value.Id, validation.When(req.Value.Id != "", is.UUID)),
|
||||
validation.Field(&req.Value.BundleId, is.UUID),
|
||||
validation.Field(&req.Value.SettingId, is.UUID),
|
||||
validation.Field(&req.Value.AccountUuid, requireAccountID...),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateResource(req.Value.Resource); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v := req.GetValue().GetCollectionValue().GetValues(); v != nil {
|
||||
for i := range v {
|
||||
if err := validation.Validate(v[i].GetKey(), validation.Required); err != nil {
|
||||
return fmt.Errorf("collectionValue.values[%d].key %w", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: validate values against the respective setting. need to check if constraints of the setting are fulfilled.
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateGetValue(req *settingssvc.GetValueRequest) error {
|
||||
return validation.Validate(req.Id, is.UUID)
|
||||
}
|
||||
|
||||
func validateGetValueByUniqueIdentifiers(req *settingssvc.GetValueByUniqueIdentifiersRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.SettingId, is.UUID),
|
||||
validation.Field(&req.AccountUuid, requireAccountID...),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListValues(req *settingssvc.ListValuesRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.BundleId, validation.When(req.BundleId != "", is.UUID)),
|
||||
validation.Field(&req.AccountUuid, validation.When(req.AccountUuid != "", validation.Match(regexForAccountUUID))),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListRoles(req *settingssvc.ListBundlesRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateListRoleAssignments(req *settingssvc.ListRoleAssignmentsRequest) error {
|
||||
return validation.Validate(req.AccountUuid, requireAccountID...)
|
||||
}
|
||||
|
||||
func validateListRoleAssignmentsFiltered(req *settingssvc.ListRoleAssignmentsFilteredRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.Filters,
|
||||
validation.Required,
|
||||
validation.Length(1, 1),
|
||||
validation.Each(validation.By(validateUserRoleAssignmentFilter)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func validateUserRoleAssignmentFilter(values any) error {
|
||||
filter, ok := values.(*settingsmsg.UserRoleAssignmentFilter)
|
||||
if !ok {
|
||||
return errors.New("expected UserRoleAssignmentFilter")
|
||||
}
|
||||
return validation.ValidateStruct(
|
||||
filter,
|
||||
validation.Field(&filter.Type,
|
||||
validation.Required,
|
||||
validation.In(settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT, settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE),
|
||||
),
|
||||
validation.Field(&filter.Term,
|
||||
validation.When(
|
||||
filter.Type == settingsmsg.UserRoleAssignmentFilter_TYPE_ACCOUNT,
|
||||
validation.By(validateFilterAccountUUID),
|
||||
),
|
||||
validation.When(
|
||||
filter.Type == settingsmsg.UserRoleAssignmentFilter_TYPE_ROLE,
|
||||
validation.By(validateFilterRoleID),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func validateFilterRoleID(value any) error {
|
||||
roleTerm, ok := value.(*settingsmsg.UserRoleAssignmentFilter_RoleId)
|
||||
if !ok {
|
||||
return errors.New("expected UserRoleAssignmentFilter_RoleId")
|
||||
}
|
||||
return validation.Validate(&roleTerm.RoleId, is.UUID)
|
||||
}
|
||||
|
||||
func validateFilterAccountUUID(value any) error {
|
||||
accountTerm, ok := value.(*settingsmsg.UserRoleAssignmentFilter_AccountUuid)
|
||||
if !ok {
|
||||
return errors.New("expected UserRoleAssignmentFilter_AccountUuid")
|
||||
}
|
||||
return validation.Validate(&accountTerm.AccountUuid, requireAccountID...)
|
||||
}
|
||||
|
||||
func validateAssignRoleToUser(req *settingssvc.AssignRoleToUserRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.AccountUuid, requireAccountID...),
|
||||
validation.Field(&req.RoleId, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateRemoveRoleFromUser(req *settingssvc.RemoveRoleFromUserRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.Id, is.UUID),
|
||||
)
|
||||
}
|
||||
|
||||
func validateListPermissionsByResource(req *settingssvc.ListPermissionsByResourceRequest) error {
|
||||
return validateResource(req.Resource)
|
||||
}
|
||||
|
||||
func validateGetPermissionByID(req *settingssvc.GetPermissionByIDRequest) error {
|
||||
return validation.ValidateStruct(
|
||||
req,
|
||||
validation.Field(&req.PermissionId, requireAlphanumeric...),
|
||||
)
|
||||
}
|
||||
|
||||
// validateResource is an internal helper for validating the content of a resource.
|
||||
func validateResource(resource *settingsmsg.Resource) error {
|
||||
if err := validation.Validate(&resource, validation.Required); err != nil {
|
||||
return err
|
||||
}
|
||||
return validation.Validate(&resource, validation.NotIn(settingsmsg.Resource_TYPE_UNKNOWN))
|
||||
}
|
||||
|
||||
// validateSetting is an internal helper for validating the content of a setting.
|
||||
func validateSetting(setting *settingsmsg.Setting) error {
|
||||
// TODO: make sanity checks, like for int settings, min <= default <= max.
|
||||
if err := validation.ValidateStruct(
|
||||
setting,
|
||||
validation.Field(&setting.Id, validation.When(setting.Id != "", is.UUID)),
|
||||
validation.Field(&setting.Name, requireAlphanumeric...),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateResource(setting.Resource)
|
||||
}
|
||||
Reference in New Issue
Block a user