diff --git a/src/onegov/org/forms/settings.py b/src/onegov/org/forms/settings.py index 5bb30d9068..99976da795 100644 --- a/src/onegov/org/forms/settings.py +++ b/src/onegov/org/forms/settings.py @@ -526,6 +526,47 @@ class LinksSettingsForm(Form): label=_('Open files in separate window') ) + short_links = TextAreaField( + label=_('Short links'), + description=( + 'kalender: /a/b/kalender\n' + 'support: https://my.support' + ), + render_kw={'rows': 10} + ) + + def validate_short_links(self, field: TextAreaField) -> None: + if not field.data: + return + + for number, line in enumerate(field.data.splitlines(), start=1): + parts = line.split(':', 1) + if len(parts) == 2: + _name, redirect_path = parts + + try: + url = URL(redirect_path.strip()) + except ValueError: + # fall-through to the end which will raise + pass + else: + if url.scheme(): + if url.host(): + # valid, skip to the next pair + continue + elif not url.host() and (path := url.path()): + if path.startswith('/'): + # valid, skip to the next pair + continue + + raise ValidationError(_( + 'Malformed short link on line ${number}. ' + 'Each line needs to be a pair of name: path where ' + 'path is either a valid path starting with a ' + 'backslash, or a complete URL with schema and host.', + mapping={'number': number} + )) + class HeaderSettingsForm(Form): diff --git a/src/onegov/org/locale/de_CH/LC_MESSAGES/onegov.org.po b/src/onegov/org/locale/de_CH/LC_MESSAGES/onegov.org.po index d54043c252..2864e4dc38 100644 --- a/src/onegov/org/locale/de_CH/LC_MESSAGES/onegov.org.po +++ b/src/onegov/org/locale/de_CH/LC_MESSAGES/onegov.org.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2026-06-23 10:57+0200\n" +"POT-Creation-Date: 2026-06-30 13:02+0200\n" "PO-Revision-Date: 2022-03-15 10:21+0100\n" "Last-Translator: Marc Sommerhalder \n" "Language-Team: German\n" @@ -218,8 +218,8 @@ msgstr "Täglicher Newsletter ${time}" msgid "${org} OneGov Cloud Status" msgstr "${org} OneGov Cloud Status" -#. #. get the resource titles and ids +#. msgid "General" msgstr "Allgemein" @@ -1236,21 +1236,21 @@ msgstr "Bitte benutzen sie ein End-Datum, welches vor dem Start-Datum liegt" msgid "" "The date range overlaps with an existing registration window (${range})." msgstr "" -"Der Datumsbereich überschneidet sich mit einem bestehenden Anmeldezeitraum " -"(${range})." +"Der Datumsbereich überschneidet sich mit einem bestehenden Anmeldezeitraum ($" +"{range})." #, python-format msgid "" -"The limit cannot be lower than the already confirmed number of attendees " -"(${claimed_spots})" +"The limit cannot be lower than the already confirmed number of attendees ($" +"{claimed_spots})" msgstr "" "Das Limit kann nicht tiefer sein als die Anzahl bereits angemeldeter " "Teilnehmer (${claimed_spots})" #, python-format msgid "" -"The limit cannot be lower than the already confirmed number attendees " -"(${claimed_spots}) and the number of pending requests (${pending_requests}). " +"The limit cannot be lower than the already confirmed number attendees ($" +"{claimed_spots}) and the number of pending requests (${pending_requests}). " "Either enable the waiting list, process the pending requests or increase the " "limit. " msgstr "" @@ -1476,8 +1476,8 @@ msgstr "Die folgenden Domänen sind für iFrames erlaubt:" msgid "To allow more domains for iFrames, please contact info@seantis.ch." msgstr "" -"Um mehr Domains für iFrames zuzulassen, kontaktieren Sie bitte info@seantis." -"ch." +"Um mehr Domains für iFrames zuzulassen, kontaktieren Sie bitte " +"info@seantis.ch." msgid "The domain of the URL is not allowed for iFrames." msgstr "Die Domäne der URL ist für iFrames nicht zulässig." @@ -2278,6 +2278,19 @@ msgstr "" msgid "Open files in separate window" msgstr "Dateien in separatem Fenster öffnen" +msgid "Short links" +msgstr "Kurzlinks" + +#, python-format +msgid "" +"Malformed short link on line ${number}. Each line needs to be a pair of " +"name: path where path is either a valid path starting with a backslash, or a " +"complete URL with schema and host." +msgstr "" +"Ungültiger Kurzlink auf Zeile ${number}. Jede Zeile muss dem Format Name: " +"Pfad entsprechen, wobei Pfad entweder ein Pfad ohne Schema oder Hostname " +"oder eine komplette URL ist." + msgid "Announcement" msgstr "Ankündigung" @@ -4184,8 +4197,8 @@ msgstr "Alle absagen mit Kommentar" msgid "Add reservation" msgstr "Reservation hinzufügen" -#. Used in sentence: "${event} published." #. +#. Used in sentence: "${event} published." msgid "Event" msgstr "Veranstaltung" @@ -4363,8 +4376,8 @@ msgstr "" "Unterseiten löschen. Um diese Seite zu löschen müssen Sie erst alle " "Unterseiten löschen, oder Sie wenden sich an einen Administrator." -#. #. Modification for the footer left on all pages +#. msgid "Source" msgstr "Herkunft" @@ -5124,8 +5137,8 @@ msgstr "Guten Tag" msgid "Your e-mail address was just used to create an account on ${homepage}." msgstr "" -"Ihre E-Mail Adresse wurde soeben zur Erstellung eines Accounts auf " -"${homepage} verwendet." +"Ihre E-Mail Adresse wurde soeben zur Erstellung eines Accounts auf $" +"{homepage} verwendet." msgid "To activate your account, click confirm below:" msgstr "Um Ihren Account zu aktivieren, bestätigen Sie bitte die Anmeldung:" @@ -5142,8 +5155,8 @@ msgstr "" msgid "Your e-mail address was just used to send a login link to ${homepage}." msgstr "" -"Ihre E-Mail Adresse wurde soeben zum Senden eines Anmeldelinks auf " -"${homepage} verwendet." +"Ihre E-Mail Adresse wurde soeben zum Senden eines Anmeldelinks auf $" +"{homepage} verwendet." msgid "Use the token below or click on the link to complete your login." msgstr "" @@ -5209,8 +5222,8 @@ msgstr "Neue Kunden Nachricht in Ticket ${link}" msgid "${author} wrote" msgstr "${author} schrieb" -#. Canonical text for ${link} is: "visit the request page" #. Canonical text for ${link} is: "visit the request status page" +#. Canonical text for ${link} is: "visit the request page" msgid "Please ${link} to reply." msgstr "Bitte ${link} um zu antworten" @@ -5222,9 +5235,9 @@ msgid "Have a great day!" msgstr "Wir wünschen Ihnen einen schönen Tag!" msgid "" -"This is the notification for customer messages on reservations for ${request." -"app.org.title}. If you no longer want to receive this e-mail please contact " -"an administrator so they can remove you from the recipients list." +"This is the notification for customer messages on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" "Dies ist die tägliche Reservations-Übersicht für ${organisation}. Falls Sie " "dieses E-Mail nicht mehr bekommen möchten, melden Sie sich bitte bei einem " @@ -5244,8 +5257,8 @@ msgid "" "want to receive this e-mail please contact an administrator so they can " "remove you from the recipients list." msgstr "" -"Dies ist die Benachrichtigung für Kunden Nachrichten zu Reservierungen für " -"${request.app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " +"Dies ist die Benachrichtigung für Kunden Nachrichten zu Reservierungen für $" +"{request.app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " "kontaktieren Sie bitte einen Administrator, damit er Sie aus der Liste " "entfernen kann." @@ -5351,12 +5364,12 @@ msgid "New note in Ticket ${link}" msgstr "Neue Notiz in Ticket ${link}" msgid "" -"This is the notification for notes on reservations for ${request.app.org." -"title}. If you no longer want to receive this e-mail please contact an " -"administrator so they can remove you from the recipients list." +"This is the notification for notes on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" -"Dies ist die Benachrichtigung für Notizen zu Reservierungen für ${request." -"app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " +"Dies ist die Benachrichtigung für Notizen zu Reservierungen für $" +"{request.app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " "kontaktieren Sie bitte einen Administrator, damit er Sie aus der Liste " "entfernen kann." @@ -5395,8 +5408,8 @@ msgid "" "you no longer want to receive this e-mail please contact an administrator so " "they can remove you from the recipients list." msgstr "" -"Dies ist die Benachrichtigung für Notizen zu Reservierungen für ${request." -"app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " +"Dies ist die Benachrichtigung für Notizen zu Reservierungen für $" +"{request.app.org.title}. Wenn Sie diese E-Mail nicht mehr erhalten möchten, " "kontaktieren Sie bitte einen Administrator, damit er Sie aus der Liste " "entfernen kann." @@ -5421,8 +5434,8 @@ msgstr "" msgid "To use your account you need the Yubikey with the serial ${number}" msgstr "" -"Um Ihr Konto zu verwenden benötigen Sie den YubiKey mit der Seriennummer " -"${number}" +"Um Ihr Konto zu verwenden benötigen Sie den YubiKey mit der Seriennummer $" +"{number}" msgid "read more" msgstr "mehr lesen" @@ -5495,8 +5508,8 @@ msgid "" "you no longer wish to receive these notifications, please contact an " "administrator so they can remove you from the recipients list." msgstr "" -"Dies ist eine Benachrichtigung über die abgelehnten Reservierungen für " -"${Organisation}. Wenn Sie diese Benachrichtigungen nicht mehr erhalten " +"Dies ist eine Benachrichtigung über die abgelehnten Reservierungen für $" +"{Organisation}. Wenn Sie diese Benachrichtigungen nicht mehr erhalten " "möchten, kontaktieren Sie bitte einen Administrator, damit er Sie aus der " "Empfängerliste entfernen kann." @@ -5819,18 +5832,18 @@ msgstr "" msgid "Request Reference" msgstr "Referenz Anfrage" +msgid "Search people" +msgstr "Personen suchen" + +msgid "Filter after Organisation or Suborganisation" +msgstr "Nach Organisation oder Unterorganisation filtern" + msgid "Select organisation" msgstr "Organisation wählen" msgid "Select sub organisation" msgstr "Unterorganisation wählen" -msgid "Filter after Organisation or Suborganisation" -msgstr "Nach Organisation oder Unterorganisation filtern" - -msgid "Search people" -msgstr "Personen suchen" - msgid "No people added yet." msgstr "Noch keine Personen erfasst." @@ -6401,8 +6414,8 @@ msgid "" "Availability period updated. ${deleted} allocations removed, ${updated} " "allocations adjusted and ${created} new allocations created." msgstr "" -"Verfügbarkeitszeitraum aktualisiert. ${deleted} Verfügbarkeiten entfernt, " -"${updated} Verfügbarkeiten angepasst und ${created} neue Verfügbarkeiten " +"Verfügbarkeitszeitraum aktualisiert. ${deleted} Verfügbarkeiten entfernt, $" +"{updated} Verfügbarkeiten angepasst und ${created} neue Verfügbarkeiten " "erstellt." msgid "Availability period not found" @@ -6615,8 +6628,8 @@ msgstr "" #, python-format msgid "" -"Cannot convert field \"${field}\" from type \"${old_type}\" to " -"\"${new_type}\"." +"Cannot convert field \"${field}\" from type \"${old_type}\" to \"${new_type}" +"\"." msgstr "" "Feld \"${field}\" kann nicht von Typ \"${old_type}\" zu \"${new_type}\" " "konvertiert werden." @@ -6722,19 +6735,19 @@ msgstr "Neuer Empfänger" #, python-format msgid "" -"Registration for notifications on new entries in the directory " -"\"${directory}\"" +"Registration for notifications on new entries in the directory \"${directory}" +"\"" msgstr "" -"Anmeldung für Benachrichtigungen bei neuen Einträgen im Verzeichnis " -"\"${directory}\"" +"Anmeldung für Benachrichtigungen bei neuen Einträgen im Verzeichnis \"$" +"{directory}\"" #, python-format msgid "" "Success! We have sent a confirmation link to ${address}, if we didn't send " "you one already." msgstr "" -"Erfolg! Wir senden eine E-Mail zur Bestätigung Ihres Abonnements an " -"${address}, sofern Sie noch nicht angemeldet sind." +"Erfolg! Wir senden eine E-Mail zur Bestätigung Ihres Abonnements an $" +"{address}, sofern Sie noch nicht angemeldet sind." msgid "Notification for new entries" msgstr "Benachrichtigung bei neuen Einträgen" @@ -7097,8 +7110,8 @@ msgid "" "Success! We have sent a confirmation link to ${address}, if we didn't send " "you one already. Your subscribed categories are ${subscribed}." msgstr "" -"Erfolg! Wir senden eine E-Mail zur Bestätigung Ihres Abonnements an " -"${address}, sofern Sie noch nicht angemeldet sind. Ihre abonnierten " +"Erfolg! Wir senden eine E-Mail zur Bestätigung Ihres Abonnements an $" +"{address}, sofern Sie noch nicht angemeldet sind. Ihre abonnierten " "Kategorien sind ${subscribed}." # python-format @@ -7454,8 +7467,8 @@ msgid "" "Failed to create visits using the dormakaba API for site ID ${site_id} " "please make sure your credentials are still valid." msgstr "" -"Das Delegieren der Entriegelung der Türen via dormakaba API für Anlage " -"${site_id} ist fehlgeschlagen. Bitte stellen Sie sicher, dass die " +"Das Delegieren der Entriegelung der Türen via dormakaba API für Anlage $" +"{site_id} ist fehlgeschlagen. Bitte stellen Sie sicher, dass die " "hinterlegten Zugangsdaten immer noch gültig sind." msgid "Accepted" @@ -7992,8 +8005,8 @@ msgstr "Beim Hochladen auf Gever ist ein Fehler aufgetreten." #, python-format msgid "" -"Encountered an error while uploading to Gever. Response status code is " -"${status}." +"Encountered an error while uploading to Gever. Response status code is $" +"{status}." msgstr "" "Beim Hochladen auf Gever ist ein Fehler aufgetreten. Der Statuscode der " "Antwort lautet ${status}." diff --git a/src/onegov/org/locale/fr_CH/LC_MESSAGES/onegov.org.po b/src/onegov/org/locale/fr_CH/LC_MESSAGES/onegov.org.po index a2476ea50f..17087b6f8c 100644 --- a/src/onegov/org/locale/fr_CH/LC_MESSAGES/onegov.org.po +++ b/src/onegov/org/locale/fr_CH/LC_MESSAGES/onegov.org.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2026-06-23 10:57+0200\n" +"POT-Creation-Date: 2026-06-30 13:02+0200\n" "PO-Revision-Date: 2022-03-15 10:50+0100\n" "Last-Translator: Marc Sommerhalder \n" "Language-Team: French\n" @@ -217,8 +217,8 @@ msgstr "Newsletter quotidienne ${time}" msgid "${org} OneGov Cloud Status" msgstr "Statut du OneGov Cloud de ${org}" -#. #. get the resource titles and ids +#. msgid "General" msgstr "Général" @@ -1240,22 +1240,22 @@ msgstr "" #, python-format msgid "" -"The limit cannot be lower than the already confirmed number of attendees " -"(${claimed_spots})" +"The limit cannot be lower than the already confirmed number of attendees ($" +"{claimed_spots})" msgstr "" -"La limite ne peut être inférieure au nombre de participants déjà confirmés " -"(${claimed_spots})" +"La limite ne peut être inférieure au nombre de participants déjà confirmés ($" +"{claimed_spots})" #, python-format msgid "" -"The limit cannot be lower than the already confirmed number attendees " -"(${claimed_spots}) and the number of pending requests (${pending_requests}). " +"The limit cannot be lower than the already confirmed number attendees ($" +"{claimed_spots}) and the number of pending requests (${pending_requests}). " "Either enable the waiting list, process the pending requests or increase the " "limit. " msgstr "" "La limite ne peut pas être inférieure au nombre de participants déjà " -"confirmés (${claims_spots}) et au nombre de demandes en attente " -"(${pending_requests}). Activez la liste d'attente, traitez les demandes en " +"confirmés (${claims_spots}) et au nombre de demandes en attente ($" +"{pending_requests}). Activez la liste d'attente, traitez les demandes en " "attente ou augmentez la limite." msgid "The end date must be later than the start date" @@ -2279,6 +2279,20 @@ msgstr "" msgid "Open files in separate window" msgstr "Ouvrir les fichiers dans une fenêtre de navigateur séparée" +msgid "Short links" +msgstr "Lien court" + +#, python-format +msgid "" +"Malformed short link on line ${number}. Each line needs to be a pair of " +"name: path where path is either a valid path starting with a backslash, or a " +"complete URL with schema and host." +msgstr "" +"Lien court mal formé à la ligne ${number}. Chaque ligne doit prendre la " +"forme « nom : chemin », où « chemin » correspond soit à un chemin valide " +"commençant par une barre oblique inversée, soit à une URL complète " +"comprenant le schéma et l'hôte." + msgid "Announcement" msgstr "Annonce" @@ -2785,8 +2799,8 @@ msgstr "" msgid "" "Invalid format. Please define subtopic(s) for '${topic}' or remove the ':'." msgstr "" -"Format incorrect. Veuillez définir un ou plusieurs sous-sujets pour " -"'${topic}' ou supprimer le ':'." +"Format incorrect. Veuillez définir un ou plusieurs sous-sujets pour '$" +"{topic}' ou supprimer le ':'." msgid "" "Invalid format. Only topics and subtopics are allowed - no deeper structures " @@ -3014,8 +3028,8 @@ msgid "" "Invalid format. Please define at least one sub-organisation for '${topic}' " "or remove the ':'" msgstr "" -"Format non valide. Veuillez définir au moins une sous-organisation pour " -"'${topic}' ou supprimer le ':'" +"Format non valide. Veuillez définir au moins une sous-organisation pour '$" +"{topic}' ou supprimer le ':'" msgid "" "Invalid format. Only organisations and sub-organisations are allowed - no " @@ -3095,9 +3109,9 @@ msgid "" "Either choose a different date range or give this window a title to " "differenciate it from other windows." msgstr "" -"L'intervalle de dates chevauche une fenêtre de soumission existante " -"(${range}). Choisissez un autre intervalle de dates ou donnez un titre à " -"cette fenêtre pour la différencier des autres fenêtres." +"L'intervalle de dates chevauche une fenêtre de soumission existante ($" +"{range}). Choisissez un autre intervalle de dates ou donnez un titre à cette " +"fenêtre pour la différencier des autres fenêtres." msgid "Short name to identify the text module" msgstr "Nom court pour identifier le module de texte" @@ -4192,8 +4206,8 @@ msgstr "Tout refuser avec message" msgid "Add reservation" msgstr "Ajouter une réservation" -#. Used in sentence: "${event} published." #. +#. Used in sentence: "${event} published." msgid "Event" msgstr "Événement" @@ -4370,8 +4384,8 @@ msgstr "" "supprimez d'abord toutes les sous-pages, ou demandez à un administrateur de " "le faire pour vous." -#. #. Modification for the footer left on all pages +#. msgid "Source" msgstr "Source" @@ -4612,8 +4626,8 @@ msgid "" "filled-out form." msgstr "" "Veuillez vérifier vos données et appuyez sur « Compléter » pour finaliser le " -"processus. S'il y a quelque chose que vous souhaitez modifier, cliquez sur « " -"Modifier » pour retourner sur le formulaire complété." +"processus. S'il y a quelque chose que vous souhaitez modifier, cliquez sur " +"« Modifier » pour retourner sur le formulaire complété." msgid "" "The image shown in the list view is a square. To have your image shown fully " @@ -4674,8 +4688,8 @@ msgstr "" "Nous n'avons pas pu réserver certains créneaux correspondant à vos critères " "pour les dates${and_rooms} suivantes. Veuillez noter que certaines de ces " "dates peuvent encore avoir un ou plusieurs créneaux disponibles qui sont " -"soit plus courts, soit plus longs que la durée que vous avez choisie. " -"${dates}" +"soit plus courts, soit plus longs que la durée que vous avez choisie. $" +"{dates}" #. Used in sentence: "We were unable to reserve some slots matching your #. criteria for the following dates${and_rooms}, please note that some of those @@ -5132,8 +5146,8 @@ msgstr "Bonjour" msgid "Your e-mail address was just used to create an account on ${homepage}." msgstr "" -"Votre adresse e-mail vient d'être utilisée pour créer un compte sur " -"${homepage}." +"Votre adresse e-mail vient d'être utilisée pour créer un compte sur $" +"{homepage}." msgid "To activate your account, click confirm below:" msgstr "Pour activer votre compte, cliquer sur confirmer ci-dessous:" @@ -5216,8 +5230,8 @@ msgstr "Nouveau message du client dans le ticket ${link}" msgid "${author} wrote" msgstr "${author} a écrit" -#. Canonical text for ${link} is: "visit the request page" #. Canonical text for ${link} is: "visit the request status page" +#. Canonical text for ${link} is: "visit the request page" msgid "Please ${link} to reply." msgstr "Veuillez vous rendre sur ${link} pour répondre." @@ -5229,9 +5243,9 @@ msgid "Have a great day!" msgstr "Passez une bonne journée!" msgid "" -"This is the notification for customer messages on reservations for ${request." -"app.org.title}. If you no longer want to receive this e-mail please contact " -"an administrator so they can remove you from the recipients list." +"This is the notification for customer messages on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" "Ceci est une notification concernant les messages clients relatifs aux " "réservations pour ${request.app.org.title}. Si vous ne souhaitez plus " @@ -5359,14 +5373,14 @@ msgid "New note in Ticket ${link}" msgstr "Nouvelle note dans le billet ${link}" msgid "" -"This is the notification for notes on reservations for ${request.app.org." -"title}. If you no longer want to receive this e-mail please contact an " -"administrator so they can remove you from the recipients list." +"This is the notification for notes on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" -"Il s'agit de la notification de notes sur les réservations pour ${request." -"app.org.title}. Si vous ne souhaitez plus recevoir cet e-mail, veuillez " -"contacter un administrateur afin qu'il puisse vous retirer de la liste des " -"destinataires." +"Il s'agit de la notification de notes sur les réservations pour $" +"{request.app.org.title}. Si vous ne souhaitez plus recevoir cet e-mail, " +"veuillez contacter un administrateur afin qu'il puisse vous retirer de la " +"liste des destinataires." msgid "Pending approval" msgstr "En attente d'approbation" @@ -5402,10 +5416,10 @@ msgid "" "you no longer want to receive this e-mail please contact an administrator so " "they can remove you from the recipients list." msgstr "" -"Ceci est la notification pour les réservations pour ${request.app.org." -"title}. Si vous ne souhaitez plus recevoir cet e-mail, veuillez contacter un " -"administrateur afin qu'il puisse vous supprimer de la liste des " -"destinataires." +"Ceci est la notification pour les réservations pour $" +"{request.app.org.title}. Si vous ne souhaitez plus recevoir cet e-mail, " +"veuillez contacter un administrateur afin qu'il puisse vous supprimer de la " +"liste des destinataires." msgid "An administrator just created a new account on ${org} for you." msgstr "" @@ -5503,8 +5517,8 @@ msgid "" "you no longer wish to receive these notifications, please contact an " "administrator so they can remove you from the recipients list." msgstr "" -"Il s'agit d'une notification concernant les réservations rejetées pour " -"${organisation}. Si vous ne souhaitez plus recevoir ces notifications, " +"Il s'agit d'une notification concernant les réservations rejetées pour $" +"{organisation}. Si vous ne souhaitez plus recevoir ces notifications, " "veuillez contacter un administrateur afin qu'il vous retire de la liste des " "destinataires." @@ -5828,12 +5842,21 @@ msgstr "" msgid "Request Reference" msgstr "Référence" +msgid "Search people" +msgstr "Rechercher des personnes" + +msgid "Filter after Organisation or Suborganisation" +msgstr "Filtrer par organisation ou sous-organisation" + msgid "Select organisation" msgstr "Sélectionner une organisation" msgid "Select sub organisation" msgstr "Sélectionner une sous-organisation" +msgid "No people added yet." +msgstr "Aucune personne n'a encore été ajoutée." + msgid "No people found for current filter selection." msgstr "Aucune personne n'a été trouvée pour la sélection de filtre actuelle." @@ -6619,11 +6642,11 @@ msgstr "" #, python-format msgid "" -"Cannot convert field \"${field}\" from type \"${old_type}\" to " -"\"${new_type}\"." +"Cannot convert field \"${field}\" from type \"${old_type}\" to \"${new_type}" +"\"." msgstr "" -"Impossible de convertir le champ \"${field}\" du type \"${old_type}\" en " -"\"${new_type}\"." +"Impossible de convertir le champ \"${field}\" du type \"${old_type}\" en \"$" +"{new_type}\"." msgid "The directory was deleted" msgstr "Le dossier a été supprimé" @@ -6726,8 +6749,8 @@ msgstr "Nouveau destinataire" #, python-format msgid "" -"Registration for notifications on new entries in the directory " -"\"${directory}\"" +"Registration for notifications on new entries in the directory \"${directory}" +"\"" msgstr "" "Inscription pour les notifications sur les nouvelles entrées dans le dossier " "\"${directory}\"" @@ -7104,8 +7127,8 @@ msgid "" "you one already. Your subscribed categories are ${subscribed}." msgstr "" "C'est fait! Nous avons envoyé un lien de confirmation vers ${address}, si " -"nous ne vous en avions pas déjà envoyé un. Vos catégories abonnées sont " -"${subscribed}." +"nous ne vous en avions pas déjà envoyé un. Vos catégories abonnées sont $" +"{subscribed}." # python-format #, python-format @@ -7994,8 +8017,8 @@ msgstr "Une erreur s'est produite lors du téléchargement sur Gever." #, python-format msgid "" -"Encountered an error while uploading to Gever. Response status code is " -"${status}." +"Encountered an error while uploading to Gever. Response status code is $" +"{status}." msgstr "" "Une erreur s'est produite lors du téléchargement sur Gever. Le code d'état " "de la réponse est ${status}." diff --git a/src/onegov/org/locale/it_CH/LC_MESSAGES/onegov.org.po b/src/onegov/org/locale/it_CH/LC_MESSAGES/onegov.org.po index f8af31afd9..3d70d480d1 100644 --- a/src/onegov/org/locale/it_CH/LC_MESSAGES/onegov.org.po +++ b/src/onegov/org/locale/it_CH/LC_MESSAGES/onegov.org.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2026-06-23 10:57+0200\n" +"POT-Creation-Date: 2026-06-30 13:02+0200\n" "PO-Revision-Date: 2022-03-15 10:52+0100\n" "Last-Translator: \n" "Language-Team: \n" @@ -219,8 +219,8 @@ msgstr "Newsletter giornaliera ${time}" msgid "${org} OneGov Cloud Status" msgstr "${org} OneGov Cloud Status" -#. #. get the resource titles and ids +#. msgid "General" msgstr "Generale" @@ -1243,22 +1243,22 @@ msgstr "" #, python-format msgid "" -"The limit cannot be lower than the already confirmed number of attendees " -"(${claimed_spots})" +"The limit cannot be lower than the already confirmed number of attendees ($" +"{claimed_spots})" msgstr "" -"Il limite non può essere inferiore numero di partecipanti già confermato " -"(${claimed_spots})" +"Il limite non può essere inferiore numero di partecipanti già confermato ($" +"{claimed_spots})" #, python-format msgid "" -"The limit cannot be lower than the already confirmed number attendees " -"(${claimed_spots}) and the number of pending requests (${pending_requests}). " +"The limit cannot be lower than the already confirmed number attendees ($" +"{claimed_spots}) and the number of pending requests (${pending_requests}). " "Either enable the waiting list, process the pending requests or increase the " "limit. " msgstr "" "Il limite non può essere inferiore al numero di partecipanti già confermato " -"(${claimed_spots}) e al numero di richieste in sospeso " -"(${pending_requests}). Abilita la lista d'attesa, elabora le richieste in " +"(${claimed_spots}) e al numero di richieste in sospeso ($" +"{pending_requests}). Abilita la lista d'attesa, elabora le richieste in " "sospeso o aumenta il limite. " msgid "The end date must be later than the start date" @@ -2281,6 +2281,19 @@ msgstr "" msgid "Open files in separate window" msgstr "Apri i file in una finestra separata" +msgid "Short links" +msgstr "Link abbreviato" + +#, python-format +msgid "" +"Malformed short link on line ${number}. Each line needs to be a pair of " +"name: path where path is either a valid path starting with a backslash, or a " +"complete URL with schema and host." +msgstr "" +"Link abbreviato non valido alla riga ${number}. Ogni riga deve essere " +"costituita da una coppia nome:percorso, dove percorso è un percorso valido " +"che inizia con una barra rovesciata oppure un URL completo con schema e host." + msgid "Announcement" msgstr "Annuncio" @@ -4187,8 +4200,8 @@ msgstr "Rifiuta tutto con messaggio" msgid "Add reservation" msgstr "Aggiungi prenotazione" -#. Used in sentence: "${event} published." #. +#. Used in sentence: "${event} published." msgid "Event" msgstr "Evento" @@ -4361,8 +4374,8 @@ msgstr "" "eliminare le pagine con sottopagine. Per eliminare questa pagina, elimina " "prima tutte le sottopagine o chiedi a un amministratore di farlo per te." -#. #. Modification for the footer left on all pages +#. msgid "Source" msgstr "Fonte" @@ -5115,8 +5128,8 @@ msgstr "Ciao!" msgid "Your e-mail address was just used to create an account on ${homepage}." msgstr "" -"Il tuo indirizzo e-mail è stato appena utilizzato per creare un account su " -"${homepage}." +"Il tuo indirizzo e-mail è stato appena utilizzato per creare un account su $" +"{homepage}." msgid "To activate your account, click confirm below:" msgstr "Per attivare il tuo account, fai clic su conferma qui di seguito:" @@ -5196,8 +5209,8 @@ msgstr "Nuovo messaggio del cliente nel ticket ${link}" msgid "${author} wrote" msgstr "${author} ha scritto" -#. Canonical text for ${link} is: "visit the request page" #. Canonical text for ${link} is: "visit the request status page" +#. Canonical text for ${link} is: "visit the request page" msgid "Please ${link} to reply." msgstr "Per favore ${link} per rispondere." @@ -5209,9 +5222,9 @@ msgid "Have a great day!" msgstr "Ti auguro una buona giornata!" msgid "" -"This is the notification for customer messages on reservations for ${request." -"app.org.title}. If you no longer want to receive this e-mail please contact " -"an administrator so they can remove you from the recipients list." +"This is the notification for customer messages on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" "Questa è la notifica relativa ai messaggi dei clienti sulle prenotazioni per " "${request.app.org.title}. Se non desideri più ricevere questa e-mail, " @@ -5340,13 +5353,13 @@ msgid "New note in Ticket ${link}" msgstr "Nuova nota nel ticket ${link}" msgid "" -"This is the notification for notes on reservations for ${request.app.org." -"title}. If you no longer want to receive this e-mail please contact an " -"administrator so they can remove you from the recipients list." +"This is the notification for notes on reservations for $" +"{request.app.org.title}. If you no longer want to receive this e-mail please " +"contact an administrator so they can remove you from the recipients list." msgstr "" -"Questa è la notifica per le note sulle prenotazioni per ${request.app.org." -"title}. Se non si desidera più ricevere questo messaggio di posta " -"elettronica si prega di contattare un amministratore in modo che possa " +"Questa è la notifica per le note sulle prenotazioni per $" +"{request.app.org.title}. Se non si desidera più ricevere questo messaggio di " +"posta elettronica si prega di contattare un amministratore in modo che possa " "rimuovervi dall'elenco dei destinatari lista" msgid "Pending approval" @@ -5803,12 +5816,21 @@ msgstr "" msgid "Request Reference" msgstr "Riferimento della richiesta" +msgid "Search people" +msgstr "Cerca persone" + +msgid "Filter after Organisation or Suborganisation" +msgstr "Filtra per organizzazione o sotto-organizzazione" + msgid "Select organisation" msgstr "Selezionare l'organizzazione" msgid "Select sub organisation" msgstr "Selezionare la sotto-organizzazione" +msgid "No people added yet." +msgstr "Non è stato aggiunto nessuno." + msgid "No people found for current filter selection." msgstr "Non sono state trovate persone per la selezione del filtro corrente." @@ -5913,8 +5935,8 @@ msgstr "ore" #, python-format msgid "Reservations can only be made at most ${n} ${unit} in advance." msgstr "" -"Le prenotazioni possono essere effettuate solo con un anticipo massimo di " -"${n} ${unit}." +"Le prenotazioni possono essere effettuate solo con un anticipo massimo di $" +"{n} ${unit}." msgid "" "Reservations must be made at least ${n1} ${unit1} and at most ${n2} ${unit2} " @@ -6380,8 +6402,8 @@ msgid "" "Availability period updated. ${deleted} allocations removed, ${updated} " "allocations adjusted and ${created} new allocations created." msgstr "" -"Periodo di disponibilità aggiornato. ${deleted} allocazioni rimosse, " -"${updated} allocazioni modificate e ${created} nuove allocazioni create." +"Periodo di disponibilità aggiornato. ${deleted} allocazioni rimosse, $" +"{updated} allocazioni modificate e ${created} nuove allocazioni create." msgid "Availability period not found" msgstr "Periodo di disponibilità non trovato" @@ -6476,8 +6498,8 @@ msgid "" "A password reset link has been sent to ${email}, provided an active account " "exists for this email address." msgstr "" -"Il collegamento per reimpostare la password è stato inviato all'indirizzo " -"${email} (se si tratta di un account attivo esistente)." +"Il collegamento per reimpostare la password è stato inviato all'indirizzo $" +"{email} (se si tratta di un account attivo esistente)." msgid "Password changed." msgstr "Password modificata." @@ -6585,11 +6607,11 @@ msgstr "" #, python-format msgid "" -"Cannot convert field \"${field}\" from type \"${old_type}\" to " -"\"${new_type}\"." +"Cannot convert field \"${field}\" from type \"${old_type}\" to \"${new_type}" +"\"." msgstr "" -"Impossibile convertire il campo \"${field}\" dal tipo \"${old_type}\" a " -"\"${new_type}\"." +"Impossibile convertire il campo \"${field}\" dal tipo \"${old_type}\" a \"$" +"{new_type}\"." msgid "The directory was deleted" msgstr "La cartella è stata cancellata" @@ -6688,11 +6710,11 @@ msgstr "Nuovo destinatario" #, python-format msgid "" -"Registration for notifications on new entries in the directory " -"\"${directory}\"" +"Registration for notifications on new entries in the directory \"${directory}" +"\"" msgstr "" -"Registrazione per le notifiche sui nuovi elementi nella cartella " -"\"${directory}\"" +"Registrazione per le notifiche sui nuovi elementi nella cartella \"$" +"{directory}\"" #, python-format msgid "" @@ -7421,8 +7443,8 @@ msgid "" "Failed to create visits using the dormakaba API for site ID ${site_id} " "please make sure your credentials are still valid." msgstr "" -"Impossibile creare visite con l'API di dormakaba per l'ID del sito " -"${site_id}. Assicurarsi che le credenziali siano ancora valide." +"Impossibile creare visite con l'API di dormakaba per l'ID del sito $" +"{site_id}. Assicurarsi che le credenziali siano ancora valide." msgid "Accepted" msgstr "Accettato" @@ -7658,8 +7680,8 @@ msgstr "Totale di ${number} collegamenti trovati." msgid "" "Migrates links from the given domain to the current domain \"${domain}\"." msgstr "" -"Migra i collegamenti dal dominio specificato al dominio corrente " -"\"${domain}\"." +"Migra i collegamenti dal dominio specificato al dominio corrente \"${domain}" +"\"." msgid "OneGov API" msgstr "API OneGov" @@ -7953,8 +7975,8 @@ msgstr "Si è verificato un errore durante il caricamento su Gever." #, python-format msgid "" -"Encountered an error while uploading to Gever. Response status code is " -"${status}." +"Encountered an error while uploading to Gever. Response status code is $" +"{status}." msgstr "" "Si è verificato un errore durante il caricamento su Gever.Il codice di stato " "della risposta è ${status}." diff --git a/src/onegov/org/models/organisation.py b/src/onegov/org/models/organisation.py index 7ba6347315..70fc33d649 100644 --- a/src/onegov/org/models/organisation.py +++ b/src/onegov/org/models/organisation.py @@ -121,6 +121,7 @@ class Organisation(Base, TimestampMixin): locales: dict_property[str | None] = meta_property() redirect_homepage_to: dict_property[str | None] = meta_property() redirect_path: dict_property[str | None] = meta_property() + short_links: dict_property[str | None] = meta_property() hidden_people_fields: dict_property[list[str]] = meta_property( default=lambda: ['external_user_id'] ) @@ -419,6 +420,24 @@ def _opening_hours_setter(self, value: str | None) -> None: def opening_hours_html(self) -> Markup: return paragraphify(linkify(self.opening_hours)) + @cached_property + def short_links_dict(self) -> dict[str, str]: + return { + parts[0].strip(): parts[1].strip() + for line in (self.short_links or '').splitlines() + if len(parts := line.split(':', 1)) == 2 + } + + @short_links.inplace.setter + def _short_links_setter(self, value: str | None) -> None: + self.meta['short_links'] = value + # update cache + self.__dict__['short_links_dict'] = { + parts[0].strip(): parts[1].strip() + for line in (self.short_links or '').splitlines() + if len(parts := line.split(':', 1)) == 2 + } + @property def title(self) -> str: return self.name.replace('|', ' ') diff --git a/src/onegov/org/path.py b/src/onegov/org/path.py index 85198b444f..cb97b5e4bd 100644 --- a/src/onegov/org/path.py +++ b/src/onegov/org/path.py @@ -15,6 +15,7 @@ from onegov.core.converters import json_converter from onegov.core.converters import LiteralConverter from onegov.core.orm.abstract import MoveDirection +from onegov.core.security import Public from onegov.directory import Directory from onegov.directory import DirectoryCollection from onegov.directory import DirectoryEntry @@ -134,6 +135,28 @@ def get_org(app: OrgApp) -> Organisation: return app.org +class ShortLink: + + __slots__ = ('name', 'to') + + def __init__(self, name: str, to: str) -> None: + self.name = name + self.to = to + + +@OrgApp.path(model=ShortLink, path='/@{name}') +def get_short_link(app: OrgApp, name: str) -> ShortLink | None: + redirect_path = app.org.short_links_dict.get(name) + if redirect_path is None: + return None + return ShortLink(name, redirect_path) + + +@OrgApp.view(model=ShortLink, permission=Public) +def view_short_link(self: ShortLink, request: OrgRequest) -> Response: + return request.redirect(self.to) + + @OrgApp.path(model=Auth, path='/auth', converters={'skip': bool}) def get_auth( app: OrgApp, diff --git a/tests/onegov/org/test_views_settings.py b/tests/onegov/org/test_views_settings.py index 525c27d0ec..cb258fd6ee 100644 --- a/tests/onegov/org/test_views_settings.py +++ b/tests/onegov/org/test_views_settings.py @@ -196,3 +196,32 @@ def test_switch_languages(client: Client) -> None: assert 'Tedesco' in page assert 'Allemand' not in page assert 'Deutsch' not in page + + +def test_settings_short_links(client: Client) -> None: + client.login_admin() + + page = client.get('/link-settings') + + # malformed entries trigger validation errors + page.form['short_links'] = 'bogus' + assert 'Ungültiger Kurzlink auf Zeile 1' in page.form.submit() + + page.form['short_links'] = 'valid: /topics/valid\nbogus: google.com' + assert 'Ungültiger Kurzlink auf Zeile 2' in page.form.submit() + + page.form['short_links'] = ( + 'org: /topics/organisation\n' + 'google: https://www.google.com' + ) + page.form.submit().follow() + + # domain relative short links work + page = client.get('/@org') + assert page.headers['Location'] == 'http://localhost/topics/organisation' + # so do full URL short links + page = client.get('/@google') + assert page.headers['Location'] == 'https://www.google.com' + # unknown short links return 404, as they should + page = client.get('/@bogus', expect_errors=True) + assert page.status_code == 404