diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 79c61aca13842af150e089297cf3049876d11b5d..9872db38359a951199911ab881e4aa3305ffe4c7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,8 @@ and this project adheres to `Semantic Versioning`_.
 Unreleased
 ----------
 
+Changes
+=======
 The "managed models" feature is mandatory for all models derived from `ExtensibleModel`
 and requires creating a migration for all downstream models to add the respective
 field.
@@ -17,10 +19,14 @@ Added
 ~~~~~
 
 * Frontend for managing rooms.
+* Global calendar system
+* Calendar for birthdays of persons
+* Holiday model to track information about holidays.
 * [Dev] Components for implementing standard CRUD operations in new frontend.
 * [Dev] Options for filtering and sorting of GraphQL queries at the server.
 * [Dev] Managed models for instances handled by other apps.
 * [Dev] Upload slot sytem for out-of-band uploads in GraphQL clients
+* Generic endpoint for retrieving objects as JSON
 
 Changed
 ~~~~~~~
@@ -33,6 +39,8 @@ Fixed
 * [Docker] The build could silently continue even if frontend bundling failed, resulting
   in an incomplete AlekSIS frontend app.
 * GraphQL mutations did not return errors in case of exceptions.
+* Rendering of "simple" PDF templates failed when used with S3 storage.
+* Log messages on some loggers did not contain log message
 
 `3.1.2`_ - 2023-07-05
 ---------------------
diff --git a/aleksis/core/frontend/app/dateTimeFormats.js b/aleksis/core/frontend/app/dateTimeFormats.js
index 567f9a196f61052ff0481c1f975a6c0d2433964e..2cea6431fa0714ebd4fd303adab620bcda94da92 100644
--- a/aleksis/core/frontend/app/dateTimeFormats.js
+++ b/aleksis/core/frontend/app/dateTimeFormats.js
@@ -6,6 +6,13 @@ const dateTimeFormats = {
       month: "short",
       day: "numeric",
     },
+    shortDateTime: {
+      year: "numeric",
+      month: "short",
+      day: "numeric",
+      hour: "numeric",
+      minute: "numeric",
+    },
     long: {
       year: "numeric",
       month: "long",
@@ -37,6 +44,13 @@ const dateTimeFormats = {
       month: "short",
       day: "numeric",
     },
+    shortDateTime: {
+      year: "numeric",
+      month: "short",
+      day: "numeric",
+      hour: "numeric",
+      minute: "numeric",
+    },
     long: {
       year: "numeric",
       month: "long",
diff --git a/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue b/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6b50ee5ba208b225709e6963ec679e826ceead82
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/BaseCalendarFeedDetails.vue
@@ -0,0 +1,94 @@
+<template>
+  <v-menu
+    v-model="model"
+    :close-on-content-click="false"
+    :activator="selectedElement"
+    :offset-x="calendarType !== 'day'"
+    min-width="350px"
+    :offset-y="calendarType === 'day'"
+  >
+    <v-card min-width="350px" flat>
+      <v-toolbar :color="color || selectedEvent.color" dark dense>
+        <v-toolbar-title>
+          <slot name="title" :selected-event="selectedEvent">{{
+            selectedEvent.name
+          }}</slot>
+        </v-toolbar-title>
+        <v-spacer></v-spacer>
+        <slot name="badge" :selected-event="selectedEvent">
+          <cancelled-calendar-status-chip
+            v-if="selectedEvent.status === 'CANCELLED' && !withoutBadge"
+          />
+        </slot>
+      </v-toolbar>
+      <slot name="time" :selected-event="selectedEvent">
+        <v-list-item v-if="!withoutTime">
+          <v-list-item-icon>
+            <v-icon color="primary">mdi-calendar-today-outline</v-icon>
+          </v-list-item-icon>
+          <v-list-item-content>
+            <v-list-item-title>
+              <span
+                v-if="
+                  selectedEvent.allDay &&
+                  selectedEvent.start.getTime() === selectedEvent.end.getTime()
+                "
+              >
+                {{ $d(selectedEvent.start, "short") }}
+              </span>
+              <span v-else-if="selectedEvent.allDay">
+                {{ $d(selectedEvent.start, "short") }} –
+                {{ $d(selectedEvent.end, "short") }}
+              </span>
+              <span
+                v-else-if="
+                  dateWithoutTime(selectedEvent.start).getTime() ===
+                  dateWithoutTime(selectedEvent.end).getTime()
+                "
+              >
+                {{ $d(selectedEvent.start, "shortDateTime") }} –
+                {{ $d(selectedEvent.end, "shortTime") }}
+              </span>
+              <span v-else>
+                {{ $d(selectedEvent.start, "shortDateTime") }} –
+                {{ $d(selectedEvent.end, "shortDateTime") }}
+              </span>
+            </v-list-item-title>
+          </v-list-item-content>
+        </v-list-item>
+      </slot>
+      <slot name="description" :selected-event="selectedEvent">
+        <v-divider
+          inset
+          v-if="selectedEvent.description && !withoutDescription"
+        />
+        <v-list-item v-if="selectedEvent.description && !withoutDescription">
+          <v-list-item-icon>
+            <v-icon color="primary">mdi-card-text-outline</v-icon>
+          </v-list-item-icon>
+          <v-list-item-content style="white-space: pre-line">
+            {{ selectedEvent.description }}
+          </v-list-item-content>
+        </v-list-item>
+      </slot>
+    </v-card>
+  </v-menu>
+</template>
+
+<script>
+import calendarFeedDetailsMixin from "../../mixins/calendarFeedDetails.js";
+import CancelledCalendarStatusChip from "./CancelledCalendarStatusChip.vue";
+
+export default {
+  name: "BaseCalendarFeedDetails",
+  components: { CancelledCalendarStatusChip },
+  mixins: [calendarFeedDetailsMixin],
+  methods: {
+    dateWithoutTime(d) {
+      d = new Date(d);
+      d.setHours(0, 0, 0, 0);
+      return d;
+    },
+  },
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue b/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1e1d1436897ad17a1875f0bf8a9cf20d20033310
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/BaseCalendarFeedEventBar.vue
@@ -0,0 +1,56 @@
+<template>
+  <div
+    class="text-truncate"
+    :class="{
+      'text-decoration-line-through': event.status === 'CANCELLED',
+      'mx-1': withPadding,
+    }"
+    :style="{ height: '100%' }"
+  >
+    <slot name="time" v-bind="$props">
+      <span
+        v-if="
+          calendarType === 'month' && eventParsed.start.hasTime && !withoutTime
+        "
+        class="mr-1 font-weight-bold ml-1"
+      >
+        {{ eventParsed.start.time }}
+      </span>
+    </slot>
+    <slot name="icon" v-bind="$props">
+      <v-icon v-if="icon" x-small color="white" class="mx-1 left">
+        {{ icon }}
+      </v-icon>
+    </slot>
+
+    <slot name="title" v-bind="$props">
+      {{ event.name }}
+    </slot>
+  </div>
+</template>
+
+<script>
+import calendarFeedEventBarMixin from "../../mixins/calendarFeedEventBar.js";
+
+export default {
+  name: "BaseCalendarFeedEventBar",
+  mixins: [calendarFeedEventBarMixin],
+  props: {
+    withPadding: {
+      required: false,
+      type: Boolean,
+      default: true,
+    },
+    icon: {
+      required: false,
+      type: String,
+      default: "",
+    },
+    withoutTime: {
+      required: false,
+      type: Boolean,
+      default: false,
+    },
+  },
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar/CalendarControlBar.vue b/aleksis/core/frontend/components/calendar/CalendarControlBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c3f9c45c424aa73c0178ea15882e4e6783a73e43
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CalendarControlBar.vue
@@ -0,0 +1,21 @@
+<script>
+export default {
+  name: "CalendarControlBar",
+  emits: ["prev", "next", "today"],
+};
+</script>
+
+<template>
+  <div class="d-flex justify-center">
+    <v-btn icon class="mx-2" @click="$emit('prev')">
+      <v-icon>mdi-chevron-left</v-icon>
+    </v-btn>
+    <v-btn outlined text class="mx-2" @click="$emit('today')">
+      <v-icon left>mdi-calendar-today-outline</v-icon>
+      {{ $t("calendar.today") }}
+    </v-btn>
+    <v-btn icon class="mx-2" @click="$emit('next')">
+      <v-icon>mdi-chevron-right</v-icon>
+    </v-btn>
+  </div>
+</template>
diff --git a/aleksis/core/frontend/components/calendar/CalendarOverview.vue b/aleksis/core/frontend/components/calendar/CalendarOverview.vue
new file mode 100644
index 0000000000000000000000000000000000000000..7153b8e1e8dce599fc00527d28bc327a713e4ed4
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CalendarOverview.vue
@@ -0,0 +1,329 @@
+<template>
+  <div class="mt-4 mb-4">
+    <v-skeleton-loader
+      v-if="
+        $apollo.queries.calendar.loading && calendar.calendarFeeds.length === 0
+      "
+      type="date-picker-options, actions"
+    />
+    <div v-else>
+      <h1
+        class="mb-4 mx-4"
+        v-if="$vuetify.breakpoint.mdAndDown && $refs.calendar"
+      >
+        {{ $refs.calendar.title }}
+      </h1>
+      <v-row align="stretch">
+        <!-- Control bar with prev, next and today buttons -->
+        <v-col cols="12" sm="4" lg="3" xl="2" align-self="center">
+          <calendar-control-bar
+            @prev="$refs.calendar.prev()"
+            @next="$refs.calendar.next()"
+            @today="calendarFocus = new Date()"
+          />
+        </v-col>
+
+        <!-- Calendar title with current calendar time range -->
+        <v-col v-if="$vuetify.breakpoint.lgAndUp" align-self="center">
+          <h1 class="mx-2" v-if="$refs.calendar">{{ $refs.calendar.title }}</h1>
+        </v-col>
+
+        <!-- Button menu for selecting currently active calendars (only tablets/mobile) -->
+        <v-col
+          v-if="$vuetify.breakpoint.mdAndDown"
+          cols="12"
+          sm="4"
+          align-self="center"
+          class="d-flex justify-center"
+        >
+          <button-menu
+            icon="mdi-calendar-check-outline"
+            text-translation-key="calendar.select"
+          >
+            <calendar-select
+              v-model="selectedCalendarFeedNames"
+              :calendar-feeds="calendar.calendarFeeds"
+              @input="storeActivatedCalendars"
+            />
+          </button-menu>
+        </v-col>
+
+        <v-spacer v-if="$vuetify.breakpoint.lgAndUp" />
+
+        <!-- Calendar type select (month, week, day) -->
+        <v-col
+          cols="12"
+          sm="4"
+          lg="3"
+          align-self="center"
+          :align="$vuetify.breakpoint.smAndUp ? 'right' : 'center'"
+        >
+          <calendar-type-select v-model="currentCalendarType" />
+        </v-col>
+      </v-row>
+      <v-row>
+        <v-col v-if="$vuetify.breakpoint.lgAndUp" lg="3" xl="2">
+          <!-- Mini date picker -->
+          <v-date-picker
+            no-title
+            v-model="calendarFocus"
+            :style="{ margin: '0px -8px' }"
+            :first-day-of-week="1"
+          ></v-date-picker>
+
+          <!-- Calendar select (only desktop) -->
+          <v-list flat subheader>
+            <v-subheader>
+              {{ $t("calendar.my_calendars") }}
+            </v-subheader>
+            <calendar-select
+              class="mb-4"
+              v-model="selectedCalendarFeedNames"
+              :calendar-feeds="calendar.calendarFeeds"
+              @input="storeActivatedCalendars"
+            />
+            <v-btn depressed block v-if="calendar" :href="calendar.allFeedsUrl">
+              <v-icon left>mdi-download-outline</v-icon>
+              {{ $t("calendar.download_all") }}
+            </v-btn>
+          </v-list>
+        </v-col>
+
+        <!-- Actual calendar -->
+        <v-col lg="9" xl="10">
+          <v-sheet height="600">
+            <v-expand-transition>
+              <v-progress-linear
+                v-if="$apollo.queries.calendar.loading"
+                indeterminate
+              />
+            </v-expand-transition>
+            <v-calendar
+              ref="calendar"
+              v-model="calendarFocus"
+              show-week
+              :events="events"
+              :type="currentCalendarType"
+              :event_color="getColorForEvent"
+              @click:date="viewDay"
+              @click:day="viewDay"
+              @click:more="viewDay"
+              @click:event="viewEvent"
+              @change="fetchMoreCalendarEvents"
+            >
+              <template #event="{ event, eventParsed, timed }">
+                <component
+                  :is="eventBarComponentForFeed(event.calendarFeedName)"
+                  :event="event"
+                  :event-parsed="eventParsed"
+                  :calendar-type="currentCalendarType"
+                />
+              </template>
+            </v-calendar>
+            <component
+              v-if="calendar && calendar.calendarFeeds && selectedEvent"
+              :is="detailComponentForFeed(selectedEvent.calendarFeedName)"
+              v-model="selectedOpen"
+              :selected-element="selectedElement"
+              :selected-event="selectedEvent"
+              :calendar-type="currentCalendarType"
+            />
+          </v-sheet>
+        </v-col>
+      </v-row>
+    </div>
+  </div>
+</template>
+
+<script>
+import ButtonMenu from "../generic/ButtonMenu.vue";
+import CalendarSelect from "./CalendarSelect.vue";
+import GenericCalendarFeedDetails from "./GenericCalendarFeedDetails.vue";
+import GenericCalendarFeedEventBar from "./GenericCalendarFeedEventBar.vue";
+
+import {
+  calendarFeedDetailComponents,
+  calendarFeedEventBarComponents,
+} from "aleksisAppImporter";
+
+import gqlCalendarOverview from "./calendarOverview.graphql";
+import gqlSetCalendarStatus from "./setCalendarStatus.graphql";
+import CalendarControlBar from "./CalendarControlBar.vue";
+import CalendarTypeSelect from "./CalendarTypeSelect.vue";
+
+export default {
+  name: "CalendarOverview",
+  components: {
+    CalendarTypeSelect,
+    CalendarControlBar,
+    ButtonMenu,
+    CalendarSelect,
+  },
+  data() {
+    return {
+      calendarFocus: "",
+      calendar: {
+        calendarFeeds: [],
+      },
+      selectedCalendarFeedNames: [],
+      currentCalendarType: "week",
+      selectedEvent: {},
+      selectedElement: null,
+      selectedOpen: false,
+      fetchedDateRange: { start: null, end: null },
+    };
+  },
+  apollo: {
+    calendar: {
+      query: gqlCalendarOverview,
+      skip: true,
+      result({ data }) {
+        this.selectedCalendarFeedNames = data.calendar.calendarFeeds
+          .filter((c) => c.activated)
+          .map((c) => c.name);
+      },
+    },
+  },
+  computed: {
+    events() {
+      return this.calendar.calendarFeeds
+        .filter((c) => this.selectedCalendarFeedNames.includes(c.name))
+        .flatMap((cf) =>
+          cf.feed.events.map((event) => ({
+            ...event,
+            category: cf.verboseName,
+            calendarFeedName: cf.name,
+            start: new Date(event.start),
+            end: new Date(event.end),
+            color: event.color ? event.color : cf.color,
+            timed: !event.allDay,
+            meta: JSON.parse(event.meta),
+          }))
+        );
+    },
+  },
+  methods: {
+    viewDay({ date }) {
+      this.calendarFocus = date;
+      this.currentCalendarType = "day";
+    },
+    viewEvent({ nativeEvent, event }) {
+      const open = () => {
+        this.selectedEvent = event;
+        this.selectedElement = nativeEvent.target;
+        requestAnimationFrame(() =>
+          requestAnimationFrame(() => (this.selectedOpen = true))
+        );
+      };
+
+      if (this.selectedOpen) {
+        this.selectedOpen = false;
+        requestAnimationFrame(() => requestAnimationFrame(() => open()));
+      } else {
+        open();
+      }
+
+      nativeEvent.stopPropagation();
+    },
+    detailComponentForFeed(feedName) {
+      if (
+        this.calendar.calendarFeeds &&
+        feedName &&
+        Object.keys(calendarFeedDetailComponents).includes(feedName + "details")
+      ) {
+        return calendarFeedDetailComponents[feedName + "details"];
+      }
+      return GenericCalendarFeedDetails;
+    },
+    eventBarComponentForFeed(feedName) {
+      if (
+        this.calendar.calendarFeeds &&
+        feedName &&
+        Object.keys(calendarFeedEventBarComponents).includes(
+          feedName + "eventbar"
+        )
+      ) {
+        return calendarFeedEventBarComponents[feedName + "eventbar"];
+      }
+      return GenericCalendarFeedEventBar;
+    },
+    getColorForEvent(event) {
+      return event.color;
+    },
+    storeActivatedCalendars() {
+      // Store currently activated calendars in the backend
+      this.$apollo.mutate({
+        mutation: gqlSetCalendarStatus,
+        variables: {
+          calendars: this.selectedCalendarFeedNames,
+        },
+      });
+    },
+    fetchMoreCalendarEvents({ start, end }) {
+      // Get the start and end dates of the current date range shown in the calendar
+      let extendedStart = this.$refs.calendar.getStartOfWeek(start).date;
+      let extendedEnd = this.$refs.calendar.getEndOfWeek(end).date;
+
+      let olderStart = extendedStart < this.fetchedDateRange.start;
+      let youngerEnd = extendedEnd > this.fetchedDateRange.end;
+
+      if (this.calendar.calendarFeeds.length === 0) {
+        // No calendar feeds have been fetched yet,
+        // so fetch all events in the current date range
+
+        this.$apollo.queries.calendar.setVariables({
+          start: extendedStart,
+          end: extendedEnd,
+        });
+        this.$apollo.queries.calendar.skip = false;
+        this.fetchedDateRange = { start: extendedStart, end: extendedEnd };
+      } else if (olderStart || youngerEnd) {
+        // Define newly fetched date range
+        let newStart = olderStart ? extendedStart : this.fetchedDateRange.start;
+        let newEnd = youngerEnd ? extendedEnd : this.fetchedDateRange.end;
+
+        // Define date range to fetch
+        let fetchStart = olderStart ? extendedStart : this.fetchedDateRange.end;
+        let fetchEnd = youngerEnd ? extendedEnd : this.fetchedDateRange.start;
+
+        this.$apollo.queries.calendar.fetchMore({
+          variables: {
+            start: fetchStart,
+            end: fetchEnd,
+          },
+          updateQuery: (previousResult, { fetchMoreResult }) => {
+            let previousCalendarFeeds = previousResult.calendar.calendarFeeds;
+            let newCalendarFeeds = fetchMoreResult.calendar.calendarFeeds;
+
+            previousCalendarFeeds.forEach((calendarFeed, i, calendarFeeds) => {
+              // Get all events except those that are updated
+              let keepEvents = calendarFeed.feed.events.filter(
+                (event) => event.end < fetchStart || event.start > fetchEnd
+              );
+
+              /// Update the events of the calendar feed
+              calendarFeeds[i].feed.events = [
+                ...keepEvents,
+                ...newCalendarFeeds[i].feed.events,
+              ];
+            });
+            return {
+              calendar: {
+                ...previousResult.calendar,
+                calendarFeeds: previousCalendarFeeds,
+              },
+            };
+          },
+        });
+
+        this.fetchedDateRange = { start: newStart, end: newEnd };
+      }
+    },
+  },
+  mounted() {
+    this.$refs.calendar.move(0);
+  },
+};
+</script>
+
+<style scoped></style>
diff --git a/aleksis/core/frontend/components/calendar/CalendarSelect.vue b/aleksis/core/frontend/components/calendar/CalendarSelect.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5748b1d5755b961ca901f075b4aac152f3874634
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CalendarSelect.vue
@@ -0,0 +1,85 @@
+<template>
+  <v-list-item-group multiple v-model="model">
+    <v-list-item
+      v-for="calendarFeed in calendarFeeds"
+      :key="calendarFeed.name"
+      :value="calendarFeed.name"
+    >
+      <template #default="{ active }">
+        <v-list-item-action>
+          <v-checkbox
+            :input-value="active"
+            :color="calendarFeed.color"
+          ></v-checkbox>
+        </v-list-item-action>
+
+        <v-list-item-content>
+          <v-list-item-title>
+            {{ calendarFeed.verboseName }}
+          </v-list-item-title>
+        </v-list-item-content>
+
+        <v-list-item-action>
+          <v-menu bottom>
+            <template v-slot:activator="{ on, attrs }">
+              <v-btn fab x-small icon v-bind="attrs" v-on="on">
+                <v-icon>mdi-dots-vertical</v-icon>
+              </v-btn>
+            </template>
+            <v-list dense>
+              <v-list-item :href="calendarFeed.url">
+                <v-list-item-icon>
+                  <v-icon>mdi-calendar-export</v-icon>
+                </v-list-item-icon>
+                <v-list-item-title>
+                  {{ $t("calendar.download_ics") }}
+                </v-list-item-title>
+              </v-list-item>
+            </v-list>
+          </v-menu>
+        </v-list-item-action>
+      </template>
+    </v-list-item>
+  </v-list-item-group>
+</template>
+
+<script>
+export default {
+  name: "CalendarSelect",
+  props: {
+    calendarFeeds: {
+      type: Array,
+      required: true,
+    },
+    value: {
+      type: Array,
+      required: true,
+    },
+  },
+  computed: {
+    model: {
+      get() {
+        return this.value;
+      },
+      set(value) {
+        this.$emit("input", value);
+      },
+    },
+    someSelected() {
+      return this.model.length > 0 && !this.allSelected;
+    },
+    allSelected() {
+      return this.model.length === this.calendarFeeds.length;
+    },
+  },
+  methods: {
+    toggleAll(newValue) {
+      if (newValue) {
+        this.model = this.calendarFeeds.map((feed) => feed.name);
+      } else {
+        this.model = [];
+      }
+    },
+  },
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue b/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..679749135dd12e302f18a797af4d8fb169a44470
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CalendarStatusChip.vue
@@ -0,0 +1,24 @@
+<script>
+export default {
+  name: "CalendarStatusChip",
+  props: {
+    color: {
+      type: String,
+      required: true,
+    },
+    icon: {
+      type: String,
+      required: true,
+    },
+  },
+};
+</script>
+
+<template>
+  <v-chip :color="color" label>
+    <v-avatar left>
+      <v-icon>{{ icon }}</v-icon>
+    </v-avatar>
+    <slot></slot>
+  </v-chip>
+</template>
diff --git a/aleksis/core/frontend/components/calendar/CalendarTypeSelect.vue b/aleksis/core/frontend/components/calendar/CalendarTypeSelect.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1868b314a1af1106fe30b54571f629f2a577995f
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CalendarTypeSelect.vue
@@ -0,0 +1,61 @@
+<script>
+export default {
+  name: "CalendarTypeSelect",
+  props: {
+    value: {
+      type: String,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      innerValue: this.value,
+      availableCalendarTypes: [
+        {
+          type: "month",
+          translationKey: "calendar.month",
+          icon: "calendar-month-outline",
+        },
+        {
+          type: "week",
+          translationKey: "calendar.week",
+          icon: "calendar-week-outline",
+        },
+        {
+          type: "day",
+          translationKey: "calendar.day",
+          icon: "calendar-today-outline",
+        },
+      ],
+    };
+  },
+  watch: {
+    value(val) {
+      this.innerValue = val;
+    },
+    innerValue(val) {
+      this.$emit("input", val);
+    },
+  },
+  methods: {
+    nameForMenu(item) {
+      return this.$t(item.translationKey);
+    },
+  },
+};
+</script>
+
+<template>
+  <v-btn-toggle dense v-model="innerValue" class="mx-2">
+    <v-btn
+      v-for="calendarType in availableCalendarTypes"
+      :value="calendarType.type"
+      :key="calendarType.type"
+    >
+      <v-icon v-if="$vuetify.breakpoint.smAndDown">{{
+        "mdi-" + calendarType.icon
+      }}</v-icon>
+      <span class="hidden-sm-and-down">{{ nameForMenu(calendarType) }}</span>
+    </v-btn>
+  </v-btn-toggle>
+</template>
diff --git a/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue b/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..00b816943654453690f32da1db95e14f57b7dce7
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/CancelledCalendarStatusChip.vue
@@ -0,0 +1,14 @@
+<script>
+import CalendarStatusChip from "./CalendarStatusChip.vue";
+
+export default {
+  name: "CancelledCalendarStatusChip",
+  components: { CalendarStatusChip },
+};
+</script>
+
+<template>
+  <calendar-status-chip icon="mdi-cancel" color="error">
+    {{ $t("calendar.cancelled") }}
+  </calendar-status-chip>
+</template>
diff --git a/aleksis/core/frontend/components/calendar/GenericCalendarFeedDetails.vue b/aleksis/core/frontend/components/calendar/GenericCalendarFeedDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..2b6f8fa1e5833260196f97a4e939187f87c80c3f
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/GenericCalendarFeedDetails.vue
@@ -0,0 +1,25 @@
+<template>
+  <div>
+    <base-calendar-feed-details v-bind="$props" />
+    <v-divider inset v-if="selectedEvent.location" />
+    <v-list-item v-if="selectedEvent.location">
+      <v-list-item-icon>
+        <v-icon color="primary">mdi-map-marker-outline</v-icon>
+      </v-list-item-icon>
+      <v-list-item-content>
+        {{ selectedEvent.location }}
+      </v-list-item-content>
+    </v-list-item>
+  </div>
+</template>
+
+<script>
+import calendarFeedDetailsMixin from "../../mixins/calendarFeedDetails.js";
+import BaseCalendarFeedDetails from "./BaseCalendarFeedDetails.vue";
+
+export default {
+  name: "GenericCalendarFeedDetails",
+  components: { BaseCalendarFeedDetails },
+  mixins: [calendarFeedDetailsMixin],
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar/GenericCalendarFeedEventBar.vue b/aleksis/core/frontend/components/calendar/GenericCalendarFeedEventBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..251b7a8b929f3b67ada0e4a207aefef16d500149
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/GenericCalendarFeedEventBar.vue
@@ -0,0 +1,14 @@
+<template>
+  <base-calendar-feed-event-bar v-bind="$props" />
+</template>
+
+<script>
+import calendarFeedEventBarMixin from "../../mixins/calendarFeedEventBar.js";
+import BaseCalendarFeedEventBar from "./BaseCalendarFeedEventBar.vue";
+
+export default {
+  name: "GenericCalendarFeedEventBar",
+  components: { BaseCalendarFeedEventBar },
+  mixins: [calendarFeedEventBarMixin],
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar/calendarOverview.graphql b/aleksis/core/frontend/components/calendar/calendarOverview.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..f277ef4bbffcf9cf68532a5e1ba9ce3ecd2c813b
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/calendarOverview.graphql
@@ -0,0 +1,27 @@
+query ($start: Date, $end: Date) {
+  calendar {
+    allFeedsUrl
+    calendarFeeds {
+      name
+      verboseName
+      description
+      url
+      color
+      activated
+      feed {
+        events(start: $start, end: $end) {
+          name
+          start
+          end
+          color
+          description
+          location
+          uid
+          allDay
+          status
+          meta
+        }
+      }
+    }
+  }
+}
diff --git a/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..633f0791c3e00f0c82886779366704086871c484
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql
@@ -0,0 +1,5 @@
+mutation ($calendars: [String]!) {
+  setCalendarStatus(calendars: $calendars) {
+    ok
+  }
+}
diff --git a/aleksis/core/frontend/components/calendar_feeds/details/BirthdaysDetails.vue b/aleksis/core/frontend/components/calendar_feeds/details/BirthdaysDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f651ff2d11fa189b057b1adc6a613b288ae84604
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar_feeds/details/BirthdaysDetails.vue
@@ -0,0 +1,24 @@
+<template>
+  <base-calendar-feed-details v-bind="$props" without-time>
+    <template #description="{ selectedEvent }">
+      <v-divider />
+      <v-card-text>
+        <span>
+          <v-icon class="mr-2">mdi-cake-variant-outline</v-icon>
+          {{ $d(selectedEvent.start) }}
+        </span>
+      </v-card-text>
+    </template>
+  </base-calendar-feed-details>
+</template>
+
+<script>
+import calendarFeedDetailsMixin from "../../../mixins/calendarFeedDetails.js";
+import BaseCalendarFeedDetails from "../../calendar/BaseCalendarFeedDetails.vue";
+
+export default {
+  name: "BirthdaysDetails",
+  components: { BaseCalendarFeedDetails },
+  mixins: [calendarFeedDetailsMixin],
+};
+</script>
diff --git a/aleksis/core/frontend/components/calendar_feeds/event_bar/BirthdaysEventBar.vue b/aleksis/core/frontend/components/calendar_feeds/event_bar/BirthdaysEventBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d0f89cfae0695cba0033722dda8d66d5686205a6
--- /dev/null
+++ b/aleksis/core/frontend/components/calendar_feeds/event_bar/BirthdaysEventBar.vue
@@ -0,0 +1,17 @@
+<template>
+  <base-calendar-feed-event-bar
+    v-bind="$props"
+    icon="mdi-cake-variant-outline"
+  />
+</template>
+
+<script>
+import calendarFeedEventBarMixin from "../../../mixins/calendarFeedEventBar.js";
+import BaseCalendarFeedEventBar from "../../calendar/BaseCalendarFeedEventBar.vue";
+
+export default {
+  name: "BirthdaysEventBar",
+  components: { BaseCalendarFeedEventBar },
+  mixins: [calendarFeedEventBarMixin],
+};
+</script>
diff --git a/aleksis/core/frontend/components/generic/ButtonMenu.vue b/aleksis/core/frontend/components/generic/ButtonMenu.vue
index b105f84a8e1254f90bb1d78a2903c42f60cec29e..acc0990d050815c8bef8967aed93729c212f6f24 100644
--- a/aleksis/core/frontend/components/generic/ButtonMenu.vue
+++ b/aleksis/core/frontend/components/generic/ButtonMenu.vue
@@ -6,6 +6,7 @@
           <v-icon center>
             {{ icon }}
           </v-icon>
+          <span v-if="textTranslationKey">{{ $t(textTranslationKey) }}</span>
         </v-btn>
       </slot>
     </template>
@@ -25,6 +26,11 @@ export default {
       required: false,
       default: "mdi-dots-horizontal",
     },
+    textTranslationKey: {
+      type: String,
+      required: false,
+      default: "",
+    },
   },
 };
 </script>
diff --git a/aleksis/core/frontend/components/generic/CopyToClipboardButton.vue b/aleksis/core/frontend/components/generic/CopyToClipboardButton.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f4c167229bb53e444696522e2643e73540536e99
--- /dev/null
+++ b/aleksis/core/frontend/components/generic/CopyToClipboardButton.vue
@@ -0,0 +1,67 @@
+<template>
+  <v-tooltip bottom :open-on-hover="hover" v-model="tooltipModel">
+    <template #activator="{ on, attrs }">
+      <v-layout wrap v-on="on" v-bind="attrs">
+        <v-btn fab x-small icon @click.stop="copyToClipboard(text)">
+          <v-scroll-x-transition mode="out-in">
+            <v-icon :key="clipboardIcon">
+              {{ clipboardIcon }}
+            </v-icon>
+          </v-scroll-x-transition>
+        </v-btn>
+      </v-layout>
+    </template>
+    <span>{{ tooltipText }}</span>
+  </v-tooltip>
+</template>
+
+<script>
+export default {
+  name: "CopyToClipboardButton",
+  data() {
+    return {
+      copied: false,
+      tooltipModel: false,
+      hover: true,
+    };
+  },
+  props: {
+    text: {
+      type: String,
+      required: true,
+    },
+    tooltipHelpText: {
+      type: String,
+      default: "",
+    },
+  },
+  computed: {
+    tooltipText() {
+      return this.copied
+        ? this.$t("actions.copied")
+        : this.tooltipHelpText
+        ? this.tooltipHelpText
+        : this.$t("actions.copy");
+    },
+    clipboardIcon() {
+      return this.copied
+        ? "mdi-clipboard-check-outline"
+        : "mdi-clipboard-outline";
+    },
+  },
+  methods: {
+    copyToClipboard(text) {
+      navigator.clipboard.writeText(text);
+      this.tooltipModel = false;
+      setTimeout(() => {
+        this.tooltipModel = this.copied = true;
+        this.hover = false;
+        setTimeout(() => {
+          this.tooltipModel = this.copied = false;
+          this.hover = true;
+        }, 1000);
+      }, 100);
+    },
+  },
+};
+</script>
diff --git a/aleksis/core/frontend/messages/de.json b/aleksis/core/frontend/messages/de.json
index 207db33feb3d4b49edb79ebae96230df241fa8fd..dd143b42332ea754758c581b844eb1d44db74d9a 100644
--- a/aleksis/core/frontend/messages/de.json
+++ b/aleksis/core/frontend/messages/de.json
@@ -74,15 +74,20 @@
   "actions": {
     "back": "Zurück",
     "cancel": "Abbrechen",
+    "clear_filters": "Filter zurücksetzen",
     "close": "Schließen",
-    "confirm_deletion": "Sind Sie sicher, dass sie dieses Objekt löschen wollen?",
+    "confirm_deletion": "Sind Sie sicher, dass Sie dieses Objekt löschen wollen?",
+    "confirm_deletion_multiple": "Sind Sie sicher, dass Sie diese Objekte löschen wollen?",
+    "create": "Hinzufügen",
     "delete": "Löschen",
     "edit": "Bearbeiten",
+    "filter": "Filter",
     "save": "Speichern",
     "search": "Suchen",
+    "select_action": "Aktion auswählen",
     "stop_editing": "Bearbeiten beenden",
-    "filter": "Filter",
-    "clear_filters": "Filter zurücksetzen"
+    "title": "Aktionen",
+    "update": "Aktualisieren"
   },
   "administration": {
     "backend_admin": {
@@ -135,8 +140,25 @@
     "notice": "Wenn der Download nicht automatisch beginnt, klicken Sie bitte auf den Button unten.",
     "title": "PDF-Datei wird heruntergeladen ..."
   },
+  "forms": {
+    "date_time": {
+      "date": "Datum",
+      "time": "Zeit"
+    },
+    "errors": {
+      "invalid_color": "Dies ist keine gültige Farbe.",
+      "invalid_date": "Dies ist kein gültiges Datum.",
+      "invalid_time": "Dies ist keine gültige Zeit.",
+      "not_a_number": "Keine gültige Nummer",
+      "not_a_whole_number": "Bitte geben Sie eine ganze Zahl ein",
+      "number_too_big": "Bitte geben Sie eine kleinere Zahl ein.",
+      "number_too_small": "Bitte geben Sie eine größere Zahl ein.",
+      "required": "Dieses Feld ist verpflichtend."
+    }
+  },
   "graphql": {
-    "snackbar_error_message": "Beim Abrufen der Seitendaten ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut."
+    "snackbar_error_message": "Beim Abrufen der Seitendaten ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.",
+    "snackbar_success_message": "Der Vorgang wurde erfolgreich beendet."
   },
   "group": {
     "additional_field": {
@@ -234,11 +256,27 @@
       "menu_title": "Konfiguration"
     }
   },
+  "rooms": {
+    "create_room": "Neuen Raum erstellen",
+    "menu_title": "Räume",
+    "name": "Name",
+    "short_name": "Kurzname",
+    "title_plural": "Räume"
+  },
   "school_term": {
+    "after": "Endet nach",
+    "before": "Beginnt vor",
+    "create_school_term": "Schuljahr erstellen",
+    "date_end": "Enddatum",
+    "date_start": "Startdatum",
     "menu_title": "Schuljahre",
+    "name": "Name",
     "title": "Schuljahr",
     "title_plural": "Schuljahre"
   },
+  "selection": {
+    "num_items_selected": "Keine Objekte ausgewählt | 1 Objekt ausgewählt | {n} Objekte ausgewählt"
+  },
   "service_worker": {
     "dismiss": "Verwerfen",
     "new_version_available": "Es ist eine neue Version der App verfügbar",
@@ -247,7 +285,20 @@
   "status": {
     "changes": "Sie haben nicht gespeicherte Änderungen.",
     "error": "Beim Speichern der letzten Änderungen ist ein Fehler aufgetreten.",
+    "object_create_success": "Das Objekt wurde erfolgreich erstellt.",
+    "object_delete_success": "Das Objekt wurde erfolgreich gelöscht.",
+    "object_edit_success": "Das Objekt wurde erfolgreich bearbeitet.",
+    "objects_delete_success": "Die Objekte wurden erfolgreich gelöscht.",
     "saved": "Alle Änderungen sind gespeichert.",
     "updating": "Änderungen werden synchronisiert."
+  },
+  "weekdays": {
+    "A_0": "Montag",
+    "A_1": "Dienstag",
+    "A_2": "Mittwoch",
+    "A_3": "Donnerstag",
+    "A_4": "Freitag",
+    "A_5": "Samstag",
+    "A_6": "Sonntag"
   }
 }
diff --git a/aleksis/core/frontend/messages/en.json b/aleksis/core/frontend/messages/en.json
index ceda00c3994bccf4fc2c7b5e9cb83fe5ec2d00d9..a477736639405a1005f8799ce09b6c88f45a95de 100644
--- a/aleksis/core/frontend/messages/en.json
+++ b/aleksis/core/frontend/messages/en.json
@@ -81,6 +81,10 @@
     "confirm_deletion_multiple": "Are you sure you want to delete these items?",
     "delete": "Delete",
     "edit": "Edit",
+    "close": "Close",
+    "select_all": "Select all",
+    "copy": "Copy",
+    "copied": "Copied",
     "save": "Save",
     "search": "Search",
     "stop_editing": "Stop editing",
@@ -252,6 +256,19 @@
     "new_version_available": "A new version of the app is available",
     "update": "Update"
   },
+  "calendar": {
+    "menu_title": "Calendar",
+    "month": "Month",
+    "week": "Week",
+    "day": "Day",
+    "today": "Today",
+    "select": "Select calendars",
+    "ics_to_clipboard": "Copy link to calendar ICS to clipboard",
+    "cancelled": "Cancelled",
+    "download_ics": "Download ICS",
+    "my_calendars": "My Calendars",
+    "download_all": "Download all"
+  },
   "graphql": {
     "snackbar_error_message": "There was an error retrieving the page data. Please try again.",
     "snackbar_success_message": "The operation has been finished successfully."
diff --git a/aleksis/core/frontend/messages/ru.json b/aleksis/core/frontend/messages/ru.json
index 161a4efd7b2dafb90d0785a3a69b224dbe6827dd..ae08b69dc0fb154702bbceb2d87a2147497d5fd6 100644
--- a/aleksis/core/frontend/messages/ru.json
+++ b/aleksis/core/frontend/messages/ru.json
@@ -61,15 +61,26 @@
         "email": "Мы отправим Вам одноразовые коды на эл.почту.",
         "generator": "Вы генерируете одноразовые коды с помощью генератора кодов.",
         "sms": "Мы отправим Вам одноразовые коды на Ваш мобильный.",
-        "webauthn": "Вы используете ключ безопасности (как внешнее устройство или встроенное в Ваше переносное устройство)."
-      }
+        "webauthn": "Вы используете ключ безопасности (как внешнее устройство или встроенное в Ваше переносное устройство).",
+        "yubikey": "Вы используете YubiKey для создания одноразовых кодов."
+      },
+      "other_devices_description": "Если Ваш основное устройство аутентификации будет недоступно во время входа, Вы можете воспользоваться одним из этих устройств:",
+      "other_devices_title": "Другие устройства аутентификации",
+      "primary_device_description": "Во время входа в систему AlekSIS попросит Вас подтвердить вход с помощью следующего устройства. Если это устройство будет недоступно, Вы сможете воспользоваться резервным устройством.",
+      "primary_device_title": "Основное устройство для аутентификации",
+      "title": "Двухфакторная аутентификация"
     }
   },
   "actions": {
     "back": "Назад",
+    "cancel": "Отменить",
     "close": "Закрыть",
+    "confirm_deletion": "Вы действительно хотите удалить этот объект?",
+    "delete": "Удалить",
     "edit": "Редактировать",
-    "search": "Поиск"
+    "save": "Сохранить",
+    "search": "Поиск",
+    "stop_editing": "Завершить редактирование"
   },
   "administration": {
     "backend_admin": {
@@ -167,6 +178,17 @@
       "title": "Приложение OAuth2",
       "title_plural": "Приложения OAuth2"
     },
+    "authorized_application": {
+      "access_since": "Доступ с {date}",
+      "description": "У упомянутых сторонних приложений есть доступ к Вашей учётной записи. Доступ, в котором больше нет необходимости или которому больше не доверяете, можете отозвать в любой момент.",
+      "has_access_to": "Имеет доступ к:",
+      "menu_title": "Приложения третьих сторон",
+      "revoke": "Отозвать доступ",
+      "revoke_question": "Вы действительно хотите отозвать доступ для этого приложения?",
+      "subtitle": "Приложения третьих сторон с доступом к Вашему аккаунту",
+      "title": "Приложения третьих сторон",
+      "valid_until": "Действует до {date}"
+    },
     "authorized_token": {
       "menu_title": "Авторизованные приложения"
     }
@@ -219,5 +241,11 @@
     "dismiss": "Отказаться",
     "new_version_available": "Доступна новая версия программы",
     "update": "Обновить"
+  },
+  "status": {
+    "changes": "У Вас есть несохранённые изменения.",
+    "error": "Во время сохранения последних изменений возникла ошибка.",
+    "saved": "Все изменения сохранены.",
+    "updating": "Изменения синхронизуются."
   }
 }
diff --git a/aleksis/core/frontend/mixins/calendarFeedDetails.js b/aleksis/core/frontend/mixins/calendarFeedDetails.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f729ba18355f7742d9080ea0188e8f3ba73e193
--- /dev/null
+++ b/aleksis/core/frontend/mixins/calendarFeedDetails.js
@@ -0,0 +1,52 @@
+/**
+ * Mixin for use with adaptable components showing details for calendar feeds.
+ */
+const calendarFeedDetailsMixin = {
+  props: {
+    selectedElement: {
+      required: false,
+      default: null,
+    },
+    selectedEvent: {
+      required: true,
+      type: Object,
+    },
+    value: { type: Boolean, required: true },
+    withoutTime: {
+      required: false,
+      type: Boolean,
+      default: false,
+    },
+    withoutDescription: {
+      required: false,
+      type: Boolean,
+      default: false,
+    },
+    withoutBadge: {
+      required: false,
+      type: Boolean,
+      default: false,
+    },
+    color: {
+      required: false,
+      type: String,
+      default: null,
+    },
+    calendarType: {
+      required: true,
+      type: String,
+    },
+  },
+  computed: {
+    model: {
+      get() {
+        return this.value;
+      },
+      set(value) {
+        this.$emit("input", value);
+      },
+    },
+  },
+};
+
+export default calendarFeedDetailsMixin;
diff --git a/aleksis/core/frontend/mixins/calendarFeedEventBar.js b/aleksis/core/frontend/mixins/calendarFeedEventBar.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b6590b0f68e75090b200bd0aa0fcc747cfdef51
--- /dev/null
+++ b/aleksis/core/frontend/mixins/calendarFeedEventBar.js
@@ -0,0 +1,21 @@
+/**
+ * Mixin for use with adaptable components showing event bar for calendar feeds.
+ */
+const calendarFeedEventBarMixin = {
+  props: {
+    event: {
+      required: true,
+      type: Object,
+    },
+    eventParsed: {
+      required: true,
+      type: Object,
+    },
+    calendarType: {
+      required: true,
+      type: String,
+    },
+  },
+};
+
+export default calendarFeedEventBarMixin;
diff --git a/aleksis/core/frontend/routes.js b/aleksis/core/frontend/routes.js
index 84b3612f33510940b97dd57fec5370e49c768d4c..b14cb40e77c84230a1555b296eda94d245df9cfb 100644
--- a/aleksis/core/frontend/routes.js
+++ b/aleksis/core/frontend/routes.js
@@ -8,7 +8,7 @@
 // aleksisAppImporter is a virtual module defined in Vite config
 import { appObjects } from "aleksisAppImporter";
 
-import { notLoggedInValidator } from "./routeValidators";
+import { hasPersonValidator, notLoggedInValidator } from "./routeValidators";
 
 const routes = [
   {
@@ -1049,6 +1049,18 @@ const routes = [
     },
     name: "invitations.accept_invite",
   },
+  {
+    path: "/calendar/",
+    component: () => import("./components/calendar/CalendarOverview.vue"),
+    name: "core.calendar_overview",
+    meta: {
+      inMenu: true,
+      icon: "mdi-calendar",
+      titleKey: "calendar.menu_title",
+      toolbarTitle: "calendar.menu_title",
+      permission: "core.view_calendar_feed_rule",
+    },
+  },
 ];
 
 // This imports all known AlekSIS app entrypoints
diff --git a/aleksis/core/locale/ar/LC_MESSAGES/django.po b/aleksis/core/locale/ar/LC_MESSAGES/django.po
index 7c475008f800e2ab26d51154f5050eb6a6e1dc32..bdff10b2b611efda26d4f27b80141942c30b1fa3 100644
--- a/aleksis/core/locale/ar/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/ar/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+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"
@@ -42,8 +42,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 msgid "Groups"
 msgstr ""
@@ -102,172 +102,172 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr ""
 
@@ -291,697 +291,744 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr ""
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr ""
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+msgid "Start date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1530
+msgid "End date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1531
+msgid "Timezone"
+msgstr ""
+
+#: aleksis/core/models.py:1534
+msgid "Recurrences"
+msgstr ""
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+msgid "Calendar Event"
+msgstr ""
+
+#: aleksis/core/models.py:1644
+msgid "Calendar Events"
+msgstr ""
+
+#: aleksis/core/models.py:1670
+msgid "Birthdays"
+msgstr ""
+
+#: aleksis/core/models.py:1674
+msgid "{}'s birthday"
+msgstr ""
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
+#: aleksis/core/preferences.py:33
+msgid "Calendar"
+msgstr ""
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
@@ -1155,7 +1202,15 @@ msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
+msgid "Birthday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Activated calendars"
 msgstr ""
 
 #: aleksis/core/settings.py:551
@@ -1170,22 +1225,9 @@ msgstr ""
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
-msgstr ""
-
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
-#: aleksis/core/templates/core/announcement/list.html:22
-msgid "Actions"
-msgstr ""
-
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
@@ -1193,6 +1235,11 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
+#: aleksis/core/templates/core/announcement/list.html:22
+msgid "Actions"
+msgstr ""
+
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1263,6 +1310,13 @@ msgstr ""
 msgid "Your AlekSIS team"
 msgstr ""
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1495,6 +1549,12 @@ msgstr ""
 msgid "Recipients"
 msgstr ""
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr ""
+
 #: aleksis/core/templates/core/announcement/list.html:50
 msgid "There are no announcements."
 msgstr ""
@@ -2043,17 +2103,6 @@ msgstr ""
 msgid "Selected persons"
 msgstr ""
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-msgid "Create school term"
-msgstr ""
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr ""
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
@@ -2813,156 +2862,148 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr ""
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr ""
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1382
+#: aleksis/core/views.py:1352
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1465
+#: aleksis/core/views.py:1435
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr ""
diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
index 549daede5b7b831b5f13648b3caa45f69e391e1b..672dca25a0db9d61b2e3f1fbab98f31ee96203ad 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+0200\n"
 "PO-Revision-Date: 2023-05-09 18:45+0200\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-core/de/>\n"
@@ -42,8 +42,8 @@ msgstr "E-Mail-Adresse"
 msgid "Home and mobile phone"
 msgstr "Festnetz- und Mobilfunknummer"
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 msgid "Groups"
 msgstr "Gruppen"
@@ -102,187 +102,172 @@ msgstr "Berechtigung"
 msgid "Content type"
 msgstr "Inhaltstyp"
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr "Benutzer"
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr "Gruppe"
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr "Basisdaten"
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 msgid "Address"
 msgstr "Adresse"
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr "Kontaktdaten"
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr "Zusätzliche persönliche Daten"
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr "Neuer Benutzer"
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr "Neues Benutzerkonto erstellen"
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
-msgstr ""
-"Sie können keine neuen Benutzer erstellen, wenn Sie gleichzeitig einen "
-"existierenden Benutzer auswählen."
+msgstr "Sie können keine neuen Benutzer erstellen, wenn Sie gleichzeitig einen existierenden Benutzer auswählen."
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Dieser Benutzername wird bereits genutzt."
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr "Schuljahr"
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr "Allgemeine Daten"
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr "Personen"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr "Zusätzliche Daten"
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr "Foto"
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr "Datum"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr "Zeit"
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr "Von wann bis wann soll die Ankündigung angezeigt werden?"
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr "Wer soll die Ankündigung sehen?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr "Schreiben Sie ihre Ankündigung:"
 
-#: aleksis/core/forms.py:277
-msgid ""
-"You are not allowed to create announcements which are only valid in the past."
-msgstr ""
-"Sie dürfen keine Ankündigungen erstellen, die nur für die Vergangenheit "
-"gültig sind."
+#: aleksis/core/forms.py:287
+msgid "You are not allowed to create announcements which are only valid in the past."
+msgstr "Sie dürfen keine Ankündigungen erstellen, die nur für die Vergangenheit gültig sind."
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
-msgstr ""
-"Das Startdatum und die Startzeit müssen vor dem Enddatum und der Endzeit "
-"sein."
+msgstr "Das Startdatum und die Startzeit müssen vor dem Enddatum und der Endzeit sein."
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr "Sie benötigen mindestens einen Empfänger."
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr "Einladungscode"
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr "Bitte geben Sie Ihren Einladungscode ein."
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr "Vorname"
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr "Nachname"
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr "Eine Person nutzt diese E-Mail-Adresse"
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr "Wer soll die Berechtigung erhalten?"
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr "Auf was?"
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
-msgstr ""
-"Wählen Sie die Objekte aus, für welche die Berechtigung vergeben werden soll:"
+msgstr "Wählen Sie die Objekte aus, für welche die Berechtigung vergeben werden soll:"
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr "Vergebe die Berechtigung für alle Objekte"
 
-#: aleksis/core/forms.py:495
-msgid ""
-"You must select at least one group or person which should get the permission."
-msgstr ""
-"Sie müssen mindestens eine Gruppe oder Person auswählen, welche die "
-"Berechtigung erhalten soll."
+#: aleksis/core/forms.py:504
+msgid "You must select at least one group or person which should get the permission."
+msgstr "Sie müssen mindestens eine Gruppe oder Person auswählen, welche die Berechtigung erhalten soll."
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
-msgstr ""
-"Sie müssen die Berechtigung auf alle Objekte oder für spezifische Objekte "
-"vergeben."
+msgstr "Sie müssen die Berechtigung auf alle Objekte oder für spezifische Objekte vergeben."
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr "Adressdaten"
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 msgid "Account data"
 msgstr "Kontodaten"
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr "Passwort"
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr "Passwort wiederholen"
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr "Die ausgewählte Aktion existiert nicht."
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
-msgstr ""
-"Sie haben nicht die Berechtigung, {} auf alle ausgewählten Objekte "
-"auszuführen."
+msgstr "Sie haben nicht die Berechtigung, {} auf alle ausgewählten Objekte auszuführen."
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr "Keine gültige Auswahl."
 
@@ -306,706 +291,762 @@ msgstr "Kein Backup gefunden!"
 msgid "No backup result found!"
 msgstr "Kein Backupergebnis gefunden!"
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr "Zugeordnetes Schuljahr"
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr "Boolean (Ja/Nein)"
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr "Text (eine Zeile)"
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr "Datum und Uhrzeit"
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr "Dezimalzahl"
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr "Ganze Zahl"
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr "IP-Adresse"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Boolean oder leer (Ja/Nein/weder)"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr "Text (mehrzeilig)"
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr "URL / Link"
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr "Name"
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr "Startdatum"
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr "Enddatum"
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr "Das Startdatum muss vor dem Enddatum liegen."
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
-msgstr ""
-"Es gibt bereits ein Schuljahr für diesen Zeitraum oder einen Teilzeitraum."
+msgstr "Es gibt bereits ein Schuljahr für diesen Zeitraum oder einen Teilzeitraum."
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr "Schuljahre"
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr "Person"
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 msgid "Can view address"
 msgstr "Kann Adresse sehen"
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr "Kann Kontaktdetails sehen"
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr "Kann Foto sehen"
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr "Kann Avatar-Bild sehen"
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr "Kann Gruppen einer Person sehen"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr "Kann persönliche Daten sehen"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr "weiblich"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr "männlich"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr "andere"
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr "Verknüpfter Benutzer"
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr "Zusätzliche Namen"
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr "Kurzname"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr "Straße"
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr "Hausnummer"
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr "Postleitzahl"
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr "Ort"
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr "Festnetz"
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr "Handy"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr "Geburtsdatum"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr "Geburtsort"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr "Geschlecht"
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
-msgid ""
-"This is an official photo, used for official documents and for internal use "
-"cases."
-msgstr ""
-"Dies ist ein offizielles Foto, genutzt für offizielle Dokumente und interne "
-"Zwecke."
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
+msgid "This is an official photo, used for official documents and for internal use cases."
+msgstr "Dies ist ein offizielles Foto, genutzt für offizielle Dokumente und interne Zwecke."
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr "Bild/Avatar anzeigen"
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr "Dies ist ein Bild oder ein Avatar für die öffentliche Darstellung."
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr "Erziehungsberechtigte / Eltern"
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr "Primärgruppe"
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr "Beschreibung"
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr "Feldtitel"
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr "Feldtyp"
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr "Pflichtfeld"
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr "Hilfetext/Beschreibung"
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr "Zusätzliche Felder für Gruppen"
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr "Zusätzliche Felder für Gruppen"
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr "Kann Kindgruppen zu Gruppen zuordnen"
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr "Kann Statistiken über Gruppen sehen."
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr "Langname"
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr "Mitglieder"
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr "Leiter/-innen"
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr "Ãœbergeordnete Gruppen"
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr "Zusätzliche Felder"
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titel"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr "Anwendung"
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr "Aktivität"
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr "Aktivitäten"
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr "Absender"
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr "Empfänger"
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr "Link"
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Symbol"
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr "Benachrichtigung schicken am"
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr "Gelesen"
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr "Versandt"
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr "Benachrichtigung"
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr "Benachrichtigungen"
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr "Link zur detaillierten Ansicht"
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr "Datum und Uhrzeit des Anzeigestarts"
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr "Anzeigezeitraum"
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr "Ankündigung"
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Ankündigungen"
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr "Widget-Titel"
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr "Widget aktivieren"
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr "Widget ist kaputt"
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr "Größe auf Mobilgeräten"
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 px, 12 Spalten"
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr "Größe auf Tablets"
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr "> 600px, 12 Spalten"
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr "Größe auf Desktopgeräten"
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr "> 992 px, 12 Spalten"
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr "Größe auf großen Desktopgeräten"
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 px, 12 Spalten"
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr "Kann Standarddashboard bearbeiten"
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr "Dashboard-Widgets"
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr "Symbol-URL"
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr "Externer-Link-Widget"
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr "Externer-Link-Widgets"
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr "Inhalt"
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr "Statischer-Inhalt-Widget"
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr "Statischer-Inhalt-Widgets"
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr "Reihenfolge"
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr "Teil des Standarddashboards"
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr "Reihenfolge der Dashboard-Widgets"
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr "Reihenfolgen der Dashboard-Widgets"
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr "Menü-ID"
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr "Benutzerdefiniertes Menü"
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr "Benutzerdefinierte Menüs"
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr "Menü"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr "Benutzerdefiniertes Menüelement"
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr "Benutzerdefinierte Menüelemente"
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr "Titel des Typs"
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 msgid "Group type"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr "Gruppentypen"
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr "Kann Systemstatus sehen"
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr "Kann Daten verwalten"
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr "Kann sich verkleiden"
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr "Kann Suche benutzen"
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr "Kann Konfiguration ändern"
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr "Kann Einstellungen einer Person verändern"
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr "Kann Einstellungen einer Gruppe verändern"
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr "Kann die PDF-Generierung testen"
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr "Kann Personen einladen"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr "Zugehörige Datenprüfungsaufgabe"
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr "Problem gelöst"
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr "Benachrichtigung gesendet"
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr "Datenprüfungsergebnis"
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr "Datenprüfungsergebnisse"
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr "Kann Datenprüfungen ausführen"
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr "Kann Datenprüfungsprobleme lösen"
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr "Leiter"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr "Datei abgelaufen am"
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr "Generierte HTML-Datei"
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr "Generierte PDF-Datei"
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr "PDF-Datei"
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr "PDF-Dateien"
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr "Task-Ergebnis"
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr "Task-Benutzer"
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr "Zurück-URL"
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr "Fortschritt-Titel"
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr "Fehlernachricht"
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr "Erfolgsnachricht"
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr "URL, auf die bei Erfolg weitergeleitet wird"
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr "Titel des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr "URL des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr "Symbol des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr "Ergebnis abgerufen"
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr "Hintergrundaufgabe erfolgreich fertiggestellt"
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr "Die Hintergrundaufgabe '{}' wurde erfolgreich fertiggestellt."
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr "Hintergrundaufgabe fehlgeschlagen"
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr "Die Hintergrundaufgabe '{}' ist fehlgeschlagen."
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr "Hintergrundaufgabe"
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr "Task-Benutzer-Zuordnung"
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr "Task-Benutzer-Zuordnungen"
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr "Zusätzliche Attribute"
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr "Erlaubte Scopes, die ein Client anfordern kann"
 
-#: aleksis/core/models.py:1433
-msgid ""
-"This image will be shown as icon in the authorization flow. It should be "
-"squared."
-msgstr ""
-"Dieses Bild wird im Autorisierungs-Vorgang als Symbol angezeigt werden. Es "
-"sollte rechteckig sein."
+#: aleksis/core/models.py:1455
+msgid "This image will be shown as icon in the authorization flow. It should be squared."
+msgstr "Dieses Bild wird im Autorisierungs-Vorgang als Symbol angezeigt werden. Es sollte rechteckig sein."
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr "Kann Raum-Stundenplan sehen"
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr "Raum"
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr "Räume"
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+#, fuzzy
+#| msgid "Date and time"
+msgid "Start date and time"
+msgstr "Datum und Uhrzeit"
+
+#: aleksis/core/models.py:1530
+#, fuzzy
+#| msgid "Date and time"
+msgid "End date and time"
+msgstr "Datum und Uhrzeit"
+
+#: aleksis/core/models.py:1531
+#, fuzzy
+#| msgid "Time"
+msgid "Timezone"
+msgstr "Zeit"
+
+#: aleksis/core/models.py:1534
+#, fuzzy
+#| msgid "Preferences"
+msgid "Recurrences"
+msgstr "Einstellungen"
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+#, fuzzy
+#| msgid "Calendar Feeds"
+msgid "Calendar Event"
+msgstr "Kalender-Feeds"
+
+#: aleksis/core/models.py:1644
+#, fuzzy
+#| msgid "Calendar Feeds"
+msgid "Calendar Events"
+msgstr "Kalender-Feeds"
+
+#: aleksis/core/models.py:1670
+#, fuzzy
+#| msgid "Birthday Calendar"
+msgid "Birthdays"
+msgstr "Geburtstagskalender"
+
+#: aleksis/core/models.py:1674
+#, fuzzy
+#| msgid "%(name)s's birthday"
+msgid "{}'s birthday"
+msgstr "%(name)ss Geburtstag"
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr "Allgemein"
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Schule"
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr "Theme"
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr "E-Mail"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr "Fußbereich"
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr "Konten"
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr "Authentifizierung"
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr "Internationalisierung"
 
+#: aleksis/core/preferences.py:33
+#, fuzzy
+#| msgid "Calendar Feeds"
+msgid "Calendar"
+msgstr "Kalender-Feeds"
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr "Seitentitel"
@@ -1083,11 +1124,8 @@ msgid "Automatically create new persons for new users"
 msgstr "Erstelle automatisch neue Personen für neue Benutzer"
 
 #: aleksis/core/preferences.py:256
-msgid ""
-"Automatically link existing persons to new users by their e-mail address"
-msgstr ""
-"Verknüpfe existierende Personen automatisch mit neuen Personen anhand ihrer "
-"E-Mail-Adresse"
+msgid "Automatically link existing persons to new users by their e-mail address"
+msgstr "Verknüpfe existierende Personen automatisch mit neuen Personen anhand ihrer E-Mail-Adresse"
 
 #: aleksis/core/preferences.py:267
 msgid "Display name of the school"
@@ -1095,8 +1133,7 @@ msgstr "Sichtbarer Name der Schule"
 
 #: aleksis/core/preferences.py:278
 msgid "Official name of the school, e.g. as given by supervisory authority"
-msgstr ""
-"Offizieller Name der Schule, wie er z.B. von der Behörde vorgegeben ist"
+msgstr "Offizieller Name der Schule, wie er z.B. von der Behörde vorgegeben ist"
 
 #: aleksis/core/preferences.py:286
 msgid "Allow users to change their passwords"
@@ -1155,11 +1192,8 @@ msgid "Fields on person model which are editable by themselves."
 msgstr "Felder des Personen-Models welche von ihnen selbst editierbar sind."
 
 #: aleksis/core/preferences.py:424
-msgid ""
-"Editable fields on person model which should trigger a notification on change"
-msgstr ""
-"Editierbare Felder des Personen-Models welche eine Benachrichtigung für "
-"Änderungen auslösen soll"
+msgid "Editable fields on person model which should trigger a notification on change"
+msgstr "Editierbare Felder des Personen-Models welche eine Benachrichtigung für Änderungen auslösen soll"
 
 #: aleksis/core/preferences.py:438
 msgid "Contact for notification if a person changes their data"
@@ -1183,13 +1217,23 @@ msgstr "Automatisch das Dashboard und seine Widgets aktualisieren"
 
 #: aleksis/core/preferences.py:480
 msgid "Automatically update the dashboard and its widgets sitewide"
-msgstr ""
-"Automatisch das Dashboard und seine Widgets aktualisieren (auf der ganzen "
-"Seite)"
+msgstr "Automatisch das Dashboard und seine Widgets aktualisieren (auf der ganzen Seite)"
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
-msgstr "Land für das Einlesen von Telefonnummern"
+#, fuzzy
+#| msgid "Birthday Calendar"
+msgid "Birthday calendar feed color"
+msgstr "Geburtstagskalender"
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+#, fuzzy
+#| msgid "Birthday Calendar"
+msgid "Activated calendars"
+msgstr "Geburtstagskalender"
 
 #: aleksis/core/settings.py:551
 msgid "English"
@@ -1203,22 +1247,9 @@ msgstr "Deutsch"
 msgid "Ukrainian"
 msgstr "Ukrainisch"
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
-msgstr "Bearbeiten"
-
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
-#: aleksis/core/templates/core/announcement/list.html:22
-msgid "Actions"
-msgstr "Aktionen"
-
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
@@ -1226,6 +1257,11 @@ msgstr "Aktionen"
 msgid "Delete"
 msgstr "Löschen"
 
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
+#: aleksis/core/templates/core/announcement/list.html:22
+msgid "Actions"
+msgstr "Aktionen"
+
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1242,14 +1278,12 @@ msgstr ""
 #: aleksis/core/templates/403.html:19 aleksis/core/templates/404.html:16
 msgid ""
 "\n"
-"            If you think this is an error in AlekSIS, please contact your "
-"site\n"
+"            If you think this is an error in AlekSIS, please contact your site\n"
 "            administrators:\n"
 "          "
 msgstr ""
 "\n"
-"            Wenn Sie der Meinung sind, dass es sich um einen Fehler in "
-"AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
+"            Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
 "     Systemadministratoren:\n"
 "          "
 
@@ -1261,8 +1295,7 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Wenn Sie über einen Link auf einer externen Seite hierher "
-"gelangt sind,\n"
+"            Wenn Sie über einen Link auf einer externen Seite hierher gelangt sind,\n"
 "      ist es möglich, dass dieser veraltet war.\n"
 "          "
 
@@ -1277,14 +1310,12 @@ msgstr ""
 #: aleksis/core/templates/500.html:13
 msgid ""
 "\n"
-"            Your site administrators will automatically be notified about "
-"this\n"
+"            Your site administrators will automatically be notified about this\n"
 "            error. You can also contact them directly:\n"
 "          "
 msgstr ""
 "\n"
-"            Ihre Administratoren werden automatisch über diesen Fehler "
-"informiert.\n"
+"            Ihre Administratoren werden automatisch über diesen Fehler informiert.\n"
 "      Sie können diese auch direkt kontaktieren:\n"
 "          "
 
@@ -1321,6 +1352,13 @@ msgstr "Hallo!"
 msgid "Your AlekSIS team"
 msgstr "Ihr AlekSIS-Team"
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1330,21 +1368,13 @@ msgstr "Bestätigen"
 
 #: aleksis/core/templates/account/email_confirm.html:12
 #, python-format
-msgid ""
-"Please confirm that <a href=\"mailto:%(email)s\">%(email)s</a> is an e-mail "
-"address for user %(user_display)s."
-msgstr ""
-"Bitte bestätigen Sie, dass <a href=\"mailto:%(email)s\">%(email)s</a> eine E-"
-"Mail-Adresse für den Benutzer %(user_display)s ist."
+msgid "Please confirm that <a href=\"mailto:%(email)s\">%(email)s</a> is an e-mail address for user %(user_display)s."
+msgstr "Bitte bestätigen Sie, dass <a href=\"mailto:%(email)s\">%(email)s</a> eine E-Mail-Adresse für den Benutzer %(user_display)s ist."
 
 #: aleksis/core/templates/account/email_confirm.html:25
 #, python-format
-msgid ""
-"This e-mail confirmation link expired or is invalid. Please <a "
-"href=\"%(email_url)s\">issue a new e-mail confirmation request</a>."
-msgstr ""
-"Dieser E-Mail-Bestätigungslink ist abgelaufen oder nicht gültig. Bitte <a "
-"href=\"%(email_url)s\">fragen Sie eine neue E-Mail-Bestätigung an</a>."
+msgid "This e-mail confirmation link expired or is invalid. Please <a href=\"%(email_url)s\">issue a new e-mail confirmation request</a>."
+msgstr "Dieser E-Mail-Bestätigungslink ist abgelaufen oder nicht gültig. Bitte <a href=\"%(email_url)s\">fragen Sie eine neue E-Mail-Bestätigung an</a>."
 
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
@@ -1359,9 +1389,7 @@ msgstr "Passwort ändern"
 
 #: aleksis/core/templates/account/password_change.html:12
 msgid "Forgot your current password? Click here to reset it:"
-msgstr ""
-"Haben Sie Ihr aktuelles Passwort vergessen? Klicken Sie hier, um es "
-"zurückzusetzen:"
+msgstr "Haben Sie Ihr aktuelles Passwort vergessen? Klicken Sie hier, um es zurückzusetzen:"
 
 #: aleksis/core/templates/account/password_change.html:12
 msgid "Forgot Password?"
@@ -1380,13 +1408,11 @@ msgstr "Passwortänderung deaktiviert."
 msgid ""
 "\n"
 "            Users are not allowed to edit their own passwords. If you think\n"
-"            this is an error please contact one of your site "
-"administrators.\n"
+"            this is an error please contact one of your site administrators.\n"
 "          "
 msgstr ""
 "\n"
-"            Benutzer dürfen ihre eigenen Passwörter nicht ändern. Wenn Sie "
-"denken, \n"
+"            Benutzer dürfen ihre eigenen Passwörter nicht ändern. Wenn Sie denken, \n"
 "dass dies ein Fehler ist, kontaktieren Sie bitte einen der Administratoren:\n"
 "          "
 
@@ -1401,12 +1427,8 @@ msgid "Reset password"
 msgstr "Passwort zurücksetzen"
 
 #: aleksis/core/templates/account/password_reset.html:17
-msgid ""
-"Forgotten your password? Enter your e-mail address below, and we'll send you "
-"an e-mail allowing you to reset it."
-msgstr ""
-"Passwort vergessen? Geben Sie Ihre E-Mail-Adresse hier ein und wir werden "
-"Ihnen eine E-Mail zum Zurücksetzen des Passwortes schicken."
+msgid "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it."
+msgstr "Passwort vergessen? Geben Sie Ihre E-Mail-Adresse hier ein und wir werden Ihnen eine E-Mail zum Zurücksetzen des Passwortes schicken."
 
 #: aleksis/core/templates/account/password_reset.html:30
 msgid ""
@@ -1429,8 +1451,7 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Wir haben Ihnen eine E-Mail gesendet. Bitte kontaktieren Sie "
-"einen der Administratoren,\n"
+"            Wir haben Ihnen eine E-Mail gesendet. Bitte kontaktieren Sie einen der Administratoren,\n"
 "wenn Sie diese nicht innerhalb weniger Minuten erhalten.\n"
 "          "
 
@@ -1442,16 +1463,12 @@ msgstr "Ungültiges Token"
 #, python-format
 msgid ""
 "\n"
-"              The password reset link was invalid, possibly because it has "
-"already been used. Please request a <a href=\"%(passwd_reset_url)s\"\n"
+"              The password reset link was invalid, possibly because it has already been used. Please request a <a href=\"%(passwd_reset_url)s\"\n"
 "              class=\"blue-text text-lighten-2\">new password reset</a>.\n"
 "            "
 msgstr ""
 "\n"
-"              Der Link zum Zurücksetzen des Passwortes war falsch, "
-"wahrscheinlich, weil er bereits benutzt wurde. Bitte starten Sie eine neue "
-"Anfrage <a href=\"%(passwd_reset_url)s\"              class=\"blue-text text-"
-"lighten-2\">zum Zurücksetzen des Passwortes</a>.\n"
+"              Der Link zum Zurücksetzen des Passwortes war falsch, wahrscheinlich, weil er bereits benutzt wurde. Bitte starten Sie eine neue Anfrage <a href=\"%(passwd_reset_url)s\"              class=\"blue-text text-lighten-2\">zum Zurücksetzen des Passwortes</a>.\n"
 "            "
 
 #: aleksis/core/templates/account/password_reset_from_key.html:25
@@ -1500,11 +1517,8 @@ msgstr "Registrierung"
 
 #: aleksis/core/templates/account/signup.html:12
 #, python-format
-msgid ""
-"Already have an account? Then please <a href=\"%(login_url)s\">sign in</a>."
-msgstr ""
-"Haben Sie bereits ein Konto? Dann <a href=\"%(login_url)s\">melden Sie sich "
-"bitte an</a>."
+msgid "Already have an account? Then please <a href=\"%(login_url)s\">sign in</a>."
+msgstr "Haben Sie bereits ein Konto? Dann <a href=\"%(login_url)s\">melden Sie sich bitte an</a>."
 
 #: aleksis/core/templates/account/signup.html:22
 #: aleksis/core/templates/socialaccount/signup.html:23
@@ -1528,8 +1542,7 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Die Registrierung ist aktuell geschlossen. Wenn Sie denken, dass "
-"dies ein Fehler ist,\n"
+"            Die Registrierung ist aktuell geschlossen. Wenn Sie denken, dass dies ein Fehler ist,\n"
 " kontaktieren Sie bitte einen Ihrer Systemadministratoren.\n"
 "          "
 
@@ -1549,17 +1562,13 @@ msgstr "Verifizieren Sie Ihre E-Mail!"
 #: aleksis/core/templates/account/verification_sent.html:16
 msgid ""
 "\n"
-"            This part of the site requires us to verify that you are who you "
-"claim to be.\n"
-"            For this purpose, we require that you verify ownership of your e-"
-"mail address.\n"
+"            This part of the site requires us to verify that you are who you claim to be.\n"
+"            For this purpose, we require that you verify ownership of your e-mail address.\n"
 "          "
 msgstr ""
 "\n"
-"            Dieser Teil der Anwendung setzt voraus, dass wir verifizieren, "
-"dass Sie die Person sind, die sie vorgeben, zu sein.\n"
-"Zu diesem Zweck setzen wir voraus, dass Sie die Inhaberschaft Ihrer E-Mail-"
-"Adresse bestätigen.\n"
+"            Dieser Teil der Anwendung setzt voraus, dass wir verifizieren, dass Sie die Person sind, die sie vorgeben, zu sein.\n"
+"Zu diesem Zweck setzen wir voraus, dass Sie die Inhaberschaft Ihrer E-Mail-Adresse bestätigen.\n"
 "          "
 
 #: aleksis/core/templates/account/verification_sent.html:22
@@ -1573,8 +1582,7 @@ msgstr ""
 "\n"
 "            Wir haben Ihnen eine E-Mail zur Verifizierung geschickt.\n"
 "Bitte klicken Sie auf den Link in dieser E-Mail.\n"
-"Bitte kontaktieren Sie uns, wenn Sie diese nicht binnen weniger Minuten "
-"erhalten.\n"
+"Bitte kontaktieren Sie uns, wenn Sie diese nicht binnen weniger Minuten erhalten.\n"
 "          "
 
 #: aleksis/core/templates/core/additional_field/edit.html:6
@@ -1616,6 +1624,12 @@ msgstr "Gültig bis"
 msgid "Recipients"
 msgstr "Empfänger"
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr "Bearbeiten"
+
 #: aleksis/core/templates/core/announcement/list.html:50
 msgid "There are no announcements."
 msgstr "Es gibt aktuell keine Ankündigungen."
@@ -1643,8 +1657,7 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Es ist ein Problem dabei aufgetreten, das Widget \"%(title)s\" "
-"zu laden.\n"
+"            Es ist ein Problem dabei aufgetreten, das Widget \"%(title)s\" zu laden.\n"
 "Sie brauchen nichts weiter machen.\n"
 "          "
 
@@ -1750,42 +1763,29 @@ msgstr "Dashboard bearbeiten"
 #: aleksis/core/templates/core/edit_dashboard.html:24
 msgid ""
 "\n"
-"          On this page you can arrange your personal dashboard. You can drag "
-"any items from \"Available widgets\" to \"Your\n"
-"          Dashboard\" or change the order by moving the widgets. After you "
-"have finished, please don't forget to click on\n"
+"          On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n"
+"          Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n"
 "          \"Save\".\n"
 "        "
 msgstr ""
 "\n"
-"          Auf dieser Seite können Sie Ihr persönliches Dashboard "
-"zusammenstallen. Sie können beliebige Elemente von den \"Verfügbaren "
-"Widgets\" \n"
-"in \"Ihr Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die "
-"Widgets bewegen. Wenn Sie fertig sind, vergessen Sie bitte nicht, \n"
+"          Auf dieser Seite können Sie Ihr persönliches Dashboard zusammenstallen. Sie können beliebige Elemente von den \"Verfügbaren Widgets\" \n"
+"in \"Ihr Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die Widgets bewegen. Wenn Sie fertig sind, vergessen Sie bitte nicht, \n"
 "auf \"Speichern\" zu drücken.\n"
 "        "
 
 #: aleksis/core/templates/core/edit_dashboard.html:30
 msgid ""
 "\n"
-"          On this page you can arrange the default dashboard which is shown "
-"when a user doesn't arrange his own\n"
-"          dashboard. You can drag any items from \"Available widgets\" to "
-"\"Default Dashboard\" or change the order\n"
-"          by moving the widgets. After you have finished, please don't "
-"forget to click on \"Save\".\n"
+"          On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n"
+"          dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n"
+"          by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n"
 "        "
 msgstr ""
 "\n"
-"          Auf dieser Seite können Sie Ihr das Standard-Dashboard "
-"zusammenstallen, welches angezeigt wird, wenn ein Benutzer kein eigenes "
-"definiert. \n"
-"Sie können beliebige Elemente von den \"Verfügbaren Widgets\" in \"Standard-"
-"Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die Widgets "
-"bewegen. \n"
-"Wenn Sie fertig sind, vergessen Sie bitte nicht, auf \"Speichern\" zu "
-"drücken.\n"
+"          Auf dieser Seite können Sie Ihr das Standard-Dashboard zusammenstallen, welches angezeigt wird, wenn ein Benutzer kein eigenes definiert. \n"
+"Sie können beliebige Elemente von den \"Verfügbaren Widgets\" in \"Standard-Dashboard\" ziehen oder die Reihenfolge verändern, indem Sie die Widgets bewegen. \n"
+"Wenn Sie fertig sind, vergessen Sie bitte nicht, auf \"Speichern\" zu drücken.\n"
 "        "
 
 #: aleksis/core/templates/core/edit_dashboard.html:48
@@ -1808,15 +1808,12 @@ msgstr "Kindgruppen zu Gruppen zuordnen"
 #: aleksis/core/templates/core/group/child_groups.html:18
 msgid ""
 "\n"
-"          You can use this to assign child groups to groups. Please use the "
-"filters below to select groups you want to\n"
+"          You can use this to assign child groups to groups. Please use the filters below to select groups you want to\n"
 "          change and click \"Next\".\n"
 "        "
 msgstr ""
 "\n"
-"          Sie können diese Seite verwenden, um Kindgruppen zu Gruppen "
-"zuzuordnen. Bitte nutzen Sie die folgenden Filter, um die Gruppen "
-"auszuwählen, die Sie \n"
+"          Sie können diese Seite verwenden, um Kindgruppen zu Gruppen zuzuordnen. Bitte nutzen Sie die folgenden Filter, um die Gruppen auszuwählen, die Sie \n"
 "          ändern möchten und klicken auf \"Weiter\".\n"
 "        "
 
@@ -1857,18 +1854,14 @@ msgstr "Bitte seien Sie vorsichtig!"
 #: aleksis/core/templates/core/group/child_groups.html:79
 msgid ""
 "\n"
-"            If you click \"Back\" or \"Next\" the current group assignments "
-"are not saved.\n"
-"            If you click \"Save\", you will overwrite all existing child "
-"group relations for this group with what you\n"
+"            If you click \"Back\" or \"Next\" the current group assignments are not saved.\n"
+"            If you click \"Save\", you will overwrite all existing child group relations for this group with what you\n"
 "            selected on this page.\n"
 "          "
 msgstr ""
 "\n"
-"            Wenn Sie auf \"Zurück\" oder \"Weiter\" klicken, werden die "
-"aktuellen Gruppenzuordnungen nicht gespeichert.\n"
-"Wenn Sie auf \"Speichern\" klicken, werden alle existierenden Zuordnungen "
-"von Kindgruppen für diese Gruppe\n"
+"            Wenn Sie auf \"Zurück\" oder \"Weiter\" klicken, werden die aktuellen Gruppenzuordnungen nicht gespeichert.\n"
+"Wenn Sie auf \"Speichern\" klicken, werden alle existierenden Zuordnungen von Kindgruppen für diese Gruppe\n"
 "mit dem überschrieben, was Sie auf dieser Seite ausgewählt haben.\n"
 "          "
 
@@ -1966,16 +1959,13 @@ msgstr "Startseite"
 #: aleksis/core/templates/core/index.html:34
 msgid ""
 "\n"
-"        You didn't customise your dashboard so that you see the system "
-"default. Please click on \"Edit dashboard\" to\n"
+"        You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n"
 "        customise your personal dashboard.\n"
 "      "
 msgstr ""
 "\n"
-"        Sie haben Ihr Dashboard nicht angepasst, sodass Sie das Standard-"
-"Dashboard sehen.\n"
-"Bitte klicken Sie auf \"Dashboard bearbeiten\", um Ihr persönliches "
-"Dashboard anzupassen.\n"
+"        Sie haben Ihr Dashboard nicht angepasst, sodass Sie das Standard-Dashboard sehen.\n"
+"Bitte klicken Sie auf \"Dashboard bearbeiten\", um Ihr persönliches Dashboard anzupassen.\n"
 "      "
 
 #: aleksis/core/templates/core/index.html:44
@@ -2018,13 +2008,11 @@ msgstr "Wartungsmodus aktiviert"
 #: aleksis/core/templates/core/pages/system_status.html:28
 msgid ""
 "\n"
-"                  Only admin and visitors from internal IPs can access the "
-"site.\n"
+"                  Only admin and visitors from internal IPs can access the site.\n"
 "                "
 msgstr ""
 "\n"
-"                  Nur Administratoren und Besucher von internen IP-Adressen "
-"können die Seite aufrufen.\n"
+"                  Nur Administratoren und Besucher von internen IP-Adressen können die Seite aufrufen.\n"
 "                "
 
 #: aleksis/core/templates/core/pages/system_status.html:39
@@ -2042,13 +2030,11 @@ msgstr "Debug-Modus aktiviert"
 #: aleksis/core/templates/core/pages/system_status.html:53
 msgid ""
 "\n"
-"                The web server throws back debug information on errors. Do "
-"not use in production!\n"
+"                The web server throws back debug information on errors. Do not use in production!\n"
 "              "
 msgstr ""
 "\n"
-"                Der Server gibt Debug-Informationen bei Fehlern zurück. "
-"Nicht im Produktivbetrieb nutzen!\n"
+"                Der Server gibt Debug-Informationen bei Fehlern zurück. Nicht im Produktivbetrieb nutzen!\n"
 "              "
 
 #: aleksis/core/templates/core/pages/system_status.html:60
@@ -2058,13 +2044,11 @@ msgstr "Debug-Modus deaktivert"
 #: aleksis/core/templates/core/pages/system_status.html:62
 msgid ""
 "\n"
-"                Debug mode is disabled. Default error pages are displayed on "
-"errors.\n"
+"                Debug mode is disabled. Default error pages are displayed on errors.\n"
 "              "
 msgstr ""
 "\n"
-"                Debug-Modus ist deaktiviert. Standard-Fehlerseiten werden "
-"bei Fehlern angezeigt.\n"
+"                Debug-Modus ist deaktiviert. Standard-Fehlerseiten werden bei Fehlern angezeigt.\n"
 "              "
 
 #: aleksis/core/templates/core/pages/system_status.html:75
@@ -2114,13 +2098,11 @@ msgstr "PDF-Generierung testen"
 #: aleksis/core/templates/core/pages/test_pdf.html:14
 msgid ""
 "\n"
-"        This simple view can be used to ensure the correct function of the "
-"built-in PDF generation system.\n"
+"        This simple view can be used to ensure the correct function of the built-in PDF generation system.\n"
 "      "
 msgstr ""
 "\n"
-"        Diese einfache Seite kann genutzt werden, um die korrekte "
-"Funktionalität des eingebauten PDF-Generierungssystem zu testen.\n"
+"        Diese einfache Seite kann genutzt werden, um die korrekte Funktionalität des eingebauten PDF-Generierungssystem zu testen.\n"
 "      "
 
 #: aleksis/core/templates/core/partials/announcements.html:8
@@ -2178,13 +2160,11 @@ msgstr "Unbekannt"
 #: aleksis/core/templates/core/partials/splash_screen.html:11
 msgid ""
 "\n"
-"      This webbrowser doesn't support JavaScript, or its execution is "
-"blocked. Please use another browser to continue.\n"
+"      This webbrowser doesn't support JavaScript, or its execution is blocked. Please use another browser to continue.\n"
 "    "
 msgstr ""
 "\n"
-"      Dieser Webbrowser unterstützt kein JavaScript oder die Ausführung "
-"wurde blockiert. Bitte nutzen Sie einen anderen Browser, um fortzufahren.\n"
+"      Dieser Webbrowser unterstützt kein JavaScript oder die Ausführung wurde blockiert. Bitte nutzen Sie einen anderen Browser, um fortzufahren.\n"
 "    "
 
 #: aleksis/core/templates/core/partials/splash_screen.html:17
@@ -2194,8 +2174,7 @@ msgid ""
 "    "
 msgstr ""
 "\n"
-"      Der Wartungsmodus ist aktuell aktiviert. Bitte versuchen Sie es später "
-"erneut.\n"
+"      Der Wartungsmodus ist aktuell aktiviert. Bitte versuchen Sie es später erneut.\n"
 "    "
 
 #: aleksis/core/templates/core/perms/assign.html:12
@@ -2267,17 +2246,6 @@ msgstr "Personen filtern"
 msgid "Selected persons"
 msgstr "Ausgewählte Personen"
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-msgid "Create school term"
-msgstr "Schuljahr erstellen"
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr "Schuljahr bearbeiten"
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr "Einstellungen"
@@ -2308,12 +2276,8 @@ msgid "The invite feature is disabled."
 msgstr "Die Einladungsfunktion ist deaktiviert."
 
 #: aleksis/core/templates/invitations/disabled.html:15
-msgid ""
-"To enable it, switch on the corresponding checkbox in the authentication "
-"section of the "
-msgstr ""
-"Um sie zu aktivieren, nutzen Sie die Checkbox im Abschnitt "
-"\"Authentifikation\" der "
+msgid "To enable it, switch on the corresponding checkbox in the authentication section of the "
+msgstr "Um sie zu aktivieren, nutzen Sie die Checkbox im Abschnitt \"Authentifikation\" der "
 
 #: aleksis/core/templates/invitations/disabled.html:16
 msgid "site preferences page"
@@ -2461,17 +2425,13 @@ msgstr "Keine Verbindung zum Server."
 #: aleksis/core/templates/offline.html:14
 msgid ""
 "\n"
-"      This page is not available without a connection to the server. Please "
-"check your internet connection and try again.\n"
-"      If you are connected and the error persists, please contact the system "
-"administrators:\n"
+"      This page is not available without a connection to the server. Please check your internet connection and try again.\n"
+"      If you are connected and the error persists, please contact the system administrators:\n"
 "    "
 msgstr ""
 "\n"
-"      Diese Seite ist ohne eine Verbindung zum Server nicht verfügbar. Bitte "
-"prüfen Sie Ihre Internetverbindung und probieren Sie es erneut.\n"
-"      Wenn Sie verbunden sind und der Fehler weiterhin auftritt, "
-"kontaktieren Sie bitte die Systemadministratoren:\n"
+"      Diese Seite ist ohne eine Verbindung zum Server nicht verfügbar. Bitte prüfen Sie Ihre Internetverbindung und probieren Sie es erneut.\n"
+"      Wenn Sie verbunden sind und der Fehler weiterhin auftritt, kontaktieren Sie bitte die Systemadministratoren:\n"
 "    "
 
 #: aleksis/core/templates/search/search.html:8
@@ -2506,14 +2466,12 @@ msgstr "Anmeldung über Drittanbieter-Konto fehlgeschlagen."
 #: aleksis/core/templates/socialaccount/authentication_error.html:15
 msgid ""
 "\n"
-"            An error occurred while attempting to login via your third-party "
-"account.\n"
+"            An error occurred while attempting to login via your third-party account.\n"
 "            Please contact one of your site administrators.\n"
 "          "
 msgstr ""
 "\n"
-"            Beim dem Versuch, die Anmeldung über Ihr Drittanbieter-Konto "
-"durchzuführen, ist ein Fehler aufgetreten.\n"
+"            Beim dem Versuch, die Anmeldung über Ihr Drittanbieter-Konto durchzuführen, ist ein Fehler aufgetreten.\n"
 "            Kontaktieren Sie bitte einen Ihrer Systemadministratoren:\n"
 "          "
 
@@ -2528,8 +2486,7 @@ msgstr "Löschen"
 
 #: aleksis/core/templates/socialaccount/connections.html:34
 msgid "You currently have no third-party accounts connected to this account."
-msgstr ""
-"Sie haben aktuell keine Drittanbieter-Konten mit Ihrem Konto verbunden."
+msgstr "Sie haben aktuell keine Drittanbieter-Konten mit Ihrem Konto verbunden."
 
 #: aleksis/core/templates/socialaccount/connections.html:37
 msgid "Add a Third-party Account"
@@ -2538,15 +2495,12 @@ msgstr "Ein Drittanbieter-Konto hinzufügen"
 #: aleksis/core/templates/socialaccount/login.html:12
 #, python-format
 msgid "You are about to connect a new third party account from %(provider)s."
-msgstr ""
-"Sie sind dabei, ein neues Drittanbieterkonto von %(provider)s zu verbinden."
+msgstr "Sie sind dabei, ein neues Drittanbieterkonto von %(provider)s zu verbinden."
 
 #: aleksis/core/templates/socialaccount/login.html:23
 #, python-format
 msgid "You are about to sign in using a third party account from %(provider)s."
-msgstr ""
-"Sie sind dabei, sich mit einem Drittanbieterkonto von %(provider)s "
-"anzumelden."
+msgstr "Sie sind dabei, sich mit einem Drittanbieterkonto von %(provider)s anzumelden."
 
 #: aleksis/core/templates/socialaccount/login.html:28
 msgid "Continue"
@@ -2562,15 +2516,11 @@ msgstr "Login abgebrochen"
 #, python-format
 msgid ""
 "\n"
-"            You decided to cancel logging in to our site using one of your "
-"existing accounts. If this was a mistake, please proceed to <a "
-"href=\"%(login_url)s\">sign in</a>.\n"
+"            You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to <a href=\"%(login_url)s\">sign in</a>.\n"
 "          "
 msgstr ""
 "\n"
-"            Sie haben sich entschieden, die Anmeldung mit einem Ihrer "
-"bestehenden Konten bei uns abzubrechen. Wenn dies ein Fehler war, <a "
-"href=\"%(login_url)s\">fahren Sie bitte mit dem Login fort</a>.\n"
+"            Sie haben sich entschieden, die Anmeldung mit einem Ihrer bestehenden Konten bei uns abzubrechen. Wenn dies ein Fehler war, <a href=\"%(login_url)s\">fahren Sie bitte mit dem Login fort</a>.\n"
 "          "
 
 #: aleksis/core/templates/socialaccount/signup.html:12
@@ -2579,8 +2529,7 @@ msgid ""
 "You are about to use your %(provider_name)s account to login to\n"
 "        %(site_name)s. As a final step, please complete the following form:"
 msgstr ""
-"Sie sind dabei, Ihr %(provider_name)s-Konto zur Anmeldung bei %(site_name)s "
-"zu nutzen. \n"
+"Sie sind dabei, Ihr %(provider_name)s-Konto zur Anmeldung bei %(site_name)s zu nutzen. \n"
 "Als ein letzter Schritt vervollständigen Sie bitte das folgende Formular:"
 
 #: aleksis/core/templates/socialaccount/snippets/provider_list.html:12
@@ -2630,8 +2579,7 @@ msgstr "Celery-Task %(task_name)s fehlgeschlagen!"
 #: aleksis/core/templates/templated_email/celery_failure.email:7
 #, python-format
 msgid "the celery task %(task_name)s failed with following information:"
-msgstr ""
-"der Celery-Task %(task_name)s ist mit folgenden Informationen fehlgeschlagen:"
+msgstr "der Celery-Task %(task_name)s ist mit folgenden Informationen fehlgeschlagen:"
 
 #: aleksis/core/templates/templated_email/celery_failure.email:10
 #: aleksis/core/templates/templated_email/celery_failure.email:29
@@ -2661,8 +2609,7 @@ msgid ""
 "    "
 msgstr ""
 "\n"
-"      der Celery-Task  %(task_name)s ist mit folgenden Informationen "
-"fehlgeschlagen:\n"
+"      der Celery-Task  %(task_name)s ist mit folgenden Informationen fehlgeschlagen:\n"
 "    "
 
 #: aleksis/core/templates/templated_email/data_checks.email:3
@@ -2672,25 +2619,21 @@ msgstr "Das System hat einige neue Probleme mit Ihren Daten entdeckt."
 #: aleksis/core/templates/templated_email/data_checks.email:6
 msgid ""
 "the system detected some new problems with your data.\n"
-"Please take some time to inspect them and solve the issues or mark them as "
-"ignored."
+"Please take some time to inspect them and solve the issues or mark them as ignored."
 msgstr ""
 "das System hat einige neue Probleme mit Ihren Daten entdeckt.\n"
-"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder "
-"als ignoriert zu markieren."
+"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder als ignoriert zu markieren."
 
 #: aleksis/core/templates/templated_email/data_checks.email:15
 msgid ""
 "\n"
 "   the system detected some new problems with your data.\n"
-"   Please take some time to inspect them and solve the issues or mark them "
-"as ignored.\n"
+"   Please take some time to inspect them and solve the issues or mark them as ignored.\n"
 "  "
 msgstr ""
 "\n"
 "   das System hat einige neue Probleme mit Ihren Daten entdeckt.\n"
-"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder "
-"als ignoriert zu markieren.\n"
+"Bitte nehmen Sie sich etwas Zeit, diese zu überprüfen und sie zu lösen oder als ignoriert zu markieren.\n"
 "  "
 
 #: aleksis/core/templates/templated_email/data_checks.email:23
@@ -2715,12 +2658,8 @@ msgstr "Hallo %(person)s"
 #: aleksis/core/templates/templated_email/invitation.email:9
 #: aleksis/core/templates/templated_email/invitation.email:18
 #, python-format
-msgid ""
-"you have been invited to register on %(site)s. If you would like to accept "
-"this invitation, please click on the following link:"
-msgstr ""
-"Sie wurden eingeladen, sich auf %(site)s zu registrieren. Wenn Sie diese "
-"Einladung annehmen möchten, klicken Sie bitte auf folgenden Link:"
+msgid "you have been invited to register on %(site)s. If you would like to accept this invitation, please click on the following link:"
+msgstr "Sie wurden eingeladen, sich auf %(site)s zu registrieren. Wenn Sie diese Einladung annehmen möchten, klicken Sie bitte auf folgenden Link:"
 
 #: aleksis/core/templates/templated_email/notification.email:4
 msgid "New notification for"
@@ -2794,21 +2733,16 @@ msgid ""
 "\n"
 "        Backup tokens can be used when your primary and backup\n"
 "        phone numbers aren't available. The backup tokens below can be used\n"
-"        for login verification. If you've used up all your backup tokens, "
-"you\n"
-"        can generate a new set of backup tokens. Only the backup tokens "
-"shown\n"
+"        for login verification. If you've used up all your backup tokens, you\n"
+"        can generate a new set of backup tokens. Only the backup tokens shown\n"
 "        below will be valid.\n"
 "      "
 msgstr ""
 "\n"
 "        Backup-Token können genutzt werden, wenn Ihre primären und Backup-\n"
-"        Telefonnummern nicht verfügbar sind. Die Backup-Tokens unten können "
-"für\n"
-"        die Anmeldungsverifizierung genutzt werden. Wenn Sie alle Backup-"
-"Tokens genutzt haben,\n"
-"        müssen Sie neue generieren. Nur gültige Backup-Tokens werden "
-"angezeigt.\n"
+"        Telefonnummern nicht verfügbar sind. Die Backup-Tokens unten können für\n"
+"        die Anmeldungsverifizierung genutzt werden. Wenn Sie alle Backup-Tokens genutzt haben,\n"
+"        müssen Sie neue generieren. Nur gültige Backup-Tokens werden angezeigt.\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/backup_tokens.html:33
@@ -2858,9 +2792,7 @@ msgstr ""
 
 #: aleksis/core/templates/two_factor/core/login.html:51
 msgid "Please login with your account to use the external application."
-msgstr ""
-"Bitte melden Sie sich mit Ihrem Konto an, um die externe Anwendung zu "
-"benutzen."
+msgstr "Bitte melden Sie sich mit Ihrem Konto an, um die externe Anwendung zu benutzen."
 
 #: aleksis/core/templates/two_factor/core/login.html:58
 msgid "Please login to see this page."
@@ -2869,8 +2801,7 @@ msgstr "Bitte melden Sie sich an, um diese Seite zu sehen."
 #: aleksis/core/templates/two_factor/core/login.html:69
 msgid ""
 "\n"
-"                        We are calling your phone right now, please enter "
-"the\n"
+"                        We are calling your phone right now, please enter the\n"
 "                        digits you hear.\n"
 "                      "
 msgstr ""
@@ -2882,8 +2813,7 @@ msgstr ""
 #: aleksis/core/templates/two_factor/core/login.html:74
 msgid ""
 "\n"
-"                        We sent you a text message, please enter the code we "
-"sent.\n"
+"                        We sent you a text message, please enter the code we sent.\n"
 "                      "
 msgstr ""
 "\n"
@@ -2894,8 +2824,7 @@ msgstr ""
 #: aleksis/core/templates/two_factor/core/login.html:78
 msgid ""
 "\n"
-"                        We sent you an email, please enter the code we "
-"sent.\n"
+"                        We sent you an email, please enter the code we sent.\n"
 "                      "
 msgstr ""
 "\n"
@@ -2906,20 +2835,17 @@ msgstr ""
 #: aleksis/core/templates/two_factor/core/login.html:82
 msgid ""
 "\n"
-"                        Please use your Webauthn-compatible device to "
-"authenticate.\n"
+"                        Please use your Webauthn-compatible device to authenticate.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Bitte nutzen Sie Ihr Webauthn-kompatibles Gerät, um "
-"sich zu authentifizieren.\n"
+"                        Bitte nutzen Sie Ihr Webauthn-kompatibles Gerät, um sich zu authentifizieren.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:86
 msgid ""
 "\n"
-"                        Please enter the code generated by your code "
-"generator.\n"
+"                        Please enter the code generated by your code generator.\n"
 "                      "
 msgstr ""
 "\n"
@@ -2930,19 +2856,14 @@ msgstr ""
 #: aleksis/core/templates/two_factor/core/login.html:91
 msgid ""
 "\n"
-"                      Use this form for entering backup tokens for logging "
-"in.\n"
-"                      These tokens have been generated for you to print and "
-"keep safe. Please\n"
-"                      enter one of these backup tokens to login to your "
-"account.\n"
+"                      Use this form for entering backup tokens for logging in.\n"
+"                      These tokens have been generated for you to print and keep safe. Please\n"
+"                      enter one of these backup tokens to login to your account.\n"
 "                    "
 msgstr ""
 "\n"
-"                      Nutzen Sie dieses Formular um Ihre Backup-Tokens zum "
-"Anmelden einzugeben.\n"
-"                Diese Tokens wurden für Sie generiert, um diese gut "
-"aufzubewahren. Bitte\n"
+"                      Nutzen Sie dieses Formular um Ihre Backup-Tokens zum Anmelden einzugeben.\n"
+"                Diese Tokens wurden für Sie generiert, um diese gut aufzubewahren. Bitte\n"
 "                geben Sie einen dieser Tokens ein, um sich einzuloggen.\n"
 "                    "
 
@@ -2973,13 +2894,11 @@ msgstr "Zugriff verwehrt"
 #: aleksis/core/templates/two_factor/core/otp_required.html:10
 msgid ""
 "The page you requested enforces users to verify using\n"
-"          two-factor authentication for security reasons. You need to enable "
-"this\n"
+"          two-factor authentication for security reasons. You need to enable this\n"
 "          security feature in order to access this page."
 msgstr ""
 "Die von Ihnen gewünschte Seite erfordert aus Sicherheitsgründen\n"
-"          eine Verifizierung durch Zwei-Faktor-Authentifizierung. Sie müssen "
-"diese\n"
+"          eine Verifizierung durch Zwei-Faktor-Authentifizierung. Sie müssen diese\n"
 "          Sicherheitsfunktion aktivieren, um die Seite aufzurufen."
 
 #: aleksis/core/templates/two_factor/core/otp_required.html:16
@@ -3023,8 +2942,7 @@ msgid ""
 msgstr ""
 "\n"
 "        Sie sind dabei, Ihre Kontosicherheit auf das\n"
-"       nächste Level zu erhöhen. Bitte folgen Sie den Schritten im "
-"Assistenten, um eine\n"
+"       nächste Level zu erhöhen. Bitte folgen Sie den Schritten im Assistenten, um eine\n"
 "       Zwei-Faktor-Authentifizierungs-Methode zu Ihrem Konto hinzuzufügen.\n"
 "      "
 
@@ -3035,22 +2953,19 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Bitte wählen Sie aus, welche Authentifikationsmethode Sie nutzen "
-"wollen:\n"
+"        Bitte wählen Sie aus, welche Authentifikationsmethode Sie nutzen wollen:\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup.html:27
 msgid ""
 "\n"
 "        To start using a code generator, please use your\n"
-"        favourite two-factor authentication (TOTP) app to scan the QR code "
-"below.\n"
+"        favourite two-factor authentication (TOTP) app to scan the QR code below.\n"
 "        Then enter the token generated by the app.\n"
 "      "
 msgstr ""
 "\n"
-"        Um mit dem Code-Generator zu starten, benutzen Sie bitte Ihre "
-"favorisierte\n"
+"        Um mit dem Code-Generator zu starten, benutzen Sie bitte Ihre favorisierte\n"
 "App für Zwei-Faktor-Authentifizierung (TOTP), um diesen QR-Code zu scannen.\n"
 "Dann geben Sie den in der App angezeigten Code ein:\n"
 "      "
@@ -3064,8 +2979,7 @@ msgid ""
 msgstr ""
 "\n"
 "        Bitte geben Sie die Telefonnummer des Gerätes an,\n"
-"        an die die SMS-Nachrichten geschickt werden sollen. Diese Nummer "
-"wird im nächsten Schritt überprüft.\n"
+"        an die die SMS-Nachrichten geschickt werden sollen. Diese Nummer wird im nächsten Schritt überprüft.\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup.html:45
@@ -3087,20 +3001,17 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Wir haben Ihnen eine E-Mail geschickt, bitte geben Sie den Code ein, "
-"den wir geschickt haben.\n"
+"        Wir haben Ihnen eine E-Mail geschickt, bitte geben Sie den Code ein, den wir geschickt haben.\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup.html:60
 msgid ""
 "\n"
-"            We are calling your phone right now, please enter the digits you "
-"hear.\n"
+"            We are calling your phone right now, please enter the digits you hear.\n"
 "          "
 msgstr ""
 "\n"
-"            Wir rufen Ihr Telefon jetzt an, bitte geben Sie die Zahlen ein, "
-"die Sie hören.\n"
+"            Wir rufen Ihr Telefon jetzt an, bitte geben Sie die Zahlen ein, die Sie hören.\n"
 "          "
 
 #: aleksis/core/templates/two_factor/core/setup.html:66
@@ -3110,30 +3021,23 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Wir haben Ihnen eine SMS geschickt, bitte geben Sie den Code "
-"ein, den wir geschickt haben.\n"
+"            Wir haben Ihnen eine SMS geschickt, bitte geben Sie den Code ein, den wir geschickt haben.\n"
 "          "
 
 #: aleksis/core/templates/two_factor/core/setup.html:73
 msgid ""
 "\n"
-"          We've encountered an issue with the selected authentication "
-"method. Please\n"
-"          go back and verify that you entered your information correctly, "
-"try\n"
-"          again, or use a different authentication method instead. If the "
-"issue\n"
+"          We've encountered an issue with the selected authentication method. Please\n"
+"          go back and verify that you entered your information correctly, try\n"
+"          again, or use a different authentication method instead. If the issue\n"
 "          persists, contact the site administrator.\n"
 "        "
 msgstr ""
 "\n"
-"          Mit der ausgewählten Authentifizierungsmethode ist ein Fehler "
-"aufgetreten. \n"
-"Bitte gehen Sie zurück und überprüfen, dass Sie die Informationen korrekt "
-"eingegeben haben, versuchen Sie es erneut,\n"
+"          Mit der ausgewählten Authentifizierungsmethode ist ein Fehler aufgetreten. \n"
+"Bitte gehen Sie zurück und überprüfen, dass Sie die Informationen korrekt eingegeben haben, versuchen Sie es erneut,\n"
 "oder benutzen Sie stattdessen eine andere Authentifizierungsmethode. \n"
-"Wenn der Fehler bestehen bleibt, kontaktieren Sie bitte einen der "
-"Administratoren.\n"
+"Wenn der Fehler bestehen bleibt, kontaktieren Sie bitte einen der Administratoren.\n"
 "        "
 
 #: aleksis/core/templates/two_factor/core/setup.html:83
@@ -3158,13 +3062,11 @@ msgstr "Zwei-Faktor-Authentifizierung erfolgreich aktiviert"
 #: aleksis/core/templates/two_factor/core/setup_complete.html:13
 msgid ""
 "\n"
-"        Congratulations, you've successfully enabled two-factor "
-"authentication.\n"
+"        Congratulations, you've successfully enabled two-factor authentication.\n"
 "      "
 msgstr ""
 "\n"
-"        Gratulation, Sie haben die Zwei-Faktor-Authentifizierung erfolgreich "
-"aktiviert.\n"
+"        Gratulation, Sie haben die Zwei-Faktor-Authentifizierung erfolgreich aktiviert.\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup_complete.html:22
@@ -3181,17 +3083,14 @@ msgstr "Backup-Codes generieren"
 msgid ""
 "\n"
 "          However, it might happen that you don't have access to\n"
-"          your primary device. To enable account recovery, generate backup "
-"codes\n"
+"          your primary device. To enable account recovery, generate backup codes\n"
 "          or add other authentication methods.\n"
 "        "
 msgstr ""
 "\n"
-"          Es kann passieren, dass Sie keinen Zugriff auf Ihr primäres Gerät "
-"haben. \n"
+"          Es kann passieren, dass Sie keinen Zugriff auf Ihr primäres Gerät haben. \n"
 "Um die Konto-Wiederherstellung zu aktivieren,\n"
-"generieren Sie Backup-Codes oder fügen Sie eine andere Authentifizierungs-"
-"Methode hinzu.\n"
+"generieren Sie Backup-Codes oder fügen Sie eine andere Authentifizierungs-Methode hinzu.\n"
 "        "
 
 #: aleksis/core/templates/two_factor/core/setup_complete.html:48
@@ -3204,12 +3103,8 @@ msgid "Disable Two-Factor Authentication"
 msgstr "Zwei-Faktor-Authentifizierung deaktiveren"
 
 #: aleksis/core/templates/two_factor/profile/disable.html:12
-msgid ""
-"You are about to disable two-factor authentication. This weakens your "
-"account security, are you sure?"
-msgstr ""
-"Sie sind dabei, Zwei-Faktor-Authentifizierung zu deaktivieren. Das "
-"verschlechtert Ihre Kontosicherheit. Sind Sie sicher?"
+msgid "You are about to disable two-factor authentication. This weakens your account security, are you sure?"
+msgstr "Sie sind dabei, Zwei-Faktor-Authentifizierung zu deaktivieren. Das verschlechtert Ihre Kontosicherheit. Sind Sie sicher?"
 
 #: aleksis/core/templates/two_factor/profile/disable.html:26
 msgid "Disable"
@@ -3243,177 +3138,172 @@ msgstr "Es ist ein Fehler beim Generieren der PDF-Datei aufgetreten."
 msgid "Download PDF"
 msgstr "PDF herunterladen"
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr "Das Schuljahr wurde erstellt."
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr "Das Schuljahr wurde gespeichert."
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr "Die Untergruppen wurden gespeichert."
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr "Die Person wurde gespeichert."
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr "Die Gruppe wurde gespeichert."
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr "Der Wartungsmodus wurde erfolgreich angeschaltet."
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr "Der Wartungsmodus wurde erfolgreich ausgeschaltet."
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr "Die Ankündigung wurde gespeichert."
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr "Ankündigung wurde gelöscht."
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr "Das angeforderte Einstellungsregister existiert nicht"
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr "Die Einstellungen wurde gespeichert."
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr "Die Person wurde gelöscht."
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr "Die Gruppe wurde gelöscht."
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr "Das zusätzliche Feld wurde gespeichert."
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr "Das zusätzliche Feld wurde gelöscht."
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr "Der Gruppentyp wurde gespeichert."
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr "Der Gruppentyp wurde gelöscht."
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr "Fortschritt: Datenprüfungen ausführen"
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr "Datenprüfungen laufen …"
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr "Die Datenprüfungen wurden erfolgreich ausgeführt."
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr "Es gab ein Problem beim Ausführen der Datenprüfungen."
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr "Die Lösungsoption \"{solve_option_obj.verbose_name}\" "
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr "Die angeforderte Lösungsoption existiert nicht"
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr "Das Dashboard-Widget wurde gespeichert."
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr "Das Dashboard-Widget wurde erstellt."
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr "Das Dashboard-Widget wurde gelöscht."
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ihre Dashboardkonfiguration wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
-msgstr ""
-"Die Konfiguration des Standard-Dashboardes wurde erfolgreich gespeichert."
+msgstr "Die Konfiguration des Standard-Dashboardes wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
-msgstr ""
-"Die Einladung wurde erfolgreich erstellt. Der Einladungscode ist {code}"
+msgstr "Die Einladung wurde erfolgreich erstellt. Der Einladungscode ist {code}"
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr "Wir haben die Berechtigungen erfolgreich zugewiesen."
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr "Die globale Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr "Die globale Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr "Die Objekt-Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr "Die Objekt-Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1382
-msgid ""
-"The third-party account could not be disconnected because it is the only "
-"login method available."
-msgstr ""
-"Das Drittanbieter-Konto konnte nicht deaktiviert werden, weil es die einzige "
-"verfügbare Anmeldeoption ist."
+#: aleksis/core/views.py:1352
+msgid "The third-party account could not be disconnected because it is the only login method available."
+msgstr "Das Drittanbieter-Konto konnte nicht deaktiviert werden, weil es die einzige verfügbare Anmeldeoption ist."
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr "Das Drittanbieter-Konto wurde erfolgreich getrennt."
 
-#: aleksis/core/views.py:1465
-msgid ""
-"Person was invited successfully and an email with further instructions has "
-"been send to them."
-msgstr ""
-"Die Person wurde erfolgreich eingeladen und eine E-Mail mit weiteren "
-"Anweisungen wurde an sie verschickt."
+#: aleksis/core/views.py:1435
+msgid "Person was invited successfully and an email with further instructions has been send to them."
+msgstr "Die Person wurde erfolgreich eingeladen und eine E-Mail mit weiteren Anweisungen wurde an sie verschickt."
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr "Person wurde bereits eingeladen."
 
+#~ msgid "Country for phone number parsing"
+#~ msgstr "Land für das Einlesen von Telefonnummern"
+
+#~ msgid "Create school term"
+#~ msgstr "Schuljahr erstellen"
+
+#~ msgid "Edit school term"
+#~ msgstr "Schuljahr bearbeiten"
+
+#~ msgid "The school term has been created."
+#~ msgstr "Das Schuljahr wurde erstellt."
+
+#~ msgid "The school term has been saved."
+#~ msgstr "Das Schuljahr wurde gespeichert."
+
 #~ msgid "Revoke access"
 #~ msgstr "Zugriff zurückziehen"
 
 #~ msgid "Are you sure to revoke the access for this application?"
-#~ msgstr ""
-#~ "Sind Sie sicher, dass Sie den Zugriff für diese Anwendung zurückziehen "
-#~ "möchten?"
+#~ msgstr "Sind Sie sicher, dass Sie den Zugriff für diese Anwendung zurückziehen möchten?"
 
 #~ msgid "Revoke"
 #~ msgstr "Zurückziehen"
@@ -3436,14 +3326,11 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "            This page is currently unavailable. If this error persists, "
-#~ "contact your site administrators:\n"
+#~ "            This page is currently unavailable. If this error persists, contact your site administrators:\n"
 #~ "          "
 #~ msgstr ""
 #~ "\n"
-#~ "            Diese Seite ist aktuell nicht erreichbar. Wenn dieser Fehler "
-#~ "bestehen bleibt, kontaktieren Sie bitte einen Ihrer "
-#~ "Systemadministratoren:\n"
+#~ "            Diese Seite ist aktuell nicht erreichbar. Wenn dieser Fehler bestehen bleibt, kontaktieren Sie bitte einen Ihrer Systemadministratoren:\n"
 #~ "          "
 
 #~ msgid ""
@@ -3452,8 +3339,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ "          security."
 #~ msgstr ""
 #~ "Die Zwei-Faktor-Authentifizierung ist nicht für Ihren Account aktiviert.\n"
-#~ "          Aktivieren Sie Zwei-Faktor-Authentifizierung für eine "
-#~ "verbesserte\n"
+#~ "          Aktivieren Sie Zwei-Faktor-Authentifizierung für eine verbesserte\n"
 #~ "          Accountsicherheit."
 
 #~ msgid "Add Phone Number"
@@ -3480,8 +3366,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ "        send backup tokens to the phone numbers listed below."
 #~ msgstr ""
 #~ "Wenn Ihre primäre Methode nicht verfügbar ist, sind wir in der Lage,\n"
-#~ "        Backup-Tokens zu den unten aufgelisteten Telefonnummern zu "
-#~ "schicken."
+#~ "        Backup-Tokens zu den unten aufgelisteten Telefonnummern zu schicken."
 
 #~ msgid "Unregister"
 #~ msgstr "Abmelden"
@@ -3522,8 +3407,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgstr ""
 #~ "\n"
 #~ "        Wir raten Ihnen jedoch dringend davon ab.\n"
-#~ "        Sie können jedoch auch die Zwei-Faktor-Authentifizierung für Ihr "
-#~ "Konto deaktivieren.\n"
+#~ "        Sie können jedoch auch die Zwei-Faktor-Authentifizierung für Ihr Konto deaktivieren.\n"
 #~ "      "
 
 #~ msgid ""
@@ -3534,31 +3418,20 @@ msgstr "Person wurde bereits eingeladen."
 #~ "      "
 #~ msgstr ""
 #~ "\n"
-#~ "        Die Zwei-Faktor-Authentifizierung ist nicht für Ihren Account "
-#~ "aktiviert.\n"
-#~ "          Aktivieren Sie Zwei-Faktor-Authentifizierung für eine "
-#~ "verbesserte\n"
+#~ "        Die Zwei-Faktor-Authentifizierung ist nicht für Ihren Account aktiviert.\n"
+#~ "          Aktivieren Sie Zwei-Faktor-Authentifizierung für eine verbesserte\n"
 #~ "          Accountsicherheit.\n"
 #~ "      "
 
-#~ msgid "Birthday Calendar"
-#~ msgstr "Geburtstagskalender"
-
 #~ msgid "A Calendar of Birthdays"
 #~ msgstr "Ein Kalender mit Geburtstagen"
 
-#, python-format
-#~ msgid "%(name)s's birthday"
-#~ msgstr "%(name)ss Geburtstag"
-
 #, python-format
 #~ msgid "%(name)s was born on %(birthday)s"
 #~ msgstr "%(name)s wurde am %(birthday)s geboren"
 
 #~ msgid "You must grant the permission to all objects and/or to some objects."
-#~ msgstr ""
-#~ "Sie müssen die Berechtigung auf alle Objekte und/oder für einige Objekte "
-#~ "vergeben."
+#~ msgstr "Sie müssen die Berechtigung auf alle Objekte und/oder für einige Objekte vergeben."
 
 #~ msgid "Dashboard"
 #~ msgstr "Dashboard"
@@ -3596,9 +3469,6 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgid "Third-party accounts"
 #~ msgstr "Drittanbieter-Konten"
 
-#~ msgid "Calendar Feeds"
-#~ msgstr "Kalender-Feeds"
-
 #~ msgid "Logout"
 #~ msgstr "Abmelden"
 
@@ -3649,14 +3519,8 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgid "ICal Feeds"
 #~ msgstr "iCal-Feeds"
 
-#~ msgid ""
-#~ "These are URLs for different Calendar Feeds in the iCal (.ics) format. "
-#~ "You can create as many as you want and import them in your calendar "
-#~ "software."
-#~ msgstr ""
-#~ "Dies sind URLs für verschiedene Kalender-Feeds im iCal-Format (.ics). Sie "
-#~ "können so viele erstellen, wie sie wollen, und in Ihre Kalender-Software "
-#~ "importieren."
+#~ msgid "These are URLs for different Calendar Feeds in the iCal (.ics) format. You can create as many as you want and import them in your calendar software."
+#~ msgstr "Dies sind URLs für verschiedene Kalender-Feeds im iCal-Format (.ics). Sie können so viele erstellen, wie sie wollen, und in Ihre Kalender-Software importieren."
 
 #~ msgid "Your iCal URLs"
 #~ msgstr "Deine iCal-URLs"
@@ -3681,30 +3545,24 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "              This platform is powered by AlekSIS®, a web-based school "
-#~ "information system (SIS) which can be used\n"
-#~ "              to manage and/or publish organisational artifacts of "
-#~ "educational institutions. AlekSIS is free software and\n"
+#~ "              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
+#~ "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 #~ "              can be used by anyone.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Diese Plattform wird mit AlekSIS®, einem webbasierten "
-#~ "Schulinformationssystem (SIS), \n"
-#~ "welches für die Verwaltung und/oder Veröffentlichung von "
-#~ "Bildungseinrichtungen verwendet werden kann.\n"
+#~ "              Diese Plattform wird mit AlekSIS®, einem webbasierten Schulinformationssystem (SIS), \n"
+#~ "welches für die Verwaltung und/oder Veröffentlichung von Bildungseinrichtungen verwendet werden kann.\n"
 #~ "AlekSIS ist freie Software und kann von jedem benutzt werden.\n"
 #~ "            "
 
 #~ msgid ""
 #~ "\n"
-#~ "              AlekSIS® is a registered trademark of the AlekSIS open "
-#~ "source project, represented by Teckids e.V.\n"
+#~ "              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              AlekSIS® ist eine eingetragene Wortmarke des Open-Source-"
-#~ "Projektes AlekSIS, vertreten durch den Teckids e.V.\n"
+#~ "              AlekSIS® ist eine eingetragene Wortmarke des Open-Source-Projektes AlekSIS, vertreten durch den Teckids e.V.\n"
 #~ "            "
 
 #~ msgid "Website of AlekSIS"
@@ -3718,18 +3576,14 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "              The core and the official apps of AlekSIS are licenced "
-#~ "under the EUPL, version 1.2 or later. For licence\n"
-#~ "              information from third-party apps, if installed, refer to "
-#~ "the respective components below. The\n"
+#~ "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
+#~ "              information from third-party apps, if installed, refer to the respective components below. The\n"
 #~ "              licences are marked like this:\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Der Core und die offiziellen Apps von AlekSIS sind unter "
-#~ "der EUPL, Version 1.2 oder später, lizenziert. Für Lizenzinformationen\n"
-#~ "zu Apps von Drittanbietern, wenn installiert, siehe direkt bei der "
-#~ "jeweiligen App weiter unten auf dieser Seite. Die Lizenzen\n"
+#~ "              Der Core und die offiziellen Apps von AlekSIS sind unter der EUPL, Version 1.2 oder später, lizenziert. Für Lizenzinformationen\n"
+#~ "zu Apps von Drittanbietern, wenn installiert, siehe direkt bei der jeweiligen App weiter unten auf dieser Seite. Die Lizenzen\n"
 #~ "sind wie folgt markiert:\n"
 #~ "            "
 
@@ -3747,24 +3601,20 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "              Without activated JavaScript the progress status can't be "
-#~ "updated.\n"
+#~ "              Without activated JavaScript the progress status can't be updated.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Ohne aktiviertes JavaScript kann der Fortschritt leider "
-#~ "nicht aktualisiert werden.\n"
+#~ "              Ohne aktiviertes JavaScript kann der Fortschritt leider nicht aktualisiert werden.\n"
 #~ "            "
 
 #~ msgid ""
 #~ "\n"
-#~ "                Only admin and visitors from internal IPs can access "
-#~ "thesite.\n"
+#~ "                Only admin and visitors from internal IPs can access thesite.\n"
 #~ "              "
 #~ msgstr ""
 #~ "\n"
-#~ "                Nur Administratoren und Besucher von internen IP-Adressen "
-#~ "können die Seite aufrufen.\n"
+#~ "                Nur Administratoren und Besucher von internen IP-Adressen können die Seite aufrufen.\n"
 #~ "              "
 
 #~ msgid "Language"
@@ -3775,14 +3625,12 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "            Your administrator account is not linked to any person. "
-#~ "Therefore,\n"
+#~ "            Your administrator account is not linked to any person. Therefore,\n"
 #~ "            a dummy person has been linked to your account.\n"
 #~ "          "
 #~ msgstr ""
 #~ "\n"
-#~ "            Ihr Administratorenkonto ist mit keiner Person verknüpft. "
-#~ "Deshalb\n"
+#~ "            Ihr Administratorenkonto ist mit keiner Person verknüpft. Deshalb\n"
 #~ "            wurde Ihr Konto mit einer Dummyperson verknüpft.\n"
 #~ "          "
 
@@ -3794,10 +3642,8 @@ msgstr "Person wurde bereits eingeladen."
 #~ "          "
 #~ msgstr ""
 #~ "\n"
-#~ "            Ihr Benutzerkonto ist nicht mit einer Person verknüpft. Das "
-#~ "bedeutet, dass Sie\n"
-#~ "        keine schulbezogenen Informationen aufrufen können. Bitte wenden "
-#~ "Sie sich an\n"
+#~ "            Ihr Benutzerkonto ist nicht mit einer Person verknüpft. Das bedeutet, dass Sie\n"
+#~ "        keine schulbezogenen Informationen aufrufen können. Bitte wenden Sie sich an\n"
 #~ "        die Verwaltenden von AlekSIS an Ihrer Schule.\n"
 #~ "          "
 
@@ -3820,27 +3666,21 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgstr "Erfolg!"
 
 #~ msgid "Please return to your application and enter this code:"
-#~ msgstr ""
-#~ "Bitte gehen Sie zurück in Ihre Anwendung und geben Sie diesen Code ein:"
+#~ msgstr "Bitte gehen Sie zurück in Ihre Anwendung und geben Sie diesen Code ein:"
 
 #~ msgid "No internet connection."
 #~ msgstr "Keine Internetverbindung."
 
 #~ msgid ""
 #~ "\n"
-#~ "      There was an error accessing this page. You probably don't have an "
-#~ "internet connection. Check to see if your WiFi\n"
-#~ "      or mobile data is turned on and try again. If you think you are "
-#~ "connected, please contact the system\n"
+#~ "      There was an error accessing this page. You probably don't have an internet connection. Check to see if your WiFi\n"
+#~ "      or mobile data is turned on and try again. If you think you are connected, please contact the system\n"
 #~ "      administrators:\n"
 #~ "    "
 #~ msgstr ""
 #~ "\n"
-#~ "      Es ist ein Fehler beim Aufrufen der Seite aufgetreten. Eventuell "
-#~ "haben Sie keine Internetverbindung. Bitte prüfen Sie, ob WLAN oder mobile "
-#~ "Daten aktiv sind, \n"
-#~ "      und probieren Sie es erneut. Wenn Sie der Meinung sind, dass Sie "
-#~ "mit dem Internet verbunden sind, kontaktieren Sie bitte einen Ihrer \n"
+#~ "      Es ist ein Fehler beim Aufrufen der Seite aufgetreten. Eventuell haben Sie keine Internetverbindung. Bitte prüfen Sie, ob WLAN oder mobile Daten aktiv sind, \n"
+#~ "      und probieren Sie es erneut. Wenn Sie der Meinung sind, dass Sie mit dem Internet verbunden sind, kontaktieren Sie bitte einen Ihrer \n"
 #~ "      Systemadministratoren:\n"
 #~ "    "
 
@@ -3936,8 +3776,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ "   <li>ID des Tasks: %(task_id)s</li>\n"
 #~ "   <li>Aufgetretene Exception: %(exception)s</li>\n"
 #~ "   <li>Argumente, mit denen der Task aufgerufen wurde: %(args)s</li>\n"
-#~ "   <li>Keyword-Argumente, mit denen der Task aufgerufen wurde: "
-#~ "%(kwargs)s</li>\n"
+#~ "   <li>Keyword-Argumente, mit denen der Task aufgerufen wurde: %(kwargs)s</li>\n"
 #~ "   <li>Stacktrace: %(traceback)s</li>\n"
 #~ " </ul>\n"
 #~ " "
@@ -3956,12 +3795,8 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgstr "Zustimmungen"
 
 #, python-brace-format
-#~ msgid ""
-#~ "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree "
-#~ "with them."
-#~ msgstr ""
-#~ "Ich habe die <a href='{privacy_policy}'>Datenschutzerklärung</a> gelesen "
-#~ "und stimme ihr zu."
+#~ msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
+#~ msgstr "Ich habe die <a href='{privacy_policy}'>Datenschutzerklärung</a> gelesen und stimme ihr zu."
 
 #~ msgid "You must type the same password each time."
 #~ msgstr "Sie müssen zweimal das gleiche Passwort eingeben."
@@ -3970,12 +3805,8 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgstr "Verkleidung"
 
 #, python-format
-#~ msgid ""
-#~ "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change "
-#~ "your e-mail address</a>"
-#~ msgstr ""
-#~ "<strong>Hinweis:</strong> Sie können immer noch <a "
-#~ "href=\"%(email_url)s\"> Ihre E-Mail-Adresse ändern</a>"
+#~ msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
+#~ msgstr "<strong>Hinweis:</strong> Sie können immer noch <a href=\"%(email_url)s\"> Ihre E-Mail-Adresse ändern</a>"
 
 #~ msgid "Impress"
 #~ msgstr "Impressum"
@@ -3997,9 +3828,7 @@ msgstr "Person wurde bereits eingeladen."
 
 #, python-format
 #~ msgid "Are you sure to delete the application %(application_name)s?"
-#~ msgstr ""
-#~ "Sind Sie sicher, dass Sie die Anwendung %(application_name)s löschen "
-#~ "möchten?"
+#~ msgstr "Sind Sie sicher, dass Sie die Anwendung %(application_name)s löschen möchten?"
 
 #~ msgid "Authorization Grant Type"
 #~ msgstr "Authorization Grant-Typ"
@@ -4024,23 +3853,17 @@ msgstr "Person wurde bereits eingeladen."
 
 #~ msgid ""
 #~ "\n"
-#~ "        You can use this form to assign user accounts to persons. Use "
-#~ "the\n"
-#~ "        dropdowns to select existing accounts; use the text fields to "
-#~ "create new\n"
-#~ "        accounts on-the-fly. The latter will create a new account with "
-#~ "the\n"
+#~ "        You can use this form to assign user accounts to persons. Use the\n"
+#~ "        dropdowns to select existing accounts; use the text fields to create new\n"
+#~ "        accounts on-the-fly. The latter will create a new account with the\n"
 #~ "        entered username and copy all other details from the person.\n"
 #~ "      "
 #~ msgstr ""
 #~ "\n"
-#~ "        Sie können dieses Formular nutzen, um Benutzerkonten Personen "
-#~ "zuzuweisen. Nutzen Sie das\n"
-#~ "    Auswahlfeld um ein existierendes Benutzerkonto auszuwählen; nutzen "
-#~ "Sie das Textfeld, um einen neuen Benutzer zu\n"
+#~ "        Sie können dieses Formular nutzen, um Benutzerkonten Personen zuzuweisen. Nutzen Sie das\n"
+#~ "    Auswahlfeld um ein existierendes Benutzerkonto auszuwählen; nutzen Sie das Textfeld, um einen neuen Benutzer zu\n"
 #~ "    erstellen. Letzteres erstellt ein neues Benutzerkonto mit dem\n"
-#~ "    eingegebenen Benutzernamen und kopiert alle anderen Daten der "
-#~ "Person.\n"
+#~ "    eingegebenen Benutzernamen und kopiert alle anderen Daten der Person.\n"
 #~ "      "
 
 #~ msgid "Existing account"
@@ -4066,8 +3889,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ "We sent you a text message, please enter the tokens we\n"
 #~ "              sent."
 #~ msgstr ""
-#~ "Wir haben Ihnen eine Textnachricht geschickt. Bitte geben Sie die Tokens "
-#~ "ein,\n"
+#~ "Wir haben Ihnen eine Textnachricht geschickt. Bitte geben Sie die Tokens ein,\n"
 #~ "              die wir Ihnen geschickt haben."
 
 #~ msgid "French"
@@ -4076,12 +3898,8 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgid "Norwegian (bokmål)"
 #~ msgstr "Norwegisch (bokmål)"
 
-#~ msgid ""
-#~ "The data check has been started. Please note that it may take a while "
-#~ "before you are able to fetch the data on this page."
-#~ msgstr ""
-#~ "Die Datenüberprüfung wurde gestartet. Bitte beachten Sie, dass es eine "
-#~ "Weile dauern kann, bevor Sie auf dieser Seite Ergebnisse abrufen können."
+#~ msgid "The data check has been started. Please note that it may take a while before you are able to fetch the data on this page."
+#~ msgstr "Die Datenüberprüfung wurde gestartet. Bitte beachten Sie, dass es eine Weile dauern kann, bevor Sie auf dieser Seite Ergebnisse abrufen können."
 
 #~ msgid "The data check has finished."
 #~ msgstr "Die Datenüberprüfung wurde beendet."
@@ -4135,8 +3953,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ "     "
 #~ msgstr ""
 #~ "\n"
-#~ "      Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS "
-#~ "handelt, kontaktieren Sie bitte einen Ihrer\n"
+#~ "      Wenn Sie der Meinung sind, dass es sich um einen Fehler in AlekSIS handelt, kontaktieren Sie bitte einen Ihrer\n"
 #~ "     Systemadministratoren:\n"
 #~ "     "
 
@@ -4210,9 +4027,7 @@ msgstr "Person wurde bereits eingeladen."
 #~ msgstr "Markdown-Anleitung (Hilfe)"
 
 #~ msgid "You are not allowed to mark notifications from other users as read!"
-#~ msgstr ""
-#~ "Es ist Ihnen nicht erlaubt, Benachrichtigungen von anderen Benutzern als "
-#~ "gelesen zu markieren!"
+#~ msgstr "Es ist Ihnen nicht erlaubt, Benachrichtigungen von anderen Benutzern als gelesen zu markieren!"
 
 #, fuzzy
 #~ msgid "School name"
diff --git a/aleksis/core/locale/fr/LC_MESSAGES/django.po b/aleksis/core/locale/fr/LC_MESSAGES/django.po
index 2b3c3874af650baf3f812cf740ee2a3f1a9af29e..d1852c66e3212133747a28ce3c53e1fd98e676ce 100644
--- a/aleksis/core/locale/fr/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+0200\n"
 "PO-Revision-Date: 2021-06-16 12:00+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis/fr/>\n"
@@ -44,8 +44,8 @@ msgstr "Détails de contact"
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 #, fuzzy
 #| msgid "Group"
@@ -108,184 +108,184 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr "groupe"
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 #, fuzzy
 #| msgid "Contact details"
 msgid "Contact data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 #, fuzzy
 #| msgid "Contact details"
 msgid "Advanced personal data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Cet nom est deja en utilisation."
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 #, fuzzy
 #| msgid "Contact details"
 msgid "Common data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 #, fuzzy
 #| msgid "Person"
 msgid "Persons"
 msgstr "Personne"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr "Date"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr "Prénom"
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr "Nom de famille"
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 #, fuzzy
 #| msgid "Contact details"
 msgid "Account data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr ""
 
@@ -309,498 +309,497 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 #, fuzzy
 #| msgid "Contact details"
 msgid "Start date"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr "Personne"
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view address"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view photo"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view avatar image"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view persons groups"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view personal details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 #, fuzzy
 #| msgid "First name"
 msgid "Short name"
 msgstr "Prénom"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr "Sexe"
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr "Description"
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr ""
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 #, fuzzy
 #| msgid "Description"
 msgid "Help text / description"
 msgstr "Description"
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view statistics about group."
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Nom de famille"
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 #, fuzzy
@@ -808,248 +807,298 @@ msgstr "Groupe"
 msgid "Group types"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view system status"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can impersonate"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can invite persons"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 #, fuzzy
 #| msgid "Contact details"
 msgid "E-Mail address"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 #, fuzzy
 #| msgid "Owners"
 msgid "Owner"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button title"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button URL"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button icon"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional attributes"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view room timetable"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+#, fuzzy
+#| msgid "Contact details"
+msgid "Start date and time"
+msgstr "Détails de contact"
+
+#: aleksis/core/models.py:1530
+msgid "End date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1531
+msgid "Timezone"
+msgstr ""
+
+#: aleksis/core/models.py:1534
+msgid "Recurrences"
+msgstr ""
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+msgid "Calendar Event"
+msgstr ""
+
+#: aleksis/core/models.py:1644
+msgid "Calendar Events"
+msgstr ""
+
+#: aleksis/core/models.py:1670
+msgid "Birthdays"
+msgstr ""
+
+#: aleksis/core/models.py:1674
+msgid "{}'s birthday"
+msgstr ""
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 #, fuzzy
 #| msgid "Contact details"
 msgid "Accounts"
 msgstr "Détails de contact"
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
+#: aleksis/core/preferences.py:33
+msgid "Calendar"
+msgstr ""
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
@@ -1231,7 +1280,15 @@ msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
+msgid "Birthday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Activated calendars"
 msgstr ""
 
 #: aleksis/core/settings.py:551
@@ -1246,22 +1303,9 @@ msgstr ""
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
-msgstr ""
-
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
-#: aleksis/core/templates/core/announcement/list.html:22
-msgid "Actions"
-msgstr ""
-
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
@@ -1269,6 +1313,11 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
+#: aleksis/core/templates/core/announcement/list.html:22
+msgid "Actions"
+msgstr ""
+
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1339,6 +1388,13 @@ msgstr ""
 msgid "Your AlekSIS team"
 msgstr ""
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1571,6 +1627,12 @@ msgstr ""
 msgid "Recipients"
 msgstr ""
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr ""
+
 #: aleksis/core/templates/core/announcement/list.html:50
 msgid "There are no announcements."
 msgstr ""
@@ -2135,17 +2197,6 @@ msgstr ""
 msgid "Selected persons"
 msgstr ""
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-msgid "Create school term"
-msgstr ""
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr ""
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
@@ -2921,157 +2972,149 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr ""
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr ""
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1382
+#: aleksis/core/views.py:1352
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1465
+#: aleksis/core/views.py:1435
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 #, fuzzy
 #| msgid "This username is already in use."
 msgid "Person was already invited."
diff --git a/aleksis/core/locale/la/LC_MESSAGES/django.po b/aleksis/core/locale/la/LC_MESSAGES/django.po
index 1b6172ee3bd421884b89c300b56ff44c0edd5683..6289dae461dda33a7e1a1c07aebec1ca2bc6cac8 100644
--- a/aleksis/core/locale/la/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/la/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+0200\n"
 "PO-Revision-Date: 2020-12-19 12:57+0000\n"
 "Last-Translator: Julian <leuckerj@gmail.com>\n"
 "Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis/la/>\n"
@@ -48,8 +48,8 @@ msgstr "Inscriptio electronica"
 msgid "Home and mobile phone"
 msgstr "Numerus telephoni mobilis"
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 msgid "Groups"
 msgstr "Greges"
@@ -112,188 +112,188 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr "Grex"
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Create a new account"
 msgstr "Personae et computi"
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr "Anus scolae"
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 #, fuzzy
 #| msgid "Data management"
 msgid "Common data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr "personae"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional data"
 msgstr "addita nomines"
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr "Photographia"
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr "dies"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr "tempus"
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr "Scribe nuntium:"
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr "Primus nomen"
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 #, fuzzy
 #| msgid "E-mail address"
 msgid "A person is using this e-mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 #, fuzzy
 #| msgid "Who should see the announcement?"
 msgid "Who should get the permission?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Address data"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 #, fuzzy
 #| msgid "Data management"
 msgid "Account data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr ""
 
@@ -319,272 +319,271 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 #, fuzzy
 #| msgid "Edit school term"
 msgid "Linked school term"
 msgstr "Muta anum scolae"
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr "Dies et hora"
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 #, fuzzy
 #| msgid "E-mail address"
 msgid "IP address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr "Nomen"
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr "ani scolae"
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr "Persona"
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view contact details"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view photo"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view avatar image"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view persons groups"
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can view personal details"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr "femininum"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr "maskulinum"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr "Breve nomen"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr "Via"
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr "Numerus domini"
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr "Numerus directorius"
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr "Urbs"
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr "Numerus telephoni domi"
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr "Numerus telephoni mobilis"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr "Genus"
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr "Parentes"
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr "Descriptio"
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr ""
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 #, fuzzy
 #| msgid "Site description"
 msgid "Help text / description"
 msgstr "Descriptio paginae"
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Addtitional field for groups"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Addtitional fields for groups"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view statistics about group."
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 #, fuzzy
@@ -592,259 +591,259 @@ msgstr ""
 msgid "Additional fields"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titulus"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr "Mittens"
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Nota"
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 #, fuzzy
 #| msgid "Notification"
 msgid "Send notification at"
 msgstr "Nuntius"
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipient"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipients"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget Title"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget is broken"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Can edit default dashboard"
 msgstr "Forum"
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widgets"
 msgstr "Forum"
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 #, fuzzy
 #| msgid "Icon"
 msgid "Icon URL"
 msgstr "Nota"
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget order"
 msgstr "Forum"
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget orders"
 msgstr "Forum"
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Grex"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 #, fuzzy
@@ -852,256 +851,310 @@ msgstr "Grex"
 msgid "Group types"
 msgstr "Greges"
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 #, fuzzy
 #| msgid "System status"
 msgid "Can view system status"
 msgstr "Status systemae"
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 #, fuzzy
 #| msgid "Data management"
 msgid "Can manage data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can impersonate"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can invite persons"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification sent"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 #, fuzzy
 #| msgid "E-mail address"
 msgid "E-Mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 #, fuzzy
 #| msgid "Icon"
 msgid "Back URL"
 msgstr "Nota"
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button title"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button URL"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button icon"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional attributes"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view room timetable"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+#, fuzzy
+#| msgid "Date and time"
+msgid "Start date and time"
+msgstr "Dies et hora"
+
+#: aleksis/core/models.py:1530
+#, fuzzy
+#| msgid "Date and time"
+msgid "End date and time"
+msgstr "Dies et hora"
+
+#: aleksis/core/models.py:1531
+#, fuzzy
+#| msgid "Time"
+msgid "Timezone"
+msgstr "tempus"
+
+#: aleksis/core/models.py:1534
+msgid "Recurrences"
+msgstr ""
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+msgid "Calendar Event"
+msgstr ""
+
+#: aleksis/core/models.py:1644
+msgid "Calendar Events"
+msgstr ""
+
+#: aleksis/core/models.py:1670
+msgid "Birthdays"
+msgstr ""
+
+#: aleksis/core/models.py:1674
+msgid "{}'s birthday"
+msgstr ""
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Scola"
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 #, fuzzy
 #| msgid "Data management"
 msgid "Accounts"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 #, fuzzy
 #| msgid "Notifications"
 msgid "Authentication"
 msgstr "Nuntii"
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Internationalisation"
 msgstr "Simulare aliquem"
 
+#: aleksis/core/preferences.py:33
+msgid "Calendar"
+msgstr ""
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr "Titulus paginae"
@@ -1287,7 +1340,15 @@ msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
+msgid "Birthday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Activated calendars"
 msgstr ""
 
 #: aleksis/core/settings.py:551
@@ -1302,31 +1363,23 @@ msgstr "Germanus"
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
+#: aleksis/core/templates/core/announcement/list.html:42
+#: aleksis/core/templates/core/group/full.html:33
+#: aleksis/core/templates/core/pages/delete.html:22
+#: aleksis/core/templates/oauth2_provider/application/detail.html:21
+msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
 #: aleksis/core/templates/core/announcement/list.html:22
 #, fuzzy
 #| msgid "Notifications"
 msgid "Actions"
 msgstr "Nuntii"
 
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
-#: aleksis/core/templates/core/announcement/list.html:42
-#: aleksis/core/templates/core/group/full.html:33
-#: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/oauth2_provider/application/detail.html:21
-msgid "Delete"
-msgstr ""
-
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1397,6 +1450,13 @@ msgstr ""
 msgid "Your AlekSIS team"
 msgstr ""
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1645,6 +1705,12 @@ msgstr ""
 msgid "Recipients"
 msgstr ""
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr ""
+
 #: aleksis/core/templates/core/announcement/list.html:50
 #, fuzzy
 #| msgid "Write your announcement:"
@@ -2233,19 +2299,6 @@ msgstr ""
 msgid "Selected persons"
 msgstr ""
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-#, fuzzy
-#| msgid "Edit school term"
-msgid "Create school term"
-msgstr "Muta anum scolae"
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr "Muta anum scolae"
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
@@ -3038,164 +3091,164 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr ""
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr ""
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "The additional field has been saved."
 msgstr "addita nomines"
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 #, fuzzy
 #| msgid "System status"
 msgid "Run data checks …"
 msgstr "Status systemae"
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1382
+#: aleksis/core/views.py:1352
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1465
+#: aleksis/core/views.py:1435
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr ""
 
+#, fuzzy
+#~| msgid "Edit school term"
+#~ msgid "Create school term"
+#~ msgstr "Muta anum scolae"
+
+#~ msgid "Edit school term"
+#~ msgstr "Muta anum scolae"
+
 #, fuzzy
 #~| msgid "Notifications"
 #~ msgid "Authorized applications"
diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
index fb8bc425d974f814c8bf58710d9b1b06d975be5a..7a97f88fbc6d45c07ee2ca84806b8fcf28a4ef53 100644
--- a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+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"
@@ -41,8 +41,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 msgid "Groups"
 msgstr ""
@@ -101,172 +101,172 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr ""
 
@@ -290,697 +290,744 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr ""
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr ""
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+msgid "Start date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1530
+msgid "End date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1531
+msgid "Timezone"
+msgstr ""
+
+#: aleksis/core/models.py:1534
+msgid "Recurrences"
+msgstr ""
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+msgid "Calendar Event"
+msgstr ""
+
+#: aleksis/core/models.py:1644
+msgid "Calendar Events"
+msgstr ""
+
+#: aleksis/core/models.py:1670
+msgid "Birthdays"
+msgstr ""
+
+#: aleksis/core/models.py:1674
+msgid "{}'s birthday"
+msgstr ""
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
+#: aleksis/core/preferences.py:33
+msgid "Calendar"
+msgstr ""
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
@@ -1154,7 +1201,15 @@ msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
+msgid "Birthday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Activated calendars"
 msgstr ""
 
 #: aleksis/core/settings.py:551
@@ -1169,22 +1224,9 @@ msgstr ""
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
-msgstr ""
-
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
-#: aleksis/core/templates/core/announcement/list.html:22
-msgid "Actions"
-msgstr ""
-
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
@@ -1192,6 +1234,11 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
+#: aleksis/core/templates/core/announcement/list.html:22
+msgid "Actions"
+msgstr ""
+
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1262,6 +1309,13 @@ msgstr ""
 msgid "Your AlekSIS team"
 msgstr ""
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1494,6 +1548,12 @@ msgstr ""
 msgid "Recipients"
 msgstr ""
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr ""
+
 #: aleksis/core/templates/core/announcement/list.html:50
 msgid "There are no announcements."
 msgstr ""
@@ -2042,17 +2102,6 @@ msgstr ""
 msgid "Selected persons"
 msgstr ""
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-msgid "Create school term"
-msgstr ""
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr ""
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
@@ -2812,157 +2861,149 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr ""
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr ""
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1382
+#: aleksis/core/views.py:1352
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1465
+#: aleksis/core/views.py:1435
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr ""
 
diff --git a/aleksis/core/locale/ru/LC_MESSAGES/django.po b/aleksis/core/locale/ru/LC_MESSAGES/django.po
index 58e9f027cf610161dc14ee829e2669b2319f8299..8c9d34c43cd5a5152fe2e1a21f861d009f85c4d9 100644
--- a/aleksis/core/locale/ru/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/ru/LC_MESSAGES/django.po
@@ -10,12 +10,15 @@ msgstr ""
 "POT-Creation-Date: 2023-02-27 13:23+0100\n"
 "PO-Revision-Date: 2023-07-04 04:21+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
-"Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-core/ru/>\n"
+"Language-Team: Russian <https://translate.edugit.org/projects/aleksis/"
+"aleksis-core/ru/>\n"
 "Language: ru\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
+"%100>=11 && n%100<=14)? 2 : 3);\n"
 "X-Generator: Weblate 4.12.1\n"
 
 #: aleksis/core/apps.py:155 aleksis/core/apps.py:161
@@ -42,6 +45,7 @@ msgstr "Домашний и мобильный телефоны"
 #: aleksis/core/models.py:494 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9 aleksis/core/apps.py:166
 #: aleksis/core/forms.py:221 aleksis/core/models.py:495
+#: aleksis/core/forms.py:231 aleksis/core/models.py:517
 msgid "Groups"
 msgstr "Группы"
 
@@ -100,26 +104,27 @@ msgid "Content type"
 msgstr "Тип содержимого"
 
 #: aleksis/core/filters.py:113 aleksis/core/models.py:720
-#: aleksis/core/models.py:721
+#: aleksis/core/models.py:721 aleksis/core/models.py:743
 msgid "User"
 msgstr "Пользователь"
 
 #: aleksis/core/filters.py:135 aleksis/core/models.py:493
-#: aleksis/core/models.py:494
+#: aleksis/core/models.py:494 aleksis/core/models.py:516
 msgid "Group"
 msgstr "Группа"
 
 #: aleksis/core/forms.py:50 aleksis/core/forms.py:581 aleksis/core/forms.py:51
-#: aleksis/core/forms.py:582
+#: aleksis/core/forms.py:582 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr "Основные данные"
 
 #: aleksis/core/forms.py:55 aleksis/core/tables.py:47 aleksis/core/forms.py:56
+#: aleksis/core/tables.py:29
 msgid "Address"
 msgstr "Адрес"
 
 #: aleksis/core/forms.py:56 aleksis/core/forms.py:590 aleksis/core/forms.py:57
-#: aleksis/core/forms.py:591
+#: aleksis/core/forms.py:591 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr "Контактные данные"
 
@@ -145,6 +150,7 @@ msgstr "Этот логин уже занят."
 
 #: aleksis/core/forms.py:153 aleksis/core/models.py:141
 #: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/models.py:154
 msgid "School term"
 msgstr "Учебный год"
 
@@ -156,130 +162,161 @@ msgstr "Общие данные"
 #: aleksis/core/models.py:164 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9 aleksis/core/forms.py:156
 #: aleksis/core/forms.py:208 aleksis/core/models.py:165
+#: aleksis/core/forms.py:218 aleksis/core/models.py:177
 msgid "Persons"
 msgstr "Люди"
 
 #: aleksis/core/forms.py:156 aleksis/core/forms.py:592
 #: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr "Дополнительные данные"
 
 #: aleksis/core/forms.py:157 aleksis/core/models.py:217
 #: aleksis/core/models.py:546 aleksis/core/tables.py:46
 #: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547
+#: aleksis/core/models.py:547 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr "Фото"
 
 #: aleksis/core/forms.py:199 aleksis/core/forms.py:202
 #: aleksis/core/models.py:84 aleksis/core/forms.py:200
 #: aleksis/core/forms.py:203 aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr "Дата"
 
 #: aleksis/core/forms.py:200 aleksis/core/forms.py:203
 #: aleksis/core/models.py:92 aleksis/core/forms.py:201
 #: aleksis/core/forms.py:204 aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr "Время"
 
 #: aleksis/core/forms.py:233 aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr "С какого и по какое время это объявление должно отображаться?"
 
 #: aleksis/core/forms.py:236 aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr "Кто должен видеть это объявление?"
 
 #: aleksis/core/forms.py:237 aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr "Напишите свое объявление:"
 
 #: aleksis/core/forms.py:276 aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid ""
 "You are not allowed to create announcements which are only valid in the past."
 msgstr "Объявления для прошлого Вам создавать не разрешено."
 
 #: aleksis/core/forms.py:280 aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr "Дата и время начала должны быть до даты и времени окончания."
 
 #: aleksis/core/forms.py:289 aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr "Нужен хотя бы один получатель."
 
 #: aleksis/core/forms.py:398 aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr "Код приглашения"
 
 #: aleksis/core/forms.py:399 aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr "Укажите, пожалуйста, код приглашения."
 
 #: aleksis/core/forms.py:418 aleksis/core/models.py:192
 #: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr "Имя"
 
 #: aleksis/core/forms.py:419 aleksis/core/models.py:193
 #: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr "Фамилия"
 
 #: aleksis/core/forms.py:428 aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr "Этот эл.адрес кем-то используется"
 
 #: aleksis/core/forms.py:456 aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr "Кто должен получить такое разрешение?"
 
 #: aleksis/core/forms.py:457 aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr "В случае чего?"
 
 #: aleksis/core/forms.py:483 aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr "Отметьте объекты, к которым будет предоставлен доступ:"
 
 #: aleksis/core/forms.py:486 aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr "Предоставить доступ ко всем объектам"
 
 #: aleksis/core/forms.py:494 aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid ""
 "You must select at least one group or person which should get the permission."
 msgstr "Вам нужно выбрать хотя бы одну группу или физлицо, кто получит доступ."
 
 #: aleksis/core/forms.py:499 aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr "Вы должны предоставить доступ ко всем или к конкретным объектам."
 
 #: aleksis/core/forms.py:586 aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr "Подробности адреса"
 
 #: aleksis/core/forms.py:598 aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 msgid "Account data"
 msgstr "Данные учётной записи"
 
 #: aleksis/core/forms.py:605 aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr "Пароль"
 
 #: aleksis/core/forms.py:608 aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr "Пароль (ещё раз)"
 
 #: aleksis/core/forms.py:761 aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr "Выбранное действие не существует."
 
 #: aleksis/core/forms.py:772 aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr "У Вас нет разрешения на запуск {} на всех выбранных объектах."
 
 #: aleksis/core/forms.py:828 aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr "Неправильный выбор."
 
@@ -303,173 +340,213 @@ msgstr "Резервная копия не найдена!"
 msgid "No backup result found!"
 msgstr "Результат резервного копирования не найден!"
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:511 aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr "Связанный учебный год"
 
 #: aleksis/core/models.py:82 aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr "Булево (Да/Нет)"
 
 #: aleksis/core/models.py:83 aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr "Текст (одна строка)"
 
 #: aleksis/core/models.py:85 aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr "Дата и время"
 
 #: aleksis/core/models.py:86 aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr "Десятичное число"
 
 #: aleksis/core/models.py:87 aleksis/core/models.py:210
 #: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr "Адрес эл.почты"
 
 #: aleksis/core/models.py:88 aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr "Целое"
 
 #: aleksis/core/models.py:89 aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr "IP адрес"
 
 #: aleksis/core/models.py:90 aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Булево или пустое (Да/Нет/Ничего)"
 
 #: aleksis/core/models.py:91 aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr "Текст (многострочный)"
 
 #: aleksis/core/models.py:93 aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr "URL / Ссылка"
 
 #: aleksis/core/models.py:105 aleksis/core/models.py:1077
 #: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr "Полное имя"
 
 #: aleksis/core/models.py:107 aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr "Дата начала"
 
 #: aleksis/core/models.py:108 aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr "Дата окончания"
 
 #: aleksis/core/models.py:127 aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr "Дата начала должна быть ранее даты окончания."
 
 #: aleksis/core/models.py:134 aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr "На это время или на его часть уже запланирован учебный год."
 
 #: aleksis/core/models.py:142
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:143 aleksis/core/models.py:155
 msgid "School terms"
 msgstr "Учебный год"
 
 #: aleksis/core/models.py:163 aleksis/core/models.py:1026
 #: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr "Физлицо"
 
 #: aleksis/core/models.py:166 aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 msgid "Can view address"
 msgstr "Может видеть адрес"
 
 #: aleksis/core/models.py:167 aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr "Может видеть контактные данные"
 
 #: aleksis/core/models.py:168 aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr "Может видеть фото"
 
 #: aleksis/core/models.py:169 aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr "Может видеть аватар"
 
 #: aleksis/core/models.py:170 aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr "Может видеть группы лиц"
 
 #: aleksis/core/models.py:171 aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr "Может видеть личные данные"
 
 #: aleksis/core/models.py:181 aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr "жен"
 
 #: aleksis/core/models.py:181 aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr "муж"
 
 #: aleksis/core/models.py:181 aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr "другой"
 
 #: aleksis/core/models.py:189 aleksis/core/models.py:1353
 #: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr "Связанный пользователь"
 
 #: aleksis/core/models.py:195 aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr "Дополнительные имена"
 
 #: aleksis/core/models.py:199 aleksis/core/models.py:511
 #: aleksis/core/models.py:1439 aleksis/core/models.py:200
 #: aleksis/core/models.py:512 aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr "Короткое имя"
 
 #: aleksis/core/models.py:202 aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr "Улица"
 
 #: aleksis/core/models.py:203 aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr "Номер дома"
 
 #: aleksis/core/models.py:204 aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr "Почтовый индекс"
 
 #: aleksis/core/models.py:205 aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr "Город/место"
 
 #: aleksis/core/models.py:207 aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr "Домашний телефон"
 
 #: aleksis/core/models.py:208 aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr "Мобильный телефон"
 
 #: aleksis/core/models.py:212 aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr "Дата рождения"
 
 #: aleksis/core/models.py:213 aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr "Место рождения"
 
 #: aleksis/core/models.py:214 aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr "Пол"
 
 #: aleksis/core/models.py:221 aleksis/core/models.py:550
 #: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid ""
 "This is an official photo, used for official documents and for internal use "
 "cases."
@@ -478,19 +555,23 @@ msgstr ""
 
 #: aleksis/core/models.py:226 aleksis/core/models.py:554
 #: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr "Отобразить фото/аватар"
 
 #: aleksis/core/models.py:229 aleksis/core/models.py:557
 #: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr "Это фото или аватар для общего отображения."
 
 #: aleksis/core/models.py:234 aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr "Опекуны / Родители"
 
 #: aleksis/core/models.py:241 aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr "Основная группа"
 
@@ -499,69 +580,82 @@ msgstr "Основная группа"
 #: aleksis/core/models.py:1112 aleksis/core/models.py:245
 #: aleksis/core/models.py:725 aleksis/core/models.py:749
 #: aleksis/core/models.py:844 aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr "Описание"
 
 #: aleksis/core/models.py:464 aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr "Название поля"
 
 #: aleksis/core/models.py:466 aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr "Тип поля"
 
 #: aleksis/core/models.py:468 aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr "Необходимое"
 
 #: aleksis/core/models.py:469 aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr "Вспомогательный текст / описание"
 
 #: aleksis/core/models.py:475 aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr "Дополнительное поле для групп"
 
 #: aleksis/core/models.py:476 aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr "Дополнительные поля для групп"
 
 #: aleksis/core/models.py:496 aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr "Может определять дочерние группы в группы"
 
 #: aleksis/core/models.py:497 aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr "Может видеть статистику группы."
 
 #: aleksis/core/models.py:509 aleksis/core/models.py:1440
 #: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr "Длинное имя"
 
 #: aleksis/core/models.py:519 aleksis/core/templates/core/group/full.html:105
-#: aleksis/core/models.py:520
+#: aleksis/core/models.py:520 aleksis/core/models.py:542
 msgid "Members"
 msgstr "Участники"
 
 #: aleksis/core/models.py:522 aleksis/core/templates/core/group/full.html:102
-#: aleksis/core/models.py:523
+#: aleksis/core/models.py:523 aleksis/core/models.py:545
 msgid "Owners"
 msgstr "Владельцы"
 
 #: aleksis/core/models.py:529 aleksis/core/templates/core/group/full.html:59
-#: aleksis/core/models.py:530
+#: aleksis/core/models.py:530 aleksis/core/models.py:552
 msgid "Parent groups"
 msgstr "Родительские группы"
 
 #: aleksis/core/models.py:537 aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr "Тип группы"
 
 #: aleksis/core/models.py:542
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:543 aleksis/core/models.py:565
 msgid "Additional fields"
 msgstr "Дополнительные поля"
 
@@ -570,31 +664,39 @@ msgstr "Дополнительные поля"
 #: aleksis/core/templates/core/announcement/list.html:18
 #: aleksis/core/models.py:724 aleksis/core/models.py:748
 #: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 msgid "Title"
 msgstr "Название"
 
 #: aleksis/core/models.py:726 aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr "Приложение"
 
 #: aleksis/core/models.py:732 aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr "Активность"
 
 #: aleksis/core/models.py:733 aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr "Активности"
 
 #: aleksis/core/models.py:739 aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr "Отправитель"
 
 #: aleksis/core/models.py:744 aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr "Получатель"
 
 #: aleksis/core/models.py:749 aleksis/core/models.py:1078
 #: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr "Ссылка"
 
@@ -602,379 +704,468 @@ msgstr "Ссылка"
 #: aleksis/core/models.py:1400
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 #: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:1429 aleksis/core/models.py:775
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1451
 msgid "Icon"
 msgstr "Иконка"
 
 #: aleksis/core/models.py:755 aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr "Отправить уведомление в"
 
 #: aleksis/core/models.py:757 aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr "Читать"
 
 #: aleksis/core/models.py:758 aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr "Отправлено"
 
 #: aleksis/core/models.py:775 aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr "Уведомление"
 
 #: aleksis/core/models.py:776 aleksis/core/preferences.py:29
-#: aleksis/core/models.py:777
+#: aleksis/core/models.py:777 aleksis/core/models.py:799
+#: aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr "Уведомления"
 
 #: aleksis/core/models.py:844 aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr "Ссылка на подробный обзор"
 
 #: aleksis/core/models.py:847 aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr "Дата и время, с которого показывать"
 
 #: aleksis/core/models.py:850 aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr "Дата и время, по какое показывать"
 
 #: aleksis/core/models.py:875 aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr "Объявление"
 
 #: aleksis/core/models.py:876
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:877 aleksis/core/models.py:899
 msgid "Announcements"
 msgstr "Объявление"
 
 #: aleksis/core/models.py:913 aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr "Получатель объявления"
 
 #: aleksis/core/models.py:914 aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr "Получатели объявления"
 
 #: aleksis/core/models.py:934 aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr "Название виджета"
 
 #: aleksis/core/models.py:935 aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr "Активировать виджет"
 
 #: aleksis/core/models.py:936 aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr "Виджет поломался"
 
 #: aleksis/core/models.py:939 aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr "Размер на мобильных"
 
 #: aleksis/core/models.py:940 aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 пикс, 12 столбцов"
 
 #: aleksis/core/models.py:945 aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr "Размер на планшетах"
 
 #: aleksis/core/models.py:946 aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr "> 600 пикс, 12 столбцов"
 
 #: aleksis/core/models.py:951 aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr "Размер на ПК"
 
 #: aleksis/core/models.py:952 aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr "> 992 пикс, 12 столбцов"
 
 #: aleksis/core/models.py:957 aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr "Размер для больших экранов"
 
 #: aleksis/core/models.py:958 aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 пикс, 12 столбцов"
 
 #: aleksis/core/models.py:989 aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr "Может редактировать типовую/стандартную информпанель"
 
 #: aleksis/core/models.py:990 aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr "Виджет информпанели"
 
 #: aleksis/core/models.py:991 aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr "Виджеты информпанели"
 
 #: aleksis/core/models.py:997 aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr "URL"
 
 #: aleksis/core/models.py:998 aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr "Иконка URL"
 
 #: aleksis/core/models.py:1004 aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr "Внешняя ссылка на виджет"
 
 #: aleksis/core/models.py:1005 aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr "Внешние ссылки на виджеты"
 
 #: aleksis/core/models.py:1011 aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr "Содержимое"
 
 #: aleksis/core/models.py:1017 aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr "Виджет с постоянным содержимым"
 
 #: aleksis/core/models.py:1018 aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr "Виджеты с постоянным содержимым"
 
 #: aleksis/core/models.py:1023 aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr "Виджет информпанели"
 
 #: aleksis/core/models.py:1028 aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr "Порядок"
 
 #: aleksis/core/models.py:1029 aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr "Часть типовой информпанели"
 
 #: aleksis/core/models.py:1044 aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr "Порядок виджета на информпанели"
 
 #: aleksis/core/models.py:1045 aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr "Порядок виджетов на информпанели"
 
 #: aleksis/core/models.py:1051 aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr "Меню ID"
 
 #: aleksis/core/models.py:1064 aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr "Пользовательское меню"
 
 #: aleksis/core/models.py:1065 aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr "Пользовательские меню"
 
 #: aleksis/core/models.py:1075 aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr "Меню"
 
 #: aleksis/core/models.py:1085 aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr "Пункт пользовательского меню"
 
 #: aleksis/core/models.py:1086 aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr "Пункты пользовательского меню"
 
 #: aleksis/core/models.py:1111 aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr "Название типа"
 
 #: aleksis/core/models.py:1118 aleksis/core/templates/core/group/full.html:50
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1119 aleksis/core/models.py:1141
 msgid "Group type"
 msgstr "Тип группы"
 
 #: aleksis/core/models.py:1119
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1120 aleksis/core/models.py:1142
 msgid "Group types"
 msgstr "Типы групп"
 
 #: aleksis/core/models.py:1132 aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr "Может просматривать состояние системы"
 
 #: aleksis/core/models.py:1133 aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr "Может управлять данными"
 
 #: aleksis/core/models.py:1134 aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr "Может маскироваться"
 
 #: aleksis/core/models.py:1135 aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr "Может использовать поиск"
 
 #: aleksis/core/models.py:1136 aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr "Может менять свойства сайта"
 
 #: aleksis/core/models.py:1137 aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr "Может менять персональные свойства"
 
 #: aleksis/core/models.py:1138 aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr "Может менять свойства группы"
 
 #: aleksis/core/models.py:1139 aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr "Может генерировать тестовые PDF"
 
 #: aleksis/core/models.py:1140 aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr "Может приглашать других"
 
 #: aleksis/core/models.py:1176 aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr "Задание проверки связанных данных"
 
 #: aleksis/core/models.py:1184 aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr "Проблема решена"
 
 #: aleksis/core/models.py:1185 aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr "Уведомление отправлено"
 
 #: aleksis/core/models.py:1198 aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr "Результат проверки данных"
 
 #: aleksis/core/models.py:1199 aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr "Результаты проверки данных"
 
 #: aleksis/core/models.py:1201 aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr "Может запускать проверки данных"
 
 #: aleksis/core/models.py:1202 aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr "Может решать проблемы проверки данных"
 
 #: aleksis/core/models.py:1209 aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr "Адрес эл.почты"
 
 #: aleksis/core/models.py:1241 aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr "Владелец"
 
 #: aleksis/core/models.py:1245 aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr "Файл действителен до"
 
 #: aleksis/core/models.py:1248 aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr "Сгенерированный файл HTML"
 
 #: aleksis/core/models.py:1251 aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr "Сгенерированный файл PDF"
 
 #: aleksis/core/models.py:1258 aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr "Файл PDF"
 
 #: aleksis/core/models.py:1259 aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr "Файлы PDF"
 
 #: aleksis/core/models.py:1264 aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr "Результат задания"
 
 #: aleksis/core/models.py:1267 aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr "Пользователь задания"
 
 #: aleksis/core/models.py:1271 aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr "URL для возврата"
 
 #: aleksis/core/models.py:1272 aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr "Название процесса"
 
 #: aleksis/core/models.py:1273 aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr "Сообщение об ошибке"
 
 #: aleksis/core/models.py:1274 aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr "Сообщение об успехе"
 
 #: aleksis/core/models.py:1275 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr "URL для перенаправления в случае успеха"
 
 #: aleksis/core/models.py:1277 aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr "Название дополнительной кнопки"
 
 #: aleksis/core/models.py:1279 aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr "URL дополнительной кнопки"
 
 #: aleksis/core/models.py:1281 aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr "Иконка дополнительной кнопки"
 
 #: aleksis/core/models.py:1283 aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr "Полученный результат"
 
 #: aleksis/core/models.py:1308 aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr "Фоновое задание успешно завершено"
 
 #: aleksis/core/models.py:1309 aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr "Фоновое задание \"{}\" успешно завершено."
 
 #: aleksis/core/models.py:1315 aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr "Ошибка фонового задания"
 
 #: aleksis/core/models.py:1316 aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr "Ошибка фонового задания \"{}\"."
 
 #: aleksis/core/models.py:1325 aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr "Фоновое задание"
 
 #: aleksis/core/models.py:1339 aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr "Назначение пользователя задания"
 
 #: aleksis/core/models.py:1340 aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr "Назначения пользователей задания"
 
 #: aleksis/core/models.py:1356 aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr "Дополнительные атрибуты"
 
 #: aleksis/core/models.py:1394 aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr "Разрешённые пределы действия, которые могут запрашивать клиенты"
 
 #: aleksis/core/models.py:1404 aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid ""
 "This image will be shown as icon in the authorization flow. It should be "
 "squared."
@@ -983,46 +1174,49 @@ msgstr ""
 "Должно быть квадратным."
 
 #: aleksis/core/models.py:1449 aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr "Может просмативать расписание комнаты"
 
 #: aleksis/core/models.py:1451 aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr "Комната"
 
 #: aleksis/core/models.py:1452 aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr "Комнаты"
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:25 aleksis/core/preferences.py:24
 msgid "General"
 msgstr "Общее"
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:26 aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Школа / уч.заведение"
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:27 aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr "Тема"
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:28 aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr "Почта"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:30 aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr "Нижний колонтитул (подвал)"
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:31 aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr "Учётные записи"
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:32 aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr "Авторизация"
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:33 aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr "Интернационализация"
 
@@ -1232,7 +1426,8 @@ msgstr "Редактировать"
 #: aleksis/core/tables.py:27 aleksis/core/tables.py:148
 #: aleksis/core/tables.py:192
 #: aleksis/core/templates/core/announcement/list.html:22
-#: aleksis/core/tables.py:185
+#: aleksis/core/tables.py:185 aleksis/core/tables.py:130
+#: aleksis/core/tables.py:167
 msgid "Actions"
 msgstr "Действия"
 
@@ -1243,7 +1438,9 @@ msgstr "Действия"
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:183 aleksis/core/tables.py:97
+#: aleksis/core/tables.py:98 aleksis/core/tables.py:112
+#: aleksis/core/tables.py:128 aleksis/core/tables.py:165
 msgid "Delete"
 msgstr "Удалить"
 
@@ -3304,15 +3501,18 @@ msgid "The school term has been saved."
 msgstr "Учебный год сохранён."
 
 #: aleksis/core/views.py:396 aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr "Дочерние группы сохранены."
 
 #: aleksis/core/views.py:415 aleksis/core/views.py:425
 #: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr "Физлицо сохранено."
 
 #: aleksis/core/views.py:475 aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr "Группа сохранена."
 
@@ -3462,7 +3662,8 @@ msgid ""
 "                "
 msgstr ""
 "\n"
-"                  Доступ к сайту имеют только администратор и пользователи с внутренними IP-адресами.\n"
+"                  Доступ к сайту имеют только администратор и пользователи с "
+"внутренними IP-адресами.\n"
 "                "
 
 #: aleksis/core/templates/offline.html:10
@@ -3479,8 +3680,10 @@ msgid ""
 "    "
 msgstr ""
 "\n"
-"      Эта страница без соединения с сервером недоступна. Проверьте наличие подключения к интернету и повторите попытку.\n"
-"      Если интернет работает и ошибка всё равно присутствует, обратитесь, пожалуйста, к системным администраторам:\n"
+"      Эта страница без соединения с сервером недоступна. Проверьте наличие "
+"подключения к интернету и повторите попытку.\n"
+"      Если интернет работает и ошибка всё равно присутствует, обратитесь, "
+"пожалуйста, к системным администраторам:\n"
 "    "
 
 #: aleksis/core/templates/templated_email/invitation.email:4
@@ -3500,7 +3703,9 @@ msgstr "Привет, %(person)s"
 msgid ""
 "you have been invited to register on %(site)s. If you would like to accept "
 "this invitation, please click on the following link:"
-msgstr "Вы приглашены зарегистрироваться на %(site)s. Если хотите принять приглашение, перейдите по указанной ссылке:"
+msgstr ""
+"Вы приглашены зарегистрироваться на %(site)s. Если хотите принять "
+"приглашение, перейдите по указанной ссылке:"
 
 #: aleksis/core/views.py:528
 msgid "Maintenance mode was turned on successfully."
@@ -3510,6 +3715,86 @@ msgstr "Режим обслуживания успешно активирова
 msgid "Maintenance mode was turned off successfully."
 msgstr "Режим обслуживания успешно выключен."
 
+#: aleksis/core/models.py:1528
+#, fuzzy
+msgid "Start date and time"
+msgstr "Дата и время"
+
+#: aleksis/core/models.py:1530
+#, fuzzy
+msgid "End date and time"
+msgstr "Дата и время"
+
+#: aleksis/core/models.py:1531
+#, fuzzy
+msgid "Timezone"
+msgstr "Время"
+
+#: aleksis/core/models.py:1534
+#, fuzzy
+msgid "Recurrences"
+msgstr "Свойства"
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+#, fuzzy
+msgid "Calendar Event"
+msgstr "Ленты календарей"
+
+#: aleksis/core/models.py:1644
+#, fuzzy
+msgid "Calendar Events"
+msgstr "Ленты календарей"
+
+#: aleksis/core/models.py:1670
+#, fuzzy
+msgid "Birthdays"
+msgstr "Календарь Дней Рождения"
+
+#: aleksis/core/models.py:1674
+#, fuzzy
+msgid "{}'s birthday"
+msgstr "%(name)s отмечает День Рождения"
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:33
+#, fuzzy
+msgid "Calendar"
+msgstr "Ленты календарей"
+
+#: aleksis/core/preferences.py:490
+#, fuzzy
+msgid "Birthday calendar feed color"
+msgstr "Календарь Дней Рождения"
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+#, fuzzy
+msgid "Activated calendars"
+msgstr "Календарь Дней Рождения"
+
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and "
+"your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following "
+"link:"
+msgstr ""
+
 #~ msgid ""
 #~ "\n"
 #~ "            This page is currently unavailable. If this error persists, "
diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
index 1b5c47ccd08980e07aac8228d3be70924eca2ede..1e24c46a4d2b614b7cfd792269a18fc6b606a37e 100644
--- a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-05-09 18:40+0200\n"
+"POT-Creation-Date: 2023-07-23 13:39+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"
@@ -41,8 +41,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:166 aleksis/core/forms.py:221
-#: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/apps.py:166 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517 aleksis/core/templates/core/group/list.html:8
 #: aleksis/core/templates/core/group/list.html:9
 msgid "Groups"
 msgstr ""
@@ -101,172 +101,172 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/filters.py:113 aleksis/core/models.py:743
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/filters.py:135 aleksis/core/models.py:516
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:50 aleksis/core/forms.py:591
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:55 aleksis/core/tables.py:29
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:56 aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:156 aleksis/core/forms.py:208
-#: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:218
+#: aleksis/core/models.py:177 aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:158 aleksis/core/models.py:218
-#: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/forms.py:210 aleksis/core/forms.py:213
+#: aleksis/core/models.py:97
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:211 aleksis/core/forms.py:214
+#: aleksis/core/models.py:105
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:287
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:467
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:495
+#: aleksis/core/forms.py:504
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:596
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:608
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:615
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr ""
 
@@ -290,697 +290,744 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:98
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:101
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:102
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:120 aleksis/core/models.py:1532
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:121 aleksis/core/models.py:1533
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:140 aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:147 aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:143
-#: aleksis/core/templates/core/school_term/list.html:8
-#: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:179
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:194
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:212 aleksis/core/models.py:534
+#: aleksis/core/models.py:1490
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:215
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:216
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:217
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:218
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:220
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:227
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:222 aleksis/core/models.py:551
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:254
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:245 aleksis/core/models.py:725
-#: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:257 aleksis/core/models.py:747
+#: aleksis/core/models.py:771 aleksis/core/models.py:866
+#: aleksis/core/models.py:1135
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:487
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:489
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:491
 msgid "Required"
 msgstr ""
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr ""
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542 aleksis/core/templates/core/group/full.html:105
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545 aleksis/core/templates/core/group/full.html:102
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552 aleksis/core/templates/core/group/full.html:59
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:560
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:543
+#: aleksis/core/models.py:565
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/models.py:724 aleksis/core/models.py:748
-#: aleksis/core/models.py:843 aleksis/core/models.py:1299
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:749
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:755
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:756
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:762
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:767
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:753 aleksis/core/models.py:1080
-#: aleksis/core/models.py:1429
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:780
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:781
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:798
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:898
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:877
+#: aleksis/core/models.py:899
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1020
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1034
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1051
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1098
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141 aleksis/core/templates/core/group/full.html:50
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1142
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1292
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1315
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1318
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1324
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1325
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1376
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1433
+#: aleksis/core/models.py:1455
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1502
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/models.py:1528
+msgid "Start date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1530
+msgid "End date and time"
+msgstr ""
+
+#: aleksis/core/models.py:1531
+msgid "Timezone"
+msgstr ""
+
+#: aleksis/core/models.py:1534
+msgid "Recurrences"
+msgstr ""
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+msgid "Calendar Event"
+msgstr ""
+
+#: aleksis/core/models.py:1644
+msgid "Calendar Events"
+msgstr ""
+
+#: aleksis/core/models.py:1670
+msgid "Birthdays"
+msgstr ""
+
+#: aleksis/core/models.py:1674
+msgid "{}'s birthday"
+msgstr ""
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
+#: aleksis/core/preferences.py:33
+msgid "Calendar"
+msgstr ""
+
 #: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
@@ -1154,7 +1201,15 @@ msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
 #: aleksis/core/preferences.py:490
-msgid "Country for phone number parsing"
+msgid "Birthday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Activated calendars"
 msgstr ""
 
 #: aleksis/core/settings.py:551
@@ -1169,22 +1224,9 @@ msgstr ""
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:25
-#: aleksis/core/templates/core/announcement/list.html:36
-#: aleksis/core/templates/core/group/full.html:26
-#: aleksis/core/templates/oauth2_provider/application/detail.html:17
-msgid "Edit"
-msgstr ""
-
-#: aleksis/core/tables.py:27 aleksis/core/tables.py:148
-#: aleksis/core/tables.py:185
-#: aleksis/core/templates/core/announcement/list.html:22
-msgid "Actions"
-msgstr ""
-
-#: aleksis/core/tables.py:115 aleksis/core/tables.py:116
-#: aleksis/core/tables.py:130 aleksis/core/tables.py:146
-#: aleksis/core/tables.py:183
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
@@ -1192,6 +1234,11 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
+#: aleksis/core/templates/core/announcement/list.html:22
+msgid "Actions"
+msgstr ""
+
 #: aleksis/core/templates/403.html:14 aleksis/core/templates/500.html:10
 #: aleksis/core/templates/oauth2_provider/authorize.html:54
 msgid "Error"
@@ -1262,6 +1309,13 @@ msgstr ""
 msgid "Your AlekSIS team"
 msgstr ""
 
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following link:"
+msgstr ""
+
 #: aleksis/core/templates/account/email_confirm.html:5
 #: aleksis/core/templates/account/email_confirm.html:6
 #: aleksis/core/templates/account/email_confirm.html:17
@@ -1494,6 +1548,12 @@ msgstr ""
 msgid "Recipients"
 msgstr ""
 
+#: aleksis/core/templates/core/announcement/list.html:36
+#: aleksis/core/templates/core/group/full.html:26
+#: aleksis/core/templates/oauth2_provider/application/detail.html:17
+msgid "Edit"
+msgstr ""
+
 #: aleksis/core/templates/core/announcement/list.html:50
 msgid "There are no announcements."
 msgstr ""
@@ -2042,17 +2102,6 @@ msgstr ""
 msgid "Selected persons"
 msgstr ""
 
-#: aleksis/core/templates/core/school_term/create.html:6
-#: aleksis/core/templates/core/school_term/create.html:7
-#: aleksis/core/templates/core/school_term/list.html:14
-msgid "Create school term"
-msgstr ""
-
-#: aleksis/core/templates/core/school_term/edit.html:6
-#: aleksis/core/templates/core/school_term/edit.html:7
-msgid "Edit school term"
-msgstr ""
-
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
@@ -2812,156 +2861,148 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:285
-msgid "The school term has been created."
-msgstr ""
-
-#: aleksis/core/views.py:297
-msgid "The school term has been saved."
-msgstr ""
-
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:528
+#: aleksis/core/views.py:498
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:530
+#: aleksis/core/views.py:500
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1382
+#: aleksis/core/views.py:1352
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1465
+#: aleksis/core/views.py:1435
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr ""
diff --git a/aleksis/core/locale/uk/LC_MESSAGES/django.po b/aleksis/core/locale/uk/LC_MESSAGES/django.po
index 1df9b63ac1233d19055d59ba64c7b081ccd1b70f..5cd5dc251e9a975a8cec68dee5e656ba8c081e8e 100644
--- a/aleksis/core/locale/uk/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/uk/LC_MESSAGES/django.po
@@ -10,12 +10,16 @@ msgstr ""
 "POT-Creation-Date: 2023-02-27 13:23+0100\n"
 "PO-Revision-Date: 2023-07-04 04:21+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
-"Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-core/uk/>\n"
+"Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/"
+"aleksis-core/uk/>\n"
 "Language: uk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
+"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != "
+"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % "
+"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || "
+"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n"
 "X-Generator: Weblate 4.12.1\n"
 
 #: aleksis/core/apps.py:151
@@ -44,7 +48,8 @@ msgstr "Домашній та мобільний телефони"
 
 #: aleksis/core/apps.py:166 aleksis/core/forms.py:221
 #: aleksis/core/models.py:495 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/group/list.html:9 aleksis/core/forms.py:231
+#: aleksis/core/models.py:517
 msgid "Groups"
 msgstr "Групи"
 
@@ -103,171 +108,188 @@ msgid "Content type"
 msgstr "Тип змісту"
 
 #: aleksis/core/filters.py:113 aleksis/core/models.py:721
+#: aleksis/core/models.py:743
 msgid "User"
 msgstr "Користувач"
 
 #: aleksis/core/filters.py:135 aleksis/core/models.py:494
+#: aleksis/core/models.py:516
 msgid "Group"
 msgstr "Група"
 
-#: aleksis/core/forms.py:51 aleksis/core/forms.py:582
+#: aleksis/core/forms.py:51 aleksis/core/forms.py:582 aleksis/core/forms.py:50
+#: aleksis/core/forms.py:591
 msgid "Base data"
 msgstr "Основні дані"
 
-#: aleksis/core/forms.py:56 aleksis/core/tables.py:47
+#: aleksis/core/forms.py:56 aleksis/core/tables.py:47 aleksis/core/forms.py:55
+#: aleksis/core/tables.py:29
 msgid "Address"
 msgstr "Адреса"
 
-#: aleksis/core/forms.py:57 aleksis/core/forms.py:591
+#: aleksis/core/forms.py:57 aleksis/core/forms.py:591 aleksis/core/forms.py:56
+#: aleksis/core/forms.py:600
 msgid "Contact data"
 msgstr "Контактні дані"
 
-#: aleksis/core/forms.py:59
+#: aleksis/core/forms.py:59 aleksis/core/forms.py:58
 msgid "Advanced personal data"
 msgstr "Додаткові особисті дані"
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:107 aleksis/core/forms.py:106
 msgid "New user"
 msgstr "Новий користувач"
 
-#: aleksis/core/forms.py:107
+#: aleksis/core/forms.py:107 aleksis/core/forms.py:106
 msgid "Create a new account"
 msgstr "Створити новий обліковий запис"
 
-#: aleksis/core/forms.py:133
+#: aleksis/core/forms.py:133 aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr "Обравши вже існуючого користувача неможливо створити новий логін."
 
-#: aleksis/core/forms.py:137
+#: aleksis/core/forms.py:137 aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Такий логін вже зайнятий."
 
 #: aleksis/core/forms.py:154 aleksis/core/models.py:142
+#: aleksis/core/forms.py:153 aleksis/core/models.py:154
 msgid "School term"
 msgstr "Навчальний рік"
 
-#: aleksis/core/forms.py:155
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:154
 msgid "Common data"
 msgstr "Загальні дані"
 
 #: aleksis/core/forms.py:156 aleksis/core/forms.py:208
 #: aleksis/core/models.py:165 aleksis/core/templates/core/person/list.html:8
-#: aleksis/core/templates/core/person/list.html:9
+#: aleksis/core/templates/core/person/list.html:9 aleksis/core/forms.py:155
+#: aleksis/core/forms.py:218 aleksis/core/models.py:177
 msgid "Persons"
 msgstr "Особи"
 
 #: aleksis/core/forms.py:157 aleksis/core/forms.py:593
+#: aleksis/core/forms.py:156 aleksis/core/forms.py:602
 msgid "Additional data"
 msgstr "Додаткові дані"
 
 #: aleksis/core/forms.py:158 aleksis/core/models.py:218
 #: aleksis/core/models.py:547 aleksis/core/tables.py:46
+#: aleksis/core/forms.py:157 aleksis/core/models.py:230
+#: aleksis/core/models.py:569 aleksis/core/tables.py:28
 msgid "Photo"
 msgstr "Фото"
 
 #: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:85
+#: aleksis/core/models.py:85 aleksis/core/forms.py:210
+#: aleksis/core/forms.py:213 aleksis/core/models.py:97
 msgid "Date"
 msgstr "Дата"
 
 #: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/models.py:93 aleksis/core/forms.py:211
+#: aleksis/core/forms.py:214 aleksis/core/models.py:105
 msgid "Time"
 msgstr "Час"
 
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:234 aleksis/core/forms.py:244
 msgid "From when until when should the announcement be displayed?"
 msgstr "З якого по який час повинно відображатися це оголошення?"
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:237 aleksis/core/forms.py:247
 msgid "Who should see the announcement?"
 msgstr "Хто повинен бачити це оголошення?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:238 aleksis/core/forms.py:248
 msgid "Write your announcement:"
 msgstr "Складіть своє оголошеня:"
 
-#: aleksis/core/forms.py:277
-msgid "You are not allowed to create announcements which are only valid in the past."
+#: aleksis/core/forms.py:277 aleksis/core/forms.py:287
+msgid ""
+"You are not allowed to create announcements which are only valid in the past."
 msgstr "Оголошення для минулого Вам створювати не дозволено."
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:281 aleksis/core/forms.py:291
 msgid "The from date and time must be earlier then the until date and time."
 msgstr "Дата і час початку повинні бути раніше за дату і час закінчення."
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:290 aleksis/core/forms.py:300
 msgid "You need at least one recipient."
 msgstr "Вам потрібен принаймні один отримувач."
 
-#: aleksis/core/forms.py:399
+#: aleksis/core/forms.py:399 aleksis/core/forms.py:408
 msgid "Invitation code"
 msgstr "Код запрошення"
 
-#: aleksis/core/forms.py:400
+#: aleksis/core/forms.py:400 aleksis/core/forms.py:409
 msgid "Please enter your invitation code."
 msgstr "Напишіть, будь ласка, свій код запрошення."
 
 #: aleksis/core/forms.py:419 aleksis/core/models.py:193
+#: aleksis/core/forms.py:428 aleksis/core/models.py:205
 msgid "First name"
 msgstr "Ім'я"
 
 #: aleksis/core/forms.py:420 aleksis/core/models.py:194
+#: aleksis/core/forms.py:429 aleksis/core/models.py:206
 msgid "Last name"
 msgstr "Прізвище"
 
-#: aleksis/core/forms.py:429
+#: aleksis/core/forms.py:429 aleksis/core/forms.py:438
 msgid "A person is using this e-mail address"
 msgstr "Цією ел.адресою хтось користується"
 
-#: aleksis/core/forms.py:457
+#: aleksis/core/forms.py:457 aleksis/core/forms.py:466
 msgid "Who should get the permission?"
 msgstr "Хто повинен отримати такий дозвіл?"
 
-#: aleksis/core/forms.py:458
+#: aleksis/core/forms.py:458 aleksis/core/forms.py:467
 msgid "On what?"
 msgstr "В разі чого?"
 
-#: aleksis/core/forms.py:484
+#: aleksis/core/forms.py:484 aleksis/core/forms.py:493
 msgid "Select objects which the permission should be granted for:"
 msgstr "Оберіть об'єкти, до яких буде наданий дозвіл:"
 
-#: aleksis/core/forms.py:487
+#: aleksis/core/forms.py:487 aleksis/core/forms.py:496
 msgid "Grant the permission for all objects"
 msgstr "Надати дозвіл до всіх об'єктів"
 
-#: aleksis/core/forms.py:495
-msgid "You must select at least one group or person which should get the permission."
-msgstr "Ви повинні обрати принаймні одну групу або особу, хто буде мати дозвіл."
+#: aleksis/core/forms.py:495 aleksis/core/forms.py:504
+msgid ""
+"You must select at least one group or person which should get the permission."
+msgstr ""
+"Ви повинні обрати принаймні одну групу або особу, хто буде мати дозвіл."
 
-#: aleksis/core/forms.py:500
+#: aleksis/core/forms.py:500 aleksis/core/forms.py:509
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr "Ви повинні надати дозвіл до всіх або до конкретних об'єктів."
 
-#: aleksis/core/forms.py:587
+#: aleksis/core/forms.py:587 aleksis/core/forms.py:596
 msgid "Address data"
 msgstr "Дані адреси"
 
-#: aleksis/core/forms.py:599
+#: aleksis/core/forms.py:599 aleksis/core/forms.py:608
 msgid "Account data"
 msgstr "Дані облікового запису"
 
-#: aleksis/core/forms.py:606
+#: aleksis/core/forms.py:606 aleksis/core/forms.py:615
 msgid "Password"
 msgstr "Пароль"
 
-#: aleksis/core/forms.py:609
+#: aleksis/core/forms.py:609 aleksis/core/forms.py:618
 msgid "Password (again)"
 msgstr "Пароль (ще раз)"
 
-#: aleksis/core/forms.py:762
+#: aleksis/core/forms.py:762 aleksis/core/forms.py:771
 msgid "The selected action does not exist."
 msgstr "Обрана дія не існує."
 
-#: aleksis/core/forms.py:773
+#: aleksis/core/forms.py:773 aleksis/core/forms.py:782
 msgid "You do not have permission to run {} on all selected objects."
 msgstr "У Вас відсутній дозвіл на запуск {} на усіх обраних об'єктах."
 
-#: aleksis/core/forms.py:829
+#: aleksis/core/forms.py:829 aleksis/core/forms.py:838
 msgid "No valid selection."
 msgstr "Неправильний вибір."
 
@@ -291,694 +313,730 @@ msgstr "Резервна копія не знайдена!"
 msgid "No backup result found!"
 msgstr "Результат резервного копіювання не знайдений!"
 
-#: aleksis/core/mixins.py:511
+#: aleksis/core/mixins.py:511 aleksis/core/mixins.py:539
 msgid "Linked school term"
 msgstr "Пов'язаний навчальний рік"
 
-#: aleksis/core/models.py:83
+#: aleksis/core/models.py:83 aleksis/core/models.py:95
 msgid "Boolean (Yes/No)"
 msgstr "Логічне (Так/Ні)"
 
-#: aleksis/core/models.py:84
+#: aleksis/core/models.py:84 aleksis/core/models.py:96
 msgid "Text (one line)"
 msgstr "Текст (один рядок)"
 
-#: aleksis/core/models.py:86
+#: aleksis/core/models.py:86 aleksis/core/models.py:98
 msgid "Date and time"
 msgstr "Дата і час"
 
-#: aleksis/core/models.py:87
+#: aleksis/core/models.py:87 aleksis/core/models.py:99
 msgid "Decimal number"
 msgstr "Десятичне число"
 
 #: aleksis/core/models.py:88 aleksis/core/models.py:211
+#: aleksis/core/models.py:100 aleksis/core/models.py:223
 msgid "E-mail address"
 msgstr "Адреса ел.пошти"
 
-#: aleksis/core/models.py:89
+#: aleksis/core/models.py:89 aleksis/core/models.py:101
 msgid "Integer"
 msgstr "Ціле"
 
-#: aleksis/core/models.py:90
+#: aleksis/core/models.py:90 aleksis/core/models.py:102
 msgid "IP address"
 msgstr "IP адреса"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:91 aleksis/core/models.py:103
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Логічне або порожнє (Так/Ні/Нічого)"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:92 aleksis/core/models.py:104
 msgid "Text (multi-line)"
 msgstr "Текст (багаторядковий)"
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:94 aleksis/core/models.py:106
 msgid "URL / Link"
 msgstr "URL / Посилання"
 
 #: aleksis/core/models.py:106 aleksis/core/models.py:1078
+#: aleksis/core/models.py:118 aleksis/core/models.py:1100
+#: aleksis/core/models.py:1729
 msgid "Name"
 msgstr "Повне ім'я"
 
-#: aleksis/core/models.py:108
+#: aleksis/core/models.py:108 aleksis/core/models.py:120
+#: aleksis/core/models.py:1532
 msgid "Start date"
 msgstr "Дата початку"
 
-#: aleksis/core/models.py:109
+#: aleksis/core/models.py:109 aleksis/core/models.py:121
+#: aleksis/core/models.py:1533
 msgid "End date"
 msgstr "Дата закінчення"
 
-#: aleksis/core/models.py:128
+#: aleksis/core/models.py:128 aleksis/core/models.py:140
+#: aleksis/core/schema/school_term.py:48
 msgid "The start date must be earlier than the end date."
 msgstr "Початкова дата повинна бути раніше кінцевої."
 
-#: aleksis/core/models.py:135
+#: aleksis/core/models.py:135 aleksis/core/models.py:147
+#: aleksis/core/schema/school_term.py:53
 msgid "There is already a school term for this time or a part of this time."
 msgstr "На цей час або на частину цього часу вже припадає навчальний рік."
 
 #: aleksis/core/models.py:143
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
+#: aleksis/core/models.py:155
 msgid "School terms"
 msgstr "Навчальний рік"
 
 #: aleksis/core/models.py:164 aleksis/core/models.py:1027
+#: aleksis/core/models.py:176 aleksis/core/models.py:1049
 msgid "Person"
 msgstr "Особа"
 
-#: aleksis/core/models.py:167
+#: aleksis/core/models.py:167 aleksis/core/models.py:179
 msgid "Can view address"
 msgstr "Може бачити адресу"
 
-#: aleksis/core/models.py:168
+#: aleksis/core/models.py:168 aleksis/core/models.py:180
 msgid "Can view contact details"
 msgstr "Може бачити контактні дані"
 
-#: aleksis/core/models.py:169
+#: aleksis/core/models.py:169 aleksis/core/models.py:181
 msgid "Can view photo"
 msgstr "Може бачити фото"
 
-#: aleksis/core/models.py:170
+#: aleksis/core/models.py:170 aleksis/core/models.py:182
 msgid "Can view avatar image"
 msgstr "Може бачити аватар"
 
-#: aleksis/core/models.py:171
+#: aleksis/core/models.py:171 aleksis/core/models.py:183
 msgid "Can view persons groups"
 msgstr "Може бачити групи особи"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:172 aleksis/core/models.py:184
 msgid "Can view personal details"
 msgstr "Може бачити особисті дані"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:182 aleksis/core/models.py:194
 msgid "female"
 msgstr "жін"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:182 aleksis/core/models.py:194
 msgid "male"
 msgstr "чол"
 
-#: aleksis/core/models.py:182
+#: aleksis/core/models.py:182 aleksis/core/models.py:194
 msgid "other"
 msgstr "інший"
 
 #: aleksis/core/models.py:190 aleksis/core/models.py:1382
+#: aleksis/core/models.py:202 aleksis/core/models.py:1404
 msgid "Linked user"
 msgstr "Пов'язаний користувач"
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:196 aleksis/core/models.py:208
 msgid "Additional name(s)"
 msgstr "Додаткові імена"
 
 #: aleksis/core/models.py:200 aleksis/core/models.py:512
-#: aleksis/core/models.py:1468
+#: aleksis/core/models.py:1468 aleksis/core/models.py:212
+#: aleksis/core/models.py:534 aleksis/core/models.py:1490
 msgid "Short name"
 msgstr "Коротке ім'я"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:203 aleksis/core/models.py:215
 msgid "Street"
 msgstr "Вулиця"
 
-#: aleksis/core/models.py:204
+#: aleksis/core/models.py:204 aleksis/core/models.py:216
 msgid "Street number"
 msgstr "Номер будинку"
 
-#: aleksis/core/models.py:205
+#: aleksis/core/models.py:205 aleksis/core/models.py:217
 msgid "Postal code"
 msgstr "Поштовий індекс"
 
-#: aleksis/core/models.py:206
+#: aleksis/core/models.py:206 aleksis/core/models.py:218
 msgid "Place"
 msgstr "Місто"
 
-#: aleksis/core/models.py:208
+#: aleksis/core/models.py:208 aleksis/core/models.py:220
 msgid "Home phone"
 msgstr "Домашній телефон"
 
-#: aleksis/core/models.py:209
+#: aleksis/core/models.py:209 aleksis/core/models.py:221
 msgid "Mobile phone"
 msgstr "Мобільний телефон"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:213 aleksis/core/models.py:225
 msgid "Date of birth"
 msgstr "Дата народження"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:214 aleksis/core/models.py:226
 msgid "Place of birth"
 msgstr "Місце народження"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:215 aleksis/core/models.py:227
 msgid "Sex"
 msgstr "Стать"
 
 #: aleksis/core/models.py:222 aleksis/core/models.py:551
-msgid "This is an official photo, used for official documents and for internal use cases."
-msgstr "Це офіційне фото, яке використовується для документів та внутрішніх потреб."
+#: aleksis/core/models.py:234 aleksis/core/models.py:573
+msgid ""
+"This is an official photo, used for official documents and for internal use "
+"cases."
+msgstr ""
+"Це офіційне фото, яке використовується для документів та внутрішніх потреб."
 
 #: aleksis/core/models.py:227 aleksis/core/models.py:555
+#: aleksis/core/models.py:239 aleksis/core/models.py:577
 msgid "Display picture / Avatar"
 msgstr "Відобразити фото/аватар"
 
 #: aleksis/core/models.py:230 aleksis/core/models.py:558
+#: aleksis/core/models.py:242 aleksis/core/models.py:580
 msgid "This is a picture or an avatar for public display."
 msgstr "Це фото або аватар для загального відображення."
 
-#: aleksis/core/models.py:235
+#: aleksis/core/models.py:235 aleksis/core/models.py:247
 msgid "Guardians / Parents"
 msgstr "Опікуни / батьки"
 
-#: aleksis/core/models.py:242
+#: aleksis/core/models.py:242 aleksis/core/models.py:254
 msgid "Primary group"
 msgstr "Основна група"
 
 #: aleksis/core/models.py:245 aleksis/core/models.py:725
 #: aleksis/core/models.py:749 aleksis/core/models.py:844
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1113 aleksis/core/models.py:257
+#: aleksis/core/models.py:747 aleksis/core/models.py:771
+#: aleksis/core/models.py:866 aleksis/core/models.py:1135
 msgid "Description"
 msgstr "Опис"
 
-#: aleksis/core/models.py:465
+#: aleksis/core/models.py:465 aleksis/core/models.py:487
 msgid "Title of field"
 msgstr "Назва поля"
 
-#: aleksis/core/models.py:467
+#: aleksis/core/models.py:467 aleksis/core/models.py:489
 msgid "Type of field"
 msgstr "Тип поля"
 
-#: aleksis/core/models.py:469
+#: aleksis/core/models.py:469 aleksis/core/models.py:491
 msgid "Required"
 msgstr "Необхідне"
 
-#: aleksis/core/models.py:470
+#: aleksis/core/models.py:470 aleksis/core/models.py:492
 msgid "Help text / description"
 msgstr "Допоміжний текст/опис"
 
-#: aleksis/core/models.py:476
+#: aleksis/core/models.py:476 aleksis/core/models.py:498
 msgid "Addtitional field for groups"
 msgstr "Додаткове поле для груп"
 
-#: aleksis/core/models.py:477
+#: aleksis/core/models.py:477 aleksis/core/models.py:499
 msgid "Addtitional fields for groups"
 msgstr "Додаткові поля для груп"
 
-#: aleksis/core/models.py:497
+#: aleksis/core/models.py:497 aleksis/core/models.py:519
 msgid "Can assign child groups to groups"
 msgstr "Може призначати підлеглі групи до груп"
 
-#: aleksis/core/models.py:498
+#: aleksis/core/models.py:498 aleksis/core/models.py:520
 msgid "Can view statistics about group."
 msgstr "Може бачити статистику групи."
 
 #: aleksis/core/models.py:510 aleksis/core/models.py:1469
+#: aleksis/core/models.py:532 aleksis/core/models.py:1491
 msgid "Long name"
 msgstr "Довге ім'я"
 
 #: aleksis/core/models.py:520 aleksis/core/templates/core/group/full.html:105
+#: aleksis/core/models.py:542
 msgid "Members"
 msgstr "Учасники"
 
 #: aleksis/core/models.py:523 aleksis/core/templates/core/group/full.html:102
+#: aleksis/core/models.py:545
 msgid "Owners"
 msgstr "Власники"
 
 #: aleksis/core/models.py:530 aleksis/core/templates/core/group/full.html:59
+#: aleksis/core/models.py:552
 msgid "Parent groups"
 msgstr "Батьківські групи"
 
-#: aleksis/core/models.py:538
+#: aleksis/core/models.py:538 aleksis/core/models.py:560
 msgid "Type of group"
 msgstr "Тип групи"
 
 #: aleksis/core/models.py:543
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
+#: aleksis/core/models.py:565
 msgid "Additional fields"
 msgstr "Додаткові поля"
 
 #: aleksis/core/models.py:724 aleksis/core/models.py:748
 #: aleksis/core/models.py:843 aleksis/core/models.py:1299
 #: aleksis/core/templates/core/announcement/list.html:18
+#: aleksis/core/models.py:746 aleksis/core/models.py:770
+#: aleksis/core/models.py:865 aleksis/core/models.py:1321
 msgid "Title"
 msgstr "Назва"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:727 aleksis/core/models.py:749
 msgid "Application"
 msgstr "Додаток"
 
-#: aleksis/core/models.py:733
+#: aleksis/core/models.py:733 aleksis/core/models.py:755
 msgid "Activity"
 msgstr "Активність"
 
-#: aleksis/core/models.py:734
+#: aleksis/core/models.py:734 aleksis/core/models.py:756
 msgid "Activities"
 msgstr "Активності"
 
-#: aleksis/core/models.py:740
+#: aleksis/core/models.py:740 aleksis/core/models.py:762
 msgid "Sender"
 msgstr "Відправник"
 
-#: aleksis/core/models.py:745
+#: aleksis/core/models.py:745 aleksis/core/models.py:767
 msgid "Recipient"
 msgstr "Отримувач"
 
 #: aleksis/core/models.py:750 aleksis/core/models.py:1079
+#: aleksis/core/models.py:772 aleksis/core/models.py:1101
 msgid "Link"
 msgstr "Посилання"
 
 #: aleksis/core/models.py:753 aleksis/core/models.py:1080
 #: aleksis/core/models.py:1429
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
+#: aleksis/core/models.py:775 aleksis/core/models.py:1102
+#: aleksis/core/models.py:1451
 msgid "Icon"
 msgstr "Піктограма"
 
-#: aleksis/core/models.py:756
+#: aleksis/core/models.py:756 aleksis/core/models.py:778
 msgid "Send notification at"
 msgstr "Надіслати сповіщення о"
 
-#: aleksis/core/models.py:758
+#: aleksis/core/models.py:758 aleksis/core/models.py:780
 msgid "Read"
 msgstr "Читати"
 
-#: aleksis/core/models.py:759
+#: aleksis/core/models.py:759 aleksis/core/models.py:781
 msgid "Sent"
 msgstr "Надіслано"
 
-#: aleksis/core/models.py:776
+#: aleksis/core/models.py:776 aleksis/core/models.py:798
 msgid "Notification"
 msgstr "Сповіщення"
 
 #: aleksis/core/models.py:777 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:799 aleksis/core/preferences.py:28
 msgid "Notifications"
 msgstr "Сповіщення"
 
-#: aleksis/core/models.py:845
+#: aleksis/core/models.py:845 aleksis/core/models.py:867
 msgid "Link to detailed view"
 msgstr "Посилання на детальний перегляд"
 
-#: aleksis/core/models.py:848
+#: aleksis/core/models.py:848 aleksis/core/models.py:870
 msgid "Date and time from when to show"
 msgstr "Дата і час, з якого показувати"
 
-#: aleksis/core/models.py:851
+#: aleksis/core/models.py:851 aleksis/core/models.py:873
 msgid "Date and time until when to show"
 msgstr "Дата і час, до якого показувати"
 
-#: aleksis/core/models.py:876
+#: aleksis/core/models.py:876 aleksis/core/models.py:898
 msgid "Announcement"
 msgstr "Оголошення"
 
 #: aleksis/core/models.py:877
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
+#: aleksis/core/models.py:899
 msgid "Announcements"
 msgstr "Оголошення"
 
-#: aleksis/core/models.py:914
+#: aleksis/core/models.py:914 aleksis/core/models.py:936
 msgid "Announcement recipient"
 msgstr "Отримувач оголошення"
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:915 aleksis/core/models.py:937
 msgid "Announcement recipients"
 msgstr "Отримувачі оголошення"
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:935 aleksis/core/models.py:957
 msgid "Widget Title"
 msgstr "Назва віджета"
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:936 aleksis/core/models.py:958
 msgid "Activate Widget"
 msgstr "Активувати віджет"
 
-#: aleksis/core/models.py:937
+#: aleksis/core/models.py:937 aleksis/core/models.py:959
 msgid "Widget is broken"
 msgstr "Віджет зламався"
 
-#: aleksis/core/models.py:940
+#: aleksis/core/models.py:940 aleksis/core/models.py:962
 msgid "Size on mobile devices"
 msgstr "Розмір на мобільних"
 
-#: aleksis/core/models.py:941
+#: aleksis/core/models.py:941 aleksis/core/models.py:963
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:946 aleksis/core/models.py:968
 msgid "Size on tablet devices"
 msgstr "Розмір на планшетах"
 
-#: aleksis/core/models.py:947
+#: aleksis/core/models.py:947 aleksis/core/models.py:969
 msgid "> 600 px, 12 columns"
 msgstr "> 600 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:952
+#: aleksis/core/models.py:952 aleksis/core/models.py:974
 msgid "Size on desktop devices"
 msgstr "Розмір на ПК"
 
-#: aleksis/core/models.py:953
+#: aleksis/core/models.py:953 aleksis/core/models.py:975
 msgid "> 992 px, 12 columns"
 msgstr "> 992 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:958
+#: aleksis/core/models.py:958 aleksis/core/models.py:980
 msgid "Size on large desktop devices"
 msgstr "Розмір на великих екранах"
 
-#: aleksis/core/models.py:959
+#: aleksis/core/models.py:959 aleksis/core/models.py:981
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:990
+#: aleksis/core/models.py:990 aleksis/core/models.py:1012
 msgid "Can edit default dashboard"
 msgstr "Може редагувати типову/стандартну інформпанель"
 
-#: aleksis/core/models.py:991
+#: aleksis/core/models.py:991 aleksis/core/models.py:1013
 msgid "Dashboard Widget"
 msgstr "Віджет інформпанелі"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:992 aleksis/core/models.py:1014
 msgid "Dashboard Widgets"
 msgstr "Віджети інформпанелі"
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:998 aleksis/core/models.py:1020
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:999 aleksis/core/models.py:1021
 msgid "Icon URL"
 msgstr "Піктограма URL"
 
-#: aleksis/core/models.py:1005
+#: aleksis/core/models.py:1005 aleksis/core/models.py:1027
 msgid "External link widget"
 msgstr "Зовнішнє посилання на віджет"
 
-#: aleksis/core/models.py:1006
+#: aleksis/core/models.py:1006 aleksis/core/models.py:1028
 msgid "External link widgets"
 msgstr "Зовнішні посилання на віджети"
 
-#: aleksis/core/models.py:1012
+#: aleksis/core/models.py:1012 aleksis/core/models.py:1034
 msgid "Content"
 msgstr "Зміст"
 
-#: aleksis/core/models.py:1018
+#: aleksis/core/models.py:1018 aleksis/core/models.py:1040
 msgid "Static content widget"
 msgstr "Віджет з постійним змістом"
 
-#: aleksis/core/models.py:1019
+#: aleksis/core/models.py:1019 aleksis/core/models.py:1041
 msgid "Static content widgets"
 msgstr "Віджети з постійним змістом"
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1024 aleksis/core/models.py:1046
 msgid "Dashboard widget"
 msgstr "Віджет інформпанелі"
 
-#: aleksis/core/models.py:1029
+#: aleksis/core/models.py:1029 aleksis/core/models.py:1051
 msgid "Order"
 msgstr "Порядок"
 
-#: aleksis/core/models.py:1030
+#: aleksis/core/models.py:1030 aleksis/core/models.py:1052
 msgid "Part of the default dashboard"
 msgstr "Частина типової інформпанелі"
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1045 aleksis/core/models.py:1067
 msgid "Dashboard widget order"
 msgstr "Порядок віджету на інформпанелі"
 
-#: aleksis/core/models.py:1046
+#: aleksis/core/models.py:1046 aleksis/core/models.py:1068
 msgid "Dashboard widget orders"
 msgstr "Порядок віджетів на інформпанелі"
 
-#: aleksis/core/models.py:1052
+#: aleksis/core/models.py:1052 aleksis/core/models.py:1074
 msgid "Menu ID"
 msgstr "Меню ID"
 
-#: aleksis/core/models.py:1065
+#: aleksis/core/models.py:1065 aleksis/core/models.py:1087
 msgid "Custom menu"
 msgstr "Користувацьке меню"
 
-#: aleksis/core/models.py:1066
+#: aleksis/core/models.py:1066 aleksis/core/models.py:1088
 msgid "Custom menus"
 msgstr "Користувацькі меню"
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1076 aleksis/core/models.py:1098
 msgid "Menu"
 msgstr "Меню"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1086 aleksis/core/models.py:1108
 msgid "Custom menu item"
 msgstr "Пункт користувацького меню"
 
-#: aleksis/core/models.py:1087
+#: aleksis/core/models.py:1087 aleksis/core/models.py:1109
 msgid "Custom menu items"
 msgstr "Пункти користувацького меню"
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1112 aleksis/core/models.py:1134
 msgid "Title of type"
 msgstr "Назва типу"
 
 #: aleksis/core/models.py:1119 aleksis/core/templates/core/group/full.html:50
+#: aleksis/core/models.py:1141
 msgid "Group type"
 msgstr "Тип групи"
 
 #: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
+#: aleksis/core/models.py:1142
 msgid "Group types"
 msgstr "Типи груп"
 
-#: aleksis/core/models.py:1133
+#: aleksis/core/models.py:1133 aleksis/core/models.py:1155
 msgid "Can view system status"
 msgstr "Може переглядати стан системи"
 
-#: aleksis/core/models.py:1134
+#: aleksis/core/models.py:1134 aleksis/core/models.py:1156
 msgid "Can manage data"
 msgstr "Може керувати даними"
 
-#: aleksis/core/models.py:1135
+#: aleksis/core/models.py:1135 aleksis/core/models.py:1157
 msgid "Can impersonate"
 msgstr "Може маскуватися"
 
-#: aleksis/core/models.py:1136
+#: aleksis/core/models.py:1136 aleksis/core/models.py:1158
 msgid "Can use search"
 msgstr "Може шукати"
 
-#: aleksis/core/models.py:1137
+#: aleksis/core/models.py:1137 aleksis/core/models.py:1159
 msgid "Can change site preferences"
 msgstr "Може змінювати властивості сайту"
 
-#: aleksis/core/models.py:1138
+#: aleksis/core/models.py:1138 aleksis/core/models.py:1160
 msgid "Can change person preferences"
 msgstr "Може змінювати властивості особи"
 
-#: aleksis/core/models.py:1139
+#: aleksis/core/models.py:1139 aleksis/core/models.py:1161
 msgid "Can change group preferences"
 msgstr "Може змінювати властивості групи"
 
-#: aleksis/core/models.py:1140
+#: aleksis/core/models.py:1140 aleksis/core/models.py:1162
 msgid "Can test PDF generation"
 msgstr "Може генерувати тестові PDF"
 
-#: aleksis/core/models.py:1141
+#: aleksis/core/models.py:1141 aleksis/core/models.py:1163
 msgid "Can invite persons"
 msgstr "Може запрошувати осіб"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1177 aleksis/core/models.py:1199
 msgid "Related data check task"
 msgstr "Завдання перевірки пов'язаних даних"
 
-#: aleksis/core/models.py:1185
+#: aleksis/core/models.py:1185 aleksis/core/models.py:1207
 msgid "Issue solved"
 msgstr "Проблема вирішена"
 
-#: aleksis/core/models.py:1186
+#: aleksis/core/models.py:1186 aleksis/core/models.py:1208
 msgid "Notification sent"
 msgstr "Сповіщення надіслане"
 
-#: aleksis/core/models.py:1199
+#: aleksis/core/models.py:1199 aleksis/core/models.py:1221
 msgid "Data check result"
 msgstr "Результат перевірки даних"
 
-#: aleksis/core/models.py:1200
+#: aleksis/core/models.py:1200 aleksis/core/models.py:1222
 msgid "Data check results"
 msgstr "Результати перевірки даних"
 
-#: aleksis/core/models.py:1202
+#: aleksis/core/models.py:1202 aleksis/core/models.py:1224
 msgid "Can run data checks"
 msgstr "Може запускати перевірки даних"
 
-#: aleksis/core/models.py:1203
+#: aleksis/core/models.py:1203 aleksis/core/models.py:1225
 msgid "Can solve data check problems"
 msgstr "Може розв'язувати проблеми перевірки даних"
 
-#: aleksis/core/models.py:1210
+#: aleksis/core/models.py:1210 aleksis/core/models.py:1232
 msgid "E-Mail address"
 msgstr "Адреса ел.пошти"
 
-#: aleksis/core/models.py:1270
+#: aleksis/core/models.py:1270 aleksis/core/models.py:1292
 msgid "Owner"
 msgstr "Власник"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1274 aleksis/core/models.py:1296
 msgid "File expires at"
 msgstr "Файл дійсний до"
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1277 aleksis/core/models.py:1299
 msgid "Generated HTML file"
 msgstr "Згенерований файл HTML"
 
-#: aleksis/core/models.py:1280
+#: aleksis/core/models.py:1280 aleksis/core/models.py:1302
 msgid "Generated PDF file"
 msgstr "Згенерований файл PDF"
 
-#: aleksis/core/models.py:1287
+#: aleksis/core/models.py:1287 aleksis/core/models.py:1309
 msgid "PDF file"
 msgstr "Файл PDF"
 
-#: aleksis/core/models.py:1288
+#: aleksis/core/models.py:1288 aleksis/core/models.py:1310
 msgid "PDF files"
 msgstr "Файли PDF"
 
-#: aleksis/core/models.py:1293
+#: aleksis/core/models.py:1293 aleksis/core/models.py:1315
 msgid "Task result"
 msgstr "Результат завдання"
 
-#: aleksis/core/models.py:1296
+#: aleksis/core/models.py:1296 aleksis/core/models.py:1318
 msgid "Task user"
 msgstr "Користувач завдання"
 
-#: aleksis/core/models.py:1300
+#: aleksis/core/models.py:1300 aleksis/core/models.py:1322
 msgid "Back URL"
 msgstr "URL для повернення"
 
-#: aleksis/core/models.py:1301
+#: aleksis/core/models.py:1301 aleksis/core/models.py:1323
 msgid "Progress title"
 msgstr "Назва процесу"
 
-#: aleksis/core/models.py:1302
+#: aleksis/core/models.py:1302 aleksis/core/models.py:1324
 msgid "Error message"
 msgstr "Повідомлення про помилку"
 
-#: aleksis/core/models.py:1303
+#: aleksis/core/models.py:1303 aleksis/core/models.py:1325
 msgid "Success message"
 msgstr "Повідомлення про успіх"
 
-#: aleksis/core/models.py:1304
+#: aleksis/core/models.py:1304 aleksis/core/models.py:1326
 msgid "Redirect on success URL"
 msgstr "Посилання для перенаправлення в разі успіху"
 
-#: aleksis/core/models.py:1306
+#: aleksis/core/models.py:1306 aleksis/core/models.py:1328
 msgid "Additional button title"
 msgstr "Назва додаткової кнопки"
 
-#: aleksis/core/models.py:1308
+#: aleksis/core/models.py:1308 aleksis/core/models.py:1330
 msgid "Additional button URL"
 msgstr "Посилання додаткової кнопки"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1310 aleksis/core/models.py:1332
 msgid "Additional button icon"
 msgstr "Піктограма додаткової кнопки"
 
-#: aleksis/core/models.py:1312
+#: aleksis/core/models.py:1312 aleksis/core/models.py:1334
 msgid "Result fetched"
 msgstr "Отриманий результат"
 
-#: aleksis/core/models.py:1337
+#: aleksis/core/models.py:1337 aleksis/core/models.py:1359
 msgid "Background task completed successfully"
 msgstr "Фонове завдання успішно завершене"
 
-#: aleksis/core/models.py:1338
+#: aleksis/core/models.py:1338 aleksis/core/models.py:1360
 msgid "The background task '{}' has been completed successfully."
 msgstr "Фонове завдання '{}' було успішно завершене."
 
-#: aleksis/core/models.py:1344
+#: aleksis/core/models.py:1344 aleksis/core/models.py:1366
 msgid "Background task failed"
 msgstr "Збій фонового завдання"
 
-#: aleksis/core/models.py:1345
+#: aleksis/core/models.py:1345 aleksis/core/models.py:1367
 msgid "The background task '{}' has failed."
 msgstr "У фонового завдання '{}' стався збій."
 
-#: aleksis/core/models.py:1354
+#: aleksis/core/models.py:1354 aleksis/core/models.py:1376
 msgid "Background task"
 msgstr "Фонове завдання"
 
-#: aleksis/core/models.py:1368
+#: aleksis/core/models.py:1368 aleksis/core/models.py:1390
 msgid "Task user assignment"
 msgstr "Призначення користувача завдання"
 
-#: aleksis/core/models.py:1369
+#: aleksis/core/models.py:1369 aleksis/core/models.py:1391
 msgid "Task user assignments"
 msgstr "Призначення користувачів завдання"
 
-#: aleksis/core/models.py:1385
+#: aleksis/core/models.py:1385 aleksis/core/models.py:1407
 msgid "Additional attributes"
 msgstr "Додаткові атрибути"
 
-#: aleksis/core/models.py:1423
+#: aleksis/core/models.py:1423 aleksis/core/models.py:1445
 msgid "Allowed scopes that clients can request"
 msgstr "Дозволені межі дії, які можуть запитувати клієнти"
 
-#: aleksis/core/models.py:1433
-msgid "This image will be shown as icon in the authorization flow. It should be squared."
-msgstr "Це зображення буде іконкою під час авторизації. Воно повинне бути квадратним."
+#: aleksis/core/models.py:1433 aleksis/core/models.py:1455
+msgid ""
+"This image will be shown as icon in the authorization flow. It should be "
+"squared."
+msgstr ""
+"Це зображення буде іконкою під час авторизації. Воно повинне бути квадратним."
 
-#: aleksis/core/models.py:1478
+#: aleksis/core/models.py:1478 aleksis/core/models.py:1500
 msgid "Can view room timetable"
 msgstr "Може переглядати розклад кімнати"
 
-#: aleksis/core/models.py:1480
+#: aleksis/core/models.py:1480 aleksis/core/models.py:1502
 msgid "Room"
 msgstr "Кімната"
 
-#: aleksis/core/models.py:1481
+#: aleksis/core/models.py:1481 aleksis/core/models.py:1503
 msgid "Rooms"
 msgstr "Кімнати"
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:25 aleksis/core/preferences.py:24
 msgid "General"
 msgstr "Загальне"
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:26 aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Школа / навч.заклад"
 
-#: aleksis/core/preferences.py:27
+#: aleksis/core/preferences.py:27 aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr "Тема"
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:28 aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr "Пошта"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:30 aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr "Нижній колонтитул (footer)"
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:31 aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr "Облікові записи"
 
-#: aleksis/core/preferences.py:32
+#: aleksis/core/preferences.py:32 aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr "Авторизація"
 
-#: aleksis/core/preferences.py:33
+#: aleksis/core/preferences.py:33 aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr "Інтернаціоналізація"
 
@@ -1059,8 +1117,10 @@ msgid "Automatically create new persons for new users"
 msgstr "Нові особи для нових користувачів створювати автоматично"
 
 #: aleksis/core/preferences.py:256
-msgid "Automatically link existing persons to new users by their e-mail address"
-msgstr "Пов'язувати існуючих осіб з новими користувачами автоматично за ел.адресами"
+msgid ""
+"Automatically link existing persons to new users by their e-mail address"
+msgstr ""
+"Пов'язувати існуючих осіб з новими користувачами автоматично за ел.адресами"
 
 #: aleksis/core/preferences.py:267
 msgid "Display name of the school"
@@ -1127,7 +1187,8 @@ msgid "Fields on person model which are editable by themselves."
 msgstr "Поля з описом особи, які можна редагувати самостійно."
 
 #: aleksis/core/preferences.py:424
-msgid "Editable fields on person model which should trigger a notification on change"
+msgid ""
+"Editable fields on person model which should trigger a notification on change"
 msgstr "Змінювані поля з описом особи, які повинні сповіщати щодо редагування"
 
 #: aleksis/core/preferences.py:438
@@ -1180,6 +1241,7 @@ msgstr "Редагувати"
 #: aleksis/core/tables.py:27 aleksis/core/tables.py:148
 #: aleksis/core/tables.py:185
 #: aleksis/core/templates/core/announcement/list.html:22
+#: aleksis/core/tables.py:130 aleksis/core/tables.py:167
 msgid "Actions"
 msgstr "Дії"
 
@@ -1190,6 +1252,9 @@ msgstr "Дії"
 #: aleksis/core/templates/core/group/full.html:33
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
+#: aleksis/core/tables.py:97 aleksis/core/tables.py:98
+#: aleksis/core/tables.py:112 aleksis/core/tables.py:128
+#: aleksis/core/tables.py:165
 msgid "Delete"
 msgstr "Видалити"
 
@@ -1209,7 +1274,8 @@ msgstr ""
 #: aleksis/core/templates/403.html:19 aleksis/core/templates/404.html:16
 msgid ""
 "\n"
-"            If you think this is an error in AlekSIS, please contact your site\n"
+"            If you think this is an error in AlekSIS, please contact your "
+"site\n"
 "            administrators:\n"
 "          "
 msgstr ""
@@ -1241,12 +1307,14 @@ msgstr ""
 #: aleksis/core/templates/500.html:13
 msgid ""
 "\n"
-"            Your site administrators will automatically be notified about this\n"
+"            Your site administrators will automatically be notified about "
+"this\n"
 "            error. You can also contact them directly:\n"
 "          "
 msgstr ""
 "\n"
-"            Адміністратори сайту будуть сповіщені щодо цієї помилки автоматично.\n"
+"            Адміністратори сайту будуть сповіщені щодо цієї помилки "
+"автоматично.\n"
 "            Ви також можете звернутися до них безпосередньо:\n"
 "          "
 
@@ -1292,13 +1360,22 @@ msgstr "Підтвердити"
 
 #: aleksis/core/templates/account/email_confirm.html:12
 #, python-format
-msgid "Please confirm that <a href=\"mailto:%(email)s\">%(email)s</a> is an e-mail address for user %(user_display)s."
-msgstr "Підтвердіть, будь ласка, що <a href=\"mailto:%(email)s\">%(email)s</a> є адресою ел.пошти для користувача %(user_display)s."
+msgid ""
+"Please confirm that <a href=\"mailto:%(email)s\">%(email)s</a> is an e-mail "
+"address for user %(user_display)s."
+msgstr ""
+"Підтвердіть, будь ласка, що <a href=\"mailto:%(email)s\">%(email)s</a> є "
+"адресою ел.пошти для користувача %(user_display)s."
 
 #: aleksis/core/templates/account/email_confirm.html:25
 #, python-format
-msgid "This e-mail confirmation link expired or is invalid. Please <a href=\"%(email_url)s\">issue a new e-mail confirmation request</a>."
-msgstr "Це посилання для підтвердження ел.пошти протерміноване або недійсне. Зробіть, будь ласка, для підтвердження <a href=\"%(email_url)s\">новий запит</a>."
+msgid ""
+"This e-mail confirmation link expired or is invalid. Please <a href="
+"\"%(email_url)s\">issue a new e-mail confirmation request</a>."
+msgstr ""
+"Це посилання для підтвердження ел.пошти протерміноване або недійсне. "
+"Зробіть, будь ласка, для підтвердження <a href=\"%(email_url)s\">новий "
+"запит</a>."
 
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
@@ -1332,12 +1409,14 @@ msgstr "Зміна паролю вимкнена."
 msgid ""
 "\n"
 "            Users are not allowed to edit their own passwords. If you think\n"
-"            this is an error please contact one of your site administrators.\n"
+"            this is an error please contact one of your site "
+"administrators.\n"
 "          "
 msgstr ""
 "\n"
 "            Користувачам не дозволяється змінювати свої паролі.\n"
-"            Якщо Ви думаєте, що це помилка, зверніться до адміністраторів сайту.\n"
+"            Якщо Ви думаєте, що це помилка, зверніться до адміністраторів "
+"сайту.\n"
 "          "
 
 #: aleksis/core/templates/account/password_reset.html:5
@@ -1351,8 +1430,12 @@ msgid "Reset password"
 msgstr "Скинути пароль"
 
 #: aleksis/core/templates/account/password_reset.html:17
-msgid "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it."
-msgstr "Забули пароль? Вкажіть нижче свою ел.пошту і ми надішлемо Вам листа для скидання пароля."
+msgid ""
+"Forgotten your password? Enter your e-mail address below, and we'll send you "
+"an e-mail allowing you to reset it."
+msgstr ""
+"Забули пароль? Вкажіть нижче свою ел.пошту і ми надішлемо Вам листа для "
+"скидання пароля."
 
 #: aleksis/core/templates/account/password_reset.html:30
 msgid ""
@@ -1376,7 +1459,8 @@ msgid ""
 msgstr ""
 "\n"
 "            Ми надіслали Вам ел.листа. Якщо Ви його не отримаєте протягом\n"
-"            декількох хвилин, зверніться, будь ласка, до адміністраторів сайту.\n"
+"            декількох хвилин, зверніться, будь ласка, до адміністраторів "
+"сайту.\n"
 "          "
 
 #: aleksis/core/templates/account/password_reset_from_key.html:15
@@ -1387,13 +1471,16 @@ msgstr "Несправний токен"
 #, python-format
 msgid ""
 "\n"
-"              The password reset link was invalid, possibly because it has already been used. Please request a <a href=\"%(passwd_reset_url)s\"\n"
+"              The password reset link was invalid, possibly because it has "
+"already been used. Please request a <a href=\"%(passwd_reset_url)s\"\n"
 "              class=\"blue-text text-lighten-2\">new password reset</a>.\n"
 "            "
 msgstr ""
 "\n"
-"              Посилання на скидання пароля недійсне або, ймовірно, вже використане. Зробіть, будь ласка, <a href=\"%(passwd_reset_url)s\"\n"
-"              class=\"blue-text text-lighten-2\">новий запит на скидання пароля</a>.\n"
+"              Посилання на скидання пароля недійсне або, ймовірно, вже "
+"використане. Зробіть, будь ласка, <a href=\"%(passwd_reset_url)s\"\n"
+"              class=\"blue-text text-lighten-2\">новий запит на скидання "
+"пароля</a>.\n"
 "            "
 
 #: aleksis/core/templates/account/password_reset_from_key.html:25
@@ -1442,8 +1529,10 @@ msgstr "Реєстрація"
 
 #: aleksis/core/templates/account/signup.html:12
 #, python-format
-msgid "Already have an account? Then please <a href=\"%(login_url)s\">sign in</a>."
-msgstr "Вже маєте обліковий запис? Тоді можете <a href=\"%(login_url)s\">увіти</a>."
+msgid ""
+"Already have an account? Then please <a href=\"%(login_url)s\">sign in</a>."
+msgstr ""
+"Вже маєте обліковий запис? Тоді можете <a href=\"%(login_url)s\">увіти</a>."
 
 #: aleksis/core/templates/account/signup.html:22
 #: aleksis/core/templates/socialaccount/signup.html:23
@@ -1487,13 +1576,17 @@ msgstr "Перевірте свою ел.пошту!"
 #: aleksis/core/templates/account/verification_sent.html:16
 msgid ""
 "\n"
-"            This part of the site requires us to verify that you are who you claim to be.\n"
-"            For this purpose, we require that you verify ownership of your e-mail address.\n"
+"            This part of the site requires us to verify that you are who you "
+"claim to be.\n"
+"            For this purpose, we require that you verify ownership of your e-"
+"mail address.\n"
 "          "
 msgstr ""
 "\n"
-"            Ця частина сайту вимагає перевірки, що Ви - саме той/та, хто має бути.\n"
-"            Саме для цього нам необхідно перевірити, що адреса ел.пошти належить саме Вам.\n"
+"            Ця частина сайту вимагає перевірки, що Ви - саме той/та, хто має "
+"бути.\n"
+"            Саме для цього нам необхідно перевірити, що адреса ел.пошти "
+"належить саме Вам.\n"
 "          "
 
 #: aleksis/core/templates/account/verification_sent.html:22
@@ -1506,8 +1599,10 @@ msgid ""
 msgstr ""
 "\n"
 "            Ми надіслали Вам для перевірки ел.листа.\n"
-"            Будь ласка, перейдіть за вказаним там посиланням. Якщо Ви не отримаєте\n"
-"            листа протягом декількох хвилин, зверніться, будь ласка, до нас.\n"
+"            Будь ласка, перейдіть за вказаним там посиланням. Якщо Ви не "
+"отримаєте\n"
+"            листа протягом декількох хвилин, зверніться, будь ласка, до "
+"нас.\n"
 "          "
 
 #: aleksis/core/templates/core/additional_field/edit.html:6
@@ -1682,29 +1777,39 @@ msgstr "Редагувати інформпанель"
 #: aleksis/core/templates/core/edit_dashboard.html:24
 msgid ""
 "\n"
-"          On this page you can arrange your personal dashboard. You can drag any items from \"Available widgets\" to \"Your\n"
-"          Dashboard\" or change the order by moving the widgets. After you have finished, please don't forget to click on\n"
+"          On this page you can arrange your personal dashboard. You can drag "
+"any items from \"Available widgets\" to \"Your\n"
+"          Dashboard\" or change the order by moving the widgets. After you "
+"have finished, please don't forget to click on\n"
 "          \"Save\".\n"
 "        "
 msgstr ""
 "\n"
-"          На цій сторінці Ви можете впорядкувати свою інформпанель. Перетягуйте будь-які елементи з \"Доступних віджетів\"\n"
-"          до \"Своєї інформпанелі\" або змінюйте порядок, перетягуючи віджети. Після закінчення не забудьте натиснути\n"
+"          На цій сторінці Ви можете впорядкувати свою інформпанель. "
+"Перетягуйте будь-які елементи з \"Доступних віджетів\"\n"
+"          до \"Своєї інформпанелі\" або змінюйте порядок, перетягуючи "
+"віджети. Після закінчення не забудьте натиснути\n"
 "          \"Зберегти\".\n"
 "        "
 
 #: aleksis/core/templates/core/edit_dashboard.html:30
 msgid ""
 "\n"
-"          On this page you can arrange the default dashboard which is shown when a user doesn't arrange his own\n"
-"          dashboard. You can drag any items from \"Available widgets\" to \"Default Dashboard\" or change the order\n"
-"          by moving the widgets. After you have finished, please don't forget to click on \"Save\".\n"
+"          On this page you can arrange the default dashboard which is shown "
+"when a user doesn't arrange his own\n"
+"          dashboard. You can drag any items from \"Available widgets\" to "
+"\"Default Dashboard\" or change the order\n"
+"          by moving the widgets. After you have finished, please don't "
+"forget to click on \"Save\".\n"
 "        "
 msgstr ""
 "\n"
-"          На цій сторінці Ви можете впорядкувати типову/стандартну інформпанель, яка відображається, якщо користувач\n"
-"          не впорядкував власну. Перетягуйте будь-які елементи з \"Доступних віджетів\" до \"Типової інформпанелі\" або змінюйте \n"
-"          порядок, перетягуючи віджети. Після закінчення не забудьте натиснути \"Зберегти\".\n"
+"          На цій сторінці Ви можете впорядкувати типову/стандартну "
+"інформпанель, яка відображається, якщо користувач\n"
+"          не впорядкував власну. Перетягуйте будь-які елементи з \"Доступних "
+"віджетів\" до \"Типової інформпанелі\" або змінюйте \n"
+"          порядок, перетягуючи віджети. Після закінчення не забудьте "
+"натиснути \"Зберегти\".\n"
 "        "
 
 #: aleksis/core/templates/core/edit_dashboard.html:48
@@ -1727,13 +1832,16 @@ msgstr "Призначити підлеглі групи до груп"
 #: aleksis/core/templates/core/group/child_groups.html:18
 msgid ""
 "\n"
-"          You can use this to assign child groups to groups. Please use the filters below to select groups you want to\n"
+"          You can use this to assign child groups to groups. Please use the "
+"filters below to select groups you want to\n"
 "          change and click \"Next\".\n"
 "        "
 msgstr ""
 "\n"
-"          Ви можете скористатися цим для призначення підлеглих груп до основних. Для обрання груп,\n"
-"          які хочете змінити, скористайтеся, будь ласка, фільтрами, що нижче та натисність \"Далі\".\n"
+"          Ви можете скористатися цим для призначення підлеглих груп до "
+"основних. Для обрання груп,\n"
+"          які хочете змінити, скористайтеся, будь ласка, фільтрами, що нижче "
+"та натисність \"Далі\".\n"
 "        "
 
 #: aleksis/core/templates/core/group/child_groups.html:31
@@ -1759,7 +1867,8 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Оберіть, будь ласка, декілька груп у порядку, за яким призначати.\n"
+"            Оберіть, будь ласка, декілька груп у порядку, за яким "
+"призначати.\n"
 "          "
 
 #: aleksis/core/templates/core/group/child_groups.html:72
@@ -1773,14 +1882,18 @@ msgstr "Обережно, будь ласка!"
 #: aleksis/core/templates/core/group/child_groups.html:79
 msgid ""
 "\n"
-"            If you click \"Back\" or \"Next\" the current group assignments are not saved.\n"
-"            If you click \"Save\", you will overwrite all existing child group relations for this group with what you\n"
+"            If you click \"Back\" or \"Next\" the current group assignments "
+"are not saved.\n"
+"            If you click \"Save\", you will overwrite all existing child "
+"group relations for this group with what you\n"
 "            selected on this page.\n"
 "          "
 msgstr ""
 "\n"
-"            Якщо натиснете \"Назад\" або \"Далі\" призначення цієї групи не збережуться.\n"
-"            Якщо натиснете \"Зберегти\", усі існуючі зв'язки підлеглої групи з цією групою будуть замінені на\n"
+"            Якщо натиснете \"Назад\" або \"Далі\" призначення цієї групи не "
+"збережуться.\n"
+"            Якщо натиснете \"Зберегти\", усі існуючі зв'язки підлеглої групи "
+"з цією групою будуть замінені на\n"
 "            обрані на цій сторінці.\n"
 "          "
 
@@ -1878,12 +1991,14 @@ msgstr "Додому"
 #: aleksis/core/templates/core/index.html:34
 msgid ""
 "\n"
-"        You didn't customise your dashboard so that you see the system default. Please click on \"Edit dashboard\" to\n"
+"        You didn't customise your dashboard so that you see the system "
+"default. Please click on \"Edit dashboard\" to\n"
 "        customise your personal dashboard.\n"
 "      "
 msgstr ""
 "\n"
-"        Ви ще не налаштували свою інформпанель, тож поки що бачите типову/стандартну. Для налаштування \n"
+"        Ви ще не налаштували свою інформпанель, тож поки що бачите типову/"
+"стандартну. Для налаштування \n"
 "         власної інформпанелі клацніть \"Редагувати інформпанель\".\n"
 "      "
 
@@ -1927,11 +2042,13 @@ msgstr "Активований режим обслуговування"
 #: aleksis/core/templates/core/pages/system_status.html:28
 msgid ""
 "\n"
-"                  Only admin and visitors from internal IPs can access the site.\n"
+"                  Only admin and visitors from internal IPs can access the "
+"site.\n"
 "                "
 msgstr ""
 "\n"
-"                  Доступ до сайту мають лише адміністратор та відвідувачі з внутрішніми IP-адресами.\n"
+"                  Доступ до сайту мають лише адміністратор та відвідувачі з "
+"внутрішніми IP-адресами.\n"
 "                "
 
 #: aleksis/core/templates/core/pages/system_status.html:39
@@ -1949,11 +2066,13 @@ msgstr "Активований режим налагодження"
 #: aleksis/core/templates/core/pages/system_status.html:53
 msgid ""
 "\n"
-"                The web server throws back debug information on errors. Do not use in production!\n"
+"                The web server throws back debug information on errors. Do "
+"not use in production!\n"
 "              "
 msgstr ""
 "\n"
-"                Веб-сервер кидає під час помилок інформацію щодо налагодження. Не використовуйте в продакшені!\n"
+"                Веб-сервер кидає під час помилок інформацію щодо "
+"налагодження. Не використовуйте в продакшені!\n"
 "              "
 
 #: aleksis/core/templates/core/pages/system_status.html:60
@@ -1963,11 +2082,13 @@ msgstr "Режим налагодження деактивований"
 #: aleksis/core/templates/core/pages/system_status.html:62
 msgid ""
 "\n"
-"                Debug mode is disabled. Default error pages are displayed on errors.\n"
+"                Debug mode is disabled. Default error pages are displayed on "
+"errors.\n"
 "              "
 msgstr ""
 "\n"
-"                Режим налагодження деактивований. В разі збоїв відображатимуться типові сторінки помилок.\n"
+"                Режим налагодження деактивований. В разі збоїв "
+"відображатимуться типові сторінки помилок.\n"
 "              "
 
 #: aleksis/core/templates/core/pages/system_status.html:75
@@ -2017,11 +2138,13 @@ msgstr "Спроба генерації PDF"
 #: aleksis/core/templates/core/pages/test_pdf.html:14
 msgid ""
 "\n"
-"        This simple view can be used to ensure the correct function of the built-in PDF generation system.\n"
+"        This simple view can be used to ensure the correct function of the "
+"built-in PDF generation system.\n"
 "      "
 msgstr ""
 "\n"
-"        Цей простий вигляд допоможе перевірити коректність налаштувань вбудованої системи генерації PDF.\n"
+"        Цей простий вигляд допоможе перевірити коректність налаштувань "
+"вбудованої системи генерації PDF.\n"
 "      "
 
 #: aleksis/core/templates/core/partials/announcements.html:8
@@ -2079,11 +2202,13 @@ msgstr "Невідомо"
 #: aleksis/core/templates/core/partials/splash_screen.html:11
 msgid ""
 "\n"
-"      This webbrowser doesn't support JavaScript, or its execution is blocked. Please use another browser to continue.\n"
+"      This webbrowser doesn't support JavaScript, or its execution is "
+"blocked. Please use another browser to continue.\n"
 "    "
 msgstr ""
 "\n"
-"      Цей веб-браузер не підтримує JavaScript або його обробка заблокована. Для продовження скористайтеся, будь ласка, іншим браузером.\n"
+"      Цей веб-браузер не підтримує JavaScript або його обробка заблокована. "
+"Для продовження скористайтеся, будь ласка, іншим браузером.\n"
 "    "
 
 #: aleksis/core/templates/core/partials/splash_screen.html:17
@@ -2206,7 +2331,9 @@ msgid "The invite feature is disabled."
 msgstr "Функція запрошення деактивована."
 
 #: aleksis/core/templates/invitations/disabled.html:15
-msgid "To enable it, switch on the corresponding checkbox in the authentication section of the "
+msgid ""
+"To enable it, switch on the corresponding checkbox in the authentication "
+"section of the "
 msgstr "Для активації увімкніть відповідний чекбокс в розділі авторизації на "
 
 #: aleksis/core/templates/invitations/disabled.html:16
@@ -2355,13 +2482,17 @@ msgstr "Немає з'єднання з сервером."
 #: aleksis/core/templates/offline.html:14
 msgid ""
 "\n"
-"      This page is not available without a connection to the server. Please check your internet connection and try again.\n"
-"      If you are connected and the error persists, please contact the system administrators:\n"
+"      This page is not available without a connection to the server. Please "
+"check your internet connection and try again.\n"
+"      If you are connected and the error persists, please contact the system "
+"administrators:\n"
 "    "
 msgstr ""
 "\n"
-"      Ця сторінка без з'єднання з сервером не доступна. Перевірте наявність підключення до інтернету та повторіть спробу.\n"
-"      Якщо інтернет працює і помилка все одно присутня, зверніться до системних адміністраторів:\n"
+"      Ця сторінка без з'єднання з сервером не доступна. Перевірте наявність "
+"підключення до інтернету та повторіть спробу.\n"
+"      Якщо інтернет працює і помилка все одно присутня, зверніться до "
+"системних адміністраторів:\n"
 "    "
 
 #: aleksis/core/templates/search/search.html:8
@@ -2396,12 +2527,14 @@ msgstr "Помилка входу облікового запису третьо
 #: aleksis/core/templates/socialaccount/authentication_error.html:15
 msgid ""
 "\n"
-"            An error occurred while attempting to login via your third-party account.\n"
+"            An error occurred while attempting to login via your third-party "
+"account.\n"
 "            Please contact one of your site administrators.\n"
 "          "
 msgstr ""
 "\n"
-"            Під час спроби входу з Вашим стороннім обліковим записом виникла помилка входу.\n"
+"            Під час спроби входу з Вашим стороннім обліковим записом виникла "
+"помилка входу.\n"
 "            Зверніться, будь ласка, до адміністратора сайту.\n"
 "          "
 
@@ -2416,7 +2549,9 @@ msgstr "Видалити"
 
 #: aleksis/core/templates/socialaccount/connections.html:34
 msgid "You currently have no third-party accounts connected to this account."
-msgstr "Зараз у Вас немає облікових записів третіх сторін, з'єднаних з цим обліковим записом."
+msgstr ""
+"Зараз у Вас немає облікових записів третіх сторін, з'єднаних з цим обліковим "
+"записом."
 
 #: aleksis/core/templates/socialaccount/connections.html:37
 msgid "Add a Third-party Account"
@@ -2430,7 +2565,8 @@ msgstr "Ви приєднуєте новий обліковий запис тр
 #: aleksis/core/templates/socialaccount/login.html:23
 #, python-format
 msgid "You are about to sign in using a third party account from %(provider)s."
-msgstr "Ви авторизуєтеся за допомогою стороннього облікового запису від %(provider)s."
+msgstr ""
+"Ви авторизуєтеся за допомогою стороннього облікового запису від %(provider)s."
 
 #: aleksis/core/templates/socialaccount/login.html:28
 msgid "Continue"
@@ -2446,11 +2582,15 @@ msgstr "Вхід скасований"
 #, python-format
 msgid ""
 "\n"
-"            You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to <a href=\"%(login_url)s\">sign in</a>.\n"
+"            You decided to cancel logging in to our site using one of your "
+"existing accounts. If this was a mistake, please proceed to <a href="
+"\"%(login_url)s\">sign in</a>.\n"
 "          "
 msgstr ""
 "\n"
-"            Схоже, що Ви скасували вхід до нашого сайту з одним із Ваших облікових записів. Якщо це сталося помилково, Ви можете <a href=\"%(login_url)s\">продовжити вхід тут</a>.\n"
+"            Схоже, що Ви скасували вхід до нашого сайту з одним із Ваших "
+"облікових записів. Якщо це сталося помилково, Ви можете <a href="
+"\"%(login_url)s\">продовжити вхід тут</a>.\n"
 "          "
 
 #: aleksis/core/templates/socialaccount/signup.html:12
@@ -2460,7 +2600,8 @@ msgid ""
 "        %(site_name)s. As a final step, please complete the following form:"
 msgstr ""
 "Ви на шляху до використання свого облікового запису у %(provider_name)s\n"
-"        для входу на %(site_name)s. Заповніть, будь ласка, для завершення цю форму:"
+"        для входу на %(site_name)s. Заповніть, будь ласка, для завершення цю "
+"форму:"
 
 #: aleksis/core/templates/socialaccount/snippets/provider_list.html:12
 #, python-format
@@ -2549,21 +2690,25 @@ msgstr "Система виявила деякі нові проблеми з В
 #: aleksis/core/templates/templated_email/data_checks.email:6
 msgid ""
 "the system detected some new problems with your data.\n"
-"Please take some time to inspect them and solve the issues or mark them as ignored."
+"Please take some time to inspect them and solve the issues or mark them as "
+"ignored."
 msgstr ""
 "система виявила деякі нові проблеми з Вашими даними.\n"
-"Приділіть, будь ласка, трохи часу для їх перевірки та вирішення проблем або позначте їх для ігнору."
+"Приділіть, будь ласка, трохи часу для їх перевірки та вирішення проблем або "
+"позначте їх для ігнору."
 
 #: aleksis/core/templates/templated_email/data_checks.email:15
 msgid ""
 "\n"
 "   the system detected some new problems with your data.\n"
-"   Please take some time to inspect them and solve the issues or mark them as ignored.\n"
+"   Please take some time to inspect them and solve the issues or mark them "
+"as ignored.\n"
 "  "
 msgstr ""
 "\n"
 "   система виявила деякі нові проблеми з Вашими даними.\n"
-"   Приділіть, будь ласка, трохи часу для їх перевірки та вирішення проблем або позначте їх для ігнору.\n"
+"   Приділіть, будь ласка, трохи часу для їх перевірки та вирішення проблем "
+"або позначте їх для ігнору.\n"
 "  "
 
 #: aleksis/core/templates/templated_email/data_checks.email:23
@@ -2588,8 +2733,12 @@ msgstr "Привіт, %(person)s"
 #: aleksis/core/templates/templated_email/invitation.email:9
 #: aleksis/core/templates/templated_email/invitation.email:18
 #, python-format
-msgid "you have been invited to register on %(site)s. If you would like to accept this invitation, please click on the following link:"
-msgstr "Ви запрошені зареєструватися на %(site)s. Якщо хочете прийняти запрошення, перейдіть за посиланням:"
+msgid ""
+"you have been invited to register on %(site)s. If you would like to accept "
+"this invitation, please click on the following link:"
+msgstr ""
+"Ви запрошені зареєструватися на %(site)s. Якщо хочете прийняти запрошення, "
+"перейдіть за посиланням:"
 
 #: aleksis/core/templates/templated_email/notification.email:4
 msgid "New notification for"
@@ -2663,16 +2812,22 @@ msgid ""
 "\n"
 "        Backup tokens can be used when your primary and backup\n"
 "        phone numbers aren't available. The backup tokens below can be used\n"
-"        for login verification. If you've used up all your backup tokens, you\n"
-"        can generate a new set of backup tokens. Only the backup tokens shown\n"
+"        for login verification. If you've used up all your backup tokens, "
+"you\n"
+"        can generate a new set of backup tokens. Only the backup tokens "
+"shown\n"
 "        below will be valid.\n"
 "      "
 msgstr ""
 "\n"
-"        Резервні токени можуть бути використані коли Ваші основний та резервний\n"
-"        телефонні номери не доступні. Резервні токени, що нижче, можуть бути використані\n"
-"        для перевірки на вході. Коли Ви використаєте усі резервні токени, Ви\n"
-"        зможете згенерувати новий набір резервних токенів. Дійсними будуть лише токени,\n"
+"        Резервні токени можуть бути використані коли Ваші основний та "
+"резервний\n"
+"        телефонні номери не доступні. Резервні токени, що нижче, можуть бути "
+"використані\n"
+"        для перевірки на вході. Коли Ви використаєте усі резервні токени, "
+"Ви\n"
+"        зможете згенерувати новий набір резервних токенів. Дійсними будуть "
+"лише токени,\n"
 "        що нижче.\n"
 "      "
 
@@ -2723,7 +2878,9 @@ msgstr ""
 
 #: aleksis/core/templates/two_factor/core/login.html:51
 msgid "Please login with your account to use the external application."
-msgstr "Для використання зовнішнього додатку увійдіть, будь ласка, у свій обліковий запис."
+msgstr ""
+"Для використання зовнішнього додатку увійдіть, будь ласка, у свій обліковий "
+"запис."
 
 #: aleksis/core/templates/two_factor/core/login.html:58
 msgid "Please login to see this page."
@@ -2732,67 +2889,83 @@ msgstr "Для перегляду цієї сторінки, будь ласка
 #: aleksis/core/templates/two_factor/core/login.html:69
 msgid ""
 "\n"
-"                        We are calling your phone right now, please enter the\n"
+"                        We are calling your phone right now, please enter "
+"the\n"
 "                        digits you hear.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Ми зателефонуємо зараз на Ваш номер. Напишіть, будь ласка, цифри,\n"
+"                        Ми зателефонуємо зараз на Ваш номер. Напишіть, будь "
+"ласка, цифри,\n"
 "                        які Ви почуєте.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:74
 msgid ""
 "\n"
-"                        We sent you a text message, please enter the code we sent.\n"
+"                        We sent you a text message, please enter the code we "
+"sent.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Ми надіслали Вам текстове повідомлення. Напишіть, будь ласка, отриманий код.\n"
+"                        Ми надіслали Вам текстове повідомлення. Напишіть, "
+"будь ласка, отриманий код.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:78
 msgid ""
 "\n"
-"                        We sent you an email, please enter the code we sent.\n"
+"                        We sent you an email, please enter the code we "
+"sent.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Ми надіслали Вам ел.листа. Напишіть, будь ласка, отриманий код.\n"
+"                        Ми надіслали Вам ел.листа. Напишіть, будь ласка, "
+"отриманий код.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:82
 msgid ""
 "\n"
-"                        Please use your Webauthn-compatible device to authenticate.\n"
+"                        Please use your Webauthn-compatible device to "
+"authenticate.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Будь ласка, для аутентифікації користуйтеся пристроєм, сумісним з Webauthn.\n"
+"                        Будь ласка, для аутентифікації користуйтеся "
+"пристроєм, сумісним з Webauthn.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:86
 msgid ""
 "\n"
-"                        Please enter the code generated by your code generator.\n"
+"                        Please enter the code generated by your code "
+"generator.\n"
 "                      "
 msgstr ""
 "\n"
-"                        Напишіть, будь ласка, код з Вашого генератора кодів.\n"
+"                        Напишіть, будь ласка, код з Вашого генератора "
+"кодів.\n"
 "                      "
 
 #: aleksis/core/templates/two_factor/core/login.html:91
 msgid ""
 "\n"
-"                      Use this form for entering backup tokens for logging in.\n"
-"                      These tokens have been generated for you to print and keep safe. Please\n"
-"                      enter one of these backup tokens to login to your account.\n"
+"                      Use this form for entering backup tokens for logging "
+"in.\n"
+"                      These tokens have been generated for you to print and "
+"keep safe. Please\n"
+"                      enter one of these backup tokens to login to your "
+"account.\n"
 "                    "
 msgstr ""
 "\n"
-"                      Для входу скористайтеся формою для введення резервних токенів.\n"
-"                      Ці токени були згенеровані, щоб Ви їх роздрукували та зберігали у надійному місці.\n"
-"                      Щоб увійти напишіть, будь ласка, один з резервних токенів.\n"
+"                      Для входу скористайтеся формою для введення резервних "
+"токенів.\n"
+"                      Ці токени були згенеровані, щоб Ви їх роздрукували та "
+"зберігали у надійному місці.\n"
+"                      Щоб увійти напишіть, будь ласка, один з резервних "
+"токенів.\n"
 "                    "
 
 #: aleksis/core/templates/two_factor/core/login.html:119
@@ -2822,12 +2995,16 @@ msgstr "Дозвіл відсутній"
 #: aleksis/core/templates/two_factor/core/otp_required.html:10
 msgid ""
 "The page you requested enforces users to verify using\n"
-"          two-factor authentication for security reasons. You need to enable this\n"
+"          two-factor authentication for security reasons. You need to enable "
+"this\n"
 "          security feature in order to access this page."
 msgstr ""
-"Для перегляду запитуваної сторінки, з огляду на безпеку, необхідна додаткова\n"
-"          перевірка користувача з використанням двофакторної аутентифікації.\n"
-"          Для доступу до цієї сторінки Вам потрібно увімкнути ці функції безпеки."
+"Для перегляду запитуваної сторінки, з огляду на безпеку, необхідна "
+"додаткова\n"
+"          перевірка користувача з використанням двофакторної "
+"аутентифікації.\n"
+"          Для доступу до цієї сторінки Вам потрібно увімкнути ці функції "
+"безпеки."
 
 #: aleksis/core/templates/two_factor/core/otp_required.html:16
 msgid "Go back"
@@ -2881,20 +3058,24 @@ msgid ""
 "      "
 msgstr ""
 "\n"
-"        Оберіть, будь ласка, метод автентифікації, який Ви хочете використовувати:\n"
+"        Оберіть, будь ласка, метод автентифікації, який Ви хочете "
+"використовувати:\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup.html:27
 msgid ""
 "\n"
 "        To start using a code generator, please use your\n"
-"        favourite two-factor authentication (TOTP) app to scan the QR code below.\n"
+"        favourite two-factor authentication (TOTP) app to scan the QR code "
+"below.\n"
 "        Then enter the token generated by the app.\n"
 "      "
 msgstr ""
 "\n"
-"        Для того, щоби почати користуватися генератором кодів, скористайтеся, будь ласка, своїм\n"
-"        улюбленим додатком для двофакторної аутентифікації (TOTP) та проскануйте QR-код, що нижче.\n"
+"        Для того, щоби почати користуватися генератором кодів, "
+"скористайтеся, будь ласка, своїм\n"
+"        улюбленим додатком для двофакторної аутентифікації (TOTP) та "
+"проскануйте QR-код, що нижче.\n"
 "        Після цього напишіть отриманий генератором токен.\n"
 "      "
 
@@ -2907,7 +3088,8 @@ msgid ""
 msgstr ""
 "\n"
 "        Напишіть, будь ласка, номер телефону, на який Ви хочете\n"
-"        отримувати текстові повідомлення. На наступному кроці він буде перевірений.\n"
+"        отримувати текстові повідомлення. На наступному кроці він буде "
+"перевірений.\n"
 "      "
 
 #: aleksis/core/templates/two_factor/core/setup.html:45
@@ -2935,11 +3117,13 @@ msgstr ""
 #: aleksis/core/templates/two_factor/core/setup.html:60
 msgid ""
 "\n"
-"            We are calling your phone right now, please enter the digits you hear.\n"
+"            We are calling your phone right now, please enter the digits you "
+"hear.\n"
 "          "
 msgstr ""
 "\n"
-"            Ми зараз телефонуємо на Ваш номер, – напишіть, будь ласка, цифри, які Ви почуєте.\n"
+"            Ми зараз телефонуємо на Ваш номер, – напишіть, будь ласка, "
+"цифри, які Ви почуєте.\n"
 "          "
 
 #: aleksis/core/templates/two_factor/core/setup.html:66
@@ -2949,22 +3133,29 @@ msgid ""
 "          "
 msgstr ""
 "\n"
-"            Ми надіслали Вам текстове повідомлення. Напишіть, будь ласка, отриманий код.\n"
+"            Ми надіслали Вам текстове повідомлення. Напишіть, будь ласка, "
+"отриманий код.\n"
 "          "
 
 #: aleksis/core/templates/two_factor/core/setup.html:73
 msgid ""
 "\n"
-"          We've encountered an issue with the selected authentication method. Please\n"
-"          go back and verify that you entered your information correctly, try\n"
-"          again, or use a different authentication method instead. If the issue\n"
+"          We've encountered an issue with the selected authentication "
+"method. Please\n"
+"          go back and verify that you entered your information correctly, "
+"try\n"
+"          again, or use a different authentication method instead. If the "
+"issue\n"
 "          persists, contact the site administrator.\n"
 "        "
 msgstr ""
 "\n"
-"          Ми помітили, що існує проблема з використанням обраного методу авторизації. Поверніться,\n"
-"          будь ласка, назад, та переконайтеся, що необхідні дані вказані правильно\n"
-"          і спробуйте увійте ще раз, або ж скористайтеся іншим варіантом входу. Якщо ж\n"
+"          Ми помітили, що існує проблема з використанням обраного методу "
+"авторизації. Поверніться,\n"
+"          будь ласка, назад, та переконайтеся, що необхідні дані вказані "
+"правильно\n"
+"          і спробуйте увійте ще раз, або ж скористайтеся іншим варіантом "
+"входу. Якщо ж\n"
 "          проблема залишається, зверніться до адміністратора сайту.\n"
 "        "
 
@@ -2990,7 +3181,8 @@ msgstr "Двохфакторна автентифікація успішно у
 #: aleksis/core/templates/two_factor/core/setup_complete.html:13
 msgid ""
 "\n"
-"        Congratulations, you've successfully enabled two-factor authentication.\n"
+"        Congratulations, you've successfully enabled two-factor "
+"authentication.\n"
 "      "
 msgstr ""
 "\n"
@@ -3011,13 +3203,16 @@ msgstr "Створити резервні коди"
 msgid ""
 "\n"
 "          However, it might happen that you don't have access to\n"
-"          your primary device. To enable account recovery, generate backup codes\n"
+"          your primary device. To enable account recovery, generate backup "
+"codes\n"
 "          or add other authentication methods.\n"
 "        "
 msgstr ""
 "\n"
-"          Між іншим, може так статися, що Ви не матимете доступу до свого основного\n"
-"          пристрою. Для увімкнення можливості відновлення облікового запису створіть резервні коди\n"
+"          Між іншим, може так статися, що Ви не матимете доступу до свого "
+"основного\n"
+"          пристрою. Для увімкнення можливості відновлення облікового запису "
+"створіть резервні коди\n"
 "          або додайте інші методи аутентифікації.\n"
 "        "
 
@@ -3031,8 +3226,12 @@ msgid "Disable Two-Factor Authentication"
 msgstr "Вимкнути двохфакторну автентифікацію"
 
 #: aleksis/core/templates/two_factor/profile/disable.html:12
-msgid "You are about to disable two-factor authentication. This weakens your account security, are you sure?"
-msgstr "Ви вимикаєте двохфакторну автентифікацію. Це знизить захист Вашого облікового запису. Ви впевнені?"
+msgid ""
+"You are about to disable two-factor authentication. This weakens your "
+"account security, are you sure?"
+msgstr ""
+"Ви вимикаєте двохфакторну автентифікацію. Це знизить захист Вашого "
+"облікового запису. Ви впевнені?"
 
 #: aleksis/core/templates/two_factor/profile/disable.html:26
 msgid "Disable"
@@ -3074,15 +3273,16 @@ msgstr "Навчальний рік створений."
 msgid "The school term has been saved."
 msgstr "Навчальний рік збережений."
 
-#: aleksis/core/views.py:401
+#: aleksis/core/views.py:401 aleksis/core/views.py:371
 msgid "The child groups were successfully saved."
 msgstr "Підлеглі групи збережені."
 
 #: aleksis/core/views.py:420 aleksis/core/views.py:430
+#: aleksis/core/views.py:390 aleksis/core/views.py:400
 msgid "The person has been saved."
 msgstr "Особа збережена."
 
-#: aleksis/core/views.py:480
+#: aleksis/core/views.py:480 aleksis/core/views.py:450
 msgid "The group has been saved."
 msgstr "Група збережена."
 
@@ -3094,132 +3294,220 @@ msgstr "Режим обслуговування увімкнений успіш
 msgid "Maintenance mode was turned off successfully."
 msgstr "Режим обслуговування успішно вимкнений."
 
-#: aleksis/core/views.py:588
+#: aleksis/core/views.py:588 aleksis/core/views.py:558
 msgid "The announcement has been saved."
 msgstr "Оголошення збережене."
 
-#: aleksis/core/views.py:604
+#: aleksis/core/views.py:604 aleksis/core/views.py:574
 msgid "The announcement has been deleted."
 msgstr "Оголошення видалене."
 
-#: aleksis/core/views.py:673
+#: aleksis/core/views.py:673 aleksis/core/views.py:643
 msgid "The requested preference registry does not exist"
 msgstr "Журналу із запитаними властивостями не існує"
 
-#: aleksis/core/views.py:692
+#: aleksis/core/views.py:692 aleksis/core/views.py:662
 msgid "The preferences have been saved successfully."
 msgstr "Властивості збережені."
 
-#: aleksis/core/views.py:716
+#: aleksis/core/views.py:716 aleksis/core/views.py:686
 msgid "The person has been deleted."
 msgstr "Особа видалена."
 
-#: aleksis/core/views.py:730
+#: aleksis/core/views.py:730 aleksis/core/views.py:700
 msgid "The group has been deleted."
 msgstr "Група видалена."
 
-#: aleksis/core/views.py:762
+#: aleksis/core/views.py:762 aleksis/core/views.py:732
 msgid "The additional field has been saved."
 msgstr "Додаткове поле збережене."
 
-#: aleksis/core/views.py:797
+#: aleksis/core/views.py:797 aleksis/core/views.py:767
 msgid "The additional field has been deleted."
 msgstr "Додаткове поле видалене."
 
-#: aleksis/core/views.py:822
+#: aleksis/core/views.py:822 aleksis/core/views.py:792
 msgid "The group type has been saved."
 msgstr "Тип групи збережений."
 
-#: aleksis/core/views.py:853
+#: aleksis/core/views.py:853 aleksis/core/views.py:823
 msgid "The group type has been deleted."
 msgstr "Тип групи видалений."
 
-#: aleksis/core/views.py:888
+#: aleksis/core/views.py:888 aleksis/core/views.py:858
 msgid "Progress: Run data checks"
 msgstr "Перебіг: Запуск перевірки даних"
 
-#: aleksis/core/views.py:889
+#: aleksis/core/views.py:889 aleksis/core/views.py:859
 msgid "Run data checks …"
 msgstr "Запускається перевірка даних …"
 
-#: aleksis/core/views.py:890
+#: aleksis/core/views.py:890 aleksis/core/views.py:860
 msgid "The data checks were run successfully."
 msgstr "Перевірка даних успішно запущена."
 
-#: aleksis/core/views.py:891
+#: aleksis/core/views.py:891 aleksis/core/views.py:861
 msgid "There was a problem while running data checks."
 msgstr "Під час запуску перевірки даних виникла проблема."
 
-#: aleksis/core/views.py:908
+#: aleksis/core/views.py:908 aleksis/core/views.py:878
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr "Варіант розв'язання \"{solve_option_obj.verbose_name}\" "
 
-#: aleksis/core/views.py:918
+#: aleksis/core/views.py:918 aleksis/core/views.py:888
 msgid "The requested solve option does not exist"
 msgstr "Запитаний варіант розв'язання не існує"
 
-#: aleksis/core/views.py:951
+#: aleksis/core/views.py:951 aleksis/core/views.py:921
 msgid "The dashboard widget has been saved."
 msgstr "Віджет інформпанелі збережений."
 
-#: aleksis/core/views.py:981
+#: aleksis/core/views.py:981 aleksis/core/views.py:951
 msgid "The dashboard widget has been created."
 msgstr "Віджет інформпанелі створений."
 
-#: aleksis/core/views.py:991
+#: aleksis/core/views.py:991 aleksis/core/views.py:961
 msgid "The dashboard widget has been deleted."
 msgstr "Віджет інформпанелі видалений."
 
-#: aleksis/core/views.py:1063
+#: aleksis/core/views.py:1063 aleksis/core/views.py:1033
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ваша конфігурація інформпанелі збережена."
 
-#: aleksis/core/views.py:1065
+#: aleksis/core/views.py:1065 aleksis/core/views.py:1035
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr "Конфігурація типової/стандартної інформпанелі збережена."
 
-#: aleksis/core/views.py:1136
+#: aleksis/core/views.py:1136 aleksis/core/views.py:1106
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr "Запрошення успішно створене. Код запрошення: {code}"
 
-#: aleksis/core/views.py:1233
+#: aleksis/core/views.py:1233 aleksis/core/views.py:1203
 msgid "We have successfully assigned the permissions."
 msgstr "Ми успішно призначили дозволи."
 
-#: aleksis/core/views.py:1243
+#: aleksis/core/views.py:1243 aleksis/core/views.py:1213
 msgid "The global user permission has been deleted."
 msgstr "Глобальний користувацький дозвіл видалений."
 
-#: aleksis/core/views.py:1253
+#: aleksis/core/views.py:1253 aleksis/core/views.py:1223
 msgid "The global group permission has been deleted."
 msgstr "Глобальний груповий дозвіл видалений."
 
-#: aleksis/core/views.py:1263
+#: aleksis/core/views.py:1263 aleksis/core/views.py:1233
 msgid "The object user permission has been deleted."
 msgstr "Об'єктний користувацький дозвіл видалений."
 
-#: aleksis/core/views.py:1273
+#: aleksis/core/views.py:1273 aleksis/core/views.py:1243
 msgid "The object group permission has been deleted."
 msgstr "Об'єктний груповий дозвіл видалений."
 
-#: aleksis/core/views.py:1382
-msgid "The third-party account could not be disconnected because it is the only login method available."
-msgstr "Обліковий запис третьої сторони не можна від'єднати оскільки він єдиний спосіб для входу."
+#: aleksis/core/views.py:1382 aleksis/core/views.py:1352
+msgid ""
+"The third-party account could not be disconnected because it is the only "
+"login method available."
+msgstr ""
+"Обліковий запис третьої сторони не можна від'єднати оскільки він єдиний "
+"спосіб для входу."
 
-#: aleksis/core/views.py:1389
+#: aleksis/core/views.py:1389 aleksis/core/views.py:1359
 msgid "The third-party account has been successfully disconnected."
 msgstr "Обліковий запис третьої сторони успішно від'єднаний."
 
-#: aleksis/core/views.py:1465
-msgid "Person was invited successfully and an email with further instructions has been send to them."
-msgstr "Особа успішно запрошена. Лист з інструкціями щодо наступних дій надісланий на її ел.пошту."
+#: aleksis/core/views.py:1465 aleksis/core/views.py:1435
+msgid ""
+"Person was invited successfully and an email with further instructions has "
+"been send to them."
+msgstr ""
+"Особа успішно запрошена. Лист з інструкціями щодо наступних дій надісланий "
+"на її ел.пошту."
 
-#: aleksis/core/views.py:1476
+#: aleksis/core/views.py:1476 aleksis/core/views.py:1446
 msgid "Person was already invited."
 msgstr "Особа вже була запрошена."
 
+#: aleksis/core/models.py:1528
+#, fuzzy
+msgid "Start date and time"
+msgstr "Дата і час"
+
+#: aleksis/core/models.py:1530
+#, fuzzy
+msgid "End date and time"
+msgstr "Дата і час"
+
+#: aleksis/core/models.py:1531
+#, fuzzy
+msgid "Timezone"
+msgstr "Час"
+
+#: aleksis/core/models.py:1534
+#, fuzzy
+msgid "Recurrences"
+msgstr "Властивості"
+
+#: aleksis/core/models.py:1540
+msgid "Amended base event"
+msgstr ""
+
+#: aleksis/core/models.py:1643
+#, fuzzy
+msgid "Calendar Event"
+msgstr "Канали календарів"
+
+#: aleksis/core/models.py:1644
+#, fuzzy
+msgid "Calendar Events"
+msgstr "Канали календарів"
+
+#: aleksis/core/models.py:1670
+#, fuzzy
+msgid "Birthdays"
+msgstr "Календар Днів Народження"
+
+#: aleksis/core/models.py:1674
+#, fuzzy
+msgid "{}'s birthday"
+msgstr "%(name)s має День Народження"
+
+#: aleksis/core/models.py:1713 aleksis/core/models.py:1775
+msgid "Holidays"
+msgstr ""
+
+#: aleksis/core/models.py:1774
+msgid "Holiday"
+msgstr ""
+
+#: aleksis/core/preferences.py:33
+#, fuzzy
+msgid "Calendar"
+msgstr "Канали календарів"
+
+#: aleksis/core/preferences.py:490
+#, fuzzy
+msgid "Birthday calendar feed color"
+msgstr "Календар Днів Народження"
+
+#: aleksis/core/preferences.py:502
+msgid "Holiday calendar feed color"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+#, fuzzy
+msgid "Activated calendars"
+msgstr "Календар Днів Народження"
+
+#: aleksis/core/templates/account/email/email_confirmation_message.txt:6
+#, python-format
+msgid ""
+"Someone tried to register an account with the username %(user_display)s and "
+"your e-mail address on %(site_domain)s.\n"
+"If it was you, please confirm the registration by clicking on the following "
+"link:"
+msgstr ""
+
 #~ msgid "Revoke access"
 #~ msgstr "Відкликати доступ"
 
@@ -3240,11 +3528,13 @@ msgstr "Особа вже була запрошена."
 
 #~ msgid ""
 #~ "\n"
-#~ "            This page is currently unavailable. If this error persists, contact your site administrators:\n"
+#~ "            This page is currently unavailable. If this error persists, "
+#~ "contact your site administrators:\n"
 #~ "          "
 #~ msgstr ""
 #~ "\n"
-#~ "            Ця сторінка наразі недоступна. Якщо ця помилка виникне знову, зверніться до адміністраторів сайту:\n"
+#~ "            Ця сторінка наразі недоступна. Якщо ця помилка виникне знову, "
+#~ "зверніться до адміністраторів сайту:\n"
 #~ "          "
 
 #~ msgid ""
@@ -3253,7 +3543,8 @@ msgstr "Особа вже була запрошена."
 #~ "          security."
 #~ msgstr ""
 #~ "Двохфакторна автентифікація у Вашому обліковому записі\n"
-#~ "          вимкнена. Для збільшення безпеки обліковки увімкніть двохфакторну\n"
+#~ "          вимкнена. Для збільшення безпеки обліковки увімкніть "
+#~ "двохфакторну\n"
 #~ "          автентифікацію."
 
 #~ msgid "Add Phone Number"
@@ -3329,7 +3620,8 @@ msgstr "Особа вже була запрошена."
 #~ msgstr ""
 #~ "\n"
 #~ "        Хоча ми рішуче застерігаємо Вас це не робити, Ви можете \n"
-#~ "        також вимкнути двохфакторну автентифікацію для свого облікового запису.\n"
+#~ "        також вимкнути двохфакторну автентифікацію для свого облікового "
+#~ "запису.\n"
 #~ "      "
 
 #~ msgid ""
@@ -3341,7 +3633,8 @@ msgstr "Особа вже була запрошена."
 #~ msgstr ""
 #~ "\n"
 #~ "        Двохфакторна автентифікація для Вашого облікового запису\n"
-#~ "        не активована. Для підвищення безпеки облікового запису увімкніть\n"
+#~ "        не активована. Для підвищення безпеки облікового запису "
+#~ "увімкніть\n"
 #~ "        двохфакторну автентифікацію.\n"
 #~ "      "
 
@@ -3448,8 +3741,14 @@ msgstr "Особа вже була запрошена."
 #~ msgid "ICal Feeds"
 #~ msgstr "iCal-стрічки"
 
-#~ msgid "These are URLs for different Calendar Feeds in the iCal (.ics) format. You can create as many as you want and import them in your calendar software."
-#~ msgstr "Тут містяться посилання на різні стрічки календарів у форматі iCal (.ics). Ви можете створити їх стільки, скільки Вам необхідно та імпортувати їх у ПЗ для роботи з календарями."
+#~ msgid ""
+#~ "These are URLs for different Calendar Feeds in the iCal (.ics) format. "
+#~ "You can create as many as you want and import them in your calendar "
+#~ "software."
+#~ msgstr ""
+#~ "Тут містяться посилання на різні стрічки календарів у форматі iCal (."
+#~ "ics). Ви можете створити їх стільки, скільки Вам необхідно та імпортувати "
+#~ "їх у ПЗ для роботи з календарями."
 
 #~ msgid "Your iCal URLs"
 #~ msgstr "Ваші посилання iCal"
@@ -3474,24 +3773,30 @@ msgstr "Особа вже була запрошена."
 
 #~ msgid ""
 #~ "\n"
-#~ "              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
-#~ "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
+#~ "              This platform is powered by AlekSIS®, a web-based school "
+#~ "information system (SIS) which can be used\n"
+#~ "              to manage and/or publish organisational artifacts of "
+#~ "educational institutions. AlekSIS is free software and\n"
 #~ "              can be used by anyone.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Ця платформа базується на AlekSIS®, веб-інструменті інформаційної системи для навчання (SIS) за допомогою якої\n"
-#~ "              можна керувати та/або висвітлювати організаційні елементи навчальних закладів. AlekSIS - безплатне ПЗ\n"
+#~ "              Ця платформа базується на AlekSIS®, веб-інструменті "
+#~ "інформаційної системи для навчання (SIS) за допомогою якої\n"
+#~ "              можна керувати та/або висвітлювати організаційні елементи "
+#~ "навчальних закладів. AlekSIS - безплатне ПЗ\n"
 #~ "              і ним може користуватися будь-хто.\n"
 #~ "            "
 
 #~ msgid ""
 #~ "\n"
-#~ "              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+#~ "              AlekSIS® is a registered trademark of the AlekSIS open "
+#~ "source project, represented by Teckids e.V.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              AlekSIS® – зареєстрована торгова марка проекту з відкритим програмним кодом AlekSIS, що представлена Teckids e.V.\n"
+#~ "              AlekSIS® – зареєстрована торгова марка проекту з відкритим "
+#~ "програмним кодом AlekSIS, що представлена Teckids e.V.\n"
 #~ "            "
 
 #~ msgid "Website of AlekSIS"
@@ -3505,14 +3810,18 @@ msgstr "Особа вже була запрошена."
 
 #~ msgid ""
 #~ "\n"
-#~ "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
-#~ "              information from third-party apps, if installed, refer to the respective components below. The\n"
+#~ "              The core and the official apps of AlekSIS are licenced "
+#~ "under the EUPL, version 1.2 or later. For licence\n"
+#~ "              information from third-party apps, if installed, refer to "
+#~ "the respective components below. The\n"
 #~ "              licences are marked like this:\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Ядро та офіційні додатки AlekSIS ліцензовані EUPL, версії 1.2 та новіше. Для отримання інформації\n"
-#~ "              щодо додатків третіх сторін, якщо такі встановлені, перейдіть до відповідних компонентів нижче.\n"
+#~ "              Ядро та офіційні додатки AlekSIS ліцензовані EUPL, версії "
+#~ "1.2 та новіше. Для отримання інформації\n"
+#~ "              щодо додатків третіх сторін, якщо такі встановлені, "
+#~ "перейдіть до відповідних компонентів нижче.\n"
 #~ "              Ці ліцензії мають таку позначку:\n"
 #~ "            "
 
@@ -3530,11 +3839,13 @@ msgstr "Особа вже була запрошена."
 
 #~ msgid ""
 #~ "\n"
-#~ "              Without activated JavaScript the progress status can't be updated.\n"
+#~ "              Without activated JavaScript the progress status can't be "
+#~ "updated.\n"
 #~ "            "
 #~ msgstr ""
 #~ "\n"
-#~ "              Без активованого JavaScript статус виконання не зможе оновлюватися.\n"
+#~ "              Без активованого JavaScript статус виконання не зможе "
+#~ "оновлюватися.\n"
 #~ "            "
 
 #~ msgid "Language"
@@ -3545,12 +3856,14 @@ msgstr "Особа вже була запрошена."
 
 #~ msgid ""
 #~ "\n"
-#~ "            Your administrator account is not linked to any person. Therefore,\n"
+#~ "            Your administrator account is not linked to any person. "
+#~ "Therefore,\n"
 #~ "            a dummy person has been linked to your account.\n"
 #~ "          "
 #~ msgstr ""
 #~ "\n"
-#~ "            Ваш адміністративний обліковий запис не поєднаний з жодною особою. Через це\n"
+#~ "            Ваш адміністративний обліковий запис не поєднаний з жодною "
+#~ "особою. Через це\n"
 #~ "            до Вашого облікового запису приєднана фейкова особа.\n"
 #~ "          "
 
@@ -3563,8 +3876,10 @@ msgstr "Особа вже була запрошена."
 #~ msgstr ""
 #~ "\n"
 #~ "            Ваш обліковий запис не пов'язаний з фізособою. Це означає,\n"
-#~ "            що Ви не маєте жодного доступу до навчальної інформації. Зверніться,\n"
-#~ "            будь ласка, до адміністраторів AlekSIS у Вашому навчальному закладі.\n"
+#~ "            що Ви не маєте жодного доступу до навчальної інформації. "
+#~ "Зверніться,\n"
+#~ "            будь ласка, до адміністраторів AlekSIS у Вашому навчальному "
+#~ "закладі.\n"
 #~ "          "
 
 #~ msgid "Impersonate"
diff --git a/aleksis/core/managers.py b/aleksis/core/managers.py
index c79e756f509ea428359c95617e3e84d573985cf2..07237fdac02af7750a2000f89167cb9bbc217a5c 100644
--- a/aleksis/core/managers.py
+++ b/aleksis/core/managers.py
@@ -8,7 +8,7 @@ from django.db.models.manager import Manager
 
 from calendarweek import CalendarWeek
 from django_cte import CTEManager, CTEQuerySet
-from polymorphic.managers import PolymorphicManager
+from polymorphic.managers import PolymorphicManager, PolymorphicQuerySet
 
 
 class AlekSISBaseManager(_CurrentSiteManager):
@@ -141,3 +141,14 @@ class InstalledWidgetsDashboardWidgetOrderManager(Manager):
 
 class PolymorphicCurrentSiteManager(AlekSISBaseManager, PolymorphicManager):
     """Default manager for extensible, polymorphic models."""
+
+
+class HolidayQuerySet(DateRangeQuerySetMixin, PolymorphicQuerySet):
+    """QuerySet with custom query methods for holidays."""
+
+    def get_all_days(self) -> list[date]:
+        """Get all days included in the selected holidays."""
+        holiday_days = []
+        for holiday in self:
+            holiday_days += list(holiday.get_days())
+        return holiday_days
diff --git a/aleksis/core/migrations/0051_calendarevent_and_holiday.py b/aleksis/core/migrations/0051_calendarevent_and_holiday.py
new file mode 100644
index 0000000000000000000000000000000000000000..28e5294027d766f64d6cb644696e84f7a9c2a6ac
--- /dev/null
+++ b/aleksis/core/migrations/0051_calendarevent_and_holiday.py
@@ -0,0 +1,164 @@
+# Generated by Django 4.1.10 on 2023-07-11 19:01
+
+import aleksis.core.managers
+import aleksis.core.mixins
+from django.db import migrations, models
+import django.db.models.deletion
+import recurrence.fields
+import timezone_field.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("contenttypes", "0002_remove_content_type_name"),
+        ("sites", "0002_alter_domain_unique"),
+        ("core", "0050_managed_by_app_label"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="CalendarEvent",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
+                    ),
+                ),
+                (
+                    "managed_by_app_label",
+                    models.CharField(
+                        blank=True,
+                        editable=False,
+                        max_length=255,
+                        verbose_name="App label of app responsible for managing this instance",
+                    ),
+                ),
+                ("extended_data", models.JSONField(default=dict, editable=False)),
+                (
+                    "datetime_start",
+                    models.DateTimeField(blank=True, null=True, verbose_name="Start date and time"),
+                ),
+                (
+                    "datetime_end",
+                    models.DateTimeField(blank=True, null=True, verbose_name="End date and time"),
+                ),
+                (
+                    "timezone",
+                    timezone_field.fields.TimeZoneField(
+                        blank=True, null=True, verbose_name="Timezone"
+                    ),
+                ),
+                ("date_start", models.DateField(blank=True, null=True, verbose_name="Start date")),
+                ("date_end", models.DateField(blank=True, null=True, verbose_name="End date")),
+                (
+                    "recurrences",
+                    recurrence.fields.RecurrenceField(
+                        blank=True, null=True, verbose_name="Recurrences"
+                    ),
+                ),
+                (
+                    "amends",
+                    models.ForeignKey(
+                        blank=True,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="amended_by",
+                        to="core.calendarevent",
+                        verbose_name="Amended base event",
+                    ),
+                ),
+                (
+                    "polymorphic_ctype",
+                    models.ForeignKey(
+                        editable=False,
+                        null=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="polymorphic_%(app_label)s.%(class)s_set+",
+                        to="contenttypes.contenttype",
+                    ),
+                ),
+                (
+                    "site",
+                    models.ForeignKey(
+                        default=1,
+                        editable=False,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="+",
+                        to="sites.site",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Calendar Event",
+                "verbose_name_plural": "Calendar Events",
+                "ordering": ["datetime_start", "date_start", "datetime_end", "date_end"],
+            },
+            bases=(aleksis.core.mixins.CalendarEventMixin, models.Model),
+            managers=[
+                ("objects", aleksis.core.managers.PolymorphicCurrentSiteManager()),
+            ],
+        ),
+        migrations.CreateModel(
+            name="Holiday",
+            fields=[
+                (
+                    "calendarevent_ptr",
+                    models.OneToOneField(
+                        auto_created=True,
+                        on_delete=django.db.models.deletion.CASCADE,
+                        parent_link=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="core.calendarevent",
+                    ),
+                ),
+                ("holiday_name", models.CharField(max_length=255, verbose_name="Name")),
+            ],
+            options={
+                "verbose_name": "Holiday",
+                "verbose_name_plural": "Holidays",
+            },
+            bases=("core.calendarevent",),
+            managers=[
+                ("objects", aleksis.core.managers.PolymorphicCurrentSiteManager()),
+            ],
+        ),
+        migrations.AddConstraint(
+            model_name="calendarevent",
+            constraint=models.CheckConstraint(
+                check=models.Q(
+                    ("date_start__isnull", True), ("datetime_start__isnull", True), _negated=True
+                ),
+                name="datetime_start_or_date_start",
+            ),
+        ),
+        migrations.AddConstraint(
+            model_name="calendarevent",
+            constraint=models.CheckConstraint(
+                check=models.Q(
+                    ("date_end__isnull", True), ("datetime_end__isnull", True), _negated=True
+                ),
+                name="datetime_end_or_date_end",
+            ),
+        ),
+        migrations.AddConstraint(
+            model_name="calendarevent",
+            constraint=models.CheckConstraint(
+                check=models.Q(
+                    ("datetime_start__isnull", False), ("timezone__isnull", True), _negated=True
+                ),
+                name="timezone_if_datetime_start",
+            ),
+        ),
+        migrations.AddConstraint(
+            model_name="calendarevent",
+            constraint=models.CheckConstraint(
+                check=models.Q(
+                    ("datetime_end__isnull", False), ("timezone__isnull", True), _negated=True
+                ),
+                name="timezone_if_datetime_end",
+            ),
+        ),
+    ]
diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index b55a700db5390895c2c827a9ec13931bc55dbac9..c6c19af0a5f58a105377bb2a36502b562056739d 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -2,7 +2,7 @@
 
 import os
 from datetime import datetime
-from typing import Any, Callable, ClassVar, List, Optional, Union
+from typing import Any, Callable, ClassVar, Iterable, List, Optional, Union
 
 from django.conf import settings
 from django.contrib import messages
@@ -14,18 +14,21 @@ from django.db import models
 from django.db.models import JSONField, QuerySet
 from django.db.models.fields import CharField, TextField
 from django.forms.forms import BaseForm
-from django.forms.models import ModelForm, ModelFormMetaclass
-from django.http import HttpResponse
+from django.forms.models import ModelForm, ModelFormMetaclass, fields_for_model
+from django.http import HttpRequest, HttpResponse
 from django.utils.functional import classproperty, lazy
 from django.utils.translation import gettext as _
 from django.views.generic import CreateView, UpdateView
 from django.views.generic.edit import DeleteView, ModelFormMixin
 
+import recurring_ical_events
 import reversion
+from django_ical.feedgenerator import ITEM_ELEMENT_FIELD_MAP
 from dynamic_preferences.settings import preferences_settings
 from dynamic_preferences.types import FilePreference
 from guardian.admin import GuardedModelAdmin
 from guardian.core import ObjectPermissionChecker
+from icalendar import Calendar
 from jsonstore.fields import IntegerField, JSONFieldMixin
 from material.base import Fieldset, Layout, LayoutNode
 from polymorphic.base import PolymorphicModelBase
@@ -40,6 +43,8 @@ from aleksis.core.managers import (
     SchoolTermRelatedQuerySet,
 )
 
+from .util.core_helpers import ExtendedICal20Feed
+
 
 class _ExtensibleModelBase(models.base.ModelBase):
     """Ensure predefined behaviour on model creation.
@@ -571,7 +576,7 @@ class PublicFilePreferenceMixin(FilePreference):
 class RegistryObject:
     """Generic registry to allow registration of subclasses over all apps."""
 
-    _registry: ClassVar[Optional[dict[str, "RegistryObject"]]] = None
+    _registry: ClassVar[Optional[dict[str, type["RegistryObject"]]]] = None
     name: ClassVar[str] = ""
 
     def __init_subclass__(cls):
@@ -583,18 +588,178 @@ class RegistryObject:
             cls._register()
 
     @classmethod
-    def _register(cls):
+    def _register(cls: type["RegistryObject"]):
         if cls.name and cls.name not in cls._registry:
             cls._registry[cls.name] = cls
 
     @classproperty
-    def registered_objects_dict(cls):
+    def registered_objects_dict(cls) -> dict[str, type["RegistryObject"]]:
+        """Get dict of registered objects."""
         return cls._registry
 
     @classproperty
-    def registered_objects_list(cls):
+    def registered_objects_list(cls) -> list[type["RegistryObject"]]:
+        """Get list of registered objects."""
         return list(cls._registry.values())
 
     @classmethod
+    def get_object_by_name(cls, name: str) -> Optional[type["RegistryObject"]]:
+        """Get registered object by name."""
+        return cls.registered_objects_dict.get(name)
+
+
+class ObjectAuthenticator(RegistryObject):
+    def authenticate(self, request, obj):
+        raise NotImplementedError()
+
+
+class CalendarEventMixin(RegistryObject):
+    """Mixin for calendar feeds.
+
+    This mixin can be used to create calendar feeds for objects. It can be used
+    by adding it to a model or another object. The basic attributes of the calendar
+    can be set by either setting the attributes of the class or by implementing
+    the corresponding class methods. Please notice that the class methods are
+    overriding the attributes. The following attributes are mandatory:
+
+    - name: Unique name for the calendar feed
+    - verbose_name: Shown name of the feed
+
+    The respective class methods have a `get_` prefix and are called without any arguments.
+    There are also some more attributes. Please refer to the class signature for more
+    information.
+
+    The list of objects used to create the calendar feed have to be provided by
+    the method `get_objects` class method. It's mandatory to implement this method.
+
+    To provide the data for the events, a certain set of class methods can be implemented.
+    The following iCal attributes are supported:
+
+    guid, title, description, link, class, created, updateddate, start_datetime, end_datetime,
+    location, geolocation, transparency, organizer, attendee, rrule, rdate, exdate, valarm, status
+
+    To implement a method for a certain attribute, the name of the method has to be
+    `value_<your_attribute>`. For example, to implement the `title` attribute, the
+    method `value_title` has to be implemented. The method has to return the value
+    for the attribute. The method is called with the reference object as argument.
+    """
+
+    name: str = ""  # Unique name for the calendar feed
+    verbose_name: str = ""  # Shown name of the feed
+    link: str = ""  # Link for the feed, optional
+    description: str = ""  # Description of the feed, optional
+    color: str = "#222222"  # Color of the feed, optional
+
+    @classmethod
+    def get_verbose_name(cls, request) -> str:
+        """Return the verbose name of the calendar feed."""
+        return cls.verbose_name
+
+    @classmethod
+    def get_link(cls, request) -> str:
+        """Return the link of the calendar feed."""
+        return cls.link
+
+    @classmethod
+    def get_description(cls, request) -> str:
+        """Return the description of the calendar feed."""
+        return cls.description
+
+    @classmethod
+    def get_language(cls, request) -> str:
+        """Return the language of the calendar feed."""
+        return "en"  # FIXME
+
+    @classmethod
+    def get_color(cls, request) -> str:
+        """Return the color of the calendar feed."""
+        return cls.color
+
+    @classmethod
+    def create_event(cls, reference_object, feed: ExtendedICal20Feed, request) -> dict[str, Any]:
+        """Create an event for the given reference object and add it to the feed."""
+        values = {}
+        for field in cls.get_event_field_names():
+            field_value = cls.get_event_field_value(reference_object, field, request)
+            if field_value is not None:
+                values[field] = field_value
+        feed.add_item(**values)
+        return values
+
+    @classmethod
+    def start_feed(cls, request) -> ExtendedICal20Feed:
+        """Start the feed and return it."""
+        feed = ExtendedICal20Feed(
+            title=cls.get_verbose_name(request),
+            link=cls.get_link(request),
+            description=cls.get_description(request),
+            language=cls.get_language(request),
+            color=cls.get_color(request),
+        )
+        return feed
+
+    @classmethod
+    def get_objects(cls, request: HttpRequest) -> Iterable:
+        """Return the objects to create the calendar feed for."""
+        raise NotImplementedError
+
+    @classmethod
+    def create_feed(cls, request: HttpRequest) -> ExtendedICal20Feed:
+        """Create the calendar feed with all events."""
+        feed = cls.start_feed(request)
+
+        for reference_object in cls.get_objects(request):
+            cls.create_event(reference_object, feed, request)
+
+        return feed
+
+    @classmethod
+    def get_calendar_object(cls, request: HttpRequest) -> Calendar:
+        """Return the calendar object."""
+        feed = cls.create_feed(request)
+        return feed.get_calendar_object()
+
+    @classmethod
+    def get_single_events(cls, request: HttpRequest, start=None, end=None):
+        """Get single events for this calendar feed."""
+        feed = cls.create_feed(request)
+        return feed.get_single_events(start, end)
+
+    @classmethod
+    def get_event_field_names(cls) -> list[str]:
+        """Return the names of the fields to be used for the feed."""
+        return [field_map[0] for field_map in ITEM_ELEMENT_FIELD_MAP]
+
+    @classmethod
+    def get_event_field_value(cls, reference_object, field_name: str, request):
+        """Return the value for the given field name."""
+        method_name = f"value_{field_name}"
+        if hasattr(cls, method_name) and callable(getattr(cls, method_name)):
+            return getattr(cls, method_name)(reference_object, request)
+        return None
+
+    @classmethod
+    def value_link(cls, reference_object, request) -> str:
+        return ""
+
+    @classmethod
+    def value_color(cls, reference_object, request) -> str:
+        return cls.get_color(request)
+
+    @classproperty
+    def valid_feed(cls):
+        """Return if the feed is valid."""
+        return cls.name != cls.__name__
+
+    @classproperty
+    def valid_feeds(cls):
+        """Return a list of valid feeds."""
+        return [feed for feed in cls.registered_objects_list if feed.valid_feed]
+
+    @classproperty
+    def valid_feed_names(cls):
+        """Return a list of valid feed names."""
+        return [feed.name for feed in cls.valid_feeds]
+
     def get_object_by_name(cls, name):
-        cls.registered_objects_dict.get(name)
+        return cls.registered_objects_dict.get(name)
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index d90790427fb3c22531ec0cda50e9c48eb615e196..4151d51b39d4cafac8ba91250a5ed64b8d4af50e 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -3,7 +3,7 @@ import base64
 import hmac
 import uuid
 from datetime import date, datetime, timedelta
-from typing import Any, Iterable, List, Optional, Sequence, Union
+from typing import Any, Iterable, Iterator, List, Optional, Sequence, Union
 from urllib.parse import urljoin, urlparse
 
 from django.conf import settings
@@ -23,6 +23,7 @@ from django.dispatch import receiver
 from django.forms.widgets import Media
 from django.urls import reverse
 from django.utils import timezone
+from django.utils.formats import date_format
 from django.utils.functional import classproperty
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
@@ -31,12 +32,17 @@ import customidenticon
 import jsonstore
 from cachalot.api import cachalot_disabled
 from cache_memoize import cache_memoize
+from calendarweek import CalendarWeek
 from celery.result import AsyncResult
 from celery_progress.backend import Progress
 from ckeditor.fields import RichTextField
 from django_celery_results.models import TaskResult
 from django_cte import CTEQuerySet, With
+from django_ical.utils import build_rrule_from_recurrences_rrule, build_rrule_from_text
 from dynamic_preferences.models import PerInstancePreferenceModel
+from guardian.shortcuts import get_objects_for_user
+from icalendar import vCalAddress, vText
+from icalendar.prop import vRecur
 from invitations import signals
 from invitations.adapters import get_invitations_adapter
 from invitations.base_invitation import AbstractBaseInvitation
@@ -52,6 +58,8 @@ from oauth2_provider.models import (
 )
 from phonenumber_field.modelfields import PhoneNumberField
 from polymorphic.models import PolymorphicModel
+from recurrence.fields import RecurrenceField
+from timezone_field import TimeZoneField
 
 from aleksis.core.data_checks import (
     BrokenDashboardWidgetDataCheck,
@@ -63,12 +71,16 @@ from .managers import (
     CurrentSiteManagerWithoutMigrations,
     GroupManager,
     GroupQuerySet,
+    HolidayQuerySet,
     InstalledWidgetsDashboardWidgetOrderManager,
+    PolymorphicCurrentSiteManager,
     SchoolTermQuerySet,
     UninstallRenitentPolymorphicManager,
 )
 from .mixins import (
+    CalendarEventMixin,
     ExtensibleModel,
+    ExtensiblePolymorphicModel,
     GlobalPermissionModel,
     PureDjangoModel,
     RegistryObject,
@@ -365,6 +377,16 @@ class Person(ExtensibleModel):
         else:
             return self.identicon_url
 
+    def get_vcal_address(self, role: str = "REQ-PARTICIPANT") -> Optional[vCalAddress]:
+        """Return a vCalAddress object for this person."""
+        if not self.email:
+            # vCalAddress requires an email address
+            return None
+        vcal = vCalAddress(f"MAILTO:{self.email}")
+        vcal.params["cn"] = vText(self.full_name)
+        vcal.params["ROLE"] = vText(role)
+        return vcal
+
     def save(self, *args, **kwargs):
         # Determine all fields that were changed since last load
         changed = self.user_info_tracker.changed()
@@ -1484,3 +1506,270 @@ class Room(ExtensibleModel):
                 fields=["site_id", "short_name"], name="unique_room_short_name_per_site"
             ),
         ]
+
+
+class CalendarEvent(CalendarEventMixin, ExtensiblePolymorphicModel):
+    """A planned event in a calendar.
+
+    To make use of this model, you need to inherit from this model.
+    Every subclass of this model represents a certain calendar (feed).
+    It therefore needs to set the basic attributes of the calendar like
+    described in the documentation of `CalendarEventMixin`.
+
+    Furthermore, every `value_*` method from `CalendarEventMixin`
+    can be implemented to provide additional data (either static or dynamic).
+    Some like start and end date are pre-implemented in this model. Others, like
+    `value_title` need to be implemented in the subclass. Some methods are
+    also optional, like `value_location` or `value_description`.
+    Please refer to the documentation of `CalendarEventMixin` for more information.
+    """
+
+    datetime_start = models.DateTimeField(
+        verbose_name=_("Start date and time"), null=True, blank=True
+    )
+    datetime_end = models.DateTimeField(verbose_name=_("End date and time"), null=True, blank=True)
+    timezone = TimeZoneField(verbose_name=_("Timezone"), null=True, blank=True)
+    date_start = models.DateField(verbose_name=_("Start date"), null=True, blank=True)
+    date_end = models.DateField(verbose_name=_("End date"), null=True, blank=True)
+    recurrences = RecurrenceField(verbose_name=_("Recurrences"), null=True, blank=True)
+    amends = models.ForeignKey(
+        "self",
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+        verbose_name=_("Amended base event"),
+        related_name="amended_by",
+    )
+
+    def provide_list_in_timezone(self, seq):
+        """Provide a list of datetimes in the saved timezone."""
+        return [dt.astimezone(self.timezone) if isinstance(dt, datetime) else dt for dt in seq]
+
+    @classmethod
+    def value_title(cls, reference_object: "CalendarEvent", request) -> str:
+        """Return the title of the calendar event."""
+        raise NotImplementedError()
+
+    @classmethod
+    def value_start_datetime(
+        cls, reference_object: "CalendarEvent", request
+    ) -> Union[datetime, date]:
+        """Return the start datetime of the calendar event."""
+        if reference_object.datetime_start:
+            return reference_object.datetime_start.astimezone(reference_object.timezone)
+        return reference_object.date_start
+
+    @classmethod
+    def value_end_datetime(
+        cls, reference_object: "CalendarEvent", request
+    ) -> Union[datetime, date, None]:
+        """Return the end datetime of the calendar event."""
+        if reference_object.datetime_end:
+            return reference_object.datetime_end.astimezone(reference_object.timezone)
+        if reference_object.date_end == reference_object.date_start:
+            # Rule for all day events: If the event is only one day long,
+            # the end date has to be empty
+            return None
+        return reference_object.date_end
+
+    @classmethod
+    def value_rrule(cls, reference_object: "CalendarEvent", request) -> Optional[vRecur]:
+        """Return the rrule of the calendar event."""
+        if not reference_object.recurrences or not reference_object.recurrences.rrules:
+            return None
+        # iCal only supports one RRULE per event as per RFC 5545 (change to older RFC 2445)
+        return build_rrule_from_recurrences_rrule(reference_object.recurrences.rrules[0])
+
+    @classmethod
+    def value_rdate(cls, reference_object: "CalendarEvent", request) -> Optional[list[datetime]]:
+        """Return the rdate of the calendar event."""
+        if not reference_object.recurrences:
+            return None
+        return reference_object.provide_list_in_timezone(reference_object.recurrences.rdates)
+
+    @classmethod
+    def value_exrule(cls, reference_object: "CalendarEvent", request) -> Optional[vRecur]:
+        """Return the exrule of the calendar event."""
+        if not reference_object.recurrences or not reference_object.recurrences.exrules:
+            return None
+        return [build_rrule_from_recurrences_rrule(r) for r in reference_object.recurrences.exrules]
+
+    @classmethod
+    def value_exdate(cls, reference_object: "CalendarEvent", request) -> Optional[list[datetime]]:
+        """Return the exdate of the calendar event."""
+        if not reference_object.recurrences:
+            return None
+        # return reference_object.recurrences.exdates
+        return reference_object.provide_list_in_timezone(reference_object.recurrences.exdates)
+
+    @classmethod
+    def value_unique_id(cls, reference_object: "CalendarEvent", request) -> str:
+        """Return an unique identifier for an event."""
+        if reference_object.amends:
+            return cls.value_unique_id(reference_object.amends, request)
+        return f"{cls.name}-{reference_object.id}"
+
+    @classmethod
+    def value_recurrence_id(
+        cls, reference_object: "CalendarEvent", request
+    ) -> Optional[Union[datetime, date]]:
+        """Return the recurrence id of the calendar event."""
+        if reference_object.amends:
+            return reference_object.amends.value_start_datetime(reference_object, request)
+        return None
+
+    @classmethod
+    def value_color(cls, reference_object: "CalendarEvent", request) -> str:
+        """Return the color of the calendar."""
+        return cls.get_color(request)
+
+    @classmethod
+    def get_objects(cls, request) -> Iterable:
+        """Return all objects that should be included in the calendar."""
+        return cls.objects.instance_of(cls)
+
+    def save(self, *args, **kwargs):
+        if (
+            self.datetime_start
+            and self.datetime_end
+            and self.datetime_start.tzinfo != self.datetime_end.tzinfo
+        ):
+            self.datetime_end = self.datetime_end.astimezone(self.datetime_start.tzinfo)
+        if self.datetime_start and self.datetime_end:
+            self.timezone = self.datetime_start.tzinfo
+        super().save(*args, **kwargs)
+
+    class Meta:
+        verbose_name = _("Calendar Event")
+        verbose_name_plural = _("Calendar Events")
+        constraints = [
+            models.CheckConstraint(
+                check=~Q(datetime_start__isnull=True, date_start__isnull=True),
+                name="datetime_start_or_date_start",
+            ),
+            models.CheckConstraint(
+                check=~Q(datetime_end__isnull=True, date_end__isnull=True),
+                name="datetime_end_or_date_end",
+            ),
+            models.CheckConstraint(
+                check=~Q(datetime_start__isnull=False, timezone__isnull=True),
+                name="timezone_if_datetime_start",
+            ),
+            models.CheckConstraint(
+                check=~Q(datetime_end__isnull=False, timezone__isnull=True),
+                name="timezone_if_datetime_end",
+            ),
+        ]
+        ordering = ["datetime_start", "date_start", "datetime_end", "date_end"]
+
+
+class BirthdayEvent(CalendarEventMixin):
+    """A calendar feed with all birthdays."""
+
+    name = "birthdays"
+    verbose_name = _("Birthdays")
+
+    @classmethod
+    def value_title(cls, reference_object: Person, request) -> str:
+        return _("{}'s birthday").format(reference_object.addressing_name)
+
+    @classmethod
+    def value_description(cls, reference_object: Person, request) -> str:
+        return ("{name} was born on {birthday}.").format(
+            name=reference_object.addressing_name,
+            birthday=date_format(reference_object.date_of_birth),
+        )
+
+    @classmethod
+    def value_start_datetime(cls, reference_object: Person, request) -> date:
+        return reference_object.date_of_birth
+
+    @classmethod
+    def value_rrule(cls, reference_object: Person, request) -> vRecur:
+        return build_rrule_from_text("FREQ=YEARLY")
+
+    @classmethod
+    def value_unique_id(cls, reference_object: Person, request) -> str:
+        return f"birthday-{reference_object.id}"
+
+    @classmethod
+    def get_color(cls, request) -> str:
+        return get_site_preferences()["calendar__birthday_color"]
+
+    @classmethod
+    def get_objects(cls, request) -> QuerySet:
+        qs = Person.objects.filter(date_of_birth__isnull=False)
+        qs = qs.filter(
+            Q(pk=request.user.person.pk)
+            | Q(pk__in=get_objects_for_user(request.user, "core.view_personal_details", qs))
+        )
+        return qs
+
+
+class Holiday(CalendarEvent):
+    """Holiday model for keeping track of school holidays."""
+
+    name = "holidays"
+    verbose_name = _("Holidays")
+
+    @classmethod
+    def value_title(cls, reference_object: "Holiday", request) -> str:
+        return reference_object.holiday_name
+
+    @classmethod
+    def value_description(cls, reference_object: "Holiday", request) -> str:
+        return ""
+
+    @classmethod
+    def get_color(cls, request) -> str:
+        return get_site_preferences()["calendar__holiday_color"]
+
+    objects = PolymorphicCurrentSiteManager.from_queryset(HolidayQuerySet)()
+
+    holiday_name = models.CharField(verbose_name=_("Name"), max_length=255)
+
+    def get_days(self) -> Iterator[date]:
+        """Get all days included in the holiday."""
+        delta = self.date_end - self.date_start
+        for i in range(delta.days + 1):
+            yield self.date_start + timedelta(days=i)
+
+    @classmethod
+    def in_week(cls, week: CalendarWeek) -> dict[int, Optional["Holiday"]]:
+        """Get the holidays that are active in a given week."""
+        per_weekday = {}
+        holidays = Holiday.objects.in_week(week)
+
+        for weekday in range(0, 7):
+            holiday_date = week[weekday]
+            filtered_holidays = list(
+                filter(
+                    lambda h: holiday_date >= h.date_start and holiday_date <= h.date_end,
+                    holidays,
+                )
+            )
+            if filtered_holidays:
+                per_weekday[weekday] = filtered_holidays[0]
+
+        return per_weekday
+
+    @classmethod
+    def get_ex_dates(cls, datetime_start, datetime_end, recurrence):
+        """Get the dates to exclude for holidays."""
+        recurrence.dtstart = recurrence.dtstart.astimezone(timezone.get_current_timezone())
+        holiday_dates = [
+            h["DTSTART"].dt for h in Holiday.get_single_events(datetime_start, datetime_end)
+        ]
+        exdates = [
+            h.astimezone(timezone.utc)
+            for h in recurrence.occurrences()
+            if h.date() in holiday_dates
+        ]
+        return exdates
+
+    def __str__(self) -> str:
+        return self.holiday_name
+
+    class Meta:
+        verbose_name = _("Holiday")
+        verbose_name_plural = _("Holidays")
diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py
index 7d10c2ae5c4a7b2538ad907c227033ea6a0ef0f2..a3be1ac89770a89c2d3ea82b64aee7adc3913c9f 100644
--- a/aleksis/core/preferences.py
+++ b/aleksis/core/preferences.py
@@ -16,7 +16,7 @@ from dynamic_preferences.types import (
 )
 from oauth2_provider.models import AbstractApplication
 
-from .mixins import PublicFilePreferenceMixin
+from .mixins import CalendarEventMixin, PublicFilePreferenceMixin
 from .models import Group, Person
 from .registries import person_preferences_registry, site_preferences_registry
 from .util.notifications import get_notification_choices_lazy
@@ -30,6 +30,7 @@ footer = Section("footer", verbose_name=_("Footer"))
 account = Section("account", verbose_name=_("Accounts"))
 auth = Section("auth", verbose_name=_("Authentication"))
 internationalisation = Section("internationalisation", verbose_name=_("Internationalisation"))
+calendar = Section("calendar", verbose_name=_("Calendar"))
 
 
 @site_preferences_registry.register
@@ -477,3 +478,42 @@ class AutoUpdatingDashboardSite(BooleanPreference):
     name = "automatically_update_dashboard_site"
     default = True
     verbose_name = _("Automatically update the dashboard and its widgets sitewide")
+
+
+@site_preferences_registry.register
+class BirthdayFeedColor(StringPreference):
+    """Color for the birthdays calendar feed."""
+
+    section = calendar
+    name = "birthday_color"
+    default = "#0d5eaf"
+    verbose_name = _("Birthday calendar feed color")
+    widget = ColorWidget
+    required = True
+
+
+@site_preferences_registry.register
+class HolidayFeedColor(StringPreference):
+    """Color for the holidays calendar feed."""
+
+    section = calendar
+    name = "holiday_color"
+    default = "#0d5eaf"
+    verbose_name = _("Holiday calendar feed color")
+    widget = ColorWidget
+    required = True
+
+
+@person_preferences_registry.register
+class ActivatedCalendars(MultipleChoicePreference):
+    """Calendars that are activated for a person."""
+
+    section = calendar
+    name = "activated_calendars"
+    default = []
+    widget = SelectMultiple
+    verbose_name = _("Activated calendars")
+    required = False
+
+    field_attribute = {"initial": []}
+    choices = [(feed.name, feed.verbose_name) for feed in CalendarEventMixin.valid_feeds]
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index 6d77fcd70fb32712c4358a9bcec5a03e306140d5..ae2f12129cd8193cb0f5fba9887a6a45c2c92785 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -409,3 +409,6 @@ rules.add_perm("core.test_pdf_rule", test_pdf_generation_predicate)
 
 view_progress_predicate = has_person & is_own_celery_task
 rules.add_perm("core.view_progress_rule", view_progress_predicate)
+
+view_calendar_feed_predicate = has_person
+rules.add_perm("core.view_calendar_feed_rule", view_calendar_feed_predicate)
diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py
index ae137be9da07fb643e17e00fd2111892802cd24b..604e0d6cbb4b1f5c08c244c42ddc42b3e4084bbb 100644
--- a/aleksis/core/schema/__init__.py
+++ b/aleksis/core/schema/__init__.py
@@ -23,6 +23,7 @@ from ..models import (
 from ..util.apps import AppConfig
 from ..util.core_helpers import get_allowed_object_ids, get_app_module, get_app_packages, has_person
 from .base import FilterOrderList
+from .calendar import CalendarBaseType, SetCalendarStatusMutation
 from .celery_progress import CeleryProgressFetchedMutation, CeleryProgressType
 from .custom_menu import CustomMenuType
 from .dynamic_routes import DynamicRouteType
@@ -93,6 +94,8 @@ class Query(graphene.ObjectType):
 
     school_terms = FilterOrderList(SchoolTermType)
 
+    calendar = graphene.Field(CalendarBaseType)
+
     def resolve_ping(root, info, payload) -> str:
         return payload
 
@@ -209,6 +212,10 @@ class Query(graphene.ObjectType):
 
         return room_object
 
+    @staticmethod
+    def resolve_calendar(root, info, **kwargs):
+        return True
+
 
 class Mutation(graphene.ObjectType):
     update_person = PersonMutation.Field()
@@ -229,6 +236,8 @@ class Mutation(graphene.ObjectType):
     delete_school_terms = SchoolTermBatchDeleteMutation.Field()
     update_school_terms = SchoolTermBatchPatchMutation.Field()
 
+    set_calendar_status = SetCalendarStatusMutation.Field()
+
 
 def build_global_schema():
     """Build global GraphQL schema from all apps."""
diff --git a/aleksis/core/schema/calendar.py b/aleksis/core/schema/calendar.py
new file mode 100644
index 0000000000000000000000000000000000000000..110e2cf826f26314b278bf293724712473a61ba8
--- /dev/null
+++ b/aleksis/core/schema/calendar.py
@@ -0,0 +1,127 @@
+from datetime import datetime
+
+from django.core.exceptions import PermissionDenied
+from django.urls import reverse
+
+import graphene
+from graphene import ObjectType
+
+from aleksis.core.mixins import CalendarEventMixin
+from aleksis.core.util.core_helpers import has_person
+
+
+class CalendarEventType(ObjectType):
+    name = graphene.String()
+    description = graphene.String()
+    location = graphene.String(required=False)
+    start = graphene.String()
+    end = graphene.String()
+    color = graphene.String()
+    uid = graphene.String()
+    all_day = graphene.Boolean()
+    status = graphene.String()
+    meta = graphene.String()
+
+    def resolve_name(root, info, **kwargs):
+        return root["SUMMARY"]
+
+    def resolve_description(root, info, **kwargs):
+        return root["DESCRIPTION"]
+
+    def resolve_location(root, info, **kwargs):
+        return root.get("LOCATION", "")
+
+    def resolve_start(root, info, **kwargs):
+        return root["DTSTART"].dt
+
+    def resolve_end(root, info, **kwargs):
+        return root["DTEND"].dt
+
+    def resolve_color(root, info, **kwargs):
+        return root["COLOR"]
+
+    def resolve_uid(root, info, **kwargs):
+        return root["UID"]
+
+    def resolve_all_day(root, info, **kwargs):
+        return not isinstance(root["DTSTART"].dt, datetime)
+
+    def resolve_status(root, info, **kwargs):
+        return root.get("STATUS", "")
+
+    def resolve_meta(root, info, **kwargs):
+        return root.get("X-META", "{}")
+
+
+class CalendarFeedType(ObjectType):
+    events = graphene.List(
+        CalendarEventType,
+        start=graphene.Date(required=False),
+        end=graphene.Date(required=False),
+    )
+
+    def resolve_events(root, info, start=None, end=None, **kwargs):
+        return root.get_single_events(start, end)
+
+
+class CalendarType(ObjectType):
+    name = graphene.String(required=True)
+    verbose_name = graphene.String(required=True)
+    description = graphene.String()
+    feed = graphene.Field(CalendarFeedType)
+    color = graphene.String()
+
+    url = graphene.String()
+
+    activated = graphene.Boolean()
+
+    def resolve_verbose_name(root, info, **kwargs):
+        return root.get_verbose_name(info.context)
+
+    def resolve_description(root, info, **kwargs):
+        return root.get_description(info.context)
+
+    def resolve_feed(root, info, **kwargs):
+        return root.create_feed(info.context)
+
+    def resolve_url(root, info, **kwargs):
+        return info.context.build_absolute_uri(reverse("calendar_feed", args=[root.name]))
+
+    def resolve_color(root, info, **kwargs):
+        return root.get_color(info.context)
+
+    def resolve_activated(root, info, **kwargs):
+        return root.name in info.context.user.person.preferences["calendar__activated_calendars"]
+
+
+class SetCalendarStatusMutation(graphene.Mutation):
+    """Mutation to change the status of a calendar."""
+
+    class Arguments:
+        calendars = graphene.List(graphene.String)
+
+    ok = graphene.Boolean()
+
+    def mutate(root, info, calendars, **kwargs):
+        if not has_person(info.context):
+            raise PermissionDenied
+        calendar_feeds = [cal for cal in calendars if cal in CalendarEventMixin.valid_feed_names]
+        info.context.user.person.preferences["calendar__activated_calendars"] = calendar_feeds
+        return SetCalendarStatusMutation(ok=True)
+
+
+class CalendarBaseType(ObjectType):
+    calendar_feeds = graphene.List(CalendarType)
+
+    calendar_feeds_by_names = graphene.List(CalendarType, names=graphene.List(graphene.String))
+
+    all_feeds_url = graphene.String()
+
+    def resolve_calendar_feeds(root, info, **kwargs):
+        return CalendarEventMixin.valid_feeds
+
+    def resolve_calendar_feeds_by_names(root, info, names, **kwargs):
+        return [CalendarEventMixin.get_object_by_name(name) for name in names]
+
+    def resolve_all_feeds_url(root, info, **kwargs):
+        return info.context.build_absolute_uri(reverse("all_calendar_feeds"))
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index 77cda4ee58ed24e8f3df6bab2196643023b3b5f7..8307ae9c16e65073b2fcc890d7aeeac4cb9bf408 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -156,6 +156,7 @@ INSTALLED_APPS = [
     "rest_framework",
     "graphene_django",
     "dj_iconify.apps.DjIconifyConfig",
+    "recurrence",
 ]
 
 merge_app_settings("INSTALLED_APPS", INSTALLED_APPS, True)
@@ -937,7 +938,7 @@ LOGGING["root"] = {
 }
 # Configure global log Format
 LOGGING["formatters"]["verbose"] = {
-    "format": "{asctime} {levelname} {name}[{process}]: {msg}",
+    "format": "{asctime} {levelname} {name}[{process}]: {message}",
     "style": "{",
 }
 # Add null handler for selective silencing
diff --git a/aleksis/core/templates/core/base_simple_print.html b/aleksis/core/templates/core/base_simple_print.html
index 6e66e28983679acebdd2df0993ae7b5fdf64e621..abb01712a5f52d4ec9a44e34a31b11b8a29f9ef1 100644
--- a/aleksis/core/templates/core/base_simple_print.html
+++ b/aleksis/core/templates/core/base_simple_print.html
@@ -4,6 +4,8 @@
 <!DOCTYPE html>
 <html lang="{{ LANGUAGE_CODE }}">
 <head>
+  <base href="{{ BASE_URL }}" />
+
   {% include "core/partials/meta.html" %}
 
   <title>
diff --git a/aleksis/core/tests/mixins/test_registry_object.py b/aleksis/core/tests/mixins/test_registry_object.py
new file mode 100644
index 0000000000000000000000000000000000000000..d872e4a7078d27fd176a4b3f557bef3b55467fbb
--- /dev/null
+++ b/aleksis/core/tests/mixins/test_registry_object.py
@@ -0,0 +1,57 @@
+import pytest
+
+from aleksis.core.mixins import RegistryObject
+
+
+def test_registry_object_name():
+    class ExampleRegistry(RegistryObject):
+        pass
+
+    class ExampleWithManualName(ExampleRegistry):
+        name = "example_a"
+
+    class ExampleWithAutomaticName(ExampleRegistry):
+        pass
+
+    class ExampleWithOverridenAutomaticName(ExampleWithManualName):
+        name = "example_b"
+
+    class ExampleWithOverridenManualName(ExampleWithAutomaticName):
+        name = "example_bb"
+
+    assert ExampleRegistry.name == ""
+    assert ExampleWithManualName.name == "example_a"
+    assert ExampleWithAutomaticName.name == "ExampleWithAutomaticName"
+    assert ExampleWithOverridenAutomaticName.name == "example_b"
+    assert ExampleWithOverridenManualName.name == "example_bb"
+
+
+def test_registry_object_registry():
+    class ExampleRegistry(RegistryObject):
+        pass
+
+    class ExampleA(ExampleRegistry):
+        pass
+
+    class ExampleB(ExampleRegistry):
+        pass
+
+    class ExampleAA(ExampleA):
+        name = "example_aa"
+
+    assert ExampleRegistry.registered_objects_dict == {
+        "ExampleA": ExampleA,
+        "ExampleB": ExampleB,
+        "example_aa": ExampleAA,
+    }
+    assert ExampleRegistry.registered_objects_dict == ExampleRegistry._registry
+
+    assert ExampleRegistry.registered_objects_list == [
+        ExampleA,
+        ExampleB,
+        ExampleAA,
+    ]
+
+    assert ExampleRegistry.get_object_by_name("ExampleA") == ExampleA
+    assert ExampleRegistry.get_object_by_name("ExampleB") == ExampleB
+    assert ExampleRegistry.get_object_by_name("example_aa") == ExampleAA
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index a79562f9b19a311abc6eac48760b18169885d1b6..339a4bc2fee1c0546e62ca452793dfc7682e3aea 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -42,6 +42,21 @@ urlpatterns = [
     ),
     path("oauth/", include("oauth2_provider.urls", namespace="oauth2_provider")),
     path("system_status/", views.SystemStatusAPIView.as_view(), name="system_status_api"),
+    path(
+        "o/<str:app_label>/<str:model>/<int:pk>/",
+        views.ObjectRepresentationView.as_view(),
+        name="object_representation_with_pk",
+    ),
+    path(
+        "o/<str:app_label>/<str:model>/",
+        views.ObjectRepresentationView.as_view(),
+        name="object_representation_with_model",
+    ),
+    path(
+        "o/",
+        views.ObjectRepresentationView.as_view(),
+        name="object_representation_anonymous",
+    ),
     path("", include("django_prometheus.urls")),
     path(
         "django/",
@@ -386,6 +401,8 @@ urlpatterns = [
                     views.AssignPermissionView.as_view(),
                     name="assign_permission",
                 ),
+                path("feeds/<str:name>.ics", views.ICalFeedView.as_view(), name="calendar_feed"),
+                path("feeds.ics", views.ICalAllFeedsView.as_view(), name="all_calendar_feeds"),
             ]
         ),
     ),
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index c7f89fb5badfbca1e5014b78a150129afd990669..9329dda6192b554efa5e546a0160b5225e09da17 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -1,3 +1,4 @@
+import json
 import os
 from datetime import datetime, timedelta
 from importlib import import_module, metadata
@@ -19,9 +20,12 @@ from django.utils.crypto import get_random_string
 from django.utils.functional import lazy
 from django.utils.module_loading import import_string
 
+import django_ical.feedgenerator as feedgenerator
+import recurring_ical_events
 from cachalot.api import invalidate
 from cachalot.signals import post_invalidation
 from cache_memoize import cache_memoize
+from icalendar import Calendar, Event, Todo
 
 
 def copyright_years(years: Sequence[int], separator: str = ", ", joiner: str = "–") -> str:
@@ -489,3 +493,69 @@ def get_ip(*args, **kwargs):
     from ipware.ip import get_client_ip  # noqa
 
     return get_client_ip(*args, **kwargs)[0]
+
+
+feedgenerator.FEED_FIELD_MAP = feedgenerator.FEED_FIELD_MAP + (("color", "color"),)
+feedgenerator.ITEM_ELEMENT_FIELD_MAP = feedgenerator.ITEM_ELEMENT_FIELD_MAP + (
+    ("color", "color"),
+    ("meta", "x-meta"),
+)
+
+
+class ExtendedICal20Feed(feedgenerator.ICal20Feed):
+    """Extends the ICal20Feed class from django-ical.
+
+    Adds a method to return the actual calendar object.
+    """
+
+    def get_calendar_object(self, with_meta=True):
+        cal = Calendar()
+        cal.add("version", "2.0")
+        cal.add("calscale", "GREGORIAN")
+
+        for ifield, efield in feedgenerator.FEED_FIELD_MAP:
+            val = self.feed.get(ifield)
+            if val is not None:
+                cal.add(efield, val)
+
+        self.write_items(cal, with_meta=with_meta)
+
+        return cal
+
+    def write(self, outfile, encoding):
+        cal = self.get_calendar_object(with_meta=False)
+
+        to_ical = getattr(cal, "as_string", None)
+        if not to_ical:
+            to_ical = cal.to_ical
+        outfile.write(to_ical())
+
+    def write_items(self, calendar, with_meta=True):
+        for item in self.items:
+            component_type = item.get("component_type")
+            if component_type == "todo":
+                element = Todo()
+            else:
+                element = Event()
+            for ifield, efield in feedgenerator.ITEM_ELEMENT_FIELD_MAP:
+                val = item.get(ifield)
+                if val is not None:
+                    if ifield == "attendee":
+                        for list_item in val:
+                            element.add(efield, list_item)
+                    elif ifield == "valarm":
+                        for list_item in val:
+                            element.add_component(list_item)
+                    elif ifield == "meta":
+                        if with_meta:
+                            element.add(efield, json.dumps(val))
+                    else:
+                        element.add(efield, val)
+            calendar.add_component(element)
+
+    def get_single_events(self, start=None, end=None):
+        """Get single event objects for this feed."""
+        events = recurring_ical_events.of(self.get_calendar_object())
+        if start and end:
+            return events.between(start, end)
+        return events.all()
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 442a3d4d9e86f1ecba128e87f556f959dbfe06fb..f9e21e5b57472664e96bded0ebb471a11aff014b 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -96,7 +96,14 @@ from .forms import (
     SelectPermissionForm,
     SitePreferenceForm,
 )
-from .mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView, SuccessNextMixin
+from .mixins import (
+    AdvancedCreateView,
+    AdvancedDeleteView,
+    AdvancedEditView,
+    CalendarEventMixin,
+    ObjectAuthenticator,
+    SuccessNextMixin,
+)
 from .models import (
     AdditionalField,
     Announcement,
@@ -1548,3 +1555,103 @@ class LoggingGraphQLView(GraphQLView):
             if not isinstance(error.original_error, GraphQLError):
                 raise error
         return result
+
+
+class ObjectRepresentationView(View):
+    """View with unique URL to get a JSON representation of an object."""
+
+    def get_model(self, request: HttpRequest, app_label: str, model: str):
+        """Get the model by app label and model name."""
+        try:
+            return apps.get_model(app_label, model)
+        except LookupError:
+            raise Http404()
+
+    def get_object(self, request: HttpRequest, app_label: str, model: str, pk: int):
+        """Get the object by app label, model name and primary key."""
+        if getattr(self, "model", None) is None:
+            self.model = self.get_model(request, app_label, model)
+
+        try:
+            return self.model.objects.get(pk=pk)
+        except self.model.DoesNotExist:
+            raise Http404()
+
+    def get(
+        self,
+        request: HttpRequest,
+        app_label: Optional[str] = None,
+        model: Optional[str] = None,
+        pk: Optional[int] = None,
+        *args,
+        **kwargs,
+    ) -> HttpResponse:
+        if app_label and model:
+            self.model = self.get_model(request, app_label, model)
+        else:
+            self.model = None
+
+        if app_label and model and pk:
+            self.object = self.get_object(request, app_label, model, pk)
+        else:
+            self.object = None
+
+        authenticators = request.GET.get("authenticators", "").split(",")
+        if authenticators == [""]:
+            authenticators = list(ObjectAuthenticator.registered_objects_dict.keys())
+        self.authenticate(request, authenticators)
+
+        if hasattr(self.object, "get_json"):
+            res = self.object.get_json(request)
+        else:
+            res = {"id": self.object.id}
+        res["_meta"] = {
+            "model": self.object._meta.model_name,
+            "app": self.object._meta.app_label,
+        }
+
+        return JsonResponse(res)
+
+    def authenticate(self, request: HttpRequest, authenticators: list[str]) -> bool:
+        """Authenticate the request against the given authenticators."""
+        for authenticator in authenticators:
+            authenticator_class = ObjectAuthenticator.get_object_by_name(authenticator)
+            if not authenticator_class:
+                continue
+            obj = authenticator_class().authenticate(request, self.object)
+            if obj:
+                if self.object is None:
+                    self.object = obj
+                elif obj != self.object:
+                    raise BadRequest("Ambiguous objects identified")
+                return True
+
+        raise PermissionDenied()
+
+
+class ICalFeedView(PermissionRequiredMixin, View):
+    """View to generate an iCal feed for a calendar."""
+
+    permission_required = "core.view_calendar_feed_rule"
+
+    def get(self, request, name, *args, **kwargs):
+        if name in CalendarEventMixin.registered_objects_dict:
+            calendar = CalendarEventMixin.registered_objects_dict[name]
+            feed = calendar.create_feed(request)
+            response = HttpResponse(content_type="text/calendar")
+            feed.write(response, "utf-8")
+            return response
+        raise Http404
+
+
+class ICalAllFeedsView(PermissionRequiredMixin, View):
+    """View to generate an iCal feed for all calendars."""
+
+    permission_required = "core.view_calendar_feed_rule"
+
+    def get(self, request, *args, **kwargs):
+        response = HttpResponse(content_type="text/calendar")
+        for calendar in CalendarEventMixin.valid_feeds:
+            feed = calendar.create_feed(request)
+            feed.write(response, "utf-8")
+        return response
diff --git a/aleksis/core/vite.config.js b/aleksis/core/vite.config.js
index 415d12aa00f6b8a2bf2747e47d5cc3e32fd7aaa3..772fa5f27eccf8f93f1051cd51f8aaeb6e60535c 100644
--- a/aleksis/core/vite.config.js
+++ b/aleksis/core/vite.config.js
@@ -60,6 +60,28 @@ function generateMessageImportCode(assetDir, name, importAppName) {
   return code;
 }
 
+/**
+ * Generate code to import all components from a specified directory of a single AlekSIS app.
+ */
+function generateComponentsImportCode(
+  assetDir,
+  componentsDir,
+  name,
+  exportName
+) {
+  let code = "";
+  let componentsPath = assetDir + "/components" + componentsDir;
+  if (fs.existsSync(componentsPath)) {
+    const files = fs.readdirSync(componentsPath);
+    for (file of files) {
+      let componentName = file.split(".")[0];
+      code += `import ${componentName} from '${componentsPath + file}';\n`;
+      code += `${exportName}["${componentName.toLowerCase()}"] = ${componentName};\n`;
+    }
+  }
+  return code;
+}
+
 /**
  * Generate a virtual module that helps the AlekSIS-Core frontend code import other apps.
  *
@@ -69,6 +91,8 @@ function generateMessageImportCode(assetDir, name, importAppName) {
 function generateAppImporter(appDetails) {
   let code = "let appObjects = {};\n";
   code += "let appMessages = {};\n";
+  code += "let calendarFeedDetailComponents = {};\n";
+  code += "let calendarFeedEventBarComponents = {};\n";
 
   for (const [appPackage, appMeta] of Object.entries(appDetails)) {
     let indexPath = appMeta.assetDir + "/index.js";
@@ -86,13 +110,46 @@ function generateAppImporter(appDetails) {
         importAppName
       );
     }
+
+    // Include calendar feed detail components from all apps
+    code += generateComponentsImportCode(
+      appMeta.assetDir,
+      "/calendar_feeds/details/",
+      appMeta.name,
+      "calendarFeedDetailComponents"
+    );
+
+    // Include calendar feed event bar components from all apps
+    code += generateComponentsImportCode(
+      appMeta.assetDir,
+      "/calendar_feeds/event_bar/",
+      appMeta.name,
+      "calendarFeedEventBarComponents"
+    );
   }
 
   // Include core messages
   code += generateMessageImportCode(django_values.coreAssetDir, "core", "Core");
 
+  // Include core calendar feed detail components
+  code += generateComponentsImportCode(
+    django_values.coreAssetDir,
+    "/calendar_feeds/details/",
+    "core",
+    "calendarFeedDetailComponents"
+  );
+
+  // Include core calendar feed event bar components
+  code += generateComponentsImportCode(
+    django_values.coreAssetDir,
+    "/calendar_feeds/event_bar/",
+    "core",
+    "calendarFeedEventBarComponents"
+  );
+
   code += "export default appObjects;\n";
-  code += "export { appObjects, appMessages };\n";
+  code +=
+    "export { appObjects, appMessages, calendarFeedDetailComponents, calendarFeedEventBarComponents };\n";
 
   return code;
 }
@@ -202,7 +259,7 @@ export default defineConfig({
         directoryIndex: null,
         navigateFallbackAllowlist: [
           new RegExp(
-            "^/(?!(django|admin|graphql|__icons__|oauth/authorize))[^.]*$"
+            "^/(?!(django|admin|graphql|__icons__|oauth/authorize|o))[^.]*$"
           ),
         ],
         additionalManifestEntries: [
@@ -217,7 +274,7 @@ export default defineConfig({
         runtimeCaching: [
           {
             urlPattern: new RegExp(
-              "^/(?!(django|admin|graphql|__icons__|oauth/authorize))[^.]*$"
+              "^/(?!(django|admin|graphql|__icons__|oauth/authorize|o))[^.]*$"
             ),
             handler: "CacheFirst",
           },
diff --git a/docs/admin/10_install.rst b/docs/admin/10_install.rst
index b829cd2c92710efa6050f068319ef052e9b4138e..98597a251ef3f8fb05c5130687df1f447a59c1d3 100644
--- a/docs/admin/10_install.rst
+++ b/docs/admin/10_install.rst
@@ -57,6 +57,7 @@ Install some packages from the Debian package system.
                chromium \
                chromium-driver \
                redis-server \
+               pkg-config \
                postgresql \
                locales-all \
                celery
diff --git a/pyproject.toml b/pyproject.toml
index 2a38377509bcb52ce50fc57917a19d209d863618..73fd5c29b41fcb4d64940a4c4f43a296e807e4e0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -124,11 +124,15 @@ django-cte = "^1.1.5"
 pycountry = "^22.0.0"
 django-iconify = "^0.3"
 customidenticon = "^0.1.5"
-graphene-django = ">=3.0.0, <=3.1.2"
+graphene-django = ">=3.0.0, <=3.1.3"
 selenium = "^4.4.3"
 django-vite = "^2.0.2"
-graphene-django-cud = "^0.10.0"
+graphene-django-cud = "^0.11.0"
 uwsgi = "^2.0.21"
+django-ical = "^1.9.2"
+django-recurrence = "^1.11.1"
+recurring-ical-events = "^2.0.2"
+django-timezone-field = "^5.0"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]