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