mirror of
https://gitlab.crans.org/bde/nk20
synced 2024-11-30 04:13:01 +00:00
Merge branch 'fix-what-i-broke' into 'master'
Corrections Closes #36 See merge request bde/nk20!56
This commit is contained in:
commit
1f004191a8
13
.env_example
Normal file
13
.env_example
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
DJANGO_APP_STAGE="dev"
|
||||||
|
# Only used in dev mode, change to "postgresql" if you want to use PostgreSQL in dev
|
||||||
|
DJANGO_DEV_STORE_METHOD="sqllite"
|
||||||
|
DJANGO_DB_HOST="localhost"
|
||||||
|
DJANGO_DB_NAME="note_db"
|
||||||
|
DJANGO_DB_USER="note"
|
||||||
|
DJANGO_DB_PASSWORD="CHANGE_ME"
|
||||||
|
DJANGO_DB_PORT=""
|
||||||
|
DJANGO_SECRET_KEY="CHANGE_ME"
|
||||||
|
DJANGO_SETTINGS_MODULE="note_kfet.settings"
|
||||||
|
DOMAIN="localhost"
|
||||||
|
CONTACT_EMAIL="tresorerie.bde@localhost"
|
||||||
|
NOTE_URL="localhost"
|
@ -9,13 +9,13 @@ RUN apt update && \
|
|||||||
apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
|
apt install -y gettext nginx uwsgi uwsgi-plugin-python3 && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY requirements.txt /code/
|
COPY . /code/
|
||||||
|
|
||||||
|
# Comment what is not needed
|
||||||
RUN pip install -r requirements/base.txt
|
RUN pip install -r requirements/base.txt
|
||||||
RUN pip install -r requirements/api.txt
|
RUN pip install -r requirements/api.txt
|
||||||
RUN pip install -r requirements/cas.txt
|
RUN pip install -r requirements/cas.txt
|
||||||
RUN pip install -r requirements/production.txt
|
RUN pip install -r requirements/production.txt
|
||||||
|
|
||||||
COPY . /code/
|
|
||||||
|
|
||||||
ENTRYPOINT ["/code/entrypoint.sh"]
|
ENTRYPOINT ["/code/entrypoint.sh"]
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
|
53
README.md
53
README.md
@ -40,13 +40,12 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
|
|||||||
|
|
||||||
$ cp nginx_note.conf_example nginx_note.conf
|
$ cp nginx_note.conf_example nginx_note.conf
|
||||||
|
|
||||||
***Modifier le fichier pour être en accord avec le reste de votre config***
|
***Modifier le fichier pour être en accord avec le reste de votre config***
|
||||||
|
|
||||||
On utilise uwsgi et Nginx pour gérer le coté serveur :
|
On utilise uwsgi et Nginx pour gérer le coté serveur :
|
||||||
|
|
||||||
$ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
|
$ sudo ln -sf /var/www/note_kfet/nginx_note.conf /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
|
||||||
Si l'on a un emperor (plusieurs instance uwsgi):
|
Si l'on a un emperor (plusieurs instance uwsgi):
|
||||||
|
|
||||||
$ sudo ln -sf /var/www/note_kfet/uwsgi_note.ini /etc/uwsgi/sites/
|
$ sudo ln -sf /var/www/note_kfet/uwsgi_note.ini /etc/uwsgi/sites/
|
||||||
@ -85,7 +84,7 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
|
|||||||
postgres=# CREATE DATABASE note_db OWNER note;
|
postgres=# CREATE DATABASE note_db OWNER note;
|
||||||
CREATE DATABASE
|
CREATE DATABASE
|
||||||
|
|
||||||
Si tout va bien:
|
Si tout va bien :
|
||||||
|
|
||||||
postgres=#\list
|
postgres=#\list
|
||||||
List of databases
|
List of databases
|
||||||
@ -97,21 +96,28 @@ On supposera pour la suite que vous utiliser debian/ubuntu sur un serveur tout n
|
|||||||
template1 | postgres | UTF8 | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres +postgres=CTc/postgres
|
template1 | postgres | UTF8 | fr_FR.UTF-8 | fr_FR.UTF-8 | =c/postgres +postgres=CTc/postgres
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
Dans un fichier `.env` à la racine du projet on renseigne des secrets:
|
|
||||||
|
|
||||||
DJANGO_APP_STAGE='prod'
|
|
||||||
DJANGO_DB_PASSWORD='le_mot_de_passe_de_la_bdd'
|
|
||||||
DJANGO_SECRET_KEY='une_secret_key_longue_et_compliquee'
|
|
||||||
ALLOWED_HOSTS='le_ndd_de_votre_instance'
|
|
||||||
|
|
||||||
|
|
||||||
6. Variable d'environnement et Migrations
|
6. Variable d'environnement et Migrations
|
||||||
|
|
||||||
|
On copie le fichier `.env_example` vers le fichier `.env` à la racine du projet
|
||||||
|
et on renseigne des secrets et des paramètres :
|
||||||
|
|
||||||
Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
|
DJANGO_APP_STAGE="dev"
|
||||||
|
DJANGO_DEV_STORE_METHOD="sqllite"
|
||||||
|
DJANGO_DB_HOST="localhost"
|
||||||
|
DJANGO_DB_NAME="note_db"
|
||||||
|
DJANGO_DB_USER="note"
|
||||||
|
DJANGO_DB_PASSWORD="CHANGE_ME"
|
||||||
|
DJANGO_DB_PORT=""
|
||||||
|
DJANGO_SECRET_KEY="CHANGE_ME"
|
||||||
|
DJANGO_SETTINGS_MODULE="note_kfet.settings"
|
||||||
|
DOMAIN="localhost"
|
||||||
|
CONTACT_EMAIL="tresorerie.bde@localhost"
|
||||||
|
NOTE_URL="localhost"
|
||||||
|
|
||||||
|
Ensuite on (re)bascule dans l'environement virtuel et on lance les migrations
|
||||||
|
|
||||||
$ source /env/bin/activate
|
$ source /env/bin/activate
|
||||||
(env)$ ./manage.py check # pas de bétise qui traine
|
(env)$ ./manage.py check # pas de bêtise qui traine
|
||||||
(env)$ ./manage.py makemigrations
|
(env)$ ./manage.py makemigrations
|
||||||
(env)$ ./manage.py migrate
|
(env)$ ./manage.py migrate
|
||||||
|
|
||||||
@ -126,17 +132,21 @@ Il est possible de travailler sur une instance Docker.
|
|||||||
|
|
||||||
$ git clone git@gitlab.crans.org:bde/nk20.git
|
$ git clone git@gitlab.crans.org:bde/nk20.git
|
||||||
|
|
||||||
2. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
|
2. Copiez le fichier `.env_example` à la racine du projet vers le fichier `.env`,
|
||||||
|
et mettez à jour vos variables d'environnement
|
||||||
|
|
||||||
|
3. Dans le fichier `docker_compose.yml`, qu'on suppose déjà configuré,
|
||||||
ajouter les lignes suivantes, en les adaptant à la configuration voulue :
|
ajouter les lignes suivantes, en les adaptant à la configuration voulue :
|
||||||
|
|
||||||
nk20:
|
nk20:
|
||||||
build: /chemin/vers/nk20
|
build: /chemin/vers/nk20
|
||||||
volumes:
|
volumes:
|
||||||
- /chemin/vers/nk20:/code/
|
- /chemin/vers/nk20:/code/
|
||||||
|
env_file: /chemin/vers/nk20/.env
|
||||||
restart: always
|
restart: always
|
||||||
labels:
|
labels:
|
||||||
- traefik.domain=ndd.exemple.com
|
- traefik.domain=ndd.example.com
|
||||||
- traefik.frontend.rule=Host:ndd.exemple.com
|
- traefik.frontend.rule=Host:ndd.example.com
|
||||||
- traefik.port=8000
|
- traefik.port=8000
|
||||||
|
|
||||||
3. Enjoy :
|
3. Enjoy :
|
||||||
@ -159,17 +169,20 @@ un serveur de développement par exemple sur son ordinateur.
|
|||||||
$ source venv/bin/activate
|
$ source venv/bin/activate
|
||||||
(env)$ pip install -r requirements.txt
|
(env)$ pip install -r requirements.txt
|
||||||
|
|
||||||
3. Migrations et chargement des données initiales :
|
3. Copier le fichier `.env_example` vers `.env` à la racine du projet et mettre à jour
|
||||||
|
ce qu'il faut
|
||||||
|
|
||||||
|
4. Migrations et chargement des données initiales :
|
||||||
|
|
||||||
(env)$ ./manage.py makemigrations
|
(env)$ ./manage.py makemigrations
|
||||||
(env)$ ./manage.py migrate
|
(env)$ ./manage.py migrate
|
||||||
(env)$ ./manage.py loaddata initial
|
(env)$ ./manage.py loaddata initial
|
||||||
|
|
||||||
4. Créer un super-utilisateur :
|
5. Créer un super-utilisateur :
|
||||||
|
|
||||||
(env)$ ./manage.py createsuperuser
|
(env)$ ./manage.py createsuperuser
|
||||||
|
|
||||||
5. Enjoy :
|
6. Enjoy :
|
||||||
|
|
||||||
(env)$ ./manage.py runserver 0.0.0.0:8000
|
(env)$ ./manage.py runserver 0.0.0.0:8000
|
||||||
|
|
||||||
@ -184,4 +197,4 @@ Il est disponible [ici](https://wiki.crans.org/NoteKfet/NoteKfet2018/CdC).
|
|||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
La documentation est générée par django et son module admindocs.
|
La documentation est générée par django et son module admindocs.
|
||||||
**Commenter votre code !**
|
**Commentez votre code !**
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
|
|
||||||
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
|
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
|
||||||
from ..models import ActivityType, Activity, Guest
|
from ..models import ActivityType, Activity, Guest
|
||||||
@ -15,6 +16,8 @@ class ActivityTypeViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = ActivityType.objects.all()
|
queryset = ActivityType.objects.all()
|
||||||
serializer_class = ActivityTypeSerializer
|
serializer_class = ActivityTypeSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
filterset_fields = ['name', 'can_invite', ]
|
||||||
|
|
||||||
|
|
||||||
class ActivityViewSet(viewsets.ModelViewSet):
|
class ActivityViewSet(viewsets.ModelViewSet):
|
||||||
@ -25,6 +28,8 @@ class ActivityViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Activity.objects.all()
|
queryset = Activity.objects.all()
|
||||||
serializer_class = ActivitySerializer
|
serializer_class = ActivitySerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
filterset_fields = ['name', 'description', 'activity_type', ]
|
||||||
|
|
||||||
|
|
||||||
class GuestViewSet(viewsets.ModelViewSet):
|
class GuestViewSet(viewsets.ModelViewSet):
|
||||||
@ -35,3 +40,5 @@ class GuestViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Guest.objects.all()
|
queryset = Guest.objects.all()
|
||||||
serializer_class = GuestSerializer
|
serializer_class = GuestSerializer
|
||||||
|
filter_backends = [SearchFilter]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
@ -3,10 +3,14 @@
|
|||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import routers, serializers, viewsets
|
from rest_framework import routers, serializers, viewsets
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
from activity.api.urls import register_activity_urls
|
from activity.api.urls import register_activity_urls
|
||||||
from member.api.urls import register_members_urls
|
from member.api.urls import register_members_urls
|
||||||
from note.api.urls import register_note_urls
|
from note.api.urls import register_note_urls
|
||||||
|
from logs.api.urls import register_logs_urls
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
@ -24,6 +28,17 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
REST API Serializer for Users.
|
||||||
|
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ContentType
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(viewsets.ModelViewSet):
|
class UserViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
REST API View set.
|
REST API View set.
|
||||||
@ -32,15 +47,30 @@ class UserViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['id', 'username', 'first_name', 'last_name', 'email', 'is_superuser', 'is_staff', 'is_active', ]
|
||||||
|
search_fields = ['$username', '$first_name', '$last_name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""
|
||||||
|
REST API View set.
|
||||||
|
The djangorestframework plugin will get all `User` objects, serialize it to JSON with the given serializer,
|
||||||
|
then render it on /api/users/
|
||||||
|
"""
|
||||||
|
queryset = ContentType.objects.all()
|
||||||
|
serializer_class = ContentTypeSerializer
|
||||||
|
|
||||||
|
|
||||||
# Routers provide an easy way of automatically determining the URL conf.
|
# Routers provide an easy way of automatically determining the URL conf.
|
||||||
# Register each app API router and user viewset
|
# Register each app API router and user viewset
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
|
router.register('models', ContentTypeViewSet)
|
||||||
router.register('user', UserViewSet)
|
router.register('user', UserViewSet)
|
||||||
register_members_urls(router, 'members')
|
register_members_urls(router, 'members')
|
||||||
register_activity_urls(router, 'activity')
|
register_activity_urls(router, 'activity')
|
||||||
register_note_urls(router, 'note')
|
register_note_urls(router, 'note')
|
||||||
|
register_logs_urls(router, 'logs')
|
||||||
|
|
||||||
app_name = 'api'
|
app_name = 'api'
|
||||||
|
|
||||||
|
0
apps/logs/api/__init__.py
Normal file
0
apps/logs/api/__init__.py
Normal file
19
apps/logs/api/serializers.py
Normal file
19
apps/logs/api/serializers.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from ..models import Changelog
|
||||||
|
|
||||||
|
|
||||||
|
class ChangelogSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
REST API Serializer for Changelog types.
|
||||||
|
The djangorestframework plugin will analyse the model `Changelog` and parse all fields in the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Changelog
|
||||||
|
fields = '__all__'
|
||||||
|
# noinspection PyProtectedMember
|
||||||
|
read_only_fields = [f.name for f in model._meta.get_fields()] # Changelogs are read-only protected
|
11
apps/logs/api/urls.py
Normal file
11
apps/logs/api/urls.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from .views import ChangelogViewSet
|
||||||
|
|
||||||
|
|
||||||
|
def register_logs_urls(router, path):
|
||||||
|
"""
|
||||||
|
Configure router for Activity REST API.
|
||||||
|
"""
|
||||||
|
router.register(path, ChangelogViewSet)
|
23
apps/logs/api/views.py
Normal file
23
apps/logs/api/views.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.filters import OrderingFilter
|
||||||
|
|
||||||
|
from .serializers import ChangelogSerializer
|
||||||
|
from ..models import Changelog
|
||||||
|
|
||||||
|
|
||||||
|
class ChangelogViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""
|
||||||
|
REST API View set.
|
||||||
|
The djangorestframework plugin will get all `Changelog` objects, serialize it to JSON with the given serializer,
|
||||||
|
then render it on /api/logs/
|
||||||
|
"""
|
||||||
|
queryset = Changelog.objects.all()
|
||||||
|
serializer_class = ChangelogSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, OrderingFilter]
|
||||||
|
filterset_fields = ['model', 'action', "instance_pk", 'user', 'ip', ]
|
||||||
|
ordering_fields = ['timestamp', ]
|
||||||
|
ordering = ['-timestamp', ]
|
@ -2,6 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.db.models.signals import pre_save, post_save, post_delete
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
@ -11,4 +12,7 @@ class LogsConfig(AppConfig):
|
|||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
import logs.signals
|
from . import signals
|
||||||
|
pre_save.connect(signals.pre_save_object)
|
||||||
|
post_save.connect(signals.save_object)
|
||||||
|
post_delete.connect(signals.delete_object)
|
||||||
|
55
apps/logs/middlewares.py
Normal file
55
apps/logs/middlewares.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
|
||||||
|
from threading import local
|
||||||
|
|
||||||
|
|
||||||
|
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
|
||||||
|
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
|
||||||
|
|
||||||
|
_thread_locals = local()
|
||||||
|
|
||||||
|
|
||||||
|
def _set_current_user_and_ip(user=None, ip=None):
|
||||||
|
setattr(_thread_locals, USER_ATTR_NAME, user)
|
||||||
|
setattr(_thread_locals, IP_ATTR_NAME, ip)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_user():
|
||||||
|
return getattr(_thread_locals, USER_ATTR_NAME, None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_ip():
|
||||||
|
return getattr(_thread_locals, IP_ATTR_NAME, None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_authenticated_user():
|
||||||
|
current_user = get_current_user()
|
||||||
|
if isinstance(current_user, AnonymousUser):
|
||||||
|
return None
|
||||||
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
|
class LogsMiddleware(object):
|
||||||
|
"""
|
||||||
|
This middleware get the current user with his or her IP address on each request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
user = request.user
|
||||||
|
if 'HTTP_X_FORWARDED_FOR' in request.META:
|
||||||
|
ip = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||||
|
else:
|
||||||
|
ip = request.META.get('REMOTE_ADDR')
|
||||||
|
|
||||||
|
_set_current_user_and_ip(user, ip)
|
||||||
|
response = self.get_response(request)
|
||||||
|
_set_current_user_and_ip(None, None)
|
||||||
|
|
||||||
|
return response
|
@ -56,6 +56,12 @@ class Changelog(models.Model):
|
|||||||
max_length=16,
|
max_length=16,
|
||||||
null=False,
|
null=False,
|
||||||
blank=False,
|
blank=False,
|
||||||
|
choices=[
|
||||||
|
('create', _('create')),
|
||||||
|
('edit', _('edit')),
|
||||||
|
('delete', _('delete')),
|
||||||
|
],
|
||||||
|
default='edit',
|
||||||
verbose_name=_('action'),
|
verbose_name=_('action'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,67 +1,40 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core import serializers
|
from rest_framework.renderers import JSONRenderer
|
||||||
from django.db.models.signals import pre_save, post_save, post_delete
|
from rest_framework.serializers import ModelSerializer
|
||||||
from django.dispatch import receiver
|
|
||||||
|
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
from note.models import NoteUser, Alias
|
||||||
|
|
||||||
|
from .middlewares import get_current_authenticated_user, get_current_ip
|
||||||
from .models import Changelog
|
from .models import Changelog
|
||||||
|
|
||||||
|
|
||||||
def get_request_in_signal(sender):
|
# Ces modèles ne nécessitent pas de logs
|
||||||
req = None
|
|
||||||
for entry in reversed(inspect.stack()):
|
|
||||||
try:
|
|
||||||
req = entry[0].f_locals['request']
|
|
||||||
# Check if there is a user
|
|
||||||
# noinspection PyStatementEffect
|
|
||||||
req.user
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not req:
|
|
||||||
print("WARNING: Attempt to save " + str(sender) + " with no user")
|
|
||||||
|
|
||||||
return req
|
|
||||||
|
|
||||||
|
|
||||||
def get_user_and_ip(sender):
|
|
||||||
req = get_request_in_signal(sender)
|
|
||||||
try:
|
|
||||||
user = req.user
|
|
||||||
if 'HTTP_X_FORWARDED_FOR' in req.META:
|
|
||||||
ip = req.META.get('HTTP_X_FORWARDED_FOR')
|
|
||||||
else:
|
|
||||||
ip = req.META.get('REMOTE_ADDR')
|
|
||||||
except:
|
|
||||||
user = None
|
|
||||||
ip = None
|
|
||||||
return user, ip
|
|
||||||
|
|
||||||
|
|
||||||
EXCLUDED = [
|
EXCLUDED = [
|
||||||
'admin.logentry',
|
'admin.logentry',
|
||||||
'authtoken.token',
|
'authtoken.token',
|
||||||
|
'cas_server.proxygrantingticket',
|
||||||
|
'cas_server.proxyticket',
|
||||||
|
'cas_server.serviceticket',
|
||||||
'cas_server.user',
|
'cas_server.user',
|
||||||
'cas_server.userattributes',
|
'cas_server.userattributes',
|
||||||
'contenttypes.contenttype',
|
'contenttypes.contenttype',
|
||||||
'logs.changelog',
|
'logs.changelog', # Never remove this line
|
||||||
'migrations.migration',
|
'migrations.migration',
|
||||||
'note.noteuser',
|
'note.note' # We only store the subclasses
|
||||||
'note.noteclub',
|
'note.transaction',
|
||||||
'note.notespecial',
|
|
||||||
'sessions.session',
|
'sessions.session',
|
||||||
'reversion.revision',
|
|
||||||
'reversion.version',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_save)
|
|
||||||
def pre_save_object(sender, instance, **kwargs):
|
def pre_save_object(sender, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Before a model get saved, we get the previous instance that is currently in the database
|
||||||
|
"""
|
||||||
qs = sender.objects.filter(pk=instance.pk).all()
|
qs = sender.objects.filter(pk=instance.pk).all()
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
instance._previous = qs.get()
|
instance._previous = qs.get()
|
||||||
@ -69,30 +42,51 @@ def pre_save_object(sender, instance, **kwargs):
|
|||||||
instance._previous = None
|
instance._previous = None
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save)
|
|
||||||
def save_object(sender, instance, **kwargs):
|
def save_object(sender, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Each time a model is saved, an entry in the table `Changelog` is added in the database
|
||||||
|
in order to store each modification made
|
||||||
|
"""
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
if instance._meta.label_lower in EXCLUDED:
|
if instance._meta.label_lower in EXCLUDED:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# noinspection PyProtectedMember
|
||||||
previous = instance._previous
|
previous = instance._previous
|
||||||
|
|
||||||
user, ip = get_user_and_ip(sender)
|
# Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
|
||||||
|
user, ip = get_current_authenticated_user(), get_current_ip()
|
||||||
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
if user is None:
|
||||||
if isinstance(user, AnonymousUser):
|
# Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
|
||||||
user = None
|
# On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
|
||||||
|
# IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
|
||||||
|
ip = "127.0.0.1"
|
||||||
|
username = Alias.normalize(getpass.getuser())
|
||||||
|
note = NoteUser.objects.filter(alias__normalized_name=username)
|
||||||
|
# if not note.exists():
|
||||||
|
# print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
|
||||||
|
# else:
|
||||||
|
if note.exists():
|
||||||
|
user = note.get().user
|
||||||
|
|
||||||
|
# noinspection PyProtectedMember
|
||||||
if user is not None and instance._meta.label_lower == "auth.user" and previous:
|
if user is not None and instance._meta.label_lower == "auth.user" and previous:
|
||||||
# Don't save last login modifications
|
# On n'enregistre pas les connexions
|
||||||
if instance.last_login != previous.last_login:
|
if instance.last_login != previous.last_login:
|
||||||
return
|
return
|
||||||
|
|
||||||
previous_json = serializers.serialize('json', [previous, ])[1:-1] if previous else None
|
# On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
|
||||||
instance_json = serializers.serialize('json', [instance, ])[1:-1]
|
class CustomSerializer(ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = instance.__class__
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
previous_json = JSONRenderer().render(CustomSerializer(previous).data).decode("UTF-8") if previous else None
|
||||||
|
instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
|
||||||
|
|
||||||
if previous_json == instance_json:
|
if previous_json == instance_json:
|
||||||
# No modification
|
# Pas de log s'il n'y a pas de modification
|
||||||
return
|
return
|
||||||
|
|
||||||
Changelog.objects.create(user=user,
|
Changelog.objects.create(user=user,
|
||||||
@ -105,15 +99,38 @@ def save_object(sender, instance, **kwargs):
|
|||||||
).save()
|
).save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete)
|
|
||||||
def delete_object(sender, instance, **kwargs):
|
def delete_object(sender, instance, **kwargs):
|
||||||
|
"""
|
||||||
|
Each time a model is deleted, an entry in the table `Changelog` is added in the database
|
||||||
|
"""
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
if instance._meta.label_lower in EXCLUDED:
|
if instance._meta.label_lower in EXCLUDED:
|
||||||
return
|
return
|
||||||
|
|
||||||
user, ip = get_user_and_ip(sender)
|
# Si un utilisateur est connecté, on récupère l'utilisateur courant ainsi que son adresse IP
|
||||||
|
user, ip = get_current_authenticated_user(), get_current_ip()
|
||||||
|
|
||||||
|
if user is None:
|
||||||
|
# Si la modification n'a pas été faite via le client Web, on suppose que c'est du à `manage.py`
|
||||||
|
# On récupère alors l'utilisateur·trice connecté·e à la VM, et on récupère la note associée
|
||||||
|
# IMPORTANT : l'utilisateur dans la VM doit être un des alias note du respo info
|
||||||
|
ip = "127.0.0.1"
|
||||||
|
username = Alias.normalize(getpass.getuser())
|
||||||
|
note = NoteUser.objects.filter(alias__normalized_name=username)
|
||||||
|
# if not note.exists():
|
||||||
|
# print("WARNING: A model attempted to be saved in the DB, but the actor is unknown: " + username)
|
||||||
|
# else:
|
||||||
|
if note.exists():
|
||||||
|
user = note.get().user
|
||||||
|
|
||||||
|
# On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
|
||||||
|
class CustomSerializer(ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = instance.__class__
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
instance_json = JSONRenderer().render(CustomSerializer(instance).data).decode("UTF-8")
|
||||||
|
|
||||||
instance_json = serializers.serialize('json', [instance, ])[1:-1]
|
|
||||||
Changelog.objects.create(user=user,
|
Changelog.objects.create(user=user,
|
||||||
ip=ip,
|
ip=ip,
|
||||||
model=ContentType.objects.get_for_model(instance),
|
model=ContentType.objects.get_for_model(instance),
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
app_name = 'logs'
|
|
||||||
|
|
||||||
# TODO User interface
|
|
||||||
urlpatterns = [
|
|
||||||
]
|
|
@ -2,6 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
|
|
||||||
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
|
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
|
||||||
from ..models import Profile, Club, Role, Membership
|
from ..models import Profile, Club, Role, Membership
|
||||||
@ -25,6 +26,8 @@ class ClubViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Club.objects.all()
|
queryset = Club.objects.all()
|
||||||
serializer_class = ClubSerializer
|
serializer_class = ClubSerializer
|
||||||
|
filter_backends = [SearchFilter]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
class RoleViewSet(viewsets.ModelViewSet):
|
class RoleViewSet(viewsets.ModelViewSet):
|
||||||
@ -35,6 +38,8 @@ class RoleViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Role.objects.all()
|
queryset = Role.objects.all()
|
||||||
serializer_class = RoleSerializer
|
serializer_class = RoleSerializer
|
||||||
|
filter_backends = [SearchFilter]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
class MembershipViewSet(viewsets.ModelViewSet):
|
class MembershipViewSet(viewsets.ModelViewSet):
|
||||||
|
@ -46,7 +46,7 @@ class Profile(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('user profile')
|
verbose_name = _('user profile')
|
||||||
verbose_name_plural = _('user profile')
|
verbose_name_plural = _('user profile')
|
||||||
indexes = [ models.Index(fields=['user']) ]
|
indexes = [models.Index(fields=['user'])]
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('user_detail', args=(self.pk,))
|
return reverse('user_detail', args=(self.pk,))
|
||||||
@ -153,7 +153,7 @@ class Membership(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('membership')
|
verbose_name = _('membership')
|
||||||
verbose_name_plural = _('memberships')
|
verbose_name_plural = _('memberships')
|
||||||
indexes = [ models.Index(fields=['user']) ]
|
indexes = [models.Index(fields=['user'])]
|
||||||
|
|
||||||
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
# @receiver(post_save, sender=settings.AUTH_USER_MODEL)
|
||||||
# def save_user_profile(instance, created, **_kwargs):
|
# def save_user_profile(instance, created, **_kwargs):
|
||||||
|
@ -300,7 +300,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
qs = User.objects.all()
|
qs = User.objects.all()
|
||||||
|
|
||||||
if self.q:
|
if self.q:
|
||||||
qs = qs.filter(username__regex=self.q)
|
qs = qs.filter(username__regex="^" + self.q)
|
||||||
|
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
@ -5,7 +5,8 @@ from rest_framework import serializers
|
|||||||
from rest_polymorphic.serializers import PolymorphicSerializer
|
from rest_polymorphic.serializers import PolymorphicSerializer
|
||||||
|
|
||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
|
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
|
||||||
|
TemplateTransaction
|
||||||
|
|
||||||
|
|
||||||
class NoteSerializer(serializers.ModelSerializer):
|
class NoteSerializer(serializers.ModelSerializer):
|
||||||
@ -78,6 +79,17 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateCategorySerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
REST API Serializer for Transaction templates.
|
||||||
|
The djangorestframework plugin will analyse the model `TemplateCategory` and parse all fields in the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = TemplateCategory
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateSerializer(serializers.ModelSerializer):
|
class TransactionTemplateSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
REST API Serializer for Transaction templates.
|
REST API Serializer for Transaction templates.
|
||||||
@ -100,6 +112,17 @@ class TransactionSerializer(serializers.ModelSerializer):
|
|||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateTransactionSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
REST API Serializer for Transactions.
|
||||||
|
The djangorestframework plugin will analyse the model `TemplateTransaction` and parse all fields in the API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = TemplateTransaction
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class MembershipTransactionSerializer(serializers.ModelSerializer):
|
class MembershipTransactionSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
REST API Serializer for Membership transactions.
|
REST API Serializer for Membership transactions.
|
||||||
@ -109,3 +132,11 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = MembershipTransaction
|
model = MembershipTransaction
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionPolymorphicSerializer(PolymorphicSerializer):
|
||||||
|
model_serializer_mapping = {
|
||||||
|
Transaction: TransactionSerializer,
|
||||||
|
TemplateTransaction: TemplateTransactionSerializer,
|
||||||
|
MembershipTransaction: MembershipTransactionSerializer,
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from .views import NotePolymorphicViewSet, AliasViewSet, \
|
from .views import NotePolymorphicViewSet, AliasViewSet, \
|
||||||
TransactionViewSet, TransactionTemplateViewSet, MembershipTransactionViewSet
|
TemplateCategoryViewSet, TransactionViewSet, TransactionTemplateViewSet
|
||||||
|
|
||||||
|
|
||||||
def register_note_urls(router, path):
|
def register_note_urls(router, path):
|
||||||
@ -12,6 +12,6 @@ def register_note_urls(router, path):
|
|||||||
router.register(path + '/note', NotePolymorphicViewSet)
|
router.register(path + '/note', NotePolymorphicViewSet)
|
||||||
router.register(path + '/alias', AliasViewSet)
|
router.register(path + '/alias', AliasViewSet)
|
||||||
|
|
||||||
|
router.register(path + '/transaction/category', TemplateCategoryViewSet)
|
||||||
router.register(path + '/transaction/transaction', TransactionViewSet)
|
router.register(path + '/transaction/transaction', TransactionViewSet)
|
||||||
router.register(path + '/transaction/template', TransactionTemplateViewSet)
|
router.register(path + '/transaction/template', TransactionTemplateViewSet)
|
||||||
router.register(path + '/transaction/membership', MembershipTransactionViewSet)
|
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
|
|
||||||
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
|
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
|
||||||
NoteUserSerializer, AliasSerializer, \
|
NoteUserSerializer, AliasSerializer, \
|
||||||
TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
|
TemplateCategorySerializer, TransactionTemplateSerializer, TransactionPolymorphicSerializer
|
||||||
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
|
||||||
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
|
from ..models.transactions import TransactionTemplate, Transaction, TemplateCategory
|
||||||
|
|
||||||
|
|
||||||
class NoteViewSet(viewsets.ModelViewSet):
|
class NoteViewSet(viewsets.ModelViewSet):
|
||||||
@ -69,8 +71,8 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
alias = self.request.query_params.get("alias", ".*")
|
alias = self.request.query_params.get("alias", ".*")
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(alias__name__regex=alias)
|
Q(alias__name__regex="^" + alias)
|
||||||
| Q(alias__normalized_name__regex=alias.lower()))
|
| Q(alias__normalized_name__regex="^" + alias.lower()))
|
||||||
|
|
||||||
note_type = self.request.query_params.get("type", None)
|
note_type = self.request.query_params.get("type", None)
|
||||||
if note_type:
|
if note_type:
|
||||||
@ -107,7 +109,7 @@ class AliasViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
alias = self.request.query_params.get("alias", ".*")
|
alias = self.request.query_params.get("alias", ".*")
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
|
Q(name__regex="^" + alias) | Q(normalized_name__regex="^" + alias.lower()))
|
||||||
|
|
||||||
note_id = self.request.query_params.get("note", None)
|
note_id = self.request.query_params.get("note", None)
|
||||||
if note_id:
|
if note_id:
|
||||||
@ -131,6 +133,18 @@ class AliasViewSet(viewsets.ModelViewSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateCategoryViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
REST API View set.
|
||||||
|
The djangorestframework plugin will get all `TemplateCategory` objects, serialize it to JSON with the given serializer,
|
||||||
|
then render it on /api/note/transaction/category/
|
||||||
|
"""
|
||||||
|
queryset = TemplateCategory.objects.all()
|
||||||
|
serializer_class = TemplateCategorySerializer
|
||||||
|
filter_backends = [SearchFilter]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
REST API View set.
|
REST API View set.
|
||||||
@ -139,6 +153,8 @@ class TransactionTemplateViewSet(viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = TransactionTemplate.objects.all()
|
queryset = TransactionTemplate.objects.all()
|
||||||
serializer_class = TransactionTemplateSerializer
|
serializer_class = TransactionTemplateSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
filterset_fields = ['name', 'amount', 'display', 'category', ]
|
||||||
|
|
||||||
|
|
||||||
class TransactionViewSet(viewsets.ModelViewSet):
|
class TransactionViewSet(viewsets.ModelViewSet):
|
||||||
@ -148,14 +164,6 @@ class TransactionViewSet(viewsets.ModelViewSet):
|
|||||||
then render it on /api/note/transaction/transaction/
|
then render it on /api/note/transaction/transaction/
|
||||||
"""
|
"""
|
||||||
queryset = Transaction.objects.all()
|
queryset = Transaction.objects.all()
|
||||||
serializer_class = TransactionSerializer
|
serializer_class = TransactionPolymorphicSerializer
|
||||||
|
filter_backends = [SearchFilter]
|
||||||
|
search_fields = ['$reason', ]
|
||||||
class MembershipTransactionViewSet(viewsets.ModelViewSet):
|
|
||||||
"""
|
|
||||||
REST API View set.
|
|
||||||
The djangorestframework plugin will get all `MembershipTransaction` objects, serialize it to JSON with the given serializer,
|
|
||||||
then render it on /api/note/transaction/membership/
|
|
||||||
"""
|
|
||||||
queryset = MembershipTransaction.objects.all()
|
|
||||||
serializer_class = MembershipTransactionSerializer
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 39,
|
"polymorphic_ctype": 40,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 2,
|
"pk": 2,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 39,
|
"polymorphic_ctype": 40,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 3,
|
"pk": 3,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 39,
|
"polymorphic_ctype": 40,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
@ -36,7 +36,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 4,
|
"pk": 4,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 39,
|
"polymorphic_ctype": 40,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
@ -47,7 +47,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 5,
|
"pk": 5,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 38,
|
"polymorphic_ctype": 39,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"model": "note.note",
|
"model": "note.note",
|
||||||
"pk": 6,
|
"pk": 6,
|
||||||
"fields": {
|
"fields": {
|
||||||
"polymorphic_ctype": 38,
|
"polymorphic_ctype": 39,
|
||||||
"balance": 0,
|
"balance": 0,
|
||||||
"is_active": true,
|
"is_active": true,
|
||||||
"display_image": "",
|
"display_image": "",
|
||||||
|
@ -235,7 +235,7 @@ class Alias(models.Model):
|
|||||||
try:
|
try:
|
||||||
sim_alias = Alias.objects.get(normalized_name=normalized_name)
|
sim_alias = Alias.objects.get(normalized_name=normalized_name)
|
||||||
if self != sim_alias:
|
if self != sim_alias:
|
||||||
raise ValidationError(_('An alias with a similar name already exists: {} '.format(sim_alias)),
|
raise ValidationError(_('An alias with a similar name already exists: {} ').format(sim_alias),
|
||||||
code="same_alias"
|
code="same_alias"
|
||||||
)
|
)
|
||||||
except Alias.DoesNotExist:
|
except Alias.DoesNotExist:
|
||||||
|
14
apps/note/templatetags/getenv.py
Normal file
14
apps/note/templatetags/getenv.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def getenv(value):
|
||||||
|
return os.getenv(value)
|
||||||
|
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
register.filter('getenv', getenv)
|
@ -69,7 +69,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
|
|
||||||
# self.q est le paramètre de la recherche
|
# self.q est le paramètre de la recherche
|
||||||
if self.q:
|
if self.q:
|
||||||
qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q))) \
|
qs = qs.filter(Q(name__regex="^" + self.q) | Q(normalized_name__regex="^" + Alias.normalize(self.q))) \
|
||||||
.order_by('normalized_name').distinct()
|
.order_by('normalized_name').distinct()
|
||||||
|
|
||||||
# Filtrage par type de note (user, club, special)
|
# Filtrage par type de note (user, club, special)
|
||||||
@ -141,7 +141,7 @@ class ConsoView(LoginRequiredMixin, SingleTableView):
|
|||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
|
context['transaction_templates'] = TransactionTemplate.objects.filter(display=True) \
|
||||||
.order_by('category')
|
.order_by('category')
|
||||||
context['title'] = _("Consommations")
|
context['title'] = _("Consumptions")
|
||||||
|
|
||||||
# select2 compatibility
|
# select2 compatibility
|
||||||
context['no_cache'] = True
|
context['no_cache'] = True
|
||||||
|
@ -2,12 +2,17 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
if [ -z ${NOTE_URL+x} ]; then
|
||||||
|
echo "Warning: your env files are not configurated."
|
||||||
|
else
|
||||||
|
sed -i -e "s/example.com/$DOMAIN/g" /code/apps/member/fixtures/initial.json
|
||||||
|
sed -i -e "s/localhost/$NOTE_URL/g" /code/note_kfet/fixtures/initial.json
|
||||||
|
sed -i -e "s/\.\*/https?:\/\/$NOTE_URL\/.*/g" /code/note_kfet/fixtures/cas.json
|
||||||
|
sed -i -e "s/REPLACEME/La Note Kfet \\\\ud83c\\\\udf7b/g" /code/note_kfet/fixtures/cas.json
|
||||||
|
fi
|
||||||
|
|
||||||
python manage.py compilemessages
|
python manage.py compilemessages
|
||||||
python manage.py makemigrations
|
python manage.py makemigrations
|
||||||
|
|
||||||
# Wait for database
|
|
||||||
sleep 5
|
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
|
|
||||||
# TODO: use uwsgi in production
|
|
||||||
python manage.py runserver 0.0.0.0:8000
|
python manage.py runserver 0.0.0.0:8000
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-03-07 18:01+0100\n"
|
"POT-Creation-Date: 2020-03-11 11:44+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -24,7 +24,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: apps/activity/models.py:19 apps/activity/models.py:44
|
#: apps/activity/models.py:19 apps/activity/models.py:44
|
||||||
#: apps/member/models.py:60 apps/member/models.py:111
|
#: apps/member/models.py:60 apps/member/models.py:111
|
||||||
#: apps/note/models/notes.py:187 apps/note/models/transactions.py:24
|
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
|
||||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -49,7 +49,7 @@ msgstr ""
|
|||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/activity/models.py:54 apps/note/models/notes.py:163
|
#: apps/activity/models.py:54 apps/note/models/notes.py:164
|
||||||
#: apps/note/models/transactions.py:62
|
#: apps/note/models/transactions.py:62
|
||||||
msgid "type"
|
msgid "type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -90,7 +90,7 @@ msgstr ""
|
|||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/logs/models.py:21 apps/note/models/notes.py:116
|
#: apps/logs/models.py:21 apps/note/models/notes.py:117
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -114,15 +114,27 @@ msgstr ""
|
|||||||
msgid "new data"
|
msgid "new data"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/logs/models.py:59
|
#: apps/logs/models.py:60
|
||||||
|
msgid "create"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: apps/logs/models.py:61
|
||||||
|
msgid "edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: apps/logs/models.py:62
|
||||||
|
msgid "delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: apps/logs/models.py:65
|
||||||
msgid "action"
|
msgid "action"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/logs/models.py:67
|
#: apps/logs/models.py:73
|
||||||
msgid "timestamp"
|
msgid "timestamp"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/logs/models.py:71
|
#: apps/logs/models.py:77
|
||||||
msgid "Logs cannot be destroyed."
|
msgid "Logs cannot be destroyed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -188,7 +200,7 @@ msgid ""
|
|||||||
"members can renew their membership."
|
"members can renew their membership."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/member/models.py:93 apps/note/models/notes.py:138
|
#: apps/member/models.py:93 apps/note/models/notes.py:139
|
||||||
msgid "club"
|
msgid "club"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -237,7 +249,7 @@ msgstr ""
|
|||||||
msgid "Account #%(id)s: %(username)s"
|
msgid "Account #%(id)s: %(username)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/member/views.py:200
|
#: apps/member/views.py:202
|
||||||
msgid "Alias successfully deleted"
|
msgid "Alias successfully deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -250,127 +262,127 @@ msgstr ""
|
|||||||
msgid "destination"
|
msgid "destination"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/apps.py:14 apps/note/models/notes.py:57
|
#: apps/note/apps.py:14 apps/note/models/notes.py:58
|
||||||
msgid "note"
|
msgid "note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/forms.py:26
|
#: apps/note/forms.py:20
|
||||||
msgid "New Alias"
|
msgid "New Alias"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/forms.py:31
|
#: apps/note/forms.py:25
|
||||||
msgid "select an image"
|
msgid "select an image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/forms.py:32
|
#: apps/note/forms.py:26
|
||||||
msgid "Maximal size: 2MB"
|
msgid "Maximal size: 2MB"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/forms.py:77
|
#: apps/note/forms.py:70
|
||||||
msgid "Source and destination must be different."
|
msgid "Source and destination must be different."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:26
|
#: apps/note/models/notes.py:27
|
||||||
msgid "account balance"
|
msgid "account balance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:27
|
#: apps/note/models/notes.py:28
|
||||||
msgid "in centimes, money credited for this instance"
|
msgid "in centimes, money credited for this instance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:31
|
#: apps/note/models/notes.py:32
|
||||||
msgid "last negative date"
|
msgid "last negative date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:32
|
#: apps/note/models/notes.py:33
|
||||||
msgid "last time the balance was negative"
|
msgid "last time the balance was negative"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:37
|
#: apps/note/models/notes.py:38
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:40
|
#: apps/note/models/notes.py:41
|
||||||
msgid ""
|
msgid ""
|
||||||
"Designates whether this note should be treated as active. Unselect this "
|
"Designates whether this note should be treated as active. Unselect this "
|
||||||
"instead of deleting notes."
|
"instead of deleting notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:44
|
#: apps/note/models/notes.py:45
|
||||||
msgid "display image"
|
msgid "display image"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:52 apps/note/models/transactions.py:102
|
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
|
||||||
msgid "created at"
|
msgid "created at"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:58
|
#: apps/note/models/notes.py:59
|
||||||
msgid "notes"
|
msgid "notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:66
|
#: apps/note/models/notes.py:67
|
||||||
msgid "Note"
|
msgid "Note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:76 apps/note/models/notes.py:100
|
#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
|
||||||
msgid "This alias is already taken."
|
msgid "This alias is already taken."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:120
|
#: apps/note/models/notes.py:121
|
||||||
msgid "one's note"
|
msgid "one's note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:121
|
#: apps/note/models/notes.py:122
|
||||||
msgid "users note"
|
msgid "users note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:127
|
#: apps/note/models/notes.py:128
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(user)s's note"
|
msgid "%(user)s's note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:142
|
#: apps/note/models/notes.py:143
|
||||||
msgid "club note"
|
msgid "club note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:143
|
#: apps/note/models/notes.py:144
|
||||||
msgid "clubs notes"
|
msgid "clubs notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:149
|
#: apps/note/models/notes.py:150
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Note of %(club)s club"
|
msgid "Note of %(club)s club"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:169
|
#: apps/note/models/notes.py:170
|
||||||
msgid "special note"
|
msgid "special note"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:170
|
#: apps/note/models/notes.py:171
|
||||||
msgid "special notes"
|
msgid "special notes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:193
|
#: apps/note/models/notes.py:194
|
||||||
msgid "Invalid alias"
|
msgid "Invalid alias"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:209
|
#: apps/note/models/notes.py:210
|
||||||
msgid "alias"
|
msgid "alias"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:210 templates/member/profile_detail.html:37
|
#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
|
||||||
msgid "aliases"
|
msgid "aliases"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:228
|
#: apps/note/models/notes.py:229
|
||||||
msgid "Alias is too long."
|
msgid "Alias is too long."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:233
|
#: apps/note/models/notes.py:234
|
||||||
msgid "An alias with a similar name already exists: {} "
|
msgid "An alias with a similar name already exists: {} "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/notes.py:242
|
#: apps/note/models/notes.py:243
|
||||||
msgid "You can't delete your main alias."
|
msgid "You can't delete your main alias."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -422,11 +434,11 @@ msgstr ""
|
|||||||
msgid "transactions"
|
msgid "transactions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:184
|
#: apps/note/models/transactions.py:185
|
||||||
msgid "membership transaction"
|
msgid "membership transaction"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:185
|
#: apps/note/models/transactions.py:186
|
||||||
msgid "membership transactions"
|
msgid "membership transactions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -434,34 +446,54 @@ msgstr ""
|
|||||||
msgid "Transfer money from your account to one or others"
|
msgid "Transfer money from your account to one or others"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/note/views.py:138
|
#: apps/note/views.py:139
|
||||||
msgid "Consommations"
|
msgid "Consommations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:162
|
#: note_kfet/settings/__init__.py:63
|
||||||
msgid "German"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:163
|
|
||||||
msgid "English"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:164
|
|
||||||
msgid "French"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:215
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"The Central Authentication Service grants you access to most of our websites "
|
"The Central Authentication Service grants you access to most of our websites "
|
||||||
"by authenticating only once, so you don't need to type your credentials "
|
"by authenticating only once, so you don't need to type your credentials "
|
||||||
"again unless your session expires or you logout."
|
"again unless your session expires or you logout."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:156
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:157
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:158
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/base.html:13
|
#: templates/base.html:13
|
||||||
msgid "The ENS Paris-Saclay BDE note."
|
msgid "The ENS Paris-Saclay BDE note."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/cas_server/base.html:7 templates/cas_server/base.html:26
|
#: templates/base.html:70
|
||||||
|
msgid "Consumptions"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/base.html:73
|
||||||
|
msgid "Clubs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/base.html:76
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/base.html:79
|
||||||
|
msgid "Button"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/base.html:82 templates/note/transaction_form.html:35
|
||||||
|
msgid "Transfer"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/cas_server/base.html:7
|
||||||
msgid "Central Authentication Service"
|
msgid "Central Authentication Service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -511,6 +543,15 @@ msgstr ""
|
|||||||
msgid "Connect to the service"
|
msgid "Connect to the service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/django_filters/rest_framework/crispy_form.html:4
|
||||||
|
#: templates/django_filters/rest_framework/form.html:2
|
||||||
|
msgid "Field filters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/django_filters/rest_framework/form.html:5
|
||||||
|
msgid "Submit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/member/club_detail.html:10
|
#: templates/member/club_detail.html:10
|
||||||
msgid "Membership starts on"
|
msgid "Membership starts on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -583,10 +624,6 @@ msgstr ""
|
|||||||
msgid "Sign Up"
|
msgid "Sign Up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/note/transaction_form.html:35
|
|
||||||
msgid "Transfer"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/registration/logged_out.html:8
|
#: templates/registration/logged_out.html:8
|
||||||
msgid "Thanks for spending some quality time with the Web site today."
|
msgid "Thanks for spending some quality time with the Web site today."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -596,7 +633,7 @@ msgid "Log in again"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/registration/login.html:7 templates/registration/login.html:8
|
#: templates/registration/login.html:7 templates/registration/login.html:8
|
||||||
#: templates/registration/login.html:22
|
#: templates/registration/login.html:26
|
||||||
#: templates/registration/password_reset_complete.html:10
|
#: templates/registration/password_reset_complete.html:10
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -608,7 +645,7 @@ msgid ""
|
|||||||
"page. Would you like to login to a different account?"
|
"page. Would you like to login to a different account?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/registration/login.html:23
|
#: templates/registration/login.html:27
|
||||||
msgid "Forgotten your password or username?"
|
msgid "Forgotten your password or username?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-03-07 18:01+0100\n"
|
"POT-Creation-Date: 2020-03-11 11:44+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -19,7 +19,7 @@ msgstr "activité"
|
|||||||
|
|
||||||
#: apps/activity/models.py:19 apps/activity/models.py:44
|
#: apps/activity/models.py:19 apps/activity/models.py:44
|
||||||
#: apps/member/models.py:60 apps/member/models.py:111
|
#: apps/member/models.py:60 apps/member/models.py:111
|
||||||
#: apps/note/models/notes.py:187 apps/note/models/transactions.py:24
|
#: apps/note/models/notes.py:188 apps/note/models/transactions.py:24
|
||||||
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
#: apps/note/models/transactions.py:44 templates/member/profile_detail.html:15
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr "nom"
|
msgstr "nom"
|
||||||
@ -44,7 +44,7 @@ msgstr "types d'activité"
|
|||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "description"
|
msgstr "description"
|
||||||
|
|
||||||
#: apps/activity/models.py:54 apps/note/models/notes.py:163
|
#: apps/activity/models.py:54 apps/note/models/notes.py:164
|
||||||
#: apps/note/models/transactions.py:62
|
#: apps/note/models/transactions.py:62
|
||||||
msgid "type"
|
msgid "type"
|
||||||
msgstr "type"
|
msgstr "type"
|
||||||
@ -85,15 +85,13 @@ msgstr ""
|
|||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/logs/models.py:21 apps/note/models/notes.py:116
|
#: apps/logs/models.py:21 apps/note/models/notes.py:117
|
||||||
msgid "user"
|
msgid "user"
|
||||||
msgstr "utilisateur"
|
msgstr "utilisateur"
|
||||||
|
|
||||||
#: apps/logs/models.py:27
|
#: apps/logs/models.py:27
|
||||||
#, fuzzy
|
|
||||||
#| msgid "address"
|
|
||||||
msgid "IP Address"
|
msgid "IP Address"
|
||||||
msgstr "adresse"
|
msgstr "Adresse IP"
|
||||||
|
|
||||||
#: apps/logs/models.py:35
|
#: apps/logs/models.py:35
|
||||||
msgid "model"
|
msgid "model"
|
||||||
@ -108,22 +106,30 @@ msgid "previous data"
|
|||||||
msgstr "Données précédentes"
|
msgstr "Données précédentes"
|
||||||
|
|
||||||
#: apps/logs/models.py:52
|
#: apps/logs/models.py:52
|
||||||
#, fuzzy
|
|
||||||
#| msgid "end date"
|
|
||||||
msgid "new data"
|
msgid "new data"
|
||||||
msgstr "Nouvelles données"
|
msgstr "Nouvelles données"
|
||||||
|
|
||||||
#: apps/logs/models.py:59
|
#: apps/logs/models.py:60
|
||||||
#, fuzzy
|
msgid "create"
|
||||||
#| msgid "section"
|
msgstr "Créer"
|
||||||
|
|
||||||
|
#: apps/logs/models.py:61
|
||||||
|
msgid "edit"
|
||||||
|
msgstr "Modifier"
|
||||||
|
|
||||||
|
#: apps/logs/models.py:62
|
||||||
|
msgid "delete"
|
||||||
|
msgstr "Supprimer"
|
||||||
|
|
||||||
|
#: apps/logs/models.py:65
|
||||||
msgid "action"
|
msgid "action"
|
||||||
msgstr "Action"
|
msgstr "Action"
|
||||||
|
|
||||||
#: apps/logs/models.py:67
|
#: apps/logs/models.py:73
|
||||||
msgid "timestamp"
|
msgid "timestamp"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
|
|
||||||
#: apps/logs/models.py:71
|
#: apps/logs/models.py:77
|
||||||
msgid "Logs cannot be destroyed."
|
msgid "Logs cannot be destroyed."
|
||||||
msgstr "Les logs ne peuvent pas être détruits."
|
msgstr "Les logs ne peuvent pas être détruits."
|
||||||
|
|
||||||
@ -193,10 +199,16 @@ msgstr ""
|
|||||||
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
|
"Combien de temps l'adhésion peut durer après le 1er Janvier de l'année "
|
||||||
"suivante avant que les adhérents peuvent renouveler leur adhésion."
|
"suivante avant que les adhérents peuvent renouveler leur adhésion."
|
||||||
|
|
||||||
#: apps/member/models.py:93 apps/note/models/notes.py:138
|
#: apps/member/models.py:93 apps/note/models/notes.py:139
|
||||||
msgid "club"
|
msgid "club"
|
||||||
msgstr "club"
|
msgstr "club"
|
||||||
|
|
||||||
|
msgid "New club"
|
||||||
|
msgstr "Nouveau club"
|
||||||
|
|
||||||
|
msgid "Clubs list"
|
||||||
|
msgstr "Liste des clubs"
|
||||||
|
|
||||||
#: apps/member/models.py:94
|
#: apps/member/models.py:94
|
||||||
msgid "clubs"
|
msgid "clubs"
|
||||||
msgstr "clubs"
|
msgstr "clubs"
|
||||||
@ -242,9 +254,9 @@ msgstr "Un alias avec un nom similaire existe déjà."
|
|||||||
msgid "Account #%(id)s: %(username)s"
|
msgid "Account #%(id)s: %(username)s"
|
||||||
msgstr "Compte n°%(id)s : %(username)s"
|
msgstr "Compte n°%(id)s : %(username)s"
|
||||||
|
|
||||||
#: apps/member/views.py:200
|
#: apps/member/views.py:202
|
||||||
msgid "Alias successfully deleted"
|
msgid "Alias successfully deleted"
|
||||||
msgstr ""
|
msgstr "L'alias a bien été supprimé"
|
||||||
|
|
||||||
#: apps/note/admin.py:120 apps/note/models/transactions.py:93
|
#: apps/note/admin.py:120 apps/note/models/transactions.py:93
|
||||||
msgid "source"
|
msgid "source"
|
||||||
@ -255,132 +267,128 @@ msgstr "source"
|
|||||||
msgid "destination"
|
msgid "destination"
|
||||||
msgstr "destination"
|
msgstr "destination"
|
||||||
|
|
||||||
#: apps/note/apps.py:14 apps/note/models/notes.py:57
|
#: apps/note/apps.py:14 apps/note/models/notes.py:58
|
||||||
msgid "note"
|
msgid "note"
|
||||||
msgstr "note"
|
msgstr "note"
|
||||||
|
|
||||||
#: apps/note/forms.py:26
|
#: apps/note/forms.py:20
|
||||||
msgid "New Alias"
|
msgid "New Alias"
|
||||||
msgstr ""
|
msgstr "Nouvel alias"
|
||||||
|
|
||||||
#: apps/note/forms.py:31
|
#: apps/note/forms.py:25
|
||||||
#, fuzzy
|
|
||||||
#| msgid "display image"
|
|
||||||
msgid "select an image"
|
msgid "select an image"
|
||||||
msgstr "image affichée"
|
msgstr "Choisissez une image"
|
||||||
|
|
||||||
#: apps/note/forms.py:32
|
#: apps/note/forms.py:26
|
||||||
msgid "Maximal size: 2MB"
|
msgid "Maximal size: 2MB"
|
||||||
msgstr ""
|
msgstr "Taille maximale : 2 Mo"
|
||||||
|
|
||||||
#: apps/note/forms.py:77
|
#: apps/note/forms.py:70
|
||||||
msgid "Source and destination must be different."
|
msgid "Source and destination must be different."
|
||||||
msgstr "La source et la destination doivent être différentes."
|
msgstr "La source et la destination doivent être différentes."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:26
|
#: apps/note/models/notes.py:27
|
||||||
msgid "account balance"
|
msgid "account balance"
|
||||||
msgstr "solde du compte"
|
msgstr "solde du compte"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:27
|
#: apps/note/models/notes.py:28
|
||||||
msgid "in centimes, money credited for this instance"
|
msgid "in centimes, money credited for this instance"
|
||||||
msgstr "en centimes, argent crédité pour cette instance"
|
msgstr "en centimes, argent crédité pour cette instance"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:31
|
#: apps/note/models/notes.py:32
|
||||||
msgid "last negative date"
|
msgid "last negative date"
|
||||||
msgstr "dernier date de négatif"
|
msgstr "dernier date de négatif"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:32
|
#: apps/note/models/notes.py:33
|
||||||
msgid "last time the balance was negative"
|
msgid "last time the balance was negative"
|
||||||
msgstr "dernier instant où la note était en négatif"
|
msgstr "dernier instant où la note était en négatif"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:37
|
#: apps/note/models/notes.py:38
|
||||||
msgid "active"
|
msgid "active"
|
||||||
msgstr "actif"
|
msgstr "actif"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:40
|
#: apps/note/models/notes.py:41
|
||||||
msgid ""
|
msgid ""
|
||||||
"Designates whether this note should be treated as active. Unselect this "
|
"Designates whether this note should be treated as active. Unselect this "
|
||||||
"instead of deleting notes."
|
"instead of deleting notes."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
|
"Indique si la note est active. Désactiver cela plutôt que supprimer la note."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:44
|
#: apps/note/models/notes.py:45
|
||||||
msgid "display image"
|
msgid "display image"
|
||||||
msgstr "image affichée"
|
msgstr "image affichée"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:52 apps/note/models/transactions.py:102
|
#: apps/note/models/notes.py:53 apps/note/models/transactions.py:102
|
||||||
msgid "created at"
|
msgid "created at"
|
||||||
msgstr "créée le"
|
msgstr "créée le"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:58
|
#: apps/note/models/notes.py:59
|
||||||
msgid "notes"
|
msgid "notes"
|
||||||
msgstr "notes"
|
msgstr "notes"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:66
|
#: apps/note/models/notes.py:67
|
||||||
msgid "Note"
|
msgid "Note"
|
||||||
msgstr "Note"
|
msgstr "Note"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:76 apps/note/models/notes.py:100
|
#: apps/note/models/notes.py:77 apps/note/models/notes.py:101
|
||||||
msgid "This alias is already taken."
|
msgid "This alias is already taken."
|
||||||
msgstr "Cet alias est déjà pris."
|
msgstr "Cet alias est déjà pris."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:120
|
#: apps/note/models/notes.py:121
|
||||||
msgid "one's note"
|
msgid "one's note"
|
||||||
msgstr "note d'un utilisateur"
|
msgstr "note d'un utilisateur"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:121
|
#: apps/note/models/notes.py:122
|
||||||
msgid "users note"
|
msgid "users note"
|
||||||
msgstr "notes des utilisateurs"
|
msgstr "notes des utilisateurs"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:127
|
#: apps/note/models/notes.py:128
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(user)s's note"
|
msgid "%(user)s's note"
|
||||||
msgstr "Note de %(user)s"
|
msgstr "Note de %(user)s"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:142
|
#: apps/note/models/notes.py:143
|
||||||
msgid "club note"
|
msgid "club note"
|
||||||
msgstr "note d'un club"
|
msgstr "note d'un club"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:143
|
#: apps/note/models/notes.py:144
|
||||||
msgid "clubs notes"
|
msgid "clubs notes"
|
||||||
msgstr "notes des clubs"
|
msgstr "notes des clubs"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:149
|
#: apps/note/models/notes.py:150
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Note of %(club)s club"
|
msgid "Note of %(club)s club"
|
||||||
msgstr "Note du club %(club)s"
|
msgstr "Note du club %(club)s"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:169
|
#: apps/note/models/notes.py:170
|
||||||
msgid "special note"
|
msgid "special note"
|
||||||
msgstr "note spéciale"
|
msgstr "note spéciale"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:170
|
#: apps/note/models/notes.py:171
|
||||||
msgid "special notes"
|
msgid "special notes"
|
||||||
msgstr "notes spéciales"
|
msgstr "notes spéciales"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:193
|
#: apps/note/models/notes.py:194
|
||||||
msgid "Invalid alias"
|
msgid "Invalid alias"
|
||||||
msgstr "Alias invalide"
|
msgstr "Alias invalide"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:209
|
#: apps/note/models/notes.py:210
|
||||||
msgid "alias"
|
msgid "alias"
|
||||||
msgstr "alias"
|
msgstr "alias"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:210 templates/member/profile_detail.html:37
|
#: apps/note/models/notes.py:211 templates/member/profile_detail.html:37
|
||||||
msgid "aliases"
|
msgid "aliases"
|
||||||
msgstr "alias"
|
msgstr "alias"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:228
|
#: apps/note/models/notes.py:229
|
||||||
msgid "Alias is too long."
|
msgid "Alias is too long."
|
||||||
msgstr "L'alias est trop long."
|
msgstr "L'alias est trop long."
|
||||||
|
|
||||||
#: apps/note/models/notes.py:233
|
#: apps/note/models/notes.py:234
|
||||||
#, fuzzy
|
|
||||||
#| msgid "An alias with a similar name already exists:"
|
|
||||||
msgid "An alias with a similar name already exists: {} "
|
msgid "An alias with a similar name already exists: {} "
|
||||||
msgstr "Un alias avec un nom similaire existe déjà."
|
msgstr "Un alias avec un nom similaire existe déjà : {}"
|
||||||
|
|
||||||
#: apps/note/models/notes.py:242
|
#: apps/note/models/notes.py:243
|
||||||
msgid "You can't delete your main alias."
|
msgid "You can't delete your main alias."
|
||||||
msgstr "Vous ne pouvez pas supprimer votre alias principal."
|
msgstr "Vous ne pouvez pas supprimer votre alias principal."
|
||||||
|
|
||||||
@ -393,7 +401,6 @@ msgid "transaction categories"
|
|||||||
msgstr "catégories de transaction"
|
msgstr "catégories de transaction"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:47
|
#: apps/note/models/transactions.py:47
|
||||||
#, fuzzy
|
|
||||||
msgid "A template with this name already exist"
|
msgid "A template with this name already exist"
|
||||||
msgstr "Un modèle de transaction avec un nom similaire existe déjà."
|
msgstr "Un modèle de transaction avec un nom similaire existe déjà."
|
||||||
|
|
||||||
@ -433,11 +440,11 @@ msgstr "transaction"
|
|||||||
msgid "transactions"
|
msgid "transactions"
|
||||||
msgstr "transactions"
|
msgstr "transactions"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:184
|
#: apps/note/models/transactions.py:185
|
||||||
msgid "membership transaction"
|
msgid "membership transaction"
|
||||||
msgstr "transaction d'adhésion"
|
msgstr "transaction d'adhésion"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:185
|
#: apps/note/models/transactions.py:186
|
||||||
msgid "membership transactions"
|
msgid "membership transactions"
|
||||||
msgstr "transactions d'adhésion"
|
msgstr "transactions d'adhésion"
|
||||||
|
|
||||||
@ -445,34 +452,53 @@ msgstr "transactions d'adhésion"
|
|||||||
msgid "Transfer money from your account to one or others"
|
msgid "Transfer money from your account to one or others"
|
||||||
msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
|
msgstr "Transfert d'argent de ton compte vers un ou plusieurs autres"
|
||||||
|
|
||||||
#: apps/note/views.py:138
|
#: note_kfet/settings/__init__.py:63
|
||||||
msgid "Consommations"
|
|
||||||
msgstr "transactions"
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:162
|
|
||||||
msgid "German"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:163
|
|
||||||
msgid "English"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:164
|
|
||||||
msgid "French"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:215
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"The Central Authentication Service grants you access to most of our websites "
|
"The Central Authentication Service grants you access to most of our websites "
|
||||||
"by authenticating only once, so you don't need to type your credentials "
|
"by authenticating only once, so you don't need to type your credentials "
|
||||||
"again unless your session expires or you logout."
|
"again unless your session expires or you logout."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:156
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:157
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: note_kfet/settings/base.py:158
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/base.html:13
|
#: templates/base.html:13
|
||||||
msgid "The ENS Paris-Saclay BDE note."
|
msgid "The ENS Paris-Saclay BDE note."
|
||||||
msgstr "La note du BDE de l'ENS Paris-Saclay."
|
msgstr "La note du BDE de l'ENS Paris-Saclay."
|
||||||
|
|
||||||
#: templates/cas_server/base.html:7 templates/cas_server/base.html:26
|
#: templates/base.html:70
|
||||||
|
msgid "Consumptions"
|
||||||
|
msgstr "Consommations"
|
||||||
|
|
||||||
|
#: templates/base.html:73
|
||||||
|
msgid "Clubs"
|
||||||
|
msgstr "Clubs"
|
||||||
|
|
||||||
|
#: templates/base.html:76
|
||||||
|
msgid "Activities"
|
||||||
|
msgstr "Activités"
|
||||||
|
|
||||||
|
#: templates/base.html:79
|
||||||
|
msgid "Buttons"
|
||||||
|
msgstr "Boutons"
|
||||||
|
|
||||||
|
msgid "Buttons list"
|
||||||
|
msgstr "Liste des boutons"
|
||||||
|
|
||||||
|
#: templates/base.html:82 templates/note/transaction_form.html:35
|
||||||
|
msgid "Transfer"
|
||||||
|
msgstr "Virement"
|
||||||
|
|
||||||
|
#: templates/cas_server/base.html:7
|
||||||
msgid "Central Authentication Service"
|
msgid "Central Authentication Service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -510,11 +536,11 @@ msgstr ""
|
|||||||
|
|
||||||
#: templates/cas_server/login.html:11
|
#: templates/cas_server/login.html:11
|
||||||
msgid ""
|
msgid ""
|
||||||
"If you don't have any Note Kfet account, please follow <a href='/accounts"
|
"If you don't have any Note Kfet account, please follow <a href='/accounts/"
|
||||||
"/signup'>this link to sign up</a>."
|
"signup'>this link to sign up</a>."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Si vous n'avez pas de compte Note Kfet, veuillez suivre <a href='/accounts"
|
"Si vous n'avez pas de compte Note Kfet, veuillez suivre <a href='/accounts/"
|
||||||
"/signup'>ce lien pour vous inscrire</a>."
|
"signup'>ce lien pour vous inscrire</a>."
|
||||||
|
|
||||||
#: templates/cas_server/login.html:17
|
#: templates/cas_server/login.html:17
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
@ -524,6 +550,15 @@ msgstr ""
|
|||||||
msgid "Connect to the service"
|
msgid "Connect to the service"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/django_filters/rest_framework/crispy_form.html:4
|
||||||
|
#: templates/django_filters/rest_framework/form.html:2
|
||||||
|
msgid "Field filters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/django_filters/rest_framework/form.html:5
|
||||||
|
msgid "Submit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: templates/member/club_detail.html:10
|
#: templates/member/club_detail.html:10
|
||||||
msgid "Membership starts on"
|
msgid "Membership starts on"
|
||||||
msgstr "L'adhésion commence le"
|
msgstr "L'adhésion commence le"
|
||||||
@ -557,10 +592,8 @@ msgid "Regenerate token"
|
|||||||
msgstr "Regénérer le jeton"
|
msgstr "Regénérer le jeton"
|
||||||
|
|
||||||
#: templates/member/profile_alias.html:10
|
#: templates/member/profile_alias.html:10
|
||||||
#, fuzzy
|
|
||||||
#| msgid "alias"
|
|
||||||
msgid "Add alias"
|
msgid "Add alias"
|
||||||
msgstr "alias"
|
msgstr "Ajouter un alias"
|
||||||
|
|
||||||
#: templates/member/profile_detail.html:15
|
#: templates/member/profile_detail.html:15
|
||||||
msgid "first name"
|
msgid "first name"
|
||||||
@ -583,10 +616,8 @@ msgid "Manage auth token"
|
|||||||
msgstr "Gérer les jetons d'authentification"
|
msgstr "Gérer les jetons d'authentification"
|
||||||
|
|
||||||
#: templates/member/profile_detail.html:49
|
#: templates/member/profile_detail.html:49
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Update Profile"
|
|
||||||
msgid "View Profile"
|
msgid "View Profile"
|
||||||
msgstr "Modifier le profil"
|
msgstr "Voir le profil"
|
||||||
|
|
||||||
#: templates/member/profile_detail.html:62
|
#: templates/member/profile_detail.html:62
|
||||||
msgid "View my memberships"
|
msgid "View my memberships"
|
||||||
@ -596,13 +627,10 @@ msgstr "Voir mes adhésions"
|
|||||||
msgid "Save Changes"
|
msgid "Save Changes"
|
||||||
msgstr "Sauvegarder les changements"
|
msgstr "Sauvegarder les changements"
|
||||||
|
|
||||||
|
#: templates/member/signup.html:8
|
||||||
#: templates/member/signup.html:14
|
#: templates/member/signup.html:14
|
||||||
msgid "Sign Up"
|
msgid "Sign up"
|
||||||
msgstr ""
|
msgstr "Inscription"
|
||||||
|
|
||||||
#: templates/note/transaction_form.html:35
|
|
||||||
msgid "Transfer"
|
|
||||||
msgstr "Virement"
|
|
||||||
|
|
||||||
#: templates/registration/logged_out.html:8
|
#: templates/registration/logged_out.html:8
|
||||||
msgid "Thanks for spending some quality time with the Web site today."
|
msgid "Thanks for spending some quality time with the Web site today."
|
||||||
@ -613,7 +641,7 @@ msgid "Log in again"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/registration/login.html:7 templates/registration/login.html:8
|
#: templates/registration/login.html:7 templates/registration/login.html:8
|
||||||
#: templates/registration/login.html:22
|
#: templates/registration/login.html:26
|
||||||
#: templates/registration/password_reset_complete.html:10
|
#: templates/registration/password_reset_complete.html:10
|
||||||
msgid "Log in"
|
msgid "Log in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -625,7 +653,7 @@ msgid ""
|
|||||||
"page. Would you like to login to a different account?"
|
"page. Would you like to login to a different account?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/registration/login.html:23
|
#: templates/registration/login.html:27
|
||||||
msgid "Forgotten your password or username?"
|
msgid "Forgotten your password or username?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
|
|
||||||
@ -30,10 +35,6 @@ read_env()
|
|||||||
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
|
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
|
||||||
if app_stage == 'prod':
|
if app_stage == 'prod':
|
||||||
from .production import *
|
from .production import *
|
||||||
|
|
||||||
DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS')
|
|
||||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
|
|
||||||
ALLOWED_HOSTS = [os.environ.get('ALLOWED_HOSTS', 'localhost')]
|
|
||||||
else:
|
else:
|
||||||
from .development import *
|
from .development import *
|
||||||
|
|
||||||
@ -46,12 +47,14 @@ except ImportError:
|
|||||||
if "cas" in INSTALLED_APPS:
|
if "cas" in INSTALLED_APPS:
|
||||||
MIDDLEWARE += ['cas.middleware.CASMiddleware']
|
MIDDLEWARE += ['cas.middleware.CASMiddleware']
|
||||||
# CAS Settings
|
# CAS Settings
|
||||||
|
CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
|
||||||
CAS_AUTO_CREATE_USER = False
|
CAS_AUTO_CREATE_USER = False
|
||||||
CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
|
CAS_LOGO_URL = "/static/img/Saperlistpopette.png"
|
||||||
CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
|
CAS_FAVICON_URL = "/static/favicon/favicon-32x32.png"
|
||||||
CAS_SHOW_SERVICE_MESSAGES = True
|
CAS_SHOW_SERVICE_MESSAGES = True
|
||||||
CAS_SHOW_POWERED = False
|
CAS_SHOW_POWERED = False
|
||||||
CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
|
CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
|
||||||
|
CAS_PROVIDE_URL_TO_LOGOUT = True
|
||||||
CAS_INFO_MESSAGES = {
|
CAS_INFO_MESSAGES = {
|
||||||
"cas_explained": {
|
"cas_explained": {
|
||||||
"message": _(
|
"message": _(
|
||||||
@ -69,6 +72,10 @@ if "cas" in INSTALLED_APPS:
|
|||||||
]
|
]
|
||||||
AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
|
AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
|
||||||
|
|
||||||
|
|
||||||
|
if "logs" in INSTALLED_APPS:
|
||||||
|
MIDDLEWARE += ('logs.middlewares.LogsMiddleware',)
|
||||||
|
|
||||||
if "debug_toolbar" in INSTALLED_APPS:
|
if "debug_toolbar" in INSTALLED_APPS:
|
||||||
MIDDLEWARE.insert(1,"debug_toolbar.middleware.DebugToolbarMiddleware")
|
MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")
|
||||||
INTERNAL_IPS = [ '127.0.0.1']
|
INTERNAL_IPS = ['127.0.0.1']
|
||||||
|
@ -39,6 +39,8 @@ INSTALLED_APPS = [
|
|||||||
'polymorphic',
|
'polymorphic',
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
'django_tables2',
|
'django_tables2',
|
||||||
|
'cas_server',
|
||||||
|
'cas',
|
||||||
# Django contrib
|
# Django contrib
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.admindocs',
|
'django.contrib.admindocs',
|
||||||
@ -135,11 +137,14 @@ REST_FRAMEWORK = {
|
|||||||
# or allow read-only access for unauthenticated users.
|
# or allow read-only access for unauthenticated users.
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
# TODO Maybe replace it with our custom permissions system
|
# TODO Maybe replace it with our custom permissions system
|
||||||
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
|
'rest_framework.permissions.DjangoModelPermissions',
|
||||||
],
|
],
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
'rest_framework.authentication.TokenAuthentication',
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
]
|
],
|
||||||
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||||
|
'PAGE_SIZE': 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
|
@ -17,12 +17,24 @@ import os
|
|||||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||||
from . import *
|
from . import *
|
||||||
|
|
||||||
DATABASES = {
|
if os.getenv("DJANGO_DEV_STORE_METHOD", "sqllite") == "postgresql":
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||||
|
'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
|
||||||
|
'USER': os.environ.get('DJANGO_DB_USER', 'note'),
|
||||||
|
'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
|
||||||
|
'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
|
||||||
|
'PORT': os.environ.get('DJANGO_DB_PORT', ''), # Use default port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Break it, fix it!
|
# Break it, fix it!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@ -39,7 +51,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|||||||
# EMAIL_HOST_USER = 'change_me'
|
# EMAIL_HOST_USER = 'change_me'
|
||||||
# EMAIL_HOST_PASSWORD = 'change_me'
|
# EMAIL_HOST_PASSWORD = 'change_me'
|
||||||
|
|
||||||
SERVER_EMAIL = 'no-reply@example.org'
|
SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
|
||||||
|
|
||||||
# Security settings
|
# Security settings
|
||||||
SECURE_CONTENT_TYPE_NOSNIFF = False
|
SECURE_CONTENT_TYPE_NOSNIFF = False
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# Production Settings #
|
# Production Settings #
|
||||||
########################
|
########################
|
||||||
@ -14,11 +16,11 @@
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||||
'NAME': 'note_db',
|
'NAME': os.environ.get('DJANGO_DB_NAME', 'note_db'),
|
||||||
'USER': 'note',
|
'USER': os.environ.get('DJANGO_DB_USER', 'note'),
|
||||||
'PASSWORD': 'update_in_env_variable',
|
'PASSWORD': os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS'),
|
||||||
'HOST': '127.0.0.1',
|
'HOST': os.environ.get('DJANGO_DB_HOST', 'localhost'),
|
||||||
'PORT': '',
|
'PORT': os.environ.get('DJANGO_DB_PORT', ''), # Use default port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +28,9 @@ DATABASES = {
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
# Mandatory !
|
# Mandatory !
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = [os.environ.get('NOTE_URL', 'localhost')]
|
||||||
|
|
||||||
|
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
|
||||||
|
|
||||||
# Emails
|
# Emails
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
@ -37,7 +41,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|||||||
# EMAIL_HOST_USER = 'change_me'
|
# EMAIL_HOST_USER = 'change_me'
|
||||||
# EMAIL_HOST_PASSWORD = 'change_me'
|
# EMAIL_HOST_PASSWORD = 'change_me'
|
||||||
|
|
||||||
SERVER_EMAIL = 'no-reply@example.org'
|
SERVER_EMAIL = 'no-reply@' + os.getenv("DOMAIN", "example.com")
|
||||||
|
|
||||||
# Security settings
|
# Security settings
|
||||||
SECURE_CONTENT_TYPE_NOSNIFF = False
|
SECURE_CONTENT_TYPE_NOSNIFF = False
|
||||||
@ -49,4 +53,4 @@ X_FRAME_OPTIONS = 'DENY'
|
|||||||
SESSION_COOKIE_AGE = 60 * 60 * 3
|
SESSION_COOKIE_AGE = 60 * 60 * 3
|
||||||
|
|
||||||
# CAS Client settings
|
# CAS Client settings
|
||||||
CAS_SERVER_URL = "https://note.crans.org/cas/"
|
CAS_SERVER_URL = "https://" + os.getenv("NOTE_URL", "note.example.com") + "/cas/"
|
||||||
|
@ -20,7 +20,6 @@ urlpatterns = [
|
|||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('logs/', include('logs.urls')),
|
|
||||||
path('api/', include('api.urls')),
|
path('api/', include('api.urls')),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -37,8 +36,8 @@ if "cas" in settings.INSTALLED_APPS:
|
|||||||
from cas import views as cas_views
|
from cas import views as cas_views
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
# Include CAS Client routers
|
# Include CAS Client routers
|
||||||
path('accounts/login/', cas_views.login, name='login'),
|
path('accounts/login/cas/', cas_views.login, name='cas_login'),
|
||||||
path('accounts/logout/', cas_views.logout, name='logout'),
|
path('accounts/logout/cas/', cas_views.logout, name='cas_logout'),
|
||||||
|
|
||||||
]
|
]
|
||||||
if "debug_toolbar" in settings.INSTALLED_APPS:
|
if "debug_toolbar" in settings.INSTALLED_APPS:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% load static i18n pretty_money static %}
|
{% load static i18n pretty_money static getenv %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
@ -75,6 +75,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
|
<a class="nav-link" href="#"><i class="fa fa-calendar"></i> {% trans 'Activities' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="{% url 'note:template_list' %}"><i class="fa fa-coffee"></i> {% trans 'Buttons' %}</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
|
<a class="nav-link" href="{% url 'note:transfer' %}"><i class="fa fa-exchange"></i>{% trans 'Transfer' %} </a>
|
||||||
</li>
|
</li>
|
||||||
@ -125,7 +128,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
class="form-inline">
|
class="form-inline">
|
||||||
<span class="text-muted mr-1">
|
<span class="text-muted mr-1">
|
||||||
NoteKfet2020 —
|
NoteKfet2020 —
|
||||||
<a href="mailto:tresorie.bde@lists.crans.org"
|
<a href="mailto:{{ "CONTACT_EMAIL" | getenv }}"
|
||||||
class="text-muted">Nous contacter</a> —
|
class="text-muted">Nous contacter</a> —
|
||||||
</span>
|
</span>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
5
templates/django_filters/rest_framework/crispy_form.html
Normal file
5
templates/django_filters/rest_framework/crispy_form.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<h2>{% trans "Field filters" %}</h2>
|
||||||
|
{% crispy filter.form %}
|
6
templates/django_filters/rest_framework/form.html
Normal file
6
templates/django_filters/rest_framework/form.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<h2>{% trans "Field filters" %}</h2>
|
||||||
|
<form class="form" action="" method="get">
|
||||||
|
{{ filter.form.as_p }}
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
|
||||||
|
</form>
|
1
templates/django_filters/widgets/multiwidget.html
Normal file
1
templates/django_filters/widgets/multiwidget.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% for widget in widget.subwidgets %}{% include widget.template_name %}{% if forloop.first %}-{% endif %}{% endfor %}
|
@ -1,11 +1,12 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
|
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Clubs list" %}</a></p>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{form|crispy}}
|
{{form|crispy}}
|
||||||
<button class="btn btn-primary" type="submit">Submit</button>
|
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% render_table table %}
|
{% render_table table %}
|
||||||
|
|
||||||
<a class="btn btn-primary" href="{% url 'member:club_create' %}">New Club</a>
|
<a class="btn btn-primary" href="{% url 'member:club_create' %}">{% trans "New club" %}</a>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block extrajavascript %}
|
{% block extrajavascript %}
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Sign Up{% endblock %}
|
{% block title %}{% trans "Sign up" %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Sign up</h2>
|
<h2>{% trans "Sign up" %}</h2>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
{{ profile_form|crispy }}
|
{{ profile_form|crispy }}
|
||||||
<button class="btn btn-success" type="submit">
|
<button class="btn btn-success" type="submit">
|
||||||
{% trans "Sign Up" %}
|
{% trans "Sign up" %}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">Template Listing</a></p>
|
<p><a class="btn btn-default" href="{% url 'note:template_list' %}">{% trans "Buttons list" %}</a></p>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{form|crispy}}
|
{{form|crispy}}
|
||||||
|
@ -17,6 +17,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Vous pouvez aussi vous connecter via l'authentification centralisée <a href="{% url 'cas_login' %}">en suivant ce lien.</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
|
<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
|
||||||
{{ form | crispy }}
|
{{ form | crispy }}
|
||||||
<input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
|
<input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
|
||||||
|
2
tox.ini
2
tox.ini
@ -32,7 +32,7 @@ deps =
|
|||||||
pep8-naming
|
pep8-naming
|
||||||
pyflakes
|
pyflakes
|
||||||
commands =
|
commands =
|
||||||
flake8 apps/activity apps/api apps/member apps/note
|
flake8 apps/activity apps/api apps/logs apps/member apps/note
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
# Ignore too many errors, should be reduced in the future
|
# Ignore too many errors, should be reduced in the future
|
||||||
|
Loading…
Reference in New Issue
Block a user