diff --git a/com/ics_calendar.py b/com/ics_calendar.py index e5324c8a..6cb10d2d 100644 --- a/com/ics_calendar.py +++ b/com/ics_calendar.py @@ -1,9 +1,11 @@ from pathlib import Path -from typing import final from dateutil.relativedelta import relativedelta from django.conf import settings +from django.contrib.sites.models import Site +from django.contrib.syndication.views import add_domain from django.db.models import F, QuerySet +from django.http import HttpRequest from django.urls import reverse from django.utils import timezone from ical.calendar import Calendar @@ -14,7 +16,14 @@ from com.models import NewsDate from core.models import User -@final +def as_absolute_url(url: str, request: HttpRequest | None = None) -> str: + return add_domain( + Site.objects.get_current(request=request), + url, + secure=request.is_secure() if request is not None else settings.HTTPS, + ) + + class IcsCalendar: _CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" _INTERNAL_CALENDAR = _CACHE_FOLDER / "internal.ics" @@ -58,7 +67,9 @@ class IcsCalendar: summary=news_date.news_title, start=news_date.start_date, end=news_date.end_date, - url=reverse("com:news_detail", kwargs={"news_id": news_date.news.id}), + url=as_absolute_url( + reverse("com:news_detail", kwargs={"news_id": news_date.news.id}) + ), ) calendar.events.append(event) diff --git a/com/static/bundled/com/components/ics-calendar-index.ts b/com/static/bundled/com/components/ics-calendar-index.ts index d8fc79d7..09336db5 100644 --- a/com/static/bundled/com/components/ics-calendar-index.ts +++ b/com/static/bundled/com/components/ics-calendar-index.ts @@ -44,7 +44,18 @@ export class IcsCalendar extends inheritHtmlElement("div") { return this.isMobile() ? "listMonth" : "dayGridMonth"; } - currentToolbar() { + currentFooterToolbar() { + if (this.isMobile()) { + return { + start: "", + center: "getCalendarLink", + end: "", + }; + } + return { start: "getCalendarLink", center: "", end: "" }; + } + + currentHeaderToolbar() { if (this.isMobile()) { return { left: "prev,next", @@ -303,14 +314,44 @@ export class IcsCalendar extends inheritHtmlElement("div") { this.calendar = new Calendar(this.node, { plugins: [dayGridPlugin, iCalendarPlugin, listPlugin], locales: [frLocale, enLocale], + customButtons: { + getCalendarLink: { + text: gettext("Copy calendar link"), + click: async (event: Event) => { + const button = event.target as HTMLButtonElement; + button.classList.add("text-copy"); + if (!button.hasAttribute("position")) { + button.setAttribute("tooltip", gettext("Link copied")); + button.setAttribute("position", "top"); + button.setAttribute("no-hover", ""); + } + if (button.classList.contains("text-copied")) { + button.classList.remove("text-copied"); + } + navigator.clipboard.writeText( + new URL( + await makeUrl(calendarCalendarInternal), + window.location.origin, + ).toString(), + ); + setTimeout(() => { + button.classList.remove("text-copied"); + button.classList.add("text-copied"); + button.classList.remove("text-copy"); + }, 1500); + }, + }, + }, height: "auto", locale: this.locale, initialView: this.currentView(), - headerToolbar: this.currentToolbar(), + headerToolbar: this.currentHeaderToolbar(), + footerToolbar: this.currentFooterToolbar(), eventSources: await this.getEventSources(), windowResize: () => { this.calendar.changeView(this.currentView()); - this.calendar.setOption("headerToolbar", this.currentToolbar()); + this.calendar.setOption("headerToolbar", this.currentHeaderToolbar()); + this.calendar.setOption("footerToolbar", this.currentFooterToolbar()); }, eventClick: (event) => { // Avoid our popup to be deleted because we clicked outside of it diff --git a/com/static/com/components/ics-calendar.scss b/com/static/com/components/ics-calendar.scss index 6c86cce0..49713f82 100644 --- a/com/static/com/components/ics-calendar.scss +++ b/com/static/com/components/ics-calendar.scss @@ -98,4 +98,26 @@ ics-calendar { background: white; } } + + .fc .fc-toolbar.fc-footer-toolbar { + margin-bottom: 0.5em; + } + + button.text-copy, + button.text-copy:focus, + button.text-copy:hover { + background-color: #67AE6E !important; + transition: 500ms ease-in; + } + + button.text-copied, + button.text-copied:focus, + button.text-copied:hover { + transition: 500ms ease-out; + } + + button.text-copied[tooltip]::before { + opacity: 0; + transition: opacity 500ms ease-out; + } } \ No newline at end of file diff --git a/core/static/core/style.scss b/core/static/core/style.scss index f064332a..a87ed3a3 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -51,24 +51,55 @@ body { [tooltip]::before { @include shadow; - opacity: 0; z-index: 1; + pointer-events: none; content: attr(tooltip); - background: hsl(219.6, 20.8%, 96%); - color: $black-color; + left: 50%; + transform: translateX(-50%); + background-color: #333; + color: #fff; border: 0.5px solid hsl(0, 0%, 50%); - ; border-radius: 5px; - padding: 5px; - top: 1em; + padding: 5px 10px; position: absolute; - margin-top: 5px; white-space: nowrap; + opacity: 0; transition: opacity 500ms ease-out; + top: 120%; // Put the tooltip under the element } [tooltip]:hover::before { opacity: 1; + transition: opacity 500ms ease-in; +} + +[no-hover][tooltip]::before { + opacity: 1; + transition: opacity 500ms ease-in; +} + +[position="top"][tooltip]::before { + top: initial; + bottom: 120%; +} + +[position="bottom"][tooltip]::before { + top: 120%; + bottom: initial; +} + +[position="left"][tooltip]::before { + top: initial; + bottom: 0%; + left: initial; + right: 65%; +} + +[position="right"][tooltip]::before { + top: initial; + bottom: 0%; + left: 150%; + right: initial; } .ib { diff --git a/locale/fr/LC_MESSAGES/djangojs.po b/locale/fr/LC_MESSAGES/djangojs.po index 7952baa4..3693c144 100644 --- a/locale/fr/LC_MESSAGES/djangojs.po +++ b/locale/fr/LC_MESSAGES/djangojs.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-06 15:47+0200\n" +"POT-Creation-Date: 2025-04-08 11:42+0200\n" "PO-Revision-Date: 2024-09-17 11:54+0200\n" "Last-Translator: Sli \n" "Language-Team: AE info \n" @@ -33,6 +33,14 @@ msgstr "Dépublier" msgid "Delete" msgstr "Supprimer" +#: com/static/bundled/com/components/ics-calendar-index.ts +msgid "Copy calendar link" +msgstr "Copier le lien du calendrier" + +#: com/static/bundled/com/components/ics-calendar-index.ts +msgid "Link copied" +msgstr "Lien copié" + #: com/static/bundled/com/components/moderation-alert-index.ts #, javascript-format msgid "" diff --git a/sith/settings.py b/sith/settings.py index e948e971..7e3ef14f 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -78,10 +78,12 @@ DEBUG = env.bool("SITH_DEBUG", default=False) TESTING = "pytest" in sys.modules INTERNAL_IPS = ["127.0.0.1"] +HTTPS = env.bool("HTTPS", default=True) + # force csrf tokens and cookies to be secure when in https -CSRF_COOKIE_SECURE = env.bool("HTTPS", default=True) +CSRF_COOKIE_SECURE = HTTPS CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[]) -SESSION_COOKIE_SECURE = env.bool("HTTPS", default=True) +SESSION_COOKIE_SECURE = HTTPS X_FRAME_OPTIONS = "SAMEORIGIN" ALLOWED_HOSTS = ["*"]