diff --git a/.dev-js/.eslintrc.js b/.dev-js/.eslintrc.js
new file mode 100644
index 0000000000000000000000000000000000000000..40571d02f4c429d79d9ec85a4f37ba8c78f74b4c
--- /dev/null
+++ b/.dev-js/.eslintrc.js
@@ -0,0 +1,252 @@
+module.exports = {
+  root: true,
+  overrides: [
+    {
+      files: ["*.js", "*.vue"],
+      // parser: "vue-eslint-parser",
+      //processor: "@graphql-eslint/graphql",
+      extends: [
+        "eslint:recommended",
+        "plugin:vue/strongly-recommended",
+        "plugin:@intlify/vue-i18n/recommended",
+      ],
+      rules: {
+        "no-unused-vars": "warn",
+        "vue/no-unused-vars": "off",
+        "vue/multi-word-component-names": "off",
+        "vue/attribute-hyphenation": "error",
+        "vue/v-slot-style": "error",
+        "@intlify/vue-i18n/key-format-style": [
+          "error",
+          "snake_case",
+          {
+            splitByDots: false,
+          },
+        ],
+        // "@intlify/vue-i18n/no-unused-keys": ["warn", {}],
+        "@intlify/vue-i18n/no-raw-text": [
+          "error",
+          {
+            ignoreNodes: ["v-icon"],
+            ignorePattern: "^[-–—·#:()\\[\\]&\\.\\s]+$",
+          },
+        ],
+        "@intlify/vue-i18n/no-deprecated-tc": "off",
+        // Fixes for prettier (avoid eslint-config-prettier)
+        // The following rules can be used in some cases. See the README for more
+        // information. (These are marked with `0` instead of `"off"` so that a
+        // script can distinguish them.)
+        curly: 0,
+        "lines-around-comment": 0,
+        "max-len": 0,
+        "no-confusing-arrow": 0,
+        "no-mixed-operators": 0,
+        "no-tabs": 0,
+        "no-unexpected-multiline": 0,
+        quotes: 0,
+        "@typescript-eslint/quotes": 0,
+        "babel/quotes": 0,
+        "vue/html-self-closing": 0,
+        "vue/max-len": 0,
+
+        // The rest are rules that you never need to enable when using Prettier.
+        "array-bracket-newline": "off",
+        "array-bracket-spacing": "off",
+        "array-element-newline": "off",
+        "arrow-parens": "off",
+        "arrow-spacing": "off",
+        "block-spacing": "off",
+        "brace-style": "off",
+        "comma-dangle": "off",
+        "comma-spacing": "off",
+        "comma-style": "off",
+        "computed-property-spacing": "off",
+        "dot-location": "off",
+        "eol-last": "off",
+        "func-call-spacing": "off",
+        "function-call-argument-newline": "off",
+        "function-paren-newline": "off",
+        "generator-star": "off",
+        "generator-star-spacing": "off",
+        "implicit-arrow-linebreak": "off",
+        indent: "off",
+        "jsx-quotes": "off",
+        "key-spacing": "off",
+        "keyword-spacing": "off",
+        "linebreak-style": "off",
+        "multiline-ternary": "off",
+        "newline-per-chained-call": "off",
+        "new-parens": "off",
+        "no-arrow-condition": "off",
+        "no-comma-dangle": "off",
+        "no-extra-parens": "off",
+        "no-extra-semi": "off",
+        "no-floating-decimal": "off",
+        "no-mixed-spaces-and-tabs": "off",
+        "no-multi-spaces": "off",
+        "no-multiple-empty-lines": "off",
+        "no-reserved-keys": "off",
+        "no-space-before-semi": "off",
+        "no-trailing-spaces": "off",
+        "no-whitespace-before-property": "off",
+        "no-wrap-func": "off",
+        "nonblock-statement-body-position": "off",
+        "object-curly-newline": "off",
+        "object-curly-spacing": "off",
+        "object-property-newline": "off",
+        "one-var-declaration-per-line": "off",
+        "operator-linebreak": "off",
+        "padded-blocks": "off",
+        "quote-props": "off",
+        "rest-spread-spacing": "off",
+        semi: "off",
+        "semi-spacing": "off",
+        "semi-style": "off",
+        "space-after-function-name": "off",
+        "space-after-keywords": "off",
+        "space-before-blocks": "off",
+        "space-before-function-paren": "off",
+        "space-before-function-parentheses": "off",
+        "space-before-keywords": "off",
+        "space-in-brackets": "off",
+        "space-in-parens": "off",
+        "space-infix-ops": "off",
+        "space-return-throw-case": "off",
+        "space-unary-ops": "off",
+        "space-unary-word-ops": "off",
+        "switch-colon-spacing": "off",
+        "template-curly-spacing": "off",
+        "template-tag-spacing": "off",
+        "unicode-bom": "off",
+        "wrap-iife": "off",
+        "wrap-regex": "off",
+        "yield-star-spacing": "off",
+        "@babel/object-curly-spacing": "off",
+        "@babel/semi": "off",
+        "@typescript-eslint/brace-style": "off",
+        "@typescript-eslint/comma-dangle": "off",
+        "@typescript-eslint/comma-spacing": "off",
+        "@typescript-eslint/func-call-spacing": "off",
+        "@typescript-eslint/indent": "off",
+        "@typescript-eslint/keyword-spacing": "off",
+        "@typescript-eslint/member-delimiter-style": "off",
+        "@typescript-eslint/no-extra-parens": "off",
+        "@typescript-eslint/no-extra-semi": "off",
+        "@typescript-eslint/object-curly-spacing": "off",
+        "@typescript-eslint/semi": "off",
+        "@typescript-eslint/space-before-blocks": "off",
+        "@typescript-eslint/space-before-function-paren": "off",
+        "@typescript-eslint/space-infix-ops": "off",
+        "@typescript-eslint/type-annotation-spacing": "off",
+        "babel/object-curly-spacing": "off",
+        "babel/semi": "off",
+        "flowtype/boolean-style": "off",
+        "flowtype/delimiter-dangle": "off",
+        "flowtype/generic-spacing": "off",
+        "flowtype/object-type-curly-spacing": "off",
+        "flowtype/object-type-delimiter": "off",
+        "flowtype/quotes": "off",
+        "flowtype/semi": "off",
+        "flowtype/space-after-type-colon": "off",
+        "flowtype/space-before-generic-bracket": "off",
+        "flowtype/space-before-type-colon": "off",
+        "flowtype/union-intersection-spacing": "off",
+        "react/jsx-child-element-spacing": "off",
+        "react/jsx-closing-bracket-location": "off",
+        "react/jsx-closing-tag-location": "off",
+        "react/jsx-curly-newline": "off",
+        "react/jsx-curly-spacing": "off",
+        "react/jsx-equals-spacing": "off",
+        "react/jsx-first-prop-new-line": "off",
+        "react/jsx-indent": "off",
+        "react/jsx-indent-props": "off",
+        "react/jsx-max-props-per-line": "off",
+        "react/jsx-newline": "off",
+        "react/jsx-one-expression-per-line": "off",
+        "react/jsx-props-no-multi-spaces": "off",
+        "react/jsx-tag-spacing": "off",
+        "react/jsx-wrap-multilines": "off",
+        "standard/array-bracket-even-spacing": "off",
+        "standard/computed-property-even-spacing": "off",
+        "standard/object-curly-even-spacing": "off",
+        "unicorn/empty-brace-spaces": "off",
+        "unicorn/no-nested-ternary": "off",
+        "unicorn/number-literal-case": "off",
+        "vue/array-bracket-newline": "off",
+        "vue/array-bracket-spacing": "off",
+        "vue/arrow-spacing": "off",
+        "vue/block-spacing": "off",
+        "vue/block-tag-newline": "off",
+        "vue/brace-style": "off",
+        "vue/comma-dangle": "off",
+        "vue/comma-spacing": "off",
+        "vue/comma-style": "off",
+        "vue/dot-location": "off",
+        "vue/func-call-spacing": "off",
+        "vue/html-closing-bracket-newline": "off",
+        "vue/html-closing-bracket-spacing": "off",
+        "vue/html-end-tags": "off",
+        "vue/html-indent": "off",
+        "vue/html-quotes": "off",
+        "vue/key-spacing": "off",
+        "vue/keyword-spacing": "off",
+        "vue/max-attributes-per-line": "off",
+        "vue/multiline-html-element-content-newline": "off",
+        "vue/multiline-ternary": "off",
+        "vue/mustache-interpolation-spacing": "off",
+        "vue/no-extra-parens": "off",
+        "vue/no-multi-spaces": "off",
+        "vue/no-spaces-around-equal-signs-in-attribute": "off",
+        "vue/object-curly-newline": "off",
+        "vue/object-curly-spacing": "off",
+        "vue/object-property-newline": "off",
+        "vue/operator-linebreak": "off",
+        "vue/quote-props": "off",
+        "vue/script-indent": "off",
+        "vue/singleline-html-element-content-newline": "off",
+        "vue/space-in-parens": "off",
+        "vue/space-infix-ops": "off",
+        "vue/space-unary-ops": "off",
+        "vue/template-curly-spacing": "off",
+      },
+      settings: {
+        "vue-i18n": {
+          localeDir: "./aleksis/core/frontend/messages/*.{json}",
+          messageSyntaxVersion: "^8.0.0",
+        },
+      },
+      env: {
+        es2021: true,
+      },
+      parserOptions: {
+        ecmaVersion: "latest",
+      },
+    },
+    {
+      files: ["*.graphql"],
+      parser: "@graphql-eslint/eslint-plugin",
+      plugins: ["@graphql-eslint"],
+      extends: "plugin:@graphql-eslint/operations-recommended",
+      parserOptions: {
+        graphQLConfig: {
+          schema: "./schema.json",
+          documents: "../aleksis/**/*/frontend/**/*.graphql",
+        },
+      },
+      rules: {
+        "@graphql-eslint/no-anonymous-operations": "error",
+        "@graphql-eslint/no-duplicate-fields": "error",
+        "@graphql-eslint/naming-convention": [
+          "error",
+          {
+            OperationDefinition: {
+              style: "camelCase",
+              forbiddenPrefixes: ["Query", "Mutation", "Subscription", "Get"],
+              forbiddenSuffixes: ["Query", "Mutation", "Subscription"],
+            },
+          },
+        ],
+      },
+    },
+  ],
+};
diff --git a/.dev-js/package.json b/.dev-js/package.json
index 5c1f550ce1732d21180fa22481010331304be36e..298e6c130173b7870174169d5968bf58e2a41050 100644
--- a/.dev-js/package.json
+++ b/.dev-js/package.json
@@ -2,11 +2,13 @@
   "name": "aleksis-builddeps",
   "version": "1.0.0",
   "dependencies": {
+    "@graphql-eslint/eslint-plugin": "^4.3.0",
     "@intlify/eslint-plugin-vue-i18n": "^3.0.0",
     "eslint": "^8.26.0",
     "eslint-config-prettier": "^9.0.0",
     "eslint-plugin-vue": "^9.7.0",
-    "prettier": "^3.0.0",
+    "graphql": "^16.10.0",
+    "prettier": "^3.4.0",
     "stylelint": "^16.0.0",
     "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-standard": "^34.0.0"
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index 60317f4987d885fc380d731c6dd8792325342f44..0000000000000000000000000000000000000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,215 +0,0 @@
-module.exports = {
-  extends: [
-    "eslint:recommended",
-    "plugin:vue/strongly-recommended",
-    "plugin:@intlify/vue-i18n/recommended",
-  ],
-  rules: {
-    "no-unused-vars": "warn",
-    "vue/no-unused-vars": "off",
-    "vue/multi-word-component-names": "off",
-    "@intlify/vue-i18n/key-format-style": [
-      "error",
-      "snake_case",
-      {
-        splitByDots: false,
-      },
-    ],
-    // "@intlify/vue-i18n/no-unused-keys": ["warn", {}],
-    "@intlify/vue-i18n/no-raw-text": [
-      "error",
-      {
-        ignoreNodes: ["v-icon"],
-        ignorePattern: "^[-–—·#:()\\[\\]&\\.\\s]+$",
-      },
-    ],
-    // Fixes for prettier (avoid eslint-config-prettier)
-    // The following rules can be used in some cases. See the README for more
-    // information. (These are marked with `0` instead of `"off"` so that a
-    // script can distinguish them.)
-    curly: 0,
-    "lines-around-comment": 0,
-    "max-len": 0,
-    "no-confusing-arrow": 0,
-    "no-mixed-operators": 0,
-    "no-tabs": 0,
-    "no-unexpected-multiline": 0,
-    quotes: 0,
-    "@typescript-eslint/quotes": 0,
-    "babel/quotes": 0,
-    "vue/html-self-closing": 0,
-    "vue/max-len": 0,
-
-    // The rest are rules that you never need to enable when using Prettier.
-    "array-bracket-newline": "off",
-    "array-bracket-spacing": "off",
-    "array-element-newline": "off",
-    "arrow-parens": "off",
-    "arrow-spacing": "off",
-    "block-spacing": "off",
-    "brace-style": "off",
-    "comma-dangle": "off",
-    "comma-spacing": "off",
-    "comma-style": "off",
-    "computed-property-spacing": "off",
-    "dot-location": "off",
-    "eol-last": "off",
-    "func-call-spacing": "off",
-    "function-call-argument-newline": "off",
-    "function-paren-newline": "off",
-    "generator-star": "off",
-    "generator-star-spacing": "off",
-    "implicit-arrow-linebreak": "off",
-    indent: "off",
-    "jsx-quotes": "off",
-    "key-spacing": "off",
-    "keyword-spacing": "off",
-    "linebreak-style": "off",
-    "multiline-ternary": "off",
-    "newline-per-chained-call": "off",
-    "new-parens": "off",
-    "no-arrow-condition": "off",
-    "no-comma-dangle": "off",
-    "no-extra-parens": "off",
-    "no-extra-semi": "off",
-    "no-floating-decimal": "off",
-    "no-mixed-spaces-and-tabs": "off",
-    "no-multi-spaces": "off",
-    "no-multiple-empty-lines": "off",
-    "no-reserved-keys": "off",
-    "no-space-before-semi": "off",
-    "no-trailing-spaces": "off",
-    "no-whitespace-before-property": "off",
-    "no-wrap-func": "off",
-    "nonblock-statement-body-position": "off",
-    "object-curly-newline": "off",
-    "object-curly-spacing": "off",
-    "object-property-newline": "off",
-    "one-var-declaration-per-line": "off",
-    "operator-linebreak": "off",
-    "padded-blocks": "off",
-    "quote-props": "off",
-    "rest-spread-spacing": "off",
-    semi: "off",
-    "semi-spacing": "off",
-    "semi-style": "off",
-    "space-after-function-name": "off",
-    "space-after-keywords": "off",
-    "space-before-blocks": "off",
-    "space-before-function-paren": "off",
-    "space-before-function-parentheses": "off",
-    "space-before-keywords": "off",
-    "space-in-brackets": "off",
-    "space-in-parens": "off",
-    "space-infix-ops": "off",
-    "space-return-throw-case": "off",
-    "space-unary-ops": "off",
-    "space-unary-word-ops": "off",
-    "switch-colon-spacing": "off",
-    "template-curly-spacing": "off",
-    "template-tag-spacing": "off",
-    "unicode-bom": "off",
-    "wrap-iife": "off",
-    "wrap-regex": "off",
-    "yield-star-spacing": "off",
-    "@babel/object-curly-spacing": "off",
-    "@babel/semi": "off",
-    "@typescript-eslint/brace-style": "off",
-    "@typescript-eslint/comma-dangle": "off",
-    "@typescript-eslint/comma-spacing": "off",
-    "@typescript-eslint/func-call-spacing": "off",
-    "@typescript-eslint/indent": "off",
-    "@typescript-eslint/keyword-spacing": "off",
-    "@typescript-eslint/member-delimiter-style": "off",
-    "@typescript-eslint/no-extra-parens": "off",
-    "@typescript-eslint/no-extra-semi": "off",
-    "@typescript-eslint/object-curly-spacing": "off",
-    "@typescript-eslint/semi": "off",
-    "@typescript-eslint/space-before-blocks": "off",
-    "@typescript-eslint/space-before-function-paren": "off",
-    "@typescript-eslint/space-infix-ops": "off",
-    "@typescript-eslint/type-annotation-spacing": "off",
-    "babel/object-curly-spacing": "off",
-    "babel/semi": "off",
-    "flowtype/boolean-style": "off",
-    "flowtype/delimiter-dangle": "off",
-    "flowtype/generic-spacing": "off",
-    "flowtype/object-type-curly-spacing": "off",
-    "flowtype/object-type-delimiter": "off",
-    "flowtype/quotes": "off",
-    "flowtype/semi": "off",
-    "flowtype/space-after-type-colon": "off",
-    "flowtype/space-before-generic-bracket": "off",
-    "flowtype/space-before-type-colon": "off",
-    "flowtype/union-intersection-spacing": "off",
-    "react/jsx-child-element-spacing": "off",
-    "react/jsx-closing-bracket-location": "off",
-    "react/jsx-closing-tag-location": "off",
-    "react/jsx-curly-newline": "off",
-    "react/jsx-curly-spacing": "off",
-    "react/jsx-equals-spacing": "off",
-    "react/jsx-first-prop-new-line": "off",
-    "react/jsx-indent": "off",
-    "react/jsx-indent-props": "off",
-    "react/jsx-max-props-per-line": "off",
-    "react/jsx-newline": "off",
-    "react/jsx-one-expression-per-line": "off",
-    "react/jsx-props-no-multi-spaces": "off",
-    "react/jsx-tag-spacing": "off",
-    "react/jsx-wrap-multilines": "off",
-    "standard/array-bracket-even-spacing": "off",
-    "standard/computed-property-even-spacing": "off",
-    "standard/object-curly-even-spacing": "off",
-    "unicorn/empty-brace-spaces": "off",
-    "unicorn/no-nested-ternary": "off",
-    "unicorn/number-literal-case": "off",
-    "vue/array-bracket-newline": "off",
-    "vue/array-bracket-spacing": "off",
-    "vue/arrow-spacing": "off",
-    "vue/block-spacing": "off",
-    "vue/block-tag-newline": "off",
-    "vue/brace-style": "off",
-    "vue/comma-dangle": "off",
-    "vue/comma-spacing": "off",
-    "vue/comma-style": "off",
-    "vue/dot-location": "off",
-    "vue/func-call-spacing": "off",
-    "vue/html-closing-bracket-newline": "off",
-    "vue/html-closing-bracket-spacing": "off",
-    "vue/html-end-tags": "off",
-    "vue/html-indent": "off",
-    "vue/html-quotes": "off",
-    "vue/key-spacing": "off",
-    "vue/keyword-spacing": "off",
-    "vue/max-attributes-per-line": "off",
-    "vue/multiline-html-element-content-newline": "off",
-    "vue/multiline-ternary": "off",
-    "vue/mustache-interpolation-spacing": "off",
-    "vue/no-extra-parens": "off",
-    "vue/no-multi-spaces": "off",
-    "vue/no-spaces-around-equal-signs-in-attribute": "off",
-    "vue/object-curly-newline": "off",
-    "vue/object-curly-spacing": "off",
-    "vue/object-property-newline": "off",
-    "vue/operator-linebreak": "off",
-    "vue/quote-props": "off",
-    "vue/script-indent": "off",
-    "vue/singleline-html-element-content-newline": "off",
-    "vue/space-in-parens": "off",
-    "vue/space-infix-ops": "off",
-    "vue/space-unary-ops": "off",
-    "vue/template-curly-spacing": "off",
-  },
-  settings: {
-    "vue-i18n": {
-      localeDir: "./aleksis/core/frontend/messages/*.{json}",
-      messageSyntaxVersion: "^8.0.0",
-    },
-  },
-  env: {
-    es2021: true,
-  },
-  parserOptions: {
-    ecmaVersion: "latest",
-  },
-};
diff --git a/.gitignore b/.gitignore
index fc620d9045075bdec195fc0bd15702daa2b3e408..613db10966de0e5b57a87f57c6fd586925ade0b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,8 @@ docs/_build/
 .dev-js/.yarn
 .dev-js/.pnp.cjs
 .dev-js/.pnp.loader.mjs
+.dev-js/.yarnrc.yml
+.dev-js/schema.json
 
 # Lock files
 poetry.lock
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4278359d4b0bcb41001dc4ff155816db45beb0eb..aacf652dcdd8f9eeed1b7c5b9624548d6a84a5a4 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -23,7 +23,7 @@ and need a replacement, please contact the development team.
 The special assignment page for groups and child groups has been removed.
 
 For the calendar system, AlekSIS now needs an extension for the PostgreSQL database.
-Please check the docs for instructions how to setup the ``pg_rrule`` extension 
+Please check the docs for instructions how to setup the ``pg_rrule`` extension
 for PostgreSQL.
 
 AlekSIS now uses Valkey as a drop-in replacement for Redis. Please update your configuration
@@ -61,6 +61,7 @@ Added
 * Make configurable which weekdays appear in the calendar
 * Introduce .well-known urlpatterns for apps
 * Global school term select for limiting data to a specific school term.
+* [Dev] Notifications based on calendar alarms.
 
 Changed
 ~~~~~~~
@@ -74,7 +75,8 @@ Changed
 * Move "Invite person" to persons page
 * Replace all mentions of Redis with Valkey where possible
 * Show avatars of groups in all places.
-* Use new auth rate limiting settings 
+* Use new auth rate limiting settings
+* Bump Python version to 3.10
 
 Fixed
 ~~~~~
@@ -91,6 +93,10 @@ Fixed
 * [Dev] Integrate model validation mechanisms into GraphQL queries.
 * [Container] Database backup failed with postgres versions 15 and 16.
 * Setting images for groups did not work
+* Update and fix URLs for 3rdparty login.
+* The OpenID Connect Discovery endpoint now returns the issuer data directly
+  under the URI without a trailing `/`.
+* Not-logged in users were able to access all PDF files.
 
 Removed
 ~~~~~~~
@@ -101,7 +107,15 @@ Removed
 * [Dev] Extended fields mechanism on top of django-jsonstore.
 * Additional fields.
 * Legacy pages are no longer themed.
+* Batching of GraphQL queries.
 
+`3.2.2`_ - 2025-01-18
+---------------------
+
+Fixed
+~~~~~
+
+* Not-logged in users were able to access all PDF files.
 
 `3.2.1`_ - 2024-06-27
 ---------------------
@@ -151,6 +165,14 @@ This release deprecates some features in preparation for the 4.0 release.
   need to account for recursion themselves.
 * [Dev] Extended fields mechanism on top of django-jsonstore.
 
+`3.1.7`_ - 2025-01-18
+---------------------
+
+Fixed
+~~~~~
+
+* Not-logged in users were able to access all PDF files.
+
 `3.1.6`_ - 2024-06-27
 ---------------------
 
@@ -1391,5 +1413,7 @@ Fixed
 .. _3.1.4: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.1.4
 .. _3.1.5: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.1.5
 .. _3.1.6: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.1.6
+.. _3.1.7: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.1.7
 .. _3.2.0: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.2.0
 .. _3.2.1: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.2.1
+.. _3.2.2: https://edugit.org/AlekSIS/official/AlekSIS-Core/-/tags/3.2.2
diff --git a/Dockerfile b/Dockerfile
index 44cb3eec27c4eadc5de78328be22926011a875f1..38dec3ced173a8329f8e7e57e25d34351021f844 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -73,7 +73,6 @@ RUN set -e; \
 
 # Define entrypoint, volumes and uWSGI running on port 8000
 EXPOSE 8000
-VOLUME ${ALEKSIS_media__root} ${ALEKSIS_backup__location}
 COPY docker-startup.sh /usr/local/bin/aleksis-docker-startup
 ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 CMD ["/usr/local/bin/aleksis-docker-startup"]
@@ -111,6 +110,7 @@ RUN chown -R www-data:www-data \
      ${ALEKSIS_media__root} \
      ${ALEKSIS_backup__location}
 USER 33:33
+VOLUME ${ALEKSIS_media__root} ${ALEKSIS_backup__location}
 
 # Additional steps
 ONBUILD ARG APPS
diff --git a/aleksis/core/filters.py b/aleksis/core/filters.py
index c8699dab872827a863a0e6c26502117b75ac8285..1009d15f7d3746efa0008fd9833a1b077293e93c 100644
--- a/aleksis/core/filters.py
+++ b/aleksis/core/filters.py
@@ -1,4 +1,4 @@
-from typing import Sequence
+from collections.abc import Sequence
 
 from django.contrib.auth.models import Group as DjangoGroup
 from django.contrib.auth.models import Permission, User
diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index 3e046dd44011297cbca740ed28e1b161b686f068..914f130492bbd79827f67abeb9599569dab0fc81 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -1,5 +1,6 @@
+from collections.abc import Sequence
 from datetime import datetime, time
-from typing import Any, Callable, Dict, Sequence
+from typing import Any, Callable
 
 from django import forms
 from django.conf import settings
@@ -462,7 +463,7 @@ class AssignPermissionForm(forms.Form):
         required=False, label=_("Grant the permission for all objects")
     )
 
-    def clean(self) -> Dict[str, Any]:
+    def clean(self) -> dict[str, Any]:
         """Clean form to ensure that at least one target and one type is selected."""
         cleaned_data = super().clean()
         if not cleaned_data.get("persons") and not cleaned_data.get("groups"):
diff --git a/aleksis/core/frontend/app/apollo.js b/aleksis/core/frontend/app/apollo.js
index 7ebde2fe407008137ae6248261db9d2f36e0cf59..4652c386e9b02f6cc2d8a9f27a6b78f3b8a41f48 100644
--- a/aleksis/core/frontend/app/apollo.js
+++ b/aleksis/core/frontend/app/apollo.js
@@ -2,12 +2,10 @@
  * Configuration for Apollo provider, client, and caches.
  */
 
-import { ApolloClient, from } from "@/apollo-boost";
+import { ApolloClient, from, HttpLink } from "@/apollo-boost";
 
-import { RetryLink } from "@/apollo-link-retry";
 import { persistCache, LocalStorageWrapper } from "@/apollo3-cache-persist";
 import { InMemoryCache } from "@/apollo-cache-inmemory";
-import { BatchHttpLink } from "@/apollo-link-batch-http";
 
 import errorCodes from "../errorCodes";
 
