diff --git a/src/onegov/org/assets/js/many.jsx b/src/onegov/org/assets/js/many.jsx index 36b0469388..f682cc8cae 100644 --- a/src/onegov/org/assets/js/many.jsx +++ b/src/onegov/org/assets/js/many.jsx @@ -453,11 +453,17 @@ function extractType(target) { } jQuery.fn.many = function() { + // Don't forget to change this file in town6 as well. return this.each(function() { var target = $(this); var type = extractType(target); - var data = JSON.parse(target.val()); + try { + var data = JSON.parse(target.val()); + } catch (e) { + console.log('Failed to parse JSON:', e); + return; // Skip this iteration if JSON is invalid + } var label = target.closest('label'); var errors = label.siblings('.error'); @@ -470,7 +476,9 @@ jQuery.fn.many = function() { label.hide(); errors.hide(); - var el = $('
'); + // Create a unique wrapper for this instance + let wrapperId = 'many-wrapper-' + Math.random().toString(36).substr(2, 9); + let el = $('
'); el.appendTo(label.parent()); // transfer javascript dependencies to the wrapper 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 a45c9c5d86..c57ba3fef8 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 @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE 1.0\n" -"POT-Creation-Date: 2025-01-30 15:51+0100\n" +"POT-Creation-Date: 2025-01-28 17:16+0100\n" "PO-Revision-Date: 2022-03-15 10:21+0100\n" "Last-Translator: Marc Sommerhalder \n" "Language-Team: German\n" @@ -2808,6 +2808,24 @@ msgstr "" msgid "Sidebar links" msgstr "Links in der Sidebar" +msgid "Contact link" +msgstr "Kontaktlink" + +msgid "Sidebar contact" +msgstr "Kontakt in der Sidebar" + +msgid "Please provide a URL if contact link text is set" +msgstr "Bitte geben Sie eine URL an, wenn der Kontaktlink-Text gesetzt ist" + +msgid "Please provide link text if contact URL is set" +msgstr "Bitte geben Sie einen Link-Text an, wenn die Kontakt-URL gesetzt ist" + +msgid "Contact Text" +msgstr "Kontakttext" + +msgid "Contact URL" +msgstr "Kontakt-URL" + msgid "Delete content when expired" msgstr "Inhalt löschen nachdem abgelaufen" @@ -6296,25 +6314,3 @@ msgstr "Ein Konto wurde für Sie erstellt" msgid "The user was created successfully" msgstr "Der Benutzer wurde erfolgreich erstellt" - -#~ msgid "Photo album. Will be shown at the end of content." -#~ msgstr "Fotoalbum. Wird am Ende des Inhalts angezeigt." - -#~ msgid "Describes in detail how this form is to be used" -#~ msgstr "Beschreibt detailliert wie dieses Formular benutzt werden soll" - -#~ msgid "Include logo in newsletter" -#~ msgstr "Logo im Newsletter anzeigen" - -#~ msgid "Newsletter Subscription" -#~ msgstr "Newsletter-Abonnement" - -#~ msgid "" -#~ "Thank you for filling out this survey. If there's anything you'd like to " -#~ "change, click on \"Edit\" below." -#~ msgstr "" -#~ "Vielen Dank für Ihre Teilnahme an dieser Umfrage. Falls Sie noch etwas " -#~ "ändern möchten, klicken Sie auf den \"Bearbeiten\"-Button unten." - -#~ msgid "Rule updated" -#~ msgstr "Regel aktualisiert" 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 feaac66372..012380548c 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 @@ -2808,6 +2808,24 @@ msgstr "" msgid "Sidebar links" msgstr "Liens dans la barre latérale" +msgid "Contact link" +msgstr "Lien de contact" + +msgid "Sidebar contact" +msgstr "Contact dans la barre latérale" + +msgid "Please provide a URL if contact link text is set" +msgstr "Veuillez fournir une URL si le texte du lien de contact est défini" + +msgid "Please provide link text if contact URL is set" +msgstr "Veuillez indiquer le texte du lien si l'URL de contact est définie" + +msgid "Contact Text" +msgstr "Lien de contact" + +msgid "Contact URL" +msgstr "URL de contact" + msgid "Delete content when expired" msgstr "Supprimer le contenu lorsqu'il est expiré" @@ -6312,6 +6330,3 @@ msgstr "Un compte a été créé pour vous" msgid "The user was created successfully" msgstr "L'utilisateur a bien été créé" - -#~ msgid "Photo album. Will be shown at the end of content." -#~ msgstr "Album photo. Sera affiché à la fin du contenu." 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 33ecfaedee..ee27ac48a4 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 @@ -2814,6 +2814,24 @@ msgstr "" msgid "Sidebar links" msgstr "Collegamenti della barra laterale" +msgid "Contact link" +msgstr "Link di contatto" + +msgid "Sidebar contact" +msgstr "Contatto laterale" + +msgid "Please provide a URL if contact link text is set" +msgstr "Si prega di fornire un URL se il testo del link di contatto è impostato" + +msgid "Please provide link text if contact URL is set" +msgstr "Fornire il testo del link se l'URL di contatto è impostato" + +msgid "Contact Text" +msgstr "Testo di contatto" + +msgid "Contact URL" +msgstr "URL di contatto" + msgid "Delete content when expired" msgstr "Eliminare il contenuto quando è scaduto" @@ -6300,6 +6318,3 @@ msgstr "È stato creato un account per te" msgid "The user was created successfully" msgstr "Utente creato correttamente" - -#~ msgid "Photo album. Will be shown at the end of content." -#~ msgstr "Album fotografico. Sarà mostrato alla fine del contenuto." diff --git a/src/onegov/org/models/extensions.py b/src/onegov/org/models/extensions.py index 0127273fd1..ae66bc988c 100644 --- a/src/onegov/org/models/extensions.py +++ b/src/onegov/org/models/extensions.py @@ -1087,6 +1087,115 @@ def links_to_json( return SidebarLinksForm +class SidebarContactLinkExtension(ContentExtension): + """ Like SidebarLinkExtension but the links are shown below the contact + field. We knowingly duplicate some code here . + """ + + sidepanel_contact = content_property() + + def extend_form( + self, + form_class: type[FormT], + request: OrgRequest + ) -> type[FormT]: + + class SidebarContactLinkForm(form_class): # type:ignore + + sidepanel_contact = StringField( + label=_('Contact link'), + fieldset=_('Sidebar contact'), + render_kw={'class_': 'many many-links'} + ) + + if TYPE_CHECKING: + contact_errors: dict[int, str] + else: + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.contact_errors = {} + + def on_request(self) -> None: + if not self.sidepanel_contact.data: + self.sidepanel_contact.data = self.links_to_json(None) + + def process_obj(self, obj: SidebarContactLinkExtension) -> None: + super().process_obj(obj) + if not obj.sidepanel_contact: + self.sidepanel_contact.data = self.links_to_json(None) + else: + self.sidepanel_contact.data = self.links_to_json( + obj.sidepanel_contact + ) + + def populate_obj( + self, + obj: SidebarContactLinkExtension, + *args: Any, **kwargs: Any + ) -> None: + super().populate_obj(obj, *args, **kwargs) + obj.sidepanel_contact = self.json_to_links( + self.sidepanel_contact.data) or None + + def validate_sidepanel_contact(self, field: StringField) -> None: + for text, link in self.json_to_links( + self.sidepanel_contact.data + ): + if text and not link: + raise ValidationError( + _('Please provide a URL if contact link text is ' + 'set')) + if link and not text: + raise ValidationError( + _('Please provide link text if contact URL is ' + 'set')) + if link and not re.match(r'^(http://|https://|/)', + link): + raise ValidationError( + _('Your URLs must start with http://,' + ' https:// or /' + ' (for internal links)') + ) + + def json_to_links( + self, + text: str | None = None + ) -> list[tuple[str | None, str | None]]: + + if not text: + return [] + + return [ + (value['text'], link) + for value in json.loads(text).get('values', []) + if (link := value['link']) or value['text'] + ] + + def links_to_json( + self, + links: Sequence[tuple[str | None, str | None]] | None + ) -> str: + contact_links = links or [] + + return json.dumps({ + 'labels': { + 'text': self.request.translate(_('Contact Text')), + 'link': self.request.translate(_('Contact URL')), + 'add': self.request.translate(_('Add')), + 'remove': self.request.translate(_('Remove')), + }, + 'values': [ + { + 'text': l[0], + 'link': l[1], + 'error': self.contact_errors.get(ix, '') + } for ix, l in enumerate(contact_links) + ] + }) + + return SidebarContactLinkForm + + class DeletableContentExtension(ContentExtension): """ Extends any class that has a meta dictionary field with the ability to mark the content as deletable after reaching the end date. A cronjob will diff --git a/src/onegov/org/models/page.py b/src/onegov/org/models/page.py index adfa5f646b..4c04642f21 100644 --- a/src/onegov/org/models/page.py +++ b/src/onegov/org/models/page.py @@ -12,7 +12,7 @@ ContactExtension, ContactHiddenOnPageExtension, PeopleShownOnMainPageExtension, ImageExtension, NewsletterExtension, PublicationExtension, DeletableContentExtension, - InlinePhotoAlbumExtension + InlinePhotoAlbumExtension, SidebarContactLinkExtension ) from onegov.org.models.extensions import AccessExtension from onegov.org.models.extensions import CoordinatesExtension @@ -44,7 +44,7 @@ class Topic(Page, TraitInfo, SearchableContent, AccessExtension, PeopleShownOnMainPageExtension, PersonLinkExtension, CoordinatesExtension, ImageExtension, GeneralFileLinkExtension, SidebarLinksExtension, - InlinePhotoAlbumExtension): + SidebarContactLinkExtension, InlinePhotoAlbumExtension): __mapper_args__ = {'polymorphic_identity': 'topic'} diff --git a/src/onegov/org/theme/styles/org.scss b/src/onegov/org/theme/styles/org.scss index 5c9aaeb2ce..dad027b0a0 100644 --- a/src/onegov/org/theme/styles/org.scss +++ b/src/onegov/org/theme/styles/org.scss @@ -6777,3 +6777,5 @@ a#results { list-style: none; } } + + diff --git a/src/onegov/town6/assets/js/many.jsx b/src/onegov/town6/assets/js/many.jsx index 96b47644f2..a8fb49b78a 100644 --- a/src/onegov/town6/assets/js/many.jsx +++ b/src/onegov/town6/assets/js/many.jsx @@ -28,6 +28,7 @@ var ManyFields = React.createClass({ onChange={this.props.onChange} /> } + { this.props.type === "opening-hours" && '); + // Create a unique wrapper for this instance + let wrapperId = 'many-wrapper-' + Math.random().toString(36).substr(2, 9); + let el = $('
'); el.appendTo(label.parent()); // transfer javascript dependencies to the wrapper @@ -675,5 +685,6 @@ jQuery.fn.many = function() { }); }; + // since we intercept the dependency setup we need to run before document.ready $('.many').many(); diff --git a/src/onegov/town6/templates/macros.pt b/src/onegov/town6/templates/macros.pt index c4fc038477..09b3732186 100644 --- a/src/onegov/town6/templates/macros.pt +++ b/src/onegov/town6/templates/macros.pt @@ -711,13 +711,20 @@ -