Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • konstantin/akplanning
  • matedealer/akplanning
  • kif/akplanning
  • mirco/akplanning
  • lordofthevoid/akplanning
  • voidptr/akplanning
  • xayomer/akplanning-fork
  • mollux/akplanning
  • neumantm/akplanning
  • mmarx/akplanning
  • nerf/akplanning
  • felix_bonn/akplanning
  • sebastian.uschmann/akplanning
13 results
Select Git revision
Show changes
Showing
with 385 additions and 53 deletions
from django.apps import apps
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from AKModel.metaviews import status_manager
from AKModel.metaviews.admin import EventSlugMixin
from AKModel.metaviews.status import TemplateStatusWidget, StatusView
@status_manager.register(name="event_overview")
class EventOverviewWidget(TemplateStatusWidget):
"""
Status page widget: Event overview
"""
required_context_type = "event"
title = _("Overview")
template_name = "admin/AKModel/status/event_overview.html"
def render_status(self, context: {}) -> str:
return "success" if not context["event"].plan_hidden else "primary"
@status_manager.register(name="event_categories")
class EventCategoriesWidget(TemplateStatusWidget):
"""
Status page widget: Category information
Show all categories of the event together with the number of AKs belonging to this category.
Offers an action to add a new category.
"""
required_context_type = "event"
title = _("Categories")
template_name = "admin/AKModel/status/event_categories.html"
actions = [
{
"text": _("Add category"),
"url": reverse_lazy("admin:AKModel_akcategory_add"),
}
]
def render_title(self, context: {}) -> str:
# Store category count as instance variable for re-use in body
self.category_count = context['event'].akcategory_set.count() # pylint: disable=attribute-defined-outside-init
return f"{super().render_title(context)} ({self.category_count})"
def render_status(self, context: {}) -> str:
return "danger" if self.category_count == 0 else "primary"
@status_manager.register(name="event_rooms")
class EventRoomsWidget(TemplateStatusWidget):
"""
Status page widget: Category information
Show all rooms of the event.
Offers actions to add a single new room as well as for batch creation.
"""
required_context_type = "event"
title = _("Rooms")
template_name = "admin/AKModel/status/event_rooms.html"
actions = [
{
"text": _("Add Room"),
"url": reverse_lazy("admin:AKModel_room_add"),
}
]
def render_title(self, context: {}) -> str:
# Store room count as instance variable for re-use in body
self.room_count = context['event'].room_set.count() # pylint: disable=attribute-defined-outside-init
return f"{super().render_title(context)} ({self.room_count})"
def render_status(self, context: {}) -> str:
return "danger" if self.room_count == 0 else "primary"
def render_actions(self, context: {}) -> list[dict]:
actions = super().render_actions(context)
# Action has to be added here since it depends on the event for URL building
import_room_url = reverse_lazy("admin:room-import", kwargs={"event_slug": context["event"].slug})
for action in actions:
if action["url"] == import_room_url:
return actions
actions.append(
{
"text": _("Import Rooms from CSV"),
"url": import_room_url,
}
)
return actions
@status_manager.register(name="event_aks")
class EventAKsWidget(TemplateStatusWidget):
"""
Status page widget: AK information
Show information about the AKs of this event.
Offers a long list of AK-related actions and also scheduling actions of AKScheduling is active
"""
required_context_type = "event"
title = _("AKs")
template_name = "admin/AKModel/status/event_aks.html"
def get_context_data(self, context) -> dict:
context["ak_count"] = context["event"].ak_set.count()
context["unscheduled_slots_count"] = context["event"].akslot_set.filter(start=None).count
return context
def render_actions(self, context: {}) -> list[dict]:
actions = [
{
"text": _("Scheduling"),
"url": reverse_lazy("admin:schedule", kwargs={"event_slug": context["event"].slug}),
},
]
if apps.is_installed("AKScheduling"):
actions.extend([
{
"text": _("AKs requiring special attention"),
"url": reverse_lazy("admin:special-attention", kwargs={"slug": context["event"].slug}),
},
])
if context["event"].ak_set.count() > 0:
actions.append({
"text": _("Enter Interest"),
"url": reverse_lazy("admin:enter-interest",
kwargs={"event_slug": context["event"].slug,
"pk": context["event"].ak_set.all().first().pk}
),
})
actions.extend([
{
"text": _("Edit Default Slots"),
"url": reverse_lazy("admin:default-slots-editor", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Manage ak tracks"),
"url": reverse_lazy("admin:tracks_manage", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Export AKs as CSV"),
"url": reverse_lazy("admin:ak_csv_export", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Export AKs for Wiki"),
"url": reverse_lazy("admin:ak_wiki_export", kwargs={"slug": context["event"].slug}),
},
{
"text": _("Export AK Slides"),
"url": reverse_lazy("admin:ak_slide_export", kwargs={"event_slug": context["event"].slug}),
},
]
)
return actions
@status_manager.register(name="event_requirements")
class EventRequirementsWidget(TemplateStatusWidget):
"""
Status page widget: Requirement information information
Show information about the requirements of this event.
Offers actions to add new requirements or to get a list of AKs having a given requirement.
"""
required_context_type = "event"
title = _("Requirements")
template_name = "admin/AKModel/status/event_requirements.html"
def render_title(self, context: {}) -> str:
# Store requirements count as instance variable for re-use in body
# pylint: disable=attribute-defined-outside-init
self.requirements_count = context['event'].akrequirement_set.count()
return f"{super().render_title(context)} ({self.requirements_count})"
def render_actions(self, context: {}) -> list[dict]:
return [
{
"text": _("Show AKs for requirements"),
"url": reverse_lazy("admin:event_requirement_overview", kwargs={"event_slug": context["event"].slug}),
},
{
"text": _("Add Requirement"),
"url": reverse_lazy("admin:AKModel_akrequirement_add"),
},
]
class EventStatusView(EventSlugMixin, StatusView):
"""
View: Show a status dashboard for the given event
"""
title = _("Event Status")
provided_context_type = "event"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["site_url"] = reverse_lazy("dashboard:dashboard_event", kwargs={'slug': context["event"].slug})
return context
......@@ -5,6 +5,9 @@ from AKOnline.models import VirtualRoom
@admin.register(VirtualRoom)
class VirtualRoomAdmin(admin.ModelAdmin):
"""
Admin interface for virtual room model
"""
model = VirtualRoom
list_display = ['room', 'event', 'url']
list_filter = ['room__event']
......
......@@ -2,4 +2,7 @@ from django.apps import AppConfig
class AkonlineConfig(AppConfig):
"""
App configuration (default -- only to set the app name)
"""
name = 'AKOnline'
......@@ -6,23 +6,38 @@ from AKOnline.models import VirtualRoom
class VirtualRoomForm(ModelForm):
"""
Form to create a virtual room
Should be used as part of a multi form (see :class:`RoomWithVirtualForm` below)
"""
class Meta:
model = VirtualRoom
exclude = ['room']
# Show all fields except for room
exclude = ['room'] #pylint: disable=modelform-uses-exclude
def __init__(self, *args, **kwargs):
super(VirtualRoomForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
# Make the URL field optional to allow submitting the multi form without creating a virtual room
self.fields['url'].required = False
class RoomWithVirtualForm(MultiModelForm):
"""
Combined form to create rooms and optionally virtual rooms
Multi-Form that combines a :class:`RoomForm` (from AKModel) and a :class:`VirtualRoomForm` (see above).
The form will always create a room on valid input
and may additionally create a virtual room if the url field of the virtual room form part is set.
"""
form_classes = {
'room': RoomForm,
'virtual': VirtualRoomForm
}
def save(self, commit=True):
objects = super(RoomWithVirtualForm, self).save(commit=False)
objects = super().save(commit=False)
if commit:
room = objects['room']
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-24 18:01+0100\n"
"POT-Creation-Date: 2023-08-16 16:30+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -34,7 +34,7 @@ msgstr "Raum"
msgid "Virtual Room"
msgstr "Virtueller Raum"
#: AKOnline/models.py:17
#: AKOnline/models.py:17 AKOnline/views.py:38
msgid "Virtual Rooms"
msgstr "Virtuelle Räume"
......@@ -42,12 +42,12 @@ msgstr "Virtuelle Räume"
msgid "Leave empty if that room is not virtual/hybrid."
msgstr "Leer lassen wenn der Raum nicht virtuell/hybrid ist"
#: AKOnline/views.py:16
#: AKOnline/views.py:25
#, python-format
msgid "Created Room '%(room)s'"
msgstr "Raum '%(room)s' angelegt"
#: AKOnline/views.py:18
#: AKOnline/views.py:28
#, python-format
msgid "Created related Virtual Room '%(vroom)s'"
msgstr "Verbundenen virtuellen Raum '%(vroom)s' angelegt"
from django.db import models
from django.utils.translation import gettext_lazy as _
from AKModel.models import Event, Room
from AKModel.models import Room
class VirtualRoom(models.Model):
......@@ -18,6 +18,12 @@ class VirtualRoom(models.Model):
@property
def event(self):
"""
Property: Event this virtual room belongs to.
:return: Event this virtual room belongs to
:rtype: Event
"""
return self.room.event
def __str__(self):
......
{% load i18n %}
<ul>
{% for room in event.room_set.all %}
{% if room.virtual and room.virtual.url %}
<li>
<a href="{% url 'admin:AKOnline_virtualroom_change' room.pk %}">{{ room }} ({{ room.virtual.url | truncatechars:30 }})</a>
</li>
{% endif %}
{% endfor %}
</ul>
# Create your tests here.
......@@ -2,18 +2,38 @@ from django.contrib import messages
from django.http import HttpResponseRedirect
from django.utils.translation import gettext_lazy as _
from AKModel.views import AdminViewMixin, RoomCreationView
from AKModel.metaviews import status_manager
from AKModel.metaviews.status import TemplateStatusWidget
from AKModel.views.room import RoomCreationView
from AKOnline.forms import RoomWithVirtualForm
class RoomCreationWithVirtualView(RoomCreationView):
"""
View to create both rooms and optionally virtual rooms by filling one form
"""
form_class = RoomWithVirtualForm
template_name = 'admin/AKOnline/room_create_with_virtual.html'
room = None
def form_valid(self, form):
# This will create the room and additionally a virtual room if the url field is not blank
# objects['room'] will always a room instance afterwards, objects['virtual'] may be empty
objects = form.save()
self.room = objects['room']
messages.success(self.request, _("Created Room '%(room)s'" % {'room': objects['room']}))
# Create a (translated) success message containing information about the created room
messages.success(self.request, _("Created Room '%(room)s'" % {'room': objects['room']})) #pylint: disable=consider-using-f-string, line-too-long
if objects['virtual'] is not None:
messages.success(self.request, _("Created related Virtual Room '%(vroom)s'" % {'vroom': objects['virtual']}))
# Create a (translated) success message containing information about the created virtual room
messages.success(self.request, _("Created related Virtual Room '%(vroom)s'" % {'vroom': objects['virtual']})) #pylint: disable=consider-using-f-string, line-too-long
return HttpResponseRedirect(self.get_success_url())
@status_manager.register(name="event_virtual_rooms")
class EventVirtualRoomsWidget(TemplateStatusWidget):
"""
Status page widget to contain information about all virtual rooms belonging to the given event
"""
required_context_type = "event"
title = _("Virtual Rooms")
template_name = "admin/AKOnline/status/event_virtual_rooms.html"
# Register your models here.
......@@ -2,4 +2,7 @@ from django.apps import AppConfig
class AkplanConfig(AppConfig):
"""
App configuration (default, only specifies name of the app)
"""
name = 'AKPlan'
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-27 00:42+0100\n"
"POT-Creation-Date: 2023-05-15 20:03+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -27,56 +27,56 @@ msgstr "Plan"
msgid "Write to organizers of this event for questions and comments"
msgstr "Fragen oder Kommentare? Schreib den Orgas dieses Events eine Mail"
#: AKPlan/templates/AKPlan/plan_index.html:32
#: AKPlan/templates/AKPlan/plan_index.html:36
msgid "Day"
msgstr "Tag"
#: AKPlan/templates/AKPlan/plan_index.html:42
#: AKPlan/templates/AKPlan/plan_index.html:46
msgid "Event"
msgstr "Veranstaltung"
#: AKPlan/templates/AKPlan/plan_index.html:55
#: AKPlan/templates/AKPlan/plan_index.html:59
#: AKPlan/templates/AKPlan/plan_room.html:13
#: AKPlan/templates/AKPlan/plan_room.html:59
#: AKPlan/templates/AKPlan/plan_wall.html:54
#: AKPlan/templates/AKPlan/plan_wall.html:65
msgid "Room"
msgstr "Raum"
#: AKPlan/templates/AKPlan/plan_index.html:76
#: AKPlan/templates/AKPlan/plan_index.html:80
#: AKPlan/templates/AKPlan/plan_room.html:11
#: AKPlan/templates/AKPlan/plan_track.html:9
msgid "AK Plan"
msgstr "AK-Plan"
#: AKPlan/templates/AKPlan/plan_index.html:88
#: AKPlan/templates/AKPlan/plan_index.html:92
#: AKPlan/templates/AKPlan/plan_room.html:49
msgid "Rooms"
msgstr "Räume"
#: AKPlan/templates/AKPlan/plan_index.html:101
#: AKPlan/templates/AKPlan/plan_index.html:105
#: AKPlan/templates/AKPlan/plan_track.html:36
msgid "Tracks"
msgstr "Tracks"
#: AKPlan/templates/AKPlan/plan_index.html:113
#: AKPlan/templates/AKPlan/plan_index.html:117
msgid "AK Wall"
msgstr "AK-Wall"
#: AKPlan/templates/AKPlan/plan_index.html:126
#: AKPlan/templates/AKPlan/plan_wall.html:119
#: AKPlan/templates/AKPlan/plan_index.html:130
#: AKPlan/templates/AKPlan/plan_wall.html:130
msgid "Current AKs"
msgstr "Aktuelle AKs"
#: AKPlan/templates/AKPlan/plan_index.html:133
#: AKPlan/templates/AKPlan/plan_wall.html:124
#: AKPlan/templates/AKPlan/plan_index.html:137
#: AKPlan/templates/AKPlan/plan_wall.html:135
msgid "Next AKs"
msgstr "Nächste AKs"
#: AKPlan/templates/AKPlan/plan_index.html:141
#: AKPlan/templates/AKPlan/plan_index.html:145
msgid "This event is not active."
msgstr "Dieses Event ist nicht aktiv."
#: AKPlan/templates/AKPlan/plan_index.html:154
#: AKPlan/templates/AKPlan/plan_index.html:158
#: AKPlan/templates/AKPlan/plan_room.html:77
#: AKPlan/templates/AKPlan/plan_track.html:58
msgid "Plan is not visible (yet)."
......@@ -99,7 +99,7 @@ msgstr "Eigenschaften"
msgid "Track"
msgstr "Track"
#: AKPlan/templates/AKPlan/plan_wall.html:134
#: AKPlan/templates/AKPlan/plan_wall.html:145
msgid "Reload page automatically?"
msgstr "Seite automatisch neu laden?"
......
# Create your models here.
......@@ -51,6 +51,8 @@
start: '{{ start | timezone:event.timezone | date:"Y-m-d H:i:s" }}',
end: '{{ end | timezone:event.timezone | date:"Y-m-d H:i:s"}}',
},
slotMinTime: '{{ earliest_start_hour }}:00:00',
slotMaxTime: '{{ latest_end_hour }}:00:00',
eventDidMount: function(info) {
$(info.el).tooltip({title: info.event.extendedProps.description});
},
......
# gradients based on http://bsou.io/posts/color-gradients-with-python
def hex_to_rgb(hex):
def hex_to_rgb(hex): #pylint: disable=redefined-builtin
"""
Convert hex color to RGB color code
:param hex: hex encoded color
......@@ -23,8 +23,7 @@ def rgb_to_hex(rgb):
"""
# Components need to be integers for hex to make sense
rgb = [int(x) for x in rgb]
return "#"+"".join(["0{0:x}".format(v) if v < 16 else
"{0:x}".format(v) for v in rgb])
return "#"+"".join([f"0{v:x}" if v < 16 else f"{v:x}" for v in rgb])
def linear_blend(start_hex, end_hex, position):
......
......@@ -11,6 +11,14 @@ register = template.Library()
@register.filter
def highlight_change_colors(akslot):
"""
Adjust color to highlight recent changes if needed
:param akslot: akslot to determine color for
:type akslot: AKSlot
:return: color that should be used (either default color of the category or some kind of red)
:rtype: str
"""
# Do not highlight in preview mode or when changes occurred before the plan was published
if akslot.event.plan_hidden or (akslot.event.plan_published_at is not None
and akslot.event.plan_published_at > akslot.updated):
......@@ -25,9 +33,14 @@ def highlight_change_colors(akslot):
# Recent change? Calculate gradient blend between red and
recentness = seconds_since_update / settings.PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS
return darken("#b71540", recentness)
# return linear_blend("#b71540", "#000000", recentness)
@register.simple_tag
def timestamp_now(tz):
"""
Get the current timestamp for the given timezone
:param tz: timezone to be used for the timestamp
:return: current timestamp in given timezone
"""
return date_format(datetime.now().astimezone(tz), "c")
......@@ -4,6 +4,9 @@ from AKModel.tests import BasicViewTests
class PlanViewTests(BasicViewTests, TestCase):
"""
Tests for AKPlan
"""
fixtures = ['model.json']
APP_NAME = 'plan'
......@@ -15,7 +18,10 @@ class PlanViewTests(BasicViewTests, TestCase):
]
def test_plan_hidden(self):
view_name_with_prefix, url = self._name_and_url(('plan_overview', {'event_slug': 'kif23'}))
"""
Test correct handling of plan visibility
"""
_, url = self._name_and_url(('plan_overview', {'event_slug': 'kif23'}))
self.client.logout()
response = self.client.get(url)
......@@ -28,8 +34,11 @@ class PlanViewTests(BasicViewTests, TestCase):
msg_prefix="Plan is not visible for staff user")
def test_wall_redirect(self):
view_name_with_prefix, url_wall = self._name_and_url(('plan_wall', {'event_slug': 'kif23'}))
view_name_with_prefix, url_plan = self._name_and_url(('plan_overview', {'event_slug': 'kif23'}))
"""
Test: Make sure that user is redirected from wall to overview when plan is hidden
"""
_, url_wall = self._name_and_url(('plan_wall', {'event_slug': 'kif23'}))
_, url_plan = self._name_and_url(('plan_overview', {'event_slug': 'kif23'}))
response = self.client.get(url_wall)
self.assertRedirects(response, url_plan,
......
from datetime import timedelta
from datetime import datetime, timedelta
from django.conf import settings
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.datetime_safe import datetime
from django.views.generic import ListView, DetailView
from django.views.generic import DetailView, ListView
from AKModel.models import AKSlot, Room, AKTrack
from AKModel.views import FilterByEventSlugMixin
from AKModel.metaviews.admin import FilterByEventSlugMixin
from AKModel.models import AKSlot, AKTrack, Room
class PlanIndexView(FilterByEventSlugMixin, ListView):
"""
Default plan view
Shows two lists of current and upcoming AKs and a graphical full plan below
"""
model = AKSlot
template_name = "AKPlan/plan_index.html"
context_object_name = "akslots"
......@@ -60,6 +64,15 @@ class PlanIndexView(FilterByEventSlugMixin, ListView):
class PlanScreenView(PlanIndexView):
"""
Plan view optimized for screens and projectors
This again shows current and upcoming AKs as well as a graphical plan,
but no navigation elements and trys to use the available space as best as possible
such that no scrolling is needed.
The view contains a frontend functionality for auto-reload.
"""
template_name = "AKPlan/plan_wall.html"
def get(self, request, *args, **kwargs):
......@@ -69,11 +82,12 @@ class PlanScreenView(PlanIndexView):
return redirect(reverse_lazy("plan:plan_overview", kwargs={"event_slug": self.event.slug}))
return s
"""
# pylint: disable=attribute-defined-outside-init
def get_queryset(self):
# Determine interesting range (some hours ago until some hours in the future as specified in the settings)
now = datetime.now().astimezone(self.event.timezone)
# Wall during event: Adjust, show only parts in the future
if self.event.start < now < self.event.end:
# Determine interesting range (some hours ago until some hours in the future as specified in the settings)
self.start = now - timedelta(hours=settings.PLAN_WALL_HOURS_RETROSPECT)
else:
self.start = self.event.start
......@@ -81,33 +95,63 @@ class PlanScreenView(PlanIndexView):
# Restrict AK slots to relevant ones
# This will automatically filter all rooms not needed for the selected range in the orginal get_context method
return super().get_queryset().filter(start__gt=self.start)
"""
akslots = super().get_queryset().filter(start__gt=self.start)
# Find the earliest hour AKs start and end (handle 00:00 as 24:00)
self.earliest_start_hour = 23
self.latest_end_hour = 1
for akslot in akslots.all():
start_hour = akslot.start.astimezone(self.event.timezone).hour
if start_hour < self.earliest_start_hour:
# Use hour - 1 to improve visibility of date change
self.earliest_start_hour = max(start_hour - 1, 0)
end_hour = akslot.end.astimezone(self.event.timezone).hour
# Special case: AK starts before but ends after midnight -- show until midnight
if end_hour < start_hour:
self.latest_end_hour = 24
elif end_hour > self.latest_end_hour:
# Always use hour + 1, since AK may end at :xy and not always at :00
self.latest_end_hour = min(end_hour + 1, 24)
return akslots
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
context["start"] = self.event.start
context["start"] = self.start
context["end"] = self.event.end
context["earliest_start_hour"] = self.earliest_start_hour
context["latest_end_hour"] = self.latest_end_hour
return context
class PlanRoomView(FilterByEventSlugMixin, DetailView):
"""
Plan view for a single room
"""
template_name = "AKPlan/plan_room.html"
model = Room
context_object_name = "room"
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
# Restrict AKSlot list to the given room
# while joining AK, room and category information to reduce the amount of necessary SQL queries
context["slots"] = AKSlot.objects.filter(room=context['room']).select_related('ak', 'ak__category', 'ak__track')
return context
class PlanTrackView(FilterByEventSlugMixin, DetailView):
"""
Plan view for a single track
"""
template_name = "AKPlan/plan_track.html"
model = AKTrack
context_object_name = "track"
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
context["slots"] = AKSlot.objects.filter(event=self.event, ak__track=context['track']).select_related('ak', 'room', 'ak__category')
# Restrict AKSlot list to given track
# while joining AK, room and category information to reduce the amount of necessary SQL queries
context["slots"] = AKSlot.objects. \
filter(event=self.event, ak__track=context['track']). \
select_related('ak', 'room', 'ak__category')
return context
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-27 00:42+0100\n"
"POT-Creation-Date: 2023-08-16 16:30+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -17,10 +17,10 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: AKPlanning/settings.py:146
#: AKPlanning/settings.py:148
msgid "German"
msgstr "Deutsch"
#: AKPlanning/settings.py:147
#: AKPlanning/settings.py:149
msgid "English"
msgstr "Englisch"
......@@ -52,9 +52,9 @@ INSTALLED_APPS = [
'rest_framework',
'simple_history',
'registration',
'bootstrap_datepicker_plus',
'django_tex',
'compressor',
'docs',
]
MIDDLEWARE = [
......@@ -217,6 +217,9 @@ PLAN_MAX_HIGHLIGHT_UPDATE_SECONDS = 2 * 60 * 60
DASHBOARD_SHOW_RECENT = True
# How many entries max?
DASHBOARD_RECENT_MAX = 25
# How many events should be featured in the dashboard
# (active events will always be featured, even if their number is higher than this threshold)
DASHBOARD_MAX_FEATURED_EVENTS = 3
# Registration/login behavior
SIMPLE_BACKEND_REDIRECT_URL = "/user/"
......@@ -224,7 +227,7 @@ LOGIN_REDIRECT_URL = SIMPLE_BACKEND_REDIRECT_URL
# Content Security Policy
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'")
CSP_SCRIPT_SRC = ("'self'", "'unsafe-inline'", "'unsafe-eval'")
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'", "fonts.googleapis.com")
CSP_IMG_SRC = ("'self'", "data:")
CSP_FRAME_SRC = ("'self'", )
......@@ -234,4 +237,9 @@ CSP_FONT_SRC = ("'self'", "data:", "fonts.gstatic.com")
SEND_MAILS = True
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Documentation
DOCS_ROOT = os.path.join(BASE_DIR, 'docs/_build/html')
DOCS_ACCESS = 'public'
include(optional("settings/*.py"))