diff --git a/config.py.example b/config.py.example index ea125480f1c12157f4828c571dc381fed5ac37ab..7dcd0a60639727197f6c2f5bf5b031efb39cb876 100644 --- a/config.py.example +++ b/config.py.example @@ -60,6 +60,11 @@ CALENDAR_URL = "https://user:password@calendar.example.com/dav/" CALENDAR_DEFAULT_DURATION = 3 # default meeting length in hours CALENDAR_MAX_REQUESTS = 10 # number of retries before giving up (some caldav servers like to randomly reply with errors) +CALENDAR_TIMEZONE_MAP = { + "CET": "Europe/Berlin", + "CEST": "Europe/Berlin", +} + SESSION_PROTECTION = "strong" # do not change # authentication diff --git a/models/database.py b/models/database.py index b7b9fa5b990c54fa90292840f9ef3a7485cabfe5..f97fc04f99564cae697003bc4d85b79e83fc4c25 100644 --- a/models/database.py +++ b/models/database.py @@ -9,6 +9,7 @@ from uuid import uuid4 from shared import db, date_filter, date_filter_short, escape_tex, DATE_KEY, START_TIME_KEY, END_TIME_KEY, current_user from utils import random_string, get_etherpad_url, split_terms, check_ip_in_networks from models.errors import DateNotMatchingException +from dateutil import tz import os @@ -331,6 +332,10 @@ class Protocol(DatabaseModel): tops_before.append(top) return tops_before + self.tops + tops_after + def get_timezone_aware_start_date(self): + return datetime.combine(self.date, self.get_time()).replace( + tzinfo=tz.tzlocal()) + @staticmethod def create_new_protocol(protocoltype, date, start_time=None): if start_time is None: diff --git a/server.py b/server.py index 98bd9391c3d1719e6972ce01601464cb3759e789..e5c6bcd4cc63442a5796edff1b0c137116bc6369 100755 --- a/server.py +++ b/server.py @@ -14,9 +14,10 @@ from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.interval import IntervalTrigger import atexit import feedgen.feed +import icalendar from io import StringIO, BytesIO import os -from datetime import datetime, time +from datetime import datetime, time, timedelta import math import mimetypes import subprocess @@ -1352,9 +1353,7 @@ def create_protocols_feed(protocoltype): entry.title(protocol.get_title()) entry.summary(",\n".join(top.name for top in protocol.get_tops())) entry.content(protocol.content_public) - aware_date = datetime.combine(protocol.date, protocoltype.usual_time).replace( - tzinfo=tz.tzlocal()) - entry.published(aware_date) + entry.published(protocol.get_timezone_aware_start_date()) return feed @@ -1381,10 +1380,13 @@ def create_appointments_feed(protocoltype): _external=True), rel="alternate") entry.title(protocol.get_title()) entry.summary("\n".join( - [",\n".join( - "{}: {}".format(meta.name, meta.value) - for meta in protocol.metas - if not meta.internal + [",\n".join([ + "Beginn: {}".format(protocol.get_time()) + ] + [ + "{}: {}".format(meta.name, meta.value) + for meta in protocol.metas + if not meta.internal + ] ), "Tagesordnung:", ",\n".join( @@ -1418,6 +1420,37 @@ def feed_appointments_atom(protocoltype): return Response(create_appointments_feed(protocoltype).atom_str(), mimetype="application/atom+xml") +@app.route("/feed/appointments/ical/<int:protocoltype_id>") +@db_lookup(ProtocolType) +def feed_appointsments_ical(protocoltype): + if not protocoltype.has_public_anonymous_view_right(): + abort(403) + protocols = [protocol + for protocol in protocoltype.protocols + if not protocol.is_done() + ] + calendar = icalendar.Calendar() + calendar["summary"] = protocoltype.short_name + calendar["prodid"] = "Protokollsystem 3" + calendar["version"] = "2.0" + for protocol in protocols: + event = icalendar.Event() + event["uid"] = protocol.id + to_datetime = icalendar.prop.vDatetime + start = protocol.get_timezone_aware_start_date() + event["dtstamp"] = to_datetime(start) + event["dtstart"] = to_datetime(start) + event["dtend"] = to_datetime(start + timedelta(hours=3)) + event["summary"] = protocoltype.short_name + event["description"] = "\n".join(top.name + for top in protocol.get_tops()) + calendar.add_component(event) + content = calendar.to_ical().decode("utf-8") + for key in config.CALENDAR_TIMEZONE_MAP: + content = content.replace("TZID={}:".format(key), + "TZID={}:".format(config.CALENDAR_TIMEZONE_MAP[key])) + return Response(content.encode("utf-8"), mimetype="text/calendar") + @app.route("/like/new") @login_required @@ -1445,6 +1478,7 @@ def new_like(): flash("Like!", "alert-success") return redirect(request.args.get("next") or url_for("index")) + @app.route("/login", methods=["GET", "POST"]) def login(): if "auth" in session and current_user() is not None: