Skip to content

Commit

Permalink
Add ical exports
Browse files Browse the repository at this point in the history
  • Loading branch information
Ash-Crow committed Jul 22, 2024
1 parent fa6a997 commit 0e1341a
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 55 deletions.
Binary file modified blog/locale/fr/LC_MESSAGES/django.mo
Binary file not shown.
7 changes: 3 additions & 4 deletions blog/locale/fr/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-17 17:25+0200\n"
"PO-Revision-Date: 2024-07-11 17:36+0200\n"
"POT-Creation-Date: 2024-07-22 18:54+0200\n"
"PO-Revision-Date: 2024-07-22 18:54+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
Expand Down Expand Up @@ -64,8 +64,7 @@ msgstr "Filtrer par source"

#: blog/models.py:44
msgid "The source is the organization of the post author"
msgstr ""
"La source est l’organisation à laquelle appartient l’auteur de l’article"
msgstr "La source est l’organisation à laquelle appartient l’auteur de l’article"

#: blog/models.py:56
msgid "Show filters"
Expand Down
1 change: 1 addition & 0 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"dashboard",
"wagtail.contrib.forms",
"wagtail.contrib.redirects",
"wagtail.contrib.routable_page",
"wagtail.contrib.settings",
"wagtail.embeds",
"wagtail.sites",
Expand Down
Binary file modified events/locale/fr/LC_MESSAGES/django.mo
Binary file not shown.
91 changes: 63 additions & 28 deletions events/locale/fr/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-17 17:55+0200\n"
"PO-Revision-Date: 2024-07-17 18:02+0200\n"
"POT-Creation-Date: 2024-07-22 17:04+0200\n"
"PO-Revision-Date: 2024-07-22 17:06+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
Expand All @@ -27,95 +27,103 @@ msgstr "Du…"
msgid "To…"
msgstr "Au…"

#: events/models.py:23
#: events/models.py:28
msgid "Events per page"
msgstr "Événements par page"

#: events/models.py:27 events/templates/events/events_index_page.html:80
#: events/models.py:32 events/templates/events/events_index_page.html:80
msgid "Filter by category"
msgstr "Filtrer par catégorie"

#: events/models.py:28 events/templates/events/events_index_page.html:97
#: events/models.py:33 events/templates/events/events_index_page.html:97
msgid "Filter by tag"
msgstr "Filtrer par étiquette"

#: events/models.py:29 events/templates/events/events_index_page.html:114
#: events/models.py:34 events/templates/events/events_index_page.html:114
msgid "Filter by author"
msgstr "Filtrer par auteur"

#: events/models.py:31 events/templates/events/events_index_page.html:129
#: events/models.py:36 events/templates/events/events_index_page.html:129
msgid "Filter by source"
msgstr "Filtrer par source"

#: events/models.py:31
#: events/models.py:36
msgid "The source is the organization of the event author"
msgstr "La source est l’organisation à laquelle appartient l’auteur de l’événement"

#: events/models.py:43
#: events/models.py:48
msgid "Show filters"
msgstr "Afficher les filtres"

#: events/models.py:50
#: events/models.py:55
msgid "Event calendar index"
msgstr "Index de l’agenda"

#: events/models.py:85
#: events/models.py:90
#, python-format
msgid "Events tagged with %(tag)s"
msgstr "Événements avec l’étiquette %(tag)s"

#: events/models.py:99
#: events/models.py:104
#, python-format
msgid "Events in category %(category)s"
msgstr "Événements dans la catégorie %(category)s"

#: events/models.py:110 events/models.py:112 events/models.py:123
#: events/models.py:126
#: events/models.py:115 events/models.py:117 events/models.py:128
#: events/models.py:131
msgid "Events created by"
msgstr "Événements créés par"

#: events/models.py:130
#: events/models.py:135
#, python-format
msgid "Events published in %(year)s"
msgstr "Événements publiés en %(year)s"

#: events/models.py:199
#: events/models.py:235
msgid "Categories"
msgstr "Catégories"

#: events/models.py:202
#: events/models.py:238
msgid "Post date"
msgstr "Date de publication"

#: events/models.py:203
#: events/models.py:239
msgid "Event start date"
msgstr "Date de début de l’événement"

#: events/models.py:204
#: events/models.py:240
msgid "Event end date"
msgstr "Date de fin de l’événement"

#: events/models.py:206
#: events/models.py:242
msgid "Location"
msgstr "Lieu"

#: events/models.py:243
msgid "Registration URL"
msgstr "Lien d’inscription"

#: events/models.py:246
msgid "Author entries can be created in Snippets > Persons"
msgstr "Les auteurs peuvent être créés via Fragments > Personnes"

#: events/models.py:225
msgid "Event date"
msgstr "Date de l’événement"
#: events/models.py:275
msgid "Event date and place"
msgstr "Date et lieu de l’événement"

#: events/models.py:237
#: events/models.py:287
msgid "Scheduled publishing"
msgstr "Publication programmée"

#: events/models.py:245
#: events/models.py:295
msgid "Tags and Categories"
msgstr "Étiquettes et Catégories"

#: events/models.py:253
#: events/models.py:351
msgid "Event page"
msgstr "Page d’événement"

#: events/models.py:261
#: events/models.py:359
msgid "Category"
msgstr "Catégorie"

Expand All @@ -127,11 +135,38 @@ msgstr "Aucun événement trouvé."
msgid "Posted by:"
msgstr "Écrit par :"

#: events/templates/events/event_entry_page.html:111
msgid "Date:"
msgstr "Date :"

