mirror of
https://gitlab.com/animath/si/plateforme.git
synced 2025-06-21 07:18:25 +02:00
Clone Corres2math platform
This commit is contained in:
2
tfjm/__init__.py
Normal file
2
tfjm/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
19
tfjm/asgi.py
Normal file
19
tfjm/asgi.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
ASGI config for tfjm project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings')
|
||||
|
||||
application = get_asgi_application()
|
26
tfjm/lists.py
Normal file
26
tfjm/lists.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
_client = None
|
||||
|
||||
|
||||
def get_sympa_client():
|
||||
global _client
|
||||
if _client is None:
|
||||
if os.getenv("SYMPA_PASSWORD", None) is not None: # pragma: no cover
|
||||
from sympasoap import Client
|
||||
_client = Client("https://" + os.getenv("SYMPA_URL"))
|
||||
_client.login(os.getenv("SYMPA_EMAIL"), os.getenv("SYMPA_PASSWORD"))
|
||||
else:
|
||||
_client = FakeSympaSoapClient()
|
||||
return _client
|
||||
|
||||
|
||||
class FakeSympaSoapClient:
|
||||
"""
|
||||
Simulate a Sympa Soap client to run tests, if no Sympa instance is connected.
|
||||
"""
|
||||
def __getattribute__(self, item):
|
||||
return lambda *args, **kwargs: None
|
393
tfjm/matrix.py
Normal file
393
tfjm/matrix.py
Normal file
@ -0,0 +1,393 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from enum import Enum
|
||||
import os
|
||||
|
||||
from asgiref.sync import async_to_sync
|
||||
|
||||
|
||||
class Matrix:
|
||||
"""
|
||||
Utility class to manage interaction with the Matrix homeserver.
|
||||
This log in the @tfjmbot account (must be created before).
|
||||
The access token is then stored.
|
||||
All is done with this bot account, that is a server administrator.
|
||||
Tasks are normally asynchronous, but for compatibility we make
|
||||
them synchronous.
|
||||
"""
|
||||
_token = None
|
||||
_device_id = None
|
||||
|
||||
@classmethod
|
||||
async def _get_client(cls): # pragma: no cover
|
||||
"""
|
||||
Retrieve the bot account.
|
||||
If not logged, log in and store access token.
|
||||
"""
|
||||
if not os.getenv("SYNAPSE_PASSWORD"):
|
||||
return FakeMatrixClient()
|
||||
|
||||
from nio import AsyncClient
|
||||
client = AsyncClient("https://tfjm.org", "@tfjmbot:tfjm.org")
|
||||
client.user_id = "@tfjmbot:tfjm.org"
|
||||
|
||||
if os.path.isfile(".matrix_token"):
|
||||
with open(".matrix_device", "r") as f:
|
||||
cls._device_id = f.read().rstrip(" \t\r\n")
|
||||
client.device_id = cls._device_id
|
||||
with open(".matrix_token", "r") as f:
|
||||
cls._token = f.read().rstrip(" \t\r\n")
|
||||
client.access_token = cls._token
|
||||
return client
|
||||
|
||||
await client.login(password=os.getenv("SYNAPSE_PASSWORD"), device_name="Plateforme")
|
||||
cls._token = client.access_token
|
||||
cls._device_id = client.device_id
|
||||
with open(".matrix_token", "w") as f:
|
||||
f.write(cls._token)
|
||||
with open(".matrix_device", "w") as f:
|
||||
f.write(cls._device_id)
|
||||
return client
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def set_display_name(cls, name: str):
|
||||
"""
|
||||
Set the display name of the bot account.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
return await client.set_displayname(name)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def set_avatar(cls, avatar_url: str): # pragma: no cover
|
||||
"""
|
||||
Set the display avatar of the bot account.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
return await client.set_avatar(avatar_url)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def upload(
|
||||
cls,
|
||||
data_provider,
|
||||
content_type: str = "application/octet-stream",
|
||||
filename: str = None,
|
||||
encrypt: bool = False,
|
||||
monitor=None,
|
||||
filesize: int = None,
|
||||
): # pragma: no cover
|
||||
"""
|
||||
Upload a file to the content repository.
|
||||
|
||||
Returns a tuple containing:
|
||||
|
||||
- Either a `UploadResponse` if the request was successful, or a
|
||||
`UploadError` if there was an error with the request
|
||||
|
||||
- A dict with file decryption info if encrypt is ``True``,
|
||||
else ``None``.
|
||||
Args:
|
||||
data_provider (Callable, SynchronousFile, AsyncFile): A function
|
||||
returning the data to upload or a file object. File objects
|
||||
must be opened in binary mode (``mode="r+b"``). Callables
|
||||
returning a path string, Path, async iterable or aiofiles
|
||||
open binary file object allow the file data to be read in an
|
||||
asynchronous and lazy way (without reading the entire file
|
||||
into memory). Returning a synchronous iterable or standard
|
||||
open binary file object will still allow the data to be read
|
||||
lazily, but not asynchronously.
|
||||
|
||||
The function will be called again if the upload fails
|
||||
due to a server timeout, in which case it must restart
|
||||
from the beginning.
|
||||
Callables receive two arguments: the total number of
|
||||
429 "Too many request" errors that occured, and the total
|
||||
number of server timeout exceptions that occured, thus
|
||||
cleanup operations can be performed for retries if necessary.
|
||||
|
||||
content_type (str): The content MIME type of the file,
|
||||
e.g. "image/png".
|
||||
Defaults to "application/octet-stream", corresponding to a
|
||||
generic binary file.
|
||||
Custom values are ignored if encrypt is ``True``.
|
||||
|
||||
filename (str, optional): The file's original name.
|
||||
|
||||
encrypt (bool): If the file's content should be encrypted,
|
||||
necessary for files that will be sent to encrypted rooms.
|
||||
Defaults to ``False``.
|
||||
|
||||
monitor (TransferMonitor, optional): If a ``TransferMonitor``
|
||||
object is passed, it will be updated by this function while
|
||||
uploading.
|
||||
From this object, statistics such as currently
|
||||
transferred bytes or estimated remaining time can be gathered
|
||||
while the upload is running as a task; it also allows
|
||||
for pausing and cancelling.
|
||||
|
||||
filesize (int, optional): Size in bytes for the file to transfer.
|
||||
If left as ``None``, some servers might refuse the upload.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
return await client.upload(data_provider, content_type, filename, encrypt, monitor, filesize) \
|
||||
if not isinstance(client, FakeMatrixClient) else None, None
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def create_room(
|
||||
cls,
|
||||
visibility=None,
|
||||
alias=None,
|
||||
name=None,
|
||||
topic=None,
|
||||
room_version=None,
|
||||
federate=True,
|
||||
is_direct=False,
|
||||
preset=None,
|
||||
invite=(),
|
||||
initial_state=(),
|
||||
power_level_override=None,
|
||||
):
|
||||
"""
|
||||
Create a new room.
|
||||
|
||||
Returns either a `RoomCreateResponse` if the request was successful or
|
||||
a `RoomCreateError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
visibility (RoomVisibility): whether to have the room published in
|
||||
the server's room directory or not.
|
||||
Defaults to ``RoomVisibility.private``.
|
||||
|
||||
alias (str, optional): The desired canonical alias local part.
|
||||
For example, if set to "foo" and the room is created on the
|
||||
"example.com" server, the room alias will be
|
||||
"#foo:example.com".
|
||||
|
||||
name (str, optional): A name to set for the room.
|
||||
|
||||
topic (str, optional): A topic to set for the room.
|
||||
|
||||
room_version (str, optional): The room version to set.
|
||||
If not specified, the homeserver will use its default setting.
|
||||
If a version not supported by the homeserver is specified,
|
||||
a 400 ``M_UNSUPPORTED_ROOM_VERSION`` error will be returned.
|
||||
|
||||
federate (bool): Whether to allow users from other homeservers from
|
||||
joining the room. Defaults to ``True``.
|
||||
Cannot be changed later.
|
||||
|
||||
is_direct (bool): If this should be considered a
|
||||
direct messaging room.
|
||||
If ``True``, the server will set the ``is_direct`` flag on
|
||||
``m.room.member events`` sent to the users in ``invite``.
|
||||
Defaults to ``False``.
|
||||
|
||||
preset (RoomPreset, optional): The selected preset will set various
|
||||
rules for the room.
|
||||
If unspecified, the server will choose a preset from the
|
||||
``visibility``: ``RoomVisibility.public`` equates to
|
||||
``RoomPreset.public_chat``, and
|
||||
``RoomVisibility.private`` equates to a
|
||||
``RoomPreset.private_chat``.
|
||||
|
||||
invite (list): A list of user id to invite to the room.
|
||||
|
||||
initial_state (list): A list of state event dicts to send when
|
||||
the room is created.
|
||||
For example, a room could be made encrypted immediatly by
|
||||
having a ``m.room.encryption`` event dict.
|
||||
|
||||
power_level_override (dict): A ``m.room.power_levels content`` dict
|
||||
to override the default.
|
||||
The dict will be applied on top of the generated
|
||||
``m.room.power_levels`` event before it is sent to the room.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
return await client.room_create(
|
||||
visibility, alias, name, topic, room_version, federate, is_direct, preset, invite, initial_state,
|
||||
power_level_override)
|
||||
|
||||
@classmethod
|
||||
async def resolve_room_alias(cls, room_alias: str):
|
||||
"""
|
||||
Resolve a room alias to a room ID.
|
||||
Return None if the alias does not exist.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
resp = await client.room_resolve_alias(room_alias)
|
||||
return resp.room_id if resp and hasattr(resp, "room_id") else None
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def invite(cls, room_id: str, user_id: str):
|
||||
"""
|
||||
Invite a user to a room.
|
||||
|
||||
Returns either a `RoomInviteResponse` if the request was successful or
|
||||
a `RoomInviteError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
room_id (str): The room id of the room that the user will be
|
||||
invited to.
|
||||
user_id (str): The user id of the user that should be invited.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
return await client.room_invite(room_id, user_id)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def send_message(cls, room_id: str, body: str, formatted_body: str = None,
|
||||
msgtype: str = "m.text", html: bool = True):
|
||||
"""
|
||||
Send a message to a room.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
content = {
|
||||
"msgtype": msgtype,
|
||||
"body": body,
|
||||
"formatted_body": formatted_body or body,
|
||||
}
|
||||
if html:
|
||||
content["format"] = "org.matrix.custom.html"
|
||||
return await client.room_send(
|
||||
room_id=room_id,
|
||||
message_type="m.room.message",
|
||||
content=content,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def kick(cls, room_id: str, user_id: str, reason: str = None):
|
||||
"""
|
||||
Kick a user from a room, or withdraw their invitation.
|
||||
|
||||
Kicking a user adjusts their membership to "leave" with an optional
|
||||
reason.
|
||||
²
|
||||
Returns either a `RoomKickResponse` if the request was successful or
|
||||
a `RoomKickError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
room_id (str): The room id of the room that the user will be
|
||||
kicked from.
|
||||
user_id (str): The user_id of the user that should be kicked.
|
||||
reason (str, optional): A reason for which the user is kicked.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
return await client.room_kick(room_id, user_id, reason)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def set_room_power_level(cls, room_id: str, user_id: str, power_level: int): # pragma: no cover
|
||||
"""
|
||||
Put a given power level to a user in a certain room.
|
||||
|
||||
Returns either a `RoomPutStateResponse` if the request was successful or
|
||||
a `RoomPutStateError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
room_id (str): The room id of the room where the power level
|
||||
of the user should be updated.
|
||||
user_id (str): The user_id of the user which power level should
|
||||
be updated.
|
||||
power_level (int): The target power level to give.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if isinstance(client, FakeMatrixClient):
|
||||
return None
|
||||
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
resp = await client.room_get_state_event(room_id, "m.room.power_levels")
|
||||
content = resp.content
|
||||
content["users"][user_id] = power_level
|
||||
return await client.room_put_state(room_id, "m.room.power_levels", content=content, state_key=resp.state_key)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def set_room_power_level_event(cls, room_id: str, event: str, power_level: int): # pragma: no cover
|
||||
"""
|
||||
Define the minimal power level to have to send a certain event type
|
||||
in a given room.
|
||||
|
||||
Returns either a `RoomPutStateResponse` if the request was successful or
|
||||
a `RoomPutStateError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
room_id (str): The room id of the room where the power level
|
||||
of the event should be updated.
|
||||
event (str): The event name which minimal power level should
|
||||
be updated.
|
||||
power_level (int): The target power level to give.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if isinstance(client, FakeMatrixClient):
|
||||
return None
|
||||
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
resp = await client.room_get_state_event(room_id, "m.room.power_levels")
|
||||
content = resp.content
|
||||
if event.startswith("m."):
|
||||
content["events"][event] = power_level
|
||||
else:
|
||||
content[event] = power_level
|
||||
return await client.room_put_state(room_id, "m.room.power_levels", content=content, state_key=resp.state_key)
|
||||
|
||||
@classmethod
|
||||
@async_to_sync
|
||||
async def set_room_avatar(cls, room_id: str, avatar_uri: str):
|
||||
"""
|
||||
Define the avatar of a room.
|
||||
|
||||
Returns either a `RoomPutStateResponse` if the request was successful or
|
||||
a `RoomPutStateError` if there was an error with the request.
|
||||
|
||||
Args:
|
||||
room_id (str): The room id of the room where the avatar
|
||||
should be changed.
|
||||
avatar_uri (str): The internal avatar URI to apply.
|
||||
"""
|
||||
client = await cls._get_client()
|
||||
if room_id.startswith("#"):
|
||||
room_id = await cls.resolve_room_alias(room_id)
|
||||
return await client.room_put_state(room_id, "m.room.avatar", content={
|
||||
"url": avatar_uri
|
||||
}, state_key="")
|
||||
|
||||
|
||||
if os.getenv("SYNAPSE_PASSWORD"): # pragma: no cover
|
||||
from nio import RoomVisibility, RoomPreset
|
||||
RoomVisibility = RoomVisibility
|
||||
RoomPreset = RoomPreset
|
||||
else:
|
||||
# When running tests, faking matrix-nio classes to don't include the module
|
||||
class RoomVisibility(Enum):
|
||||
private = 'private'
|
||||
public = 'public'
|
||||
|
||||
class RoomPreset(Enum):
|
||||
private_chat = "private_chat"
|
||||
trusted_private_chat = "trusted_private_chat"
|
||||
public_chat = "public_chat"
|
||||
|
||||
|
||||
class FakeMatrixClient:
|
||||
"""
|
||||
Simulate a Matrix client to run tests, if no Matrix homeserver is connected.
|
||||
"""
|
||||
|
||||
def __getattribute__(self, item):
|
||||
async def func(*_, **_2):
|
||||
return None
|
||||
return func
|
85
tfjm/middlewares.py
Normal file
85
tfjm/middlewares.py
Normal file
@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from threading import local
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
|
||||
USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user')
|
||||
SESSION_ATTR_NAME = getattr(settings, 'LOCAL_SESSION_ATTR_NAME', '_current_session')
|
||||
IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip')
|
||||
|
||||
_thread_locals = local()
|
||||
|
||||
|
||||
def _set_current_user_and_ip(user=None, session=None, ip=None):
|
||||
setattr(_thread_locals, USER_ATTR_NAME, user)
|
||||
setattr(_thread_locals, SESSION_ATTR_NAME, session)
|
||||
setattr(_thread_locals, IP_ATTR_NAME, ip)
|
||||
|
||||
|
||||
def get_current_user() -> User:
|
||||
return getattr(_thread_locals, USER_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_ip() -> str:
|
||||
return getattr(_thread_locals, IP_ATTR_NAME, None)
|
||||
|
||||
|
||||
def get_current_authenticated_user():
|
||||
current_user = get_current_user()
|
||||
return None if isinstance(current_user, AnonymousUser) else current_user
|
||||
|
||||
|
||||
class SessionMiddleware(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):
|
||||
if "_fake_user_id" in request.session:
|
||||
request.user = User.objects.get(pk=request.session["_fake_user_id"])
|
||||
|
||||
user = request.user
|
||||
ip = request.META.get('HTTP_X_REAL_IP' if 'HTTP_X_REAL_IP' in request.META else 'REMOTE_ADDR')
|
||||
|
||||
_set_current_user_and_ip(user, request.session, ip)
|
||||
response = self.get_response(request)
|
||||
_set_current_user_and_ip(None, None, None)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class TurbolinksMiddleware(object): # pragma: no cover
|
||||
"""
|
||||
Send the `Turbolinks-Location` header in response to a visit that was redirected,
|
||||
and Turbolinks will replace the browser's topmost history entry.
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
|
||||
is_turbolinks = request.META.get('HTTP_TURBOLINKS_REFERRER')
|
||||
is_response_redirect = response.has_header('Location')
|
||||
|
||||
if is_turbolinks:
|
||||
if is_response_redirect:
|
||||
location = response['Location']
|
||||
prev_location = request.session.pop('_turbolinks_redirect_to', None)
|
||||
if prev_location is not None:
|
||||
# relative subsequent redirect
|
||||
if location.startswith('.'):
|
||||
location = prev_location.split('?')[0] + location
|
||||
request.session['_turbolinks_redirect_to'] = location
|
||||
else:
|
||||
if request.session.get('_turbolinks_redirect_to'):
|
||||
location = request.session.pop('_turbolinks_redirect_to')
|
||||
response['Turbolinks-Location'] = location
|
||||
return response
|
227
tfjm/settings.py
Normal file
227
tfjm/settings.py
Normal file
@ -0,0 +1,227 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
Django settings for tfjm project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.0.5.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
APPS_DIR = os.path.realpath(os.path.join(BASE_DIR, "apps"))
|
||||
sys.path.append(APPS_DIR)
|
||||
|
||||
ADMINS = [("Yohann D'ANELLO", "yohann.danello@animath.fr")]
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '6$wl1=ehfoiymin3m3i-wyx5d3t=1h7g4(j2izn*my)*yiq#he'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.forms',
|
||||
|
||||
'bootstrap_datepicker_plus',
|
||||
'crispy_forms',
|
||||
'django_tables2',
|
||||
'haystack',
|
||||
'logs',
|
||||
'polymorphic',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
|
||||
'api',
|
||||
'eastereggs',
|
||||
'registration',
|
||||
'participation',
|
||||
]
|
||||
|
||||
if "test" not in sys.argv: # pragma: no cover
|
||||
INSTALLED_APPS += [
|
||||
'cas_server',
|
||||
'django_extensions',
|
||||
'mailer',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.contrib.sites.middleware.CurrentSiteMiddleware',
|
||||
'tfjm.middlewares.SessionMiddleware',
|
||||
'tfjm.middlewares.TurbolinksMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'tfjm.urls'
|
||||
|
||||
LOGIN_REDIRECT_URL = "index"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'tfjm/templates')],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
|
||||
|
||||
WSGI_APPLICATION = 'tfjm.wsgi.application'
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
PASSWORD_HASHERS = [
|
||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||
'django.contrib.auth.hashers.BCryptPasswordHasher',
|
||||
]
|
||||
|
||||
CAS_AUTH_CLASS = 'registration.auth.CustomAuthUser'
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'rest_framework.permissions.IsAdminUser'
|
||||
],
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
],
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||
'PAGE_SIZE': 50,
|
||||
}
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en'
|
||||
|
||||
LANGUAGES = [
|
||||
('en', _('English')),
|
||||
('fr', _('French')),
|
||||
]
|
||||
|
||||
TIME_ZONE = 'Europe/Paris'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, "tfjm/static"),
|
||||
]
|
||||
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
|
||||
DJANGO_TABLES2_TEMPLATE = 'django_tables2/bootstrap4.html'
|
||||
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
|
||||
}
|
||||
}
|
||||
|
||||
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
|
||||
|
||||
_db_type = os.getenv('DJANGO_DB_TYPE', 'sqlite').lower()
|
||||
|
||||
if _db_type == 'mysql' or _db_type.startswith('postgres') or _db_type == 'psql': # pragma: no cover
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql' if _db_type == 'mysql' else 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': os.environ.get('DJANGO_DB_NAME', 'tfjm'),
|
||||
'USER': os.environ.get('DJANGO_DB_USER', 'tfjm'),
|
||||
'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': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, os.getenv('DJANGO_DB_HOST', 'db.sqlite3')),
|
||||
}
|
||||
}
|
||||
|
||||
if os.getenv("TFJM_STAGE", "dev") == "prod": # pragma: no cover
|
||||
from .settings_prod import * # noqa: F401,F403
|
||||
else:
|
||||
from .settings_dev import * # noqa: F401,F403
|
7
tfjm/settings_dev.py
Normal file
7
tfjm/settings_dev.py
Normal file
@ -0,0 +1,7 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
33
tfjm/settings_prod.py
Normal file
33
tfjm/settings_prod.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
# Break it, fix it!
|
||||
DEBUG = False
|
||||
|
||||
# Mandatory !
|
||||
ALLOWED_HOSTS = ['inscription.tfjm.org', 'plateforme.tfjm.org']
|
||||
|
||||
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
|
||||
|
||||
# Emails
|
||||
EMAIL_BACKEND = 'mailer.backend.DbBackend'
|
||||
MAILER_EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_USE_SSL = True
|
||||
EMAIL_HOST = os.getenv("SMTP_HOST")
|
||||
EMAIL_PORT = os.getenv("SMTP_PORT")
|
||||
EMAIL_HOST_USER = os.getenv("SMTP_HOST_USER")
|
||||
EMAIL_HOST_PASSWORD = os.getenv("SMTP_HOST_PASSWORD")
|
||||
|
||||
DEFAULT_FROM_EMAIL = os.getenv('FROM_EMAIL', 'Contact TFJM² <contact@tfjm.org>')
|
||||
SERVER_EMAIL = os.getenv('SERVER_EMAIL', 'contact@tfjm.org')
|
||||
|
||||
# Security settings
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = False
|
||||
SECURE_BROWSER_XSS_FILTER = False
|
||||
SESSION_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_SECURE = False
|
||||
CSRF_COOKIE_HTTPONLY = False
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
SESSION_COOKIE_AGE = 60 * 60 * 3
|
113
tfjm/static/Autorisation_droit_image_majeur.tex
Normal file
113
tfjm/static/Autorisation_droit_image_majeur.tex
Normal file
@ -0,0 +1,113 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[frenchb]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
|
||||
|
||||
\LARGE
|
||||
Autorisation d'enregistrement et de diffusion de l'image ({TOURNAMENT_NAME})
|
||||
\end{center}
|
||||
\normalsize
|
||||
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\bigskip
|
||||
|
||||
|
||||
|
||||
Je soussign\'e {PARTICIPANT_NAME}\\
|
||||
demeurant au {ADDRESS}
|
||||
|
||||
\medskip
|
||||
Cochez la/les cases correspondantes.\\
|
||||
\medskip
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ du {START_DATE} au {END_DATE} {YEAR} à : {PLACE}, \`a me photographier ou \`a me filmer et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser mon image sur tous ses supports d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des droits pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\
|
||||
|
||||
\medskip
|
||||
Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la publication et la diffusion de l'image ainsi que des commentaires l'accompagnant ne portent pas atteinte \`a la vie priv\'ee, \`a la dignit\'e et \`a la r\'eputation de la personne photographiée.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de photographies prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\
|
||||
|
||||
\medskip
|
||||
|
||||
Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es, de rectification, de modification et de suppression des donn\'ees qui vous concernent.
|
||||
Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre recommand\'ee avec accus\'e de r\'eception adress\'ee \`a Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant, pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses partenaires.
|
||||
|
||||
\bigskip
|
||||
|
||||
Signature pr\'ec\'ed\'ee de la mention \og lu et approuv\'e \fg{}
|
||||
|
||||
\medskip
|
||||
|
||||
|
||||
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{L'\'el\`eve :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le
|
||||
\end{minipage}
|
||||
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize
|
||||
\begin{flushright}
|
||||
Association agréée par\\le Ministère de l'éducation nationale.
|
||||
\end{flushright}
|
||||
\end{minipage}
|
||||
\end{document}
|
122
tfjm/static/Autorisation_droit_image_mineur.tex
Normal file
122
tfjm/static/Autorisation_droit_image_mineur.tex
Normal file
@ -0,0 +1,122 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[frenchb]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
|
||||
|
||||
\LARGE
|
||||
Autorisation d'enregistrement et de diffusion de l'image
|
||||
({TOURNAMENT_NAME})
|
||||
\end{center}
|
||||
\normalsize
|
||||
|
||||
|
||||
\thispagestyle{empty}
|
||||
|
||||
\bigskip
|
||||
|
||||
|
||||
|
||||
Je soussign\'e \dotfill (p\`ere, m\`ere, responsable l\'egal) \\
|
||||
agissant en qualit\'e de repr\'esentant de {PARTICIPANT_NAME}\\
|
||||
demeurant au {ADDRESS}
|
||||
|
||||
\medskip
|
||||
Cochez la/les cases correspondantes.\\
|
||||
\medskip
|
||||
|
||||
\fbox{\textcolor{white}{A}} Autorise l'association Animath, \`a l'occasion du $\mathbb{TFJM}^2$ du {START_DATE} au {END_DATE} {YEAR} à : {PLACE}, \`a photographier ou \`a filmer l'enfant et \`a diffuser les photos et/ou les vid\'eos r\'ealis\'ees \`a cette occasion sur son site et sur les sites partenaires. D\'eclare c\'eder \`a titre gracieux \`a Animath le droit d’utiliser l'image de l'enfant sur tous ses supports d'information : brochures, sites web, r\'eseaux sociaux. Animath devient, par la pr\'esente, cessionnaire des droits pendant toute la dur\'ee pour laquelle ont \'et\'e acquis les droits d'auteur de ces photographies.\\
|
||||
|
||||
\medskip
|
||||
Animath s'engage, conform\'ement aux dispositions l\'egales en vigueur relatives au droit \`a l'image, \`a ce que la publication et la diffusion de l'image de l'enfant ainsi que des commentaires l'accompagnant ne portent pas atteinte \`a la vie priv\'ee, \`a la dignit\'e et \`a la r\'eputation de l’enfant.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise la diffusion dans les medias (Presse, T\'el\'evision, Internet) de photographies de mon enfant prises \`a l'occasion d’une \'eventuelle m\'ediatisation de cet événement.\\
|
||||
|
||||
\medskip
|
||||
|
||||
Conform\'ement \`a la loi informatique et libert\'es du 6 janvier 1978, vous disposez d'un droit de libre acc\`es, de rectification, de modification et de suppression des donn\'ees qui vous concernent.
|
||||
Cette autorisation est donc r\'evocable \`a tout moment sur volont\'e express\'ement manifest\'ee par lettre recommand\'ee avec accus\'e de r\'eception adress\'ee \`a Animath, IHP, 11 rue Pierre et Marie Curie, 75231 Paris cedex 05.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} Autorise Animath à conserver mes données personnelles, dans le cadre défini par la loi n 78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés et les textes la modifiant, pendant une durée de quatre ans à compter de ma dernière participation à un événement organisé par Animath.\\
|
||||
|
||||
\medskip
|
||||
\fbox{\textcolor{white}{A}} J'accepte d'être tenu informé d'autres activités organisées par l'association et ses partenaires.
|
||||
|
||||
\bigskip
|
||||
|
||||
Signatures pr\'ec\'ed\'ees de la mention \og lu et approuv\'e \fg{}
|
||||
|
||||
\medskip
|
||||
|
||||
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{Le responsable l\'egal :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le :
|
||||
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
|
||||
\underline{L'\'el\`eve :}\\
|
||||
|
||||
Fait \`a :\\
|
||||
le
|
||||
\end{minipage}
|
||||
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018
|
||||
\end{minipage}
|
||||
\begin{minipage}[c]{0.5\textwidth}
|
||||
\footnotesize
|
||||
\begin{flushright}
|
||||
Association agréée par\\le Ministère de l'éducation nationale.
|
||||
\end{flushright}
|
||||
\end{minipage}
|
||||
\end{document}
|
66
tfjm/static/Autorisation_parentale.tex
Normal file
66
tfjm/static/Autorisation_parentale.tex
Normal file
@ -0,0 +1,66 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[french]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{55pt}{55pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
\vfill
|
||||
|
||||
\begin{center}
|
||||
\Large \bf Autorisation parentale pour les mineurs ({TOURNAMENT_NAME})
|
||||
\end{center}
|
||||
|
||||
Je soussigné(e) \hrulefill,\\
|
||||
responsable légal, demeurant \writingsep\hrulefill\\
|
||||
\writingsep\hrulefill,\\
|
||||
\writingsep autorise {PARTICIPANT_NAME},\\
|
||||
né(e) le {BIRTHDAY},
|
||||
à participer au Tournoi Français des Jeunes Mathématiciennes et Mathématiciens ($\mathbb{TFJM}^2$) organisé \`a : {PLACE}, du {START_DATE} au {END_DATE} {YEAR}.
|
||||
|
||||
{PRONOUN} se rendra au lieu indiqu\'e ci-dessus le vendredi matin et quittera les lieux l'après-midi du dimanche par ses propres moyens et sous la responsabilité du représentant légal.
|
||||
|
||||
|
||||
|
||||
\vspace{8ex}
|
||||
|
||||
Fait à \vrule width 10cm height 0pt depth 0.4pt, le \phantom{232323}/\phantom{XXX}/{YEAR},
|
||||
|
||||
\vfill
|
||||
\vfill
|
||||
|
||||
\end{document}
|
BIN
tfjm/static/Fiche synthèse.pdf
Normal file
BIN
tfjm/static/Fiche synthèse.pdf
Normal file
Binary file not shown.
194
tfjm/static/Fiche synthèse.tex
Normal file
194
tfjm/static/Fiche synthèse.tex
Normal file
@ -0,0 +1,194 @@
|
||||
\documentclass{article}
|
||||
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[french]{babel}
|
||||
\usepackage{graphicx}
|
||||
|
||||
\usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry} % marges
|
||||
|
||||
\usepackage{amsthm}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{tikz}
|
||||
|
||||
\newcommand{\N}{{\bf N}}
|
||||
\newcommand{\Z}{{\bf Z}}
|
||||
\newcommand{\Q}{{\bf Q}}
|
||||
\newcommand{\R}{{\bf R}}
|
||||
\newcommand{\C}{{\bf C}}
|
||||
\newcommand{\A}{{\bf A}}
|
||||
|
||||
\newtheorem{theo}{Théorème}
|
||||
\newtheorem{theo-defi}[theo]{Théorème-Définition}
|
||||
\newtheorem{defi}[theo]{Définition}
|
||||
\newtheorem{lemme}[theo]{Lemme}
|
||||
\newtheorem{slemme}[theo]{Sous-lemme}
|
||||
\newtheorem{prop}[theo]{Proposition}
|
||||
\newtheorem{coro}[theo]{Corollaire}
|
||||
\newtheorem{conj}[theo]{Conjecture}
|
||||
|
||||
\title{Note de synthèse}
|
||||
|
||||
\begin{document}
|
||||
\pagestyle{empty}
|
||||
|
||||
\begin{center}
|
||||
\begin{Huge}
|
||||
$\mathbb{TFJM}^2$
|
||||
\end{Huge}
|
||||
|
||||
\bigskip
|
||||
|
||||
\begin{Large}
|
||||
NOTE DE SYNTHESE
|
||||
\end{Large}
|
||||
\end{center}
|
||||
|
||||
Tour \underline{~~~~} poule \underline{~~~~}
|
||||
|
||||
\medskip
|
||||
|
||||
Problème \underline{~~~~} défendu par l'équipe \underline{~~~~~~~~~~~~~~~~~~~~~~~~}
|
||||
|
||||
\medskip
|
||||
|
||||
Synthèse par l'équipe \underline{~~~~~~~~~~~~~~~~~~~~~~~~} dans le rôle de : ~ $\square$ Opposant ~ $\square$ Rapporteur
|
||||
|
||||
\section*{Questions traitées}
|
||||
|
||||
\begin{tabular}{r c l}
|
||||
\begin{tabular}{|c|c|c|c|c|c|}
|
||||
\hline
|
||||
Question ~ & ER & ~PR~ & QE & NT \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
\end{tabular}
|
||||
& ~~ &
|
||||
\begin{tabular}{|c|c|c|c|c|c|}
|
||||
\hline
|
||||
Question ~ & ER & ~PR~ & QE & NT \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
& & & & \\
|
||||
\hline
|
||||
\end{tabular} \\
|
||||
|
||||
& & \\
|
||||
|
||||
ER : entièrement résolue & & PR : partiellement résolue \\
|
||||
|
||||
\smallskip
|
||||
|
||||
QE : quelques éléments de réponse & & NT : non traitée
|
||||
\end{tabular}
|
||||
|
||||
~
|
||||
|
||||
\smallskip
|
||||
|
||||
Remarque : il est possible de cocher entre les cases pour un cas intermédiaire.
|
||||
|
||||
\section*{Evaluation qualitative de la solution}
|
||||
|
||||
Donnez votre avis concernant la solution. Mettez notamment en valeur les points positifs (des idées
|
||||
importantes, originales, etc.) et précisez ce qui aurait pu améliorer la solution.
|
||||
|
||||
\vfill
|
||||
|
||||
\textbf{Evaluation générale :} ~ $\square$ Excellente ~ $\square$ Bonne ~ $\square$ Suffisante ~ $\square$ Passable
|
||||
|
||||
\newpage
|
||||
|
||||
\section*{Erreurs et imprécisions}
|
||||
|
||||
Listez ci-dessous les cinq erreurs et/ou imprécisions les plus importantes selon vous, par ordre d'importance, en précisant la
|
||||
question concernée, la page, le paragraphe et le type de remarque.
|
||||
|
||||
\bigskip
|
||||
|
||||
1. Question \underline{~~~~} Page \underline{~~~~} Paragraphe \underline{~~~~}
|
||||
|
||||
$\square$ Erreur majeure ~ $\square$ Erreur mineure ~ $\square$ Imprécision ~ $\square$ Autre : \underline{~~~~~~~~}
|
||||
|
||||
Description :
|
||||
|
||||
\vfill
|
||||
|
||||
2. Question \underline{~~~~} Page \underline{~~~~} Paragraphe \underline{~~~~}
|
||||
|
||||
$\square$ Erreur majeure ~ $\square$ Erreur mineure ~ $\square$ Imprécision ~ $\square$ Autre : \underline{~~~~~~~~}
|
||||
|
||||
Description :
|
||||
|
||||
\vfill
|
||||
|
||||
3. Question \underline{~~~~} Page \underline{~~~~} Paragraphe \underline{~~~~}
|
||||
|
||||
$\square$ Erreur majeure ~ $\square$ Erreur mineure ~ $\square$ Imprécision ~ $\square$ Autre : \underline{~~~~~~~~}
|
||||
|
||||
Description :
|
||||
|
||||
\vfill
|
||||
|
||||
4. Question \underline{~~~~} Page \underline{~~~~} Paragraphe \underline{~~~~}
|
||||
|
||||
$\square$ Erreur majeure ~ $\square$ Erreur mineure ~ $\square$ Imprécision ~ $\square$ Autre : \underline{~~~~~~~~}
|
||||
|
||||
Description :
|
||||
|
||||
\vfill
|
||||
|
||||
5. Question \underline{~~~~} Page \underline{~~~~} Paragraphe \underline{~~~~}
|
||||
|
||||
$\square$ Erreur majeure ~ $\square$ Erreur mineure ~ $\square$ Imprécision ~ $\square$ Autre : \underline{~~~~~~~~}
|
||||
|
||||
Description :
|
||||
|
||||
\vfill
|
||||
|
||||
\section*{Remarques formelles (facultatif)}
|
||||
|
||||
Donnez votre avis concernant la présentation de la solution (lisibilité, etc.).
|
||||
|
||||
\vfill
|
||||
|
||||
|
||||
|
||||
\end{document}
|
BIN
tfjm/static/Fiche_sanitaire.pdf
Normal file
BIN
tfjm/static/Fiche_sanitaire.pdf
Normal file
Binary file not shown.
88
tfjm/static/Instructions.tex
Normal file
88
tfjm/static/Instructions.tex
Normal file
@ -0,0 +1,88 @@
|
||||
\documentclass[a4paper,french,11pt]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[frenchb]{babel}
|
||||
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
%\usepackage{anyfontsize}
|
||||
\usepackage{fancybox}
|
||||
\usepackage{eso-pic,graphicx}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{hyperref}
|
||||
|
||||
|
||||
% Specials
|
||||
\newcommand{\writingsep}{\vrule height 4ex width 0pt}
|
||||
|
||||
% Page formating
|
||||
\hoffset -1in
|
||||
\voffset -1in
|
||||
\textwidth 180 mm
|
||||
\textheight 250 mm
|
||||
\oddsidemargin 15mm
|
||||
\evensidemargin 15mm
|
||||
\pagestyle{fancy}
|
||||
|
||||
% Headers and footers
|
||||
\fancyfoot{}
|
||||
\lhead{}
|
||||
\rhead{}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\lfoot{\footnotesize 11 rue Pierre et Marie Curie, 75231 Paris Cedex 05\\ Numéro siret 431 598 366 00018}
|
||||
\rfoot{\footnotesize Association agréée par\\le Ministère de l'éducation nationale.}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\includegraphics[height=2cm]{assets/logo_animath.png}\hfill{\fontsize{50pt}{50pt}{$\mathbb{TFJM}^2$}}
|
||||
|
||||
|
||||
|
||||
\begin{center}
|
||||
\Large \bf Instructions ({TOURNAMENT_NAME})
|
||||
\end{center}
|
||||
|
||||
\section{Documents}
|
||||
\subsection{Autorisation parentale}
|
||||
Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi).
|
||||
|
||||
\subsection{Autorisation de prise de vue}
|
||||
Si l'élève est mineur \textbf{au moment de la signature}, il convient de remplir l'autorisation pour les mineurs. En revanche, s'il est majeur \textbf{au moment de la signature}, il convient de remplir la fiche pour majeur.
|
||||
|
||||
\subsection{Fiche sanitaire}
|
||||
Elle est nécessaire si l'élève est mineur au moment du tournoi (y compris si son anniversaire est pendant le tournoi).
|
||||
|
||||
|
||||
\section{Paiement}
|
||||
|
||||
\subsection{Montant}
|
||||
Les frais d'inscription sont fixés à {PRICE} euros. Vous devez vous en acquitter \textbf{avant le {END_PAYMENT_DATE} {YEAR}}. Si l'élève est boursier, il en est dispensé, vous devez alors fournir une copie de sa notification de bourse directement sur la plateforme \textbf{avant le {END_PAYMENT_DATE} {YEAR}}.
|
||||
|
||||
\subsection{Procédure}
|
||||
|
||||
Si le paiement de plusieurs élèves est fait en une seule opération, merci de contacter \href{mailto: contact@tfjm.org}{contact@tfjm.org} \textbf{avant le paiement} pour garantir l'identification de ce dernier
|
||||
|
||||
\subsubsection*{Carte bancaire (uniquement les cartes françaises)}
|
||||
Le paiement s'effectue en ligne via la plateforme à l'adresse : \url{https://www.helloasso.com/associations/animath/evenements/tfjm-2020}
|
||||
|
||||
Vous devez impérativement indiquer dans le champ "Référence" la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}.
|
||||
|
||||
\subsubsection*{Virement}
|
||||
\textbf{Si vous ne pouvez pas utiliser le paiement par carte}, vous pouvez faire un virement sur le compte ci-dessous en indiquant bien dans le champ "motif" (ou autre champ propre à votre banque dont le contenu est communiqué au destinataire) la mention "TFJMpu" suivie des noms et prénoms \textbf{de l'élève}.
|
||||
|
||||
IBAN FR76 1027 8065 0000 0206 4290 127
|
||||
|
||||
BIC CMCIFR2A
|
||||
|
||||
\subsubsection*{Autre}
|
||||
|
||||
Si aucune de ces procédures n'est possible pour vous, envoyez un mail à \href{mailto: contact@tfjm.org}{contact@tfjm.org} pour que nous trouvions une solution à vos difficultés.
|
||||
|
||||
|
||||
|
||||
|
||||
\end{document}
|
BIN
tfjm/static/favicon.ico
Normal file
BIN
tfjm/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
114
tfjm/static/logo.svg
Normal file
114
tfjm/static/logo.svg
Normal file
@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="209.843 -2.284 30.311995 9.7779996"
|
||||
version="1.1"
|
||||
id="svg27"
|
||||
sodipodi:docname="logo.svg"
|
||||
width="30.311995"
|
||||
height="9.7779999"
|
||||
style="fill:black"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||
<metadata
|
||||
id="metadata31">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1055"
|
||||
id="namedview29"
|
||||
showgrid="false"
|
||||
inkscape:zoom="41.779237"
|
||||
inkscape:cx="15.215997"
|
||||
inkscape:cy="4.3644999"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg27" />
|
||||
<defs
|
||||
id="defs15">
|
||||
<path
|
||||
id="b"
|
||||
d="m 2.58,-3.347 c 0.409,0 1.405,0.02 1.485,1.135 0.01,0.12 0.02,0.25 0.18,0.25 0.168,0 0.168,-0.14 0.168,-0.32 v -2.7 c 0,-0.159 0,-0.318 -0.169,-0.318 -0.13,0 -0.17,0.1 -0.18,0.21 -0.059,1.155 -0.756,1.354 -1.484,1.384 v -2.102 c 0,-0.668 0.19,-0.668 0.429,-0.668 h 0.468 c 1.275,0 1.923,0.688 1.983,1.375 0.01,0.08 0.02,0.23 0.179,0.23 0.17,0 0.17,-0.16 0.17,-0.33 v -1.295 c 0,-0.308 -0.02,-0.328 -0.33,-0.328 h -5 c -0.18,0 -0.34,0 -0.34,0.179 0,0.17 0.19,0.17 0.27,0.17 0.567,0 0.607,0.079 0.607,0.567 v 4.991 c 0,0.469 -0.03,0.568 -0.558,0.568 -0.15,0 -0.319,0 -0.319,0.17 C 0.14,0 0.3,0 0.48,0 h 2.878 c 0.18,0 0.33,0 0.33,-0.18 0,-0.169 -0.17,-0.169 -0.3,-0.169 -0.767,0 -0.807,-0.07 -0.807,-0.597 v -2.401 z m 2.88,-3.129 v 0.469 A 2.557,2.557 0 0 0 4.922,-6.476 Z M 4.065,-3.158 A 1.51,1.51 0 0 0 3.537,-3.547 c 0.189,-0.09 0.388,-0.249 0.528,-0.418 z m -2.7,-2.77 c 0,-0.12 0,-0.368 -0.08,-0.548 h 1.056 c -0.11,0.23 -0.11,0.558 -0.11,0.648 v 4.901 c 0,0.15 0,0.389 0.1,0.578 H 1.285 c 0.08,-0.179 0.08,-0.428 0.08,-0.548 v -5.03 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="c"
|
||||
d="m 1.564,-6.824 c -0.18,0 -0.339,0 -0.339,0.179 0,0.17 0.18,0.17 0.29,0.17 0.687,0 0.727,0.069 0.727,0.577 v 5.59 c 0,0.169 0,0.358 -0.17,0.527 -0.08,0.07 -0.239,0.18 -0.478,0.18 -0.07,0 -0.369,0 -0.369,-0.11 0,-0.08 0.04,-0.12 0.09,-0.17 A 0.704,0.704 0 0 0 0.777,-1.057 0.704,0.704 0 0 0 0.06,-0.359 c 0,0.629 0.637,1.106 1.604,1.106 1.106,0 2.042,-0.387 2.192,-1.614 0.01,-0.09 0.01,-0.647 0.01,-0.966 v -4.184 c 0,-0.449 0.139,-0.449 0.707,-0.459 0.09,0 0.17,-0.08 0.17,-0.17 0,-0.178 -0.15,-0.178 -0.33,-0.178 z M 0.867,0.239 C 0.767,0.19 0.408,0.02 0.408,-0.349 c 0,-0.259 0.22,-0.358 0.37,-0.358 0.168,0 0.368,0.12 0.368,0.348 0,0.15 -0.08,0.24 -0.12,0.27 -0.04,0.04 -0.13,0.139 -0.16,0.328 z M 2.59,-5.918 c 0,-0.11 0,-0.378 -0.09,-0.558 h 1.097 c -0.08,0.18 -0.08,0.369 -0.08,0.708 v 4.015 c 0,0.298 0,0.797 -0.01,0.896 C 3.427,-0.349 3.198,0.11 2.44,0.31 2.59,0.08 2.59,-0.109 2.59,-0.288 v -5.629 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="d"
|
||||
d="M 4.643,-2.092 2.74,-6.625 c -0.08,-0.2 -0.09,-0.2 -0.359,-0.2 H 0.528 c -0.18,0 -0.329,0 -0.329,0.18 0,0.17 0.18,0.17 0.23,0.17 0.119,0 0.388,0.02 0.607,0.099 v 5.32 c 0,0.21 0,0.648 -0.677,0.707 -0.19,0.02 -0.19,0.16 -0.19,0.17 C 0.17,0 0.33,0 0.51,0 h 1.543 c 0.18,0 0.33,0 0.33,-0.18 0,-0.089 -0.08,-0.159 -0.16,-0.169 -0.767,-0.06 -0.767,-0.478 -0.767,-0.707 v -4.961 l 0.01,-0.01 2.429,5.817 c 0.08,0.18 0.15,0.209 0.21,0.209 0.12,0 0.149,-0.08 0.199,-0.2 l 2.44,-5.827 0.01,0.01 v 4.961 c 0,0.21 0,0.648 -0.677,0.707 -0.19,0.02 -0.19,0.16 -0.19,0.17 0,0.179 0.16,0.179 0.34,0.179 h 2.66 c 0.179,0 0.328,0 0.328,-0.18 C 9.215,-0.27 9.135,-0.34 9.056,-0.35 8.289,-0.41 8.289,-0.828 8.289,-1.057 v -4.712 c 0,-0.21 0,-0.648 0.677,-0.708 0.1,-0.01 0.19,-0.06 0.19,-0.17 0,-0.178 -0.15,-0.178 -0.33,-0.178 H 6.905 c -0.259,0 -0.279,0 -0.369,0.209 z m -0.3,0.18 c 0.08,0.169 0.09,0.178 0.21,0.218 L 4.115,-0.638 H 4.095 L 1.823,-6.058 C 1.773,-6.187 1.693,-6.356 1.554,-6.476 h 0.867 l 1.923,4.563 z M 1.336,-0.35 h -0.17 c 0.02,-0.03 0.04,-0.06 0.06,-0.08 0.01,-0.01 0.01,-0.02 0.02,-0.03 z M 7.104,-6.477 H 8.16 c -0.219,0.25 -0.219,0.508 -0.219,0.688 v 4.752 c 0,0.18 0,0.438 0.23,0.687 H 6.883 c 0.22,-0.249 0.22,-0.508 0.22,-0.687 v -5.44 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="a"
|
||||
d="m 4.135,-6.466 c 1.305,0.07 1.793,0.917 1.833,1.385 0.01,0.15 0.02,0.299 0.179,0.299 0.18,0 0.18,-0.17 0.18,-0.359 v -1.325 c 0,-0.348 -0.04,-0.358 -0.34,-0.358 H 0.658 c -0.308,0 -0.328,0.02 -0.328,0.318 V -5.1 c 0,0.16 0,0.319 0.17,0.319 0.17,0 0.178,-0.18 0.178,-0.2 0.04,-0.826 0.788,-1.424 1.834,-1.484 v 5.54 c 0,0.498 -0.04,0.577 -0.668,0.577 -0.12,0 -0.299,0 -0.299,0.17 0,0.179 0.16,0.179 0.339,0.179 h 2.89 C 4.95,0 5.1,0 5.1,-0.18 c 0,-0.169 -0.17,-0.169 -0.28,-0.169 -0.647,0 -0.686,-0.07 -0.686,-0.578 v -5.539 z m -3.458,-0.01 h 0.598 c -0.249,0.15 -0.458,0.349 -0.598,0.518 z m 5.3,0 v 0.528 A 2.606,2.606 0 0 0 5.37,-6.476 H 5.978 Z M 2.77,-0.349 c 0.09,-0.179 0.09,-0.428 0.09,-0.558 v -5.569 h 0.926 v 5.57 c 0,0.129 0,0.378 0.09,0.557 H 2.77 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
id="e"
|
||||
d="M 3.522,-1.27 H 3.285 c -0.021,0.154 -0.091,0.566 -0.182,0.635 -0.055,0.042 -0.592,0.042 -0.69,0.042 H 1.13 c 0.732,-0.648 0.976,-0.844 1.395,-1.171 0.516,-0.412 0.997,-0.844 0.997,-1.507 0,-0.844 -0.74,-1.36 -1.632,-1.36 -0.865,0 -1.45,0.607 -1.45,1.249 0,0.355 0.3,0.39 0.369,0.39 0.167,0 0.37,-0.118 0.37,-0.37 0,-0.125 -0.05,-0.369 -0.412,-0.369 0.216,-0.495 0.69,-0.649 1.018,-0.649 0.698,0 1.06,0.544 1.06,1.11 0,0.606 -0.432,1.087 -0.655,1.338 l -1.68,1.66 C 0.44,-0.209 0.44,-0.195 0.44,0 h 2.873 z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</defs>
|
||||
<use
|
||||
x="209.843"
|
||||
y="6.6110001"
|
||||
xlink:href="#a"
|
||||
id="use17"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-0.33000232,0.13600003)" />
|
||||
<use
|
||||
x="216.485"
|
||||
y="6.6110001"
|
||||
xlink:href="#b"
|
||||
id="use19"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-0.33000232,0.13600003)" />
|
||||
<use
|
||||
x="222.573"
|
||||
y="6.6110001"
|
||||
xlink:href="#c"
|
||||
id="use21"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-0.33000232,0.13600003)" />
|
||||
<use
|
||||
x="227.554"
|
||||
y="6.6110001"
|
||||
xlink:href="#d"
|
||||
id="use23"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-0.33000232,0.13600003)" />
|
||||
<use
|
||||
x="236.963"
|
||||
y="2.211"
|
||||
xlink:href="#e"
|
||||
id="use25"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(-0.33000232,0.13600003)" />
|
||||
</svg>
|
After Width: | Height: | Size: 7.1 KiB |
BIN
tfjm/static/logo_animath.png
Normal file
BIN
tfjm/static/logo_animath.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 104 KiB |
8
tfjm/templates/400.html
Normal file
8
tfjm/templates/400.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Bad request" %}</h1>
|
||||
{% blocktrans %}Sorry, your request was bad. Don't know what could be wrong. An email has been sent to webmasters with the details of the error. You can now watch some videos.{% endblocktrans %}
|
||||
{% endblock %}
|
13
tfjm/templates/403.html
Normal file
13
tfjm/templates/403.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Permission denied" %}</h1>
|
||||
{% blocktrans %}You don't have the right to perform this request.{% endblocktrans %}
|
||||
{% if exception %}
|
||||
<div>
|
||||
{% trans "Exception message:" %} {{ exception }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
13
tfjm/templates/404.html
Normal file
13
tfjm/templates/404.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Page not found" %}</h1>
|
||||
{% blocktrans %}The requested path <code>{{ request_path }}</code> was not found on the server.{% endblocktrans %}
|
||||
{% if exception != "Resolver404" %}
|
||||
<div>
|
||||
{% trans "Exception message:" %} {{ exception }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
8
tfjm/templates/500.html
Normal file
8
tfjm/templates/500.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Server error" %}</h1>
|
||||
{% blocktrans %}Sorry, an error occurred when processing your request. An email has been sent to webmasters with the detail of the error, and this will be fixed soon. You can now watch some videos.{% endblocktrans %}
|
||||
{% endblock %}
|
40
tfjm/templates/about.html
Normal file
40
tfjm/templates/about.html
Normal file
@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block contenttitle %}
|
||||
<h1>À propos</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
La plateforme d'inscription du TFJM² des Jeunes Mathématiciennes a été développée entre 2019 et 2021
|
||||
par Yohann D'ANELLO, bénévole pour l'association Animath. Elle est vouée à être utilisée par les participants
|
||||
pour intéragir avec les organisateurs et les autres participants.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
La plateforme est développée avec le framework <a href="https://www.djangoproject.com/">Django</a> et le code
|
||||
source est accessible librement sur <a href="https://gitlab.com/animath/si/plateforme-tfjm">Gitlab</a>.
|
||||
Le code est distribué sous la licence <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPL v3</a>,
|
||||
qui vous autorise à consulter le code, à le partager, à réutiliser des parties du code et à contribuer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Le site principal présent sur <a href="https://inscription.tfjm.org/">https://inscription.tfjm.org</a>
|
||||
est hébergé chez <a href="https://www.scaleway.com/fr/">Scaleway</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Les données collectées par cette plateforme sont utilisées uniquement dans le cadre du TFJM² et sont
|
||||
détruites dès l'action touche à sa fin, soit au plus tard 1 an après le début de l'action. Sur autorisation
|
||||
explicite, des informations de contact peuvent être conservées afin d'être tenu au courant des actions futures
|
||||
de l'association Animath. Aucune information personnelle n'est collectée à votre insu. Aucune information
|
||||
personnelle n'est cédée à des tiers.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pour toute demande ou réclammation, merci de nous contacter à l'adresse
|
||||
<a target="_blank" href="mailto:contact@correspondances-maths.fr">
|
||||
contact@correspondances-maths.fr
|
||||
</a>.
|
||||
</p>
|
||||
{% endblock %}
|
11
tfjm/templates/amount_input.html
Normal file
11
tfjm/templates/amount_input.html
Normal file
@ -0,0 +1,11 @@
|
||||
<div class="input-group">
|
||||
<input class="form-control mx-auto d-block" type="number" min="0" step="0.01"
|
||||
{% if widget.value != None and widget.value != "" %}value="{{ widget.value }}"{% endif %}
|
||||
name="{{ widget.name }}"
|
||||
{% for name, value in widget.attrs.items %}
|
||||
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
|
||||
{% endfor %}>
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">€</span>
|
||||
</div>
|
||||
</div>
|
9
tfjm/templates/autocomplete_model.html
Normal file
9
tfjm/templates/autocomplete_model.html
Normal file
@ -0,0 +1,9 @@
|
||||
<input type="hidden" name="{{ widget.name }}" {% if widget.attrs.model_pk %}value="{{ widget.attrs.model_pk }}"{% endif %} id="{{ widget.attrs.id }}_pk">
|
||||
<input type="text"
|
||||
{% if widget.value != None and widget.value != "" %}value="{{ widget.value }}"{% endif %}
|
||||
name="{{ widget.name }}_name" autocomplete="off"
|
||||
{% for name, value in widget.attrs.items %}
|
||||
{% ifnotequal value False %}{{ name }}{% ifnotequal value True %}="{{ value|stringformat:'s' }}"{% endifnotequal %}{% endifnotequal %}
|
||||
{% endfor %}>
|
||||
<ul class="list-group list-group-flush" id="{{ widget.attrs.id }}_list">
|
||||
</ul>
|
305
tfjm/templates/base.html
Normal file
305
tfjm/templates/base.html
Normal file
@ -0,0 +1,305 @@
|
||||
{% load static i18n static calendar %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:"en" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %} class="position-relative h-100">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>
|
||||
{% block title %}{{ title }}{% endblock title %} - Plateforme du TFJM²
|
||||
</title>
|
||||
<meta name="description" content="Plateform d'inscription au TFJM².">
|
||||
|
||||
{# Favicon #}
|
||||
<link rel="shortcut icon" href="{% static "favicon.ico" %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
{% if no_cache %}
|
||||
<meta name="turbolinks-cache-control" content="no-cache">
|
||||
{% endif %}
|
||||
|
||||
{# Bootstrap CSS #}
|
||||
<link rel="stylesheet"
|
||||
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||
crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/v4-shims.css">
|
||||
|
||||
{# JQuery, Bootstrap and Turbolinks JavaScript #}
|
||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js"
|
||||
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
|
||||
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
|
||||
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/turbolinks/5.2.0/turbolinks.js"
|
||||
crossorigin="anonymous"></script>
|
||||
|
||||
{# Si un formulaire requiert des données supplémentaires (notamment JS), les données sont chargées #}
|
||||
{% if form.media %}
|
||||
{{ form.media }}
|
||||
{% endif %}
|
||||
|
||||
{% block extracss %}{% endblock %}
|
||||
</head>
|
||||
<body class="d-flex w-100 h-100 flex-column">
|
||||
<main class="mb-auto">
|
||||
<nav class="navbar navbar-expand-md navbar-light bg-light fixed-navbar shadow-sm">
|
||||
<a class="navbar-brand" href="https://tfjm.org/">
|
||||
<img src="{% static "logo.svg" %}" style="width: 42px;" alt="Logo TFJM²" id="navbar-logo">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse"
|
||||
data-target="#navbarNavDropdown"
|
||||
aria-controls="navbarNavDropdown" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div id="navbarNavDropdown" class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "index" %}" class="nav-link"><i class="fas fa-home"></i> {% trans "Home" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
{% if user.registration.is_admin %}
|
||||
<a href="{% url "participation:calendar" %}" class="nav-link"><i class="fas fa-calendar"></i> {% trans "Calendar" %}</a>
|
||||
{% else %}
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#calendarModal"><i class="fas fa-calendar"></i> {% trans "Calendar" %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% if user.is_authenticated and user.registration.is_admin %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
|
||||
</li>
|
||||
{% elif user.is_authenticated and user.registration.participates %}
|
||||
{% if not user.registration.team %}
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#createTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Create team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#joinTeamModal">
|
||||
<i class="fas fa-users"></i> {% trans "Join team" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_team_detail" %}" class="nav-link">
|
||||
<i class="fas fa-users"></i> {% trans "My team" %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "participation:my_participation_detail" %}" class="nav-link">
|
||||
<i class="fas fa-video"></i> {% trans "My participation" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "participation:chat" %}">
|
||||
<i class="fas fa-comments"></i> {% trans "Chat" %}</a>
|
||||
</li>
|
||||
{% if user.admin %}
|
||||
<li class="nav-item active">
|
||||
<a data-turbolinks="false" class="nav-link" href="{% url "admin:index" %}"><i class="fas fa-cog"></i> {% trans "Administration" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
{% if user.registration.is_admin %}
|
||||
<form class="navbar-form" role="search" onsubmit="event.preventDefault()">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="{% trans "Search..." %}" name="q" id="search-term" value="{{ request.GET.q }}">
|
||||
<div class="input-group-btn">
|
||||
<button class="btn btn-default" data-toggle="modal" data-target="#searchModal"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if "_fake_user_id" in request.session %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:reset_admin" %}?path={{ request.path }}"><i class="fas fa-tools"></i> {% trans "Return to admin view" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if not user.is_authenticated %}
|
||||
{% if 1|current_phase %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#" data-toggle="modal" data-target="#loginModal">
|
||||
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fas fa-user"></i> {{ user.first_name }} {{ user.last_name }}
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right"
|
||||
aria-labelledby="navbarDropdownMenuLink">
|
||||
<a class="dropdown-item" href="{% url "registration:my_account_detail" %}">
|
||||
<i class="fas fa-user"></i> {% trans "My account" %}
|
||||
</a>
|
||||
<a class="dropdown-item" href="{% url "logout" %}">
|
||||
<i class="fas fa-sign-out-alt"></i> {% trans "Log out" %}
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% block fullcontent %}
|
||||
<div class="{% block containertype %}container{% endblock %} my-3">
|
||||
{% block contenttitle %}{% endblock %}
|
||||
{% if user.is_authenticated and not user.registration.email_confirmed %}
|
||||
<div class="alert alert-warning alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
{% url "registration:email_validation_resend" pk=user.pk as send_email_url %}
|
||||
{% blocktrans trimmed %}
|
||||
Your email address is not validated. Please click on the link you received by email.
|
||||
You can resend a mail by clicking on <a href="{{ send_email_url }}">this link</a>.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="messages"></div>
|
||||
<div id="content">
|
||||
{% block content %}
|
||||
<p>Default content...</p>
|
||||
{% endblock content %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="bg-light text-primary mt-auto py-2">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<form action="{% url 'set_language' %}" method="post"
|
||||
class="form-inline">
|
||||
<span class="text-muted mr-1">
|
||||
<a target="_blank" href="mailto:contact@correspondances-maths.fr"
|
||||
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
|
||||
</span>
|
||||
{% csrf_token %}
|
||||
<select title="language" name="language"
|
||||
class="form-control form-control-sm language"
|
||||
onchange="this.form.submit()">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% for lang_code, lang_name in LANGUAGES %}
|
||||
<option value="{{ lang_code }}"
|
||||
{% if lang_code == LANGUAGE_CODE %}
|
||||
selected{% endif %}>
|
||||
{{ lang_name }} ({{ lang_code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<noscript>
|
||||
<input type="submit">
|
||||
</noscript>
|
||||
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> —
|
||||
<a target="_blank" class="text-muted"
|
||||
href="https://gitlab.com/animath/si/plateforme-tfjm">
|
||||
<i class="fab fa-gitlab"></i>
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col text-right">
|
||||
<a href="#" data-turbolinks="false" class="text-muted">
|
||||
<i class="fa fa-arrow-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
{% trans "Calendar" as modal_title %}
|
||||
{% include "base_modal.html" with modal_id="calendar" modal_additional_class="modal-lg" %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
{% trans "All teams" as modal_title %}
|
||||
{% include "base_modal.html" with modal_id="teams" modal_additional_class="modal-lg" %}
|
||||
|
||||
{% trans "Search results" as modal_title %}
|
||||
{% include "base_modal.html" with modal_id="search" modal_form_method="get" modal_additional_class="modal-lg" %}
|
||||
|
||||
{% trans "Join team" as modal_title %}
|
||||
{% trans "Join" as modal_button %}
|
||||
{% url "participation:join_team" as modal_action %}
|
||||
|
||||
{% include "base_modal.html" with modal_id="joinTeam" %}
|
||||
{% trans "Create team" as modal_title %}
|
||||
{% trans "Create" as modal_button %}
|
||||
{% url "participation:create_team" as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="createTeam" modal_button_type="success" %}
|
||||
{% else %}
|
||||
{% trans "Log in" as modal_title %}
|
||||
{% trans "Log in" as modal_button %}
|
||||
{% url "login" as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="login" %}
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
CSRF_TOKEN = "{{ csrf_token }}";
|
||||
$(".invalid-feedback").addClass("d-block");
|
||||
|
||||
$(document).ready(function () {
|
||||
$('a[data-target="#calendarModal"]').click(function() {
|
||||
let modalBody = $("#calendarModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:calendar" %} #form-content")
|
||||
});
|
||||
{% if user.is_authenticated and user.registration.is_admin %}
|
||||
$('a[data-target="#teamsModal"]').click(function() {
|
||||
let modalBody = $("#teamsModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:team_list" %} #form-content")
|
||||
});
|
||||
$('button[data-target="#searchModal"]').click(function() {
|
||||
let modalBody = $("#searchModal div.modal-body");
|
||||
let q = encodeURI($("#search-term").val());
|
||||
modalBody.load("{% url "haystack_search" %}?q=" + q + " #search-results");
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if not user.is_authenticated %}
|
||||
$('a[data-target="#loginModal"]').click(function() {
|
||||
let modalBody = $("#loginModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "login" %} #form-content")
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated and user.registration.participates and not user.registration.team %}
|
||||
$('a[data-target="#createTeamModal"]').click(function() {
|
||||
let modalBody = $("#createTeamModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:create_team" %} #form-content");
|
||||
});
|
||||
$('a[data-target="#joinTeamModal"]').click(function() {
|
||||
let modalBody = $("#joinTeamModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:join_team" %} #form-content");
|
||||
});
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extrajavascript %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
24
tfjm/templates/base_modal.html
Normal file
24
tfjm/templates/base_modal.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div id="{{ modal_id }}Modal" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog {{ modal_additional_class }}" role="document">
|
||||
<form id="{{ modal_id }}-form" method="{{ modal_form_method|default:"post" }}" action="{{ modal_action }}" enctype="{{ modal_enctype|default:"application/x-www-form-urlencoded" }}">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ modal_title }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">{{ modal_content }}</div>
|
||||
<div class="modal-footer">
|
||||
{{ extra_modal_button }}
|
||||
{% if modal_button %}
|
||||
<button type="submit" class="btn btn-{{ modal_button_type|default:"primary" }}">{{ modal_button }}</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
80
tfjm/templates/index.html
Normal file
80
tfjm/templates/index.html
Normal file
@ -0,0 +1,80 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
|
||||
<div class="jumbotron bg-white">
|
||||
<div class="row">
|
||||
<h1 class="display-3">
|
||||
Bienvenue sur le site d'inscription au <a href="https://tfjm.org/" target="_blank">
|
||||
TFJM²</a> !
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row jumbotron bg-white">
|
||||
<div class="col-sm">
|
||||
<h3>
|
||||
Tu souhaites participer au TFJM² ?
|
||||
<br/>
|
||||
Ton équipe est déjà formée ?
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-sm text-right">
|
||||
<div class="btn-group-vertical">
|
||||
<a class="btn btn-primary btn-lg" href="{% url "registration:signup" %}" role="button">Inscris-toi maintenant !</a>
|
||||
<a class="btn btn-light btn-lg" href="{% url "login" %}" role="button">J'ai déjà un compte</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
Avertissement : certains rencontrent des difficultés à recevoir les mails automatiques. Merci de vérifier
|
||||
régulièrement votre boîte spam, et à nous contacter en cas de problème.
|
||||
</div>
|
||||
|
||||
<div class="jumbotron">
|
||||
<h5 class="display-4">Comment ça marche ?</h5>
|
||||
<p>
|
||||
Pour participer au TFJM², il suffit de créer un compte sur la rubrique <strong><a href="{% url "registration:signup" %}">Inscription</a></strong>.
|
||||
Vous devrez ensuite confirmer votre adresse e-mail.
|
||||
</p>
|
||||
|
||||
<p class="text-justify">
|
||||
Vous pouvez accéder à votre compte via la rubrique <strong><a href="{% url "login" %}">Connexion</a></strong>.
|
||||
Une fois connecté, vous pourrez créer une équipe ou en rejoindre une déjà créée par l'un de vos camarades
|
||||
via un code d'accès qui vous aura été transmis. Vous serez ensuite invité à soumettre une autorisation de droit à l'image,
|
||||
indispensable au bon déroulement du TFJM². Une fois que votre équipe comporte au moins 3 participants (maximum 5)
|
||||
et un encadrant, vous pourrez demander à valider votre équipe pour être apte à travailler sur le problème de votre choix.
|
||||
</p>
|
||||
|
||||
<h2>Je ne trouve pas d'équipe, aidez-moi !</h2>
|
||||
|
||||
<p class="text-justify">
|
||||
En ayant créé un compte sur cette plateforme, vous créez également un compte sur la plateforme de chat dédiée
|
||||
au TFJM², basée sur le protocole <a href="https://matrix.org/">Matrix</a> et le client
|
||||
<a href="https://element.io">Element</a>. Cette interface de chat vous permet de communiquer avec les membres
|
||||
de votre équipe, mais surtout de pouvoir échanger durant la troisième phase du TFJM², et
|
||||
également de poser vos questions en cas de besoin.
|
||||
</p>
|
||||
|
||||
<p class="text-justify">
|
||||
Ce chat contient également un salon <code>#je-cherche-une-equipe</code> où vous pouvez crier à l'aide pour trouver
|
||||
une équipe, ou compléter la votre s'il vous manque des participants. C'est un petit coin auprès du feu, un parc à
|
||||
jeu où vous pouvez descendre en toboggan (en le désinfectant après utilisation), ne cherchez pas à être trop formel.
|
||||
</p>
|
||||
|
||||
<h2>J'ai une question</h2>
|
||||
|
||||
<p class="text-justify">
|
||||
Pour toute question, vous pouvez soit la poser dans <code>#faq</code> dans l'onglet chat comme indiqué ci-dessus, soit nous
|
||||
contacter par mail à l'adresse <a href="mailto:contact@tfjm.org">contact@tfjm.org</a>.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<strong>Attention aux dates !</strong> Si vous ne finalisez pas votre inscription dans le délai indiqué, vous
|
||||
ne pourrez malheureusement pas participer au TFJM².
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
10
tfjm/templates/registration/logged_out.html
Normal file
10
tfjm/templates/registration/logged_out.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "Thanks for spending some quality time with the Web site today." %}</p>
|
||||
<p><a href="{% url 'index' %}">{% trans 'Log in again' %}</a></p>
|
||||
{% endblock %}
|
27
tfjm/templates/registration/login.html
Normal file
27
tfjm/templates/registration/login.html
Normal file
@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_filters %}
|
||||
|
||||
{% block title %}{% trans "Log in" %}{% endblock %}
|
||||
{% block contenttitle %}<h1>{% trans "Log in" %}</h1>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if user.is_authenticated %}
|
||||
<p class="errornote">
|
||||
{% blocktrans trimmed %}
|
||||
You are authenticated as {{ user }}, but are not authorized to
|
||||
access this page. Would you like to login to a different account?
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="post" id="login-form">
|
||||
<div id="form-content">
|
||||
{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
<a href="{% url 'password_reset' %}" class="badge badge-light">{% trans 'Forgotten your password or username?' %}</a>
|
||||
</div>
|
||||
<input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary">
|
||||
</form>
|
||||
{% endblock %}
|
28
tfjm/templates/search/search.html
Normal file
28
tfjm/templates/search/search.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% load crispy_forms_filters highlight i18n search_results_tables django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% trans "Search" %}</h2>
|
||||
|
||||
<form>
|
||||
{{ form|crispy }}
|
||||
<button type="submit" class="btn btn-primary">{% trans "Search" %}</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>{% trans "Results" %}</h3>
|
||||
|
||||
<div id="search-results">
|
||||
{% regroup object_list by model_name as categories %}
|
||||
{% for category in categories %}
|
||||
<h4>{% trans category.grouper|capfirst %}</h4>
|
||||
{% with table=category.list|search_table %}
|
||||
{% render_table table %}
|
||||
{% endwith %}
|
||||
{% empty %}
|
||||
<p>{% trans "No results found." %}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
27
tfjm/tests.py
Normal file
27
tfjm/tests.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from django.core.handlers.asgi import ASGIHandler
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class TestLoadModules(TestCase):
|
||||
"""
|
||||
Load modules that are not used in development mode in order to increase coverage.
|
||||
"""
|
||||
def test_asgi(self):
|
||||
from tfjm import asgi
|
||||
self.assertTrue(isinstance(asgi.application, ASGIHandler))
|
||||
|
||||
def test_wsgi(self):
|
||||
from tfjm import wsgi
|
||||
self.assertTrue(isinstance(wsgi.application, WSGIHandler))
|
||||
|
||||
def test_load_production_settings(self):
|
||||
os.putenv("TFJM_STAGE", "prod")
|
||||
os.putenv("DJANGO_DB_TYPE", "postgres")
|
||||
from tfjm import settings_prod
|
||||
self.assertFalse(settings_prod.DEBUG)
|
30
tfjm/tokens.py
Normal file
30
tfjm/tokens.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
|
||||
|
||||
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
|
||||
"""
|
||||
Create a unique token generator to confirm email addresses.
|
||||
"""
|
||||
def _make_hash_value(self, user, timestamp):
|
||||
"""
|
||||
Hash the user's primary key and some user state that's sure to change
|
||||
after an account validation to produce a token that invalidated when
|
||||
it's used:
|
||||
1. The user.profile.email_confirmed field will change upon an account
|
||||
validation.
|
||||
2. The last_login field will usually be updated very shortly after
|
||||
an account validation.
|
||||
Failing those things, settings.PASSWORD_RESET_TIMEOUT_DAYS eventually
|
||||
invalidates the token.
|
||||
"""
|
||||
# Truncate microseconds so that tokens are consistent even if the
|
||||
# database doesn't support microseconds.
|
||||
login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)
|
||||
return str(user.pk) + str(user.email) + str(user.registration.email_confirmed)\
|
||||
+ str(login_timestamp) + str(timestamp)
|
||||
|
||||
|
||||
email_validation_token = AccountActivationTokenGenerator()
|
54
tfjm/urls.py
Normal file
54
tfjm/urls.py
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""tfjm URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from django.views.defaults import bad_request, page_not_found, permission_denied, server_error
|
||||
from django.views.generic import TemplateView
|
||||
from registration.views import PhotoAuthorizationView
|
||||
|
||||
from .views import AdminSearchView
|
||||
|
||||
urlpatterns = [
|
||||
path('', TemplateView.as_view(template_name="index.html"), name='index'),
|
||||
path('about/', TemplateView.as_view(template_name="about.html"), name='about'),
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
path('admin/', admin.site.urls, name="admin"),
|
||||
path('accounts/', include('django.contrib.auth.urls')),
|
||||
path('search/', AdminSearchView.as_view(), name="haystack_search"),
|
||||
|
||||
path('api/', include('api.urls')),
|
||||
path('participation/', include('participation.urls')),
|
||||
path('registration/', include('registration.urls')),
|
||||
|
||||
path('media/authorization/photo/<str:filename>/', PhotoAuthorizationView.as_view(), name='photo_authorization'),
|
||||
|
||||
path('', include('eastereggs.urls')),
|
||||
]
|
||||
|
||||
if 'cas_server' in settings.INSTALLED_APPS: # pragma: no cover
|
||||
urlpatterns += [
|
||||
path('cas/', include('cas_server.urls', namespace="cas_server")),
|
||||
]
|
||||
|
||||
handler400 = bad_request
|
||||
handler403 = permission_denied
|
||||
handler404 = page_not_found
|
||||
handler500 = server_error
|
19
tfjm/views.py
Normal file
19
tfjm/views.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from haystack.generic_views import SearchView
|
||||
|
||||
|
||||
class AdminMixin(LoginRequiredMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
if not request.user.registration.is_admin:
|
||||
raise PermissionDenied
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AdminSearchView(AdminMixin, SearchView):
|
||||
pass
|
BIN
tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg
Normal file
BIN
tfjm/whoosh_index/MAIN_3c7ed6y4sljdfo5s.seg
Normal file
Binary file not shown.
0
tfjm/whoosh_index/MAIN_WRITELOCK
Executable file
0
tfjm/whoosh_index/MAIN_WRITELOCK
Executable file
BIN
tfjm/whoosh_index/_MAIN_11.toc
Normal file
BIN
tfjm/whoosh_index/_MAIN_11.toc
Normal file
Binary file not shown.
19
tfjm/wsgi.py
Normal file
19
tfjm/wsgi.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
WSGI config for tfjm project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tfjm.settings')
|
||||
|
||||
application = get_wsgi_application()
|
Reference in New Issue
Block a user