forked from digitales/kompass
Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
92706dbaae | 3 years ago |
|
|
04fea4fa4b | 3 years ago |
@ -1,21 +0,0 @@
|
|||||||
node {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
|
|
||||||
stages {
|
|
||||||
stage('Build') {
|
|
||||||
steps {
|
|
||||||
sh "make build-test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Test') {
|
|
||||||
steps {
|
|
||||||
sh "make test"
|
|
||||||
recordCoverage(tools: [[parser: 'COBERTURA', pattern: 'docker/test/coverage.xml']])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
build-test:
|
|
||||||
cd docker/test; docker compose build
|
|
||||||
|
|
||||||
test:
|
|
||||||
touch docker/test/coverage.xml
|
|
||||||
chmod 666 docker/test/coverage.xml
|
|
||||||
cd docker/test; docker compose up --abort-on-container-exit
|
|
||||||
sed -i 's/\/app\/jdav_web/jdav_web/g' docker/test/coverage.xml
|
|
||||||
@ -1 +0,0 @@
|
|||||||
GRANT ALL PRIVILEGES ON test_kompass.* TO 'kompass'@'%';
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
FROM python:3.9-bullseye
|
|
||||||
|
|
||||||
# install additional dependencies
|
|
||||||
RUN apt-get update && apt-get install -y gettext texlive texlive-fonts-extra
|
|
||||||
|
|
||||||
# create user
|
|
||||||
RUN groupadd -g 501 app && useradd -g 501 -u 501 -m -d /app app
|
|
||||||
|
|
||||||
# create static directory and set permissions, when doing this before mounting a named volume
|
|
||||||
# in docker-compose.yaml, the permissions are inherited during the mount.
|
|
||||||
RUN mkdir -p /var/www/jdav_web/static && chown -R app:app /var/www/jdav_web/static
|
|
||||||
|
|
||||||
# create static directory and set permissions, when doing this before mounting a named volume
|
|
||||||
# in docker-compose.yaml, the permissions are inherited during the mount.
|
|
||||||
RUN mkdir -p /tmp/uwsgi && chown -R app:app /tmp/uwsgi
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
USER app
|
|
||||||
|
|
||||||
# add .local/bin to PATH
|
|
||||||
ENV PATH="/app/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# install requirements
|
|
||||||
COPY --chown=app:app ./requirements.txt /app/requirements.txt
|
|
||||||
# we install uwsgi here to check if packages dependencies are resolved, but we don't actually
|
|
||||||
# need uwsgi in test
|
|
||||||
RUN pip install coverage uwsgi -r requirements.txt
|
|
||||||
|
|
||||||
COPY --chown=app:app . /app
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
version: "3.9"
|
|
||||||
|
|
||||||
services:
|
|
||||||
master:
|
|
||||||
image: kompass:test
|
|
||||||
build:
|
|
||||||
context: ./../../
|
|
||||||
dockerfile: docker/test/Dockerfile
|
|
||||||
env_file: docker.env
|
|
||||||
depends_on:
|
|
||||||
- redis
|
|
||||||
- cache
|
|
||||||
- db
|
|
||||||
entrypoint: /app/docker/test/entrypoint-master.sh
|
|
||||||
volumes:
|
|
||||||
- type: bind
|
|
||||||
source: ./coverage.xml
|
|
||||||
target: /app/jdav_web/coverage.xml
|
|
||||||
|
|
||||||
cache:
|
|
||||||
restart: always
|
|
||||||
image: memcached:alpine
|
|
||||||
|
|
||||||
redis:
|
|
||||||
restart: always
|
|
||||||
image: redis:6-alpine
|
|
||||||
|
|
||||||
db:
|
|
||||||
restart: always
|
|
||||||
image: mariadb
|
|
||||||
volumes:
|
|
||||||
- ./db:/var/lib/mysql
|
|
||||||
- ./provision/mysql/init:/docker-entrypoint-initdb.d
|
|
||||||
env_file: docker.env
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
DJANGO_ALLOWED_HOST='*'
|
|
||||||
DJANGO_BASE_URL='localhost:8000'
|
|
||||||
DJANGO_PROTOCOL='http'
|
|
||||||
|
|
||||||
EMAIL_HOST='localhost'
|
|
||||||
EMAIL_HOST_USER='test'
|
|
||||||
EMAIL_HOST_PASSWORD='password'
|
|
||||||
EMAIL_SENDING_ADDRESS='test@localhost'
|
|
||||||
|
|
||||||
DJANGO_DEPLOY=1
|
|
||||||
DJANGO_DEBUG=1
|
|
||||||
|
|
||||||
DJANGO_DATABASE_NAME='kompass'
|
|
||||||
DJANGO_DATABASE_USER='kompass'
|
|
||||||
DJANGO_DATABASE_PASSWORD='password'
|
|
||||||
DJANGO_DATABASE_HOST='db'
|
|
||||||
DJANGO_DATABASE_PORT=3306
|
|
||||||
|
|
||||||
MYSQL_ROOT_PASSWORD='secretpassword'
|
|
||||||
MYSQL_PASSWORD='password'
|
|
||||||
MYSQL_USER='kompass'
|
|
||||||
MYSQL_DATABASE='kompass'
|
|
||||||
|
|
||||||
DJANGO_SETTINGS_MODULE='jdav_web.settings'
|
|
||||||
DJANGO_STATIC_ROOT='/var/www/jdav_web/assets'
|
|
||||||
|
|
||||||
MEMCACHED_URL='cache:11211'
|
|
||||||
BROKER_URL='redis://redis:6379/0'
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
mysql_ready() {
|
|
||||||
cd /app/jdav_web
|
|
||||||
python << END
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.db import connections
|
|
||||||
from django.db.utils import OperationalError
|
|
||||||
|
|
||||||
db_conn = connections['default']
|
|
||||||
|
|
||||||
try:
|
|
||||||
c = db_conn.cursor()
|
|
||||||
except OperationalError:
|
|
||||||
sys.exit(-1)
|
|
||||||
else:
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
END
|
|
||||||
}
|
|
||||||
|
|
||||||
until mysql_ready; do
|
|
||||||
>&2 echo 'Waiting for MySQL to become available...'
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
>&2 echo 'MySQL is available'
|
|
||||||
|
|
||||||
cd /app
|
|
||||||
|
|
||||||
if ! [ -f /tmp/completed_initial_run ]; then
|
|
||||||
echo 'Initialising kompass master container'
|
|
||||||
|
|
||||||
python jdav_web/manage.py compilemessages --locale de
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd jdav_web
|
|
||||||
|
|
||||||
coverage run manage.py test startpage finance members -v 2
|
|
||||||
coverage xml
|
|
||||||
@ -1 +0,0 @@
|
|||||||
GRANT ALL PRIVILEGES ON test_kompass.* TO 'kompass'@'%';
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
# Minimal makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line, and also
|
|
||||||
# from the environment for the first two.
|
|
||||||
SPHINXOPTS ?=
|
|
||||||
SPHINXBUILD ?= sphinx-build
|
|
||||||
SOURCEDIR = source
|
|
||||||
BUILDDIR = build
|
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
|
||||||
help:
|
|
||||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
|
|
||||||
.PHONY: help Makefile
|
|
||||||
|
|
||||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
||||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
||||||
%: Makefile
|
|
||||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
|
|
||||||
pushd %~dp0
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
if "%SPHINXBUILD%" == "" (
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
)
|
|
||||||
set SOURCEDIR=source
|
|
||||||
set BUILDDIR=build
|
|
||||||
|
|
||||||
%SPHINXBUILD% >NUL 2>NUL
|
|
||||||
if errorlevel 9009 (
|
|
||||||
echo.
|
|
||||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
|
||||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
|
||||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
|
||||||
echo.may add the Sphinx directory to PATH.
|
|
||||||
echo.
|
|
||||||
echo.If you don't have Sphinx installed, grab it from
|
|
||||||
echo.https://www.sphinx-doc.org/
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:help
|
|
||||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
|
||||||
|
|
||||||
:end
|
|
||||||
popd
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# Configuration file for the Sphinx documentation builder.
|
|
||||||
#
|
|
||||||
# For the full list of built-in configuration values, see the documentation:
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
||||||
|
|
||||||
project = 'Kompass'
|
|
||||||
copyright = '2024, Christian Merten'
|
|
||||||
author = 'Christian Merten'
|
|
||||||
release = '2.0'
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
||||||
|
|
||||||
extensions = []
|
|
||||||
|
|
||||||
templates_path = ['_templates']
|
|
||||||
exclude_patterns = []
|
|
||||||
|
|
||||||
language = 'de'
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
|
||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
||||||
|
|
||||||
html_theme = 'alabaster'
|
|
||||||
html_static_path = ['_static']
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
.. _excursions:
|
|
||||||
|
|
||||||
Ausfahrten
|
|
||||||
==========
|
|
||||||
|
|
||||||
Neben der :ref:`Teilnehmer\*innenverwaltung <members>` ist das Abwickeln von Ausfahrten
|
|
||||||
die zweite wichtige Aufgabe des Kompass. Eine Ausfahrt für die eigene Jugendgruppe
|
|
||||||
anbieten ist neben der ganzen inhaltlichen Arbeit auch jede Menge bürokratischer Aufwand. Der Kompass
|
|
||||||
versucht dir von diesem Aufwand so viel wie möglich abzunehmen.
|
|
||||||
|
|
||||||
Konkret hilft dir der Kompass dabei
|
|
||||||
|
|
||||||
- Kriseninterventionslisten zu generieren
|
|
||||||
- Stadtjugendring oder Landesjugendplan Anträge zu erstellen
|
|
||||||
- Abrechnungen anzufertigen
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Diese Seite ist noch im Aufbau.
|
|
||||||
|
|
||||||
Stammdaten
|
|
||||||
----------
|
|
||||||
|
|
||||||
Sobald du mit deinen Co-Jugendleiter\*innen eine Ausfahrt angedacht hast, kannst du diese im Kompass `anlegen`_.
|
|
||||||
Die bekannten Informationen trägst du schon ein, die noch unbekannten lässt du leer oder trägst
|
|
||||||
vorläufige Daten ein.
|
|
||||||
|
|
||||||
Wenn du weißt wer mitkommt, trägst du im Tab *Teilnehmer\*innen* alle ein, die zur Ausfahrt kommen.
|
|
||||||
|
|
||||||
.. _crisis-intervention-list:
|
|
||||||
|
|
||||||
Kriseninterventionsliste
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Bevor die Ausfahrt stattfindet, lässt du dir eine Kriseninterventionsliste mit allen Notfallkontakten der
|
|
||||||
Mitfahrer\*innen erstellen und schickst sie an die Geschäftsstelle.
|
|
||||||
|
|
||||||
Landesjugendplanantrag
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Möchtest du einen Landesjugendplan- oder SJR Antrag stellen? Dann trage alle Informationen für den
|
|
||||||
Seminarbericht direkt ein und lass dir den Papierkram vom Kompass erledigen.
|
|
||||||
|
|
||||||
SJR Antrag
|
|
||||||
----------
|
|
||||||
|
|
||||||
Abrechnung
|
|
||||||
----------
|
|
||||||
|
|
||||||
Im Nachhinein trägst du deine Ausgaben ein, lädst Belege hoch und reichst deine Abrechnung per Knopfdruck ein.
|
|
||||||
|
|
||||||
.. _anlegen: https://jdav-hd.de/kompassmembers/freizeit/add/
|
|
||||||
.. _Teilnehmer\*innen: https://jdav-hd.de/kompassmembers/member/
|
|
||||||
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
Finanzen
|
|
||||||
========
|
|
||||||
|
|
||||||
Auf dieser Seite wird das Einreichen, Bearbeiten und Abwickeln von Abrechnungen
|
|
||||||
erklärt. Diese Seite ist für Finanzbeauftragte der Sektion gedacht und daher
|
|
||||||
für die meisten Benutzer\*innen des Kompass unwichtig.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Diese Seite ist noch im Aufbau.
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
.. _first-steps:
|
|
||||||
|
|
||||||
Erste Schritte
|
|
||||||
==============
|
|
||||||
|
|
||||||
Wenn du zum ersten Mal den Kompass deiner Sektion benutzt ist diese
|
|
||||||
Seite der richtige Einstieg. Wir verfolgen in dieser Anleitung den Jugendleiter
|
|
||||||
Fritz Walter bei seinen ersten Schritten mit seinem Kompass. Fritz Walter leitet
|
|
||||||
die Gruppe *Kletterfüchse*.
|
|
||||||
|
|
||||||
Wie finde ich die Teilnehmer\*innen meiner Jugendgruppe?
|
|
||||||
--------------------------------------------------------
|
|
||||||
|
|
||||||
Auf der `Startseite`_ siehst du eine Auflistung der von dir geleiteten Jugendgruppen.
|
|
||||||
Klickst du auf eine der Gruppen landest du in der `Teilnehmer\*innenanzeige`_.
|
|
||||||
|
|
||||||
.. image:: images/members_changelist_filters.png
|
|
||||||
|
|
||||||
Fritz hat die Gruppe *Kletterfüchse* ausgewählt, wie du oben rechts sehen kannst.
|
|
||||||
Versuche einmal dort bei dir eine andere Gruppe auszuwählen. Falls dir keine Teilnehmer\*innen
|
|
||||||
angezeigt werden liegt das daran, dass deine *Zugriffsrechte* nicht ausreichen.
|
|
||||||
|
|
||||||
Wie ändere ich eine\*n Teilnehmer\*in meiner Jugendgruppe?
|
|
||||||
----------------------------------------------------------
|
|
||||||
|
|
||||||
Fritz möchte das eingetragene Geburtsdatum von *Lisa Lotte* ändern. Dazu klickt
|
|
||||||
er auf den entsprechenden Eintrag, ändert das Geburtsdatum und klickt auf *Speichern*.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Nicht alle Einträge in der `Teilnehmer\*innenanzeige`_ sind klickbar. Das liegt daran,
|
|
||||||
dass du manche Teilnehmer\*innen zwar sehen, aber nicht ihre Details einsehen kannst.
|
|
||||||
Manche Einträge wiederum kannst du einsehen, aber nicht bearbeiten.
|
|
||||||
|
|
||||||
Probier doch einmal aus deinen eigenen Eintrag zu ändern. Sicherlich gibt es einige
|
|
||||||
Felder, die nicht ausgefüllt oder nicht mehr aktuell sind.
|
|
||||||
|
|
||||||
Wie schicke ich eine E-Mail an meine Gruppe?
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
Nachdem Fritz die Daten seiner Gruppe auf den neusten Stand gebracht hat, möchte er nun
|
|
||||||
eine E-Mail über die bevorstehende Hallenübernachtung an seine Gruppe schreiben. Dazu
|
|
||||||
geht er zurück auf die `Startseite`_ und wählt `Nachricht verfassen`_ aus.
|
|
||||||
|
|
||||||
Als Empfänger wählt er im Feld *An Gruppe* die *Kletterfüchse* aus. Damit seine
|
|
||||||
Co-Jugendleiterin Julia auch die Antworten erhält, wählt er im Feld
|
|
||||||
*Antwort an Teilnehmer* sowohl sich selbst, als auch Julia aus. Schließlich
|
|
||||||
klickt er auf *Speichern und Email senden*, um die Nachricht zu verschicken.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Es kann sein, dass über den Kompass verschickte E-Mails nur verzögert ankommen. Das
|
|
||||||
liegt daran, dass pro Minute stets nur 10 E-Mails verschickt werden um Stau
|
|
||||||
zu verhindern.
|
|
||||||
|
|
||||||
Probier doch mal aus dir selbst eine Nachricht zu schicken. Wähle einfach im Feld
|
|
||||||
*An Teilnehmer* dich selbst aus.
|
|
||||||
|
|
||||||
Wie organisiere ich eine Ausfahrt?
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Nun da Fritz seine Gruppe zur Hallenübernachtung eingeladen hat, möchte er die
|
|
||||||
Ausfahrt auch im Kompass anlegen. Dazu navigiert er zurück zur `Startseite`_ und wählt
|
|
||||||
`Ausfahrten`_ aus.
|
|
||||||
|
|
||||||
Dort wählt er oben rechts *Ausfahrt hinzufügen* aus und füllt die verschiedenen Felder
|
|
||||||
aus. Im Reiter *Teilnehmer* trägt er bereits Julia und sich selbst ein, die stehen ja
|
|
||||||
schließlich schon fest. Schließlich speichert er die Ausfahrt mit *Sichern*.
|
|
||||||
|
|
||||||
Wie geht es weiter?
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Nun hat Fritz den Bürokratiekram für heute erledigt. Du willst noch mehr wissen? Dann
|
|
||||||
geh zurück zum :ref:`index`.
|
|
||||||
|
|
||||||
.. _Startseite: https://jdav-hd.de/kompass
|
|
||||||
.. _Teilnehmer\*innenanzeige: https://jdav-hd.de/kompassmembers/member/
|
|
||||||
.. _Nachricht verfassen: https://jdav-hd.de/kompassmailer/message/add/
|
|
||||||
.. _Ausfahrten: https://jdav-hd.de/kompassmembers/freizeit/
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB |
@ -1,43 +0,0 @@
|
|||||||
.. Kompass documentation master file, created by
|
|
||||||
sphinx-quickstart on Sun Nov 24 18:37:20 2024.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
=======
|
|
||||||
Kompass
|
|
||||||
=======
|
|
||||||
|
|
||||||
Der Kompass ist dein Kompass in der Jugendarbeit in deiner JDAV Sektion. Wenn du das
|
|
||||||
erste mal hier bist, schau doch mal :ref:`first-steps` an.
|
|
||||||
|
|
||||||
Was ist der Kompass?
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Der Kompass ist eine Verwaltungsplattform für die tägliche Jugendarbeit in der JDAV.
|
|
||||||
Die wichtigsten Funktionen sind
|
|
||||||
|
|
||||||
- Verwaltung von Teilnehmer\*innen von Jugendgruppen
|
|
||||||
- Organisation von Ausfahrten
|
|
||||||
- Abwicklung von Abrechnungen
|
|
||||||
- Senden von E-Mails
|
|
||||||
|
|
||||||
Neben diesen Funktionen für die tägliche Arbeit, automatisiert der Kompass die
|
|
||||||
Aufnahme von neuen Mitgliedern und die Pflege der Daten durch
|
|
||||||
|
|
||||||
- Wartelistenverwaltung
|
|
||||||
- Registrierung neuer Mitglieder
|
|
||||||
- Rückmeldeverfahren
|
|
||||||
|
|
||||||
.. _index:
|
|
||||||
|
|
||||||
Inhaltsverzeichnis
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
getstarted
|
|
||||||
members
|
|
||||||
excursions
|
|
||||||
waitinglist
|
|
||||||
finance
|
|
||||||
@ -1,170 +0,0 @@
|
|||||||
.. _members:
|
|
||||||
|
|
||||||
Jugendgruppenverwaltung
|
|
||||||
=======================
|
|
||||||
|
|
||||||
Das wichtigste Objekt im Kompass ist ein\*e Teilnehmer\*in. Hier meint ein\*e Teilnehmer\*in ein im
|
|
||||||
Kompass hinterlegtes Mitglied der JDAV deiner Sektion, das heißt ob 5-jähriges Jugendgruppenkind,
|
|
||||||
langgediente\*r Jugendleiter\*in oder frischgebackene\*r Jugendreferent\*in, alle haben
|
|
||||||
einen Eintrag als Teilnehmer\*in im Kompass. Insbesondere heißt das, dass auch du selbst hier einen
|
|
||||||
Eintrag hast.
|
|
||||||
|
|
||||||
Der Startpunkt der Teilnehmer\*innenverwaltung ist der Abschnitt `Meine Jugendgruppe`_. Hier siehst du
|
|
||||||
in der Regel zwei Menüpunkte:
|
|
||||||
|
|
||||||
- Teilnehmer\*innen
|
|
||||||
- Ausfahrten
|
|
||||||
|
|
||||||
In diesem Abschnitt geht es nur um den ersten Menüpunkt. Falls du etwas über den zweiten Menüpunkt
|
|
||||||
lernen möchtest, kannst du zu :ref:`excursions` springen.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Falls du ein Amt in deiner Sektion ausübst und zum Beispiel für Jugendgruppenkoordination
|
|
||||||
oder die Verwaltung der Warteliste zuständig ist, siehst du hier noch mehr Punkte. Mehr
|
|
||||||
Informationen dazu findest du unter :ref:`waitinglist`.
|
|
||||||
|
|
||||||
Teilnehmer\*innen Übersicht
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
Um eine Übersicht über alle Teilnehmer\*innen zu bekommen, klicke auf `Teilnehmer\*innen`_. Hier siehst du
|
|
||||||
nun alle Mitglieder, für die du die einfachen Anzeigeberechtigungen hast, das heißt deren Namen du sehen darfst.
|
|
||||||
Typischerweise sind das die Gruppenkinder deiner Jugendgruppe, aber vielleicht noch zusätzlich alle Mitglieder
|
|
||||||
des Jugendausschuss.
|
|
||||||
|
|
||||||
Wie sehe ich meine Gruppenkinder?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Oberhalb der großen Auflistung mit allen Teilnehmer\*innen siehst du verschiedene Auswahlfelder.
|
|
||||||
Eines davon heißt *Nach Gruppe*. Wenn du dort drauf klickst, kannst du die Ansicht nach einer Gruppe
|
|
||||||
filtern.
|
|
||||||
|
|
||||||
.. image:: images/members_changelist_group_filter.png
|
|
||||||
|
|
||||||
In der selben Zeile siehst du noch weitere Filtermöglichkeiten.
|
|
||||||
|
|
||||||
Ich möchte nach Alter sortieren, wie geht das?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Standardmäßig ist die Teilnehmer\*innenanzeige nach Nachname sortiert, wie du im folgenden Bild an dem
|
|
||||||
kleinen Pfeil erkennen kannst:
|
|
||||||
|
|
||||||
.. image:: images/members_changelist_sorting.png
|
|
||||||
|
|
||||||
Um zum Beispiel nach Geburtsdatum zu sortieren, klicke auf die Spalte *Geburtsdatum*. Wenn du die Reihenfolge
|
|
||||||
(das heißt von jung nach alt oder von alt nach jung), klicke auf den kleinen Pfeil im *Geburtsdatum* Reiter.
|
|
||||||
|
|
||||||
Wieso sehe ich nicht alle meine Gruppenkinder?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Hast du deine Gruppe ausgewählt und siehst trotzdem nicht alle deine Gruppenkinder auf einer Seite?
|
|
||||||
Dann liegt das vermutlich daran, dass deine Gruppe mehr als 25 Teilnehmer\*innen hat. Chapeau!
|
|
||||||
In diesem Fall kannst du unten Rechts auf der Seite zwischen den verschiedenen Seiten auswählen oder
|
|
||||||
alle auf einmal anzeigen lassen:
|
|
||||||
|
|
||||||
.. image:: images/members_changelist_pages.png
|
|
||||||
|
|
||||||
.. _Meine Jugendgruppe: https://jdav-hd.de/kompassmembers
|
|
||||||
.. _Teilnehmer\*innen: https://jdav-hd.de/kompassmembers/member/
|
|
||||||
|
|
||||||
|
|
||||||
Teilnehmer\*in Detailansicht
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Möchtest du eine\*n Teilnehmer\*in im Detail ansehen, um zum Beispiel Personendaten, wie die Anschrift
|
|
||||||
nachzuschauen oder eine Änderung an den Daten machen, klicke auf den entsprechenden Eintrag in der Liste.
|
|
||||||
|
|
||||||
Die nun folgende Seite kann auf den ersten Blick ein wenig erschlagen, daher dröseln wir hier die wichtigsten
|
|
||||||
Punkte auf. Zunächst ist die Seite in mehrere Reiter unterteilt:
|
|
||||||
|
|
||||||
.. image:: images/members_change_tabs.png
|
|
||||||
|
|
||||||
Diese sind
|
|
||||||
|
|
||||||
- Allgemein: wichtigste Informationen wie Name und E-Mail Adresse
|
|
||||||
- Kontaktinformationen: Anschrift, Kontodaten (für Jugendleiter\*innen beim Abwickeln von Ausfahrten)
|
|
||||||
- Fähigkeiten: z.B. alpine Erfahrungen
|
|
||||||
- Sonstiges: z.B. medizinische Daten
|
|
||||||
- Notfallkontakte: Liste mit Namen und mindestens Telefonnummern. Mehr Informationen
|
|
||||||
unter :ref:`emergency-contacts`.
|
|
||||||
- Fortbildungen: eine Liste von besuchten Fortbildungen.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Der Reiter *Fortbildungen* wird nur auf deiner Seite angezeigt, das heißt falls du eines deiner
|
|
||||||
Gruppenkinder ausgewählt hast, ist dieser Reiter nicht vorhanden.
|
|
||||||
|
|
||||||
Wieso kann ich nicht alle Felder ändern?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Manche Felder werden dir nur angezeigt, sind aber nicht änderbar. Das sind entweder
|
|
||||||
|
|
||||||
- geschützte Felder, für die du besondere Berechtigungen benötigst um sie zu ändern
|
|
||||||
(z.B. das *Gruppe* Feld). Um diese Felder zu ändern, wende dich an deine\*n Jugendreferent\*in
|
|
||||||
für Jugendkoordination. Oder,
|
|
||||||
- automatisch berechnete Felder wie zum Beispiel das *Rückgemeldet* Feld.
|
|
||||||
|
|
||||||
Wieso haben manche Einträge in der Teilnehmer\*innenübersicht keinen Link?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Die Teilnehmer\*innen die dir in der Übersicht angezeigt werden sind diejenigen für die du
|
|
||||||
einfache Ansichtberechtigungen hast. Um die Personendetails eines\*einer Teilnehmer\*in einzusehen,
|
|
||||||
benötigst du normale Ansichtberechtigungen. Falls du diese nicht hast, wird anstatt des Links
|
|
||||||
in der Übersicht nur der Name angezeigt.
|
|
||||||
|
|
||||||
Falls du denkst, dass du eine\*n Teilnehmer\*in einsehen können solltest, aber es nicht kannst, melde
|
|
||||||
dich gerne bei deine\*r Jugendreferent\*in für Jugendkoordination.
|
|
||||||
|
|
||||||
.. _echo:
|
|
||||||
|
|
||||||
Rückmeldung
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Damit die Teilnehmer\*innendaten im Kompass aktuell bleiben, kannst du jederzeit deine Gruppenkinder
|
|
||||||
zu einer Rückmeldung auffordern. Dazu wählst du in der Teilnehmer\*innenübersicht alle
|
|
||||||
Teilnehmer\*innen aus, die du zur Rückmeldung auffordern möchtest,
|
|
||||||
|
|
||||||
.. image:: images/members_changelist_action.png
|
|
||||||
|
|
||||||
und wählst dann im Menü unten links *Rückmeldungsaufforderungen an ausgewählte Teilnehmer\*innen verschicken*
|
|
||||||
aus. Um die Aufforderungen zu verschicken, musst du dann nur noch auf *Ausführen* klicken.
|
|
||||||
|
|
||||||
Was passiert nach der Aufforderung zur Rückmeldung?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Der\*die ausgewählte Teilnehmer\*in erhält eine E-Mail mit einem Link. Dieser Link führt auf eine
|
|
||||||
Seite auf der die Person ihr Geburtsdatum eingeben muss.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Das Geburtsdatumsformat ist TT.MM.JJJJ, also wenn Peter am
|
|
||||||
1.4.1999 geboren ist, müsste er *01.04.1999* eingeben.
|
|
||||||
|
|
||||||
Nach erfolgreich eingegebenem Geburtsdatum, wird die Person auf ein Formular mit ihren Daten weitergeleitet.
|
|
||||||
Dann einfach prüfen, gegebenenfalls aktualisieren und schließlich speichern. Der Link ist
|
|
||||||
immer 30 Tage lang gültig und kann in dieser Zeit auch beliebig oft benutzt werden.
|
|
||||||
|
|
||||||
Klingt alles noch abstrakt? Dann fordere doch mal dich selbst zur Rückmeldung auf und probiere es aus.
|
|
||||||
|
|
||||||
.. _emergency-contacts:
|
|
||||||
|
|
||||||
Notfallkontakte
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Im Notfall helfen uns die Anschrift oder Telefonnummer einer\*eines Teilnehmer\*in nicht weiter. Stattdessen
|
|
||||||
benötigen wir Kontaktdaten von Personen, die wir im Notfall kontaktieren können. Diese können
|
|
||||||
im Reiter *Notfallkontakte* gepflegt werden. Bei der initialen Registrierung muss jede\*r Teilnehmer\*in
|
|
||||||
mindestens einen Notfallkontakt angeben.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Bei vielen Teilnehmer\*innen sind keine Notfallkontakte eingetragen. Das liegt dann vermutlich daran,
|
|
||||||
dass die aus einem anderen System migriert wurden und daher nicht verfügbar sind.
|
|
||||||
|
|
||||||
Bei der regelmäßigen :ref:`echo` werden die Notfallkontakte ebenfalls abgefragt. Falls
|
|
||||||
du bei einem deiner Gruppenkinder feststellst, dass die Notfallkontakte fehlen
|
|
||||||
oder nicht mehr aktuell sind, trage das so schnell wie möglich nach oder benutze die :ref:`echo`.
|
|
||||||
|
|
||||||
Was bringen mir die Notfallkontakte im Kompass?
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Passiert ein Notfall auf einer Ausfahrt, wirst du natürlich nicht immer die Möglichkeit
|
|
||||||
haben im Kompass die Notfallkontakte herauszusuchen. Daher kannst du dir zu jeder Ausfahrt
|
|
||||||
eine :ref:`crisis-intervention-list` generieren lassen, die zu allen Teilnehmer\*innen deiner Ausfahrt
|
|
||||||
auch alle Notfallkontakte auflistet.
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
.. _waitinglist:
|
|
||||||
|
|
||||||
Warteliste und neue Mitglieder
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Hier wird die Warteliste erklärt und verschiedene Möglichkeiten erläutert, wie
|
|
||||||
neue Teilnehmer\*innen angelegt werden können.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
Diese Seite ist noch im Aufbau.
|
|
||||||
@ -1,226 +0,0 @@
|
|||||||
import copy
|
|
||||||
from django.contrib.auth import get_permission_codename
|
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
|
||||||
from django.contrib import admin, messages
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
|
||||||
from django.urls import path, reverse
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib.admin import helpers, widgets
|
|
||||||
import rules.contrib.admin
|
|
||||||
from rules.permissions import perm_exists
|
|
||||||
|
|
||||||
|
|
||||||
class FieldPermissionsAdminMixin:
|
|
||||||
field_change_permissions = {}
|
|
||||||
field_view_permissions = {}
|
|
||||||
|
|
||||||
def may_view_field(self, field_desc, request, obj=None):
|
|
||||||
if not type(field_desc) is tuple:
|
|
||||||
field_desc = (field_desc,)
|
|
||||||
for fd in field_desc:
|
|
||||||
if fd not in self.field_view_permissions:
|
|
||||||
continue
|
|
||||||
if not request.user.has_perm(self.field_view_permissions[fd]):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
|
||||||
fieldsets = super(FieldPermissionsAdminMixin, self).get_fieldsets(request, obj)
|
|
||||||
d = []
|
|
||||||
for title, attrs in fieldsets:
|
|
||||||
allowed = [f for f in attrs['fields'] if self.may_view_field(f, request, obj)]
|
|
||||||
if len(allowed) == 0:
|
|
||||||
continue
|
|
||||||
d.append((title, dict(attrs, **{'fields': allowed})))
|
|
||||||
return d
|
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
|
||||||
fields = super(FieldPermissionsAdminMixin, self).get_fields(request, obj)
|
|
||||||
return [fd for fd in fields if self.may_view_field(fd, request, obj)]
|
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
|
||||||
readonly_fields = super(FieldPermissionsAdminMixin, self).get_readonly_fields(request, obj)
|
|
||||||
return list(readonly_fields) +\
|
|
||||||
[fd for fd, perm in self.field_change_permissions.items() if not request.user.has_perm(perm)]
|
|
||||||
|
|
||||||
|
|
||||||
class ChangeViewAdminMixin:
|
|
||||||
def change_view(self, request, object_id, form_url="", extra_context=None):
|
|
||||||
try:
|
|
||||||
return super(ChangeViewAdminMixin, self).change_view(request, object_id,
|
|
||||||
form_url=form_url,
|
|
||||||
extra_context=extra_context)
|
|
||||||
except PermissionDenied:
|
|
||||||
opts = self.opts
|
|
||||||
obj = self.model.objects.get(pk=object_id)
|
|
||||||
messages.error(request,
|
|
||||||
_("You are not allowed to view %(name)s.") % {'name': str(obj)})
|
|
||||||
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (opts.app_label, opts.model_name)))
|
|
||||||
|
|
||||||
|
|
||||||
class FilteredQuerysetAdminMixin:
|
|
||||||
def get_queryset(self, request):
|
|
||||||
"""
|
|
||||||
Return a QuerySet of all model instances that can be edited by the
|
|
||||||
admin site. This is used by changelist_view.
|
|
||||||
"""
|
|
||||||
qs = self.model._default_manager.get_queryset()
|
|
||||||
ordering = self.get_ordering(request)
|
|
||||||
if ordering:
|
|
||||||
qs = qs.order_by(*ordering)
|
|
||||||
queryset = qs
|
|
||||||
list_global_perm = '%s.list_global_%s' % (self.opts.app_label, self.opts.model_name)
|
|
||||||
if request.user.has_perm(list_global_perm):
|
|
||||||
view_global_perm = '%s.view_global_%s' % (self.opts.app_label, self.opts.model_name)
|
|
||||||
if request.user.has_perm(view_global_perm):
|
|
||||||
return queryset
|
|
||||||
if hasattr(request.user, 'member'):
|
|
||||||
return request.user.member.annotate_view_permission(queryset, model=self.model)
|
|
||||||
return queryset.annotate(_viewable=models.Value(False))
|
|
||||||
|
|
||||||
if not hasattr(request.user, 'member'):
|
|
||||||
return self.model.objects.none()
|
|
||||||
|
|
||||||
return request.user.member.filter_queryset_by_permissions(queryset, annotate=True, model=self.model)
|
|
||||||
|
|
||||||
#class ObjectPermissionsInlineModelAdminMixin(rules.contrib.admin.ObjectPermissionsInlineModelAdminMixin):
|
|
||||||
|
|
||||||
class CommonAdminMixin(FieldPermissionsAdminMixin, ChangeViewAdminMixin, FilteredQuerysetAdminMixin):
|
|
||||||
def has_add_permission(self, request, obj=None):
|
|
||||||
assert obj is None
|
|
||||||
opts = self.opts
|
|
||||||
codename = get_permission_codename("add_global", opts)
|
|
||||||
perm = "%s.%s" % (opts.app_label, codename)
|
|
||||||
return request.user.has_perm(perm, obj)
|
|
||||||
|
|
||||||
def has_view_permission(self, request, obj=None):
|
|
||||||
opts = self.opts
|
|
||||||
if obj is None:
|
|
||||||
codename = get_permission_codename("view", opts)
|
|
||||||
else:
|
|
||||||
codename = get_permission_codename("view_obj", opts)
|
|
||||||
perm = "%s.%s" % (opts.app_label, codename)
|
|
||||||
if perm_exists(perm):
|
|
||||||
return request.user.has_perm(perm, obj)
|
|
||||||
else:
|
|
||||||
return self.has_change_permission(request, obj)
|
|
||||||
|
|
||||||
def has_change_permission(self, request, obj=None):
|
|
||||||
opts = self.opts
|
|
||||||
if obj is None:
|
|
||||||
codename = get_permission_codename("view", opts)
|
|
||||||
else:
|
|
||||||
codename = get_permission_codename("change_obj", opts)
|
|
||||||
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
|
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
|
||||||
opts = self.opts
|
|
||||||
if obj is None:
|
|
||||||
codename = get_permission_codename("delete_global", opts)
|
|
||||||
else:
|
|
||||||
codename = get_permission_codename("delete_obj", opts)
|
|
||||||
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
|
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, request, **kwargs):
|
|
||||||
"""
|
|
||||||
COPIED from django to disable related actions
|
|
||||||
|
|
||||||
Hook for specifying the form Field instance for a given database Field
|
|
||||||
instance.
|
|
||||||
|
|
||||||
If kwargs are given, they're passed to the form Field's constructor.
|
|
||||||
"""
|
|
||||||
# If the field specifies choices, we don't need to look for special
|
|
||||||
# admin widgets - we just need to use a select widget of some kind.
|
|
||||||
if db_field.choices:
|
|
||||||
return self.formfield_for_choice_field(db_field, request, **kwargs)
|
|
||||||
|
|
||||||
# ForeignKey or ManyToManyFields
|
|
||||||
if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
|
|
||||||
# Combine the field kwargs with any options for formfield_overrides.
|
|
||||||
# Make sure the passed in **kwargs override anything in
|
|
||||||
# formfield_overrides because **kwargs is more specific, and should
|
|
||||||
# always win.
|
|
||||||
if db_field.__class__ in self.formfield_overrides:
|
|
||||||
kwargs = {**self.formfield_overrides[db_field.__class__], **kwargs}
|
|
||||||
|
|
||||||
# Get the correct formfield.
|
|
||||||
if isinstance(db_field, models.ForeignKey):
|
|
||||||
formfield = self.formfield_for_foreignkey(db_field, request, **kwargs)
|
|
||||||
elif isinstance(db_field, models.ManyToManyField):
|
|
||||||
formfield = self.formfield_for_manytomany(db_field, request, **kwargs)
|
|
||||||
|
|
||||||
# For non-raw_id fields, wrap the widget with a wrapper that adds
|
|
||||||
# extra HTML -- the "add other" interface -- to the end of the
|
|
||||||
# rendered output. formfield can be None if it came from a
|
|
||||||
# OneToOneField with parent_link=True or a M2M intermediary.
|
|
||||||
#if formfield and db_field.name not in self.raw_id_fields:
|
|
||||||
# formfield.widget = widgets.RelatedFieldWidgetWrapper(
|
|
||||||
# formfield.widget,
|
|
||||||
# db_field.remote_field,
|
|
||||||
# self.admin_site,
|
|
||||||
# )
|
|
||||||
|
|
||||||
return formfield
|
|
||||||
|
|
||||||
# If we've got overrides for the formfield defined, use 'em. **kwargs
|
|
||||||
# passed to formfield_for_dbfield override the defaults.
|
|
||||||
for klass in db_field.__class__.mro():
|
|
||||||
if klass in self.formfield_overrides:
|
|
||||||
kwargs = {**copy.deepcopy(self.formfield_overrides[klass]), **kwargs}
|
|
||||||
return db_field.formfield(**kwargs)
|
|
||||||
|
|
||||||
# For any other type of field, just call its formfield() method.
|
|
||||||
return db_field.formfield(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class CommonAdminInlineMixin(CommonAdminMixin):
|
|
||||||
def has_add_permission(self, request, obj):
|
|
||||||
#assert obj is not None
|
|
||||||
if obj is None:
|
|
||||||
return True
|
|
||||||
if obj.pk is None:
|
|
||||||
return True
|
|
||||||
codename = get_permission_codename("add_obj", self.opts)
|
|
||||||
return request.user.has_perm('%s.%s' % (self.opts.app_label, codename), obj)
|
|
||||||
|
|
||||||
def has_view_permission(self, request, obj=None): # pragma: no cover
|
|
||||||
if obj is None:
|
|
||||||
return True
|
|
||||||
if obj.pk is None:
|
|
||||||
return True
|
|
||||||
opts = self.opts
|
|
||||||
if obj is None:
|
|
||||||
codename = get_permission_codename("view", opts)
|
|
||||||
else:
|
|
||||||
codename = get_permission_codename("view_obj", opts)
|
|
||||||
perm = "%s.%s" % (opts.app_label, codename)
|
|
||||||
if perm_exists(perm):
|
|
||||||
return request.user.has_perm(perm, obj)
|
|
||||||
else:
|
|
||||||
return self.has_change_permission(request, obj)
|
|
||||||
|
|
||||||
def has_change_permission(self, request, obj=None): # pragma: no cover
|
|
||||||
if obj is None:
|
|
||||||
return True
|
|
||||||
if obj.pk is None:
|
|
||||||
return True
|
|
||||||
opts = self.opts
|
|
||||||
if opts.auto_created:
|
|
||||||
for field in opts.fields:
|
|
||||||
if field.rel and field.rel.to != self.parent_model:
|
|
||||||
opts = field.rel.to._meta
|
|
||||||
break
|
|
||||||
codename = get_permission_codename("change_obj", opts)
|
|
||||||
return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj)
|
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None): # pragma: no cover
|
|
||||||
if obj is None:
|
|
||||||
return True
|
|
||||||
if obj.pk is None:
|
|
||||||
return True
|
|
||||||
if self.opts.auto_created:
|
|
||||||
return self.has_change_permission(request, obj)
|
|
||||||
return super().has_delete_permission(request, obj)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ContribConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'contrib'
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
from rules.contrib.models import RulesModelBase, RulesModelMixin
|
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
class CommonModel(models.Model, RulesModelMixin, metaclass=RulesModelBase):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
default_permissions = (
|
|
||||||
'add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view',
|
|
||||||
)
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
from django.contrib.auth import get_permission_codename
|
|
||||||
import rules.contrib.admin
|
|
||||||
import rules
|
|
||||||
|
|
||||||
def memberize_user(func):
|
|
||||||
def inner(user, other):
|
|
||||||
if not hasattr(user, 'member'):
|
|
||||||
return False
|
|
||||||
return func(user.member, other)
|
|
||||||
return inner
|
|
||||||
|
|
||||||
|
|
||||||
def has_global_perm(name):
|
|
||||||
@rules.predicate
|
|
||||||
def pred(user, obj):
|
|
||||||
return user.has_perm(name)
|
|
||||||
|
|
||||||
return pred
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
from django import template
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
# settings value
|
|
||||||
@register.simple_tag
|
|
||||||
def settings_value(name):
|
|
||||||
return getattr(settings, name, "")
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.shortcuts import render
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-03 20:34
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
#replaces = [('finance', '0002_billonexcursionproxy_billonstatementproxy_and_more'), ('finance', '0003_alter_statementunsubmitted_options'), ('finance', '0004_alter_billonexcursionproxy_options'), ('finance', '0005_alter_billonstatementproxy_options'), ('finance', '0006_alter_statementsubmitted_options'), ('finance', '0007_alter_billonexcursionproxy_options_and_more')]
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('finance', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statementsubmitted',
|
|
||||||
options={'permissions': [('process_statementsubmitted', 'Can manage submitted statements.')], 'verbose_name': 'Submitted statement', 'verbose_name_plural': 'Submitted statements'},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='BillOnExcursionProxy',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Bill',
|
|
||||||
'verbose_name_plural': 'Bills',
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('finance.bill',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='BillOnStatementProxy',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Bill',
|
|
||||||
'verbose_name_plural': 'Bills',
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('finance.bill',),
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statementunsubmitted',
|
|
||||||
options={'verbose_name': 'Statement in preparation', 'verbose_name_plural': 'Statements in preparation'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-04 12:37
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('finance', '0002_alter_permissions'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='bill',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Bill', 'verbose_name_plural': 'Bills'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='billonexcursionproxy',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Bill', 'verbose_name_plural': 'Bills'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='billonstatementproxy',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Bill', 'verbose_name_plural': 'Bills'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statement',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': [('may_edit_submitted_statements', 'Is allowed to edit submitted statements')], 'verbose_name': 'Statement', 'verbose_name_plural': 'Statements'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statementconfirmed',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': [('may_manage_confirmed_statements', 'Can view and manage confirmed statements.')], 'verbose_name': 'Paid statement', 'verbose_name_plural': 'Paid statements'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statementsubmitted',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': [('process_statementsubmitted', 'Can manage submitted statements.')], 'verbose_name': 'Submitted statement', 'verbose_name_plural': 'Submitted statements'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='statementunsubmitted',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Statement in preparation', 'verbose_name_plural': 'Statements in preparation'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-12-02 00:22
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('finance', '0003_alter_bill_options_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='bill',
|
|
||||||
name='amount',
|
|
||||||
field=models.DecimalField(decimal_places=2, default=0, max_digits=6, verbose_name='Amount'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
from members.models import Freizeit
|
|
||||||
from contrib.rules import memberize_user
|
|
||||||
from rules import predicate
|
|
||||||
from members.rules import _is_leader
|
|
||||||
|
|
||||||
|
|
||||||
@predicate
|
|
||||||
@memberize_user
|
|
||||||
def is_creator(self, statement):
|
|
||||||
assert statement is not None
|
|
||||||
return statement.created_by == self
|
|
||||||
|
|
||||||
|
|
||||||
@predicate
|
|
||||||
@memberize_user
|
|
||||||
def not_submitted(self, statement):
|
|
||||||
assert statement is not None
|
|
||||||
if isinstance(statement, Freizeit):
|
|
||||||
if hasattr(statement, 'statement'):
|
|
||||||
return not statement.statement.submitted
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
return not statement.submitted
|
|
||||||
|
|
||||||
|
|
||||||
@predicate
|
|
||||||
@memberize_user
|
|
||||||
def leads_excursion(self, statement):
|
|
||||||
assert statement is not None
|
|
||||||
if isinstance(statement, Freizeit):
|
|
||||||
return _is_leader(self, statement)
|
|
||||||
if not hasattr(statement, 'excursion'):
|
|
||||||
return False
|
|
||||||
if statement.excursion is None:
|
|
||||||
return False
|
|
||||||
return _is_leader(self, statement.excursion)
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{% extends "admin/base_site.html" %}
|
|
||||||
{% load i18n admin_urls static %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
|
||||||
{{ block.super }}
|
|
||||||
{{ media }}
|
|
||||||
<script src="{% static 'admin/js/cancel.js' %}" async></script>
|
|
||||||
<script type="text/javascript" src="{% static "admin/js/vendor/jquery/jquery.js" %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static "admin/js/jquery.init.js" %}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} admin-view
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
|
||||||
<div class="breadcrumbs">
|
|
||||||
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
|
|
||||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
|
||||||
› <a href="{% url opts|admin_urlname:'change' statement.pk|admin_urlquote %}">{{ statement|truncatewords:"18" }}</a>
|
|
||||||
› {% translate 'Unconfirm' %}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<h2>{% translate "Unconfirm statement" %}</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{% blocktrans %}You are entering risk zone! Do you really want to manually set this statement back to unconfirmed?{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form action="" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<p>
|
|
||||||
<input type="checkbox" required>
|
|
||||||
{% blocktrans %}I am aware that this is not a standard procedure and this might cause data integrity issues.{% endblocktrans %}
|
|
||||||
</p>
|
|
||||||
<input class="default danger" type="submit" name="unconfirm" value="{% translate 'Unconfirm' %}">
|
|
||||||
<a class="button cancel-link" href="{% add_preserved_filters change_url %}">{% trans 'Cancel' %}</a>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 69133f184f0f9b53a3b0a39f91e8eba99698cabc
|
Subproject commit 6cf14c28de9bfefc28c0c296d87e52dc098f98b8
|
||||||
@ -1,52 +0,0 @@
|
|||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin as BaseAuthGroupAdmin
|
|
||||||
from django.contrib.auth.models import User as BaseUser, Group as BaseAuthGroup
|
|
||||||
from .models import AuthGroup, LoginDatum, RegistrationPassword
|
|
||||||
from members.models import Member
|
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
class AuthGroupAdmin(BaseAuthGroupAdmin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UserInline(admin.StackedInline):
|
|
||||||
model = Member
|
|
||||||
can_delete = False
|
|
||||||
verbose_name_plural = "member"
|
|
||||||
|
|
||||||
|
|
||||||
class LoginDatumAdmin(BaseUserAdmin):
|
|
||||||
list_display = ('username', 'is_superuser')
|
|
||||||
#inlines = [UserInline]
|
|
||||||
fieldsets = (
|
|
||||||
(None, {"fields": ("username", "password")}),
|
|
||||||
(
|
|
||||||
_("Permissions"),
|
|
||||||
{
|
|
||||||
"fields": (
|
|
||||||
"is_active",
|
|
||||||
"is_staff",
|
|
||||||
"is_superuser",
|
|
||||||
"groups",
|
|
||||||
"user_permissions",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
|
||||||
)
|
|
||||||
add_fieldsets = (
|
|
||||||
(
|
|
||||||
None,
|
|
||||||
{
|
|
||||||
"classes": ("wide",),
|
|
||||||
"fields": ("username", "password1", "password2"),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
admin.site.unregister(BaseUser)
|
|
||||||
admin.site.unregister(BaseAuthGroup)
|
|
||||||
admin.site.register(LoginDatum, LoginDatumAdmin)
|
|
||||||
admin.site.register(AuthGroup, AuthGroupAdmin)
|
|
||||||
admin.site.register(RegistrationPassword)
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
|
|
||||||
class LoginDataConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'logindata'
|
|
||||||
verbose_name = _('Authentication')
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-11-23 21:15
|
|
||||||
|
|
||||||
import django.contrib.auth.models
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('auth', '0012_alter_user_first_name_max_length'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='RegistrationPassword',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('password', models.CharField(max_length=100, verbose_name='Password')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='AuthGroup',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Permission group',
|
|
||||||
'verbose_name_plural': 'Permission groups',
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('auth.group',),
|
|
||||||
managers=[
|
|
||||||
('objects', django.contrib.auth.models.GroupManager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='LoginDatum',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Login Datum',
|
|
||||||
'verbose_name_plural': 'Login Data',
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('auth.user',),
|
|
||||||
managers=[
|
|
||||||
('objects', django.contrib.auth.models.UserManager()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-11-24 00:36
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('logindata', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='registrationpassword',
|
|
||||||
options={'verbose_name': 'Active registration password', 'verbose_name_plural': 'Active registration passwords'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.db import models
|
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin, GroupAdmin as BaseAuthGroupAdmin
|
|
||||||
from django.contrib.auth.models import User as BaseUser, Group as BaseAuthGroup
|
|
||||||
|
|
||||||
|
|
||||||
class AuthGroup(BaseAuthGroup):
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
verbose_name = _('Permission group')
|
|
||||||
verbose_name_plural = _('Permission groups')
|
|
||||||
|
|
||||||
|
|
||||||
class LoginDatum(BaseUser):
|
|
||||||
class Meta:
|
|
||||||
proxy = True
|
|
||||||
verbose_name = _('Login Datum')
|
|
||||||
verbose_name_plural = _('Login Data')
|
|
||||||
|
|
||||||
|
|
||||||
class RegistrationPassword(models.Model):
|
|
||||||
"""
|
|
||||||
A password that can be used to register after inviting a member.
|
|
||||||
"""
|
|
||||||
password = models.CharField(max_length=100, verbose_name=_('Password'))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.password
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Active registration password')
|
|
||||||
verbose_name_plural = _('Active registration passwords')
|
|
||||||
|
|
||||||
def initial_user_setup(user, member):
|
|
||||||
try:
|
|
||||||
standard_group = AuthGroup.objects.get(name='Standard')
|
|
||||||
except AuthGroup.DoesNotExist:
|
|
||||||
return False
|
|
||||||
|
|
||||||
user.is_staff = True
|
|
||||||
user.save()
|
|
||||||
user.groups.add(standard_group)
|
|
||||||
member.user = user
|
|
||||||
member.invite_as_user_key = ''
|
|
||||||
member.save()
|
|
||||||
return True
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{% extends "members/base.html" %}
|
|
||||||
{% load i18n static common %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans "Registration" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Set login data" %}</h1>
|
|
||||||
|
|
||||||
<p>{% trans "Something went wrong. The registration key is invalid or has expired." %}</p>
|
|
||||||
|
|
||||||
<p>{% trans "If you think this is a mistake, please" %} <a href="mailto:{% settings_value 'RESPONSIBLE_MAIL' %}">{% trans "contact us." %}</a></p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
{% extends "members/base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans "Register" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{% static "ludwigsburgalpin/termine.css" static %}">
|
|
||||||
|
|
||||||
<h1>{% trans "Set login data" %}</h1>
|
|
||||||
|
|
||||||
<p>{% trans "Welcome, " %} {{ member.prename }}.
|
|
||||||
{% blocktrans %}To set your personal login data, please enter the password that you received.{% endblocktrans %}</p>
|
|
||||||
|
|
||||||
{% if error_message %}
|
|
||||||
<p><b>{{ error_message }}</b></p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form action="" method="post" enctype="multipart/form-data">
|
|
||||||
<table class="termine">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form}}
|
|
||||||
</table>
|
|
||||||
<input name="key" type="hidden" value="{{key}}">
|
|
||||||
<input name="password" type="hidden" value="{{password}}">
|
|
||||||
<input name="save" type="hidden">
|
|
||||||
<input type="submit" value="{% trans "submit" %}"/>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
{% extends "members/base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans "Register" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Set login data" %}</h1>
|
|
||||||
|
|
||||||
<p>{% trans "Welcome, " %} {{ member.prename }}. {% blocktrans %}To set your personal login data for Kompass, please enter the password that you received.{% endblocktrans%}</p>
|
|
||||||
|
|
||||||
{% if error_message %}
|
|
||||||
<p class="errorlist">{{ error_message }}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form action="" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="password" name="password" required>
|
|
||||||
<input type="hidden" name="key" value="{{key}}">
|
|
||||||
<p><input type="submit" value="{% trans "submit" %}"/></p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{% extends "members/base.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
{% trans "Registration successful" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<h1>{% trans "Set login data" %}</h1>
|
|
||||||
|
|
||||||
<p>{% blocktrans %}You successfully set your login data. You can now proceed to{% endblocktrans%}
|
|
||||||
<a href="/kompass">login</a>.</p>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
from django.urls import re_path
|
|
||||||
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
app_name = "logindata"
|
|
||||||
urlpatterns = [
|
|
||||||
re_path(r'^register', views.register , name='register'),
|
|
||||||
]
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
from django import forms
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.urls import reverse
|
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
|
||||||
from members.models import Member
|
|
||||||
from .models import initial_user_setup, RegistrationPassword
|
|
||||||
|
|
||||||
|
|
||||||
def render_register_password(request, key, member, error_message=''):
|
|
||||||
return render(request, 'logindata/register_password.html',
|
|
||||||
context={'key': key,
|
|
||||||
'member': member,
|
|
||||||
'error_message': error_message})
|
|
||||||
|
|
||||||
|
|
||||||
def render_register_failed(request):
|
|
||||||
return render(request, 'logindata/register_failed.html')
|
|
||||||
|
|
||||||
|
|
||||||
def render_register_form(request, key, password, member, form):
|
|
||||||
return render(request, 'logindata/register_form.html',
|
|
||||||
context={'key': key,
|
|
||||||
'password': password,
|
|
||||||
'member': member,
|
|
||||||
'form': form})
|
|
||||||
|
|
||||||
|
|
||||||
def render_register_success(request):
|
|
||||||
return render(request, 'logindata/register_success.html')
|
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
def register(request):
|
|
||||||
if request.method == 'GET' and 'key' not in request.GET:
|
|
||||||
return HttpResponseRedirect(reverse('startpage:index'))
|
|
||||||
if request.method == 'POST' and 'key' not in request.POST:
|
|
||||||
return HttpResponseRedirect(reverse('startpage:index'))
|
|
||||||
|
|
||||||
key = request.GET['key'] if request.method == 'GET' else request.POST['key']
|
|
||||||
if not key:
|
|
||||||
return render_register_failed(request)
|
|
||||||
try:
|
|
||||||
member = Member.objects.get(invite_as_user_key=key)
|
|
||||||
except (Member.DoesNotExist, Member.MultipleObjectsReturned):
|
|
||||||
return render_register_failed(request)
|
|
||||||
|
|
||||||
if request.method == 'GET':
|
|
||||||
return render_register_password(request, request.GET['key'], member)
|
|
||||||
|
|
||||||
if 'password' not in request.POST:
|
|
||||||
return render_register_failed(request)
|
|
||||||
|
|
||||||
password = request.POST['password']
|
|
||||||
|
|
||||||
# check if the entered password is one of the active registration passwords
|
|
||||||
if RegistrationPassword.objects.filter(password=password).count() == 0:
|
|
||||||
return render_register_password(request, key, member, error_message=_('You entered a wrong password.'))
|
|
||||||
|
|
||||||
if "save" in request.POST:
|
|
||||||
form = UserCreationForm(request.POST)
|
|
||||||
if not form.is_valid():
|
|
||||||
# form is invalid, reprint form with (automatic) error messages
|
|
||||||
return render_register_form(request, key, password, member, form)
|
|
||||||
user = form.save(commit=False)
|
|
||||||
success = initial_user_setup(user, member)
|
|
||||||
if success:
|
|
||||||
return render_register_success(request)
|
|
||||||
else:
|
|
||||||
return render_register_failed(request)
|
|
||||||
else:
|
|
||||||
prefill = {'username': member.suggested_username()}
|
|
||||||
form = UserCreationForm(initial=prefill)
|
|
||||||
return render_register_form(request, key, password, member, form)
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-02 12:06
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0006_rename_permissions'),
|
|
||||||
('mailer', '0001_initial_squashed_0006_auto_20210924_1155'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='message',
|
|
||||||
name='created_by',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_messages', to='members.member', verbose_name='Created by'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-04 12:35
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('mailer', '0002_message_created_by'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='message',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': (('submit_mails', 'Can submit mails'),), 'verbose_name': 'message', 'verbose_name_plural': 'messages'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-11-17 23:31
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('mailer', '0003_alter_message_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='attachment',
|
|
||||||
name='f',
|
|
||||||
field=utils.RestrictedFileField(upload_to='attachments', verbose_name='file'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-11-23 14:03
|
|
||||||
|
|
||||||
import django.core.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('mailer', '0004_alter_attachment_f'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='emailaddress',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=50, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z._-]*$', 'Only alphanumeric characters, ., - and _ are allowed')], verbose_name='name'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-12-01 15:54
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0029_alter_member_gender_alter_memberwaitinglist_gender'),
|
|
||||||
('mailer', '0005_alter_emailaddress_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='emailaddress',
|
|
||||||
name='allowed_senders',
|
|
||||||
field=models.ManyToManyField(blank=True, help_text='Only forward e-mails of members of selected groups. Leave empty to allow all senders.', related_name='allowed_sender_on_emailaddresses', to='members.Group', verbose_name='Allowed sender'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2024-12-01 17:45
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('mailer', '0006_emailaddress_allowed_senders'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='emailaddress',
|
|
||||||
name='internal_only',
|
|
||||||
field=models.BooleanField(default=False, help_text='Only allow forwarding to this e-mail address from the internal domain.', verbose_name='Restrict to internal email addresses'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
from contrib.rules import memberize_user
|
|
||||||
from rules import predicate
|
|
||||||
|
|
||||||
@predicate
|
|
||||||
@memberize_user
|
|
||||||
def is_creator(self, message):
|
|
||||||
if message is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return message.created_by == self
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-01 20:46
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0004_add_training_data_alter_required_flags'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='oldmemberonlist',
|
|
||||||
name='member',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='oldmemberonlist',
|
|
||||||
name='memberlist',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='MemberList',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='OldMemberOnList',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
# Stub Generated by Django 4.0.1 on 2023-04-02 08:29 and edited manually
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
def rename_permissions(apps, schema_editor):
|
|
||||||
Permission = apps.get_model("auth", "Permission")
|
|
||||||
|
|
||||||
for modelcodename in ["klettertreffattendee", "member", "newmemberonlist"]:
|
|
||||||
for action in ["view", "add", "change", "delete"]:
|
|
||||||
Permission.objects \
|
|
||||||
.filter(codename="{action}_{modelcodename}".format(action=action, modelcodename=modelcodename)) \
|
|
||||||
.update(name='Can {action} {modelcodename}'.format(action=action, modelcodename=modelcodename))
|
|
||||||
|
|
||||||
|
|
||||||
def remove_old_memberlist_permissions(apps, schema_editor):
|
|
||||||
Permission = apps.get_model("auth", "Permission")
|
|
||||||
|
|
||||||
for action in ["view", "add", "change", "delete"]:
|
|
||||||
Permission.objects.filter(codename="{action}_oldmemberonlist".format(action=action)).delete()
|
|
||||||
Permission.objects.filter(codename="{action}_memberlist".format(action=action)).delete()
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0005_remove_oldmemberonlist_member_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(rename_permissions, migrations.RunPython.noop),
|
|
||||||
migrations.RunPython(remove_old_memberlist_permissions, migrations.RunPython.noop),
|
|
||||||
]
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-03 20:33
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0006_rename_permissions'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='freizeit',
|
|
||||||
options={'verbose_name': 'Freizeit', 'verbose_name_plural': 'Freizeiten'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='member',
|
|
||||||
options={'permissions': (('may_see_qualities', 'Is allowed to see the quality overview'), ('may_set_auth_user', 'Is allowed to set auth user member connections.'), ('change_member_group', 'Can change the group field')), 'verbose_name': 'member', 'verbose_name_plural': 'members'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='membertraining',
|
|
||||||
options={'verbose_name': 'Training', 'verbose_name_plural': 'Trainings'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-03 21:20
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
import django.db.migrations.operations.special
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0007_alter_permission_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='member',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'delete_global', 'view_global'), 'permissions': (('may_see_qualities', 'Is allowed to see the quality overview'), ('may_set_auth_user', 'Is allowed to set auth user member connections.'), ('change_member_group', 'Can change the group field')), 'verbose_name': 'member', 'verbose_name_plural': 'members'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='freizeit',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global'), 'verbose_name': 'Freizeit', 'verbose_name_plural': 'Freizeiten'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='ljpproposal',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global'), 'verbose_name': 'LJP Proposal', 'verbose_name_plural': 'LJP Proposals'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='membertraining',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global'), 'verbose_name': 'Training', 'verbose_name_plural': 'Trainings'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='newmemberonlist',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global'), 'verbose_name': 'Member', 'verbose_name_plural': 'Members'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='member',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global'), 'permissions': (('may_see_qualities', 'Is allowed to see the quality overview'), ('may_set_auth_user', 'Is allowed to set auth user member connections.'), ('change_member_group', 'Can change the group field')), 'verbose_name': 'member', 'verbose_name_plural': 'members'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-04 12:20
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0008_change_default_permissions'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='freizeit',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Freizeit', 'verbose_name_plural': 'Freizeiten'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='ljpproposal',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'LJP Proposal', 'verbose_name_plural': 'LJP Proposals'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='member',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': (('may_see_qualities', 'Is allowed to see the quality overview'), ('may_set_auth_user', 'Is allowed to set auth user member connections.'), ('change_member_group', 'Can change the group field')), 'verbose_name': 'member', 'verbose_name_plural': 'members'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='membertraining',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Training', 'verbose_name_plural': 'Trainings'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='memberwaitinglist',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'permissions': (('may_manage_waiting_list', 'Can view and manage the waiting list.'),), 'verbose_name': 'Waiter', 'verbose_name_plural': 'Waiters'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='newmemberonlist',
|
|
||||||
options={'default_permissions': ('add_global', 'change_global', 'view_global', 'delete_global', 'list_global', 'view'), 'verbose_name': 'Member', 'verbose_name_plural': 'Members'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,125 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-04 14:23
|
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.db import migrations
|
|
||||||
from django.contrib.auth.management import create_permissions
|
|
||||||
|
|
||||||
STANDARD_PERMS = [
|
|
||||||
('members', 'view_member'),
|
|
||||||
('members', 'view_freizeit'),
|
|
||||||
('members', 'add_global_freizeit'),
|
|
||||||
('mailer', 'view_message'),
|
|
||||||
('mailer', 'add_global_message'),
|
|
||||||
('finance', 'view_statementunsubmitted'),
|
|
||||||
('finance', 'add_global_statementunsubmitted'),
|
|
||||||
]
|
|
||||||
|
|
||||||
FINANCE_PERMS = [
|
|
||||||
('finance', 'view_bill'),
|
|
||||||
('finance', 'view_ledger'),
|
|
||||||
('finance', 'add_ledger'),
|
|
||||||
('finance', 'change_ledger'),
|
|
||||||
('finance', 'delete_ledger'),
|
|
||||||
('finance', 'view_statementsubmitted'),
|
|
||||||
('finance', 'view_global_statementsubmitted'),
|
|
||||||
('finance', 'change_global_statementsubmitted'),
|
|
||||||
('finance', 'view_transaction'),
|
|
||||||
('finance', 'change_transaction'),
|
|
||||||
('finance', 'add_transaction'),
|
|
||||||
('finance', 'delete_transaction'),
|
|
||||||
('finance', 'process_statementsubmitted'),
|
|
||||||
('members', 'list_global_freizeit'),
|
|
||||||
('members', 'view_global_freizeit'),
|
|
||||||
]
|
|
||||||
|
|
||||||
WAITINGLIST_PERMS = [
|
|
||||||
('members', 'view_memberwaitinglist'),
|
|
||||||
('members', 'view_global_memberwaitinglist'),
|
|
||||||
('members', 'list_global_memberwaitinglist'),
|
|
||||||
('members', 'change_global_memberwaitinglist'),
|
|
||||||
('members', 'delete_global_memberwaitinglist'),
|
|
||||||
]
|
|
||||||
|
|
||||||
TRAINING_PERMS = [
|
|
||||||
('members', 'change_global_member'),
|
|
||||||
('members', 'list_global_member'),
|
|
||||||
('members', 'view_global_member'),
|
|
||||||
('members', 'add_global_membertraining'),
|
|
||||||
('members', 'change_global_membertraining'),
|
|
||||||
('members', 'list_global_membertraining'),
|
|
||||||
('members', 'view_global_membertraining'),
|
|
||||||
('members', 'view_trainingcategory'),
|
|
||||||
('members', 'add_trainingcategory'),
|
|
||||||
('members', 'change_trainingcategory'),
|
|
||||||
('members', 'delete_trainingcategory'),
|
|
||||||
]
|
|
||||||
|
|
||||||
REGISTRATION_PERMS = [
|
|
||||||
('members', 'may_manage_all_registrations'),
|
|
||||||
('members', 'change_memberunconfirmedproxy'),
|
|
||||||
('members', 'view_memberunconfirmedproxy'),
|
|
||||||
('members', 'delete_memberunconfirmedproxy'),
|
|
||||||
]
|
|
||||||
|
|
||||||
MATERIAL_PERMS = [
|
|
||||||
('members', 'list_global_member'),
|
|
||||||
('material', 'view_materialpart'),
|
|
||||||
('material', 'change_materialpart'),
|
|
||||||
('material', 'add_materialpart'),
|
|
||||||
('material', 'delete_materialpart'),
|
|
||||||
('material', 'view_materialcategory'),
|
|
||||||
('material', 'change_materialcategory'),
|
|
||||||
('material', 'add_materialcategory'),
|
|
||||||
('material', 'delete_materialcategory'),
|
|
||||||
('material', 'view_ownership'),
|
|
||||||
('material', 'change_ownership'),
|
|
||||||
('material', 'add_ownership'),
|
|
||||||
('material', 'delete_ownership'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def create_group_with_perms(apps, schema_editor, name, perm_names):
|
|
||||||
db_alias = schema_editor.connection.alias
|
|
||||||
Group = apps.get_model("auth", "Group")
|
|
||||||
Permission = apps.get_model("auth", "Permission")
|
|
||||||
if Group.objects.filter(name=name).exists():
|
|
||||||
raise ValueError("A group with name %s already exists." % name)
|
|
||||||
perms = [ Permission.objects.get(codename=codename, content_type__app_label=app_label) for app_label, codename in perm_names ]
|
|
||||||
g = Group.objects.using(db_alias).create(name=name)
|
|
||||||
g.permissions.set(perms)
|
|
||||||
g.save()
|
|
||||||
|
|
||||||
|
|
||||||
def try_create_group_with_perms(apps, schema_editor, name, perm_names):
|
|
||||||
Group = apps.get_model("auth", "Group")
|
|
||||||
if not Group.objects.filter(name=name).exists():
|
|
||||||
create_group_with_perms(apps, schema_editor, name, perm_names)
|
|
||||||
|
|
||||||
|
|
||||||
def create_default_permission_groups(apps, schema_editor):
|
|
||||||
for app_config in apps.get_app_configs():
|
|
||||||
app_config.models_module = True
|
|
||||||
create_permissions(app_config, verbosity=0)
|
|
||||||
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Standard", STANDARD_PERMS)
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Finance", FINANCE_PERMS)
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Waitinglist", WAITINGLIST_PERMS)
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Trainings", TRAINING_PERMS)
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Registrations", REGISTRATION_PERMS)
|
|
||||||
try_create_group_with_perms(apps, schema_editor, "Material", MATERIAL_PERMS)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('auth', '0001_initial'),
|
|
||||||
('contenttypes', '0001_initial'),
|
|
||||||
('material', '0001_initial_squashed_0002_auto_20171011_2045'),
|
|
||||||
('finance', '0003_alter_bill_options_and_more'),
|
|
||||||
('mailer', '0003_alter_message_options'),
|
|
||||||
('members', '0009_alter_freizeit_options_alter_ljpproposal_options_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(create_default_permission_groups, migrations.RunPython.noop),
|
|
||||||
]
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-04 21:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0010_create_default_permission_groups'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='freizeit',
|
|
||||||
name='date',
|
|
||||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Begin'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='freizeit',
|
|
||||||
name='end',
|
|
||||||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='End (optional)'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
# Generated by Django 4.0.1 on 2023-04-09 11:46
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import utils
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('members', '0011_alter_freizeit_date_alter_freizeit_end'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='group',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True, default='', verbose_name='description'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='group',
|
|
||||||
name='show_website',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='show on website'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='member',
|
|
||||||
name='image',
|
|
||||||
field=utils.RestrictedFileField(blank=True, upload_to='people', verbose_name='image'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue