Skip to content
Commits on Source (767)
module.exports = {
extends: [
'plugin:vue/strongly-recommended',
"eslint:recommended",
"plugin:vue/strongly-recommended",
"prettier",
"plugin:@intlify/vue-i18n/recommended",
],
rules: {
'vue/no-unused-vars': 'off',
'vue/multi-word-component-names': 'off'
}
}
"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]+$",
},
],
},
settings: {
"vue-i18n": {
localeDir: "./aleksis/core/frontend/messages/*.{json}",
messageSyntaxVersion: "^8.0.0",
},
},
env: {
es2021: true,
},
parserOptions: {
ecmaVersion: "latest",
},
};
<!-- AlekSIS is developed on EduGit. GitHub only serves as
backup mirror and to help people find the project. If
possible, please submit your merge request on EduGit!
EduGit accepts logins with GitHub accounts.
-->
[ ] I have read the above and have no way to contribute on EduGit
[ ] I understand that GitHub's terms of service exclude young and
learning contributors, but still cannot contribute on EduGit
instead.
......@@ -82,3 +82,5 @@ yarn.lock
*.code-workspace
/cache
/node_modules
.vite
include:
- project: "AlekSIS/official/AlekSIS"
file: /ci/general.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/prepare/lock.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/test.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/lint.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/security.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/build/dist.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/publish/pypi.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/docker/image.yml
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/review.yml"
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/trigger_dist.yml"
- project: "AlekSIS/official/AlekSIS"
file: /ci/general.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/prepare/lock.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/test.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/lint.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/test/security.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/build/dist.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/publish/pypi.yml
- project: "AlekSIS/official/AlekSIS"
file: /ci/docker/image.yml
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/review.yml"
- project: "AlekSIS/official/AlekSIS"
file: "/ci/deploy/trigger_dist.yml"
# Byte-compiled / optimized / DLL files
*$py.class
*.py[cod]
__pycache__/
# Distribution / packaging
*.egg
*.egg-info/
.Python
.eggs/
.installed.cfg
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
# Installer logs
pip-delete-this-directory.txt
pip-log.txt
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# pyenv
.python-version
# Environments
.env
.venv
ENV/
env/
venv/
# Editors
*~
DEADJOE
\#*#
# IntelliJ
.idea
.idea/
# Database
db.sqlite3
# Sphinx
docs/_build/
# TeX
*.aux
# Generated files
/node_modules/
/static/
/whoosh_index/
poetry.lock
.coverage
.mypy_cache/
.tox/
htmlcov/
maintenance_mode_state.txt
media/
package-lock.json
yarn.lock
# VSCode
.vscode/
.history/
*.code-workspace
/cache
# Add HTML files to avoid problems with unsupported Django templates
*.html
# Do not check/reformat generated files
aleksis/core/util/licenses.json
.vite/
{
"extends": "stylelint-config-standard"
"extends": ["stylelint-config-standard", "stylelint-config-prettier"]
}
......@@ -6,28 +6,241 @@ All notable changes to this project will be documented in this file.
The format is based on `Keep a Changelog`_,
and this project adheres to `Semantic Versioning`_.
Unreleased
----------
`3.0`_ - 2022-05-11
-------------------
Added
~~~~~
Breaking changes
~~~~~~~~~~~~~~~~
* GraphQL schema for Rooms
* Provide API endpoint for system status.
* [Dev] UpdateIndicator Vue Component to display the status of interactive pages
* [Dev] DeleteDialog Vue Component to unify item deletion in the new frontend
* The frontend is being rewritten as a Vue.js application compeltely separated from
the backend. This requires changes to deployments. The admin docs have been updated
accordingly. Administrators need to make sure to read the updated guide before
upgrading AlekSIS-Core to this version!
Changed
~~~~~~~
* Show message on successful logout to inform users properly.
* Phone number country now has to be configured in config file insted of frontend.
Fixed
~~~~~
* GraphQL endpoints for groups, persons, and notifications didn't expose all necessary fields.
* Loading indicator in toolbar was not shown at the complete loading progress.
* 404 page was sometimes shown while the page was still loading.
* Setting of page height in the iframe was not working correctly.
* App switched to offline state when the user was logged out/in.
* The `Stop Impersonation` button is not shown due to an oversee when changing the type of the whoAmI query to an object of UserType
* Offline fallback page for legacy pages was misleading sometimes.
* Route changes in the Legacy-Component iframe didn't trigger a scroll to the top
* Query strings did not get passed when navigating legacy pages inside of the SPA.
* Retry button on error 500 page did not trigger a reload of the page.
* When the Celery worker wasn't able to execute all tasks in time, notifications were sent multiple times.
* Changing the maintenance mode state spawned another SPA instance in the iframe
* Phone numbers couldn't be in regional format.
* System status view wasn't accessible through new frontend if a check failed.
* Progress page didn't show error message on failure.
* Dynamic routes were not removed/hidden when the respective object registering it was deleted.
* Django messages were not displayed in Vue frontend.
* Links to data check objects did not work properly.
* Backend cleanup task for Celery wasn't working.
* URLs in invitation email were broken.
* Invitation view didn't work.
* Invitation emails were using wrong styling.
* GraphQL queries and mutations did not log exceptions.
`3.0b3`_ - 2023-03-19
---------------------
Fixed
~~~~~
* Some GraphQL queries could return more data than permitted in related fields.
`3.0b2`_ - 2023-03-09
---------------------
Changed
~~~~~~~
* Change default network policy of the Apollo client to `cache-and-network`.
Fixed
~~~~~
* In case the status code of a response was not in the range between 200 and 299
but still indicates that the response should be delivered, e. g. in the case
of a redirected request, the service worker served the offline fallback page.
* In some cases, the resize listener for the IFrame in the `LegacyBaseTemplate`
did not trigger.
* [Dev] Allow apps to declare URLs in the non-legacy namespace again
`3.0b1`_ - 2023-02-27
---------------------
Added
~~~~~
* Support for two factor authentication via email codes and Webauthn.
`3.0b0`_ - 2023-02-15
---------------------
This release starts a new era of the AlekSIS® framework, by introducing a
dynamic frontend app written in Vue.js which communicates with the backend
through GraphQL. Support for legacy views (Django templates and
Materialize) was removed; while there is backwards compatibility for now,
this is only used by official apps until their views are fully migrated.
AlekSIS and its new frontend require Node.js version 18 or higher to run the
Vite bundler. On Debian, this means that Debian 12 (bookworm) is needed, or
Node.js must be installed from a third-party repository.
Removed
~~~~~~~
* Official support for views rendered server-side in Django is removed. The
`LegacyBaseTemplate` provided for backwards compatibility must not be used
by apps declaring a dependency on AlekSIS >= 3.0.
* Support for deploying AlekSIS in sub-URLs
* Support for production deployments without HTTPS
Deprecated
~~~~~~~~~~
* The `webpack_bundle` management command is replaced by the new `vite`
command. The `webpack_bundle` command will be removed in AlekSIS-Core 4.0.
Added
~~~~~
* Notification drawer in top nav bar
* GraphQL queries for base system and some core data management
* [Dev] New mechanism to register classes over all apps (RegistryObject)
* Model for rooms
Changed
~~~~~~~
* Show languages in local language
* Rewrite of frontend (base template) using Vuetify
* Frontend bundling migrated from Webpack to Vite (cf. installation docs)
* [Dev] The runuwsgi dev server now starts a Vite dev server with HMR in the
background
* OIDC scope "profile" now exposes the avatar instead of the official photo
* Based on Django 4.0
* Use built-in Redis cache backend
* Introduce PBKDF2-SHA1 password hashing
* Persistent database connections are now health-checked as to not fail
requests
* [Dev] The undocumented field `check` on `DataCheckResult` was renamed to `data_check`
* Frontend bundling migrated from Webpack to Vite
* Get dashboard widgets and data checks from apps with new registration mechanism.
* Use write-through cache for sessions to retain on clear_cache
* Better error page with redirect option to login page when user has no permission to access a route.
* Users now can setup as many 2FA devices as they want.
* The 2FA profile overview was completely redesigned.
Fixed
~~~~~
* The system tried to send notifications for done background tasks
in addition to tasks started in the foreground
* 2FA via messages or phone calls didn't work after a faulty dependency
update
* [Dev] Site reference on extensible models can no longer cause name clashes
because of its related name
Removed
~~~~~~~
* iCal feed URLs for birthdays (will be reintroduced later)
* [Dev] Django debug toolbar
* It caused major performance issues and is not useful with the new
frontend anymore
`2.12.3`_ - 2023-03-07
----------------------
Fixed
~~~~~
* The permission check for the dashboard edit page failed when the user had no person assigned.
* OIDC scope "phone" had no claims.
* AlekSIS groups were not synced to Django groups on registration of existing persons
* Invitations for existing short name did not work.
* Invitations for persons without pre-defined e-mail address did not behave correctly
`2.12.2`_ - 2022-12-18
----------------------
Fixed
~~~~~
* Incorporate SPDX license list for app licenses on About page because
spdx-license-list dependency vanished.
`2.12.1`_ - 2022-11-06
----------------------
Fixed
~~~~~
* An invalid backport caused OIDC clients without PKCD to fail.
`2.12`_ - 2022-11-04
--------------------
Added
~~~~~
* Show also group ownerships on person detail page
* [Dev] Provide plain PDF template without header/footer for special layouts.
* [Dev] Introduce support for reformattinga and linting JS, Vue, and CSS files.
Changed
~~~~~~~
* OIDC scope "profile" now exposes the avatar instead of the official photo
* Language selection on Vue pages now runs via GraphQL queries.
* [Dev] Provide function to generate PDF files from fully-rendered templates.
* [Dev] Accept pre-created file object for PDF generation to define
the redirect URL in advance.
Fixed
~~~~~
* The logo in the PDF files was displayed at the wrong position.
* Sometimes the PDF files were not generated correctly
and images were displayed only partially.
* Error message in permission form was misleading.
* Personal invites did not work
* Invite Person view threw an error when personal invites existed
* Detailed information for done Celery tasks weren't saved.
`2.11`_ - 2022-08-27
--------------------
This release sunsets the 2.x series of the AleKSIS core.
Deprecated
~~~~~~~~~~
* All frontends using Django views and Django templates are deprecated and support
for them will be removed in AlekSIS-Core 4.0. All frontend code must be written in
for them will be removed in AlekSIS-Core 3.0. All frontend code must be written in
Vue.js and be properly separated from the backend. In the same spirit, all backend
features must expose GraphQL APIs for the frontendd to use
features must expose GraphQL APIs for the frontend to use.
Added
~~~~~
The following features are introduced here mainly to simplify gradual
updates. GraphQL and the Vuetify/Vue.js frontend mechanisms are preview
functionality and app developers should not rely on them before AlekSIS-Core
3.0.
* Introduce GraphQL API and Vue.js frontend implementation
* Introduce webpack bundling for frontend code
......@@ -104,6 +317,7 @@ Fixed
* Due to a merge error, the once removed account menu in the sidenav appeared again.
* Scheduled notifications were shown on dashboard before time.
* Remove broken notifications menu item in favor of item next to account menu.
* Serve OAuth discovery information under root of domain
* [OAuth2] Resources which are protected with client credentials
allowed access if no scopes were allowed (CVE-2022-29773).
* The site logo could overlap with the menu for logos with an unexpected aspect ratio.
......@@ -916,3 +1130,14 @@ Fixed
.. _2.10: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.10
.. _2.10.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.10.1
.. _2.10.2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.10.2
.. _2.11: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.11
.. _2.11.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.11.1
.. _2.12: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.12
.. _2.12.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.12.1
.. _2.12.2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.12.2
.. _2.12.3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.12.3
.. _3.0b0: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/3.0b0
.. _3.0b1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/3.0b1
.. _3.0b2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/3.0b2
.. _3.0b3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/3.0b3
.. _3.0: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/3.0
......@@ -2,7 +2,7 @@ FROM debian:bookworm-slim AS core
# Build arguments
ARG EXTRAS="ldap,s3,sentry"
ARG APP_VERSION="==2.10.1.dev0+20220801181456.7ba74939"
ARG APP_VERSION="==3.0b0"
# Configure Python to be nice inside Docker and pip to stfu
ENV PYTHONUNBUFFERED 1
......@@ -30,6 +30,7 @@ RUN apt-get -y update && \
eatmydata apt-get install -y --no-install-recommends \
build-essential \
chromium \
chromium-driver \
curl \
dumb-init \
gettext \
......@@ -63,6 +64,7 @@ RUN set -e; \
${ALEKSIS_static__root} \
${ALEKSIS_media__root} \
${ALEKSIS_backup__location}; \
dpkg-divert --rename --add /usr/lib/$(py3versions -d)/EXTERNALLY-MANAGED; \
eatmydata pip install AlekSIS-Core\[$EXTRAS\]$APP_VERSION
# Define entrypoint, volumes and uWSGI running on port 8000
......@@ -74,7 +76,7 @@ CMD ["/usr/local/bin/aleksis-docker-startup"]
# Install assets
FROM core as assets
RUN eatmydata aleksis-admin webpack_bundle; \
RUN eatmydata aleksis-admin vite build; \
eatmydata aleksis-admin collectstatic --no-input; \
rm -rf /usr/local/share/.cache
# FIXME Introduce deletion after we don't need materializecss anymore for SASS
......@@ -123,7 +125,7 @@ ONBUILD RUN set -e; \
if [ -n "$APPS" ]; then \
eatmydata pip install $APPS; \
fi; \
eatmydata aleksis-admin webpack_bundle; \
eatmydata aleksis-admin vite build; \
eatmydata aleksis-admin collectstatic --no-input; \
rm -rf /usr/local/share/.cache; \
eatmydata apt-get remove --purge -y yarnpkg $BUILD_DEPS; \
......
......@@ -61,16 +61,17 @@ Licence
::
Copyright © 2017, 2018, 2019, 2020, 2021, 2022 Jonathan Weth <dev@jonathanweth.de>
Copyright © 2017, 2018, 2019, 2020, 2021, 2022, 2023 Jonathan Weth <dev@jonathanweth.de>
Copyright © 2017, 2018, 2019, 2020 Frank Poetzsch-Heffter <p-h@katharineum.de>
Copyright © 2018, 2019, 2020, 2021, 2022 Hangzhi Yu <yuha@katharineum.de>
Copyright © 2018, 2019, 2020, 2021, 2022 Julian Leucker <leuckeju@katharineum.de>
Copyright © 2019, 2020, 2021, 2022 Dominik George <dominik.george@teckids.org>
Copyright © 2018, 2019, 2020, 2021, 2022, 2023 Hangzhi Yu <yuha@katharineum.de>
Copyright © 2018, 2019, 2020, 2021, 2022, 2023 Julian Leucker <leuckeju@katharineum.de>
Copyright © 2019, 2020, 2021, 2022, 2023 Dominik George <dominik.george@teckids.org>
Copyright © 2019, 2020, 2021, 2022 Tom Teichler <tom.teichler@teckids.org>
Copyright © 2019 mirabilos <thorsten.glaser@teckids.org>
Copyright © 2021, 2022, 2023 magicfelix <felix@felix-zauberer.de>
Copyright © 2021 Lloyd Meins <meinsll@katharineum.de>
Copyright © 2021 magicfelix <felix@felix-zauberer.de>
Copyright © 2022 Benedict Suska <benedict.suska@teckids.org>
Copyright © 2022 Lukas Weichelt <lukas.weichelt@teckids.org>
Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany).
......
......@@ -6,5 +6,3 @@ try:
__version__ = metadata.distribution("AlekSIS-Core").version
except Exception:
__version__ = "unknown"
default_app_config = "aleksis.core.apps.CoreConfig"
......@@ -3,6 +3,7 @@ from typing import Any, Optional
import django.apps
from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.http import HttpRequest
from django.utils.module_loading import autodiscover_modules
from django.utils.translation import gettext as _
......@@ -36,16 +37,17 @@ class CoreConfig(AppConfig):
}
licence = "EUPL-1.2+"
copyright_info = (
([2017, 2018, 2019, 2020, 2021, 2022], "Jonathan Weth", "wethjo@katharineum.de"),
([2017, 2018, 2019, 2020, 2021, 2022, 2023], "Jonathan Weth", "wethjo@katharineum.de"),
([2017, 2018, 2019, 2020], "Frank Poetzsch-Heffter", "p-h@katharineum.de"),
([2018, 2019, 2020, 2021, 2022], "Hangzhi Yu", "yuha@katharineum.de"),
([2018, 2019, 2020, 2021, 2022], "Julian Leucker", "leuckeju@katharineum.de"),
([2019, 2020, 2021, 2022], "Dominik George", "dominik.george@teckids.org"),
([2018, 2019, 2020, 2021, 2022, 2023], "Hangzhi Yu", "yuha@katharineum.de"),
([2018, 2019, 2020, 2021, 2022, 2023], "Julian Leucker", "leuckeju@katharineum.de"),
([2019, 2020, 2021, 2022, 2023], "Dominik George", "dominik.george@teckids.org"),
([2019, 2020, 2021, 2022], "Tom Teichler", "tom.teichler@teckids.org"),
([2019], "mirabilos", "thorsten.glaser@teckids.org"),
([2021, 2022, 2023], "magicfelix", "felix@felix-zauberer.de"),
([2021], "Lloyd Meins", "meinsll@katharineum.de"),
([2021], "magicfelix", "felix@felix-zauberer.de"),
([2022], "Benedict Suska", "benedict.suska@teckids.org"),
([2022], "Lukas Weichelt", "lukas.weichelt@teckids.org"),
)
def ready(self):
......@@ -64,8 +66,6 @@ class CoreConfig(AppConfig):
preference_models.register(personpreferencemodel, person_preferences_registry)
preference_models.register(grouppreferencemodel, group_preferences_registry)
self._load_data_checks()
from .health_checks import (
BackupJobHealthCheck,
DataChecksHealthCheckBackend,
......@@ -78,16 +78,6 @@ class CoreConfig(AppConfig):
plugin_dir.register(MediaBackupAgeHealthCheck)
plugin_dir.register(BackupJobHealthCheck)
@classmethod
def _load_data_checks(cls):
"""Get all data checks from all loaded models."""
from aleksis.core.data_checks import DataCheckRegistry
data_checks = set()
for model in apps.get_models():
data_checks.update(getattr(model, "data_checks", []))
DataCheckRegistry.data_checks = data_checks
def preference_updated(
self,
sender: Any,
......@@ -155,6 +145,11 @@ class CoreConfig(AppConfig):
# Save the associated person to pick up defaults
user.person.save()
def user_logged_out(
self, sender: type, request: Optional[HttpRequest], user: "User", **kwargs
) -> None:
messages.success(request, _("You have been logged out successfully."))
@classmethod
def get_all_scopes(cls) -> dict[str, str]:
scopes = {
......@@ -188,9 +183,9 @@ class CoreConfig(AppConfig):
claims["profile"] = django_request.build_absolute_uri(
request.user.person.get_absolute_url()
)
if request.user.person.photo:
if request.user.person.avatar:
claims["picture"] = django_request.build_absolute_uri(
request.user.person.photo.url
request.user.person.avatar.url
)
else:
claims["given_name"] = request.user.first_name
......@@ -211,6 +206,10 @@ class CoreConfig(AppConfig):
"postal_code": request.user.person.postal_code,
}
if "phone" in scopes and has_person(request.user):
claims["mobile_number"] = request.user.person.mobile_number
claims["phone_number"] = request.user.person.phone_number
if "groups" in scopes and has_person(request.user):
claims["groups"] = list(
request.user.person.member_of.values_list("name", flat=True).all()
......
import Vue from "vue"
import VueRouter from "vue-router"
import Vuetify from "vuetify"
import "vuetify/dist/vuetify.min.css"
import ApolloClient from 'apollo-boost'
import VueApollo from 'vue-apollo'
import "./css/global.scss"
Vue.use(Vuetify)
Vue.use(VueRouter)
const vuetify = new Vuetify({
// TODO: load theme data dynamically
// - find a way to load template context data
// - include all site preferences
// - load menu stuff to render the sidenav
icons: {
iconfont: 'mdi', // default - only for display purposes
values: {
cancel: 'mdi-close-circle-outline',
delete: 'mdi-close-circle-outline',
success: 'mdi-check-circle-outline',
info: 'mdi-information-outline',
warning: 'mdi-alert-outline',
error: 'mdi-alert-octagon-outline',
prev: 'mdi-chevron-left',
next: 'mdi-chevron-right',
checkboxOn: 'mdi-checkbox-marked-outline',
checkboxIndeterminate: 'mdi-minus-box-outline',
edit: 'mdi-pencil-outline',
},
},
theme: {
dark: JSON.parse(document.getElementById("design-mode").textContent) === "dark",
themes: {
light: {
primary: JSON.parse(document.getElementById("primary-color").textContent),
secondary: JSON.parse(document.getElementById("secondary-color").textContent),
},
dark: {
primary: JSON.parse(document.getElementById("primary-color").textContent),
secondary: JSON.parse(document.getElementById("secondary-color").textContent),
},
},
},
lang: {
locales: JSON.parse(document.getElementById("language-info-list").textContent),
current: JSON.parse(document.getElementById("current-language").textContent),
}
})
const apolloClient = new ApolloClient({
uri: JSON.parse(document.getElementById("graphql-url").textContent)
})
import CacheNotification from "./components/CacheNotification.vue";
import LanguageForm from "./components/LanguageForm.vue";
import MessageBox from "./components/MessageBox.vue";
import NotificationList from "./components/notifications/NotificationList.vue";
import SidenavSearch from "./components/SidenavSearch.vue";
Vue.component(MessageBox.name, MessageBox); // Load MessageBox globally as other components depend on it
Vue.use(VueApollo)
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
const router = new VueRouter({
mode: "history",
// routes: [
// { path: "/", component: "TheApp" },
// }
});
const app = new Vue({
el: '#app',
apolloProvider,
vuetify: vuetify,
// delimiters: ["<%","%>"] // FIXME: discuss new delimiters, [[ <% [{ {[ <[ (( …
data: () => ({
drawer: vuetify.framework.breakpoint.lgAndUp,
group: null, // what does this mean?
urls: window.Urls,
django: window.django,
// FIXME: maybe just use window.django in every component or find a suitable way to access this property everywhere
showCacheAlert: false,
languageCode: JSON.parse(document.getElementById("current-language").textContent),
}),
components: {
"cache-notification": CacheNotification,
"language-form": LanguageForm,
"notification-list": NotificationList,
"sidenav-search": SidenavSearch,
},
router
})
window.app = app;
window.router = router;
<template>
<message-box :value="cache" type="warning">
{{ this.$root.django.gettext('This page may contain outdated information since there is no internet connection.') }}
</message-box>
</template>
<script>
export default {
name: "cache-notification",
data () {
return {
cache: false,
}
},
created() {
this.channel = new BroadcastChannel("cache-or-not");
this.channel.onmessage = (event) => {
this.cache = event.data === true;
}
},
destroyed(){
this.channel.close()
},
}
</script>
<template>
<form method="post" ref="form" :action="action" id="language-form">
<v-text-field
v-show="false"
name="csrfmiddlewaretoken"
:value="csrf_value"
type="hidden"
></v-text-field>
<v-text-field
v-show="false"
name="next"
:value="next_url"
type="hidden"
></v-text-field>
<v-text-field
v-show="false"
v-model="language"
name="language"
type="hidden"
></v-text-field>
<v-menu offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn
depressed
v-bind="attrs"
v-on="on"
color="primary"
>
<v-icon icon color="white">mdi-translate</v-icon>
{{ language }}
</v-btn>
</template>
<v-list id="language-dropdown" class="dropdown-content">
<v-list-item-group
v-model="language"
color="primary"
>
<v-list-item v-for="language_option in items" :key="language_option[0]" :value="language_option[0]" @click="submit(language_option[0])">
<v-list-item-title>{{ language_option[1] }}</v-list-item-title>
</v-list-item>
</v-list-item-group>
</v-list>
</v-menu>
</form>
</template>
<script>
export default {
data: () => ({
items: JSON.parse(document.getElementById("language-info-list").textContent),
language: JSON.parse(document.getElementById("current-language").textContent),
}),
methods: {
submit: function (new_language) {
this.language = new_language;
this.$nextTick(() => {
this.$refs.form.submit();
});
},
},
props: ["action", "csrf_value", "next_url"],
name: "language-form",
}
</script>
<script>
export default {
name: "message-box",
// Due to this component being a wrapper to a v-alert, all props of this can be used (and overridden).
}
</script>
<template>
<v-alert border="left" text v-bind="$attrs">
<slot>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</slot>
</v-alert>
</template>
<script>
export default {
methods: {
submit: function () {
this.$refs.form.submit()
},
},
props: ["action", "placeholder"],
name: "sidenav-search",
}
// FIXME: implement suggestions etc, use "loading" attribute
</script>
<template>
<form method="get" ref="form" :action="action" id="search-form">
<v-text-field
:append-icon="'mdi-magnify'" @click:append="submit" single-line
id="search" name="q" type="search" enterkeyhint="search" :placeholder="placeholder"
></v-text-field>
</form>
</template>
<template>
<ApolloMutation
:mutation="require('./markNotificationRead.graphql')"
:variables="{ id: this.notification.id }"
>
<template v-slot="{ mutate, loading, error }">
<v-list-item
v-intersect="mutate"
>
<v-list-item-content>
<v-list-item-title>{{ notification.title }}</v-list-item-title>
<v-list-item-subtitle>
<v-icon>mdi-clock-outline</v-icon>
{{ notification.created }}
</v-list-item-subtitle>
<v-list-item-subtitle>
{{ notification.description }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action v-if="notification.link">
<v-btn text :href="notification.link">
{{ $root.django.gettext('More information →') }}
</v-btn>
</v-list-item-action>
<v-list-item-icon>
<v-chip color="primary">{{ notification.sender }}</v-chip>
</v-list-item-icon>
</v-list-item>
</template>
</ApolloMutation>
</template>
<script>
export default {
props: {
notification: Object,
},
}
</script>
<template>
<ApolloQuery
:query="require('./myNotifications.graphql')"
:pollInterval="1000"
>
<template v-slot="{ result: { error, data }, isLoading }">
<v-list two-line v-if="data && data.myNotifications.notifications.length">
<NotificationItem
v-for="notification in data.myNotifications.notifications"
:key="notification.id"
:notification="notification"
/>
</v-list>
<p v-else>{{ $root.django.gettext('No notifications available yet.') }}</p>
</template>
</ApolloQuery>
</template>
<script>
import NotificationItem from "./NotificationItem.vue";
export default {
components: {
NotificationItem,
},
}
</script>
{
myNotifications: whoAmI {
notifications {
id
title
description
link
created
sender
}
}
}
import '@mdi/font/css/materialdesignicons.css'
import "./util"
import "./app"