@@ -33,20 +31,15 @@ function getGraphqlURL() {
 
 // Define Apollo links for handling query operations.
 const links = [
-  // Automatically retry failed queries
-  // new RetryLink(),
-  // Finally, the HTTP link to the real backend (Django)
-  new BatchHttpLink({
+  // HTTP link to the real backend (Django)
+  new HttpLink({
     uri: getGraphqlURL(),
-    batchInterval: 200,
-    batchDebounce: true,
   }),
 ];
 
 /** Upstream Apollo GraphQL client */
 const apolloClient = new ApolloClient({
   cache,
-  shouldBatch: true,
   link: from(links),
 });
 
diff --git a/aleksis/core/frontend/app/vuetify.js b/aleksis/core/frontend/app/vuetify.js
index 4cccdd2978ed417ea1dd62f1da9ee3770013b733..1511760ae0011e4332e1f1ba00c4f874d2a51107 100644
--- a/aleksis/core/frontend/app/vuetify.js
+++ b/aleksis/core/frontend/app/vuetify.js
@@ -33,6 +33,7 @@ const vuetifyOpts = {
       groupType: "mdi-shape-outline",
       print: "mdi-printer-outline",
       schoolTerm: "mdi-calendar-range-outline",
+      updatePwa: "mdi-update",
     },
   },
 };
diff --git a/aleksis/core/frontend/components/about/installedApps.graphql b/aleksis/core/frontend/components/about/installedApps.graphql
index 01ceaf99eb8d79d44ee7014d9f582d7dfeb54ace..be2a7d1995f8107a3588f62d9d08c3e35c5aeec6 100644
--- a/aleksis/core/frontend/components/about/installedApps.graphql
+++ b/aleksis/core/frontend/components/about/installedApps.graphql
@@ -1,4 +1,4 @@
-{
+query installedApps {
   installedApps {
     name
     verboseName
diff --git a/aleksis/core/frontend/components/app/App.vue b/aleksis/core/frontend/components/app/App.vue
index d522d33fc65106b853a07e4e8285b632858fc84c..d48aff1a9c50adf60e531bb4f8f30c1ceeedff9b 100644
--- a/aleksis/core/frontend/components/app/App.vue
+++ b/aleksis/core/frontend/components/app/App.vue
@@ -192,8 +192,7 @@
                       class="white--text text-decoration-none"
                       >{{ $t("base.about_aleksis") }}
                     </router-link>
-                    <!-- eslint-disable-next-line -->
-                    <span>© The AlekSIS Team</span>
+                    <span>{{ $t("base.about_copyright") }}</span>
                   </div>
                 </v-col>
                 <v-col class="d-flex justify-end">
@@ -227,18 +226,30 @@
       :key="item.id"
       :snackbar-item="item"
     />
-    <v-snackbar v-model="needRefresh" v-if="!refreshDismissed" timeout="-1">
-      {{ $t("service_worker.new_version_available") }}
+    <v-dialog :value="needRefresh" persistent max-width="400px">
+      <v-card>
+        <v-card-title>
+          {{ $t("service_worker.new_version_available.header") }}
+        </v-card-title>
 
-      <template #action="{ attrs }">
-        <v-btn color="primary" text @click="updateServiceWorker()">
-          {{ $t("service_worker.update") }}
-        </v-btn>
-        <v-btn color="primary" text @click="refreshDismissed = true">
-          {{ $t("service_worker.dismiss") }}
-        </v-btn>
-      </template>
-    </v-snackbar>
+        <v-card-text>
+          {{
+            $t("service_worker.new_version_available.body", {
+              instance: $getBaseTitle(),
+            })
+          }}
+        </v-card-text>
+
+        <v-card-actions>
+          <v-spacer />
+
+          <v-btn color="primary" text @click="updateServiceWorker()">
+            <v-icon left>$updatePwa</v-icon>
+            {{ $t("service_worker.update") }}
+          </v-btn>
+        </v-card-actions>
+      </v-card>
+    </v-dialog>
   </v-app>
 </template>
 
diff --git a/aleksis/core/frontend/components/app/customMenu.graphql b/aleksis/core/frontend/components/app/customMenu.graphql
index 9591126f8226a590355969d643a5db9257c2b4e6..20fed201f1270c47a9afadb9fa96ddb73f9ca6fc 100644
--- a/aleksis/core/frontend/components/app/customMenu.graphql
+++ b/aleksis/core/frontend/components/app/customMenu.graphql
@@ -1,7 +1,9 @@
-query ($name: String!) {
+query customMenu($name: String!) {
   customMenuByName(name: $name) {
+    id
     name
     items {
+      id
       name
       url
       icon
diff --git a/aleksis/core/frontend/components/app/dynamicRoutes.graphql b/aleksis/core/frontend/components/app/dynamicRoutes.graphql
index 49c208b729f4185b0053e8bfbff13fd3ca8c78f5..433a1ca753a8d16ce1281575301bb700329a6d46 100644
--- a/aleksis/core/frontend/components/app/dynamicRoutes.graphql
+++ b/aleksis/core/frontend/components/app/dynamicRoutes.graphql
@@ -1,4 +1,4 @@
-{
+query dynamicRoutes {
   dynamicRoutes {
     parentRouteName
 
diff --git a/aleksis/core/frontend/components/app/messages.graphql b/aleksis/core/frontend/components/app/messages.graphql
index 96c09c62c90962b103fae88dc53bc4c96e1a2b49..ffbdd6e40ad308d30a13e561efcbd5dc4e747941 100644
--- a/aleksis/core/frontend/components/app/messages.graphql
+++ b/aleksis/core/frontend/components/app/messages.graphql
@@ -1,4 +1,4 @@
-{
+query messages {
   messages {
     tags
     message
diff --git a/aleksis/core/frontend/components/app/ping.graphql b/aleksis/core/frontend/components/app/ping.graphql
index 1775ef4a61beab635ff199bd454e328cd02c3304..83101ecfc0a7c223607a9ff9e24504d5c77a6d1c 100644
--- a/aleksis/core/frontend/components/app/ping.graphql
+++ b/aleksis/core/frontend/components/app/ping.graphql
@@ -1,3 +1,3 @@
-query Ping($payload: String) {
+query ping($payload: String) {
   ping(payload: $payload)
 }
diff --git a/aleksis/core/frontend/components/app/systemProperties.graphql b/aleksis/core/frontend/components/app/systemProperties.graphql
index b8ec991bda2b1b689c97f2fb339dafce9d0e1175..eec69756e5857ecc4fa3ec6d8c0b19aa6afe49f2 100644
--- a/aleksis/core/frontend/components/app/systemProperties.graphql
+++ b/aleksis/core/frontend/components/app/systemProperties.graphql
@@ -1,4 +1,4 @@
-{
+query systemProperties {
   systemProperties {
     availableLanguages {
       code
diff --git a/aleksis/core/frontend/components/app/whoAmI.graphql b/aleksis/core/frontend/components/app/whoAmI.graphql
index 159e4c088e1ba1327130711f9fac9e8d1645b607..1af27f4aeb41dcbe1e2722895de0019806762e45 100644
--- a/aleksis/core/frontend/components/app/whoAmI.graphql
+++ b/aleksis/core/frontend/components/app/whoAmI.graphql
@@ -6,6 +6,7 @@ query whoAmI($permissions: [String]!) {
     isAnonymous
     isImpersonate
     person {
+      id
       photo {
         url
       }
diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql b/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
index 68ab08b05d479b8149b3772406a81dd0673c3169..b143296677bd0d96453f98a2adb1378a4eff17f4 100644
--- a/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
@@ -1,4 +1,4 @@
-{
+query oauthAccessTokens {
   accessTokens: oauthAccessTokens {
     id
     created
diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql b/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql
index 232f231cc531453dc47d14f4715985772ed3d3fb..8a48c410a6dfec246b755688591fbee047aae170 100644
--- a/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql
@@ -1,5 +1,5 @@
-mutation ($ids: [ID]!) {
+mutation revokeOauthTokens($ids: [ID]!) {
   revokeOauthTokens(ids: $ids) {
-    revokationCount
+    ok
   }
 }
diff --git a/aleksis/core/frontend/components/calendar/Calendar.vue b/aleksis/core/frontend/components/calendar/Calendar.vue
index 5f5c44c9ced529606f9d621294a30d42cf47e5d4..d8fcdfe39a6e5f46568315c94c5b32cd3bdaf112 100644
--- a/aleksis/core/frontend/components/calendar/Calendar.vue
+++ b/aleksis/core/frontend/components/calendar/Calendar.vue
@@ -40,6 +40,44 @@
             :calendar-type="internalCalendarType"
           />
         </template>
+        <template
+          v-if="Object.keys(daysWithHiddenEvents).length"
+          #interval-header
+        >
+          <div
+            v-if="
+              !internalCalendarType === 'day' ||
+              Object.keys(daysWithHiddenEvents).includes(internalCalendarFocus)
+            "
+            class="d-flex justify-center align-end"
+            :style="{ height: '100%' }"
+          >
+            <v-btn
+              icon
+              class="ma-2"
+              @click="showAllAllDayEvents = !showAllAllDayEvents"
+            >
+              <v-icon>{{ showAllAllDayEventsButtonIcon }}</v-icon>
+            </v-btn>
+          </div>
+        </template>
+        <template #day-header="{ date }">
+          <template
+            v-if="
+              Object.keys(daysWithHiddenEvents).includes(date) &&
+              !showAllAllDayEvents
+            "
+          >
+            <v-spacer />
+            <div
+              class="v-event-more ml-1"
+              v-ripple
+              @click="showAllAllDayEvents = true"
+            >
+              {{ $tc("calendar.more_events", daysWithHiddenEvents[date]) }}
+            </div>
+          </template>
+        </template>
       </v-calendar>
       <component
         v-if="selectedEvent"
@@ -65,6 +103,8 @@ import {
 
 import { gqlCalendar, calendarDaysPreference } from "./calendar.graphql";
 
+import { Interval } from "luxon";
+
 export default {
   name: "Calendar",
   props: {
@@ -104,6 +144,11 @@ export default {
       required: false,
       default: "current",
     },
+    maxAllDayEvents: {
+      type: Number,
+      required: false,
+      default: 5,
+    },
   },
   data() {
     return {
@@ -139,6 +184,9 @@ export default {
           daysOfWeek: [1, 2, 3, 4, 5, 6, 0],
         },
       },
+
+      showAllAllDayEvents: false,
+      daysWithHiddenEvents: {},
     };
   },
   emits: ["changeCalendarType", "changeCalendarFocus", "selectEvent"],
@@ -164,7 +212,7 @@ export default {
       };
     },
     events() {
-      return this.calendar.calendarFeeds
+      let events = this.calendar.calendarFeeds
         .filter((c) => this.calendarFeeds.map((cf) => cf.name).includes(c.name))
         .flatMap((cf) =>
           cf.events.map((event) => {
@@ -184,6 +232,34 @@ export default {
             };
           }),
         );
+      if (this.internalCalendarType === "month" || this.showAllAllDayEvents) {
+        return events;
+      }
+
+      let dateFullEventCount = {};
+      this.clearDaysWithHiddenEvents();
+
+      return events.filter((event) => {
+        if (!event.allDay) {
+          return true;
+        }
+        const start = event.startDateTime;
+        dateFullEventCount[start] = (dateFullEventCount[start] || 0) + 1;
+        const show = dateFullEventCount[start] <= this.maxAllDayEvents;
+        if (!show) {
+          const dateInterval = Interval.fromDateTimes(
+            start,
+            event.endDateTime.endOf("day"),
+          )
+            .splitBy({ day: 1 })
+            .map((date) => date.start.toISODate());
+          for (const date of dateInterval) {
+            this.daysWithHiddenEvents[date] =
+              (this.daysWithHiddenEvents[date] || 0) + 1;
+          }
+        }
+        return show;
+      });
     },
     paramsForSend() {
       if (this.params !== null) {
@@ -251,6 +327,9 @@ export default {
 
       return this.personByIdOrMe.preferences.daysOfWeek;
     },
+    showAllAllDayEventsButtonIcon() {
+      return this.showAllAllDayEvents ? "mdi-chevron-up" : "mdi-chevron-down";
+    },
   },
   watch: {
     params(newParams) {
@@ -526,6 +605,9 @@ export default {
       // TODO: is this destroyed when unloading?
       setInterval(() => this.cal.updateTimes(), 60 * 1000);
     },
+    clearDaysWithHiddenEvents() {
+      this.daysWithHiddenEvents = {};
+    },
   },
   mounted() {
     this.ready = true;
diff --git a/aleksis/core/frontend/components/calendar/calendarFeeds.graphql b/aleksis/core/frontend/components/calendar/calendarFeeds.graphql
index 917cf386965d820bc634583a1af6339e8500c0cc..f49deafb6a0f2bfd45cc8fa762fde98f81eb680d 100644
--- a/aleksis/core/frontend/components/calendar/calendarFeeds.graphql
+++ b/aleksis/core/frontend/components/calendar/calendarFeeds.graphql
@@ -1,4 +1,4 @@
-query {
+query calendarFeeds {
   calendar {
     allFeedsUrl
     calendarFeeds {
diff --git a/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql
index 633f0791c3e00f0c82886779366704086871c484..3a2c8a72ad9aff66d7d6f8c8f88f628892f546e1 100644
--- a/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql
+++ b/aleksis/core/frontend/components/calendar/setCalendarStatus.graphql
@@ -1,4 +1,4 @@
-mutation ($calendars: [String]!) {
+mutation setCalendarStatus($calendars: [String]!) {
   setCalendarStatus(calendars: $calendars) {
     ok
   }
diff --git a/aleksis/core/frontend/components/celery_progress/celeryProgress.graphql b/aleksis/core/frontend/components/celery_progress/celeryProgress.graphql
index 557e33517d4f3536e043ba0e64cb8c3f622741d2..985c12c713ab480926e67ff27355b8f1eec43a1b 100644
--- a/aleksis/core/frontend/components/celery_progress/celeryProgress.graphql
+++ b/aleksis/core/frontend/components/celery_progress/celeryProgress.graphql
@@ -1,4 +1,4 @@
-query ($taskId: String!) {
+query celeryProgress($taskId: String!) {
   celeryProgressByTaskId(taskId: $taskId) {
     state
     success
diff --git a/aleksis/core/frontend/components/celery_progress/celeryProgressBottom.graphql b/aleksis/core/frontend/components/celery_progress/celeryProgressBottom.graphql
index 5cae8f3baa46b14b4cf1031dc248d2dd7757b576..17392bb390a31e287dfc9d8b2be26b6dfe44587a 100644
--- a/aleksis/core/frontend/components/celery_progress/celeryProgressBottom.graphql
+++ b/aleksis/core/frontend/components/celery_progress/celeryProgressBottom.graphql
@@ -1,4 +1,4 @@
-{
+query celeryProgressByUser {
   celeryProgressByUser {
     state
     success
diff --git a/aleksis/core/frontend/components/celery_progress/celeryProgressFetched.graphql b/aleksis/core/frontend/components/celery_progress/celeryProgressFetched.graphql
index b3fedc916e6a3851677f0fe7a3c322c9311a33e4..4556d69113e6970b33254eb36bf8dd401c245e70 100644
--- a/aleksis/core/frontend/components/celery_progress/celeryProgressFetched.graphql
+++ b/aleksis/core/frontend/components/celery_progress/celeryProgressFetched.graphql
@@ -1,4 +1,4 @@
-mutation ($taskId: String!) {
+mutation celeryProgressFetched($taskId: String!) {
   celeryProgressFetched(taskId: $taskId) {
     celeryProgress {
       state
diff --git a/aleksis/core/frontend/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue b/aleksis/core/frontend/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue
index e67168a8e23eb87f0f4cb9c1a34227f1fb24ee5a..6322e58fcc8e563af5edefbaa2d6f5947fc77a46 100644
--- a/aleksis/core/frontend/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue
+++ b/aleksis/core/frontend/components/generic/InfiniteScrollingDateSortedCRUDIterator.vue
@@ -59,9 +59,12 @@ import { DateTime } from "luxon";
         ref="days"
       >
         <v-list-item-content>
-          <v-subheader class="text-h6">{{
-            $d(date, "dateWithWeekday")
-          }}</v-subheader>
+          <v-subheader
+            class="text-h5 px-2 hover-hash"
+            @click="gotoDate(date.toISODate())"
+          >
+            {{ $d(date, "dateWithWeekday") }}
+          </v-subheader>
           <v-list max-width="100%" class="pt-0 mt-n1">
             <v-list-item
               class="px-1"
@@ -126,6 +129,13 @@ import { DateTime } from "luxon";
           }}
         </CRUDIteratorEmptyMessage>
       </slot>
+
+      <date-select-footer
+        :value="currentDate"
+        @input="gotoDate"
+        @prev="gotoPrev"
+        @next="gotoNext"
+      />
     </template>
 
     <template #no-results>
@@ -577,12 +587,11 @@ export default {
         this.gotoDate(next.toISODate());
       }
     },
-    focus(element, how) {
+    focus(element, how = "smooth") {
       // Helper function used to scroll to day group.
-      element.$el.scrollIntoView({
-        behavior: how,
-        block: "start",
-        inline: "nearest",
+      this.$vuetify.goTo(element, {
+        duration: how === "instant" ? 0 : 400,
+        offset: this.topMargin,
       });
     },
   },
@@ -601,3 +610,11 @@ export default {
   },
 };
 </script>
+
+<style scoped>
+.hover-hash:hover::before {
+  position: absolute;
+  left: -1ch;
+  content: "#";
+}
+</style>
diff --git a/aleksis/core/frontend/components/generic/forms/DateField.vue b/aleksis/core/frontend/components/generic/forms/DateField.vue
index 37f39dbfe27f3244939c147e07029fc6b3390b82..517bddc8e579c91e582d61162c376743fe2d50a6 100644
--- a/aleksis/core/frontend/components/generic/forms/DateField.vue
+++ b/aleksis/core/frontend/components/generic/forms/DateField.vue
@@ -10,7 +10,7 @@
   >
     <template #activator="{ on, attrs }">
       <v-text-field
-        v-model="date"
+        :value="date"
         v-bind="{ ...$attrs, ...attrs }"
         @click="handleClick"
         @focusin="handleFocusIn"
@@ -27,8 +27,8 @@
       ref="picker"
       no-title
       scrollable
-      :min="min"
-      :max="max"
+      :min="limitSelectableRange ? min : ''"
+      :max="limitSelectableRange ? max : ''"
       :locale="$i18n.locale"
       first-day-of-week="1"
       show-adjacent-months
@@ -69,6 +69,11 @@ export default {
       required: false,
       default: () => [],
     },
+    limitSelectableRange: {
+      type: Boolean,
+      required: false,
+      default: true,
+    },
   },
   computed: {
     date: {
@@ -112,8 +117,9 @@ export default {
       this.openDueToFocus = true;
       this.menu = true;
     },
-    handleFocusOut() {
+    handleFocusOut(event) {
       if (this.openDueToFocus) this.menu = false;
+      this.date = event.target.value;
     },
   },
   watch: {
diff --git a/aleksis/core/frontend/components/generic/forms/GroupField.vue b/aleksis/core/frontend/components/generic/forms/GroupField.vue
index 3433c5c3fb0695cfd5842ad0a461ab5cfb6aa765..736425d59cfc3e49cad459cd94ca50ff77cc4952 100644
--- a/aleksis/core/frontend/components/generic/forms/GroupField.vue
+++ b/aleksis/core/frontend/components/generic/forms/GroupField.vue
@@ -13,7 +13,7 @@
 
 <script>
 import queryMixin from "../../../mixins/queryMixin.js";
-import { groups } from "./group.graphql";
+import { formGroups } from "./group.graphql";
 
 export default {
   name: "GroupField",
@@ -26,7 +26,7 @@ export default {
     gqlQuery: {
       type: Object,
       required: false,
-      default: () => groups,
+      default: () => formGroups,
     },
   },
 };
diff --git a/aleksis/core/frontend/components/generic/forms/PersonField.vue b/aleksis/core/frontend/components/generic/forms/PersonField.vue
index 44479c3f66b29c244919f06f54b0b1a71f28c7dc..4df1df64d9a33c0d1203af62df7be6e841f3d54d 100644
--- a/aleksis/core/frontend/components/generic/forms/PersonField.vue
+++ b/aleksis/core/frontend/components/generic/forms/PersonField.vue
@@ -4,7 +4,7 @@
     v-on="$listeners"
     hide-no-data
     :items="items"
-    :item-text="(person) => `${person.fullName} (${person.shortName})`"
+    :item-text="getItemText"
     item-value="id"
     :loading="loading"
   />
@@ -12,7 +12,7 @@
 
 <script>
 import queryMixin from "../../../mixins/queryMixin.js";
-import { persons } from "./person.graphql";
+import { formPersons } from "./person.graphql";
 
 export default {
   name: "PersonField",
@@ -25,7 +25,15 @@ export default {
     gqlQuery: {
       type: Object,
       required: false,
-      default: () => persons,
+      default: () => formPersons,
+    },
+  },
+  methods: {
+    getItemText(person) {
+      if (person?.shortName) {
+        return `${person.fullName} (${person.shortName})`;
+      }
+      return person.fullName;
     },
   },
 };
diff --git a/aleksis/core/frontend/components/generic/forms/TimeField.vue b/aleksis/core/frontend/components/generic/forms/TimeField.vue
index 9d6fbd154a13870b25a14f45924242030630d7f1..7ed63015aaa5e7865ff602e89613952c60caa752 100644
--- a/aleksis/core/frontend/components/generic/forms/TimeField.vue
+++ b/aleksis/core/frontend/components/generic/forms/TimeField.vue
@@ -10,7 +10,7 @@
   >
     <template #activator="{ on, attrs }">
       <v-text-field
-        v-model="time"
+        :value="time"
         v-bind="{ ...$attrs, ...attrs }"
         @click="handleClick"
         @focusin="handleFocusIn"
@@ -26,8 +26,8 @@
     <v-time-picker
       v-model="time"
       ref="picker"
-      :min="min"
-      :max="max"
+      :min="limitSelectableRange ? min : ''"
+      :max="limitSelectableRange ? max : ''"
       full-width
       format="24hr"
       @click:minute="menu = false"
@@ -72,6 +72,11 @@ export default {
       required: false,
       default: () => [],
     },
+    limitSelectableRange: {
+      type: Boolean,
+      required: false,
+      default: true,
+    },
   },
   computed: {
     time: {
@@ -107,8 +112,9 @@ export default {
       this.openDueToFocus = true;
       this.menu = true;
     },
-    handleFocusOut() {
+    handleFocusOut(event) {
       if (this.openDueToFocus) this.menu = false;
+      this.time = event.target.value;
     },
   },
   watch: {
diff --git a/aleksis/core/frontend/components/generic/forms/group.graphql b/aleksis/core/frontend/components/generic/forms/group.graphql
index 5a4906b549225bab5e2ba98e2ad681fb03d3583e..fcd97f2b49c19ca765b5d5d91b77c36c838bcf89 100644
--- a/aleksis/core/frontend/components/generic/forms/group.graphql
+++ b/aleksis/core/frontend/components/generic/forms/group.graphql
@@ -1,4 +1,4 @@
-query groups {
+query formGroups {
   items: groups {
     id
     shortName
diff --git a/aleksis/core/frontend/components/generic/forms/person.graphql b/aleksis/core/frontend/components/generic/forms/person.graphql
index 6985dc9428b9c171706e170a35d2bd38e038a7ff..b7aaae4f2e3a8ccc4c54e8f0962ed518d75c5317 100644
--- a/aleksis/core/frontend/components/generic/forms/person.graphql
+++ b/aleksis/core/frontend/components/generic/forms/person.graphql
@@ -1,4 +1,4 @@
-query persons {
+query formPersons {
   items: persons {
     id
     shortName
diff --git a/aleksis/core/frontend/components/group/GroupAvatarClickbox.vue b/aleksis/core/frontend/components/group/GroupAvatarClickbox.vue
index 160a07501b5d12433fed1d1ac1720e16ca7b0d7a..d78e68ba09bc0b1555fb0d980f555b4ef45fb566 100644
--- a/aleksis/core/frontend/components/group/GroupAvatarClickbox.vue
+++ b/aleksis/core/frontend/components/group/GroupAvatarClickbox.vue
@@ -1,9 +1,9 @@
 <template>
   <avatar-clickbox>
     <template #activator>
-      <avatar-content :imageUrl="url" class="rounded-circle" />
+      <avatar-content :image-url="url" class="rounded-circle" />
     </template>
-    <avatar-content :imageUrl="url" contain />
+    <avatar-content :image-url="url" contain />
   </avatar-clickbox>
 </template>
 
diff --git a/aleksis/core/frontend/components/group/GroupList.vue b/aleksis/core/frontend/components/group/GroupList.vue
index 0b7b5ac115d63a0d04bacaa3f6567786898d2048..cbe3c49dd09e2a16513e9574c81ee03bbf95cfa6 100644
--- a/aleksis/core/frontend/components/group/GroupList.vue
+++ b/aleksis/core/frontend/components/group/GroupList.vue
@@ -1,7 +1,7 @@
 <script>
 import CRUDList from "../generic/CRUDList.vue";
 
-import { deleteGroups, groups } from "./groupList.graphql";
+import { deleteGroups, groups } from "./groups.graphql";
 import CreateButton from "../generic/buttons/CreateButton.vue";
 import TableLink from "../generic/TableLink.vue";
 import AvatarContent from "../person/AvatarContent.vue";
@@ -73,7 +73,7 @@ export default {
     <template #avatarUrl="{ item }">
       <table-link :to="{ name: 'core.group', params: { id: item.id } }">
         <v-avatar class="my-1 me-2">
-          <avatar-content :imageUrl="item.avatarUrl" contain />
+          <avatar-content :image-url="item.avatarUrl" contain />
         </v-avatar>
       </table-link>
     </template>
diff --git a/aleksis/core/frontend/components/group/GroupMembers.vue b/aleksis/core/frontend/components/group/GroupMembers.vue
index d0dc18a3de9925e40ab2dedaf6bef59c201fb212..b841283bb7bf312ff654b234edb575130f3bf838 100644
--- a/aleksis/core/frontend/components/group/GroupMembers.vue
+++ b/aleksis/core/frontend/components/group/GroupMembers.vue
@@ -76,7 +76,7 @@ export default {
     <!-- eslint-disable-next-line vue/valid-v-slot -->
     <template #item.id="{ item }">
       <v-tooltip bottom>
-        <template v-slot:activator="{ on, attrs }">
+        <template #activator="{ on, attrs }">
           <secondary-action-button
             v-bind="attrs"
             v-on="on"
diff --git a/aleksis/core/frontend/components/group/groupList.graphql b/aleksis/core/frontend/components/group/groupList.graphql
deleted file mode 100644
index 7df6604b94608301147eec994d6f0add1b5d9013..0000000000000000000000000000000000000000
--- a/aleksis/core/frontend/components/group/groupList.graphql
+++ /dev/null
@@ -1,24 +0,0 @@
-query groups($orderBy: [String], $filters: JSONString) {
-  items: groups(orderBy: $orderBy, filters: $filters) {
-    id
-    shortName
-    name
-    avatarUrl
-    schoolTerm {
-      id
-      name
-    }
-    groupType {
-      id
-      name
-    }
-    canEdit
-    canDelete
-  }
-}
-
-mutation deleteGroups($ids: [ID]!) {
-  deleteGroups(ids: $ids) {
-    deletionCount
-  }
-}
diff --git a/aleksis/core/frontend/components/group/groups.graphql b/aleksis/core/frontend/components/group/groups.graphql
index 701ecde468d517da81410cb35faba0022c91fa09..cc5e716216d05589f05f2ea65a1655cbf07abb3e 100644
--- a/aleksis/core/frontend/components/group/groups.graphql
+++ b/aleksis/core/frontend/components/group/groups.graphql
@@ -1,3 +1,22 @@
+query groups($orderBy: [String], $filters: JSONString) {
+  items: groups(orderBy: $orderBy, filters: $filters) {
+    id
+    shortName
+    name
+    avatarUrl
+    schoolTerm {
+      id
+      name
+    }
+    groupType {
+      id
+      name
+    }
+    canEdit
+    canDelete
+  }
+}
+
 query groupById($id: ID) {
   object: groupById(id: $id) {
     id
diff --git a/aleksis/core/frontend/components/notifications/markNotificationRead.graphql b/aleksis/core/frontend/components/notifications/markNotificationRead.graphql
index 8cc7bed4325857b3407701900a356150d3614b68..689d3c1e44172efabd8c62b03488dd5e71f6d695 100644
--- a/aleksis/core/frontend/components/notifications/markNotificationRead.graphql
+++ b/aleksis/core/frontend/components/notifications/markNotificationRead.graphql
@@ -1,4 +1,4 @@
-mutation ($id: ID!) {
+mutation markNotificationRead($id: ID!) {
   markNotificationRead(id: $id) {
     notification {
       id
diff --git a/aleksis/core/frontend/components/notifications/myNotifications.graphql b/aleksis/core/frontend/components/notifications/myNotifications.graphql
index 5c430731b0e3993e2c4b0872c4a44b5d8639a0bf..d51ef53f22a4724d33d97bd223281ff3e3d1a705 100644
--- a/aleksis/core/frontend/components/notifications/myNotifications.graphql
+++ b/aleksis/core/frontend/components/notifications/myNotifications.graphql
@@ -1,7 +1,8 @@
-{
+query myNotifications {
   myNotifications: whoAmI {
     id
     person {
+      id
       notifications {
         id
         title
diff --git a/aleksis/core/frontend/components/pdf/pdf.graphql b/aleksis/core/frontend/components/pdf/pdf.graphql
index aac3228d75c3c77ab131500b5e0d5e2ae1b8f503..e469aaaf5ffd4c5005eb603ce387f9f82cae15d0 100644
--- a/aleksis/core/frontend/components/pdf/pdf.graphql
+++ b/aleksis/core/frontend/components/pdf/pdf.graphql
@@ -1,5 +1,6 @@
-query ($id: ID!) {
+query pdf($id: ID!) {
   pdf: pdfById(id: $id) {
+    id
     file {
       url
     }
diff --git a/aleksis/core/frontend/components/person/PersonActions.vue b/aleksis/core/frontend/components/person/PersonActions.vue
index afa3b04efd549910a7197a44af3d859af04935f8..5bc9c70558462c10ee76507a94eb2118e76cf66c 100644
--- a/aleksis/core/frontend/components/person/PersonActions.vue
+++ b/aleksis/core/frontend/components/person/PersonActions.vue
@@ -101,7 +101,8 @@
 </template>
 
 <script>
-import { actions, deletePersons } from "./personActions.graphql";
+import { personActions } from "./personActions.graphql";
+import { deletePersons } from "./personList.graphql";
 import DeleteDialog from "../generic/dialogs/DeleteDialog.vue";
 
 export default {
@@ -115,7 +116,7 @@ export default {
   },
   apollo: {
     person: {
-      query: actions,
+      query: personActions,
       variables() {
         return {
           id: this.id,
diff --git a/aleksis/core/frontend/components/person/personActions.graphql b/aleksis/core/frontend/components/person/personActions.graphql
index ebfbb0fcd137be8a35114f60a39d99da1be1436e..be1b9209a9a6a6b3938423e8ccb20eed016b9398 100644
--- a/aleksis/core/frontend/components/person/personActions.graphql
+++ b/aleksis/core/frontend/components/person/personActions.graphql
@@ -1,4 +1,4 @@
-query actions($id: ID!) {
+query personActions($id: ID!) {
   person: personById(id: $id) {
     id
     userid
@@ -10,9 +10,3 @@ query actions($id: ID!) {
     canImpersonatePerson
   }
 }
-
-mutation deletePersons($ids: [ID]!) {
-  deletePersons(ids: $ids) {
-    deletionCount
-  }
-}
diff --git a/aleksis/core/frontend/components/room/RoomChip.vue b/aleksis/core/frontend/components/room/RoomChip.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6aa70777d217a3a54b1a6292f2df6d30d817e8ff
--- /dev/null
+++ b/aleksis/core/frontend/components/room/RoomChip.vue
@@ -0,0 +1,25 @@
+<script>
+export default {
+  name: "RoomChip",
+  props: {
+    room: {
+      type: Object,
+      required: true,
+    },
+  },
+};
+</script>
+
+<template>
+  <v-tooltip bottom>
+    <template #activator="{ on, attrs }">
+      <v-chip v-bind="{ ...attrs, ...$attrs }" v-on="{ ...on, ...$listeners }">
+        <v-avatar>
+          <v-icon> mdi-door </v-icon>
+        </v-avatar>
+        {{ room.shortName }}
+      </v-chip>
+    </template>
+    <span>{{ room.name }}</span>
+  </v-tooltip>
+</template>
diff --git a/aleksis/core/frontend/components/school_term/ActiveSchoolTermSelect.vue b/aleksis/core/frontend/components/school_term/ActiveSchoolTermSelect.vue
index 3de29edcf36b0661ee5a4dba6a59bee48c565183..e9c0543837faf74b89c6f314bc11c8079bdc70a6 100644
--- a/aleksis/core/frontend/components/school_term/ActiveSchoolTermSelect.vue
+++ b/aleksis/core/frontend/components/school_term/ActiveSchoolTermSelect.vue
@@ -1,7 +1,7 @@
 <script>
 import {
   activeSchoolTerm,
-  schoolTerms,
+  schoolTermsForActiveSchoolTerm,
   setActiveSchoolTerm,
 } from "./activeSchoolTerm.graphql";
 import loadingMixin from "../../mixins/loadingMixin";
@@ -10,7 +10,7 @@ export default {
   mixins: [loadingMixin],
   apollo: {
     schoolTerms: {
-      query: schoolTerms,
+      query: schoolTermsForActiveSchoolTerm,
     },
     activeSchoolTerm: {
       query: activeSchoolTerm,
diff --git a/aleksis/core/frontend/components/school_term/activeSchoolTerm.graphql b/aleksis/core/frontend/components/school_term/activeSchoolTerm.graphql
index 95b5fc170064e4dea9a24a17f3b10ad4bccb5af8..f005e1488979c348af957b4f09710cff9c254032 100644
--- a/aleksis/core/frontend/components/school_term/activeSchoolTerm.graphql
+++ b/aleksis/core/frontend/components/school_term/activeSchoolTerm.graphql
@@ -10,7 +10,7 @@ query activeSchoolTerm {
   }
 }
 
-query schoolTerms {
+query schoolTermsForActiveSchoolTerm {
   schoolTerms {
     id
     name
diff --git a/aleksis/core/frontend/components/two_factor/twoFactor.graphql b/aleksis/core/frontend/components/two_factor/twoFactor.graphql
index 431215ed1840f9ad06e61e1c8c63cf6fe0af35b7..bd0837e2437a1708739efb2f1554af86825d9649 100644
--- a/aleksis/core/frontend/components/two_factor/twoFactor.graphql
+++ b/aleksis/core/frontend/components/two_factor/twoFactor.graphql
@@ -1,4 +1,4 @@
-{
+query twoFactor {
   twoFactor {
     activated
     backupTokensCount
diff --git a/aleksis/core/frontend/css/global.scss b/aleksis/core/frontend/css/global.scss
index 6bbdf6e47d9cef354efcb3fa46eedca16e0b9483..135927a8572c1c38f805eab2027ad7a9bc58224d 100644
--- a/aleksis/core/frontend/css/global.scss
+++ b/aleksis/core/frontend/css/global.scss
@@ -39,3 +39,8 @@ h6,
 .full-width {
   width: 100%;
 }
+
+.v-calendar-daily_head-day {
+  display: flex;
+  flex-direction: column;
+}
diff --git a/aleksis/core/frontend/messages/ar.json b/aleksis/core/frontend/messages/ar.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/aleksis/core/frontend/messages/ar.json
@@ -0,0 +1 @@
+{}
diff --git a/aleksis/core/frontend/messages/de.json b/aleksis/core/frontend/messages/de.json
index 70c4b17328c9d85fc97ad8307a0601c578545bd2..54b0bc72769e3892de46a4660adc49b0a87b380e 100644
--- a/aleksis/core/frontend/messages/de.json
+++ b/aleksis/core/frontend/messages/de.json
@@ -116,6 +116,7 @@
   },
   "base": {
     "about_aleksis": "Über AlekSIS® — The Free School Information System",
+    "about_copyright": "© Das AlekSIS-Team",
     "imprint": "Impressum",
     "logo": "Logo",
     "no_permission": "Keine Berechtigung",
@@ -230,25 +231,43 @@
     "snackbar_success_message": "Der Vorgang wurde erfolgreich beendet."
   },
   "group": {
+    "avatar": "Avatar",
     "child_groups": "Kind-Gruppen",
     "child_groups_n": "Keine Kindgruppen | {n} Kindgruppe | {n} Kindgruppen",
+    "confirm_delete": "Möchten Sie diese Gruppe wirklich löschen?",
     "group_type": {
+      "additional_attributes": "Zusätzliche Attribute",
+      "allowed_information": {
+        "address": "Adresse",
+        "avatar": "Avatar",
+        "contact_details": "Kontaktdetails",
+        "groups": "Gruppen",
+        "personal_details": "Persönliche Details",
+        "photo": "Foto"
+      },
       "create": "Gruppentyp erstellen",
       "description": "Beschreibung",
       "menu_title": "Gruppentypen",
       "name": "Name",
       "no_group_type": "Kein Gruppentyp",
+      "owners_can_see_groups": "Besitzer von Gruppen mit diesem Gruppentyp können die Gruppen sehen",
+      "owners_can_see_members": "Besitzer von Gruppen mit diesem Gruppentyp können die Gruppenmitglieder sehen",
+      "owners_can_see_members_allowed_information": "Informationen, die Besitzer von Gruppen mit diesem Gruppentyp über die Gruppenmitglieder sehen können",
+      "owners_can_see_members_including": "(einschließlich {allowedInformation})",
       "title": "Gruppentyp",
       "title_plural": "Gruppentypen"
     },
     "groups_and_child_groups": "Gruppen und Kindgruppen",
+    "member_of_n": "Keine Gruppenmitgliedschaften | Mitglied in einer Gruppe | Mitglied in {n} Gruppen",
     "menu_title": "Gruppen",
+    "name": "Name",
+    "no_groups": "Keine Gruppen",
+    "owner_of_n": "Keine Gruppeneigentümerschaften | Besitzt eine Gruppe | Besitzt {n} Gruppen",
     "ownership": "Gruppen-Eigentümerschaft",
     "parent_groups": "Übergeordnete Gruppen",
     "parent_groups_n": "Keine übergeordneten Gruppen | {n} übergeordnete Gruppe | {n} übergeordnete Gruppen",
-    "member_of_n": "Keine Gruppenmitgliedschaften | Mitglied in einer Gruppe | Mitglied in {n} Gruppen",
-    "owner_of_n": "Keine Gruppeneigentümerschaften | Besitzt eine Gruppe | Besitzt {n} Gruppen",
     "properties": "Eigenschaften",
+    "school_term": "Schuljahr",
     "short_name": "Kurzname",
     "statistics": {
       "age_average": "Durchschnittsalter",
@@ -284,6 +303,7 @@
     "error_404": "404",
     "offline_notification": "Sie sind offline. Einige Funktionen werden nicht funktionieren und einige Daten werden nicht aktuell sein.",
     "page_not_found": "Die aufgerufene Seite oder Ressource konnte nicht gefunden werden.",
+    "service_unavailable": "Der Server ist aktuell im Wartungsmodus und daher temporär nicht erreichbar.",
     "snackbar_error_message": "Es ist ein Netzwerkfehler aufgetreten. Bitte versuchen Sie es erneut."
   },
   "notifications": {
@@ -399,30 +419,32 @@
     "title_plural": "Räume"
   },
   "school_term": {
+    "active_school_term": {
+      "select_action": "Aktuelles auswählen",
+      "subtitle": "Die Auswahl wird auf allen Seiten in AlekSIS berücksichtigt.",
+      "title": "Aktives Schuljahr",
+      "warning": "Hinweis: Sie sehen aktuell Daten aus einem anderen Schuljahr ({termName}). Informationen können daher veraltet sein und entsprechen möglicherweise nicht dem aktuellen Stand."
+    },
     "after": "Endet nach",
     "before": "Beginnt vor",
     "create_school_term": "Schuljahr erstellen",
+    "current": "Aktuell",
     "date_end": "Enddatum",
     "date_start": "Startdatum",
     "menu_title": "Schuljahre",
     "name": "Name",
     "title": "Schuljahr",
-    "title_plural": "Schuljahre",
-    "current": "Aktuell",
-    "active_school_term": {
-      "title": "Aktives Schuljahr",
-      "subtitle": "Die Auswahl wird auf allen Seiten in AlekSIS berücksichtigt.",
-      "warning": "Hinweis: Sie sehen aktuell Daten aus einem anderen Schuljahr ({termName}). Informationen können daher veraltet sein und entsprechen möglicherweise nicht dem aktuellen Stand.",
-      "select_action": "Aktuelles auswählen"
-    }
+    "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",
-    "update": "Aktualisieren"
+    "new_version_available": {
+      "body": "AlekSIS® wurde im Hintergrund aktualisiert. Um {instance} weiterhin zu verwenden, muss die Aktualisierung jetzt eingespielt werden.",
+      "header": "Neue Version verfügbar"
+    },
+    "update": "Fertigstellen"
   },
   "status": {
     "changes": "Sie haben nicht gespeicherte Änderungen.",
diff --git a/aleksis/core/frontend/messages/en.json b/aleksis/core/frontend/messages/en.json
index dda331d9ae8105f1ac43b7bdf8c011859597271d..ac23c920d6f0075565a8bd4da58216f1a928e841 100644
--- a/aleksis/core/frontend/messages/en.json
+++ b/aleksis/core/frontend/messages/en.json
@@ -113,6 +113,7 @@
   },
   "base": {
     "about_aleksis": "About AlekSIS® — The Free School Information System",
+    "about_copyright": "© The AlekSIS Team",
     "imprint": "Imprint",
     "logo": "Logo",
     "no_permission": "No Permission",
@@ -134,7 +135,8 @@
     "my_calendars": "My Calendars",
     "select": "Select calendars",
     "today": "Today",
-    "week": "Week"
+    "week": "Week",
+    "more_events": "{n} more"
   },
   "celery_progress": {
     "error_message": "The operation couldn't be finished successfully.",
@@ -361,9 +363,11 @@
     "num_items_selected": "No items selected | 1 item selected | {n} items selected"
   },
   "service_worker": {
-    "dismiss": "Dismiss",
-    "new_version_available": "A new version of the app is available",
-    "update": "Update"
+    "new_version_available": {
+      "header": "New version available",
+      "body": "AlekSIS® has been updated in the background. Refresh your version to keep using {instance}."
+    },
+    "update": "Refresh"
   },
   "status": {
     "changes": "You have unsaved changes.",
diff --git a/aleksis/core/frontend/messages/fr.json b/aleksis/core/frontend/messages/fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/aleksis/core/frontend/messages/fr.json
@@ -0,0 +1 @@
+{}
diff --git a/aleksis/core/frontend/messages/la.json b/aleksis/core/frontend/messages/la.json
new file mode 100644
index 0000000000000000000000000000000000000000..5c0ddcc9576185d24aadc606bcdb0d244b138d5e
--- /dev/null
+++ b/aleksis/core/frontend/messages/la.json
@@ -0,0 +1,72 @@
+{
+  "accounts": {
+    "login": {
+      "menu_title": "nomen profiteri"
+    },
+    "logout": {
+      "menu_title": "nomen retractare"
+    }
+  },
+  "actions": {
+    "search": "Quaerere"
+  },
+  "announcement": {
+    "menu_title": "Nuntii",
+    "title_plural": "Nuntii"
+  },
+  "forms": {
+    "date_time": {
+      "date": "dies",
+      "time": "tempus"
+    },
+    "labels": {
+      "persons": "personae"
+    }
+  },
+  "group": {
+    "group_type": {
+      "allowed_information": {
+        "groups": "Greges",
+        "photo": "Photographia"
+      },
+      "description": "Descriptio",
+      "name": "Nomen"
+    },
+    "menu_title": "Greges",
+    "name": "Nomen",
+    "short_name": "Breve nomen",
+    "title": "Grex",
+    "title_plural": "Greges"
+  },
+  "holidays": {
+    "holiday_name": "Nomen"
+  },
+  "notifications": {
+    "notifications": "Nuntii"
+  },
+  "person": {
+    "birth_date": "Dies natalis",
+    "guardians": "Parentes",
+    "home": "Numerus telephoni domi",
+    "menu_title": "personae",
+    "mobile": "Numerus telephoni mobilis",
+    "name": "Nomen",
+    "page_title": "Persona",
+    "sex": {
+      "field": "Genus"
+    },
+    "sex_description": "Genus",
+    "title": "Persona",
+    "title_plural": "personae"
+  },
+  "personal_events": {
+    "description": "Descriptio",
+    "title": "Titulus"
+  },
+  "rooms": {
+    "name": "Nomen"
+  },
+  "school_term": {
+    "name": "Nomen"
+  }
+}
diff --git a/aleksis/core/frontend/messages/nb_NO.json b/aleksis/core/frontend/messages/nb_NO.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/aleksis/core/frontend/messages/nb_NO.json
@@ -0,0 +1 @@
+{}
diff --git a/aleksis/core/frontend/messages/ru.json b/aleksis/core/frontend/messages/ru.json
index 55b38b268ae7e64e1769ee0b32edd92782eb52a6..50ab11618d55a474ff37afc585066748f33aede4 100644
--- a/aleksis/core/frontend/messages/ru.json
+++ b/aleksis/core/frontend/messages/ru.json
@@ -194,6 +194,7 @@
     },
     "groups_and_child_groups": "Группы и дочерние группы",
     "menu_title": "Группы",
+    "name": "Имя",
     "ownership": "Владельцы группы",
     "title": "Группа",
     "title_plural": "Группы"
diff --git a/aleksis/core/frontend/messages/tr.json b/aleksis/core/frontend/messages/tr.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/aleksis/core/frontend/messages/tr.json
@@ -0,0 +1 @@
+{}
diff --git a/aleksis/core/frontend/messages/uk.json b/aleksis/core/frontend/messages/uk.json
index 49d74190628e145aa9ab324c5eb0ccf2e13f66e0..3d0aa93fe3acf06181bf4ffb4a5be4d5e2f0e827 100644
--- a/aleksis/core/frontend/messages/uk.json
+++ b/aleksis/core/frontend/messages/uk.json
@@ -187,6 +187,12 @@
   },
   "group": {
     "group_type": {
+      "allowed_information": {
+        "address": "Адреса",
+        "contact_details": "Контактні дані",
+        "groups": "Групи",
+        "photo": "Фото"
+      },
       "description": "Опис",
       "menu_title": "Типи груп",
       "name": "Назва",
@@ -195,6 +201,7 @@
     },
     "groups_and_child_groups": "Групи та підлеглі групи",
     "menu_title": "Групи",
+    "name": "Назва",
     "ownership": "Власність групи",
     "short_name": "Коротка назва",
     "title": "Група",
diff --git a/aleksis/core/frontend/plugins/aleksis.js b/aleksis/core/frontend/plugins/aleksis.js
index 4f2b3a17db5428237fd0b89f8d12af9419751ea3..22bf3e16551989d60c26e76ca9e2043d353d22bb 100644
--- a/aleksis/core/frontend/plugins/aleksis.js
+++ b/aleksis/core/frontend/plugins/aleksis.js
@@ -144,6 +144,14 @@ AleksisVue.install = function (Vue) {
     this.$root.toolbarTitle = newTitle;
   };
 
+  /**
+   * Get base title defined by current Instance
+   * @return {string} Title as defined in site preferences
+   */
+  Vue.prototype.$getBaseTitle = function () {
+    return Vue.$pageBaseTitle;
+  };
+
   /**
    * Load i18n messages from all known AlekSIS apps.
    */
diff --git a/aleksis/core/frontend/routes.js b/aleksis/core/frontend/routes.js
index c90c3facf230c73ec0685610b22663433ab21eeb..b4164c69db163ab1211301e60a7ab813cd5ba7ad 100644
--- a/aleksis/core/frontend/routes.js
+++ b/aleksis/core/frontend/routes.js
@@ -840,7 +840,7 @@ const routes = [
     name: "core.accounts.confirmEmailKey",
   },
   {
-    path: "/accounts/social/login/cancelled/",
+    path: "/accounts/3rdparty/login/cancelled/",
     component: () => import("./components/LegacyBaseTemplate.vue"),
     props: {
       byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
@@ -848,7 +848,7 @@ const routes = [
     name: "core.accounts.socialLoginCancelled",
   },
   {
-    path: "/accounts/social/login/error/",
+    path: "/accounts/3rdparty/login/error/",
     component: () => import("./components/LegacyBaseTemplate.vue"),
     props: {
       byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
@@ -856,7 +856,7 @@ const routes = [
     name: "core.accounts.socialLoginError",
   },
   {
-    path: "/accounts/social/signup/",
+    path: "/accounts/3rdparty/signup/",
     component: () => import("./components/LegacyBaseTemplate.vue"),
     props: {
       byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
@@ -864,7 +864,7 @@ const routes = [
     name: "core.accounts.socialSignup",
   },
   {
-    path: "/accounts/social/connections/",
+    path: "/accounts/3rdparty/",
     component: () => import("./components/LegacyBaseTemplate.vue"),
     props: {
       byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
@@ -877,6 +877,14 @@ const routes = [
       permission: "core.manage_social_connections_rule",
     },
   },
+  {
+    path: "/accounts/3rdparty/:pk(\\d+)/delete",
+    component: () => import("./components/LegacyBaseTemplate.vue"),
+    props: {
+      byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+    },
+    name: "core.accounts.deleteSocialConnection",
+  },
   {
     path: "/oauth/authorized_tokens/",
     component: () =>
diff --git a/aleksis/core/locale/ar/LC_MESSAGES/django.po b/aleksis/core/locale/ar/LC_MESSAGES/django.po
index 10f93edc4f11e009839a4fccceb3a32a0c041675..00243818489006421cb014b86cde008d050a54cf 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: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\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,10 +42,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr ""
 
@@ -99,199 +97,191 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr ""
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr ""
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr ""
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr ""
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr ""
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr ""
 
@@ -315,739 +305,779 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr ""
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 msgid "Personal details"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr ""
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr ""
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr ""
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr ""
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr ""
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr ""
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr ""
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr ""
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr ""
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 msgid "Personal events"
 msgstr ""
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr ""
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 msgid "Related group"
 msgstr ""
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr ""
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr ""
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+msgid "Action"
+msgstr ""
+
+#: aleksis/core/models.py:2017
+msgid "Send notifications"
+msgstr ""
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr ""
+
+#: aleksis/core/models.py:2140
+msgid "Personal event alarm"
+msgstr ""
+
+#: aleksis/core/models.py:2141
+msgid "Personal event alarms"
+msgstr ""
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr ""
@@ -1256,46 +1286,82 @@ msgstr ""
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr ""
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr ""
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr ""
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr ""
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
@@ -1742,22 +1808,6 @@ msgstr ""
 msgid "Edit group"
 msgstr ""
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr ""
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr ""
@@ -2209,6 +2259,11 @@ msgid ""
 "    "
 msgstr ""
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr ""
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr ""
@@ -2759,11 +2814,11 @@ msgstr ""
 msgid "This username is not allowed."
 msgstr ""
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr ""
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr ""
 
@@ -2787,128 +2842,128 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 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 d6b2946b27a272a155b663f80a73142db675994c..5917cef914a3b477b1ccccaa6e0a4cb958c0f57f 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-10-15 15:50+0200\n"
-"PO-Revision-Date: 2024-08-19 16:45+0000\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\n"
+"PO-Revision-Date: 2025-01-14 17:21+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-core/de/>\n"
 "Language: de_DE\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 5.0.2\n"
+"X-Generator: Weblate 5.8.4\n"
 
 #: aleksis/core/apps.py:167
 msgid "You have been logged out successfully."
@@ -42,10 +42,8 @@ msgstr "E-Mail-Adresse"
 msgid "Home and mobile phone"
 msgstr "Festnetz- und Mobilfunknummer"
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr "Gruppen"
 
@@ -99,199 +97,191 @@ msgstr "Sicherstellen, dass E-Mail-Adressen unter allen Personen eindeutig sind"
 msgid "There was a non-unique email address."
 msgstr "Es wurde eine nicht-eindeutige E-Mail-Adresse gefunden."
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr "Suchen"
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr "Nach Namen suchen"
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr "Nach Kontaktdetails suchen"
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr "Berechtigung"
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr "Inhaltstyp"
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr "Benutzer"
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr "Gruppe"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr "Basisdaten"
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr "Adresse"
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr "Kontaktdaten"
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr "Zusätzliche persönliche Daten"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr "Neuer Benutzer"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr "Neues Benutzerkonto erstellen"
 
-#: aleksis/core/forms.py:131
+#: 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."
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Dieser Benutzername wird bereits genutzt."
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr "Schuljahr"
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr "Allgemeine Daten"
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr "Personen"
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr "Foto"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr "Datum"
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr "Zeit"
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 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:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Wer soll die Ankündigung sehen?"
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Schreiben Sie ihre Ankündigung:"
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
-msgstr ""
+msgstr "Eine Priorität setzen"
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 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:283
+#: aleksis/core/forms.py:284
 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."
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr "Sie benötigen mindestens einen Empfänger."
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr "Einladungscode"
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr "Bitte geben Sie Ihren Einladungscode ein."
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr "Vorname"
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr "Nachname"
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr "Eine Person nutzt diese E-Mail-Adresse"
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr "Wer soll die Berechtigung erhalten?"
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr "Auf was?"
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 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:"
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr "Vergebe die Berechtigung für alle Objekte"
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 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:475
+#: aleksis/core/forms.py:476
 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."
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr "Adressdaten"
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr "Zusätzliche Daten"
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr "Kontodaten"
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr "Passwort"
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr "Passwort wiederholen"
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr "Die ausgewählte Aktion existiert nicht."
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 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."
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr "Keine gültige Auswahl."
 
@@ -315,741 +305,779 @@ msgstr "Kein Backup gefunden!"
 msgid "No backup result found!"
 msgstr "Kein Backupergebnis gefunden!"
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr "Zugeordnetes Schuljahr"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr "Boolean (Ja/Nein)"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr "Text (eine Zeile)"
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr "Datum und Uhrzeit"
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr "Dezimalzahl"
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr "Ganze Zahl"
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr "IP-Adresse"
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Boolean oder leer (Ja/Nein/weder)"
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr "Text (mehrzeilig)"
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr "URL / Link"
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr "Name"
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr "Startdatum"
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr "Enddatum"
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr "Das Startdatum muss vor dem Enddatum liegen."
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 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."
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr "Schuljahre"
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr "Person"
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr "Kann Adresse sehen"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr "Kann Kontaktdetails sehen"
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr "Kann Foto sehen"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr "Kann Avatar-Bild sehen"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr "Kann Gruppen einer Person sehen"
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr "Kann persönliche Daten sehen"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr "weiblich"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr "männlich"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr "andere"
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr "Verknüpfter Benutzer"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr "Zusätzliche Namen"
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr "Kurzname"
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr "Straße"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr "Hausnummer"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr "Postleitzahl"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr "Ort"
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr "Festnetz"
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr "Handy"
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr "Geburtsdatum"
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr "Geburtsort"
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr "Geschlecht"
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 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:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr "Bild/Avatar anzeigen"
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 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:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr "Erziehungsberechtigte / Eltern"
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr "Primärgruppe"
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr "Beschreibung"
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr "Kann Kindgruppen zu Gruppen zuordnen"
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr "Kann Statistiken über Gruppen sehen."
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr "Langname"
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr "Mitglieder"
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr "Leiter/-innen"
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr "Übergeordnete Gruppen"
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titel"
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr "Anwendung"
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr "Aktivität"
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr "Aktivitäten"
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr "Absender"
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr "Empfänger"
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr "Link"
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Symbol"
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr "Benachrichtigung schicken am"
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr "Gelesen"
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr "Versandt"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr "Kalenderalarm"
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr "Benachrichtigung"
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr "Benachrichtigungen"
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr "Link zur detaillierten Ansicht"
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
-msgstr ""
+msgstr "Priorität"
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr "Datum und Uhrzeit des Anzeigestarts"
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr "Anzeigezeitraum"
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr "Ankündigung"
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: 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:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr "Widget-Titel"
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr "Widget aktivieren"
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr "Widget ist kaputt"
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr "Größe auf Mobilgeräten"
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 px, 12 Spalten"
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr "Größe auf Tablets"
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr "> 600px, 12 Spalten"
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr "Größe auf Desktopgeräten"
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr "> 992 px, 12 Spalten"
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr "Größe auf großen Desktopgeräten"
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 px, 12 Spalten"
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr "Kann Standarddashboard bearbeiten"
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr "Dashboard-Widgets"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr "Symbol-URL"
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr "Externer-Link-Widget"
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr "Externer-Link-Widgets"
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr "Inhalt"
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr "Statischer-Inhalt-Widget"
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr "Statischer-Inhalt-Widgets"
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr "Reihenfolge"
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr "Teil des Standarddashboards"
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr "Reihenfolge der Dashboard-Widgets"
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr "Reihenfolgen der Dashboard-Widgets"
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr "Menü-ID"
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr "Benutzerdefiniertes Menü"
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr "Benutzerdefinierte Menüs"
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr "Menü"
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr "Benutzerdefiniertes Menüelement"
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr "Benutzerdefinierte Menüelemente"
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr "Titel des Typs"
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
-msgstr ""
+msgstr "Besitzer von Gruppen mit diesem Gruppentyp können die Gruppen sehen"
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
-msgstr ""
+msgstr "Besitzer von Gruppen mit diesem Gruppentyp können die Gruppenmitglieder sehen"
 
-#: aleksis/core/models.py:1084
-#, fuzzy
-#| msgid "Personal events"
+#: aleksis/core/models.py:1116
 msgid "Personal details"
-msgstr "Persönliche Veranstaltungen"
+msgstr "Persönliche Details"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr "Kontaktdetails"
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr "Avatar"
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
-msgstr ""
+msgstr "Informationen die Besitzer von Gruppen mit diesem Gruppentyp über die Gruppenmitglieder sehen können"
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr "Gruppentypen"
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr "Kann Systemstatus sehen"
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr "Kann Daten verwalten"
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr "Kann sich verkleiden"
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr "Kann Suche benutzen"
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr "Kann Konfiguration ändern"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr "Kann Einstellungen einer Person verändern"
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr "Kann Einstellungen einer Gruppe verändern"
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr "Kann die PDF-Generierung testen"
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr "Kann Personen einladen"
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr "Kann Geburtstagskalender sehen"
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr "Zugehörige Datenprüfungsaufgabe"
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr "Problem gelöst"
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr "Benachrichtigung gesendet"
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr "Datenprüfungsergebnis"
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr "Datenprüfungsergebnisse"
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr "Kann Datenprüfungen ausführen"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr "Kann Datenprüfungsprobleme lösen"
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr "Leiter"
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr "Datei abgelaufen am"
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr "Generierte HTML-Datei"
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr "Generierte PDF-Datei"
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr "PDF-Datei"
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr "PDF-Dateien"
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr "Task-Ergebnis"
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr "Task-Benutzer"
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr "Zurück-URL"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr "Fortschritt-Titel"
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr "Fehlernachricht"
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr "Erfolgsnachricht"
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr "URL, auf die bei Erfolg weitergeleitet wird"
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr "Titel des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr "URL des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr "Symbol des zusätzlichen Buttons"
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr "Ergebnis abgerufen"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr "Hintergrundaufgabe erfolgreich fertiggestellt"
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr "Die Hintergrundaufgabe '{}' wurde erfolgreich fertiggestellt."
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr "Hintergrundaufgabe fehlgeschlagen"
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr "Die Hintergrundaufgabe '{}' ist fehlgeschlagen."
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr "Hintergrundaufgabe"
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr "Task-Benutzer-Zuordnung"
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr "Task-Benutzer-Zuordnungen"
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr "Zusätzliche Attribute"
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr "Erlaubte Scopes, die ein Client anfordern kann"
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 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:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr "Kann Raum-Stundenplan sehen"
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr "Raum"
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr "Räume"
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr "Startdatum und -uhrzeit"
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr "Enddatum und -uhrzeit"
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr "Zeitzone"
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr "Wiederholungen"
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr "Ergänztes Basis-Ereignis"
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr "Kalender-Ereignis"
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr "Kalender-Ereignisse"
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr "Geburtstage"
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr "Geburtstag von {}"
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr "Ferien"
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr "Ferien"
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr "Kann Ferienkalender sehen"
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 msgid "Personal events"
 msgstr "Persönliche Veranstaltungen"
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr "Ort"
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr "E-Mail"
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 msgid "Related group"
 msgstr "Zugehörige Gruppe"
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr "Organisation"
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr "Organisationen"
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr "Audio"
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr "Anzeige"
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr "Prozedur"
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr "Veranstaltung"
+
+#: aleksis/core/models.py:2014
+msgid "Action"
+msgstr "Aktion"
+
+#: aleksis/core/models.py:2017
+msgid "Send notifications"
+msgstr "Benachrichtigungen schicken"
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr "Kalenderalarme"
+
+#: aleksis/core/models.py:2140
+msgid "Personal event alarm"
+msgstr "Persönlicher Kalenderalarm"
+
+#: aleksis/core/models.py:2141
+msgid "Personal event alarms"
+msgstr "Persönliche Kalenderalarme"
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr "Allgemein"
@@ -1258,46 +1286,82 @@ msgstr "Automatisch das Dashboard und seine Widgets aktualisieren"
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr "Automatisch das Dashboard und seine Widgets aktualisieren (auf der ganzen Seite)"
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr "Erster Tag, der im Kalender erscheint"
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr "Montag"
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr "Dienstag"
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr "Mittwoch"
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr "Donnerstag"
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr "Freitag"
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr "Samstag"
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr "Sonntag"
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr "Seitenstandard benutzen"
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr "Geburtstagskalender-Farbe"
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr "Ferienkalender-Farbe"
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr "Persönliche-Veranstaltungen-Farbe"
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr "Aktivierte Kalender"
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr "Komma-getrennte Liste von verbotenen Benutzernamen"
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr "Englisch"
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr "Deutsch"
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr "Ukrainisch"
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr "Löschen"
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr "Aktionen"
@@ -1814,22 +1878,6 @@ msgstr "Standard-Dashboard"
 msgid "Edit group"
 msgstr "Gruppe editieren"
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr "Gruppe erstellen"
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr "Gruppen filtern"
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr "Zurücksetzen"
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr "Ausgewählte Gruppen"
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr "Startseite"
@@ -2291,15 +2339,13 @@ msgstr "Verbieten"
 
 #: aleksis/core/templates/oauth2_provider/logout_confirm.html:5
 #: aleksis/core/templates/oauth2_provider/logout_confirm.html:29
-#, fuzzy
-#| msgid "Confirm"
 msgid "Confirm Logout"
-msgstr "Bestätigen"
+msgstr "Abmeldung bestätigen"
 
 #: aleksis/core/templates/oauth2_provider/logout_confirm.html:27
 #, python-format
 msgid "Confirm Logout requested by %(name)s"
-msgstr ""
+msgstr "Bestätigen Sie die von %(name)s angefragte Abmeldung"
 
 #: aleksis/core/templates/oauth2_provider/logout_confirm.html:44
 msgid "Logout"
@@ -2325,6 +2371,11 @@ msgstr ""
 "      Wenn Sie verbunden sind und der Fehler weiterhin auftritt, kontaktieren Sie bitte die Systemadministratoren:\n"
 "    "
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr "Suchen"
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr "Globale Suche"
@@ -2995,11 +3046,11 @@ msgstr "Deaktivieren"
 msgid "This username is not allowed."
 msgstr "Der Benutzername ist verboten."
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr "E-Mail"
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr "SMS"
 
@@ -3023,132 +3074,144 @@ msgstr "Es ist ein Fehler beim Generieren der PDF-Datei aufgetreten."
 msgid "Download PDF"
 msgstr "PDF herunterladen"
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr "Die Person wurde gespeichert."
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr "Die Gruppe wurde gespeichert."
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr "Der Wartungsmodus wurde erfolgreich angeschaltet."
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr "Der Wartungsmodus wurde erfolgreich ausgeschaltet."
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr "Die Ankündigung wurde gespeichert."
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr "Ankündigung wurde gelöscht."
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr "Das angeforderte Einstellungsregister existiert nicht"
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr "Die Einstellungen wurde gespeichert."
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr "Die Gruppe wurde gelöscht."
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr "Fortschritt: Datenprüfungen ausführen"
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr "Datenprüfungen laufen …"
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr "Die Datenprüfungen wurden erfolgreich ausgeführt."
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr "Es gab ein Problem beim Ausführen der Datenprüfungen."
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, 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:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr "Die angeforderte Lösungsoption existiert nicht"
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr "Das Dashboard-Widget wurde gespeichert."
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr "Das Dashboard-Widget wurde erstellt."
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr "Das Dashboard-Widget wurde gelöscht."
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ihre Dashboardkonfiguration wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr "Die Konfiguration des Standard-Dashboardes wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr "Die Einladung wurde erfolgreich erstellt. Der Einladungscode ist {code}"
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr "Wir haben die Berechtigungen erfolgreich zugewiesen."
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr "Die globale Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr "Die globale Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr "Die Objekt-Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr "Die Objekt-Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr "Wir haben Ihnen eine E-Mail mit Anweisungen zum Zurücksetzen Ihres Passworts gesendet."
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 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:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr "Das Drittanbieter-Konto wurde erfolgreich getrennt."
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 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:1216
+#: aleksis/core/views.py:1168
 msgid "Person was already invited."
 msgstr "Person wurde bereits eingeladen."
 
+#~ msgid "Create group"
+#~ msgstr "Gruppe erstellen"
+
+#~ msgid "Filter groups"
+#~ msgstr "Gruppen filtern"
+
+#~ msgid "Clear"
+#~ msgstr "Zurücksetzen"
+
+#~ msgid "Selected groups"
+#~ msgstr "Ausgewählte Gruppen"
+
 #~ msgid "Assign child groups to groups"
 #~ msgstr "Kindgruppen zu Gruppen zuordnen"
 
diff --git a/aleksis/core/locale/fr/LC_MESSAGES/django.po b/aleksis/core/locale/fr/LC_MESSAGES/django.po
index a7c63ae547d8a34164af552cdab6b41a49048aaa..a60dbead8094306a5bdfa18d3c37267aa7404a96 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: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\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,10 +44,8 @@ msgstr "Détails de contact"
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 #, fuzzy
 #| msgid "Group"
 msgid "Groups"
@@ -105,213 +103,205 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr ""
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr ""
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr ""
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 #, fuzzy
 #| msgid "Contact details"
 msgid "Search by contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr ""
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr "groupe"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 #, fuzzy
 #| msgid "Contact details"
 msgid "Contact data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 #, fuzzy
 #| msgid "Contact details"
 msgid "Advanced personal data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Cet nom est deja en utilisation."
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 #, fuzzy
 #| msgid "Contact details"
 msgid "Common data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 #, fuzzy
 #| msgid "Person"
 msgid "Persons"
 msgstr "Personne"
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr "Date"
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr "Prénom"
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr "Nom de famille"
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 #, fuzzy
 #| msgid "Contact details"
 msgid "Account data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr ""
 
@@ -335,793 +325,837 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 #, fuzzy
 #| msgid "Contact details"
 msgid "Start date"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr "Personne"
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view address"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view photo"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view avatar image"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view persons groups"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view personal details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 #, fuzzy
 #| msgid "First name"
 msgid "Short name"
 msgstr "Prénom"
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr "Sexe"
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr "Description"
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view statistics about group."
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Nom de famille"
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr ""
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 #, fuzzy
 #| msgid "Person"
 msgid "Personal details"
 msgstr "Personne"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 #, fuzzy
 #| msgid "Group"
 msgid "Group types"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view system status"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can impersonate"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can invite persons"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 #, fuzzy
 #| msgid "Contact details"
 msgid "E-Mail address"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 #, fuzzy
 #| msgid "Owners"
 msgid "Owner"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button title"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button URL"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional button icon"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional attributes"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view room timetable"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 #, fuzzy
 #| msgid "Contact details"
 msgid "Start date and time"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr ""
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr ""
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr ""
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr ""
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr ""
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr ""
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr ""
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr ""
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr ""
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 #, fuzzy
 #| msgid "Person"
 msgid "Personal events"
 msgstr "Personne"
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr ""
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 #, fuzzy
 #| msgid "Group"
 msgid "Related group"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr ""
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr ""
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+msgid "Action"
+msgstr ""
+
+#: aleksis/core/models.py:2017
+msgid "Send notifications"
+msgstr ""
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr ""
+
+#: aleksis/core/models.py:2140
+#, fuzzy
+#| msgid "Person"
+msgid "Personal event alarm"
+msgstr "Personne"
+
+#: aleksis/core/models.py:2141
+#, fuzzy
+#| msgid "Person"
+msgid "Personal event alarms"
+msgstr "Personne"
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr ""
@@ -1340,46 +1374,82 @@ msgstr ""
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr ""
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr ""
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr ""
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr ""
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
@@ -1832,22 +1902,6 @@ msgstr ""
 msgid "Edit group"
 msgstr ""
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr ""
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr ""
@@ -2314,6 +2368,11 @@ msgid ""
 "    "
 msgstr ""
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr ""
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr ""
@@ -2873,11 +2932,11 @@ msgstr ""
 msgid "This username is not allowed."
 msgstr "Cet nom est deja en utilisation."
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr ""
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr ""
 
@@ -2901,129 +2960,129 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 #, 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 6d5f93f8b104eb1ffbdea76e9d3c6510a1f3f5c4..cc4d428e9622401c5441e977be5e327e29fd6078 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: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\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,10 +48,8 @@ msgstr "Inscriptio electronica"
 msgid "Home and mobile phone"
 msgstr "Numerus telephoni mobilis"
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr "Greges"
 
@@ -111,217 +109,209 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr "Quaerere"
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr "Quaerere cum breve nomine"
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Search by contact details"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr ""
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr "Grex"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Create a new account"
 msgstr "Personae et computi"
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr "Anus scolae"
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 #, fuzzy
 #| msgid "Data management"
 msgid "Common data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr "personae"
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr "Photographia"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr "dies"
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr "tempus"
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Scribe nuntium:"
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr "Primus nomen"
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 #, fuzzy
 #| msgid "E-mail address"
 msgid "A person is using this e-mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 #, fuzzy
 #| msgid "Who should see the announcement?"
 msgid "Who should get the permission?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Address data"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional data"
 msgstr "addita nomines"
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 #, fuzzy
 #| msgid "Data management"
 msgid "Account data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr ""
 
@@ -347,837 +337,885 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 #, fuzzy
 #| msgid "Edit school term"
 msgid "Linked school term"
 msgstr "Muta anum scolae"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr "Dies et hora"
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 #, fuzzy
 #| msgid "E-mail address"
 msgid "IP address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr "Nomen"
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr "ani scolae"
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr "Persona"
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view contact details"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view photo"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view avatar image"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view persons groups"
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can view personal details"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr "femininum"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr "maskulinum"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr "Breve nomen"
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr "Via"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr "Numerus domini"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr "Numerus directorius"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr "Urbs"
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr "Numerus telephoni domi"
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr "Numerus telephoni mobilis"
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr "Genus"
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr "Parentes"
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr "Descriptio"
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view statistics about group."
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titulus"
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr "Mittens"
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Nota"
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 #, fuzzy
 #| msgid "Notification"
 msgid "Send notification at"
 msgstr "Nuntius"
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr ""
+
+#: aleksis/core/models.py:752
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipient"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipients"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget Title"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget is broken"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Can edit default dashboard"
 msgstr "Forum"
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widgets"
 msgstr "Forum"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 #, fuzzy
 #| msgid "Icon"
 msgid "Icon URL"
 msgstr "Nota"
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget order"
 msgstr "Forum"
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget orders"
 msgstr "Forum"
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 #, fuzzy
 #| msgid "Persons"
 msgid "Personal details"
 msgstr "personae"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Contact details"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Grex"
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 #, fuzzy
 #| msgid "Groups"
 msgid "Group types"
 msgstr "Greges"
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 #, fuzzy
 #| msgid "System status"
 msgid "Can view system status"
 msgstr "Status systemae"
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 #, fuzzy
 #| msgid "Data management"
 msgid "Can manage data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can impersonate"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can invite persons"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification sent"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 #, fuzzy
 #| msgid "E-mail address"
 msgid "E-Mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 #, fuzzy
 #| msgid "Icon"
 msgid "Back URL"
 msgstr "Nota"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button title"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button URL"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional button icon"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional attributes"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view room timetable"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 #, fuzzy
 #| msgid "Date and time"
 msgid "Start date and time"
 msgstr "Dies et hora"
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 #, fuzzy
 #| msgid "Date and time"
 msgid "End date and time"
 msgstr "Dies et hora"
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 #, fuzzy
 #| msgid "Time"
 msgid "Timezone"
 msgstr "tempus"
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr ""
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr ""
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr ""
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr ""
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr ""
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr ""
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr ""
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr ""
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 #, fuzzy
 #| msgid "Persons"
 msgid "Personal events"
 msgstr "personae"
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 #, fuzzy
 #| msgid "Notifications"
 msgid "Location"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 #, fuzzy
 #| msgid "Group"
 msgid "Related group"
 msgstr "Grex"
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Organisation"
 msgstr "Simulare aliquem"
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Organisations"
 msgstr "Simulare aliquem"
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+#, fuzzy
+#| msgid "Notifications"
+msgid "Action"
+msgstr "Nuntii"
+
+#: aleksis/core/models.py:2017
+#, fuzzy
+#| msgid "Notification"
+msgid "Send notifications"
+msgstr "Nuntius"
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr ""
+
+#: aleksis/core/models.py:2140
+#, fuzzy
+#| msgid "Persons"
+msgid "Personal event alarm"
+msgstr "personae"
+
+#: aleksis/core/models.py:2141
+#, fuzzy
+#| msgid "Persons"
+msgid "Personal event alarms"
+msgstr "personae"
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr ""
@@ -1404,46 +1442,82 @@ msgstr ""
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr ""
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr ""
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr ""
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr "Britannicus"
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr "Germanus"
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 #, fuzzy
 #| msgid "Notifications"
@@ -1924,22 +1998,6 @@ msgstr "Forum"
 msgid "Edit group"
 msgstr ""
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr ""
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr ""
@@ -2424,6 +2482,11 @@ msgid ""
 "    "
 msgstr ""
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr "Quaerere"
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr ""
@@ -2990,11 +3053,11 @@ msgstr ""
 msgid "This username is not allowed."
 msgstr ""
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr ""
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr ""
 
@@ -3018,131 +3081,131 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 #, fuzzy
 #| msgid "System status"
 msgid "Run data checks …"
 msgstr "Status systemae"
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 msgid "Person was already invited."
 msgstr ""
 
diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
index add3eaa2d5f6eb6f504d4065087d46cc29b7f7e0..6da342f356a37e9f98b071eb971832bbc8eae54f 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: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\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,10 +41,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr ""
 
@@ -98,199 +96,191 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr ""
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr ""
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr ""
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr ""
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr ""
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr ""
 
@@ -314,739 +304,779 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr ""
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 msgid "Personal details"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr ""
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr ""
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr ""
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr ""
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr ""
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr ""
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr ""
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr ""
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr ""
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 msgid "Personal events"
 msgstr ""
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr ""
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 msgid "Related group"
 msgstr ""
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr ""
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr ""
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+msgid "Action"
+msgstr ""
+
+#: aleksis/core/models.py:2017
+msgid "Send notifications"
+msgstr ""
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr ""
+
+#: aleksis/core/models.py:2140
+msgid "Personal event alarm"
+msgstr ""
+
+#: aleksis/core/models.py:2141
+msgid "Personal event alarms"
+msgstr ""
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr ""
@@ -1255,46 +1285,82 @@ msgstr ""
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr ""
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr ""
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr ""
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr ""
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
@@ -1741,22 +1807,6 @@ msgstr ""
 msgid "Edit group"
 msgstr ""
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr ""
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr ""
@@ -2208,6 +2258,11 @@ msgid ""
 "    "
 msgstr ""
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr ""
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr ""
@@ -2758,11 +2813,11 @@ msgstr ""
 msgid "This username is not allowed."
 msgstr ""
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr ""
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr ""
 
@@ -2786,129 +2841,129 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 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 484afef140a8ab77f8a002e09250147ce5c39f20..dd47ad4c961409f04421a0d7d49211c701e3a149 100644
--- a/aleksis/core/locale/ru/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/ru/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\n"
 "PO-Revision-Date: 2023-08-12 09:43+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
 "Language-Team: Russian <https://translate.edugit.org/projects/aleksis/aleksis-core/ru/>\n"
@@ -42,10 +42,8 @@ msgstr "Адрес эл.почты"
 msgid "Home and mobile phone"
 msgstr "Домашний и мобильный телефоны"
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr "Группы"
 
@@ -110,199 +108,191 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr "Подтвердите свой адрес эл.почты"
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr "Поиск"
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr "Поиск по имени"
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr "Поиск по контактным данным"
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr "Разрешение"
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr "Тип содержимого"
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr "Пользователь"
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr "Группа"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr "Основные данные"
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr "Адрес"
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr "Контактные данные"
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr "Дополнительные личные данные"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr "Новый пользователь"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr "Создать новую учётную запись"
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr "После выбора существующего пользователя создать новый логин нельзя."
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Этот логин уже занят."
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr "Учебный год"
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr "Общие данные"
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr "Люди"
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr "Фото"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr "Дата"
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr "Время"
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr "С какого и по какое время это объявление должно отображаться?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Кто должен видеть это объявление?"
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Напишите свое объявление:"
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr "Объявления для прошлого Вам создавать не разрешено."
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr "Дата и время начала должны быть до даты и времени окончания."
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr "Нужен хотя бы один получатель."
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr "Код приглашения"
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr "Укажите, пожалуйста, код приглашения."
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr "Имя"
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr "Фамилия"
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr "Этот эл.адрес кем-то используется"
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr "Кто должен получить такое разрешение?"
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr "В случае чего?"
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr "Отметьте объекты, к которым будет предоставлен доступ:"
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr "Предоставить доступ ко всем объектам"
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr "Вам нужно выбрать хотя бы одну группу или физлицо, кто получит доступ."
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr "Вы должны предоставить доступ ко всем или к конкретным объектам."
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr "Подробности адреса"
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr "Дополнительные данные"
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr "Данные учётной записи"
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr "Пароль"
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr "Пароль (ещё раз)"
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr "Выбранное действие не существует."
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr "У Вас нет разрешения на запуск {} на всех выбранных объектах."
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr "Неправильный выбор."
 
@@ -326,755 +316,807 @@ msgstr "Резервная копия не найдена!"
 msgid "No backup result found!"
 msgstr "Результат резервного копирования не найден!"
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr "Связанный учебный год"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr "Булево (Да/Нет)"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr "Текст (одна строка)"
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr "Дата и время"
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr "Десятичное число"
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr "Адрес эл.почты"
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr "Целое"
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr "IP адрес"
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Булево или пустое (Да/Нет/Ничего)"
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr "Текст (многострочный)"
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr "URL / Ссылка"
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr "Полное имя"
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr "Дата начала"
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr "Дата окончания"
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr "Дата начала должна быть ранее даты окончания."
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr "На это время или на его часть уже запланирован учебный год."
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr "Учебный год"
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr "Физлицо"
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr "Может видеть адрес"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr "Может видеть контактные данные"
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr "Может видеть фото"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr "Может видеть аватар"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr "Может видеть группы лиц"
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr "Может видеть личные данные"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr "жен"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr "муж"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr "другой"
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr "Связанный пользователь"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr "Дополнительные имена"
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr "Короткое имя"
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr "Улица"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr "Номер дома"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr "Почтовый индекс"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr "Город/место"
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr "Домашний телефон"
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr "Мобильный телефон"
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr "Дата рождения"
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr "Место рождения"
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr "Пол"
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr "Это официальное фото, которое используется для документов и внутренних нужд."
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr "Отобразить фото/аватар"
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr "Это фото или аватар для общего отображения."
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr "Опекуны / Родители"
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr "Основная группа"
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr "Описание"
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr "Может определять дочерние группы в группы"
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr "Может видеть статистику группы."
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr "Длинное имя"
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr "Участники"
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr "Владельцы"
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr "Родительские группы"
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr "Тип группы"
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Название"
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr "Приложение"
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr "Активность"
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr "Активности"
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr "Отправитель"
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr "Получатель"
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr "Ссылка"
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Иконка"
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr "Отправить уведомление в"
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr "Читать"
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr "Отправлено"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+#, fuzzy
+#| msgid "Calendar"
+msgid "Calendar alarm"
+msgstr "Календарь"
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr "Уведомление"
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr "Уведомления"
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr "Ссылка на подробный обзор"
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr "Дата и время, с которого показывать"
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr "Дата и время, по какое показывать"
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr "Объявление"
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Объявление"
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr "Получатель объявления"
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr "Получатели объявления"
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr "Название виджета"
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr "Активировать виджет"
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr "Виджет поломался"
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr "Размер на мобильных"
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 пикс, 12 столбцов"
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr "Размер на планшетах"
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr "> 600 пикс, 12 столбцов"
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr "Размер на ПК"
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr "> 992 пикс, 12 столбцов"
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr "Размер для больших экранов"
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 пикс, 12 столбцов"
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr "Может редактировать типовую/стандартную информпанель"
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr "Виджет информпанели"
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr "Виджеты информпанели"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr "Иконка URL"
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr "Внешняя ссылка на виджет"
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr "Внешние ссылки на виджеты"
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr "Содержимое"
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr "Виджет с постоянным содержимым"
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr "Виджеты с постоянным содержимым"
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr "Виджет информпанели"
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr "Порядок"
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr "Часть типовой информпанели"
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr "Порядок виджета на информпанели"
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr "Порядок виджетов на информпанели"
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr "Меню ID"
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr "Пользовательское меню"
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr "Пользовательские меню"
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr "Меню"
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr "Пункт пользовательского меню"
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr "Пункты пользовательского меню"
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr "Название типа"
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 #, fuzzy
 #| msgid "Personal Calendar URLs"
 msgid "Personal details"
 msgstr "URL-ссылка собственных календарей"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr "Контактные данные"
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr "Аватар"
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr "Тип группы"
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr "Типы групп"
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr "Может просматривать состояние системы"
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr "Может управлять данными"
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr "Может маскироваться"
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr "Может использовать поиск"
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr "Может менять свойства сайта"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr "Может менять персональные свойства"
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr "Может менять свойства группы"
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr "Может генерировать тестовые PDF"
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr "Может приглашать других"
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 #, fuzzy
 #| msgid "Birthday Calendar"
 msgid "Can view birthday calendar"
 msgstr "Календарь Дней Рождения"
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr "Задание проверки связанных данных"
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr "Проблема решена"
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr "Уведомление отправлено"
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr "Результат проверки данных"
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr "Результаты проверки данных"
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr "Может запускать проверки данных"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr "Может решать проблемы проверки данных"
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr "Адрес эл.почты"
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr "Владелец"
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr "Файл действителен до"
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr "Сгенерированный файл HTML"
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr "Сгенерированный файл PDF"
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr "Файл PDF"
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr "Файлы PDF"
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr "Результат задания"
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr "Пользователь задания"
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr "URL для возврата"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr "Название процесса"
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr "Сообщение об ошибке"
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr "Сообщение об успехе"
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr "URL для перенаправления в случае успеха"
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr "Название дополнительной кнопки"
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr "URL дополнительной кнопки"
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr "Иконка дополнительной кнопки"
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr "Полученный результат"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr "Фоновое задание успешно завершено"
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr "Фоновое задание \"{}\" успешно завершено."
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr "Ошибка фонового задания"
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr "Ошибка фонового задания \"{}\"."
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr "Фоновое задание"
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr "Назначение пользователя задания"
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr "Назначения пользователей задания"
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr "Дополнительные атрибуты"
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr "Разрешённые пределы действия, которые могут запрашивать клиенты"
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr "Это изображение будет использоваться в качестве значка во время авторизации. Должно быть квадратным."
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr "Может просмативать расписание комнаты"
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr "Комната"
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr "Комнаты"
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr "Дата и время начала"
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr "Дата и время окончания"
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr "Часовой пояс"
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr "Повторы"
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr "Исправлено основное событие"
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr "Календарное событие"
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr "Календарные события"
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr "Дни Рождения"
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr "{} отмечает День Рождения"
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr "Выходные"
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr "Выходной"
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 #, fuzzy
 #| msgid "Birthday Calendar"
 msgid "Can view holiday calendar"
 msgstr "Календарь Дней Рождения"
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 #, fuzzy
 #| msgid "Personal Calendar URLs"
 msgid "Personal events"
 msgstr "URL-ссылка собственных календарей"
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 #, fuzzy
 #| msgid "Notification"
 msgid "Location"
 msgstr "Уведомление"
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 #, fuzzy
 #| msgid "Create group"
 msgid "Related group"
 msgstr "Создать группу"
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 #, fuzzy
 #| msgid "Internationalisation"
 msgid "Organisation"
 msgstr "Интернационализация"
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 #, fuzzy
 #| msgid "Invitations"
 msgid "Organisations"
 msgstr "Приглашения"
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+#, fuzzy
+#| msgid "Actions"
+msgid "Action"
+msgstr "Действия"
+
+#: aleksis/core/models.py:2017
+#, fuzzy
+#| msgid "Send notification at"
+msgid "Send notifications"
+msgstr "Отправить уведомление в"
+
+#: aleksis/core/models.py:2131
+#, fuzzy
+#| msgid "Calendar"
+msgid "Calendar alarms"
+msgstr "Календарь"
+
+#: aleksis/core/models.py:2140
+#, fuzzy
+#| msgid "Personal Calendar URLs"
+msgid "Personal event alarm"
+msgstr "URL-ссылка собственных календарей"
+
+#: aleksis/core/models.py:2141
+#, fuzzy
+#| msgid "Personal Calendar URLs"
+msgid "Personal event alarms"
+msgstr "URL-ссылка собственных календарей"
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr "Общее"
@@ -1283,50 +1325,88 @@ msgstr "Автоматически обновлять информпанель 
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr "Автоматически обновлять информпанель и её виджеты (для всего сайта)"
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+#, fuzzy
+#| msgid "Holiday"
+msgid "Friday"
+msgstr "Выходной"
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr "Цвет календаря Дней Рождения"
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr "Цвет календаря Выходных"
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 #, fuzzy
 #| msgid "Holiday calendar feed color"
 msgid "Personal events feed color"
 msgstr "Цвет календаря Выходных"
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr "Активные календари"
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 #, fuzzy
 #| msgid "Regular expression for allowed usernames"
 msgid "Comma-separated list of disallowed usernames"
 msgstr "Регулярное выражение для разрешённых логинов"
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr "Английский"
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr "Немецкий"
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr "Украинский"
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr "Удалить"
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr "Действия"
@@ -1846,22 +1926,6 @@ msgstr "Типовая информпанель"
 msgid "Edit group"
 msgstr "Редактировать группу"
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr "Создать группу"
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr "Фильтровать группы"
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr "Очистить"
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr "Выбранные группы"
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr "Домой"
@@ -2357,6 +2421,11 @@ msgstr ""
 "      Если интернет работает и ошибка всё равно присутствует, обратитесь, пожалуйста, к системным администраторам:\n"
 "    "
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr "Поиск"
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr "Глобальный поиск"
@@ -3027,11 +3096,11 @@ msgstr "Отключить"
 msgid "This username is not allowed."
 msgstr "Этот логин уже занят."
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr "Эл.почта"
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr "СМС"
 
@@ -3055,132 +3124,144 @@ msgstr "Во время создания файла PDF возникла про
 msgid "Download PDF"
 msgstr "Скачать PDF"
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr "Физлицо сохранено."
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr "Группа сохранена."
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr "Режим обслуживания успешно активирован."
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr "Режим обслуживания успешно выключен."
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr "Объявление сохранено."
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr "Объявление удалено."
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr "Журнал с запрошенными свойствами не существует"
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr "Свойства сохранены."
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr "Группа удалена."
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr "В процессе: Запуск проверки данных"
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr "Запускается проверка данных …"
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr "Проверка данных успешно запущена."
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr "Во время запуска проверки данных возникла проблема."
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr "Вариант решения \"{solve_option_obj.verbose_name}\" "
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr "Запрошенный вариант решения не существует"
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr "Виджет информпанели сохранён."
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr "Виджет информпанели создан."
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr "Виджет информпанели удалён."
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ваша конфигурация информпанели сохранена."
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr "Конфигурация типовой/стандартной информпанели."
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr "Приглашение успешно создано. Код приглашения: {code}"
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr "Мы успешно назначили доступы."
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr "Глобальный пользовательский доступ удалён."
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr "Глобальный групповой доступ удалён."
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr "Объектный пользовательский доступ удалён."
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr "Объектный групповой доступ удалён."
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr "Учётную запись третьей стороны нельзя отключить, т.к. это единственный способ входа."
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr "Учётная запись третьей стороны успешно отключена."
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr "Владелец указанного эл.адреса успешно приглашён. Инструкции о дальнейших действиях отправлены на эл.почту."
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 msgid "Person was already invited."
 msgstr "Кто-то уже пригласил его/её."
 
+#~ msgid "Create group"
+#~ msgstr "Создать группу"
+
+#~ msgid "Filter groups"
+#~ msgstr "Фильтровать группы"
+
+#~ msgid "Clear"
+#~ msgstr "Очистить"
+
+#~ msgid "Selected groups"
+#~ msgstr "Выбранные группы"
+
 #~ msgid "Assign child groups to groups"
 #~ msgstr "Определить дочерние группы к группе"
 
diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
index d0b7ea7cafb46c09c643f3d2093b261cd7b695f2..42ede65bb7463c988d4d183a1a1f830d4c9d1eaa 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: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\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,10 +41,8 @@ msgstr ""
 msgid "Home and mobile phone"
 msgstr ""
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr ""
 
@@ -98,199 +96,191 @@ msgstr ""
 msgid "There was a non-unique email address."
 msgstr ""
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr ""
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr ""
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr ""
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr ""
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr ""
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr ""
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr ""
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr ""
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr ""
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr ""
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr ""
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr ""
 
@@ -314,739 +304,779 @@ msgstr ""
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr ""
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr ""
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr ""
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr ""
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+msgid "Calendar alarm"
+msgstr ""
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr ""
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr ""
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr ""
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 msgid "Personal details"
 msgstr ""
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr ""
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr ""
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr ""
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr ""
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr ""
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr ""
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr ""
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr ""
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr ""
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr ""
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr ""
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr ""
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr ""
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr ""
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr ""
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr ""
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr ""
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr ""
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr ""
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr ""
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr ""
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr ""
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr ""
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr ""
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr ""
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr ""
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr ""
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr ""
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr ""
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr ""
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr ""
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr ""
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr ""
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 msgid "Personal events"
 msgstr ""
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr ""
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr ""
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 msgid "Related group"
 msgstr ""
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr ""
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr ""
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+msgid "Action"
+msgstr ""
+
+#: aleksis/core/models.py:2017
+msgid "Send notifications"
+msgstr ""
+
+#: aleksis/core/models.py:2131
+msgid "Calendar alarms"
+msgstr ""
+
+#: aleksis/core/models.py:2140
+msgid "Personal event alarm"
+msgstr ""
+
+#: aleksis/core/models.py:2141
+msgid "Personal event alarms"
+msgstr ""
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr ""
@@ -1255,46 +1285,82 @@ msgstr ""
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+msgid "Friday"
+msgstr ""
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr ""
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr ""
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr ""
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr ""
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr ""
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
@@ -1741,22 +1807,6 @@ msgstr ""
 msgid "Edit group"
 msgstr ""
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr ""
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr ""
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr ""
@@ -2208,6 +2258,11 @@ msgid ""
 "    "
 msgstr ""
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr ""
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr ""
@@ -2758,11 +2813,11 @@ msgstr ""
 msgid "This username is not allowed."
 msgstr ""
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr ""
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr ""
 
@@ -2786,128 +2841,128 @@ msgstr ""
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr ""
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr ""
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr ""
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr ""
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr ""
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr ""
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 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 1bdaa62464e777fca04ad1537580c7e0cc508125..e50d207a27f0890617bacf28e1da7832268d857b 100644
--- a/aleksis/core/locale/uk/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/uk/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-10-15 15:50+0200\n"
+"POT-Creation-Date: 2025-01-14 18:15+0100\n"
 "PO-Revision-Date: 2024-08-29 08:21+0000\n"
 "Last-Translator: Serhii Horichenko <m@sgg.im>\n"
 "Language-Team: Ukrainian <https://translate.edugit.org/projects/aleksis/aleksis-core/uk/>\n"
@@ -42,10 +42,8 @@ msgstr "Ел.адреса"
 msgid "Home and mobile phone"
 msgstr "Домашній та мобільний телефони"
 
-#: aleksis/core/apps.py:182 aleksis/core/forms.py:222
-#: aleksis/core/models.py:479 aleksis/core/models.py:1089
-#: aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/apps.py:182 aleksis/core/forms.py:223
+#: aleksis/core/models.py:495 aleksis/core/models.py:1121
 msgid "Groups"
 msgstr "Групи"
 
@@ -99,199 +97,191 @@ msgstr "Переконайтеся, що е-адреси для всіх є ун
 msgid "There was a non-unique email address."
 msgstr "Знайдені неунікальні е-адреси."
 
-#: aleksis/core/filters.py:39 aleksis/core/templates/core/group/list.html:20
-#: aleksis/core/templates/search/search.html:7
-#: aleksis/core/templates/search/search.html:22
-msgid "Search"
-msgstr "Пошук"
-
-#: aleksis/core/filters.py:56
+#: aleksis/core/filters.py:43
 msgid "Search by name"
 msgstr "Пошук за ім'ям"
 
-#: aleksis/core/filters.py:68
+#: aleksis/core/filters.py:55
 msgid "Search by contact details"
 msgstr "Пошук за контактними даними"
 
-#: aleksis/core/filters.py:89
+#: aleksis/core/filters.py:76
 msgid "Permission"
 msgstr "Дозвіл"
 
-#: aleksis/core/filters.py:97
+#: aleksis/core/filters.py:84
 msgid "Content type"
 msgstr "Тип змісту"
 
-#: aleksis/core/filters.py:110 aleksis/core/models.py:672
+#: aleksis/core/filters.py:97 aleksis/core/models.py:688
 msgid "User"
 msgstr "Користувач"
 
-#: aleksis/core/filters.py:132 aleksis/core/models.py:478
+#: aleksis/core/filters.py:119 aleksis/core/models.py:494
 msgid "Group"
 msgstr "Група"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:556
+#: aleksis/core/forms.py:47 aleksis/core/forms.py:557
 msgid "Base data"
 msgstr "Основні дані"
 
-#: aleksis/core/forms.py:51 aleksis/core/models.py:1085
-#: aleksis/core/tables.py:29
+#: aleksis/core/forms.py:52 aleksis/core/models.py:1117
 msgid "Address"
 msgstr "Адреса"
 
-#: aleksis/core/forms.py:52 aleksis/core/forms.py:565
+#: aleksis/core/forms.py:53 aleksis/core/forms.py:566
 msgid "Contact data"
 msgstr "Контактні дані"
 
-#: aleksis/core/forms.py:54
+#: aleksis/core/forms.py:55
 msgid "Advanced personal data"
 msgstr "Додаткові особисті дані"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr "Новий користувач"
 
-#: aleksis/core/forms.py:104
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr "Створити новий обліковий запис"
 
-#: aleksis/core/forms.py:131
+#: aleksis/core/forms.py:132
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr "Обравши вже існуючого користувача неможливо створити новий логін."
 
-#: aleksis/core/forms.py:135
+#: aleksis/core/forms.py:136
 msgid "This username is already in use."
 msgstr "Такий логін вже зайнятий."
 
-#: aleksis/core/forms.py:152 aleksis/core/models.py:150
+#: aleksis/core/forms.py:153 aleksis/core/models.py:163
 msgid "School term"
 msgstr "Навчальний рік"
 
-#: aleksis/core/forms.py:153
+#: aleksis/core/forms.py:154
 msgid "Common data"
 msgstr "Загальні дані"
 
-#: aleksis/core/forms.py:154 aleksis/core/forms.py:209
-#: aleksis/core/models.py:171
+#: aleksis/core/forms.py:155 aleksis/core/forms.py:210
+#: aleksis/core/models.py:187
 msgid "Persons"
 msgstr "Особи"
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:227
-#: aleksis/core/models.py:532 aleksis/core/models.py:1087
-#: aleksis/core/tables.py:28
+#: aleksis/core/forms.py:156 aleksis/core/models.py:243
+#: aleksis/core/models.py:548 aleksis/core/models.py:1119
 msgid "Photo"
 msgstr "Фото"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:93
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:95
 msgid "Date"
 msgstr "Дата"
 
-#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
-#: aleksis/core/models.py:101
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:103
 msgid "Time"
 msgstr "Час"
 
-#: aleksis/core/forms.py:235
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr "З якого по який час повинно відображатися це оголошення?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Хто повинен бачити це оголошення?"
 
-#: aleksis/core/forms.py:239
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Складіть своє оголошеня:"
 
-#: aleksis/core/forms.py:240
+#: aleksis/core/forms.py:241
 msgid "Set a priority"
 msgstr ""
 
-#: aleksis/core/forms.py:279
+#: aleksis/core/forms.py:280
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr "Оголошення для минулого Вам створювати не дозволено."
 
-#: aleksis/core/forms.py:283
+#: aleksis/core/forms.py:284
 msgid "The from date and time must be earlier then the until date and time."
 msgstr "Дата і час початку повинні бути раніше за дату і час закінчення."
 
-#: aleksis/core/forms.py:292
+#: aleksis/core/forms.py:293
 msgid "You need at least one recipient."
 msgstr "Вам потрібен принаймні один отримувач."
 
-#: aleksis/core/forms.py:386
+#: aleksis/core/forms.py:387
 msgid "Invitation code"
 msgstr "Код запрошення"
 
-#: aleksis/core/forms.py:387
+#: aleksis/core/forms.py:388
 msgid "Please enter your invitation code."
 msgstr "Напишіть, будь ласка, свій код запрошення."
 
-#: aleksis/core/forms.py:394 aleksis/core/models.py:200
+#: aleksis/core/forms.py:395 aleksis/core/models.py:216
 msgid "First name"
 msgstr "Ім'я"
 
-#: aleksis/core/forms.py:395 aleksis/core/models.py:201
+#: aleksis/core/forms.py:396 aleksis/core/models.py:217
 msgid "Last name"
 msgstr "Прізвище"
 
-#: aleksis/core/forms.py:404
+#: aleksis/core/forms.py:405
 msgid "A person is using this e-mail address"
 msgstr "Цією е-адресою хтось користується"
 
-#: aleksis/core/forms.py:432
+#: aleksis/core/forms.py:433
 msgid "Who should get the permission?"
 msgstr "Хто повинен отримати такий дозвіл?"
 
-#: aleksis/core/forms.py:433
+#: aleksis/core/forms.py:434
 msgid "On what?"
 msgstr "В разі чого?"
 
-#: aleksis/core/forms.py:459
+#: aleksis/core/forms.py:460
 msgid "Select objects which the permission should be granted for:"
 msgstr "Оберіть об'єкти, до яких буде наданий дозвіл:"
 
-#: aleksis/core/forms.py:462
+#: aleksis/core/forms.py:463
 msgid "Grant the permission for all objects"
 msgstr "Надати дозвіл до всіх об'єктів"
 
-#: aleksis/core/forms.py:470
+#: aleksis/core/forms.py:471
 msgid "You must select at least one group or person which should get the permission."
 msgstr "Ви повинні обрати принаймні одну групу або особу, хто буде мати дозвіл."
 
-#: aleksis/core/forms.py:475
+#: aleksis/core/forms.py:476
 msgid "You must grant the permission to all objects or to specific objects."
 msgstr "Ви повинні надати дозвіл до всіх або до конкретних об'єктів."
 
-#: aleksis/core/forms.py:561
+#: aleksis/core/forms.py:562
 msgid "Address data"
 msgstr "Дані адреси"
 
-#: aleksis/core/forms.py:567
+#: aleksis/core/forms.py:568
 msgid "Additional data"
 msgstr "Додаткові дані"
 
-#: aleksis/core/forms.py:573
+#: aleksis/core/forms.py:574
 msgid "Account data"
 msgstr "Дані облікового запису"
 
-#: aleksis/core/forms.py:580
+#: aleksis/core/forms.py:581
 msgid "Password"
 msgstr "Пароль"
 
-#: aleksis/core/forms.py:583
+#: aleksis/core/forms.py:584
 msgid "Password (again)"
 msgstr "Пароль (ще раз)"
 
-#: aleksis/core/forms.py:735
+#: aleksis/core/forms.py:736
 msgid "The selected action does not exist."
 msgstr "Обрана дія не існує."
 
-#: aleksis/core/forms.py:746
+#: aleksis/core/forms.py:747
 msgid "You do not have permission to run {} on all selected objects."
 msgstr "У Вас відсутній дозвіл на запуск {} на усіх обраних об'єктах."
 
-#: aleksis/core/forms.py:802
+#: aleksis/core/forms.py:803
 msgid "No valid selection."
 msgstr "Неправильний вибір."
 
@@ -315,741 +305,793 @@ msgstr "Резервна копія не знайдена!"
 msgid "No backup result found!"
 msgstr "Результат резервного копіювання не знайдений!"
 
-#: aleksis/core/mixins.py:447
+#: aleksis/core/mixins.py:449
 msgid "Linked school term"
 msgstr "Пов'язаний навчальний рік"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:93
 msgid "Boolean (Yes/No)"
 msgstr "Логічне (Так/Ні)"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:94
 msgid "Text (one line)"
 msgstr "Текст (один рядок)"
 
-#: aleksis/core/models.py:94
+#: aleksis/core/models.py:96
 msgid "Date and time"
 msgstr "Дата і час"
 
-#: aleksis/core/models.py:95
+#: aleksis/core/models.py:97
 msgid "Decimal number"
 msgstr "Десятичне число"
 
-#: aleksis/core/models.py:96 aleksis/core/models.py:220
+#: aleksis/core/models.py:98 aleksis/core/models.py:236
 msgid "E-mail address"
 msgstr "Адреса е-пошти"
 
-#: aleksis/core/models.py:97
+#: aleksis/core/models.py:99
 msgid "Integer"
 msgstr "Ціле"
 
-#: aleksis/core/models.py:98
+#: aleksis/core/models.py:100
 msgid "IP address"
 msgstr "IP адреса"
 
-#: aleksis/core/models.py:99
+#: aleksis/core/models.py:101
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Логічне або порожнє (Так/Ні/Нічого)"
 
-#: aleksis/core/models.py:100
+#: aleksis/core/models.py:102
 msgid "Text (multi-line)"
 msgstr "Текст (багаторядковий)"
 
-#: aleksis/core/models.py:102
+#: aleksis/core/models.py:104
 msgid "URL / Link"
 msgstr "URL / Посилання"
 
-#: aleksis/core/models.py:114 aleksis/core/models.py:1036
-#: aleksis/core/models.py:1761 aleksis/core/models.py:1907
+#: aleksis/core/models.py:127 aleksis/core/models.py:1068
+#: aleksis/core/models.py:1819 aleksis/core/models.py:1970
 msgid "Name"
 msgstr "Повне ім'я"
 
-#: aleksis/core/models.py:116 aleksis/core/models.py:1491
+#: aleksis/core/models.py:129 aleksis/core/models.py:1522
 msgid "Start date"
 msgstr "Дата початку"
 
-#: aleksis/core/models.py:117 aleksis/core/models.py:1492
+#: aleksis/core/models.py:130 aleksis/core/models.py:1523
 msgid "End date"
 msgstr "Дата закінчення"
 
-#: aleksis/core/models.py:136
+#: aleksis/core/models.py:149
 msgid "The start date must be earlier than the end date."
 msgstr "Початкова дата повинна бути раніше кінцевої дати."
 
-#: aleksis/core/models.py:143
+#: aleksis/core/models.py:156
 msgid "There is already a school term for this time or a part of this time."
 msgstr "На цей час або на частину цього часу вже припадає навчальний рік."
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:164
 msgid "School terms"
 msgstr "Навчальні роки"
 
-#: aleksis/core/models.py:170 aleksis/core/models.py:985
+#: aleksis/core/models.py:186 aleksis/core/models.py:1017
 msgid "Person"
 msgstr "Особа"
 
-#: aleksis/core/models.py:173
+#: aleksis/core/models.py:189
 msgid "Can view address"
 msgstr "Може бачити адресу"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:190
 msgid "Can view contact details"
 msgstr "Може бачити контактні дані"
 
-#: aleksis/core/models.py:175
+#: aleksis/core/models.py:191
 msgid "Can view photo"
 msgstr "Може бачити фото"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:192
 msgid "Can view avatar image"
 msgstr "Може бачити аватар"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:193
 msgid "Can view persons groups"
 msgstr "Може бачити групи особи"
 
-#: aleksis/core/models.py:178
+#: aleksis/core/models.py:194
 msgid "Can view personal details"
 msgstr "Може бачити особисті дані"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "female"
 msgstr "жін"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "male"
 msgstr "чол"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:205
 msgid "other"
 msgstr "інший"
 
-#: aleksis/core/models.py:197 aleksis/core/models.py:1355
+#: aleksis/core/models.py:213 aleksis/core/models.py:1386
 msgid "Linked user"
 msgstr "Пов'язаний користувач"
 
-#: aleksis/core/models.py:203
+#: aleksis/core/models.py:219
 msgid "Additional name(s)"
 msgstr "Додаткові імена"
 
-#: aleksis/core/models.py:207 aleksis/core/models.py:497
-#: aleksis/core/models.py:1447
+#: aleksis/core/models.py:223 aleksis/core/models.py:513
+#: aleksis/core/models.py:1478
 msgid "Short name"
 msgstr "Коротке ім'я"
 
-#: aleksis/core/models.py:212
+#: aleksis/core/models.py:228
 msgid "Street"
 msgstr "Вулиця"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:229
 msgid "Street number"
 msgstr "Номер будинку"
 
-#: aleksis/core/models.py:214
+#: aleksis/core/models.py:230
 msgid "Postal code"
 msgstr "Поштовий індекс"
 
-#: aleksis/core/models.py:215
+#: aleksis/core/models.py:231
 msgid "Place"
 msgstr "Місто"
 
-#: aleksis/core/models.py:217
+#: aleksis/core/models.py:233
 msgid "Home phone"
 msgstr "Домашній телефон"
 
-#: aleksis/core/models.py:218
+#: aleksis/core/models.py:234
 msgid "Mobile phone"
 msgstr "Мобільний телефон"
 
-#: aleksis/core/models.py:222
+#: aleksis/core/models.py:238
 msgid "Date of birth"
 msgstr "Дата народження"
 
-#: aleksis/core/models.py:223
+#: aleksis/core/models.py:239
 msgid "Place of birth"
 msgstr "Місце народження"
 
-#: aleksis/core/models.py:224
+#: aleksis/core/models.py:240
 msgid "Sex"
 msgstr "Стать"
 
-#: aleksis/core/models.py:231 aleksis/core/models.py:536
+#: aleksis/core/models.py:247 aleksis/core/models.py:552
 msgid "This is an official photo, used for official documents and for internal use cases."
 msgstr "Це офіційне фото, яке використовується для документів та внутрішніх потреб."
 
-#: aleksis/core/models.py:236 aleksis/core/models.py:540
+#: aleksis/core/models.py:252 aleksis/core/models.py:556
 msgid "Display picture / Avatar"
 msgstr "Відобразити фото/аватар"
 
-#: aleksis/core/models.py:239 aleksis/core/models.py:543
+#: aleksis/core/models.py:255 aleksis/core/models.py:559
 msgid "This is a picture or an avatar for public display."
 msgstr "Це фото або аватар для загального відображення."
 
-#: aleksis/core/models.py:244
+#: aleksis/core/models.py:260
 msgid "Guardians / Parents"
 msgstr "Опікуни / батьки"
 
-#: aleksis/core/models.py:251
+#: aleksis/core/models.py:267
 msgid "Primary group"
 msgstr "Первинна група"
 
-#: aleksis/core/models.py:254 aleksis/core/models.py:676
-#: aleksis/core/models.py:700 aleksis/core/models.py:795
-#: aleksis/core/models.py:1070 aleksis/core/models.py:1820
+#: aleksis/core/models.py:270 aleksis/core/models.py:692
+#: aleksis/core/models.py:716 aleksis/core/models.py:827
+#: aleksis/core/models.py:1102 aleksis/core/models.py:1878
 msgid "Description"
 msgstr "Опис"
 
-#: aleksis/core/models.py:481
+#: aleksis/core/models.py:497
 msgid "Can assign child groups to groups"
 msgstr "Може призначати підлеглі групи до груп"
 
-#: aleksis/core/models.py:482
+#: aleksis/core/models.py:498
 msgid "Can view statistics about group."
 msgstr "Може бачити статистику групи."
 
-#: aleksis/core/models.py:495 aleksis/core/models.py:1448
+#: aleksis/core/models.py:511 aleksis/core/models.py:1479
 msgid "Long name"
 msgstr "Повна назва"
 
-#: aleksis/core/models.py:508
+#: aleksis/core/models.py:524
 msgid "Members"
 msgstr "Учасники"
 
-#: aleksis/core/models.py:511
+#: aleksis/core/models.py:527
 msgid "Owners"
 msgstr "Власники"
 
-#: aleksis/core/models.py:518
+#: aleksis/core/models.py:534
 msgid "Parent groups"
 msgstr "Батьківські групи"
 
-#: aleksis/core/models.py:526
+#: aleksis/core/models.py:542
 msgid "Type of group"
 msgstr "Тип групи"
 
-#: aleksis/core/models.py:675 aleksis/core/models.py:699
-#: aleksis/core/models.py:794 aleksis/core/models.py:1272
-#: aleksis/core/models.py:1819
+#: aleksis/core/models.py:691 aleksis/core/models.py:715
+#: aleksis/core/models.py:826 aleksis/core/models.py:1304
+#: aleksis/core/models.py:1877
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Назва"
 
-#: aleksis/core/models.py:678
+#: aleksis/core/models.py:694
 msgid "Application"
 msgstr "Додаток"
 
-#: aleksis/core/models.py:684
+#: aleksis/core/models.py:700
 msgid "Activity"
 msgstr "Активність"
 
-#: aleksis/core/models.py:685
+#: aleksis/core/models.py:701
 msgid "Activities"
 msgstr "Активності"
 
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:707
 msgid "Sender"
 msgstr "Відправник"
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:712
 msgid "Recipient"
 msgstr "Отримувач"
 
-#: aleksis/core/models.py:701 aleksis/core/models.py:1037
+#: aleksis/core/models.py:717 aleksis/core/models.py:1069
 msgid "Link"
 msgstr "Посилання"
 
-#: aleksis/core/models.py:704 aleksis/core/models.py:1038
-#: aleksis/core/models.py:1405
+#: aleksis/core/models.py:720 aleksis/core/models.py:1070
+#: aleksis/core/models.py:1436
 #: aleksis/core/templates/oauth2_provider/application/detail.html:26
 msgid "Icon"
 msgstr "Піктограма"
 
-#: aleksis/core/models.py:707
+#: aleksis/core/models.py:723
 msgid "Send notification at"
 msgstr "Надіслати сповіщення о"
 
-#: aleksis/core/models.py:709
+#: aleksis/core/models.py:725
 msgid "Read"
 msgstr "Читати"
 
-#: aleksis/core/models.py:710
+#: aleksis/core/models.py:726
 msgid "Sent"
 msgstr "Надіслано"
 
-#: aleksis/core/models.py:727
+#: aleksis/core/models.py:731 aleksis/core/models.py:2130
+#, fuzzy
+#| msgid "Calendar"
+msgid "Calendar alarm"
+msgstr "Календар"
+
+#: aleksis/core/models.py:752
 msgid "Notification"
 msgstr "Сповіщення"
 
-#: aleksis/core/models.py:728 aleksis/core/preferences.py:29
+#: aleksis/core/models.py:753 aleksis/core/preferences.py:29
 msgid "Notifications"
 msgstr "Сповіщення"
 
-#: aleksis/core/models.py:796
+#: aleksis/core/models.py:828
 msgid "Link to detailed view"
 msgstr "Посилання на детальний перегляд"
 
-#: aleksis/core/models.py:797
+#: aleksis/core/models.py:829
 msgid "Priority"
 msgstr ""
 
-#: aleksis/core/models.py:800
+#: aleksis/core/models.py:832
 msgid "Date and time from when to show"
 msgstr "Дата і час, з якого показувати"
 
-#: aleksis/core/models.py:803
+#: aleksis/core/models.py:835
 msgid "Date and time until when to show"
 msgstr "Дата і час, до якого показувати"
 
-#: aleksis/core/models.py:828
+#: aleksis/core/models.py:860
 msgid "Announcement"
 msgstr "Оголошення"
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:861
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Оголошення"
 
-#: aleksis/core/models.py:872
+#: aleksis/core/models.py:904
 msgid "Announcement recipient"
 msgstr "Отримувач оголошення"
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:905
 msgid "Announcement recipients"
 msgstr "Отримувачі оголошення"
 
-#: aleksis/core/models.py:893
+#: aleksis/core/models.py:925
 msgid "Widget Title"
 msgstr "Назва віджета"
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:926
 msgid "Activate Widget"
 msgstr "Активувати віджет"
 
-#: aleksis/core/models.py:895
+#: aleksis/core/models.py:927
 msgid "Widget is broken"
 msgstr "Віджет зламався"
 
-#: aleksis/core/models.py:898
+#: aleksis/core/models.py:930
 msgid "Size on mobile devices"
 msgstr "Розмір на мобільних"
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:931
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:904
+#: aleksis/core/models.py:936
 msgid "Size on tablet devices"
 msgstr "Розмір на планшетах"
 
-#: aleksis/core/models.py:905
+#: aleksis/core/models.py:937
 msgid "> 600 px, 12 columns"
 msgstr "> 600 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:910
+#: aleksis/core/models.py:942
 msgid "Size on desktop devices"
 msgstr "Розмір на ПК"
 
-#: aleksis/core/models.py:911
+#: aleksis/core/models.py:943
 msgid "> 992 px, 12 columns"
 msgstr "> 992 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:948
 msgid "Size on large desktop devices"
 msgstr "Розмір на великих екранах"
 
-#: aleksis/core/models.py:917
+#: aleksis/core/models.py:949
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 пікс, 12 стовпчиків"
 
-#: aleksis/core/models.py:948
+#: aleksis/core/models.py:980
 msgid "Can edit default dashboard"
 msgstr "Може редагувати типову/стандартну інформпанель"
 
-#: aleksis/core/models.py:949
+#: aleksis/core/models.py:981
 msgid "Dashboard Widget"
 msgstr "Віджет інформпанелі"
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:982
 msgid "Dashboard Widgets"
 msgstr "Віджети інформпанелі"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:988
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:989
 msgid "Icon URL"
 msgstr "Піктограма URL"
 
-#: aleksis/core/models.py:963
+#: aleksis/core/models.py:995
 msgid "External link widget"
 msgstr "Зовнішнє посилання на віджет"
 
-#: aleksis/core/models.py:964
+#: aleksis/core/models.py:996
 msgid "External link widgets"
 msgstr "Зовнішні посилання на віджети"
 
-#: aleksis/core/models.py:970
+#: aleksis/core/models.py:1002
 msgid "Content"
 msgstr "Зміст"
 
-#: aleksis/core/models.py:976
+#: aleksis/core/models.py:1008
 msgid "Static content widget"
 msgstr "Віджет з постійним змістом"
 
-#: aleksis/core/models.py:977
+#: aleksis/core/models.py:1009
 msgid "Static content widgets"
 msgstr "Віджети з постійним змістом"
 
-#: aleksis/core/models.py:982
+#: aleksis/core/models.py:1014
 msgid "Dashboard widget"
 msgstr "Віджет інформпанелі"
 
-#: aleksis/core/models.py:987
+#: aleksis/core/models.py:1019
 msgid "Order"
 msgstr "Порядок"
 
-#: aleksis/core/models.py:988
+#: aleksis/core/models.py:1020
 msgid "Part of the default dashboard"
 msgstr "Частина типової інформпанелі"
 
-#: aleksis/core/models.py:1003
+#: aleksis/core/models.py:1035
 msgid "Dashboard widget order"
 msgstr "Порядок віджету на інформпанелі"
 
-#: aleksis/core/models.py:1004
+#: aleksis/core/models.py:1036
 msgid "Dashboard widget orders"
 msgstr "Порядок віджетів на інформпанелі"
 
-#: aleksis/core/models.py:1010
+#: aleksis/core/models.py:1042
 msgid "Menu ID"
 msgstr "Меню ID"
 
-#: aleksis/core/models.py:1023
+#: aleksis/core/models.py:1055
 msgid "Custom menu"
 msgstr "Користувацьке меню"
 
-#: aleksis/core/models.py:1024
+#: aleksis/core/models.py:1056
 msgid "Custom menus"
 msgstr "Користувацькі меню"
 
-#: aleksis/core/models.py:1034
+#: aleksis/core/models.py:1066
 msgid "Menu"
 msgstr "Меню"
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1076
 msgid "Custom menu item"
 msgstr "Пункт користувацького меню"
 
-#: aleksis/core/models.py:1045
+#: aleksis/core/models.py:1077
 msgid "Custom menu items"
 msgstr "Пункти користувацького меню"
 
-#: aleksis/core/models.py:1069
+#: aleksis/core/models.py:1101
 msgid "Title of type"
 msgstr "Назва типу"
 
-#: aleksis/core/models.py:1073
+#: aleksis/core/models.py:1105
 msgid "Owners of groups with this group type can see the groups"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1108
 msgid "Owners of groups with this group type can see group members"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1116
 #, fuzzy
 #| msgid "Personal events"
 msgid "Personal details"
 msgstr "Особисті події"
 
-#: aleksis/core/models.py:1086
+#: aleksis/core/models.py:1118
 msgid "Contact details"
 msgstr "Контактні дані"
 
-#: aleksis/core/models.py:1088
+#: aleksis/core/models.py:1120
 #: aleksis/core/templates/core/partials/avatar_content.html:14
 #: aleksis/core/templates/core/partials/avatar_content.html:15
 msgid "Avatar"
 msgstr "Аватар"
 
-#: aleksis/core/models.py:1093
+#: aleksis/core/models.py:1125
 msgid "Information owners of groups with this group type can see of the group's members"
 msgstr ""
 
-#: aleksis/core/models.py:1103
+#: aleksis/core/models.py:1135
 msgid "Group type"
 msgstr "Тип групи"
 
-#: aleksis/core/models.py:1104
+#: aleksis/core/models.py:1136
 msgid "Group types"
 msgstr "Типи груп"
 
-#: aleksis/core/models.py:1115
+#: aleksis/core/models.py:1147
 msgid "Can view system status"
 msgstr "Може бачити стан системи"
 
-#: aleksis/core/models.py:1116
+#: aleksis/core/models.py:1148
 msgid "Can manage data"
 msgstr "Може керувати даними"
 
-#: aleksis/core/models.py:1117
+#: aleksis/core/models.py:1149
 msgid "Can impersonate"
 msgstr "Може маскуватися"
 
-#: aleksis/core/models.py:1118
+#: aleksis/core/models.py:1150
 msgid "Can use search"
 msgstr "Може шукати"
 
-#: aleksis/core/models.py:1119
+#: aleksis/core/models.py:1151
 msgid "Can change site preferences"
 msgstr "Може змінювати властивості сайту"
 
-#: aleksis/core/models.py:1120
+#: aleksis/core/models.py:1152
 msgid "Can change person preferences"
 msgstr "Може змінювати властивості особи"
 
-#: aleksis/core/models.py:1121
+#: aleksis/core/models.py:1153
 msgid "Can change group preferences"
 msgstr "Може змінювати властивості групи"
 
-#: aleksis/core/models.py:1122
+#: aleksis/core/models.py:1154
 msgid "Can test PDF generation"
 msgstr "Може генерувати тестові PDF"
 
-#: aleksis/core/models.py:1123
+#: aleksis/core/models.py:1155
 msgid "Can invite persons"
 msgstr "Може запрошувати осіб"
 
-#: aleksis/core/models.py:1124
+#: aleksis/core/models.py:1156
 msgid "Can view birthday calendar"
 msgstr "Може бачити календар днів народження"
 
-#: aleksis/core/models.py:1151
+#: aleksis/core/models.py:1183
 msgid "Related data check task"
 msgstr "Завдання перевірки пов'язаних даних"
 
-#: aleksis/core/models.py:1159
+#: aleksis/core/models.py:1191
 msgid "Issue solved"
 msgstr "Проблема вирішена"
 
-#: aleksis/core/models.py:1160
+#: aleksis/core/models.py:1192
 msgid "Notification sent"
 msgstr "Сповіщення надіслане"
 
-#: aleksis/core/models.py:1173
+#: aleksis/core/models.py:1205
 msgid "Data check result"
 msgstr "Результат перевірки даних"
 
-#: aleksis/core/models.py:1174
+#: aleksis/core/models.py:1206
 msgid "Data check results"
 msgstr "Результати перевірки даних"
 
-#: aleksis/core/models.py:1176
+#: aleksis/core/models.py:1208
 msgid "Can run data checks"
 msgstr "Може запускати перевірки даних"
 
-#: aleksis/core/models.py:1177
+#: aleksis/core/models.py:1209
 msgid "Can solve data check problems"
 msgstr "Може розв'язувати проблеми перевірки даних"
 
-#: aleksis/core/models.py:1184
+#: aleksis/core/models.py:1216
 msgid "E-Mail address"
 msgstr "Адреса е-пошти"
 
-#: aleksis/core/models.py:1243 aleksis/core/models.py:1827
+#: aleksis/core/models.py:1275 aleksis/core/models.py:1885
 msgid "Owner"
 msgstr "Власник"
 
-#: aleksis/core/models.py:1247
+#: aleksis/core/models.py:1279
 msgid "File expires at"
 msgstr "Файл дійсний до"
 
-#: aleksis/core/models.py:1250
+#: aleksis/core/models.py:1282
 msgid "Generated HTML file"
 msgstr "Згенерований файл HTML"
 
-#: aleksis/core/models.py:1253
+#: aleksis/core/models.py:1285
 msgid "Generated PDF file"
 msgstr "Згенерований файл PDF"
 
-#: aleksis/core/models.py:1260
+#: aleksis/core/models.py:1292
 msgid "PDF file"
 msgstr "Файл PDF"
 
-#: aleksis/core/models.py:1261
+#: aleksis/core/models.py:1293
 msgid "PDF files"
 msgstr "Файли PDF"
 
-#: aleksis/core/models.py:1266
+#: aleksis/core/models.py:1298
 msgid "Task result"
 msgstr "Результат завдання"
 
-#: aleksis/core/models.py:1269
+#: aleksis/core/models.py:1301
 msgid "Task user"
 msgstr "Користувач завдання"
 
-#: aleksis/core/models.py:1273
+#: aleksis/core/models.py:1305
 msgid "Back URL"
 msgstr "URL для повернення"
 
-#: aleksis/core/models.py:1274
+#: aleksis/core/models.py:1306
 msgid "Progress title"
 msgstr "Назва процесу"
 
-#: aleksis/core/models.py:1275
+#: aleksis/core/models.py:1307
 msgid "Error message"
 msgstr "Повідомлення про помилку"
 
-#: aleksis/core/models.py:1276
+#: aleksis/core/models.py:1308
 msgid "Success message"
 msgstr "Повідомлення про успіх"
 
-#: aleksis/core/models.py:1277
+#: aleksis/core/models.py:1309
 msgid "Redirect on success URL"
 msgstr "Посилання для перенаправлення в разі успіху"
 
-#: aleksis/core/models.py:1279
+#: aleksis/core/models.py:1311
 msgid "Additional button title"
 msgstr "Назва додаткової кнопки"
 
-#: aleksis/core/models.py:1281
+#: aleksis/core/models.py:1313
 msgid "Additional button URL"
 msgstr "Посилання додаткової кнопки"
 
-#: aleksis/core/models.py:1283
+#: aleksis/core/models.py:1315
 msgid "Additional button icon"
 msgstr "Піктограма додаткової кнопки"
 
-#: aleksis/core/models.py:1285
+#: aleksis/core/models.py:1317
 msgid "Result fetched"
 msgstr "Отриманий результат"
 
-#: aleksis/core/models.py:1310
+#: aleksis/core/models.py:1341
 msgid "Background task completed successfully"
 msgstr "Фонове завдання успішно завершене"
 
-#: aleksis/core/models.py:1311
+#: aleksis/core/models.py:1342
 msgid "The background task '{}' has been completed successfully."
 msgstr "Фонове завдання '{}' було успішно завершене."
 
-#: aleksis/core/models.py:1317
+#: aleksis/core/models.py:1348
 msgid "Background task failed"
 msgstr "Збій фонового завдання"
 
-#: aleksis/core/models.py:1318
+#: aleksis/core/models.py:1349
 msgid "The background task '{}' has failed."
 msgstr "У фонового завдання '{}' стався збій."
 
-#: aleksis/core/models.py:1327
+#: aleksis/core/models.py:1358
 msgid "Background task"
 msgstr "Фонове завдання"
 
-#: aleksis/core/models.py:1341
+#: aleksis/core/models.py:1372
 msgid "Task user assignment"
 msgstr "Призначення користувача завдання"
 
-#: aleksis/core/models.py:1342
+#: aleksis/core/models.py:1373
 msgid "Task user assignments"
 msgstr "Призначення користувачів завдання"
 
-#: aleksis/core/models.py:1358
+#: aleksis/core/models.py:1389
 msgid "Additional attributes"
 msgstr "Додаткові атрибути"
 
-#: aleksis/core/models.py:1399
+#: aleksis/core/models.py:1430
 msgid "Allowed scopes that clients can request"
 msgstr "Дозволені межі дії, які можуть запитувати клієнти"
 
-#: aleksis/core/models.py:1409
+#: aleksis/core/models.py:1440
 msgid "This image will be shown as icon in the authorization flow. It should be squared."
 msgstr "Це зображення буде іконкою під час авторизації. Воно повинне бути квадратним."
 
-#: aleksis/core/models.py:1459
+#: aleksis/core/models.py:1490
 msgid "Can view room timetable"
 msgstr "Може бачити розклад кімнати"
 
-#: aleksis/core/models.py:1461
+#: aleksis/core/models.py:1492
 msgid "Room"
 msgstr "Кімната"
 
-#: aleksis/core/models.py:1462
+#: aleksis/core/models.py:1493
 msgid "Rooms"
 msgstr "Кімнати"
 
-#: aleksis/core/models.py:1487
+#: aleksis/core/models.py:1518
 msgid "Start date and time"
 msgstr "Дата і час початку"
 
-#: aleksis/core/models.py:1489
+#: aleksis/core/models.py:1520
 msgid "End date and time"
 msgstr "Дата і час закінчення"
 
-#: aleksis/core/models.py:1490
+#: aleksis/core/models.py:1521
 msgid "Timezone"
 msgstr "Часовий пояс"
 
-#: aleksis/core/models.py:1493
+#: aleksis/core/models.py:1525
 msgid "Recurrences"
 msgstr "Повтори"
 
-#: aleksis/core/models.py:1499
+#: aleksis/core/models.py:1533
 msgid "Amended base event"
 msgstr "Виправлена основна подія"
 
-#: aleksis/core/models.py:1651
+#: aleksis/core/models.py:1701
 msgid "Calendar Event"
 msgstr "Календарна подія"
 
-#: aleksis/core/models.py:1652
+#: aleksis/core/models.py:1702
 msgid "Calendar Events"
 msgstr "Календарні події"
 
-#: aleksis/core/models.py:1682
+#: aleksis/core/models.py:1742
 msgid "Birthdays"
 msgstr "Дні Народження"
 
-#: aleksis/core/models.py:1687
+#: aleksis/core/models.py:1747
 msgid "{}'s birthday"
 msgstr "{} святкує День Народження"
 
-#: aleksis/core/models.py:1742 aleksis/core/models.py:1811
+#: aleksis/core/models.py:1800 aleksis/core/models.py:1869
 msgid "Holidays"
 msgstr "Вихідні"
 
-#: aleksis/core/models.py:1810
+#: aleksis/core/models.py:1868
 msgid "Holiday"
 msgstr "Вихідний"
 
-#: aleksis/core/models.py:1812
+#: aleksis/core/models.py:1870
 msgid "Can view holiday calendar"
 msgstr "Може бачити календар зі святами"
 
-#: aleksis/core/models.py:1817
+#: aleksis/core/models.py:1875
 msgid "Personal events"
 msgstr "Особисті події"
 
-#: aleksis/core/models.py:1821
+#: aleksis/core/models.py:1879
 msgid "Location"
 msgstr "Нас.пункт"
 
-#: aleksis/core/models.py:1908
+#: aleksis/core/models.py:1971 aleksis/core/models.py:2005
 msgid "Email"
 msgstr "Е-пошта"
 
-#: aleksis/core/models.py:1911
+#: aleksis/core/models.py:1974
 msgid "Related group"
 msgstr "Пов'язана група"
 
-#: aleksis/core/models.py:1918
+#: aleksis/core/models.py:1981
 msgid "Organisation"
 msgstr "Організація"
 
-#: aleksis/core/models.py:1919
+#: aleksis/core/models.py:1982
 msgid "Organisations"
 msgstr "Організації"
 
+#: aleksis/core/models.py:2003
+msgid "Audio"
+msgstr ""
+
+#: aleksis/core/models.py:2004
+msgid "Display"
+msgstr ""
+
+#: aleksis/core/models.py:2006
+msgid "Procedure"
+msgstr ""
+
+#: aleksis/core/models.py:2010
+msgid "Event"
+msgstr ""
+
+#: aleksis/core/models.py:2014
+#, fuzzy
+#| msgid "Actions"
+msgid "Action"
+msgstr "Дії"
+
+#: aleksis/core/models.py:2017
+#, fuzzy
+#| msgid "Send notification at"
+msgid "Send notifications"
+msgstr "Надіслати сповіщення о"
+
+#: aleksis/core/models.py:2131
+#, fuzzy
+#| msgid "Calendar"
+msgid "Calendar alarms"
+msgstr "Календар"
+
+#: aleksis/core/models.py:2140
+#, fuzzy
+#| msgid "Personal events"
+msgid "Personal event alarm"
+msgstr "Особисті події"
+
+#: aleksis/core/models.py:2141
+#, fuzzy
+#| msgid "Personal events"
+msgid "Personal event alarms"
+msgstr "Особисті події"
+
 #: aleksis/core/preferences.py:25
 msgid "General"
 msgstr "Загальне"
@@ -1258,46 +1300,84 @@ msgstr "Автоматично оновлювати інформпанель т
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr "Автоматично оновлювати інформпанель та її віджети (для всього сайту)"
 
-#: aleksis/core/preferences.py:491
+#: aleksis/core/preferences.py:491 aleksis/core/preferences.py:513
+msgid "First day that appears in the calendar"
+msgstr ""
+
+#: aleksis/core/preferences.py:493 aleksis/core/preferences.py:516
+msgid "Monday"
+msgstr ""
+
+#: aleksis/core/preferences.py:494 aleksis/core/preferences.py:517
+msgid "Tuesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:495 aleksis/core/preferences.py:518
+msgid "Wednesday"
+msgstr ""
+
+#: aleksis/core/preferences.py:496 aleksis/core/preferences.py:519
+msgid "Thursday"
+msgstr ""
+
+#: aleksis/core/preferences.py:497 aleksis/core/preferences.py:520
+#, fuzzy
+#| msgid "Holiday"
+msgid "Friday"
+msgstr "Вихідний"
+
+#: aleksis/core/preferences.py:498 aleksis/core/preferences.py:521
+msgid "Saturday"
+msgstr ""
+
+#: aleksis/core/preferences.py:499 aleksis/core/preferences.py:522
+msgid "Sunday"
+msgstr ""
+
+#: aleksis/core/preferences.py:515
+msgid "Use page default"
+msgstr ""
+
+#: aleksis/core/preferences.py:534
 msgid "Birthday calendar feed color"
 msgstr "Колір календаря Днів Народження"
 
-#: aleksis/core/preferences.py:503
+#: aleksis/core/preferences.py:546
 msgid "Holiday calendar feed color"
 msgstr "Колір календаря Вихідних"
 
-#: aleksis/core/preferences.py:515
+#: aleksis/core/preferences.py:558
 msgid "Personal events feed color"
 msgstr "Колір стрічки з особистими подіями"
 
-#: aleksis/core/preferences.py:528
+#: aleksis/core/preferences.py:571
 msgid "Activated calendars"
 msgstr "Активні календарі"
 
-#: aleksis/core/preferences.py:546
+#: aleksis/core/preferences.py:589
 msgid "Comma-separated list of disallowed usernames"
 msgstr "Заборонені імена користувачів через кому"
 
-#: aleksis/core/settings.py:550
+#: aleksis/core/settings.py:552
 msgid "English"
 msgstr "Англійська"
 
-#: aleksis/core/settings.py:551
+#: aleksis/core/settings.py:553
 msgid "German"
 msgstr "Німецька"
 
-#: aleksis/core/settings.py:552
+#: aleksis/core/settings.py:554
 msgid "Ukrainian"
 msgstr "Українська"
 
-#: aleksis/core/tables.py:96 aleksis/core/tables.py:133
+#: aleksis/core/tables.py:23 aleksis/core/tables.py:60
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/pages/delete.html:22
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr "Видалити"
 
-#: aleksis/core/tables.py:98 aleksis/core/tables.py:135
+#: aleksis/core/tables.py:25 aleksis/core/tables.py:62
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr "Дії"
@@ -1815,22 +1895,6 @@ msgstr "Типова інформпанель"
 msgid "Edit group"
 msgstr "Редагувати групу"
 
-#: aleksis/core/templates/core/group/list.html:14
-msgid "Create group"
-msgstr "Створити групу"
-
-#: aleksis/core/templates/core/group/list.html:17
-msgid "Filter groups"
-msgstr "Фільтрувати групи"
-
-#: aleksis/core/templates/core/group/list.html:24
-msgid "Clear"
-msgstr "Очистити"
-
-#: aleksis/core/templates/core/group/list.html:28
-msgid "Selected groups"
-msgstr "Вибрані групи"
-
 #: aleksis/core/templates/core/index.html:4
 msgid "Home"
 msgstr "Додому"
@@ -2326,6 +2390,11 @@ msgstr ""
 "      Якщо інтернет працює і помилка все одно присутня, зверніться до системних адміністраторів:\n"
 "    "
 
+#: aleksis/core/templates/search/search.html:7
+#: aleksis/core/templates/search/search.html:22
+msgid "Search"
+msgstr "Пошук"
+
 #: aleksis/core/templates/search/search.html:8
 msgid "Global Search"
 msgstr "Глобальний пошук"
@@ -2994,11 +3063,11 @@ msgstr "Вимкнути"
 msgid "This username is not allowed."
 msgstr "Цей логін заборонений."
 
-#: aleksis/core/util/notifications.py:67
+#: aleksis/core/util/notifications.py:68
 msgid "E-Mail"
 msgstr "Е-пошта"
 
-#: aleksis/core/util/notifications.py:68
+#: aleksis/core/util/notifications.py:69
 msgid "SMS"
 msgstr "SMS"
 
@@ -3022,132 +3091,144 @@ msgstr "Під час створення файлу PDF виникла проб
 msgid "Download PDF"
 msgstr "Звантажити PDF"
 
-#: aleksis/core/views.py:303 aleksis/core/views.py:313
+#: aleksis/core/views.py:255 aleksis/core/views.py:265
 msgid "The person has been saved."
 msgstr "Особа збережена."
 
-#: aleksis/core/views.py:362
+#: aleksis/core/views.py:314
 msgid "The group has been saved."
 msgstr "Група збережена."
 
-#: aleksis/core/views.py:410
+#: aleksis/core/views.py:362
 msgid "Maintenance mode was turned on successfully."
 msgstr "Режим обслуговування увімкнений успішно."
 
-#: aleksis/core/views.py:412
+#: aleksis/core/views.py:364
 msgid "Maintenance mode was turned off successfully."
 msgstr "Режим обслуговування успішно вимкнений."
 
-#: aleksis/core/views.py:469
+#: aleksis/core/views.py:421
 msgid "The announcement has been saved."
 msgstr "Оголошення збережене."
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:437
 msgid "The announcement has been deleted."
 msgstr "Оголошення видалене."
 
-#: aleksis/core/views.py:554
+#: aleksis/core/views.py:506
 msgid "The requested preference registry does not exist"
 msgstr "Журналу із запитаними властивостями не існує"
 
-#: aleksis/core/views.py:573
+#: aleksis/core/views.py:525
 msgid "The preferences have been saved successfully."
 msgstr "Властивості збережені."
 
-#: aleksis/core/views.py:596
+#: aleksis/core/views.py:548
 msgid "The group has been deleted."
 msgstr "Група видалена."
 
-#: aleksis/core/views.py:631
+#: aleksis/core/views.py:583
 msgid "Progress: Run data checks"
 msgstr "Перебіг: Запуск перевірки даних"
 
-#: aleksis/core/views.py:632
+#: aleksis/core/views.py:584
 msgid "Run data checks …"
 msgstr "Запускається перевірка даних …"
 
-#: aleksis/core/views.py:633
+#: aleksis/core/views.py:585
 msgid "The data checks were run successfully."
 msgstr "Перевірка даних успішно запущена."
 
-#: aleksis/core/views.py:634
+#: aleksis/core/views.py:586
 msgid "There was a problem while running data checks."
 msgstr "Під час запуску перевірки даних виникла проблема."
 
-#: aleksis/core/views.py:651
+#: aleksis/core/views.py:603
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr "Варіант розв'язання \"{solve_option_obj.verbose_name}\" "
 
-#: aleksis/core/views.py:661
+#: aleksis/core/views.py:613
 msgid "The requested solve option does not exist"
 msgstr "Запитаний варіант розв'язання не існує"
 
-#: aleksis/core/views.py:694
+#: aleksis/core/views.py:646
 msgid "The dashboard widget has been saved."
 msgstr "Віджет інформпанелі збережений."
 
-#: aleksis/core/views.py:724
+#: aleksis/core/views.py:676
 msgid "The dashboard widget has been created."
 msgstr "Віджет інформпанелі створений."
 
-#: aleksis/core/views.py:734
+#: aleksis/core/views.py:686
 msgid "The dashboard widget has been deleted."
 msgstr "Віджет інформпанелі видалений."
 
-#: aleksis/core/views.py:806
+#: aleksis/core/views.py:758
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ваша конфігурація інформпанелі збережена."
 
-#: aleksis/core/views.py:808
+#: aleksis/core/views.py:760
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr "Конфігурація типової/стандартної інформпанелі збережена."
 
-#: aleksis/core/views.py:879
+#: aleksis/core/views.py:831
 #, python-brace-format
 msgid "The invitation was successfully created. The invitation code is {code}"
 msgstr "Запрошення успішно створене. Код запрошення: {code}"
 
-#: aleksis/core/views.py:976
+#: aleksis/core/views.py:928
 msgid "We have successfully assigned the permissions."
 msgstr "Ми успішно призначили дозволи."
 
-#: aleksis/core/views.py:986
+#: aleksis/core/views.py:938
 msgid "The global user permission has been deleted."
 msgstr "Глобальний користувацький дозвіл видалений."
 
-#: aleksis/core/views.py:996
+#: aleksis/core/views.py:948
 msgid "The global group permission has been deleted."
 msgstr "Глобальний груповий дозвіл видалений."
 
-#: aleksis/core/views.py:1006
+#: aleksis/core/views.py:958
 msgid "The object user permission has been deleted."
 msgstr "Об'єктний користувацький дозвіл видалений."
 
-#: aleksis/core/views.py:1016
+#: aleksis/core/views.py:968
 msgid "The object group permission has been deleted."
 msgstr "Об'єктний груповий дозвіл видалений."
 
-#: aleksis/core/views.py:1098
+#: aleksis/core/views.py:1050
 msgid "We sent you an email with instructions for resetting your password."
 msgstr "Ми надіслали Вам е-листа з настановами щодо відновлення пароля."
 
-#: aleksis/core/views.py:1122
+#: aleksis/core/views.py:1074
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr "Обліковий запис третьої сторони не можна від'єднати оскільки він єдиний спосіб для входу."
 
-#: aleksis/core/views.py:1129
+#: aleksis/core/views.py:1081
 msgid "The third-party account has been successfully disconnected."
 msgstr "Обліковий запис третьої сторони успішно від'єднаний."
 
-#: aleksis/core/views.py:1205
+#: aleksis/core/views.py:1157
 msgid "Person was invited successfully and an email with further instructions has been send to them."
 msgstr "Особа успішно запрошена. Лист з інструкціями щодо наступних дій надісланий на її ел.пошту."
 
-#: aleksis/core/views.py:1216
+#: aleksis/core/views.py:1168
 msgid "Person was already invited."
 msgstr "Особа вже була запрошена."
 
+#~ msgid "Create group"
+#~ msgstr "Створити групу"
+
+#~ msgid "Filter groups"
+#~ msgstr "Фільтрувати групи"
+
+#~ msgid "Clear"
+#~ msgstr "Очистити"
+
+#~ msgid "Selected groups"
+#~ msgstr "Вибрані групи"
+
 #~ msgid "Assign child groups to groups"
 #~ msgstr "Призначити підлеглі групи до груп"
 
diff --git a/aleksis/core/migrations/0069_add_calendar_alarm.py b/aleksis/core/migrations/0069_add_calendar_alarm.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ecbdbe511af95fa2244e06986131b876d0e00f1
--- /dev/null
+++ b/aleksis/core/migrations/0069_add_calendar_alarm.py
@@ -0,0 +1,53 @@
+# Generated by Django 5.0.6 on 2024-07-12 11:34
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('core', '0068_calendar_event_amends_unique_constraints'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='CalendarAlarm',
+            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)),
+                ('action', models.CharField(choices=[('audio', 'Audio'), ('display', 'Display'), ('email', 'Email'), ('procedure', 'Procedure')], default='display', max_length=10, verbose_name='Action')),
+                ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='alarms', to='core.calendarevent', verbose_name='Event')),
+                ('send_notifications', models.BooleanField(default=False, verbose_name='Send notifications')),
+                ('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')),
+            ],
+            options={
+                'verbose_name': 'Calendar alarm',
+                'verbose_name_plural': 'Calendar alarms',
+            },
+        ),
+        migrations.CreateModel(
+            name='PersonalEventAlarm',
+            fields=[
+                ('calendaralarm_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.calendaralarm')),
+            ],
+            options={
+                'verbose_name': 'Personal event alarm',
+                'verbose_name_plural': 'Personal event alarms',
+            },
+            bases=('core.calendaralarm',),
+        ),
+        migrations.AddField(
+            model_name='notification',
+            name='calendar_alarm',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='core.calendaralarm', verbose_name='Calendar alarm'),
+        ),
+        migrations.AddConstraint(
+            model_name='notification',
+            constraint=models.UniqueConstraint(condition=models.Q(('calendar_alarm__isnull', False)), fields=('calendar_alarm', 'recipient'), name='unique_recipient_per_calendar_alarm'),
+        ),
+    ]
diff --git a/aleksis/core/migrations/0070_oauth_token_checksum.py b/aleksis/core/migrations/0070_oauth_token_checksum.py
new file mode 100644
index 0000000000000000000000000000000000000000..b091d273be8f03b3b72d39defe0f950412871364
--- /dev/null
+++ b/aleksis/core/migrations/0070_oauth_token_checksum.py
@@ -0,0 +1,48 @@
+# Generated by Django 5.1.5 on 2025-01-17 15:18
+
+import django.db.models.deletion
+import oauth2_provider.models
+from django.conf import settings
+from django.db import migrations, models
+from oauth2_provider.settings import oauth2_settings
+
+def forwards_func(apps, schema_editor):
+    """
+    Forward migration touches every "old" accesstoken.token which will cause the checksum to be computed.
+    """
+    AccessToken = apps.get_model(oauth2_settings.ACCESS_TOKEN_MODEL)
+    accesstokens = AccessToken._default_manager.iterator()
+    for accesstoken in accesstokens:
+        accesstoken.save(update_fields=['token_checksum'])
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0069_add_calendar_alarm'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='oauthaccesstoken',
+            name='token_checksum',
+            field=oauth2_provider.models.TokenChecksumField(db_index=True, default='', max_length=64, unique=True, blank=True),
+            preserve_default=False,
+        ),
+        migrations.RunPython(forwards_func, migrations.RunPython.noop),
+        migrations.AlterField(
+            model_name='oauthaccesstoken',
+            name='token_checksum',
+            field=oauth2_provider.models.TokenChecksumField(blank=False, max_length=64,  db_index=True, unique=True),
+        ),
+        migrations.AddField(
+            model_name='oauthrefreshtoken',
+            name='token_family',
+            field=models.UUIDField(blank=True, editable=False, null=True),
+        ),
+        migrations.AlterField(
+            model_name='oauthaccesstoken',
+            name='token',
+            field=models.TextField(),
+        ),
+    ]
diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index eb2f31556e4e36fa05eacde98c2a9fdb81a891f1..fcba78889865e00191e81df9cbe771ec54be8e29 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -1,8 +1,9 @@
 # flake8: noqa: DJ12
 
 import os
+from collections.abc import Iterable
 from datetime import datetime
-from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterable, List, Optional, Union
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union
 
 from django.conf import settings
 from django.contrib import messages
@@ -20,7 +21,6 @@ from django.views.generic import CreateView, UpdateView
 from django.views.generic.edit import DeleteView, ModelFormMixin
 
 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
@@ -38,7 +38,7 @@ from aleksis.core.managers import (
     SchoolTermRelatedQuerySet,
 )
 
-from .util.core_helpers import ExtendedICal20Feed
+from .util.core_helpers import EXTENDED_ITEM_ELEMENT_FIELD_MAP, ExtendedICal20Feed
 
 if TYPE_CHECKING:
     from .models import Person
@@ -216,7 +216,7 @@ class ExtensibleModel(models.Model, metaclass=_ExtensibleModelBase):
         cls._safe_add(classmethod(func), name or func.__name__)
 
     @classmethod
-    def get_filter_fields(cls) -> List[str]:
+    def get_filter_fields(cls) -> list[str]:
         """Get names of all text-searchable fields of this model."""
         fields = []
         for field in cls.syncable_fields():
@@ -659,6 +659,7 @@ class CalendarEventMixin(RegistryObject):
         start: Optional[datetime] = None,
         end: Optional[datetime] = None,
         queryset: Optional[QuerySet] = None,
+        **kwargs,
     ) -> ExtendedICal20Feed:
         """Create the calendar feed with all events."""
         feed = cls.start_feed(request=request, params=params)
@@ -667,7 +668,7 @@ class CalendarEventMixin(RegistryObject):
             reference_queryset = queryset
         else:
             reference_queryset = cls.get_objects(
-                request=request, params=params, start=start, end=end
+                request=request, params=params, start=start, end=end, **kwargs
             )
 
         for reference_object in reference_queryset:
@@ -695,11 +696,12 @@ class CalendarEventMixin(RegistryObject):
         params: dict[str, any] | None = None,
         with_reference_object: bool = False,
         queryset: Optional[QuerySet] = None,
+        **kwargs,
     ) -> Calendar:
         """Get events for this calendar feed."""
 
         feed = cls.create_feed(
-            request=request, params=params, start=start, end=end, queryset=queryset
+            request=request, params=params, start=start, end=end, queryset=queryset, **kwargs
         )
         return feed.get_calendar_object(with_reference_object=with_reference_object)
 
@@ -712,11 +714,12 @@ class CalendarEventMixin(RegistryObject):
         params: dict[str, any] | None = None,
         with_reference_object: bool = False,
         queryset: Optional[QuerySet] = None,
+        **kwargs,
     ):
         """Get single events for this calendar feed."""
 
         feed = cls.create_feed(
-            request=request, params=params, start=start, end=end, queryset=queryset
+            request=request, params=params, start=start, end=end, queryset=queryset, **kwargs
         )
         events = feed.get_single_events(start, end, with_reference_object=with_reference_object)
         return events
@@ -724,7 +727,7 @@ class CalendarEventMixin(RegistryObject):
     @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]
+        return [field_map[0] for field_map in EXTENDED_ITEM_ELEMENT_FIELD_MAP]
 
     @classmethod
     def get_event_field_value(
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index 02a60685a11ee349fd9341a2ba3b48adc05e7e9b..6a813fa84be2a47dcfe8ce4bfe73f0d6376c1f7f 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1,8 +1,9 @@
 # flake8: noqa: DJ01
 import base64
+from collections.abc import Iterable, Iterator, Sequence
 from datetime import date, datetime, timedelta
 from itertools import chain
-from typing import TYPE_CHECKING, Any, Iterable, Iterator, List, Optional, Sequence, Union
+from typing import TYPE_CHECKING, Any, Optional, Union
 from urllib.parse import urljoin
 
 from django.conf import settings
@@ -35,7 +36,7 @@ from django_ical.utils import build_rrule_from_recurrences_rrule, build_rrule_fr
 from django_pg_rrule.models import RecurrenceModel
 from dynamic_preferences.models import PerInstancePreferenceModel
 from guardian.shortcuts import get_objects_for_user
-from icalendar import vCalAddress, vText
+from icalendar import Alarm, vCalAddress, vText
 from icalendar.prop import vRecur
 from invitations import signals
 from invitations.base_invitation import AbstractBaseInvitation
@@ -103,6 +104,17 @@ FIELD_CHOICES = (
     ("URLField", _("URL / Link")),
 )
 
+CALENDAR_ALARM_FIELD_MAP = (
+    ("action", "action"),
+    ("trigger", "trigger"),
+    ("duration", "duration"),
+    ("repeat", "repeat"),
+    ("attach", "attach"),
+    ("description", "description"),
+    ("summary", "summary"),
+    ("attendee", "attendee"),
+)
+
 
 class SchoolTerm(ExtensibleModel):
     """School term model.
@@ -432,7 +444,7 @@ class Person(ExtensibleModel):
             self.primary_group = self.member_of.filter(**{f"{field}__regex": pattern}).first()
 
     def notify_about_changed_data(
-        self, changed_fields: Iterable[str], recipients: Optional[List[str]] = None
+        self, changed_fields: Iterable[str], recipients: Optional[list[str]] = None
     ):
         """Notify (configured) recipients about changed data of this person."""
         context = {"person": self, "changed_fields": changed_fields}
@@ -713,6 +725,15 @@ class Notification(ExtensibleModel, TimeStampedModel):
     read = models.BooleanField(default=False, verbose_name=_("Read"))
     sent = models.BooleanField(default=False, verbose_name=_("Sent"))
 
+    calendar_alarm = models.ForeignKey(
+        "CalendarAlarm",
+        related_name="notifications",
+        verbose_name=_("Calendar alarm"),
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+    )
+
     def __str__(self):
         return str(self.title)
 
@@ -730,6 +751,13 @@ class Notification(ExtensibleModel, TimeStampedModel):
     class Meta:
         verbose_name = _("Notification")
         verbose_name_plural = _("Notifications")
+        constraints = [
+            models.UniqueConstraint(
+                fields=["calendar_alarm", "recipient"],
+                condition=Q(calendar_alarm__isnull=False),
+                name="unique_recipient_per_calendar_alarm",
+            ),
+        ]
 
 
 class AnnouncementQuerySet(models.QuerySet):
@@ -775,7 +803,7 @@ class AnnouncementQuerySet(models.QuerySet):
 
         return announcements
 
-    def for_person(self, person: Person) -> List:
+    def for_person(self, person: Person) -> list:
         """Get all announcements for one person."""
         # Filter by person
         announcements_for_person = []
@@ -1608,6 +1636,15 @@ class CalendarEvent(CalendarEventMixin, ExtensiblePolymorphicModel, RecurrenceMo
         """Return the reference object itself."""
         return reference_object
 
+    @classmethod
+    def value_valarm(
+        cls, reference_object: "CalendarEvent", request: HttpRequest | None = None
+    ) -> list[Alarm]:
+        """Return all CalendarAlarms associated with the event, converted into Alarm objects."""
+        return [
+            calendar_alarm.get_alarm(request) for calendar_alarm in reference_object.alarms.all()
+        ]
+
     @classmethod
     def get_objects(
         cls,
@@ -1943,3 +1980,162 @@ class Organisation(ExtensibleModel):
     class Meta:
         verbose_name = _("Organisation")
         verbose_name_plural = _("Organisations")
+
+
+class CalendarAlarm(ExtensiblePolymorphicModel):
+    """An alarm bound to a CalendarEvent.
+
+    To make use of this model, you need to inherit from this model.
+    Every subclass of this model represents a certain group of alarms.
+
+    Every `value_*` method from can be implemented to provide additional data
+    (either static or dynamic). Some like `value_action` are pre-implemented
+    in this model. Some like `value_action` are optional and need to be implemented
+    in a model inheriting from this model. The following iCal attributes are supported:
+
+    action, trigger, duration, repeat, attach, description, summary, attendee
+
+    Whether the implementation of some methods is required depends on the action
+    type of the alarm. See the iCalendar RFC 5545 documentation for more information.
+    """
+
+    ACTION_CHOICES = [
+        ("audio", _("Audio")),
+        ("display", _("Display")),
+        ("email", _("Email")),
+        ("procedure", _("Procedure")),
+    ]
+
+    event = models.ForeignKey(
+        CalendarEvent, on_delete=models.CASCADE, related_name="alarms", verbose_name=_("Event")
+    )
+
+    action = models.CharField(
+        verbose_name=_("Action"), max_length=10, default="display", choices=ACTION_CHOICES
+    )
+
+    send_notifications = models.BooleanField(verbose_name=_("Send notifications"), default=False)
+
+    def value_action(self, request: HttpRequest | None = None) -> str:
+        """Return the action type of the calendar alarm.
+
+        The action type determines in which way the alarm shall be communicated to the user.
+        """
+        return self.action
+
+    def value_trigger(self, request: HttpRequest | None = None) -> Union[datetime, timedelta]:
+        """Return the trigger of the calendar alarm.
+
+        The trigger can be either a time delta value indicating at which time relative to the
+        reference event the alarm shall be triggered or a datetime value indicating an absolute
+        time at which this shall happen.
+        """
+        raise NotImplementedError()
+
+    def value_attach(self, request: HttpRequest | None = None) -> Optional[str]:
+        """Return the attachment of the calendar alarm."""
+        if self.value_action(request) == "procedure":
+            raise NotImplementedError()
+        return None
+
+    def value_description(self, request: HttpRequest | None = None) -> Optional[str]:
+        """Return the description of the calendar alarm."""
+        if self.value_action(request) == "display" or self.value_action(request) == "email":
+            raise NotImplementedError()
+        return None
+
+    def value_summary(self, request: HttpRequest | None = None) -> Optional[str]:
+        """Return the summary of the calendar alarm."""
+        if self.value_action(request) == "email":
+            raise NotImplementedError()
+        return None
+
+    def value_attendee(self, request: HttpRequest | None = None) -> Optional[str]:
+        """Return the attendees of the calendar alarm."""
+        if self.value_action(request) == "email":
+            raise NotImplementedError()
+        return None
+
+    def value_notification_recipients(self, request: HttpRequest | None = None) -> list[Person]:
+        """Return the recipients of the notification linked to the calendar alarm."""
+        raise NotImplementedError()
+
+    def value_notification_sender(self, request: HttpRequest | None = None) -> str:
+        """Return the sender of the notification linked to the calendar alarm."""
+        raise NotImplementedError()
+
+    def value_notification_title(self, request: HttpRequest | None = None) -> str:
+        """Return the title of the notification linked to the calendar alarm."""
+        raise NotImplementedError()
+
+    def value_notification_description(self, request: HttpRequest | None = None) -> str:
+        """Return the description of the notification linked to the calendar alarm."""
+        return self.value_description(request)
+
+    def value_notification_send_at(self, request: HttpRequest | None = None) -> datetime:
+        """Return the absolute time to send the notification linked to the calendar alarm."""
+        if isinstance(self.value_trigger(request), datetime):
+            return self.value_trigger(request)
+        elif isinstance(self.value_trigger(request), timedelta):
+            return self.event.datetime_start - self.value_trigger(request)
+
+    def get_alarm(self, request: Optional[HttpRequest] = None) -> Alarm:
+        alarm = Alarm()
+        for field in CALENDAR_ALARM_FIELD_MAP:
+            method_name = f"value_{field[0]}"
+            if hasattr(self, method_name) and callable(getattr(self, method_name)):
+                value = getattr(self, method_name)(request=request)
+                if value:
+                    alarm.add(field[1], value)
+        return alarm
+
+    def update_or_create_notifications(
+        self, request: Optional[HttpRequest] = None
+    ) -> Optional[list[Notification]]:
+        """Update or create notifications for this calendar alarm (and send them)."""
+        notifications = []
+
+        default_dict = {}
+        for field_name in [field.name for field in Notification._meta.get_fields()]:
+            method_name = f"value_notification_{field_name}"
+            if hasattr(self, method_name) and callable(getattr(self, method_name)):
+                value = getattr(self, method_name)(request=request)
+                if value:
+                    default_dict[field_name] = value
+
+        for recipient in self.value_notification_recipients(request):
+            try:
+                notification = Notification.objects.get(calendar_alarm=self, recipient=recipient)
+                for key, value in default_dict.items():
+                    setattr(notification, key, value)
+                notification.save()
+            except Notification.DoesNotExist:
+                new_values = {"calendar_alarm": self, "recipient": recipient}
+                new_values.update(default_dict)
+                notification = Notification(**new_values)
+                notification.save()
+
+            # Using django's update_or_create creates id collisions, for some reason.
+            notifications.append(notification)
+
+        return notifications
+
+    def save(self, *args, **kwargs):
+        super().save(*args, **kwargs)
+
+        if self.send_notifications:
+            self.update_or_create_notifications()
+
+    class Meta:
+        verbose_name = _("Calendar alarm")
+        verbose_name_plural = _("Calendar alarms")
+
+
+class PersonalEventAlarm(CalendarAlarm):
+    def value_description(self, request: HttpRequest | None = None) -> Optional[str]:
+        """Return the description of the personal event alarm."""
+        return self.event.description
+
+    class Meta:
+        verbose_name = _("Personal event alarm")
+        verbose_name_plural = _("Personal event alarms")
diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py
index a551a5ec8568cad8238a43d3ef68bdb3ca79aaa4..696e2867e85219523041d21c9172422c1f577b05 100644
--- a/aleksis/core/schema/__init__.py
+++ b/aleksis/core/schema/__init__.py
@@ -148,21 +148,15 @@ class Query(graphene.ObjectType):
         if has_person(info.context.user):
             qs = qs | Person.objects.filter(id=info.context.user.person.id)
 
-        active_school_term = get_active_school_term(info.context)
-        group_q = Q(school_term=active_school_term) | Q(school_term=None)
-        qs = (
-            (
-                qs
-                | Person.objects.filter(
-                    member_of__in=Group.objects.filter(group_q).filter(
-                        owners=info.context.user.person,
-                        group_type__owners_can_see_members=True,
-                    ),
-                )
+            active_school_term = get_active_school_term(info.context)
+            group_q = Q(school_term=active_school_term) | Q(school_term=None)
+            qs = qs | Person.objects.filter(
+                member_of__in=Group.objects.filter(group_q).filter(
+                    owners=info.context.user.person,
+                    group_type__owners_can_see_members=True,
+                ),
             )
-            .distinct()
-            .annotate_permissions(info.context.user)
-        )
+        qs = qs.distinct().annotate_permissions(info.context.user)
         return graphene_django_optimizer.query(qs, info)
 
     def resolve_person_by_id(root, info, id):  # noqa
@@ -252,9 +246,9 @@ class Query(graphene.ObjectType):
 
     def resolve_pdf_by_id(root, info, id, **kwargs):  # noqa
         pdf_file = PDFFile.objects.get(pk=id)
-        if has_person(info.context) and info.context.user.person != pdf_file.person:
-            return None
-        return pdf_file
+        if has_person(info.context) and info.context.user.person == pdf_file.person:
+            return pdf_file
+        return None
 
     def resolve_search_snippets(root, info, query, limit=-1, **kwargs):
         indexed_models = UnifiedIndex().get_indexed_models()
diff --git a/aleksis/core/schema/personal_event.py b/aleksis/core/schema/personal_event.py
index f97a1d0dfceb7dfde10077f988813c045a742ec9..3bf36b2e4c352cee626529002897cfc6095b6e3e 100644
--- a/aleksis/core/schema/personal_event.py
+++ b/aleksis/core/schema/personal_event.py
@@ -1,4 +1,4 @@
-from typing import Iterable
+from collections.abc import Iterable
 
 from django.utils import timezone
 
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index 1bde194250b368081bb8ce1a15a3af147ad3aa28..11b27b5a16825109cbe8fce44b5b3a4ee2110bdd 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -583,13 +583,10 @@ YARN_INSTALLED_APPS = [
     "jquery-sortablejs@^1.0.1",
     "sortablejs@^1.15.0",
     "@sentry/tracing@^7.28.0",
-    "luxon@^2.3.2",
     "@iconify/iconify@^2.2.1",
     "@iconify/json@^2.1.30",
     "@mdi/font@^7.2.96",
     "apollo-boost@^0.4.9",
-    "apollo-link-batch-http@^1.2.14",
-    "apollo-link-retry@^2.2.16",
     "apollo3-cache-persist@^0.14.1",
     "deepmerge@^4.2.2",
     "graphql@^15.8.0",
@@ -615,6 +612,9 @@ YARN_INSTALLED_APPS = [
     "vue-draggable-grid@^0.4.0",
     "rrule",
     "luxon@^3.4.3",
+    "apollo-upload-client@^18.0.1",
+    "@vitejs/plugin-legacy@^6.0.0",
+    "terser@^5.37.0",
 ]
 
 merge_app_settings("YARN_INSTALLED_APPS", YARN_INSTALLED_APPS, True)
diff --git a/aleksis/core/static/print.css b/aleksis/core/static/print.css
index 1c3e9d486a27e913671a453219c60b70b8778462..e65660d48517619b2346a2435320adcf4f4398d6 100644
--- a/aleksis/core/static/print.css
+++ b/aleksis/core/static/print.css
@@ -117,12 +117,23 @@ header .col {
 
 /* Some stuff for tables */
 
+table.thead {
+  display: table-row-group;
+  break-inside: avoid;
+}
+
 table.small-print,
 td.small-print,
 th.small-print {
   font-size: 10pt;
 }
 
+table.tbody tr td,
+table.tbody tr th {
+  break-inside: avoid !important;
+  break-after: auto;
+}
+
 tr {
   border-bottom: 1px solid rgba(0, 0, 0, 0.3);
 }
diff --git a/aleksis/core/templates/core/vue_index.html b/aleksis/core/templates/core/vue_index.html
index 8a000ad1d5e8147183207b00fe4ea5d4cecb4844..19e192432ac050d51ca23ed3a13bead25099a346 100644
--- a/aleksis/core/templates/core/vue_index.html
+++ b/aleksis/core/templates/core/vue_index.html
@@ -34,6 +34,8 @@
     {% block no_frontend %}
       {% if not need_maintenance_response %}
         {% vite_asset 'aleksis/core/frontend/index.js' %}
+        {% vite_legacy_asset 'aleksis/core/frontend/index-legacy.js' %}
+        {% vite_legacy_polyfills nomodule=False %}
       {% endif %}
     {% endblock no_frontend %}
   </body>
diff --git a/aleksis/core/tests/managers/test_aleksisbasemanager.py b/aleksis/core/tests/managers/test_aleksisbasemanager.py
index 205bf023bfec007ee2e8c4114f35f59f14136c5e..3690831008fb9830fd4e847baae34685a3a6df51 100644
--- a/aleksis/core/tests/managers/test_aleksisbasemanager.py
+++ b/aleksis/core/tests/managers/test_aleksisbasemanager.py
@@ -1,18 +1,24 @@
-
+import pytest
 
 from aleksis.core.models import Person
-import pytest
 
 pytestmark = pytest.mark.django_db
 
+
 def test_managed_by():
-    managed_person = Person.objects.create(first_name="Jane", last_name="Doe", managed_by_app_label="core")
+    managed_person = Person.objects.create(
+        first_name="Jane", last_name="Doe", managed_by_app_label="core"
+    )
     unmanaged_person_1 = Person.objects.create(first_name="Jane 2", last_name="Doe 2")
     unmanaged_person_2 = Person.objects.create(first_name="Jane 2", last_name="Doe 2")
 
-    assert list(Person.objects.managed_by_app("core")) ==  [managed_person]
+    assert list(Person.objects.managed_by_app("core")) == [managed_person]
 
     assert list(Person.objects.all()) == [unmanaged_person_1, unmanaged_person_2]
     assert list(Person.objects.unmanaged()) == [unmanaged_person_1, unmanaged_person_2]
 
-    assert list(Person.objects.managed_and_unmanaged()) == [managed_person, unmanaged_person_1, unmanaged_person_2]
\ No newline at end of file
+    assert list(Person.objects.managed_and_unmanaged()) == [
+        managed_person,
+        unmanaged_person_1,
+        unmanaged_person_2,
+    ]
diff --git a/aleksis/core/tests/mixins/test_registry_object.py b/aleksis/core/tests/mixins/test_registry_object.py
index d872e4a7078d27fd176a4b3f557bef3b55467fbb..9ee68d0a615844759e127e57ba44cc3c7999881f 100644
--- a/aleksis/core/tests/mixins/test_registry_object.py
+++ b/aleksis/core/tests/mixins/test_registry_object.py
@@ -1,5 +1,3 @@
-import pytest
-
 from aleksis.core.mixins import RegistryObject
 
 
diff --git a/aleksis/core/tests/models/test_calendar_event.py b/aleksis/core/tests/models/test_calendar_event.py
index 6c65afa612871dad427dc6b2368f987a3d8c932c..f26e7dcdeccdaaea29698bd1acfe1a120b800f83 100644
--- a/aleksis/core/tests/models/test_calendar_event.py
+++ b/aleksis/core/tests/models/test_calendar_event.py
@@ -1,26 +1,36 @@
 from datetime import datetime, timezone
+from zoneinfo import ZoneInfo
 
 import pytest
-from aleksis.core.models import CalendarEvent
-from recurrence import Recurrence, WEEKLY, Rule
+from recurrence import WEEKLY, Recurrence, Rule
 
-from zoneinfo import ZoneInfo
+from aleksis.core.models import CalendarEvent
 
 pytestmark = pytest.mark.django_db
 
 
 def test_calendar_event_timezone():
-    datetime_start = datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc).astimezone(ZoneInfo("Europe/Berlin"))
-    datetime_end = datetime(2024, 12, 31, 0, 0, tzinfo=timezone.utc).astimezone(ZoneInfo("Europe/Berlin"))
+    datetime_start = datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc).astimezone(
+        ZoneInfo("Europe/Berlin")
+    )
+    datetime_end = datetime(2024, 12, 31, 0, 0, tzinfo=timezone.utc).astimezone(
+        ZoneInfo("Europe/Berlin")
+    )
 
     # No timezone set
-    calendar_event = CalendarEvent.objects.create(datetime_start=datetime_start, datetime_end=datetime_end)
+    calendar_event = CalendarEvent.objects.create(
+        datetime_start=datetime_start, datetime_end=datetime_end
+    )
     calendar_event.refresh_from_db()
 
     assert calendar_event.datetime_start == datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc)
     assert calendar_event.datetime_end == datetime(2024, 12, 31, 0, 0, tzinfo=timezone.utc)
-    assert CalendarEvent.value_start_datetime(calendar_event) == datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc)
-    assert CalendarEvent.value_end_datetime(calendar_event) == datetime(2024, 12, 31, 0, 0, tzinfo=timezone.utc)
+    assert CalendarEvent.value_start_datetime(calendar_event) == datetime(
+        2024, 1, 1, 0, 0, tzinfo=timezone.utc
+    )
+    assert CalendarEvent.value_end_datetime(calendar_event) == datetime(
+        2024, 12, 31, 0, 0, tzinfo=timezone.utc
+    )
     assert calendar_event.timezone is None
 
     # Set timezone if not allowed
diff --git a/aleksis/core/tests/models/test_holiday.py b/aleksis/core/tests/models/test_holiday.py
index cacc860921c742351ed00d02da39607ad73e1695..02cb9de042d8bfc47d99ac3513bb3edb04d242a0 100644
--- a/aleksis/core/tests/models/test_holiday.py
+++ b/aleksis/core/tests/models/test_holiday.py
@@ -1,32 +1,37 @@
+from datetime import date, datetime
+
 import pytest
+from recurrence import WEEKLY, Recurrence, Rule
 
-pytestmark = pytest.mark.django_db
 from aleksis.core.models import Holiday
 
-from datetime import date, datetime
-from recurrence import Recurrence, Rule, WEEKLY
+pytestmark = pytest.mark.django_db
 
 
 def test_holiday_get_days():
-    holiday = Holiday.objects.create(date_start=date(2024, 2, 1), date_end=date(2024, 2, 4), holiday_name="Test Holiday")
+    holiday = Holiday.objects.create(
+        date_start=date(2024, 2, 1), date_end=date(2024, 2, 4), holiday_name="Test Holiday"
+    )
     assert set(holiday.get_days()) == {
         date(2024, 2, 1),
         date(2024, 2, 2),
         date(2024, 2, 3),
-        date(2024, 2, 4)
+        date(2024, 2, 4),
     }
 
-def test_holiday_exdates():
 
-    holiday = Holiday.objects.create(date_start=date(2024, 2, 1), date_end=date(2024, 2, 28), holiday_name="Test Holiday")
+def test_holiday_exdates():
+    holiday = Holiday.objects.create(
+        date_start=date(2024, 2, 1), date_end=date(2024, 2, 28), holiday_name="Test Holiday"
+    )
 
     pattern = Recurrence(datetime(2024, 2, 3))
     pattern.rrules.append(Rule(WEEKLY, until=datetime(2024, 6, 1)))
 
-    exdates = holiday.get_ex_dates(datetime(2024, 2,3), datetime(2024, 4, 1), pattern)
+    exdates = holiday.get_ex_dates(datetime(2024, 2, 3), datetime(2024, 4, 1), pattern)
     assert set(exdates) == {
         datetime(2024, 2, 3),
         datetime(2024, 2, 10),
         datetime(2024, 2, 17),
-        datetime(2024, 2, 24)
+        datetime(2024, 2, 24),
     }
diff --git a/aleksis/core/tests/regression/test_regression.py b/aleksis/core/tests/regression/test_regression.py
index c540111d38b29bb35b33fa562f78c06c74f2cd30..a578145dba3bf3c98ccffb8df00a4f78e3c0b7ee 100644
--- a/aleksis/core/tests/regression/test_regression.py
+++ b/aleksis/core/tests/regression/test_regression.py
@@ -1,16 +1,14 @@
 import base64
 
 from django.contrib.auth import get_user_model
+from django.test import override_settings
+from django.urls import reverse
 
 import pytest
 
 from aleksis.core.models import Group, OAuthApplication, Person
 
 pytestmark = pytest.mark.django_db
-from django.http import HttpResponse
-from django.test import override_settings
-from django.urls import path, reverse
-from django.views.generic import View
 
 
 def test_all_settigns_registered():
diff --git a/aleksis/core/tests/regression/view_oauth.py b/aleksis/core/tests/regression/view_oauth.py
index d5671208e127e79c30d7bb179bbf2e82a35616fb..5116a66b48d86b34a063a85ec380face37495ec2 100644
--- a/aleksis/core/tests/regression/view_oauth.py
+++ b/aleksis/core/tests/regression/view_oauth.py
@@ -1,6 +1,5 @@
 from django.http import HttpResponse
-from django.test import override_settings
-from django.urls import path, reverse
+from django.urls import path
 from django.views.generic import View
 
 from oauth2_provider.views.mixins import ScopedResourceMixin
diff --git a/aleksis/core/tests/schema/test_groups.py b/aleksis/core/tests/schema/test_groups.py
index 7b7b2297efa9125faf042d2de3441ce7e8e2dff1..222e1aca5bc9ca68aea892bc3e5fd3930c37c02b 100644
--- a/aleksis/core/tests/schema/test_groups.py
+++ b/aleksis/core/tests/schema/test_groups.py
@@ -1,12 +1,8 @@
-import json
-import pytest
-from graphql.error.graphql_error import GraphQLError
-
-from aleksis.core.models import Group, GroupType, Person
 from django.contrib.auth.models import Permission
 
-from aleksis.core.util.core_helpers import get_site_preferences
+import pytest
 
+from aleksis.core.models import Group, GroupType, Person
 
 pytestmark = pytest.mark.django_db
 
@@ -17,19 +13,27 @@ def test_groups_query(client_query):
     wrong_group_type = GroupType.objects.create(name="wrong")
 
     group_not_owner = Group.objects.create(name="not_owner")
-    group_correct_group_type_owner = Group.objects.create(name="correct_group_type_owner", group_type=correct_group_type)
+    group_correct_group_type_owner = Group.objects.create(
+        name="correct_group_type_owner", group_type=correct_group_type
+    )
 
-    group2_correct_group_type_owner = Group.objects.create(name="correct_group_type_owner", group_type=correct_group_type)
-    group_wrong_group_type_owner = Group.objects.create(name="wrong_group_type_owner", group_type=wrong_group_type)
+    group2_correct_group_type_owner = Group.objects.create(
+        name="correct_group_type_owner", group_type=correct_group_type
+    )
+    group_wrong_group_type_owner = Group.objects.create(
+        name="wrong_group_type_owner", group_type=wrong_group_type
+    )
     group_no_group_type_owner = Group.objects.create(name="no_group_type_owner")
 
-    for g in (group_correct_group_type_owner, group2_correct_group_type_owner, group_wrong_group_type_owner, group_no_group_type_owner):
+    for g in (
+        group_correct_group_type_owner,
+        group2_correct_group_type_owner,
+        group_wrong_group_type_owner,
+        group_no_group_type_owner,
+    ):
         g.owners.add(p)
 
-
-    response, content = client_query(
-        "{groups{id}}"
-    )
+    response, content = client_query("{groups{id}}")
     assert len(content["data"]["groups"]) == 0
 
     for g in Group.objects.all():
@@ -39,23 +43,26 @@ def test_groups_query(client_query):
         )
         assert content["data"]["object"] is None
 
-    global_permission = Permission.objects.get(codename="view_group", content_type__app_label="core")
+    global_permission = Permission.objects.get(
+        codename="view_group", content_type__app_label="core"
+    )
     p.user.user_permissions.add(global_permission)
 
-    response, content = client_query(
-        "{groups{id}}"
+    response, content = client_query("{groups{id}}")
+    assert set(int(g["id"]) for g in content["data"]["groups"]) == set(
+        Group.objects.values_list("id", flat=True)
     )
-    assert set(int(g["id"]) for g in content["data"]["groups"]) == set(Group.objects.values_list("id", flat=True))
 
     p.user.user_permissions.remove(global_permission)
 
     correct_group_type.owners_can_see_groups = True
     correct_group_type.save()
 
-    response, content = client_query(
-        "{groups{id}}"
-    )
-    assert set(int(g["id"]) for g in content["data"]["groups"]) == {group_correct_group_type_owner.id, group2_correct_group_type_owner.id}
+    response, content = client_query("{groups{id}}")
+    assert set(int(g["id"]) for g in content["data"]["groups"]) == {
+        group_correct_group_type_owner.id,
+        group2_correct_group_type_owner.id,
+    }
 
     for g in (group_correct_group_type_owner, group2_correct_group_type_owner):
         response, content = client_query(
@@ -64,7 +71,7 @@ def test_groups_query(client_query):
         )
         assert content["data"]["object"]["id"] == str(g.id)
 
-    for g in (group_not_owner,  group_wrong_group_type_owner, group_no_group_type_owner):
+    for g in (group_not_owner, group_wrong_group_type_owner, group_no_group_type_owner):
         response, content = client_query(
             "query groupById($id: ID) {object: groupById(id: $id) { id } }",
             variables={"id": g.id},
diff --git a/aleksis/core/tests/schema/test_persons.py b/aleksis/core/tests/schema/test_persons.py
index 70b876129ce4d2051e21b17c12b67e464bd6b402..06f7de81c7127dd9d0817d1e6e02dcc7107a0472 100644
--- a/aleksis/core/tests/schema/test_persons.py
+++ b/aleksis/core/tests/schema/test_persons.py
@@ -1,8 +1,8 @@
+from django.contrib.auth.models import Permission
+
 import pytest
 
 from aleksis.core.models import Group, GroupType, Person
-from django.contrib.auth.models import Permission
-
 
 pytestmark = pytest.mark.django_db
 
@@ -13,17 +13,30 @@ def test_persons_query(client_query):
     wrong_group_type = GroupType.objects.create(name="wrong")
 
     group_not_owner = Group.objects.create(name="not_owner")
-    group_correct_group_type_owner = Group.objects.create(name="correct_group_type_owner", group_type=correct_group_type)
+    group_correct_group_type_owner = Group.objects.create(
+        name="correct_group_type_owner", group_type=correct_group_type
+    )
 
-    group2_correct_group_type_owner = Group.objects.create(name="correct_group_type_owner", group_type=correct_group_type)
-    group_wrong_group_type_owner = Group.objects.create(name="wrong_group_type_owner", group_type=wrong_group_type)
+    group2_correct_group_type_owner = Group.objects.create(
+        name="correct_group_type_owner", group_type=correct_group_type
+    )
+    group_wrong_group_type_owner = Group.objects.create(
+        name="wrong_group_type_owner", group_type=wrong_group_type
+    )
     group_no_group_type_owner = Group.objects.create(name="no_group_type_owner")
 
-    for g in (group_correct_group_type_owner, group2_correct_group_type_owner, group_wrong_group_type_owner, group_no_group_type_owner):
+    for g in (
+        group_correct_group_type_owner,
+        group2_correct_group_type_owner,
+        group_wrong_group_type_owner,
+        group_no_group_type_owner,
+    ):
         g.owners.add(p)
 
     correct_member = Person.objects.create(first_name="correct_member", last_name="correct_member")
-    correct_member_2 = Person.objects.create(first_name="correct_member_2", last_name="correct_member_2")
+    correct_member_2 = Person.objects.create(
+        first_name="correct_member_2", last_name="correct_member_2"
+    )
     wrong_member = Person.objects.create(first_name="wrong_member", last_name="wrong_member")
 
     for g in (group_correct_group_type_owner, group2_correct_group_type_owner):
@@ -32,11 +45,7 @@ def test_persons_query(client_query):
     for g in (group_not_owner, group_wrong_group_type_owner, group_no_group_type_owner):
         g.members.add(wrong_member)
 
-
-
-    response, content = client_query(
-        "{persons{id}}"
-    )
+    response, content = client_query("{persons{id}}")
     assert len(content["data"]["persons"]) == 1
     assert content["data"]["persons"][0]["id"] == str(p.id)
 
@@ -45,25 +54,29 @@ def test_persons_query(client_query):
             "query personById($id: ID) {object: personById(id: $id) { id } }",
             variables={"id": g.id},
         )
-        assert content["data"]["object"] == None
+        assert content["data"]["object"] is None
 
-    global_permission = Permission.objects.get(codename="view_person", content_type__app_label="core")
+    global_permission = Permission.objects.get(
+        codename="view_person", content_type__app_label="core"
+    )
     p.user.user_permissions.add(global_permission)
 
-    response, content = client_query(
-        "{persons{id}}"
+    response, content = client_query("{persons{id}}")
+    assert set(int(g["id"]) for g in content["data"]["persons"]) == set(
+        Person.objects.values_list("id", flat=True)
     )
-    assert set(int(g["id"]) for g in content["data"]["persons"]) == set(Person.objects.values_list("id", flat=True))
 
     p.user.user_permissions.remove(global_permission)
 
     correct_group_type.owners_can_see_members = True
     correct_group_type.save()
 
-    response, content = client_query(
-        "{persons{id}}"
-    )
-    assert set(int(g["id"]) for g in content["data"]["persons"]) == {p.id, correct_member.id, correct_member_2.id}
+    response, content = client_query("{persons{id}}")
+    assert set(int(g["id"]) for g in content["data"]["persons"]) == {
+        p.id,
+        correct_member.id,
+        correct_member_2.id,
+    }
 
     for g in (correct_member, correct_member_2):
         response, content = client_query(
@@ -76,4 +89,4 @@ def test_persons_query(client_query):
         "query personById($id: ID) {object: personById(id: $id) { id } }",
         variables={"id": wrong_member.id},
     )
-    assert content["data"]["object"] == None
+    assert content["data"]["object"] is None
diff --git a/aleksis/core/tests/templatetags/test_data_helpers.py b/aleksis/core/tests/templatetags/test_data_helpers.py
index 176e08b22ecb252d5b0113583ab534f46e978072..677c115ef94f0e9585520caf5e39053c444a4d21 100644
--- a/aleksis/core/tests/templatetags/test_data_helpers.py
+++ b/aleksis/core/tests/templatetags/test_data_helpers.py
@@ -8,7 +8,7 @@ pytestmark = pytest.mark.django_db
 
 
 def test_get_dict_object():
-    class _Foo(object):
+    class _Foo:
         bar = 12
 
     assert _Foo.bar == get_dict(_Foo, "bar")
diff --git a/aleksis/core/tests/views/test_account.py b/aleksis/core/tests/views/test_account.py
index fa55e8465bfa50b2a12479f6c72ee747cd1f2ef6..b6a6d48e548df6a7d748ffa745251df35fbe8352 100644
--- a/aleksis/core/tests/views/test_account.py
+++ b/aleksis/core/tests/views/test_account.py
@@ -49,7 +49,7 @@ def test_logout(client, django_user_model):
     ],
     AUTH_LDAP_SERVER_URI="ldap://[100::0]",
     AUTH_LDAP_SET_USABLE_PASSWORD=True,
-    **LDAP_SETTINGS
+    **LDAP_SETTINGS,
 )
 def test_login_ldap_fail_if_previously_ldap_authenticated(client, django_user_model):
     username = "foo"
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index d5870ee34e9d751ed693faede71749532e70c362..ad25573c525634a9d08f6a2822336502ea4f4185 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -30,12 +30,12 @@ urlpatterns = [
     path("__icons__/", include("dj_iconify.urls")),
     path(
         "graphql/",
-        csrf_exempt(views.LoggingGraphQLView.as_view(batch=True)),
+        csrf_exempt(views.LoggingGraphQLView.as_view()),
         name="graphql",
     ),
     path("logo", force_maintenance_mode_off(views.LogoView.as_view()), name="logo"),
     path(
-        ".well-known/openid-configuration/",
+        ".well-known/openid-configuration",
         ConnectDiscoveryInfoView.as_view(),
         name="oidc_configuration",
     ),
@@ -98,7 +98,7 @@ urlpatterns = [
                 ),
                 path("accounts/", include("allauth.urls")),
                 path(
-                    "accounts/social/connections/<int:pk>/delete/",
+                    "accounts/3rdparty/<int:pk>/delete/",
                     views.SocialAccountDeleteView.as_view(),
                     name="delete_social_account_by_pk",
                 ),
diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py
index fce211f57ea53d280841d7e74ce99d30f091827e..56816725acb3de1c43f150e8e33a6e80ba611f97 100644
--- a/aleksis/core/util/apps.py
+++ b/aleksis/core/util/apps.py
@@ -1,6 +1,7 @@
 import logging
+from collections.abc import Sequence
 from importlib import metadata
-from typing import TYPE_CHECKING, Any, Optional, Sequence
+from typing import TYPE_CHECKING, Any, Optional
 
 import django.apps
 from django.contrib.auth.signals import user_logged_in, user_logged_out
diff --git a/aleksis/core/util/celery_progress.py b/aleksis/core/util/celery_progress.py
index 008b979ec37207d05b2b22538a7761591ed8b22c..469c19a47ac2b70d518a934d0383edd369bf68f1 100644
--- a/aleksis/core/util/celery_progress.py
+++ b/aleksis/core/util/celery_progress.py
@@ -1,6 +1,7 @@
+from collections.abc import Generator, Iterable, Sequence
 from functools import wraps
 from numbers import Number
-from typing import Callable, Generator, Iterable, Optional, Sequence, Union
+from typing import Callable, Optional, Union
 
 from django.apps import apps
 from django.contrib import messages
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index 71c3163af23d68d8305318be3486c72f402ed0b3..3a6aa841a4d26823e24c18a34b6e9ca50a4bfff7 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -1,11 +1,12 @@
 import json
 import os
+from collections.abc import Sequence
 from datetime import datetime, timedelta
 from importlib import import_module, metadata
 from itertools import groupby
 from operator import itemgetter
 from types import ModuleType
-from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Sequence, Union
+from typing import TYPE_CHECKING, Any, Callable, Optional, Union
 from warnings import warn
 
 from django.conf import settings
@@ -217,10 +218,7 @@ def has_person(obj: Union[HttpRequest, Model]) -> bool:
         return False
 
     person = getattr(obj, "person", None)
-    if person is None or getattr(person, "is_dummy", False):
-        return False
-
-    return True
+    return not (person is None or getattr(person, "is_dummy", False))
 
 
 def custom_information_processor(request: Union[HttpRequest, None]) -> dict:
@@ -354,7 +352,7 @@ def get_allowed_object_ids(request: HttpRequest, models: list) -> list:
     return allowed_object_ids
 
 
-def process_custom_context_processors(context_processors: list) -> Dict[str, Any]:
+def process_custom_context_processors(context_processors: list) -> dict[str, Any]:
     """Process custom context processors."""
     context = {}
     processors = tuple(import_string(path) for path in context_processors)
@@ -478,8 +476,8 @@ def get_ip(*args, **kwargs):
     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 + (
+EXTENDED_FEED_FIELD_MAP = feedgenerator.FEED_FIELD_MAP + (("color", "color"),)
+EXTENDED_ITEM_ELEMENT_FIELD_MAP = feedgenerator.ITEM_ELEMENT_FIELD_MAP + (
     ("color", "color"),
     ("meta", "x-meta"),
     ("reference_object", "reference_object"),
@@ -498,7 +496,7 @@ class ExtendedICal20Feed(feedgenerator.ICal20Feed):
         cal.add("calscale", "GREGORIAN")
         cal.add("prodid", "-//AlekSIS//AlekSIS//EN")
 
-        for ifield, efield in feedgenerator.FEED_FIELD_MAP:
+        for ifield, efield in EXTENDED_FEED_FIELD_MAP:
             val = self.feed.get(ifield)
             if val is not None:
                 cal.add(efield, val)
@@ -520,7 +518,7 @@ class ExtendedICal20Feed(feedgenerator.ICal20Feed):
             component_type = item.get("component_type")
             element = Todo() if component_type == "todo" else Event()
 
-            for ifield, efield in feedgenerator.ITEM_ELEMENT_FIELD_MAP:
+            for ifield, efield in EXTENDED_ITEM_ELEMENT_FIELD_MAP:
                 val = item.get(ifield)
                 if val is not None:
                     if ifield == "attendee":
@@ -597,3 +595,4 @@ def filter_active_school_term_by_date(
                 }
             )
         )
+    return qs
diff --git a/aleksis/core/util/email.py b/aleksis/core/util/email.py
index eae0e584604aa4e7141edcb6b244a2e1a079a762..99f0e1e71c25b5aa968c9323753ac50721bde795 100644
--- a/aleksis/core/util/email.py
+++ b/aleksis/core/util/email.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, List, Optional
+from typing import Any, Optional
 
 from django.conf import settings
 
@@ -9,8 +9,8 @@ from aleksis.core.util.core_helpers import get_site_preferences, process_custom_
 
 def send_email(
     template_name: str,
-    recipient_list: List[str],
-    context: Dict[str, Any],
+    recipient_list: list[str],
+    context: dict[str, Any],
     from_email: Optional[str] = None,
     **kwargs,
 ):
diff --git a/aleksis/core/util/frontend_helpers.py b/aleksis/core/util/frontend_helpers.py
index 885ac39fd51833c55ce072cc8a425991cfea7db2..0565cfb2272c99723c189788a294040d50231300 100644
--- a/aleksis/core/util/frontend_helpers.py
+++ b/aleksis/core/util/frontend_helpers.py
@@ -1,7 +1,8 @@
 import json
 import os
 import shutil
-from typing import Any, Optional, Sequence
+from collections.abc import Sequence
+from typing import Any, Optional
 
 from django.conf import settings
 
diff --git a/aleksis/core/util/notifications.py b/aleksis/core/util/notifications.py
index 183858f9407ce5afb04c1dd8b60693ec4973cd02..d6126a9f3e5d274f0087c473dd7a63f38bbf80e5 100644
--- a/aleksis/core/util/notifications.py
+++ b/aleksis/core/util/notifications.py
@@ -1,6 +1,7 @@
 """Utility code for notification system."""
 
-from typing import TYPE_CHECKING, Sequence, Union
+from collections.abc import Sequence
+from typing import TYPE_CHECKING, Union
 
 from django.apps import apps
 from django.conf import settings
diff --git a/aleksis/core/util/pdf.py b/aleksis/core/util/pdf.py
index 01bf7601c92cc81425f04268a6276509ea4ab4f5..1a606220150870444d8da8cee0dd0ad574173068 100644
--- a/aleksis/core/util/pdf.py
+++ b/aleksis/core/util/pdf.py
@@ -3,7 +3,7 @@ import os
 import subprocess  # noqa
 from datetime import timedelta
 from tempfile import TemporaryDirectory
-from typing import Callable, Optional, Tuple, Union
+from typing import Callable, Optional, Union
 from urllib.parse import urljoin
 
 from django.conf import settings
@@ -89,7 +89,7 @@ def process_context_for_pdf(context: Optional[dict] = None, request: Optional[Ht
 
 def generate_pdf_from_html(
     html: str, request: Optional[HttpRequest] = None, file_object: Optional[PDFFile] = None
-) -> Tuple[PDFFile, AsyncResult]:
+) -> tuple[PDFFile, AsyncResult]:
     """Start a PDF generation task and return the matching file object and Celery result."""
     html_file = ContentFile(html.encode(), name="source.html")
 
@@ -119,7 +119,7 @@ def generate_pdf_from_template(
     request: Optional[HttpRequest] = None,
     render_method: Optional[Callable] = None,
     file_object: Optional[PDFFile] = None,
-) -> Tuple[PDFFile, AsyncResult]:
+) -> tuple[PDFFile, AsyncResult]:
     """Start a PDF generation task and return the matching file object and Celery result."""
     processed_context = process_context_for_pdf(context, request)
 
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index be56108b6d3c6811b19d262e119993b4b238cda4..a356ad0df0ded55ee7bdb967ef5953bd054d8897 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -1,5 +1,5 @@
 from textwrap import wrap
-from typing import Any, Dict, Optional, Type
+from typing import Any, Optional
 from urllib.parse import urlencode, urlparse, urlunparse
 
 from django.apps import apps
@@ -46,7 +46,9 @@ from django_filters.views import FilterView
 from django_tables2 import SingleTableMixin, SingleTableView
 from dynamic_preferences.forms import preference_form_builder
 from graphene_django.views import GraphQLView
-from graphql import GraphQLError
+from graphql import (
+    GraphQLError,
+)
 from guardian.shortcuts import GroupObjectPermission, UserObjectPermission
 from haystack.generic_views import SearchView
 from haystack.inputs import AutoQuery
@@ -134,6 +136,9 @@ from .util.core_helpers import (
 from .util.forms import PreferenceLayout
 from .util.pdf import render_pdf
 
+if settings.SENTRY_ENABLED:
+    import sentry_sdk
+
 
 class LogoView(View):
     def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
@@ -630,7 +635,7 @@ class DashboardWidgetListView(PermissionRequiredMixin, SingleTableView):
 class DashboardWidgetEditView(PermissionRequiredMixin, AdvancedEditView):
     """Edit view for dashboard widgets."""
 
-    def get_form_class(self) -> Type[BaseModelForm]:
+    def get_form_class(self) -> type[BaseModelForm]:
         return modelform_factory(self.object.__class__, fields=self.fields)
 
     model = DashboardWidget
@@ -905,12 +910,12 @@ class AssignPermissionView(SuccessNextMixin, PermissionRequiredMixin, DetailView
     form_class = AssignPermissionForm
     success_url = "manage_user_global_permissions"
 
-    def get_form_kwargs(self) -> Dict[str, Any]:
+    def get_form_kwargs(self) -> dict[str, Any]:
         kwargs = super().get_form_kwargs()
         kwargs["permission"] = self.get_object()
         return kwargs
 
-    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
+    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
         # Overwrite get_context_data to ensure correct function call order
         self.object = self.get_object()
         context = super().get_context_data(**kwargs)
@@ -1265,8 +1270,17 @@ class TwoFactorLoginView(two_factor_views.LoginView):
 class LoggingGraphQLView(GraphQLView):
     """GraphQL view that raises unknown exceptions instead of blindly catching them."""
 
-    def execute_graphql_request(self, *args, **kwargs):
-        result = super().execute_graphql_request(*args, **kwargs)
+    def execute_graphql_request(
+        self, request, data, query, variables, operation_name, show_graphiql=False
+    ):
+        if settings.SENTRY_ENABLED and operation_name:
+            scope = sentry_sdk.get_current_scope()
+            scope.set_transaction_name(operation_name)
+
+        result = super().execute_graphql_request(
+            request, data, query, variables, operation_name, show_graphiql
+        )
+
         errors = result.errors or []
         for error in errors:
             if not isinstance(
diff --git a/aleksis/core/vite.config.js b/aleksis/core/vite.config.js
index fb43a46a0a9533a5282f1b3424078791529d3186..c08d285ed2565c56c23c20e07c0ba838c8391f77 100644
--- a/aleksis/core/vite.config.js
+++ b/aleksis/core/vite.config.js
@@ -33,6 +33,7 @@ import topLevelAwait from "vite-plugin-top-level-await";
 import browserslistToEsbuild from "browserslist-to-esbuild";
 const license = require("rollup-plugin-license");
 import SupportedBrowsers from "vite-plugin-browserslist-useragent";
+import legacy from "@vitejs/plugin-legacy";
 
 // Read the hints writen by `aleksis-admin vite`
 const django_values = JSON.parse(fs.readFileSync("./django-vite-values.json"));
@@ -301,6 +302,10 @@ export default defineConfig({
       ignoreMinor: true,
       allowHigherVersions: true,
     }),
+    legacy({
+      targets: browsersList,
+      modernPolyfills: true,
+    }),
     VitePWA({
       injectRegister: "null",
       devOptions: {
diff --git a/conftest.py b/conftest.py
index 1a448a6f93b2cc6ae536c916c8a9ab56d5b9fe5a..6591233d81c8ec06a3bf2934c64e0023c52cdde4 100644
--- a/conftest.py
+++ b/conftest.py
@@ -30,15 +30,15 @@ def graphql_query(
         header_params = {"headers": headers}
         resp = client.post(
             graphql_url,
-            json.dumps([body]),
+            json.dumps(body),
             content_type="application/json",
             **header_params,
         )
     else:
         resp = client.post(
-            graphql_url, json.dumps([body]), content_type="application/json"
+            graphql_url, json.dumps(body), content_type="application/json"
         )
-    content = json.loads(resp.content)[0]
+    content = json.loads(resp.content)
     return resp, content
 
 
diff --git a/docs/admin/10_install.rst b/docs/admin/10_install.rst
index 0d35c8f65b5ce21d16d8f306368ea75ac957c92f..f7346c108c0537dc14b7eb76aef5208f7ca9c2db 100644
--- a/docs/admin/10_install.rst
+++ b/docs/admin/10_install.rst
@@ -32,7 +32,7 @@ For an installation on a dedicated server, the following prerequisites are neede
  * Valkey (or legacy Redis)
  * uWSGI
  * nginx
- * Python 3.9 or newer
+ * Python 3.10 or newer
  * Node.js 18 or newer
  * Some system dependencies to build Python modules and manage frontend files
  * System locales for all supported languages
diff --git a/pyproject.toml b/pyproject.toml
index 214edf7e6487edcb6f3e4fb2f0b1537015b17212..cbae5d45357ead68b5ed875031f5e3eafb14a813 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ authors = [
     "Lukas Weichelt <lukas.weichelt@teckids.org>",
     "Michael Bauer <michael-bauer@posteo.de>"
 ]
-maintainers = ["Jonathan Weth <dev@jonathanweth.de>", "Dominik George <dominik.george@teckids.org>"]
+maintainers = ["Jonathan Weth <jonathan.weth@teckids.org>", "Dominik George <dominik.george@teckids.org>"]
 license = "EUPL-1.2-or-later"
 homepage = "https://aleksis.org"
 repository = "https://edugit.org/AlekSIS/official/AlekSIS-Core"
@@ -61,10 +61,10 @@ python = "^3.10"
 Django = "^5.1"
 django-any-js = "^1.1"  # Legacy
 django-tables2 = "^2.1"  # Legacy
-django-phonenumber-field = {version = "^7.0", extras = ["phonenumbers"]}
+django-phonenumber-field = {version = "^8.0", extras = ["phonenumbers"]}
 colour = "^0.1.5"
 dynaconf = {version = "^3.1", extras = ["yaml", "toml", "ini"]}
-django-auth-ldap = { version = "^4.0", optional = true }
+django-auth-ldap = { version = "^5.1", optional = true }
 django-maintenance-mode = "^0.21.0"
 django-ipware = "^7.0"
 django-impersonate = "^1.4"
@@ -103,11 +103,11 @@ django-prometheus = "^2.1.0"
 django-model-utils = "^5.0.0"
 bs4 = "^0.0.2"
 django-invitations = "^2.0.0"
-django-allauth = "^0.63.6"
+django-allauth = "^65.0.0"
 django-uwsgi-ng = "^2.0"
 django-extensions = "^3.1.1"
 ipython = "^8.0.0"
-django-oauth-toolkit = "^2.0.0"
+django-oauth-toolkit = "^3.0.0"
 django-storages = {version = "^1.14.2", optional = true}
 boto3 = {version = "^1.34.52", optional = true}
 django-cleanup = "^9.0.0"
@@ -122,7 +122,7 @@ django-iconify = "^0.4"
 customidenticon = "^0.1.5"
 graphene-django = ">=3.0.0, <=3.2.2"
 selenium = "^4.4.3"
-django-vite = "^3.0.0"
+django-vite = "^3.0.6"
 graphene-django-cud = "^0.13.0"
 django-ical = "^1.9.2"
 django-recurrence = "^1.11.1"
@@ -146,16 +146,16 @@ aleksis-admin = 'aleksis.core.__main__:aleksis_cmd'
 django-stubs = "^4.2"
 safety = "^2.3.5"
 
-ruff = "^0.1.5"
+ruff = "^0.8.2"
 
 [tool.poetry.group.test.dependencies]
-pytest = "^7.2"
-pytest-django = "^4.1"
+pytest = "^8.3"
+pytest-django = "^4.9"
 pytest-django-testing-postgresql = "^0.2"
-pytest-cov = "^4.0.0"
-pytest-sugar = "^0.9.2"
-selenium = "<4.10.0"
-freezegun = "^1.1.0"
+pytest-cov = "^6.0.0"
+pytest-sugar = "^1.0.0"
+selenium = "^4.27.0"
+freezegun = "^1.5.0"
 
 [tool.poetry.group.docs]
 optional = true
@@ -166,20 +166,20 @@ sphinxcontrib-django = "^2.3.0"
 sphinxcontrib-svg2pdfconverter = "^1.1.1"
 sphinx-autodoc-typehints = "^1.7"
 sphinx_material = "^0.0.35"
-
 [tool.ruff]
-exclude = ["migrations", "tests"]
+exclude = ["migrations"]
 line-length = 100
 
 [tool.ruff.lint]
 select = ["E", "F", "UP", "B", "SIM", "I", "DJ", "A", "S"]
 ignore = ["UP034", "UP015", "B028"]
-
-[tool.ruff.isort]
+[tool.ruff.lint.extend-per-file-ignores]
+"**/*/tests/**/*.py" = ["S101", "ARG", "FBT", "PLR2004", "S311", "S105", "S107"]
+[tool.ruff.lint.isort]
 known-first-party = ["aleksis"]
 section-order = ["future", "standard-library", "django", "third-party", "first-party", "local-folder"]
 
-[tool.ruff.isort.sections]
+[tool.ruff.lint.isort.sections]
 django = ["django"]
 [build-system]
 requires = ["poetry-core>=1.0.0"]