...
 
Commits (12)
......@@ -82,7 +82,8 @@ TEMPLATES = [
'OPTIONS': {
'builtins': [
'grouprise.core.templatetags.defaultfilters',
'grouprise.core.templatetags.defaulttags'
'grouprise.core.templatetags.defaulttags',
'rules.templatetags.rules',
],
'context_processors': [
# django core
......
{% extends 'stadt/stadt.html' %}
{% block title %}{{ association.container }} - {{ block.super }}{% endblock %}
{% block menu %}{% menu association.entity %}{% endblock %}
{% block breadcrumb %}{% breadcrumb association.entity association.container %}{% endblock %}
{% block sidebar %}
......
......@@ -2,6 +2,8 @@
{% block title %}Beitrag erstellen - {{ block.super }}{% endblock %}
{% block menu %}{% menu view.entity %}{% endblock %}
{% block breadcrumb %}
{% if view.entity.is_group %}
{% breadcrumb view.entity 'Beitrag erstellen' %}
......
......@@ -2,6 +2,8 @@
{% block title %}Beitrag bearbeiten - {{ block.super }}{% endblock %}
{% block menu %}{% menu view.entity %}{% endblock %}
{% block breadcrumb %}
{% breadcrumb association.entity association 'Beitrag bearbeiten' %}
{% endblock %}
......
......@@ -166,8 +166,6 @@ class Update(PermissionMixin, generic.UpdateView):
name = 'galleries/update.html'
elif self.object.container.is_file:
name = 'files/update.html'
elif self.object.container.is_event:
name = 'events/update.html'
else:
name = 'articles/update.html'
name = 'content/update.html'
return [name]
import grouprise.features
from grouprise.features.content.forms import Create as ContentCreateForm
class Create(grouprise.features.content.forms.Create):
class EventCreateForm(ContentCreateForm):
""" model form for creating event content
Sends post_create signal after successful creation.
"""
def save(self, commit=True):
super().save(commit)
instance = super().save(commit)
if commit:
self.send_post_create()
return self.instance
return instance
import icalendar
from django.contrib import auth
from django.core.management.base import BaseCommand
import django.contrib.sites.models
from content.models import Event
class Command(BaseCommand):
help = "Export von Kalender-Daten"
def add_arguments(self, parser):
parser.add_argument('--username')
def handle(self, username=None, **kwargs):
if username:
user = auth.get_user_model().objects.get(username=username)
events = Event.objects.permitted(user)
else:
events = Event.objects.public()
calendar = icalendar.Calendar()
uid_format = "{}@" + django.contrib.sites.models.Site.objects.get_current().domain
for event in events:
cal_event = icalendar.Event()
# jede UID sollte global eindeutig sein - also inkl. Domain
cal_event.add("uid", uid_format.format(event.id))
cal_event.add("dtstart", event.time)
cal_event.add("summary", event.title)
cal_event.add("description", event.text)
cal_event.add("location", event.place)
calendar.add_component(cal_event)
print(calendar.to_ical().decode("utf-8"))
import rules
from rules import add_perm, always_allow
rules.add_perm(
'events.view_day',
rules.always_allow)
rules.add_perm(
'events.view_list',
rules.always_allow)
add_perm('events.view_day', always_allow)
add_perm('events.view_list', always_allow)
{% load events %}
<div class="box box-striped">
<div class="box-inner">
<header class="box-header">
......@@ -9,6 +7,7 @@
</h2>
</header>
<div class="box-content" {% if component_id %}data-component="calendar" id="{{ component_id }}"{% endif %}>
{% load events %}
{% calendar associations size='preview' %}
{% if upcoming %}
......
{% extends 'content/create.html' %}
{% block menu %}{% menu view.entity %}{% endblock %}
{% extends 'content/detail.html' %}
{% block menu %}{% menu association.entity %}{% endblock %}
{% block content_type %}
<i class="sg sg-content-type-event" data-day="{{ association.container.time|date:'d' }}"></i>
<span>Veranstaltung</span>
......
{% extends 'stadt/stadt.html' %}
{% load memberships %}
{% block sidebar %}{% endblock %}
{% block title %}Kalenderexport - {{ block.super }}{% endblock %}
{# {% block menu %}{% menu association.entity %}{% endblock %} #}
{# {% block breadcrumb %}{% breadcrumb association.entity association.container %}{% endblock %} #}
{% block sidebar %}
{% comment %}
{% if association.entity.is_group %}
{% include 'groups/_sidebar.html' with group=association.entity %}
{% else %}
{% if association.entity.public %}
{% include 'gestalten/_sidebar.html' with gestalt=association.entity %}
{% endif %}
{% endif %}
{% endcomment %}
{% endblock %}
{% block heading_title %}
{% include 'core/_heading.html' with title='Kalenderexport' icon='sg-calendar' %}
{% endblock %}
{% block heading %}
<div class="content-section-header">
<h2 class="content-classification">Kalenderexport</h2>
{% include "core/_decoration.html" with icon="sg-calendar" %}
</div>
{% endblock %}
{% block content %}
<div class="content-body">
<p>
Kalender werden im ICS-Exportformat bereitgestellt und lassen sich in dieser Form sowohl als
Abonnement einbinden, als auch einmalig herunterladen. Programme wie Thunderbird, aber
auch Web-Anwendungen wie owncloud oder Horde bieten es an diesen Kalender zu importieren.
Im Regelfall musst Du dafür die URL des Kalenders kopieren und im jeweiligen Programm einfügen.
Häufig wird die Funktionalität auch unter dem Namen WebCal angeboten.
Kalender werden im ICS-Exportformat bereitgestellt und lassen sich in dieser Form
sowohl als Abonnement einbinden, als auch einmalig herunterladen. Programme wie
Thunderbird, aber auch Web-Anwendungen wie owncloud oder Horde bieten es an diesen
Kalender zu importieren. Im Regelfall musst Du dafür die URL des Kalenders kopieren
und im jeweiligen Programm einfügen. Häufig wird die Funktionalität auch unter dem
Namen WebCal angeboten.
</p>
<h3>Öffentlicher Kalender</h3>
......@@ -35,8 +47,9 @@
{% if private_export_url %}
<h3>Privater Kalender</h3>
<p class="disclaimer">
Der private Kalender enthält alle nicht-öffentlichen Veranstaltungen. Wenn du die URL mit anderen
Menschen teilst, werden diese in deinem Namen auch Zugriff auf den Kalender haben.
Der private Kalender enthält alle nicht-öffentlichen Veranstaltungen. Wenn du
die URL mit anderen Menschen teilst, werden diese in deinem Namen auch Zugriff
auf den Kalender haben.
</p>
<div class="selectable-row">
<pre class="selectable selectable-mono">{{ private_export_url }}</pre>
......
{% extends 'stadt/stadt.html' %}
{% load events groups rules %}
{% block title %}Veranstaltungen - {{ block.super }}{% endblock %}
{% block menu %}{% with menu='event' %}{{ block.super }}{% endwith %}{% endblock %}
{% block heading_title %}
{% include 'core/_heading.html' with title='Veranstaltungen' icon='sg-pen' %}
......@@ -11,17 +9,19 @@
{% block heading_toolbar %}
{% has_perm 'content.create' user as can_create %}
{% if can_create %}
{% url 'create-event' as url %}
{% include 'core/_toolbutton.html' with label='<i class="sg sg-add"></i> Veranstaltung' url=url %}
{% url 'create-event' as create_url %}
{% include 'core/_toolbutton.html' with label='<i class="sg sg-add"></i> Veranstaltung' url=create_url %}
{% endif %}
{% endblock %}
{% block sidebar %}
{% load groups %}
{% sidebar_groups user %}
{% endblock %}
{% block content %}
<div class="calendar-wrapper" id="calendar-page" data-component="calendar" data-component-basepath="{% url 'events' %}">
{% load events %}
{% calendar view.get_content size="large" %}
<div class="content-list">
......
{% extends 'content/update.html' %}
{% block menu %}{% menu view.entity %}{% endblock %}
import calendar as python_calendar
import datetime
import itertools
import django.utils.formats
import django.utils.timezone
from django import template, urls
from django import template
from django.urls import reverse
from django.utils.safestring import mark_safe
from ..utils import get_requested_time
from grouprise.features.events.utils import get_requested_time, EventCalendar
register = template.Library()
class Calendar(python_calendar.LocaleHTMLCalendar):
def __init__(self, event_dict, firstweekday=0, locale=None):
super().__init__(firstweekday, locale)
self.today = datetime.date.today()
self.events = event_dict
def formatday(self, thedate, themonth):
events = self.events.get(thedate, [])
url = ''
if len(events) == 1:
url = events[0].get_absolute_url()
elif len(events) > 1:
url = urls.reverse(
'day-events', args=['{{:%{}}}'.format(c).format(thedate) for c in 'Ybd'])
return {
'day': thedate.day,
'events': events,
'in_month': thedate.month == themonth,
'today': thedate == self.today,
'url': url,
}
def formatmonthname(self, theyear, themonth):
with python_calendar.different_locale(self.locale):
return '%s %s' % (python_calendar.month_name[themonth], theyear)
def formatmonthweeks(self, theyear, themonth):
return [self.formatweek(week, themonth)
for week in self.monthdatescalendar(theyear, themonth)]
def formatweek(self, theweek, themonth):
return [self.formatday(d, themonth) for d in theweek]
@register.filter
def day_preview(associations):
return ', '.join([
'{}{}'.format(
'{} '.format(django.utils.formats.time_format(
django.utils.timezone.localtime(a.container.time)))
if not a.container.all_day else '',
a.container.title)
for a in associations])
def formatweekday(self, day):
with python_calendar.different_locale(self.locale):
return python_calendar.day_abbr[day]
def formatweekheader(self):
return [self.formatweekday(i) for i in self.iterweekdays()]
@register.simple_tag(takes_context=True)
def event_time(context, event):
context['event'] = event
time_str = context.template.engine.get_template('events/_time.html').render(context)
return time_str.strip()
@register.inclusion_tag('events/_calendar.html', takes_context=True)
......@@ -79,7 +55,7 @@ def calendar(context, associations, size='preview', component_id=None):
).order_by('content__time')
calendar_event_dict = {date: list(events) for date, events in itertools.groupby(
calendar_associations, key=lambda a: a.container.time.astimezone().date())}
calendar = Calendar(calendar_event_dict)
calendar = EventCalendar(calendar_event_dict)
last_month = around.replace(day=1) + datetime.timedelta(days=-1)
next_month = around.replace(day=1) + datetime.timedelta(days=31)
context.update({
......@@ -94,24 +70,6 @@ def calendar(context, associations, size='preview', component_id=None):
return context
@register.filter
def day_preview(associations):
return ', '.join([
'{}{}'.format(
'{} '.format(django.utils.formats.time_format(
django.utils.timezone.localtime(a.container.time)))
if not a.container.all_day else '',
a.container.title)
for a in associations])
@register.simple_tag(takes_context=True)
def event_time(context, event):
context['event'] = event
time_str = context.template.engine.get_template('events/_time.html').render(context)
return time_str.strip()
@register.inclusion_tag('events/_sidebar_calendar.html', takes_context=True)
def sidebar_calendar(
context, associations, group=None, preview_length=5, show_group=True,
......@@ -153,8 +111,3 @@ def sidebar_calendar(
'component_id': component_id
})
return context
@register.filter
def upcoming_events(events, preview_length):
return events.upcoming(preview_length)
......@@ -291,9 +291,6 @@ class TestUrls(grouprise.core.tests.Test):
self.assertEqual(r.status_code, 404)
r = self.client.get(self.get_url('create-group-event', 'non-existent'))
self.assertEqual(r.status_code, 404)
r = self.client.get(django.urls.reverse(
'gestalt-events-feed', args=['non-existent', 'public']))
self.assertEqual(r.status_code, 404)
r = self.client.get(self.get_url('group-events-export', 'non-existent'))
self.assertEqual(r.status_code, 404)
r = self.client.get(django.urls.reverse(
......
from django.conf.urls import url
from django.urls import path, re_path
from . import views
from grouprise.features.events.views import List, Create, SiteCalendarExport, \
SiteCalendarFeed, Day, GroupCalendarExport, GroupCalendarFeed
urlpatterns = [
url(
r'^stadt/events/$',
views.List.as_view(),
name='events'),
url(
r'^stadt/events/add/$',
views.Create.as_view(),
name='create-event'),
url(
r'^stadt/events/export$',
views.SiteCalendarExport.as_view(),
name='export-site-events'),
url(
r'^stadt/events/public.ics$',
views.SiteCalendarFeed(),
name='site-events-feed'),
url(
r'^stadt/events/(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/$',
views.Day.as_view(),
name='day-events'),
url(
r'^(?P<entity_slug>[\w.@+-]+)/events/add/$',
views.Create.as_view(),
name='create-group-event'),
url(
# TODO: remove 'gestalt/' prefix
r'^gestalt/(?P<gestalt_slug>[\w.@+-]+)/events/(?P<domain>public|private).ics$',
views.GestaltCalendarFeed(),
name='gestalt-events-feed'),
url(
r'^(?P<group_slug>[\w-]+)/events/export$',
views.GroupCalendarExport.as_view(),
name='group-events-export'),
url(
r'^(?P<group_slug>[\w-]+)/events/(?P<domain>public|private).ics$',
views.GroupCalendarFeed(),
name='group-events-feed'),
path('stadt/events', List.as_view(), name='events'),
path('stadt/events/add', Create.as_view(), name='create-event'),
path('stadt/events/export', SiteCalendarExport.as_view(), name='export-site-events'),
path('stadt/events/public.ics', SiteCalendarFeed(), name='site-events-feed'),
path('stadt/events/<int:year>/<slug:month>/<int:day>', Day.as_view(), name='day-events'),
path('<slug:entity_slug>/events/add', Create.as_view(), name='create-group-event'),
path('<slug:group_slug>/events/export', GroupCalendarExport.as_view(),
name='group-events-export'),
re_path(r'(?P<group_slug>[\w-]+)/events/(?P<domain>public|private).ics',
GroupCalendarFeed(), name='group-events-feed'),
]
from calendar import day_abbr, different_locale, month_name, LocaleHTMLCalendar
from datetime import date
from django import urls
from django.utils import timezone
......@@ -11,3 +15,44 @@ def get_requested_time(request):
except ValueError:
pass
return None
class EventCalendar(LocaleHTMLCalendar):
def __init__(self, event_dict, firstweekday=0, locale=None):
super().__init__(firstweekday, locale)
self.today = date.today()
self.events = event_dict
def formatday(self, thedate, themonth):
events = self.events.get(thedate, [])
url = ''
if len(events) == 1:
url = events[0].get_absolute_url()
elif len(events) > 1:
url = urls.reverse(
'day-events', args=['{{:%{}}}'.format(c).format(thedate) for c in 'Ybd'])
return {
'day': thedate.day,
'events': events,
'in_month': thedate.month == themonth,
'today': thedate == self.today,
'url': url,
}
def formatmonthname(self, theyear, themonth):
with different_locale(self.locale):
return '%s %s' % (month_name[themonth], theyear)
def formatmonthweeks(self, theyear, themonth):
return [self.formatweek(week, themonth)
for week in self.monthdatescalendar(theyear, themonth)]
def formatweek(self, theweek, themonth):
return [self.formatday(d, themonth) for d in theweek]
def formatweekday(self, day):
with different_locale(self.locale):
return day_abbr[day]
def formatweekheader(self):
return [self.formatweekday(i) for i in self.iterweekdays()]
......@@ -40,8 +40,8 @@ class List(grouprise.core.views.PermissionMixin, django.views.generic.ListView):
class Create(grouprise.features.content.views.Create):
form_class = forms.Create
template_name = 'events/create.html'
form_class = forms.EventCreateForm
template_name = 'content/create.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
......