#: events/templates/events/event_entry_page.html:114
msgid "Location:"
msgstr "Lieu :"

#: events/templates/events/event_entry_page.html:118
msgid "Registration link:"
msgstr "Lien d’inscription :"

#: events/templates/events/event_entry_page.html:138
#: events/templates/events/events_index_page.html:146
msgid "Add to your calendar (iCal)"
msgstr "Ajouter à votre calendrier (iCal)"

#: events/templates/events/events_index_page.html:70
#: events/templates/events/events_index_page.html:72
msgid "Filters"
msgstr "Filtres"

#: events/templates/events/events_index_page.html:72
msgid "Filters and export"
msgstr "Filtres et export"

#: events/templates/events/events_index_page.html:74
msgid "Filter by date"
msgstr "Filtrer par date"

#: events/templates/events/events_index_page.html:142
msgid "Export"
msgstr "Export"

#~ msgid "Event date"
#~ msgstr "Date de l’événement"
100 changes: 96 additions & 4 deletions events/models.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import get_language, gettext_lazy as _
from icalendar import Calendar, Event, vText
from modelcluster.fields import ParentalKey, ParentalManyToManyField
from modelcluster.tags import ClusterTaggableManager
from taggit.models import TaggedItemBase
from wagtail.admin.panels import FieldPanel, FieldRowPanel, MultiFieldPanel
from wagtail.contrib.routable_page.models import RoutablePageMixin, path
from wagtail.models.i18n import Locale
from wagtail.search import index

from blog.models import Category, Organization, Person
from content_manager.abstract import SitesFacilesBasePage
from content_manager.models import Tag
from content_manager.models import CmsDsfrConfig, Tag
from events.forms import EventSearchForm


class EventsIndexPage(SitesFacilesBasePage):
class EventsIndexPage(RoutablePageMixin, SitesFacilesBasePage):
posts_per_page = models.PositiveSmallIntegerField(
default=10,
validators=[MaxValueValidator(100), MinValueValidator(1)],
Expand Down Expand Up @@ -57,7 +62,7 @@ def posts(self):
EventEntryPage.objects.descendant_of(self)
.live()
.filter(event_date_start__date__gte=today)
.order_by("-event_date_start")
.order_by("event_date_start")
.select_related("owner")
.prefetch_related("tags", "event_categories", "date__year")
)
Expand Down Expand Up @@ -188,8 +193,39 @@ def get_tags(self) -> models.QuerySet:
ids = self.posts.specific().values_list("tags", flat=True)
return Tag.objects.filter(id__in=ids).order_by("name")

@path("ical/")
def ical_view(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Render the full calendar as an iCal file
"""
cal = Calendar()

class EventEntryPage(SitesFacilesBasePage):
cms_settings = CmsDsfrConfig.for_request(request=request)
site_name = cms_settings.site_title
language_code = get_language()

# See https://www.kanzaki.com/docs/ical/prodid.html
prodid = [
"-",
f"{site_name}{self.title}",
"Sites faciles",
language_code.upper(),
]

cal.add("prodid", "//".join(prodid))
cal.add("version", "2.0")

dtstamp = timezone.now()
for entry in self.posts:
event = entry.ical_event(dtstamp)
cal.add_component(event)

response = HttpResponse(cal.to_ical(), content_type="text/calendar")
response["Content-Disposition"] = f'attachment; filename="{slugify(site_name)}.ics"'
return response


class EventEntryPage(RoutablePageMixin, SitesFacilesBasePage):
tags = ClusterTaggableManager(through="TagEventEntryPage", blank=True)

event_categories = ParentalManyToManyField(
Expand All @@ -213,6 +249,14 @@ class EventEntryPage(SitesFacilesBasePage):
parent_page_types = ["events.EventsIndexPage"]
subpage_types = []

search_fields = SitesFacilesBasePage.search_fields + [
index.SearchField("event_categories"),
index.SearchField("event_date_start"),
index.SearchField("event_date_end"),
index.SearchField("location"),
index.SearchField("registration_url"),
]

settings_panels = SitesFacilesBasePage.settings_panels + [
FieldPanel("authors"),
FieldPanel("date"),
Expand Down Expand Up @@ -255,6 +299,54 @@ class EventEntryPage(SitesFacilesBasePage):
def get_absolute_url(self):
return self.url

def ical_event(self, dtstamp=None):
"""
Formats the event as an iCalendar event
"""
if not dtstamp:
dtstamp = timezone.now()

event = Event()
event.add("summary", self.title)
event.add("dtstart", self.event_date_start)
event.add("dtend", self.event_date_end)
event.add("dtstamp", dtstamp)
event.add("uid", str(self.pk))
event["location"] = vText(self.location)

return event

@path("ical/")
def ical_view(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""
Render the event as an iCal file
"""
cal = Calendar()

cms_settings = CmsDsfrConfig.for_request(request=request)
site_name = cms_settings.site_title
language_code = get_language()

title = f"{site_name}{self.title}"

# See https://www.kanzaki.com/docs/ical/prodid.html for format
prodid = [
"-",
title,
"Sites faciles",
language_code.upper(),
]

cal.add("prodid", "//".join(prodid))
cal.add("version", "2.0")

event = self.ical_event()
cal.add_component(event)

response = HttpResponse(cal.to_ical(), content_type="text/calendar")
response["Content-Disposition"] = f'attachment; filename="{slugify(title)}.ics"'
return response

class Meta:
verbose_name = _("Event page")

Expand Down
Loading

0 comments on commit 0e1341a

Please sign in to comment.