diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..12806a1
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,7 @@
+ErrorDocument 403 /tfjm/server_files/403.php
+ErrorDocument 404 /tfjm/server_files/404.php
+
+Options +FollowSymlinks
+Options -Indexes
+RewriteEngine On
+RewriteRule ^(.*)$ dispatcher.php?path=$1 [QSA,L]
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..0ed6303
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,6 @@
+# Default ignored files
+/workspace.xml
+
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
\ No newline at end of file
diff --git a/.idea/TFJM.iml b/.idea/TFJM.iml
new file mode 100644
index 0000000..940f6f9
--- /dev/null
+++ b/.idea/TFJM.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..c28cc86
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ mysql.8
+ true
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://ynerant.fr:3306/tfjm
+
+
+
+ GMT+1
+
+
+
\ No newline at end of file
diff --git a/.idea/deployment.xml b/.idea/deployment.xml
new file mode 100644
index 0000000..f213652
--- /dev/null
+++ b/.idea/deployment.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..28a804d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..6fcadc0
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/Autorisation_droit_image_majeur.tex b/assets/Autorisation_droit_image_majeur.tex
new file mode 100644
index 0000000..7cb1727
--- /dev/null
+++ b/assets/Autorisation_droit_image_majeur.tex
@@ -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}
diff --git a/assets/Autorisation_droit_image_mineur.tex b/assets/Autorisation_droit_image_mineur.tex
new file mode 100644
index 0000000..4f14a43
--- /dev/null
+++ b/assets/Autorisation_droit_image_mineur.tex
@@ -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}
diff --git a/assets/Autorisation_parentale.tex b/assets/Autorisation_parentale.tex
new file mode 100644
index 0000000..6c56ac4
--- /dev/null
+++ b/assets/Autorisation_parentale.tex
@@ -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}
diff --git a/assets/Fiche synthèse.pdf b/assets/Fiche synthèse.pdf
new file mode 100644
index 0000000..af8ed1c
Binary files /dev/null and b/assets/Fiche synthèse.pdf differ
diff --git a/assets/Fiche synthèse.tex b/assets/Fiche synthèse.tex
new file mode 100644
index 0000000..bc2daa9
--- /dev/null
+++ b/assets/Fiche synthèse.tex
@@ -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}
diff --git a/assets/Fiche_sanitaire.pdf b/assets/Fiche_sanitaire.pdf
new file mode 100644
index 0000000..b828b9d
Binary files /dev/null and b/assets/Fiche_sanitaire.pdf differ
diff --git a/assets/Instructions.tex b/assets/Instructions.tex
new file mode 100644
index 0000000..da293ef
--- /dev/null
+++ b/assets/Instructions.tex
@@ -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}
diff --git a/assets/favicon.ico b/assets/favicon.ico
new file mode 100644
index 0000000..97757d3
Binary files /dev/null and b/assets/favicon.ico differ
diff --git a/assets/logo.svg b/assets/logo.svg
new file mode 100644
index 0000000..699316b
--- /dev/null
+++ b/assets/logo.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/assets/logo_animath.png b/assets/logo_animath.png
new file mode 100644
index 0000000..da4533e
Binary files /dev/null and b/assets/logo_animath.png differ
diff --git a/assets/style.css b/assets/style.css
new file mode 100644
index 0000000..5c8d3ff
--- /dev/null
+++ b/assets/style.css
@@ -0,0 +1,47 @@
+html, body {
+ height: 100%;
+ margin: 0;
+}
+
+:root {
+ --navbar-height: 32px;
+}
+
+.container {
+ min-height: 78%;
+}
+
+.inner {
+ margin: 20px;
+}
+
+.alert {
+ text-align: justify;
+}
+
+
+footer .alert {
+ text-align: center;
+}
+
+#navbar-logo {
+ height: var(--navbar-height);
+ display: block;
+}
+
+ul .deroule {
+ display: none;
+ position: absolute;
+ background: #f8f9fa !important;
+ list-style-type: none;
+ padding: 20px;
+ z-index: 42;
+}
+
+li:hover ul.deroule {
+ display:block;
+}
+
+a.nav-link:hover {
+ background-color: #d8d9da;
+}
diff --git a/dispatcher.php b/dispatcher.php
new file mode 100644
index 0000000..1f4106f
--- /dev/null
+++ b/dispatcher.php
@@ -0,0 +1,92 @@
+ $file) {
+ if (preg_match('#' . $route . '#', $path, $matches)) {
+ for ($i = 1; $i < sizeof($file); ++$i)
+ $_GET[$file[$i]] = $matches[$i];
+
+ if (!preg_match("#php$#", $file[0])) {
+ header("Content-Type: " . $file[1]);
+ readfile($file[0]);
+ exit();
+ }
+
+ $view = $file[0];
+ /** @noinspection PhpIncludeInspection */
+ require $view;
+ exit();
+ }
+}
+
+require_once "server_files/404.php";
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..da8c197
--- /dev/null
+++ b/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Erreur
+
+
+
+Le mode Rewrite n'est pas activé.
+
+
\ No newline at end of file
diff --git a/server_files/403.php b/server_files/403.php
new file mode 100644
index 0000000..72127d0
--- /dev/null
+++ b/server_files/403.php
@@ -0,0 +1,16 @@
+
+
+
+
+ Vous n'êtes pas autorisé à accéder à cette page.
+
+
+
+
+
+
+
+ Cette page n'existe pas.
+
+
+
+prepare("SELECT * FROM `documents` WHERE `file_id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ return self::fromData($data);
+ }
+
+ public static function fromData($data)
+ {
+ $doc = new Document();
+ $doc->fill($data);
+ return $doc;
+ }
+
+ private function fill($data)
+ {
+ $this->file_id = $data["file_id"];
+ $this->user_id = $data["user"];
+ $this->team_id = $data["team"];
+ $this->tournament_id = $data["tournament"];
+ $this->type = DocumentType::fromName($data["type"]);
+ $this->uploaded_at = $data["uploaded_at"];
+ $this->version = isset($data["version"]) ? $data["version"] : 1;
+ }
+
+ public function getFileId()
+ {
+ return $this->file_id;
+ }
+
+ public function getUserId()
+ {
+ return $this->user_id;
+ }
+
+ public function getTeamId()
+ {
+ return $this->team_id;
+ }
+
+ public function getTournamentId()
+ {
+ return $this->tournament_id;
+ }
+
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ public function getUploadedAt()
+ {
+ return $this->uploaded_at;
+ }
+
+ public function getVersion()
+ {
+ return $this->version;
+ }
+}
+
+class Solution
+{
+ private $file_id;
+ private $team_id;
+ private $tournament_id;
+ private $problem;
+ private $uploaded_at;
+ private $version;
+
+ private function __construct() {}
+
+ public static function fromId($id)
+ {
+ global $DB;
+ $req = $DB->prepare("SELECT * FROM `solutions` WHERE `file_id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ return self::fromData($data);
+ }
+
+ public static function fromData($data)
+ {
+ $sol = new Solution();
+ $sol->fill($data);
+ return $sol;
+ }
+
+ private function fill($data)
+ {
+ $this->file_id = $data["file_id"];
+ $this->team_id = $data["team"];
+ $this->tournament_id = $data["tournament"];
+ $this->problem = $data["problem"];
+ $this->uploaded_at = $data["uploaded_at"];
+ $this->version = isset($data["version"]) ? $data["version"] : 1;
+ }
+
+ public function getFileId()
+ {
+ return $this->file_id;
+ }
+
+ public function getTeamId()
+ {
+ return $this->team_id;
+ }
+
+ public function getTournamentId()
+ {
+ return $this->tournament_id;
+ }
+
+ public function getProblem()
+ {
+ return $this->problem;
+ }
+
+ public function getUploadedAt()
+ {
+ return $this->uploaded_at;
+ }
+
+ public function getVersion()
+ {
+ return $this->version;
+ }
+}
+
+class Synthesis
+{
+ private $file_id;
+ private $team_id;
+ private $tournament_id;
+ private $dest;
+ private $round;
+ private $uploaded_at;
+ private $version;
+
+ private function __construct() {}
+
+ public static function fromId($id)
+ {
+ global $DB;
+ $req = $DB->prepare("SELECT * FROM `syntheses` WHERE `file_id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ return self::fromData($data);
+ }
+
+ public static function fromData($data)
+ {
+ $synthese = new Synthesis();
+ $synthese->fill($data);
+ return $synthese;
+ }
+
+ private function fill($data)
+ {
+ $this->file_id = $data["file_id"];
+ $this->team_id = $data["team"];
+ $this->tournament_id = $data["tournament"];
+ $this->dest = $data["dest"];
+ $this->round = $data["round"];
+ $this->uploaded_at = $data["uploaded_at"];
+ $this->version = isset($data["version"]) ? $data["version"] : 1;
+ }
+
+ public function getFileId()
+ {
+ return $this->file_id;
+ }
+
+ public function getTeamId()
+ {
+ return $this->team_id;
+ }
+
+ public function getTournamentId()
+ {
+ return $this->tournament_id;
+ }
+
+ public function getDest()
+ {
+ return $this->dest;
+ }
+
+ public function getRound()
+ {
+ return $this->round;
+ }
+
+ public function getUploadedAt()
+ {
+ return $this->uploaded_at;
+ }
+
+ public function getVersion()
+ {
+ return $this->version;
+ }
+}
+
+class DestType
+{
+ const DEFENSEUR = 0;
+ const OPPOSANT = 1;
+ const RAPPORTEUR = 2;
+
+ public static function getTranslatedName($status) {
+ switch ($status) {
+ case self::OPPOSANT:
+ return "Opposant";
+ case self::RAPPORTEUR:
+ return "Rapporteur";
+ default:
+ return "Défenseur";
+ }
+ }
+
+ public static function getName($status) {
+ switch ($status) {
+ case self::OPPOSANT:
+ return "OPPOSANT";
+ case self::RAPPORTEUR:
+ return "RAPPORTEUR";
+ default:
+ return "DEFENSEUR";
+ }
+ }
+
+ public static function fromName($name) {
+ switch ($name) {
+ case "OPPOSANT":
+ return self::OPPOSANT;
+ case "RAPPORTEUR":
+ return self::RAPPORTEUR;
+ default:
+ return self::DEFENSEUR;
+ }
+ }
+}
+
+class DocumentType
+{
+ const PARENTAL_CONSENT = 0;
+ const PHOTO_CONSENT = 1;
+ const SANITARY_PLUG = 2;
+ const SOLUTION = 3;
+ const SYNTHESIS = 4;
+ const SCHOLARSHIP = 5;
+ const MOTIVATION_LETTER = 6;
+
+ public static function getTranslatedName($type) {
+ switch ($type) {
+ case self::PARENTAL_CONSENT:
+ return "Autorisation parentale";
+ case self::PHOTO_CONSENT:
+ return "Autorisation de droit à l'image";
+ case self::SANITARY_PLUG:
+ return "Fiche sanitaire";
+ case self::SCHOLARSHIP:
+ return "Notification de bourse";
+ case self::MOTIVATION_LETTER:
+ return "Lettre de motivation";
+ case self::SOLUTION:
+ return "Solution";
+ default:
+ return "Note de synthèse";
+ }
+ }
+
+ public static function getName($type) {
+ switch ($type) {
+ case self::PARENTAL_CONSENT:
+ return "PARENTAL_CONSENT";
+ case self::PHOTO_CONSENT:
+ return "PHOTO_CONSENT";
+ case self::SANITARY_PLUG:
+ return "SANITARY_PLUG";
+ case self::SCHOLARSHIP:
+ return "SCHOLARSHIP";
+ case self::MOTIVATION_LETTER:
+ return "MOTIVATION_LETTER";
+ case self::SOLUTION:
+ return "SOLUTION";
+ default:
+ return "SYNTHESIS";
+ }
+ }
+
+ public static function fromName($name) {
+ switch ($name) {
+ case "PARENTAL_CONSENT":
+ return self::PARENTAL_CONSENT;
+ case "PHOTO_CONSENT":
+ return self::PHOTO_CONSENT;
+ case "SANITARY_PLUG":
+ return self::SANITARY_PLUG;
+ case "SCHOLARSHIP":
+ return self::SCHOLARSHIP;
+ case "MOTIVATION_LETTER":
+ return self::MOTIVATION_LETTER;
+ case "SOLUTION":
+ return self::SOLUTION;
+ default:
+ return self::SYNTHESIS;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server_files/classes/Payment.php b/server_files/classes/Payment.php
new file mode 100644
index 0000000..2847403
--- /dev/null
+++ b/server_files/classes/Payment.php
@@ -0,0 +1,152 @@
+prepare("SELECT * FROM `payments` WHERE `id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $payment = new Payment();
+ $payment->fill($data);
+ return $payment;
+ }
+
+ private function fill($data)
+ {
+ $this->id = $data["id"];
+ $this->user_id = $data["user"];
+ $this->tournament_id = $data["tournament"];
+ $this->amount = $data["amount"];
+ $this->method = PaymentMethod::fromName($data["method"]);
+ $this->transaction_infos = $data["transaction_infos"];
+ $this->validation_status = ValidationStatus::fromName($data["validation_status"]);
+ }
+
+ /**
+ * @return int
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return int
+ */
+ public function getAmount()
+ {
+ return $this->amount;
+ }
+
+ /**
+ * @param mixed $amount
+ */
+ public function setAmount($amount)
+ {
+ global $DB;
+ $this->amount = $amount;
+ $DB->prepare("UPDATE `payments` SET `amount` = ? WHERE `id` = ?;")->execute([$amount, $this->id]);
+ }
+
+ /**
+ * @return int
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ /**
+ * @param int $method
+ */
+ public function setMethod($method)
+ {
+ global $DB;
+ $this->method = $method;
+ $DB->prepare("UPDATE `payments` SET `method` = ? WHERE `id` = ?;")->execute([PaymentMethod::getName($method), $this->id]);
+ }
+
+ /**
+ * @return int
+ */
+ public function getTournamentId()
+ {
+ return $this->tournament_id;
+ }
+
+ /**
+ * @return Tournament|null
+ */
+ public function getTournament()
+ {
+ return Tournament::fromId($this->getTournamentId());
+ }
+
+ /**
+ * @return int
+ */
+ public function getUserId()
+ {
+ return $this->user_id;
+ }
+
+ /**
+ * @return User|null
+ */
+ public function getUser()
+ {
+ return User::fromId($this->getUserId());
+ }
+
+ /**
+ * @return string
+ */
+ public function getTransactionInfos()
+ {
+ return $this->transaction_infos;
+ }
+
+ /**
+ * @param string $transaction_infos
+ */
+ public function setTransactionInfos($transaction_infos)
+ {
+ global $DB;
+ $this->transaction_infos = $transaction_infos;
+ $DB->prepare("UPDATE `payments` SET `transaction_infos` = ? WHERE `id` = ?;")->execute([$transaction_infos, $this->id]);
+ }
+
+ /**
+ * @return int
+ */
+ public function getValidationStatus()
+ {
+ return $this->validation_status;
+ }
+
+ /**
+ * @param int $validation_status
+ */
+ public function setValidationStatus($validation_status)
+ {
+ global $DB;
+ $this->validation_status = $validation_status;
+ $DB->prepare("UPDATE `payments` SET `validation_status` = ? WHERE `id` = ?;")->execute([ValidationStatus::getName($validation_status), $this->id]);
+ }
+}
\ No newline at end of file
diff --git a/server_files/classes/PaymentMethod.php b/server_files/classes/PaymentMethod.php
new file mode 100644
index 0000000..f0d5420
--- /dev/null
+++ b/server_files/classes/PaymentMethod.php
@@ -0,0 +1,62 @@
+prepare("SELECT * FROM `teams` WHERE `id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $team = new Team();
+ $team->fill($data);
+ return $team;
+ }
+
+ public static function fromTrigram($trigram)
+ {
+ global $DB, $YEAR;
+ $req = $DB->prepare("SELECT * FROM `teams` WHERE `trigram` = ? AND `year` = $YEAR;");
+ $req->execute([htmlspecialchars($trigram)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $team = new Team();
+ $team->fill($data);
+ return $team;
+ }
+
+ public static function fromAccessCode($access_code)
+ {
+ global $DB, $YEAR;
+ $req = $DB->prepare("SELECT * FROM `teams` WHERE `access_code` = ? AND `year` = $YEAR;");
+ $req->execute([htmlspecialchars($access_code)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $team = new Team();
+ $team->fill($data);
+ return $team;
+ }
+
+ private function fill($data)
+ {
+ $this->id = $data["id"];
+ $this->name = $data["name"];
+ $this->trigram = $data["trigram"];
+ $this->tournament = $data["tournament"];
+ $this->encadrants = [$data["encadrant_1"], $data["encadrant_2"], $data["encadrant_3"]];
+ $this->participants = [$data["participant_1"], $data["participant_2"], $data["participant_3"], $data["participant_4"], $data["participant_5"], $data["participant_6"]];
+ $this->inscription_date = $data["inscription_date"];
+ $this->validation_status = ValidationStatus::fromName($data["validation_status"]);
+ $this->final_selection = $data["final_selection"] == true;
+ $this->access_code = $data["access_code"];
+ $this->year = $data["year"];
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ global $DB;
+ $this->name = $name;
+ $DB->prepare("UPDATE `teams` SET `name` = ? WHERE `id` = ?;")->execute([$name, $this->id]);
+ }
+
+ public function getTrigram()
+ {
+ return $this->trigram;
+ }
+
+ public function setTrigram($trigram)
+ {
+ global $DB;
+ $this->trigram = $trigram;
+ $DB->prepare("UPDATE `teams` SET `trigram` = ? WHERE `id` = ?;")->execute([$trigram, $this->id]);
+ }
+
+ public function getTournamentId()
+ {
+ return $this->tournament;
+ }
+
+ /**
+ * @return Tournament
+ */
+ public function getEffectiveTournament()
+ {
+ return $this->isSelectedForFinal() ? Tournament::getFinalTournament() : Tournament::fromId($this->getTournamentId());
+ }
+
+ public function setTournamentId($tournament)
+ {
+ global $DB;
+ $this->tournament = $tournament;
+ $DB->prepare("UPDATE `teams` SET `tournament` = ? WHERE `id` = ?;")->execute([$tournament, $this->id]);
+ }
+
+ public function getEncadrants()
+ {
+ return $this->encadrants;
+ }
+
+ public function setEncadrant($i, $encadrant)
+ {
+ global $DB;
+ $this->encadrants[$i - 1] = $encadrant;
+ /** @noinspection SqlResolve */
+ $DB->prepare("UPDATE `teams` SET `encadrant_$i` = ? WHERE `id` = ?;")->execute([$encadrant, $this->id]);
+ }
+
+ public function getParticipants()
+ {
+ return $this->participants;
+ }
+
+ public function setParticipant($i, $participant)
+ {
+ global $DB;
+ $this->participants[$i - 1] = $participant;
+ /** @noinspection SqlResolve */
+ $DB->prepare("UPDATE `teams` SET `participant_$i` = ? WHERE `id` = ?;")->execute([$participant, $this->id]);
+ }
+
+ public function getInscriptionDate()
+ {
+ return $this->inscription_date;
+ }
+
+ public function getValidationStatus()
+ {
+ return $this->validation_status;
+ }
+
+ public function setValidationStatus($status)
+ {
+ global $DB;
+ $this->validation_status = $status;
+ /** @noinspection PhpUndefinedMethodInspection */
+ $DB->prepare("UPDATE `teams` SET `validation_status` = ? WHERE `id` = ?;")->execute([ValidationStatus::getName($status), $this->id]);
+ }
+
+ public function isSelectedForFinal()
+ {
+ return $this->final_selection;
+ }
+
+ public function selectForFinal($selected)
+ {
+ global $DB;
+ $this->final_selection = $selected;
+ $DB->prepare("UPDATE `teams` SET `final_selection` = ? WHERE `id` = ?;")->execute([$selected, $this->id]);
+ }
+
+ public function getAccessCode()
+ {
+ return $this->access_code;
+ }
+
+ public function getYear()
+ {
+ return $this->year;
+ }
+
+ public static function getAllTeams($only_not_validated = false)
+ {
+ global $DB, $YEAR;
+ $req = $DB->query("SELECT * FROM `teams` WHERE " . ($only_not_validated ? "`validation_status` = 0 AND " : "") . "`year` = $YEAR;");
+
+ $teams = [];
+
+ while (($data = $req->fetch()) != false) {
+ $team = new Team();
+ $team->fill($data);
+ $teams[] = $team;
+ }
+
+ return $teams;
+ }
+}
diff --git a/server_files/classes/Tournament.php b/server_files/classes/Tournament.php
new file mode 100644
index 0000000..6860bad
--- /dev/null
+++ b/server_files/classes/Tournament.php
@@ -0,0 +1,385 @@
+prepare("SELECT * FROM `tournaments` WHERE `id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $tournament = new Tournament();
+ $tournament->fill($data);
+ return $tournament;
+ }
+
+ public static function fromName($name)
+ {
+ global $DB, $YEAR;
+ $req = $DB->prepare("SELECT * FROM `tournaments` WHERE `name` = ? AND `year` = $YEAR;");
+ $req->execute([htmlspecialchars($name)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $tournament = new Tournament();
+ $tournament->fill($data);
+ return $tournament;
+ }
+
+ public static function getFinalTournament()
+ {
+ global $DB, $YEAR;
+ $req = $DB->query("SELECT * FROM `tournaments` WHERE `final` AND `year` = $YEAR;");
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $tournament = new Tournament();
+ $tournament->fill($data);
+ return $tournament;
+ }
+
+ public static function getAllTournaments($include_final = true, $only_future = false)
+ {
+ global $DB, $YEAR;
+ $sql = "SELECT * FROM `tournaments` WHERE ";
+ if (!$include_final)
+ $sql .= "`final` = 0 AND ";
+ if ($only_future)
+ $sql .= "`date_start` > CURRENT_DATE AND ";
+ $sql .= "`year` = $YEAR ORDER BY `date_start`, `name`;";
+ $req = $DB->query($sql);
+
+ $tournaments = [];
+
+ while (($data = $req->fetch()) !== false) {
+ $tournament = new Tournament();
+ $tournament->fill($data);
+ $tournaments[] = $tournament;
+ }
+
+ return $tournaments;
+ }
+
+ private function fill($data)
+ {
+ $this->id = $data["id"];
+ $this->name = $data["name"];
+ $this->size = $data["size"];
+ $this->place = $data["place"];
+ $this->price = $data["price"];
+ $this->description = $data["description"];
+ $this->date_start = $data["date_start"];
+ $this->date_end = $data["date_end"];
+ $this->date_inscription = $data["date_inscription"];
+ $this->date_solutions = $data["date_solutions"];
+ $this->date_solutions_2 = $data["date_solutions_2"];
+ $this->date_syntheses = $data["date_syntheses"];
+ $this->date_syntheses_2 = $data["date_syntheses_2"];
+ $this->final = $data["final"] == true;
+ $this->year = $data["year"];
+
+ global $DB;
+ $req = $DB->prepare("SELECT `organizer` FROM `organizers` WHERE `tournament` = ?;");
+ $req->execute([$this->id]);
+
+ while (($data = $req->fetch()) !== false)
+ $this->organizers[] = User::fromId($data["organizer"]);
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName($name)
+ {
+ global $DB;
+ $this->name = $name;
+ $DB->prepare("UPDATE `tournaments` SET `name` = ? WHERE `id` = ?;")->execute([$name, $this->id]);
+ }
+
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ public function setSize($size)
+ {
+ global $DB;
+ $this->size = $size;
+ $DB->prepare("UPDATE `tournaments` SET `size` = ? WHERE `id` = ?;")->execute([$size, $this->id]);
+ }
+
+ public function getPlace()
+ {
+ return $this->place;
+ }
+
+ public function setPlace($place)
+ {
+ global $DB;
+ $this->place = $place;
+ $DB->prepare("UPDATE `tournaments` SET `place` = ? WHERE `id` = ?;")->execute([$place, $this->id]);
+ }
+
+ public function getPrice()
+ {
+ return $this->price;
+ }
+
+ public function setPrice($price)
+ {
+ global $DB;
+ $this->price = $price;
+ $DB->prepare("UPDATE `tournaments` SET `price` = ? WHERE `id` = ?;")->execute([$price, $this->id]);
+ }
+
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ public function setDescription($desc)
+ {
+ global $DB;
+ $this->description = $desc;
+ $DB->prepare("UPDATE `tournaments` SET `description` = ? WHERE `id` = ?;")->execute([$desc, $this->id]);
+ }
+
+ public function getStartDate()
+ {
+ return $this->date_start;
+ }
+
+ public function setStartDate($date)
+ {
+ global $DB;
+ $this->date_start = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_start` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getEndDate()
+ {
+ return $this->date_end;
+ }
+
+ public function setEndDate($date)
+ {
+ global $DB;
+ $this->date_end = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_end` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getInscriptionDate()
+ {
+ return $this->date_inscription;
+ }
+
+ public function setInscriptionDate($date)
+ {
+ global $DB;
+ $this->date_inscription = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_inscription` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getSolutionsDate()
+ {
+ return $this->date_solutions;
+ }
+
+ public function setSolutionsDate($date)
+ {
+ global $DB;
+ $this->date_solutions = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_solutions` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getSynthesesDate()
+ {
+ return $this->date_syntheses;
+ }
+
+ public function setSynthesesDate($date)
+ {
+ global $DB;
+ $this->date_syntheses = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_syntheses` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getSolutionsDate2()
+ {
+ return $this->date_solutions_2;
+ }
+
+ public function setSolutionsDate2($date)
+ {
+ global $DB;
+ $this->date_solutions_2 = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_solutions_2` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function getSynthesesDate2()
+ {
+ return $this->date_syntheses_2;
+ }
+
+ public function setSynthesesDate2($date)
+ {
+ global $DB;
+ $this->date_syntheses_2 = $date;
+ $DB->prepare("UPDATE `tournaments` SET `date_syntheses_2` = ? WHERE `id` = ?;")->execute([$date, $this->id]);
+ }
+
+ public function isFinal()
+ {
+ return $this->final;
+ }
+
+ public function setFinal($final)
+ {
+ global $DB;
+ $this->final = $final;
+ $DB->prepare("UPDATE `tournaments` SET `final` = ? WHERE `id` = ?;")->execute([$final, $this->id]);
+ }
+
+ public function getAllTeams()
+ {
+ global $DB, $YEAR;
+ if ($this->final)
+ $req = $DB->query("SELECT `id` FROM `teams` WHERE `final_selection` AND `year` = $YEAR;");
+ else
+ $req = $DB->query("SELECT `id` FROM `teams` WHERE `tournament` = $this->id AND `year` = $YEAR;");
+
+ $teams = [];
+
+ while (($data = $req->fetch()) !== false)
+ $teams[] = Team::fromId($data["id"]);
+
+ return $teams;
+ }
+
+ public function getOrganizers()
+ {
+ return $this->organizers;
+ }
+
+ public function organize($user_id)
+ {
+ foreach ($this->organizers as $organizer) {
+ if ($organizer->getId() == $user_id)
+ return true;
+ }
+
+ return false;
+ }
+
+ public function addOrganizer(User $user)
+ {
+ global $DB;
+
+ $this->organizers[] = $user;
+
+ $req = $DB->prepare("INSERT INTO `organizers`(`organizer`, `tournament`) VALUES(?, ?);");
+ $req->execute([$user->getId(), $this->id]);
+ }
+
+ public function clearOrganizers()
+ {
+ global $DB;
+
+ $this->organizers = [];
+
+ $req = $DB->prepare("DELETE FROM `organizers` WHERE `tournament` = ?;");
+ $req->execute([$this->id]);
+ }
+
+ public function getYear()
+ {
+ return $this->year;
+ }
+
+ public function getAllDocuments($team_id = -1)
+ {
+ global $DB;
+
+ $req = $DB->query("SELECT * FROM `documents` AS `t1` "
+ . "INNER JOIN (SELECT `user`, `type`, `tournament`, MAX(`uploaded_at`) AS `last_upload`, COUNT(`team`) AS `version` FROM `documents` GROUP BY `tournament`, `team`, `type`, `user`) `t2` "
+ . "ON `t1`.`user` = `t2`.`user` AND `t1`.`type` = `t2`.`type` AND `t1`.`tournament` = `t2`.`tournament` "
+ . "WHERE `t1`.`uploaded_at` = `t2`.`last_upload` AND `t1`.`tournament` = $this->id " . ($team_id == -1 ? "" : "AND `t1`.`team` = $team_id") . " ORDER BY `t1`.`team`, `t1`.`type`;");
+
+ $docs = [];
+
+ while (($data = $req->fetch()) !== false)
+ $docs[] = Document::fromData($data);
+
+ return $docs;
+ }
+
+ public function getAllSolutions($team_id = -1)
+ {
+ global $DB;
+
+ $req = $DB->query("SELECT * FROM `solutions` AS `t1` "
+ . "INNER JOIN (SELECT `team`, `problem`, `tournament`, MAX(`uploaded_at`) AS `last_upload`, COUNT(`team`) AS `version` FROM `solutions` GROUP BY `tournament`, `team`, `problem`) `t2` "
+ . "ON `t1`.`team` = `t2`.`team` AND `t1`.`problem` = `t2`.`problem` AND `t1`.`tournament` = `t2`.`tournament` "
+ . "WHERE `t1`.`uploaded_at` = `t2`.`last_upload` AND `t1`.`tournament` = $this->id " . ($team_id == -1 ? "" : "AND `t1`.`team` = $team_id") . " ORDER BY `t1`.`team`, `t1`.`problem`;");
+
+ $sols = [];
+
+ while (($data = $req->fetch()) !== false)
+ $sols[] = Solution::fromData($data);
+
+ return $sols;
+ }
+
+ public function getAllSyntheses($team_id = -1)
+ {
+ global $DB;
+
+ $req = $DB->query("SELECT * FROM `syntheses` AS `t1` "
+ . "INNER JOIN (SELECT `team`, `dest`, `round`, `tournament`, MAX(`uploaded_at`) AS `last_upload`, COUNT(`team`) AS `version` FROM `syntheses` GROUP BY `tournament`, `team`, `dest`, `round`) `t2` "
+ . "ON `t1`.`team` = `t2`.`team` AND `t1`.`dest` = `t2`.`dest` AND `t1`.`tournament` = `t2`.`tournament` AND `t1`.`round` = `t2`.`round` "
+ . "WHERE `t1`.`uploaded_at` = `t2`.`last_upload` AND `t1`.`tournament` = $this->id " . ($team_id == -1 ? "" : "AND `t1`.`team` = $team_id") . " ORDER BY `t1`.`team`, `t1`.`round`, `t1`.`dest`;");
+
+ $syntheses = [];
+
+ while (($data = $req->fetch()) !== false)
+ $syntheses[] = Synthesis::fromData($data);
+
+ return $syntheses;
+ }
+}
\ No newline at end of file
diff --git a/server_files/classes/User.php b/server_files/classes/User.php
new file mode 100644
index 0000000..cc8111e
--- /dev/null
+++ b/server_files/classes/User.php
@@ -0,0 +1,481 @@
+prepare("SELECT * FROM `users` WHERE `id` = ?;");
+ $req->execute([htmlspecialchars($id)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $user = new User();
+ $user->fill($data);
+ return $user;
+ }
+
+ public static function fromEmail($email)
+ {
+ global $DB, $YEAR;
+ $req = $DB->prepare("SELECT * FROM `users` WHERE `email` = ? AND `year` = $YEAR;");
+ $req->execute([htmlspecialchars($email)]);
+ $data = $req->fetch();
+
+ if ($data === false)
+ return null;
+
+ $user = new User();
+ $user->fill($data);
+ return $user;
+ }
+
+ private function fill($data)
+ {
+ $this->id = $data["id"];
+ $this->email = $data["email"];
+ $this->pwd_hash = $data["pwd_hash"];
+ $this->surname = $data["surname"];
+ $this->first_name = $data["first_name"];
+ $this->birth_date = $data["birth_date"];
+ $this->gender = $data["gender"];
+ $this->address = $data["address"];
+ $this->postal_code = $data["postal_code"];
+ $this->city = $data["city"];
+ $this->country = $data["country"];
+ $this->phone_number = $data["phone_number"];
+ $this->school = $data["school"];
+ $this->class = SchoolClass::fromName($data["class"]);
+ $this->responsible_name = $data["responsible_name"];
+ $this->responsible_phone = $data["responsible_phone"];
+ $this->responsible_email = $data["responsible_email"];
+ $this->description = $data["description"];
+ $this->role = Role::fromName($data["role"]);
+ $this->team_id = $data["team_id"];
+ $this->year = $data["year"];
+ $this->confirm_email = $data["confirm_email"];
+ $this->forgotten_password = $data["forgotten_password"];
+ $this->inscription_date = $data["inscription_date"];
+ }
+ public static function getOrganizers()
+ {
+ global $DB, $YEAR;
+ $admins = [];
+ $req = $DB->query("SELECT * FROM `users` WHERE `role` = 'ORGANIZER' OR `role` = 'ADMIN' AND `year` = $YEAR ORDER BY `role` DESC, `surname`, `first_name`;");
+
+ while (($data = $req->fetch()) !== false) {
+ $admin = new User();
+ $admin->fill($data);
+ $admins[] = $admin;
+ }
+
+ return $admins;
+ }
+
+ public static function getAdmins()
+ {
+ global $DB, $YEAR;
+ $users = [];
+ $req = $DB->query("SELECT * FROM `users` WHERE (`role` = 'ADMIN') "
+ . "AND `year` = $YEAR ORDER BY `role`, `inscription_date`;");
+
+ while (($data = $req->fetch()) !== false) {
+ $orphan = new User();
+ $orphan->fill($data);
+ $users[] = $orphan;
+ }
+
+ return $users;
+ }
+
+ public static function getAllUsers()
+ {
+ global $DB, $YEAR;
+ $users = [];
+ $req = $DB->query("SELECT * FROM `users` WHERE `year` = $YEAR ORDER BY `role` DESC, `inscription_date`;");
+
+ while (($data = $req->fetch()) !== false) {
+ $orphan = new User();
+ $orphan->fill($data);
+ $users[] = $orphan;
+ }
+
+ return $users;
+ }
+
+ public static function getOrphanUsers()
+ {
+ global $DB, $YEAR;
+ $orphans = [];
+ $req = $DB->query("SELECT * FROM `users` WHERE `role` != 'ADMIN' AND `team_id` IS NULL "
+ . "AND `year` = $YEAR ORDER BY `role`, `inscription_date`;");
+
+ while (($data = $req->fetch()) !== false) {
+ $orphan = new User();
+ $orphan->fill($data);
+ $orphans[] = $orphan;
+ }
+
+ return $orphans;
+ }
+
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ public function setEmail($email)
+ {
+ global $DB;
+ $this->email = $email;
+ $DB->prepare("UPDATE `users` SET `email` = ? WHERE `id` = ?;")->execute([$email, $this->getId()]);
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function checkPassword($password)
+ {
+ return password_verify($password, $this->pwd_hash);
+ }
+
+ public function setPassword($password)
+ {
+ $this->setPasswordHash(password_hash($password, PASSWORD_BCRYPT));
+ }
+
+ private function setPasswordHash($password_hash)
+ {
+ global $DB;
+ $this->pwd_hash = $password_hash;
+ $DB->prepare("UPDATE `users` SET `pwd_hash` = ? WHERE `id` = ?;")->execute([$password_hash, $this->getId()]);
+ }
+
+ public function getSurname()
+ {
+ return $this->surname;
+ }
+
+ public function setSurname($surname)
+ {
+ global $DB;
+ $this->surname = $surname;
+ $DB->prepare("UPDATE `users` SET `surname` = ? WHERE `id` = ?;")->execute([$surname, $this->getId()]);
+ }
+
+ public function getFirstName()
+ {
+ return $this->first_name;
+ }
+
+ public function setFirstName($first_name)
+ {
+ global $DB;
+ $this->first_name = $first_name;
+ $DB->prepare("UPDATE `users` SET `first_name` = ? WHERE `id` = ?;")->execute([$first_name, $this->getId()]);
+ }
+
+ public function getBirthDate()
+ {
+ return $this->birth_date;
+ }
+
+ public function setBirthDate($birth_date)
+ {
+ global $DB;
+ $this->birth_date = $birth_date;
+ $DB->prepare("UPDATE `users` SET `birth_date` = ? WHERE `id` = ?;")->execute([$birth_date, $this->getId()]);
+ }
+
+ public function getGender()
+ {
+ return $this->gender;
+ }
+
+ public function setGender($gender)
+ {
+ global $DB;
+ $this->gender = $gender;
+ $DB->prepare("UPDATE `users` SET `gender` = ? WHERE `id` = ?;")->execute([$gender, $this->getId()]);
+ }
+
+ public function getAddress()
+ {
+ return $this->address;
+ }
+
+ public function setAddress($address)
+ {
+ global $DB;
+ $this->address = $address;
+ $DB->prepare("UPDATE `users` SET `address` = ? WHERE `id` = ?;")->execute([$address, $this->getId()]);
+ }
+
+ public function getPostalCode()
+ {
+ return $this->postal_code;
+ }
+
+ public function setPostalCode($postal_code)
+ {
+ global $DB;
+ $this->postal_code = $postal_code;
+ $DB->prepare("UPDATE `users` SET `postal_code` = ? WHERE `id` = ?;")->execute([$postal_code, $this->getId()]);
+ }
+
+ public function getCity()
+ {
+ return $this->city;
+ }
+
+ public function setCity($city)
+ {
+ global $DB;
+ $this->city = $city;
+ $DB->prepare("UPDATE `users` SET `city` = ? WHERE `id` = ?;")->execute([$city, $this->getId()]);
+ }
+
+ public function getCountry()
+ {
+ return $this->country;
+ }
+
+ public function setCountry($country)
+ {
+ global $DB;
+ $this->country = $country;
+ $DB->prepare("UPDATE `users` SET `country` = ? WHERE `id` = ?;")->execute([$country, $this->getId()]);
+ }
+
+ public function getPhoneNumber()
+ {
+ return $this->phone_number;
+ }
+
+ public function setPhoneNumber($phone_number)
+ {
+ global $DB;
+ $this->phone_number = $phone_number;
+ $DB->prepare("UPDATE `users` SET `phone_number` = ? WHERE `id` = ?;")->execute([$phone_number, $this->getId()]);
+ }
+
+ public function getSchool()
+ {
+ return $this->school;
+ }
+
+ public function setSchool($school)
+ {
+ global $DB;
+ $this->school = $school;
+ $DB->prepare("UPDATE `users` SET `school` = ? WHERE `id` = ?;")->execute([$school, $this->getId()]);
+ }
+
+ public function getClass()
+ {
+ return $this->class;
+ }
+
+ public function setClass($class)
+ {
+ global $DB;
+ $this->class = $class;
+ $DB->prepare("UPDATE `users` SET `class` = ? WHERE `id` = ?;")->execute([SchoolClass::getName($class), $this->getId()]);
+ }
+
+ public function getResponsibleName()
+ {
+ return $this->responsible_name;
+ }
+
+ public function setResponsibleName($responsible_name)
+ {
+ global $DB;
+ $this->responsible_name = $responsible_name;
+ $DB->prepare("UPDATE `users` SET `responsible_name` = ? WHERE `id` = ?;")->execute([$responsible_name, $this->getId()]);
+ }
+
+ public function getResponsiblePhone()
+ {
+ return $this->responsible_phone;
+ }
+
+ public function setResponsiblePhone($responsible_phone)
+ {
+ global $DB;
+ $this->responsible_phone = $responsible_phone;
+ $DB->prepare("UPDATE `users` SET `responsible_phone` = ? WHERE `id` = ?;")->execute([$responsible_phone, $this->getId()]);
+ }
+
+ public function getResponsibleEmail()
+ {
+ return $this->responsible_email;
+ }
+
+ public function setResponsibleEmail($responsible_email)
+ {
+ global $DB;
+ $this->responsible_email = $responsible_email;
+ $DB->prepare("UPDATE `users` SET `responsible_email` = ? WHERE `id` = ?;")->execute([$responsible_email, $this->getId()]);
+ }
+
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ public function setDescription($desc)
+ {
+ global $DB;
+ $this->description = $desc;
+ $DB->prepare("UPDATE `users` SET `description` = ? WHERE `id` = ?;")->execute([$desc, $this->getId()]);
+ }
+
+ public function getRole()
+ {
+ return $this->role;
+ }
+
+ public function setRole($role)
+ {
+ global $DB;
+ $this->role = $role;
+ /** @noinspection PhpUndefinedMethodInspection */
+ $DB->prepare("UPDATE `users` SET `role` = ? WHERE `id` = ?;")->execute([Role::getName($role), $this->getId()]);
+ }
+
+ public function getTeamId()
+ {
+ return $this->team_id;
+ }
+
+ public function setTeamId($team_id)
+ {
+ global $DB;
+ $this->team_id = $team_id;
+ $DB->prepare("UPDATE `users` SET `team_id` = ? WHERE `id` = ?;")->execute([$team_id, $this->getId()]);
+ }
+
+ public function getYear()
+ {
+ return $this->year;
+ }
+
+ public function getConfirmEmailToken()
+ {
+ return $this->confirm_email;
+ }
+
+ public function setConfirmEmailToken($token)
+ {
+ global $DB;
+ $this->confirm_email = $token;
+ $DB->prepare("UPDATE `users` SET `confirm_email` = ? WHERE `id` = ?;")->execute([$token, $this->getId()]);
+ }
+
+ public function getForgottenPasswordToken()
+ {
+ return $this->forgotten_password;
+ }
+
+ public function setForgottenPasswordToken($token)
+ {
+ global $DB;
+ $this->forgotten_password = $token;
+ $DB->prepare("UPDATE `users` SET `forgotten_password` = ? WHERE `id` = ?;")->execute([$token, $this->getId()]);
+ }
+
+ public function getInscriptionDate()
+ {
+ return $this->inscription_date;
+ }
+
+ public function getAllDocuments($tournament_id)
+ {
+ global $DB;
+ $req = $DB->query("SELECT * FROM `documents` AS `t1` "
+ . "INNER JOIN (SELECT `user`, `type`, `tournament`, MAX(`uploaded_at`) AS `last_upload`, COUNT(`team`) AS `version` FROM `documents` GROUP BY `tournament`, `type`, `user`) `t2` "
+ . "ON `t1`.`user` = `t2`.`user` AND `t1`.`type` = `t2`.`type` AND `t1`.`tournament` = `t2`.`tournament` "
+ . "WHERE `t1`.`uploaded_at` = `t2`.`last_upload` AND `t1`.`tournament` = $tournament_id AND `t1`.`user` = $this->id ORDER BY `t1`.`type`;");
+
+ $docs = [];
+
+ while (($data = $req->fetch()) !== false)
+ $docs[] = Document::fromData($data);
+
+ if ($this->team_id > 0) {
+ $req = $DB->query("SELECT * FROM `documents` AS `t1` "
+ . "INNER JOIN (SELECT `user`, `type`, `tournament`, MAX(`uploaded_at`) AS `last_upload`, COUNT(`team`) AS `version` FROM `documents` GROUP BY `tournament`, `type`, `user`) `t2` "
+ . "ON `t1`.`user` = `t2`.`user` AND `t1`.`type` = `t2`.`type` AND `t1`.`tournament` = `t2`.`tournament` "
+ . "WHERE `t1`.`uploaded_at` = `t2`.`last_upload` AND `t1`.`tournament` = $tournament_id AND `t1`.`team` = $this->team_id "
+ . "AND `t1`.`type` = 'MOTIVATION_LETTER';");
+
+ while (($data = $req->fetch()) !== false)
+ $docs[] = Document::fromData($data);
+ }
+
+ return $docs;
+ }
+
+ public function getPayment() {
+ global $DB;
+
+ $team = Team::fromId($this->team_id);
+ $tournament = $team->getEffectiveTournament();
+
+ $req = $DB->prepare("SELECT `id` FROM `payments` WHERE `user` = ?;");
+ $req->execute([$this->id]);
+
+ if (($data = $req->fetch()) !== false)
+ return Payment::fromId($data["id"]);
+
+ $req = $DB->prepare("INSERT INTO `payments`(`user`, `tournament`, `amount`, `method`, `transaction_infos`, `validation_status`) VALUES (?, ?, ?, ?, ?, ?);");
+ $req->execute([$this->id, $tournament->getId(), 0, PaymentMethod::getName(PaymentMethod::NOT_PAID), "L'inscription n'est pas encore payée.", ValidationStatus::getName(ValidationStatus::NOT_READY)]);
+
+ return $this->getPayment();
+ }
+
+ public function getOrganizedTournaments()
+ {
+ global $DB;
+ $req = $DB->query("SELECT `tournament` FROM `organizers` JOIN `tournaments` ON `tournaments`.`id` = `tournament` WHERE `organizer` = $this->id ORDER BY `date_start`, `name`;");
+
+ $tournaments = [];
+
+ while (($data = $req->fetch()) !== false)
+ $tournaments[] = Tournament::fromId($data["tournament"]);
+
+ return $tournaments;
+ }
+}
\ No newline at end of file
diff --git a/server_files/classes/ValidationStatus.php b/server_files/classes/ValidationStatus.php
new file mode 100644
index 0000000..459d7ad
--- /dev/null
+++ b/server_files/classes/ValidationStatus.php
@@ -0,0 +1,41 @@
+ PDO::ERRMODE_EXCEPTION));
+}
+catch (Exception $ex) {
+ die("Erreur lors de la connexion à la base de données : " . $ex->getMessage());
+}
+
+date_default_timezone_set("Europe/Paris");
+
+session_start();
+setlocale(LC_ALL, "fr_FR.utf8");
diff --git a/server_files/controllers/ajouter_equipe.php b/server_files/controllers/ajouter_equipe.php
new file mode 100644
index 0000000..95112bb
--- /dev/null
+++ b/server_files/controllers/ajouter_equipe.php
@@ -0,0 +1,63 @@
+query("SELECT `id`, `name` FROM `tournaments` WHERE `date_inscription` > CURRENT_TIMESTAMP AND `year` = $YEAR;");
+
+$has_error = false;
+$error_message = null;
+
+if (isset($_POST["add_team"])) {
+ $new_team = new NewTeam($_POST);
+ try {
+ $new_team->makeVerifications();
+ $new_team->register();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class NewTeam {
+ public $name;
+ public $trigram;
+ public $tournament_id;
+ public $tournament;
+ public $access_code;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications() {
+ ensure($_SESSION["team"] == null, "Vous êtes déjà dans une équipe.");
+ ensure($this->name != null && $this->name != "", "Vous devez spécifier un nom d'équipe.");
+ ensure(preg_match("#^[A-Z]{3}$#", $this->trigram), "Le trigramme entré n'est pas valide.");
+ ensure(!teamExists($this->name), "Une équipe existe déjà avec ce nom.");
+ ensure(!trigramExists($this->trigram), "Une équipe a déjà choisi ce trigramme.");
+ $this->tournament = Tournament::fromId($this->tournament_id);
+ ensure($this->tournament != null, "Le tournoi spécifié n'existe pas.");
+ }
+
+ public function register() {
+ global $DB, $YEAR;
+
+ $this->access_code = genRandomPhrase(6);
+
+ $req = $DB->prepare("INSERT INTO `teams` (`name`, `trigram`, `tournament`, `encadrant_1`, `participant_1`, `validation_status`, `access_code`, `year`)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
+ $req->execute([$this->name, $this->trigram, $this->tournament_id, $_SESSION["role"] == Role::ENCADRANT ? $_SESSION["user_id"] : NULL,
+ $_SESSION["role"] == Role::PARTICIPANT ? $_SESSION["user_id"] : NULL, ValidationStatus::getName(ValidationStatus::NOT_READY), $this->access_code, $YEAR]);
+
+ $_SESSION["team"] = Team::fromTrigram($this->trigram);
+ $_SESSION["user"]->setTeamId($_SESSION["team"]->getId());
+
+ Mailer::sendAddTeamMail($_SESSION["user"], $_SESSION["team"], $this->tournament);
+ }
+}
+
+require_once "server_files/views/ajouter_equipe.php";
diff --git a/server_files/controllers/ajouter_organisateur.php b/server_files/controllers/ajouter_organisateur.php
new file mode 100644
index 0000000..d196a88
--- /dev/null
+++ b/server_files/controllers/ajouter_organisateur.php
@@ -0,0 +1,59 @@
+makeVerifications();
+ $orga->register();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class NewOrganizer {
+ public $surname;
+ public $first_name;
+ public $email;
+ public $admin;
+ public $password;
+ public $token;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications()
+ {
+ ensure($this->surname != null && $this->surname != "", "Le nom est invalide.");
+ ensure($this->first_name != null && $this->first_name != "", "Le prénom est invalide.");
+ ensure(filter_var($this->email, FILTER_VALIDATE_EMAIL), "L'adresse e-mail est invalide.");
+ $this->email = strtolower($this->email);
+ ensure(!userExists($this->email), "Cette adresse e-mail est déjà utilisée.");
+ $this->admin = isset($this->admin) ? 1 : 0;
+ }
+
+ public function register() {
+ global $DB, $YEAR;
+
+ $this->password = genRandomPhrase(16, true);
+ $this->token = genRandomPhrase(64);
+
+ $req = $DB->prepare("INSERT INTO `users`(`email`, `pwd_hash`, `surname`, `first_name`, `role`, `forgotten_password`, `year`)
+ VALUES (?, ?, ?, ?, ?, ?, ?);");
+ $req->execute([$this->email, password_hash($this->password, PASSWORD_BCRYPT), $this->surname, $this->first_name, $this->admin ? "ADMIN" : "ORGANIZER", $this->token, $YEAR]);
+
+ Mailer::sendAddOrganizerMail($this);
+ }
+}
+
+require_once "server_files/views/ajouter_organisateur.php";
\ No newline at end of file
diff --git a/server_files/controllers/ajouter_tournoi.php b/server_files/controllers/ajouter_tournoi.php
new file mode 100644
index 0000000..6618de7
--- /dev/null
+++ b/server_files/controllers/ajouter_tournoi.php
@@ -0,0 +1,111 @@
+makeVerifications();
+ $tournament->register();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class NewTournament {
+ public $name;
+ public $organizers;
+ public $size;
+ public $place;
+ public $price;
+ public $date_start;
+ public $date_end;
+ public $date_inscription;
+ public $time_inscription;
+ public $date_solutions;
+ public $time_solutions;
+ public $date_syntheses;
+ public $time_syntheses;
+ public $date_solutions_2;
+ public $time_solutions_2;
+ public $date_syntheses_2;
+ public $time_syntheses_2;
+ public $description;
+ public $final;
+ public $tournament;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = ($key == "organizers" ? $value : htmlspecialchars($value));
+ }
+
+ public function makeVerifications()
+ {
+ global $FINAL;
+
+ ensure($this->name != null && $this->name != "", "Le nom est invalide.");
+ ensure(!tournamentExists($this->name), "Un tournoi existe déjà avec ce nom.");
+ ensure(sizeof($this->organizers) > 0, "Aucun organisateur n'a été choisi.");
+
+ $orgas = [];
+ foreach ($this->organizers as $orga_id) {
+ $orga = User::fromId($orga_id);
+ ensure($orga != null, "Un organisateur spécifié n'existe pas.");
+ ensure($orga->getRole() == Role::ORGANIZER || $orga->getRole() == Role::ADMIN, "Une personne indiquée ne peut pas organiser de tournoi.");
+ $orgas[] = $orga;
+ }
+ $this->organizers = $orgas;
+
+ ensure(preg_match("#[0-9]*#", $this->size), "Le nombre d'équipes indiqué n'est pas un nombre valide.");
+ $this->size = intval($this->size);
+ ensure($this->size >= 3 && $this->size <= 15, "Un tournoi doit avoir au moins 3 et au plus 15 équipes.");
+
+ ensure(preg_match("#[0-9]*#", $this->price), "Le tarif pour les participants n'est pas un entier valide.");
+ $this->price = intval($this->price);
+ ensure($this->price >= 0, "Le TFJM² ne va pas payer les élèves pour venir.");
+ ensure($this->price <= 50, "Soyons raisonnable sur le prix.");
+
+ ensure(dateWellFormed($this->date_start), "La date de début n'est pas valide.");
+ ensure(dateWellFormed($this->date_end), "La date de fin n'est pas valide.");
+ ensure(dateWellFormed($this->date_inscription . " " . $this->time_inscription), "La date de clôture des inscriptions n'est pas valide.");
+ ensure(dateWellFormed($this->date_solutions . " " . $this->time_solutions), "La date limite de remise des solutions n'est pas valide.");
+ ensure(dateWellFormed($this->date_syntheses . " " . $this->time_syntheses), "La date limite de remise des notes de synthèse pour le tour 1 n'est pas valide.");
+ ensure(dateWellFormed($this->date_solutions_2 . " " . $this->time_solutions_2), "La date limite de visibilité des solutions du tour 2 n'est pas valide.");
+ ensure(dateWellFormed($this->date_syntheses . " " . $this->time_syntheses), "La date limite de remise des notes de synthèse pour le tour 2 n'est pas valide.");
+
+ $this->final = $this->final ? 1 : 0;
+
+ ensure(!$this->final || $FINAL == NULL, "Une finale nationale est déjà enregistrée.");
+ }
+
+ public function register()
+ {
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("INSERT INTO `tournaments` (`name`, `size`, `place`, `price`, `description`,
+ `date_start`, `date_end`, `date_inscription`, `date_solutions`, `date_syntheses`,
+ `date_solutions_2`, `date_syntheses_2`, `final`, `year`)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
+ $req->execute([$this->name, $this->size, $this->place, $this->price, $this->description, $this->date_start, $this->date_end,
+ "$this->date_inscription $this->time_inscription", "$this->date_solutions $this->time_solutions",
+ "$this->date_syntheses $this->time_syntheses", "$this->date_solutions_2 $this->time_solutions_2",
+ "$this->date_syntheses_2 $this->time_syntheses_2", $this->final ? 1 : 0, $YEAR]);
+
+ $this->tournament = Tournament::fromName($this->name);
+
+ /** @var User $organizer */
+ foreach ($this->organizers as $organizer) {
+ $this->tournament->addOrganizer($organizer);
+ Mailer::sendAddOrganizerForTournamentMail($organizer, $this->tournament);
+ }
+ }
+}
+
+require_once "server_files/views/ajouter_tournoi.php";
diff --git a/server_files/controllers/autorisation_droit_image.php b/server_files/controllers/autorisation_droit_image.php
new file mode 100644
index 0000000..41b20c5
--- /dev/null
+++ b/server_files/controllers/autorisation_droit_image.php
@@ -0,0 +1,52 @@
+getEffectiveTournament();
+
+ $majeur = $user->getBirthDate() > strval($YEAR - 18) . substr($tournament->getStartDate(), 4);
+
+ $tex = file_get_contents("assets/Autorisation_droit_image_" . ($majeur ? "majeur" : "mineur") . ".tex");
+
+ $tex = preg_replace("#{PARTICIPANT_NAME}#", "\\texttt{" . $user->getFirstName() . " " . $user->getSurname() . "}", $tex);
+ $tex = preg_replace("#{BIRTHDAY}#", "\\texttt{" . strftime("%d %B %G", strtotime($user->getBirthDate())) . "}", $tex);
+ $tex = preg_replace("#{ADDRESS}#", "\\texttt{" . $user->getAddress() . ", " . $user->getPostalCode() . ", " . $user->getCity()
+ . ($user->getCountry() == "France" ? "" : $user->getCountry()) . "}.", $tex);
+}
+
+$tex = preg_replace("#{TOURNAMENT_NAME}#", $tournament->getName(), $tex);
+$tex = preg_replace("#{PLACE}#", $tournament->getPlace(), $tex);
+$tex = preg_replace("#{START_DATE}#", strftime("%d %B", strtotime($tournament->getStartDate())), $tex);
+$tex = preg_replace("#{END_DATE}#", strftime("%d %B", strtotime($tournament->getEndDate())), $tex);
+$tex = preg_replace("#{YEAR}#", $YEAR, $tex);
+
+shell_exec("mkdir tmp");
+
+file_put_contents("tmp/file.tex", $tex);
+
+shell_exec("pdflatex -synctex=1 -interaction=nonstopmode -shell-escape -output-directory=tmp tmp/file.tex");
+header("Content-type: application/pdf");
+readfile("tmp/file.pdf");
+
+exit(0);
\ No newline at end of file
diff --git a/server_files/controllers/autorisation_parentale.php b/server_files/controllers/autorisation_parentale.php
new file mode 100644
index 0000000..40cdc29
--- /dev/null
+++ b/server_files/controllers/autorisation_parentale.php
@@ -0,0 +1,44 @@
+getEffectiveTournament();
+ $tex = preg_replace("#{PARTICIPANT_NAME}#", "\\texttt{" . $user->getFirstName() . " " . $user->getSurname() . "}", $tex);
+ $tex = preg_replace("#{BIRTHDAY}#", "\\texttt{" . strftime("%d %B %G", strtotime($user->getBirthDate())) . "}", $tex);
+ $tex = preg_replace("#{PRONOUN}#", $user->getGender() == "M" ? "Il" : "Elle", $tex);
+}
+
+$tex = preg_replace("#{TOURNAMENT_NAME}#", $tournament->getName(), $tex);
+$tex = preg_replace("#{PLACE}#", $tournament->getPlace(), $tex);
+$tex = preg_replace("#{START_DATE}#", strftime("%d %B", strtotime($tournament->getStartDate())), $tex);
+$tex = preg_replace("#{END_DATE}#", strftime("%d %B", strtotime($tournament->getEndDate())), $tex);
+$tex = preg_replace("#{YEAR}#", $YEAR, $tex);
+
+shell_exec("mkdir tmp");
+
+file_put_contents("tmp/file.tex", $tex);
+
+shell_exec("pdflatex -synctex=1 -interaction=nonstopmode -shell-escape -output-directory=tmp tmp/file.tex");
+header("Content-type: application/pdf");
+readfile("tmp/file.pdf");
+
+exit(0);
\ No newline at end of file
diff --git a/server_files/controllers/confirmer_mail.php b/server_files/controllers/confirmer_mail.php
new file mode 100644
index 0000000..3b2326f
--- /dev/null
+++ b/server_files/controllers/confirmer_mail.php
@@ -0,0 +1,22 @@
+query("SELECT `email` FROM `users` WHERE `confirm_email` = '$token' AND `year` = '$YEAR';");
+ if (($data = $result->fetch()) === FALSE)
+ $error_message = "Le jeton est invalide. Votre compte est peut-être déjà validé ?";
+ else {
+ $DB->exec("UPDATE `users` SET `confirm_email` = NULL WHERE `confirm_email` = '$token';");
+ $error_message = "Votre adresse mail a été validée ! Vous pouvez désormais vous connecter.";
+ $alert = "success";
+ }
+}
+else {
+ $error_message = "Il n'y a pas de compte à valider !";
+ $alert = "warning";
+}
+require_once "server_files/views/header.php";
+echo "
$error_message
";
+require_once "server_files/views/footer.php";
diff --git a/server_files/controllers/connexion.php b/server_files/controllers/connexion.php
new file mode 100644
index 0000000..47c8b69
--- /dev/null
+++ b/server_files/controllers/connexion.php
@@ -0,0 +1,170 @@
+makeVerifications();
+ $logging_in_user->login();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["forgotten_password"]) && !isset($_SESSION["user_id"])) {
+ $recuperate_account = new RecuperateAccount($_POST);
+ try {
+ $recuperate_account->makeVerifications();
+ $recuperate_account->recuperateAccount();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_GET["reset_password"]) && isset($_GET["token"]) && !isset($_SESSION["user_id"])) {
+ $reset_password = new ResetPassword($_GET, $_POST);
+ try {
+ $reset_password->makeVerifications();
+ if (isset($_POST["password"]))
+ $reset_password->resetPassword();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_GET["confirmation-mail"]) && !isset($_SESSION["user_id"]))
+ sendConfirmEmail();
+
+class LoggingInUser
+{
+ public $email;
+ /** @var User $user */
+ public $user;
+ private $password;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications()
+ {
+ global $URL_BASE;
+
+ ensure(filter_var($this->email, FILTER_VALIDATE_EMAIL), "L'adresse email est invalide.");
+ $this->user = User::fromEmail($this->email);
+ ensure($this->user != null, "Le compte n'existe pas.");
+ ensure($this->user->checkPassword($this->password), "Le mot de passe est incorrect.");
+ if ($this->user->getConfirmEmailToken() != null) {
+ $_SESSION["confirm_email"] = $this->email;
+ throw new AssertionError("L'adresse mail n'a pas été validée. Veuillez vérifier votre boîte mail (surtout vos spams). "
+ . "Cliquez ici pour renvoyer le mail de confirmation.");
+ }
+ }
+
+ public function login()
+ {
+ $_SESSION["user_id"] = $this->user->getId();
+ loadUserValues();
+ }
+}
+
+class RecuperateAccount
+{
+ public $email;
+ /** @var User $user */
+ public $user;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications()
+ {
+ ensure(filter_var($this->email, FILTER_VALIDATE_EMAIL), "L'adresse email est invalide.");
+ $this->user = User::fromEmail($this->email);
+ ensure($this->user != null, "Le compte n'existe pas.");
+ }
+
+ public function recuperateAccount()
+ {
+ $token = genRandomPhrase(64);
+ $this->user->setForgottenPasswordToken($token);
+ Mailer::sendForgottenPasswordProcedureMail($this->user);
+ }
+}
+
+class ResetPassword
+{
+ public $token;
+ /** @var User $user */
+ public $user;
+ private $password;
+ private $confirm_password;
+
+ public function __construct($data, $data2)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ foreach ($data2 as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications()
+ {
+ global $DB;
+ $data = $DB->query("SELECT `id` FROM `users` WHERE `forgotten_password` = '" . $this->token . "';")->fetch();
+ ensure($data !== false, "Il n'y a pas de compte à récupérer avec ce jeton.");
+ $this->user = User::fromId($data["id"]);
+
+ if ($this->password == null)
+ return;
+
+ ensure($this->password == $this->confirm_password, "Les deux mots de passe sont différents.");
+ ensure(strlen($this->password) >= 8, "Le mot de passe doit comporter au moins 8 caractères.");
+ }
+
+ public function resetPassword()
+ {
+ $this->user->setForgottenPasswordToken(null);
+ $this->user->setPassword($this->password);
+
+ Mailer::sendChangePasswordMail($this->user);
+
+ return false;
+ }
+}
+
+function sendConfirmEmail()
+{
+ global $URL_BASE;
+
+ $email = htmlspecialchars($_SESSION["confirm_email"]);
+
+ if (!isset($email)) {
+ header("Location: $URL_BASE/connexion");
+ exit();
+ }
+
+ $user = User::fromEmail($email);
+
+ if ($user === null) {
+ unset($_SESSION["confirm_email"]);
+ header("Location: $URL_BASE/connexion");
+ exit();
+ }
+
+ Mailer::sendConfirmEmail($user);
+
+ return false;
+}
+
+require_once "server_files/views/connexion.php";
diff --git a/server_files/controllers/deconnexion.php b/server_files/controllers/deconnexion.php
new file mode 100644
index 0000000..eb04b0a
--- /dev/null
+++ b/server_files/controllers/deconnexion.php
@@ -0,0 +1,14 @@
+
+
+
+ Déconnexion réussie !
+
+
+getTournamentId());
+
+if ($_SESSION["role"] == Role::ORGANIZER && !$tournament->organize($_SESSION["user_id"]))
+ require_once "server_files/403.php";
+
+if ($team === null)
+ require_once "server_files/404.php";
+
+if (isset($_POST["team_edit"])) {
+ $edit_team = new EditTeam($_POST);
+ try {
+ $edit_team->makeVerifications();
+ $edit_team->updateTeam();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["validate"])) {
+ $team->setValidationStatus(ValidationStatus::VALIDATED);
+ Mailer::sendValidateTeam($team, $_POST["message"]);
+}
+elseif (isset($_POST["unvalidate"])) {
+ $team->setValidationStatus(ValidationStatus::NOT_READY);
+ Mailer::sendUnvalidateTeam($team, $_POST["message"]);
+}
+
+if (isset($_POST["select"])) {
+ $team->selectForFinal(true);
+ # $team->setValidationStatus(ValidationStatus::NOT_READY);
+ $sols = $tournament->getAllSolutions($team->getId());
+ /** @var Solution $sol */
+ foreach ($sols as $sol) {
+ $old_id = $sol->getFileId();
+ do
+ $id = genRandomPhrase(64);
+ while (file_exists("$LOCAL_PATH/files/$id"));
+
+ copy("$LOCAL_PATH/files/$old_id", "$LOCAL_PATH/files/$id");
+
+ $req = $DB->prepare("INSERT INTO `solutions`(`file_id`, `team`, `tournament`, `problem`) VALUES (?, ?, ?, ?);");
+ $req->execute([$id, $team->getId(), $FINAL->getId(), $sol->getProblem()]);
+ }
+}
+
+if (isset($_POST["download_zip"])) {
+ $final = isset($_POST["final"]);
+ $tournament_dest = $final ? $FINAL : $tournament;
+
+ $file_name = getZipFile(DocumentType::PARENTAL_CONSENT, $tournament_dest->getId(), $team->getId());
+
+ header("Content-Type: application/zip");
+ header("Content-Disposition: attachment; filename=\"Documents de l'équipe " . $team->getTrigram() . ".zip\"");
+ header("Content-Length: " . strval(filesize($file_name)));
+
+ readfile($file_name);
+
+ exit();
+}
+
+if (isset($_POST["select_tournament"])) {
+ $new_tournament = Tournament::fromId($_POST["select_tournament"]);
+ ensure($new_tournament != null, "Le tournoi indiqué n'existe pas.");
+ $team->setTournamentId($new_tournament->getId());
+ $DB->prepare("UPDATE `documents` SET `tournament` = ? WHERE `team` = ?;")->execute([$tournament->getId(), $team->getId()]);
+ $DB->prepare("UPDATE `solutions` SET `tournament` = ? WHERE `team` = ?;")->execute([$tournament->getId(), $team->getId()]);
+ $DB->prepare("UPDATE `syntheses` SET `tournament` = ? WHERE `team` = ?;")->execute([$tournament->getId(), $team->getId()]);
+ foreach ($team->getParticipants() as $user) {
+ if ($user != null)
+ $DB->prepare("UPDATE `payments` SET `tournament` = ? WHERE `user` = ?;")->execute([$tournament->getId(), $user]);
+ }
+ foreach ($team->getEncadrants() as $user) {
+ if ($user != null)
+ $DB->prepare("UPDATE `payments` SET `tournament` = ? WHERE `user` = ?;")->execute([$tournament->getId(), $user]);
+ }
+ $tournament = $new_tournament;
+}
+
+if (isset($_POST["delete_team"])) {
+ foreach ($team->getEncadrants() as $encadrant_id) {
+ quitTeam($encadrant_id);
+ }
+ foreach ($team->getParticipants() as $participant_id) {
+ quitTeam($participant_id);
+ }
+
+ header("Location: /");
+ return;
+
+}
+
+class EditTeam
+{
+ public $name;
+ public $trigram;
+ public $tournament_id;
+ private $team;
+ private $tournament;
+
+ public function __construct($data)
+ {
+ global $team;
+
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->trigram = strtoupper($this->trigram);
+ $this->team = $team;
+ $this->tournament = Tournament::fromId($this->tournament_id);
+ }
+
+ public function makeVerifications()
+ {
+ ensure($this->name != "" && $this->name != null, "Veuillez spécifier un nom d'équipe.");
+ ensure($this->name == $this->team->getName() || !teamExists($this->name), "Une équipe existe déjà avec ce nom.");
+ ensure(preg_match("#^[A-Z]{3}$#", $this->trigram), "Le trigramme n'est pas valide.");
+ ensure($this->trigram == $this->team->getTrigram() || !trigramExists($this->trigram), "Une équipe a déjà choisi ce trigramme.");
+ ensure($this->tournament != null, "Le tournoi indiqué n'existe pas.");
+ ensure($this->tournament_id == $this->team->getTournamentId() || $_SESSION["role"] == Role::ADMIN, "Vous n'avez pas la permission pour changer cette équipe de tournoi.");
+ }
+
+ public function updateTeam()
+ {
+ global $URL_BASE;
+
+ $this->team->setName($this->name);
+ $this->team->setTrigram($this->trigram);
+ $this->team->setTournamentId($this->tournament_id);
+
+ $_SESSION["tournament"] = $this->tournament;
+
+ header("Location: $URL_BASE/equipe/$this->trigram");
+ }
+}
+
+$documents = $tournament->getAllDocuments($team->getId());
+$documents_final = null;
+
+if ($team->isSelectedForFinal())
+ $documents_final = $FINAL->getAllDocuments($team->getId());
+
+$emails = [];
+
+if ($_SESSION["role"] == Role::ORGANIZER || $_SESSION["role"] == Role::ADMIN) {
+ foreach ($team->getEncadrants() as $encadrant_id) {
+ $encadrant = User::fromId($encadrant_id);
+ if ($encadrant != null) {
+ $emails[] = $encadrant->getEmail();
+ }
+ }
+
+ foreach ($team->getParticipants() as $participant_id) {
+ $participant = User::fromId($participant_id);
+ if ($participant != null) {
+ $emails[] = $participant->getEmail();
+ if ($participant->getResponsibleEmail() != null) {
+ $emails[] = $participant->getResponsibleEmail();
+ }
+ }
+ }
+}
+
+require_once "server_files/views/equipe.php";
diff --git a/server_files/controllers/index.php b/server_files/controllers/index.php
new file mode 100644
index 0000000..5a23b80
--- /dev/null
+++ b/server_files/controllers/index.php
@@ -0,0 +1,20 @@
+getTeamId());
+
+if ($_SESSION["role"] != Role::ADMIN) {
+ if ($_SESSION["role"] == Role::ORGANIZER) {
+ if (($user->getRole() == Role::PARTICIPANT || $user->getRole() == Role::PARTICIPANT) && ($team == null || $team->getTournamentId() == null || !Tournament::fromId($team->getTournamentId())->organize($_SESSION["user_id"])))
+ require_once "server_files/403.php";
+ }
+ elseif ($user->getId() != $_SESSION["user_id"])
+ require_once "server_files/403.php";
+}
+
+if ($user === null)
+ require_once "server_files/404.php";
+
+if ($team != null) {
+ $documents = $user->getAllDocuments($team->getTournamentId());
+ $documents_final = $user->getAllDocuments($FINAL->getId());
+ $payment = $user->getPayment();
+ $tournament = Tournament::fromId($team->getTournamentId());
+}
+
+$has_error = false;
+$error_message = null;
+
+if (isset($_POST["kick"])) {
+ if ($team == null) {
+ $has_error = true;
+ $error_message = "La personne à expulser n'est dans aucune équipe.";
+ }
+ else {
+ quitTeam($id);
+ $team = null;
+ }
+}
+
+if (isset($_POST["attribute_team"])) {
+ $attribute_team = new AttributeTeam($_POST);
+ try {
+ $attribute_team->makeVerifications();
+ $attribute_team->attribute();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["validate_payment"])) {
+ $validate_payment = new ValidatePayment($_POST);
+ try {
+ $validate_payment->makeVerifications();
+ $validate_payment->validate();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["view_as"]) && $_SESSION["role"] == Role::ADMIN) {
+ if (!isset($_SESSION["admin"]))
+ $_SESSION["admin"] = $_SESSION["user_id"];
+ $_SESSION["user_id"] = $user->getId();
+ header("Location: /");
+ exit();
+}
+
+if (isset($_POST["delete_account"]) && $team == null && $_SESSION["role"] == Role::ADMIN) {
+ $DB->prepare("DELETE FROM `documents` WHERE `user` = ?;")->execute([$user->getId()]);
+ $DB->prepare("DELETE FROM `organizers` WHERE `organizer` = ?;")->execute([$user->getId()]);
+ $DB->prepare("DELETE FROM `users` WHERE `id` = ?;")->execute([$user->getId()]);
+ header("Location: /");
+ exit();
+}
+
+class AttributeTeam
+{
+ private $team_id;
+ private $team;
+ private $min_null_index;
+
+ public function __construct($data)
+ {
+ $this->team_id = htmlspecialchars($data["team"]);
+ $this->team = Team::fromId($this->team_id);
+ }
+
+ public function makeVerifications()
+ {
+ global $user;
+
+ ensure($user->getConfirmEmailToken() == null, "Ce participant n'a pas encore validé son adresse e-mail.");
+ ensure($this->team_id != "no_team", "Vous n'avez pas choisi d'équipe.");
+ ensure($this->team != null, "Cette équipe n'existe pas.");
+ ensure($user->getTeamId() <= 0, "Cette personne est déjà dans une équipe !");
+ ensure($this->team->getValidationStatus() == ValidationStatus::NOT_READY, "Cette équipe est déjà validée ou en cours de validation.");
+
+ $role = $user->getRole();
+ for ($i = 1; $i <= $role == Role::ENCADRANT ? 3 : 6; ++$i) {
+ if (($role == Role::PARTICIPANT ? $this->team->getParticipants()[$i - 1] : $this->team->getEncadrants()[$i]) == NULL)
+ break;
+ }
+
+ $this->min_null_index = $i;
+
+ ensure($role == Role::PARTICIPANT && $this->min_null_index <= 6 || $role == Role::ENCADRANT && $this->min_null_index <= 2,
+ "Il n'y a plus de place pour vous dans l'équipe.");
+ }
+
+ public function attribute()
+ {
+ global $user, $team;
+
+ $user->setTeamId($this->team->getId());
+
+ if ($user->getRole() == Role::ENCADRANT)
+ $this->team->setEncadrant($this->min_null_index, $user->getId());
+ else
+ $this->team->setParticipant($this->min_null_index, $user->getId());
+
+ Mailer::sendJoinTeamMail($user, $this->team, Tournament::fromId($this->team->getTournamentId()));
+
+ $team = $this->team;
+
+ global $documents, $payment, $tournament;
+
+ $documents = $user->getAllDocuments($team->getTournamentId());
+ $payment = $user->getPayment();
+ $tournament = Tournament::fromId($team->getTournamentId());
+ }
+}
+
+class ValidatePayment
+{
+ private $accept, $reject;
+ private $message;
+ private $payment;
+
+ public function __construct($data)
+ {
+ global $user;
+
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->payment = $user->getPayment();
+ }
+
+ public function makeVerifications()
+ {
+ ensure($this->payment->getValidationStatus() == ValidationStatus::WAITING, "Le paiement n'était pas en attente.");
+ ensure(isset($this->accept) ^ isset($this->reject), "La sélection de validation est invalide.");
+ }
+
+ public function validate()
+ {
+ global $user, $team, $tournament;
+
+ if ($this->accept)
+ $this->payment->setValidationStatus(ValidationStatus::VALIDATED);
+ else
+ $this->payment->setValidationStatus(ValidationStatus::NOT_READY);
+
+ Mailer::sendValidatePayment($user, $team, $tournament, $this->payment, $this->message);
+ }
+}
+
+require_once "server_files/views/informations.php";
diff --git a/server_files/controllers/inscription.php b/server_files/controllers/inscription.php
new file mode 100644
index 0000000..d0ab6f3
--- /dev/null
+++ b/server_files/controllers/inscription.php
@@ -0,0 +1,99 @@
+makeVerifications();
+ $user->register();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class NewUser
+{
+ public $email;
+ public $first_name;
+ public $surname;
+ public $birth_date;
+ public $gender;
+ public $address = "";
+ public $postal_code;
+ public $city = "";
+ public $country;
+ public $phone_number;
+ public $role;
+ public $school;
+ public $class;
+ public $responsible_name;
+ public $responsible_phone;
+ public $responsible_email;
+ public $description;
+ public $confirm_email_token;
+ private $password;
+ private $confirm_password;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+ }
+
+ public function makeVerifications()
+ {
+ global $YEAR;
+
+ ensure(filter_var($this->email, FILTER_VALIDATE_EMAIL), "L'adresse e-mail entrée est invalide.");
+ $this->email = strtolower($this->email);
+ ensure(!userExists($this->email), "Un compte existe déjà avec cette adresse e-mail.");
+ ensure(strlen($this->password) >= 8, "Le mot de passe doit comporter au moins 8 caractères.");
+ ensure($this->password == $this->confirm_password, "Les deux mots de passe sont différents.");
+ ensure($this->surname != "", "Le nom de famille est obligatoire.");
+ ensure($this->first_name != "", "Le prénom est obligatoire.");
+ ensure(dateWellFormed($this->birth_date), "La date de naissance est invalide.");
+ ensure($this->birth_date < $YEAR . "-01-01", "Vous devez être né.");
+ ensure($this->gender == "M" || $this->gender == "F", "Merci de spécifier un genre.");
+ ensure(preg_match("#^[0-9]{4}[0-9]?$#", $this->postal_code) && intval($this->postal_code) >= 01000 && intval($this->postal_code) <= 95999, "Le code postal est invalide.");
+ if ($this->country == "")
+ $this->country = "France";
+ ensure(strlen($this->phone_number) >= 10 && strlen($this->phone_number) <= 20, "Le numéro de téléphone est invalide.");
+ $this->role = Role::fromName(strtoupper($this->role));
+
+ if ($this->role == Role::PARTICIPANT) {
+ $this->class = SchoolClass::fromName(strtoupper($this->class));
+ if ($this->birth_date > strval($YEAR - 18) . "04-01") {
+ ensure($this->responsible_name != "", "Veuillez spécifier un responsable légal.");
+ ensure(strlen($this->responsible_phone) >= 10, "Veuillez rentrer le numéro de téléphone de votre responsable légal.");
+ ensure(filter_var($this->responsible_email, FILTER_VALIDATE_EMAIL), "Veuillez spécifier un responsable légal.");
+ }
+ }
+ else {
+ $this->class = SchoolClass::ADULT;
+ }
+
+ if (count(User::getAllUsers()) == 0)
+ $this->role = Role::ADMIN;
+
+ $this->confirm_email_token = genRandomPhrase(64);
+ }
+
+ public function register()
+ {
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("INSERT INTO `users`(`email`, `pwd_hash`, `confirm_email`, `surname`, `first_name`, `birth_date`, `gender`,
+ `address`, `postal_code`, `city`, `country`, `phone_number`, `school`, `class`, `role`, `description`, `responsible_name`, `responsible_phone`, `responsible_email`, `year`)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
+ $req->execute([$this->email, password_hash($this->password, PASSWORD_BCRYPT), $this->confirm_email_token, $this->surname, $this->first_name, $this->birth_date, $this->gender, $this->address,
+ $this->postal_code, $this->city, $this->country, $this->phone_number, $this->school, SchoolClass::getName($this->class), Role::getName($this->role), $this->description, $this->responsible_name, $this->responsible_phone, $this->responsible_email, $YEAR]);
+
+ Mailer::sendRegisterMail($this);
+ }
+}
+
+require_once "server_files/views/inscription.php";
diff --git a/server_files/controllers/instructions.php b/server_files/controllers/instructions.php
new file mode 100644
index 0000000..20b58b4
--- /dev/null
+++ b/server_files/controllers/instructions.php
@@ -0,0 +1,36 @@
+getEffectiveTournament();
+}
+
+$tex = preg_replace("#{TOURNAMENT_NAME}#", $tournament->getName(), $tex);
+$tex = preg_replace("#{PLACE}#", $tournament->getPlace(), $tex);
+$tex = preg_replace("#{PRICE}#", $tournament->getPrice(), $tex);
+$tex = preg_replace("#{END_PAYMENT_DATE}#", strftime("%d %B", strtotime($tournament->getInscriptionDate())), $tex);
+$tex = preg_replace("#{YEAR}#", $YEAR, $tex);
+
+shell_exec("mkdir tmp");
+file_put_contents("tmp/file.tex", $tex);
+shell_exec("pdflatex -synctex=1 -interaction=nonstopmode -shell-escape -output-directory=tmp tmp/file.tex");
+header("Content-type: application/pdf");
+readfile("tmp/file.pdf");
+shell_exec("rm -rf tmp");
+
+exit(0);
\ No newline at end of file
diff --git a/server_files/controllers/mon_compte.php b/server_files/controllers/mon_compte.php
new file mode 100644
index 0000000..71d26cf
--- /dev/null
+++ b/server_files/controllers/mon_compte.php
@@ -0,0 +1,220 @@
+getTournamentId());
+
+$has_error = false;
+$error_message = null;
+
+if (isset($_POST["update_account"])) {
+ $my_account = new MyAccount($_POST);
+ try {
+ $my_account->makeVerifications();
+ $my_account->updateAccount();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["update_password"])) {
+ $new_password = new NewPassword($_POST);
+ try {
+ $new_password->makeVerifications();
+ $new_password->updatePassword();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_POST["send_document"])) {
+ $send_document = new SendDocument();
+ try {
+ $send_document->makeVerifications();
+ $send_document->sendDocument();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class MyAccount
+{
+ public $email;
+ public $surname;
+ public $first_name;
+ public $birth_date;
+ public $gender;
+ public $address;
+ public $postal_code;
+ public $city;
+ public $country;
+ public $phone_number;
+ public $school;
+ public $class;
+ public $responsible_name;
+ public $responsible_phone;
+ public $responsible_email;
+ public $description;
+ private $user;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->user = $_SESSION["user"];
+
+ $keys = ["email", "surname", "first_name", "birth_date", "gender", "address", "postal_code", "city", "country", "phone_number",
+ "school", "class", "responsible_name", "responsible_phone", "responsible_email", "description"];
+
+ if ($this->user->getRole() == Role::PARTICIPANT)
+ $this->class = SchoolClass::fromName(strtoupper($this->class));
+ else
+ $this->class = SchoolClass::ADULT;
+
+ foreach ($keys as $key)
+ $this->$key = $this->$key != null && $this->$key != "" ? $this->$key : $this->user->$key;
+ }
+
+ public function makeVerifications()
+ {
+ global $YEAR;
+
+ ensure(filter_var($this->email, FILTER_VALIDATE_EMAIL), "L'adresse e-mail entrée est invalide.");
+ $this->email = strtolower($this->email);
+ ensure($this->email == $this->user->getEmail() || !userExists($this->email), "Un compte existe déjà avec cette adresse e-mail.");
+ ensure(dateWellFormed($this->birth_date), "La date de naissance est invalide.");
+ ensure($this->birth_date < $YEAR . "-01-01", "Vous devez être né.");
+ ensure($this->gender == "M" || $this->gender == "F", "Merci de spécifier un genre.");
+ ensure(preg_match("#^[0-9]{4}[0-9]?$#", $this->postal_code) && intval($this->postal_code) >= 01000 && intval($this->postal_code) <= 95999, "Le code postal est invalide.");
+ ensure(strlen($this->phone_number) >= 10, "Le numéro de téléphone est invalide.");
+
+ if ($this->user->getRole() == Role::PARTICIPANT) {
+ if ($this->birth_date > strval($YEAR - 18) . "04-01") {
+ ensure($this->responsible_name != "", "Veuillez spécifier un responsable légal.");
+ ensure(strlen($this->responsible_phone) >= 10, "Veuillez rentrer le numéro de téléphone de votre responsable légal.");
+ ensure(filter_var($this->responsible_email, FILTER_VALIDATE_EMAIL), "Veuillez spécifier un responsable légal.");
+ }
+ }
+ }
+
+ public function updateAccount()
+ {
+ $this->user->setSurname($this->surname);
+ $this->user->setFirstName($this->first_name);
+ $this->user->setBirthDate($this->birth_date);
+ $this->user->setGender($this->gender);
+ $this->user->setAddress($this->address);
+ $this->user->setPostalCode($this->postal_code);
+ $this->user->setCity($this->city);
+ $this->user->setCountry($this->country);
+ $this->user->setPhoneNumber($this->phone_number);
+ $this->user->setSchool($this->school);
+ $this->user->setClass($this->class);
+ $this->user->setResponsibleName($this->responsible_name);
+ $this->user->setResponsiblePhone($this->responsible_phone);
+ $this->user->setResponsibleEmail($this->responsible_email);
+ $this->user->setDescription($this->description);
+
+ if ($this->email != $this->user->getEmail()) {
+ $this->user->setEmail($this->email);
+ $this->user->setConfirmEmailToken(genRandomPhrase(64));
+
+ Mailer::sendChangeEmailAddressMail($this->user);
+ }
+ }
+}
+
+class NewPassword
+{
+ private $user;
+ private $old_password;
+ private $new_password;
+ private $confirm_password;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->user = $_SESSION["user"];
+ }
+
+ public function makeVerifications()
+ {
+ ensure($this->user->checkPassword($this->old_password), "L'ancien mot de passe est incorrect.");
+ ensure(strlen($this->new_password) >= 8, "Le mot de passe doit comporter au moins 8 caractères.");
+ ensure($this->new_password == $this->confirm_password, "Les deux mots de passe sont différents.");
+ }
+
+ public function updatePassword()
+ {
+ $this->user->setPassword($this->new_password);
+
+ Mailer::sendChangePasswordMail($this->user);
+ }
+}
+
+class SendDocument
+{
+ private $file;
+ private $type;
+
+ public function __construct()
+ {
+ $this->file = $_FILES["document"];
+ $this->type = strtoupper(htmlspecialchars($_POST["type"]));
+ }
+
+ public function makeVerifications()
+ {
+ global $LOCAL_PATH;
+
+ ensure($this->file["size"] <= 2e6, "Le fichier doit peser moins que 2 Mo.");
+ ensure(!$this->file["error"], "Une erreur est survenue.");
+ $mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $this->file["tmp_name"]);
+ ensure($mime == "application/pdf" || $mime = "image/png" || $mime == "image/jpeg", "Le fichier doit être au format PDF.");
+ ensure(is_dir("$LOCAL_PATH/files") || mkdir("$LOCAL_PATH/files"), "Un problème est survenue dans l'envoi du fichier. Veuillez contacter l'administrateur du serveur.");
+ }
+
+ public function sendDocument()
+ {
+ global $LOCAL_PATH, $DB, $FINAL;
+
+ do
+ $id = genRandomPhrase(64);
+ while (file_exists("$LOCAL_PATH/files/$id"));
+
+ if (!rename($this->file["tmp_name"], "$LOCAL_PATH/files/$id"))
+ throw new AssertionError("Une erreur est survenue lors de l'envoi du fichier.");
+
+ $req = $DB->prepare("INSERT INTO `documents`(`file_id`, `user`, `team`, `tournament`, `type`)
+ VALUES (?, ?, ?, ?, ?);");
+ $req->execute([$id, $this->type == DocumentType::getName(DocumentType::MOTIVATION_LETTER) ? -1 : $_SESSION["user_id"], $_SESSION["team"]->getId(),
+ $_SESSION["team"]->isSelectedForFinal() ? $FINAL->getId() : $_SESSION["team"]->getTournamentId(), $this->type]);
+ }
+}
+
+if ($team != null) {
+ $documents = $user->getAllDocuments($team->getTournamentId());
+ if ($team->isSelectedForFinal())
+ $documents_final = $user->getAllDocuments($FINAL->getId());
+}
+
+require_once "server_files/views/mon_compte.php";
diff --git a/server_files/controllers/mon_equipe.php b/server_files/controllers/mon_equipe.php
new file mode 100644
index 0000000..e165dcb
--- /dev/null
+++ b/server_files/controllers/mon_equipe.php
@@ -0,0 +1,193 @@
+makeVerifications();
+ $my_team->updateTeam();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+if (isset($_SESSION["user_id"]) && isset($_SESSION["team"]) && $_SESSION["team"] !== null) {
+ /**
+ * @var User $user
+ * @var Team $team
+ */
+ $user = $_SESSION["user"];
+ $team = $_SESSION["team"];
+
+ $tournament = Tournament::fromId($team->getTournamentId());
+ $documents = $tournament->getAllDocuments($team->getId());
+ if ($team->isSelectedForFinal())
+ $documents_final = $FINAL->getAllDocuments($team->getId());
+}
+else
+ require_once "server_files/403.php";
+
+if (isset($_POST["request_validation"])) {
+ if (!canValidate($team, $tournament))
+ $error_message = "Votre équipe ne peut pas demander la validation : il manque soit des participants, soit des documents.";
+ else {
+ $team->setValidationStatus(ValidationStatus::WAITING);
+ Mailer::sendRequestValidationMail($team, $team->isSelectedForFinal() ? $FINAL : $tournament);
+ }
+}
+
+$DUMPS = [
+ ["TKT", 6, "PGA", 3, "IRD", 1],
+ ["OUI", 8, "LEP", 1, "REX", 7],
+ ["ASP", 1, "ABC", 3, "TDP", 6],
+ ["GIF", 8, "ETM", 1, "LPC", 3],
+ ["MST", 6, "LQF", 1, "WAL", 2],
+];
+
+$DUMPS_2 = [
+ ["TKT", 4, "PGA", 1, "IRD", 6],
+ ["LEP", 6, "OUI", 5, "REX", 8],
+ ["ASP", 5, "ABC", 8, "TDP", 4],
+ ["ETM", 8, "LPC", 4, "GIF", 6],
+ ["MST", 5, "LQF", 4, "WAL", 8],
+];
+
+foreach ($DUMPS as $dump) {
+ $team1 = Team::fromTrigram($dump[0]);
+ $team2 = Team::fromTrigram($dump[2]);
+ $team3 = Team::fromTrigram($dump[4]);
+ $problem1 = $dump[1];
+ $problem2 = $dump[3];
+ $problem3 = $dump[5];
+
+ $req1 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req1->execute([$team1->getId(), $problem1]);
+ $data1 = $req1->fetch();
+ $sol1 = Solution::fromData($data1);
+ $req2 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req2->execute([$team2->getId(), $problem2]);
+ $data2 = $req2->fetch();
+ $sol2 = Solution::fromData($data2);
+ $req3 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req3->execute([$team3->getId(), $problem3]);
+ $data3 = $req3->fetch();
+ $sol3 = Solution::fromData($data3);
+
+ $req1 = $DB->prepare("UPDATE `teams` SET `opposed_problem` = ?, `rapported_problem` = ? WHERE `id` = ?;");
+ $req1->execute([$sol2->getFileId(), $sol3->getFileId(), $team1->getId()]);
+
+ $req2 = $DB->prepare("UPDATE `teams` SET `opposed_problem` = ?, `rapported_problem` = ? WHERE `id` = ?;");
+ $req2->execute([$sol3->getFileId(), $sol1->getFileId(), $team2->getId()]);
+
+ $req3 = $DB->prepare("UPDATE `teams` SET `opposed_problem` = ?, `rapported_problem` = ? WHERE `id` = ?;");
+ $req3->execute([$sol1->getFileId(), $sol2->getFileId(), $team3->getId()]);
+}
+
+foreach ($DUMPS_2 as $dump) {
+ $team1 = Team::fromTrigram($dump[0]);
+ $team2 = Team::fromTrigram($dump[2]);
+ $team3 = Team::fromTrigram($dump[4]);
+ $problem1 = $dump[1];
+ $problem2 = $dump[3];
+ $problem3 = $dump[5];
+
+ $req1 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req1->execute([$team1->getId(), $problem1]);
+ $data1 = $req1->fetch();
+ $sol1 = Solution::fromData($data1);
+ $req2 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req2->execute([$team2->getId(), $problem2]);
+ $data2 = $req2->fetch();
+ $sol2 = Solution::fromData($data2);
+ $req3 = $DB->prepare("SELECT * FROM `solutions` WHERE `team` = ? AND `problem` = ? ORDER BY uploaded_at DESC LIMIT 1");
+ $req3->execute([$team3->getId(), $problem3]);
+ $data3 = $req3->fetch();
+ $sol3 = Solution::fromData($data3);
+
+ $req1 = $DB->prepare("UPDATE `teams` SET `opposed_problem_2` = ?, `rapported_problem_2` = ? WHERE `id` = ?;");
+ $req1->execute([$sol2->getFileId(), $sol3->getFileId(), $team1->getId()]);
+
+ $req2 = $DB->prepare("UPDATE `teams` SET `opposed_problem_2` = ?, `rapported_problem_2` = ? WHERE `id` = ?;");
+ $req2->execute([$sol3->getFileId(), $sol1->getFileId(), $team2->getId()]);
+
+ $req3 = $DB->prepare("UPDATE `teams` SET `opposed_problem_2` = ?, `rapported_problem_2` = ? WHERE `id` = ?;");
+ $req3->execute([$sol1->getFileId(), $sol2->getFileId(), $team3->getId()]);
+}
+
+
+$req = $DB->prepare("SELECT opposed_problem, rapported_problem, opposed_problem_2, rapported_problem_2 FROM teams WHERE id = ?;");
+$req->execute([$team->getId()]);
+$data = $req->fetch();
+
+$opposed_solution = Solution::fromId($data["opposed_problem"]);
+$rapported_solution = Solution::fromId($data["rapported_problem"]);
+$opposed_solution_2 = Solution::fromId($data["opposed_problem_2"]);
+$rapported_solution_2 = Solution::fromId($data["rapported_problem_2"]);
+
+class MyTeam
+{
+ public $name;
+ public $trigram;
+ public $tournament_id;
+ private $team;
+ private $tournament;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->trigram = strtoupper($this->trigram);
+ $this->team = $_SESSION["team"];
+ $this->tournament = Tournament::fromId($this->tournament_id);
+ }
+
+ public function makeVerifications()
+ {
+ ensure($this->name != "" && $this->name != null, "Veuillez spécifier un nom d'équipe.");
+ ensure($this->name == $this->team->getName() || !teamExists($this->name), "Une équipe existe déjà avec ce nom.");
+ ensure(preg_match("#^[A-Z]{3}$#", $this->trigram), "Le trigramme n'est pas valide.");
+ ensure($this->trigram == $this->team->getTrigram() || !trigramExists($this->trigram), "Une équipe a déjà choisi ce trigramme.");
+ ensure($this->tournament != null, "Le tournoi indiqué n'existe pas.");
+ ensure(date("Y-m-d H:i:s") <= $this->tournament->getInscriptionDate(), "Les inscriptions sont terminées.");
+ ensure($this->team->getValidationStatus() == ValidationStatus::NOT_READY, "Votre équipe est déjà validée ou en cours de validation.");
+ }
+
+ public function updateTeam()
+ {
+ global $URL_BASE, $DB;
+
+ $this->team->setName($this->name);
+ $this->team->setTrigram($this->trigram);
+ $this->team->setTournamentId($this->tournament_id);
+
+ $DB->prepare("UPDATE `documents` SET `tournament` = ? WHERE `team` = ?;")->execute([$this->tournament_id, $this->team->getId()]);
+ $DB->prepare("UPDATE `solutions` SET `tournament` = ? WHERE `team` = ?;")->execute([$this->tournament_id, $this->team->getId()]);
+ $DB->prepare("UPDATE `syntheses` SET `tournament` = ? WHERE `team` = ?;")->execute([$this->tournament_id, $this->team->getId()]);
+ foreach ($this->team->getParticipants() as $user) {
+ if ($user != null)
+ $DB->prepare("UPDATE `payments` SET `tournament` = ? WHERE `user` = ?;")->execute([$this->tournament_id, $user]);
+ }
+ foreach ($this->team->getEncadrants() as $user) {
+ if ($user != null)
+ $DB->prepare("UPDATE `payments` SET `tournament` = ? WHERE `user` = ?;")->execute([$this->tournament_id, $user]);
+ }
+
+ $_SESSION["tournament"] = $this->tournament;
+
+ header("Location: $URL_BASE/mon-equipe");
+ }
+}
+
+require_once "server_files/views/mon_equipe.php";
diff --git a/server_files/controllers/organisateurs.php b/server_files/controllers/organisateurs.php
new file mode 100644
index 0000000..bfe340e
--- /dev/null
+++ b/server_files/controllers/organisateurs.php
@@ -0,0 +1,8 @@
+getEffectiveTournament();
+$payment = $user->getPayment();
+
+if ($team->getValidationStatus() != ValidationStatus::VALIDATED)
+ require_once "server_files/403.php";
+
+if (isset($_POST["pay"])) {
+ $pay = new Pay($_POST);
+ try {
+ $pay->makeVerifications();
+ $pay->submit();
+ }
+ catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class Pay {
+ private $method;
+ private $infos;
+ private $scholarship;
+
+ public function __construct($data)
+ {
+ foreach ($data as $key => $value)
+ $this->$key = htmlspecialchars($value);
+
+ $this->method = PaymentMethod::fromName(strtoupper($this->method));
+
+ $this->scholarship = $_FILES["scholarship"];
+ }
+
+ public function makeVerifications()
+ {
+ global $payment;
+
+ ensure($payment->getValidationStatus() == ValidationStatus::NOT_READY, "Un paiement est déjà initié.");
+ ensure($this->method != PaymentMethod::NOT_PAID, "Vous n'avez pas payé.");
+ ensure($this->method == PaymentMethod::SCHOLARSHIP || ($this->infos != null && strlen($this->infos) > 0), "Merci d'indiquer des informations pour retrouver votre paiement.");
+ ensure($this->method != PaymentMethod::SCHOLARSHIP || ($this->scholarship != null && !$this->scholarship["error"]), "Si vous êtes boursier, vous devez indiquer votre notifcation de bourse (une erreur est survenue).");
+ }
+
+ public function submit()
+ {
+ global $DB, $LOCAL_PATH, $payment, $user, $team, $tournament;
+
+ $payment->setMethod($this->method);
+ $payment->setAmount($this->method == PaymentMethod::SCHOLARSHIP ? 0 : $tournament->getPrice());
+ $payment->setValidationStatus(ValidationStatus::WAITING);
+ if ($this->method == PaymentMethod::SCHOLARSHIP) {
+ do
+ $id = genRandomPhrase(64);
+ while (file_exists("$LOCAL_PATH/files/$id"));
+
+ if (!rename($this->scholarship["tmp_name"], "$LOCAL_PATH/files/$id"))
+ throw new AssertionError("Une erreur est survenue lors de l'envoi du fichier.");
+
+ $req = $DB->prepare("INSERT INTO `documents`(`file_id`, `user`, `team`, `tournament`, `type`)
+ VALUES (?, ?, ?, ?, ?);");
+ $req->execute([$id, $_SESSION["user_id"], $_SESSION["team"]->getId(), $tournament->getId(), DocumentType::getName(DocumentType::SCHOLARSHIP)]);
+ $payment->setTransactionInfos($id);
+ }
+ else
+ $payment->setTransactionInfos($this->infos);
+
+ Mailer::requestPaymentValidation($user, $team, $tournament, $payment);
+ }
+}
+
+require_once "server_files/views/paiement.php";
\ No newline at end of file
diff --git a/server_files/controllers/profils.php b/server_files/controllers/profils.php
new file mode 100644
index 0000000..51dc8b9
--- /dev/null
+++ b/server_files/controllers/profils.php
@@ -0,0 +1,16 @@
+getEmail();
+}
+
+require_once "server_files/views/profils.php";
\ No newline at end of file
diff --git a/server_files/controllers/rejoindre_equipe.php b/server_files/controllers/rejoindre_equipe.php
new file mode 100644
index 0000000..c7db182
--- /dev/null
+++ b/server_files/controllers/rejoindre_equipe.php
@@ -0,0 +1,68 @@
+makeVerifications();
+ $join_team->joinTeam();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+class JoinTeam
+{
+ private $access_code;
+ private $team;
+ private $min_null_index;
+
+ public function __construct($data)
+ {
+ $this->access_code = strtolower(htmlspecialchars($data["access_code"]));
+ $this->team = Team::fromAccessCode($this->access_code);
+ }
+
+ public function makeVerifications()
+ {
+ ensure(preg_match("#[a-z0-9]{6}#", $this->access_code), "Le code d'accès doit comporter 6 caractères alphanumériques.");
+ ensure($this->team != null, "Ce code d'accès est invalide.");
+ ensure($this->team->getValidationStatus() == ValidationStatus::NOT_READY, "Cette équipe est déjà validée ou en cours de validation, vous ne pouvez pas la rejoindre.");
+
+ for ($i = 1; $i <= $_SESSION["role"] == Role::PARTICIPANT ? 6 : 3; ++$i) {
+ if (($_SESSION["role"] == Role::PARTICIPANT ? $this->team->getParticipants()[$i - 1] : $this->team->getEncadrants()[$i - 1]) == NULL)
+ break;
+ }
+
+ $this->min_null_index = $i;
+
+ ensure($_SESSION["role"] == Role::PARTICIPANT && $this->min_null_index <= 6 || $_SESSION["role"] == Role::ENCADRANT && $this->min_null_index <= 3, "Il n'y a plus de place pour vous dans l'équipe.");
+ }
+
+ public function joinTeam()
+ {
+ global $team;
+
+ $user = $_SESSION["user"];
+
+ $user->setTeamId($this->team->getId());
+
+ if ($_SESSION["role"] == Role::ENCADRANT)
+ $this->team->setEncadrant($this->min_null_index, $user->getId());
+ else
+ $this->team->setParticipant($this->min_null_index, $user->getId());
+
+ $team = $_SESSION["team"] = $this->team;
+ $tournament = $_SESSION["tournament"] = Tournament::fromId($this->team->getTournamentId());
+
+ Mailer::sendJoinTeamMail($user, $this->team, $tournament);
+ }
+}
+
+require_once "server_files/views/rejoindre_equipe.php";
diff --git a/server_files/controllers/solutions.php b/server_files/controllers/solutions.php
new file mode 100644
index 0000000..66262b7
--- /dev/null
+++ b/server_files/controllers/solutions.php
@@ -0,0 +1,72 @@
+getTournamentId());
+
+$has_error = false;
+$error_message = null;
+
+if (isset($_POST["send_solution"])) {
+ $save_solution = new SaveSolution();
+ try {
+ $save_solution->makeVerifications();
+ $save_solution->saveSolution();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+$solutions = $tournament->getAllSolutions($team->getId());
+$solutions_final = null;
+if ($team->isSelectedForFinal())
+ $solutions_final = $FINAL->getAllSolutions($team->getId());
+
+class SaveSolution
+{
+ private $problem;
+ private $file;
+
+ public function __construct()
+ {
+ $this->file = $_FILES["solution"];
+ $this->problem = htmlspecialchars($_POST["problem"]);
+ }
+
+ public function makeVerifications()
+ {
+ global $LOCAL_PATH;
+
+ ensure(preg_match("#[1-9]#", $this->problem), "Le numéro du problème est invalide.");
+ ensure($this->file["size"] <= 2e6, "Le fichier doit peser moins que 2 Mo.");
+ ensure(!$this->file["error"], "Une erreur est survenue.");
+ ensure(finfo_file(finfo_open(FILEINFO_MIME_TYPE), $this->file["tmp_name"]) == "application/pdf", "Le fichier doit être au format PDF.");
+ ensure(is_dir("$LOCAL_PATH/files") || mkdir("$LOCAL_PATH/files"), "Un problème est survenue dans l'envoi du fichier. Veuillez contacter l'administrateur du serveur.");
+ }
+
+ public function saveSolution()
+ {
+ global $LOCAL_PATH, $DB, $team, $tournament, $FINAL;
+
+ do
+ $id = genRandomPhrase(64);
+ while (file_exists("$LOCAL_PATH/files/$id"));
+
+ if (!rename($this->file["tmp_name"], "$LOCAL_PATH/files/$id"))
+ throw new AssertionError("Une erreur est survenue lors de l'envoi du fichier.");
+
+ $req = $DB->prepare("INSERT INTO `solutions`(`file_id`, `team`, `tournament`, `problem`) VALUES (?, ?, ?, ?);");
+ $req->execute([$id, $team->getId(), $team->isSelectedForFinal() ? $FINAL->getId() : $tournament->getId(), $this->problem]);
+
+ return false;
+ }
+}
+
+require_once "server_files/views/solutions.php";
diff --git a/server_files/controllers/solutions_orga.php b/server_files/controllers/solutions_orga.php
new file mode 100644
index 0000000..945c9f8
--- /dev/null
+++ b/server_files/controllers/solutions_orga.php
@@ -0,0 +1,24 @@
+getName() . ".zip\"");
+ header("Content-Length: " . strval(filesize($file_name)));
+
+ readfile($file_name);
+
+ exit();
+}
+
+$user = $_SESSION["user"];
+$tournaments = $_SESSION["role"] == Role::ADMIN ? Tournament::getAllTournaments() : $user->getOrganizedTournaments();
+
+require_once "server_files/views/solutions_orga.php";
diff --git a/server_files/controllers/syntheses.php b/server_files/controllers/syntheses.php
new file mode 100644
index 0000000..804618d
--- /dev/null
+++ b/server_files/controllers/syntheses.php
@@ -0,0 +1,73 @@
+getTournamentId());
+
+if (isset($_POST["send_synthesis"])) {
+ $save_synthesis = new SaveSynthesis();
+ try {
+ $save_synthesis->makeVerifications();
+ $save_synthesis->saveSynthesis();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+$syntheses = $tournament->getAllSyntheses($team->getId());
+$syntheses_final = null;
+if ($team->isSelectedForFinal())
+ $syntheses_final = $FINAL->getAllSyntheses($team->getId());
+
+class SaveSynthesis
+{
+ private $dest;
+ private $round;
+ private $file;
+
+ public function __construct()
+ {
+ $this->file = $_FILES["synthese"];
+ $this->round = htmlspecialchars($_POST["round"]);
+ $this->dest = DestType::fromName(strtoupper(htmlspecialchars($_POST["dest"])));
+ }
+
+ public function makeVerifications()
+ {
+ global $LOCAL_PATH, $tournament;
+
+ ensure($this->dest != DestType::DEFENSEUR, "La source est invalide.");
+ ensure($this->round == 1 || $this->round == 2, "Le tour est invalide.");
+ $now = date("Y-m-d H:i");
+ ensure($this->round == 1 && $now < $tournament->getSynthesesDate() || $this->round == 2 && $now < $tournament->getSynthesesDate2(), "Vous ne pouvez plus rendre de note de synthèse pour le tour $this->round.");
+ ensure($this->file["size"] <= 2e6, "Le fichier doit peser moins que 2 Mo.");
+ ensure(!$this->file["error"], "Une erreur est survenue.");
+ ensure(finfo_file(finfo_open(FILEINFO_MIME_TYPE), $this->file["tmp_name"]) == "application/pdf", "Le fichier doit être au format PDF.");
+ ensure(is_dir("$LOCAL_PATH/files") || mkdir("$LOCAL_PATH/files"), "Un problème est survenue dans l'envoi du fichier. Veuillez contacter l'administrateur du serveur.");
+ }
+
+ public function saveSynthesis()
+ {
+ global $LOCAL_PATH, $DB, $team, $tournament, $FINAL;
+ do
+ $id = genRandomPhrase(64);
+ while (file_exists("$LOCAL_PATH/files/$id"));
+
+ if (!rename($this->file["tmp_name"], "$LOCAL_PATH/files/$id"))
+ throw new AssertionError("Une erreur est survenue lors de l'envoi du fichier.");
+
+ $req = $DB->prepare("INSERT INTO `syntheses`(`file_id`, `team`, `tournament`, `round`, `dest`) VALUES (?, ?, ?, ?, ?);");
+ $req->execute([$id, $team->getId(), $team->isSelectedForFinal() ? $FINAL->getId() : $tournament->getId(), $this->round, $this->dest]);
+
+ return false;
+ }
+}
+
+require_once "server_files/views/syntheses.php";
diff --git a/server_files/controllers/syntheses_orga.php b/server_files/controllers/syntheses_orga.php
new file mode 100644
index 0000000..c4415e2
--- /dev/null
+++ b/server_files/controllers/syntheses_orga.php
@@ -0,0 +1,22 @@
+getName() . ".zip\"");
+ header("Content-Length: " . filesize($file_name));
+
+ readfile($file_name);
+
+ exit();
+}
+
+$user = $_SESSION["user"];
+$tournaments = $_SESSION["role"] == Role::ADMIN ? Tournament::getAllTournaments() : $user->getOrganizedTournaments();
+
+require_once "server_files/views/syntheses_orga.php";
\ No newline at end of file
diff --git a/server_files/controllers/tournoi.php b/server_files/controllers/tournoi.php
new file mode 100644
index 0000000..e34bb9a
--- /dev/null
+++ b/server_files/controllers/tournoi.php
@@ -0,0 +1,167 @@
+organize($_SESSION["user_id"]))
+ require_once "server_files/403.php";
+
+$has_error = false;
+$error_message = null;
+
+if (isset($_POST["edit_tournament"])) {
+ $update_tournament = new UpdateTournament($_POST);
+ try {
+ $update_tournament->makeVerifications();
+ $update_tournament->updateTournament();
+ } catch (AssertionError $e) {
+ $has_error = true;
+ $error_message = $e->getMessage();
+ }
+}
+
+$orgas = $tournament->getOrganizers();
+$teams = $tournament->getAllTeams();
+
+class UpdateTournament
+{
+ public $name;
+ public $organizers;
+ public $size;
+ public $place;
+ public $price;
+ public $date_start;
+ public $date_end;
+ public $date_inscription;
+ public $time_inscription;
+ public $date_solutions;
+ public $time_solutions;
+ public $date_syntheses;
+ public $time_syntheses;
+ public $date_solutions_2;
+ public $time_solutions_2;
+ public $date_syntheses_2;
+ public $time_syntheses_2;
+ public $description;
+ public $final;
+
+ public function __construct($data)
+ {
+ global $tournament;
+
+ foreach ($data as $key => $value)
+ $this->$key = ($key == "organizers" ? $value : htmlspecialchars($value));
+
+ if ($_SESSION["role"] != Role::ADMIN) {
+ $this->organizers = [];
+ /** @var User $organizer */
+ foreach ($tournament->getOrganizers() as $organizer)
+ $this->organizers[] = $organizer->getId();
+ }
+ }
+
+ public function makeVerifications()
+ {
+ global $tournament;
+
+ ensure($this->name != null && $this->name != "", "Le nom est invalide.");
+ ensure($this->name == $tournament->getName() || !tournamentExists($this->name), "Un tournoi existe déjà avec ce nom.");
+ ensure(sizeof($this->organizers) > 0, "Aucun organisateur n'a été choisi.");
+
+ $orgas = [];
+ foreach ($this->organizers as $orga_id) {
+ $orga = User::fromId($orga_id);
+ ensure($orga != null, "Un organisateur spécifié n'existe pas.");
+ ensure($orga->getRole() == Role::ORGANIZER || $orga->getRole() == Role::ADMIN, "Une personne indiquée ne peut pas organiser de tournoi.");
+ $orgas[] = $orga;
+ }
+ $this->organizers = $orgas;
+
+ ensure(preg_match("#[0-9]*#", $this->size), "Le nombre d'équipes indiqué n'est pas un nombre valide.");
+ $this->size = intval($this->size);
+ ensure($this->size >= 3 && $this->size <= 15, "Un tournoi doit avoir au moins 3 et au plus 15 équipes.");
+
+ ensure(preg_match("#[0-9]*#", $this->price), "Le tarif pour les participants n'est pas un entier valide.");
+ $this->price = intval($this->price);
+ ensure($this->price >= 0, "Le TFJM² ne va pas payer les élèves pour venir.");
+ ensure($this->price <= 50, "Soyons raisonnable sur le prix.");
+
+ ensure(dateWellFormed($this->date_start), "La date de début n'est pas valide.");
+ ensure(dateWellFormed($this->date_end), "La date de fin n'est pas valide.");
+ ensure(dateWellFormed($this->date_inscription . " " . $this->time_inscription), "La date de clôture des inscriptions n'est pas valide.");
+ ensure(dateWellFormed($this->date_solutions . " " . $this->time_solutions), "La date limite de remise des solutions n'est pas valide.");
+ ensure(dateWellFormed($this->date_syntheses . " " . $this->time_syntheses), "La date limite de remise des notes de synthèse pour le tour 1 n'est pas valide.");
+ ensure(dateWellFormed($this->date_solutions_2 . " " . $this->time_solutions_2), "La date limite de visibilité des solutions du tour 2 n'est pas valide.");
+ ensure(dateWellFormed($this->date_syntheses_2 . " " . $this->time_syntheses_2), "La date limite de remise des notes de synthèse pour le tour 2 n'est pas valide.");
+ }
+
+ public function updateTournament()
+ {
+ global $URL_BASE, $tournament;
+
+ $tournament->setName($this->name);
+ $tournament->setSize($this->size);
+ $tournament->setPlace($this->place);
+ $tournament->setPrice($this->price);
+ $tournament->setStartDate($this->date_start);
+ $tournament->setEndDate($this->date_end);
+ $tournament->setInscriptionDate("$this->date_inscription $this->time_inscription");
+ $tournament->setSolutionsDate("$this->date_solutions $this->time_solutions");
+ $tournament->setSynthesesDate("$this->date_syntheses $this->time_syntheses");
+ $tournament->setSolutionsDate2("$this->date_solutions_2 $this->time_solutions_2");
+ $tournament->setSynthesesDate2("$this->date_syntheses_2 $this->time_syntheses_2");
+ $tournament->setDescription($this->description);
+
+ foreach ($this->organizers as $organizer) {
+ if (!$tournament->organize($organizer->getId()))
+ Mailer::sendAddOrganizerForTournamentMail($organizer, $tournament);
+ }
+
+ $tournament->clearOrganizers();
+ /** @var User $organizer */
+ foreach ($this->organizers as $organizer)
+ $tournament->addOrganizer($organizer);
+
+ header("Location: $URL_BASE/tournoi/" . $this->name);
+ exit();
+ }
+}
+
+if ($_SESSION["role"] == Role::ORGANIZER || $_SESSION["role"] == Role::ADMIN) {
+ $emails = [];
+ $emails_validated = [];
+ foreach ($tournament->getOrganizers() as $organizer) {
+ $emails[] = $organizer->getEmail();
+ $emails_validated[] = $organizer->getEmail();
+ }
+
+ foreach ($teams as $team) {
+ foreach ($team->getEncadrants() as $encadrant_id) {
+ $encadrant = User::fromId($encadrant_id);
+ if ($encadrant != null) {
+ $emails[] = $encadrant->getEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $encadrant->getEmail();
+ }
+ }
+
+ foreach ($team->getParticipants() as $participant_id) {
+ $participant = User::fromId($participant_id);
+ if ($participant != null) {
+ $emails[] = $participant->getEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $participant->getEmail();
+ if ($participant->getResponsibleEmail() != null) {
+ $emails[] = $participant->getResponsibleEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $participant->getResponsibleEmail();
+ }
+ }
+ }
+ }
+}
+
+require_once "server_files/views/tournoi.php";
diff --git a/server_files/controllers/tournois.php b/server_files/controllers/tournois.php
new file mode 100644
index 0000000..7a304fc
--- /dev/null
+++ b/server_files/controllers/tournois.php
@@ -0,0 +1,42 @@
+getOrganizers() as $organizer) {
+ $emails[] = $organizer->getEmail();
+ $emails_validated[] = $organizer->getEmail();
+ }
+
+ foreach ($tournament->getAllTeams() as $team) {
+ foreach ($team->getEncadrants() as $encadrant_id) {
+ $encadrant = User::fromId($encadrant_id);
+ if ($encadrant != null) {
+ $emails[] = $encadrant->getEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $encadrant->getEmail();
+ }
+ }
+
+ foreach ($team->getParticipants() as $participant_id) {
+ $participant = User::fromId($participant_id);
+ if ($participant != null) {
+ $emails[] = $participant->getEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $participant->getEmail();
+ if ($participant->getResponsibleEmail() != null) {
+ $emails[] = $participant->getResponsibleEmail();
+ if ($team->getValidationStatus() == ValidationStatus::VALIDATED)
+ $emails_validated[] = $participant->getResponsibleEmail();
+ }
+ }
+ }
+ }
+ }
+}
+
+require_once "server_files/views/tournois.php";
diff --git a/server_files/controllers/view_file.php b/server_files/controllers/view_file.php
new file mode 100644
index 0000000..cdc4904
--- /dev/null
+++ b/server_files/controllers/view_file.php
@@ -0,0 +1,102 @@
+getTeamId());
+ $tournament = Tournament::fromId($file->getTournamentId());
+ $trigram = $team->getTrigram();
+
+ if ($_SESSION["role"] == Role::ORGANIZER && !$tournament->organize($_SESSION["user_id"]))
+ require_once "server_files/403.php";
+
+ if ($type == DocumentType::SOLUTION) {
+ $problem = $file->getProblem();
+ $name = "Problème $problem $trigram";
+
+ if (($_SESSION["role"] == Role::PARTICIPANT || $_SESSION["role"] == Role::ENCADRANT) && (!isset($_SESSION["team"]) || $_SESSION["team"]->getId() != $team->getId())) {
+ $req = $DB->prepare("SELECT opposed_problem, rapported_problem, opposed_problem_2, rapported_problem_2 FROM teams WHERE id = ?;");
+ $req->execute([$_SESSION["team"]->getId()]);
+ $data = $req->fetch();
+ if ($id != $data["opposed_problem"] && $id != $data["rapported_problem"]) {
+ if (date("Y-m-d H:i") < $tournament->getSolutionsDate2() || ($id != $data["opposed_problem_2"] && $id != $data["rapported_problem_2"]))
+ require_once "server_files/403.php";
+ }
+ }
+ }
+ else if ($type == DocumentType::SYNTHESIS) {
+ $dest = $file->getDest();
+ $name = "Note de synthèse $trigram " . ($dest == DestType::OPPOSANT ? "de l'opposant" : "du rapporteur");
+
+ if (($_SESSION["role"] == Role::PARTICIPANT || $_SESSION["role"] == Role::ENCADRANT) && (!isset($_SESSION["team"]) || $_SESSION["team"]->getId() != $team->getId()))
+ require_once "server_files/403.php";
+ }
+ else {
+ $user = User::fromId($file->getUserId());
+ $type = $file->getType();
+
+ if (($_SESSION["role"] == Role::PARTICIPANT || $_SESSION["role"] == Role::ENCADRANT)) {
+ if ($type != DocumentType::MOTIVATION_LETTER && $user->getId() != $_SESSION["user_id"] || $file->getTeamId() != $team->getId())
+ require_once "server_files/403.php";
+ }
+
+ switch ($type) {
+ case DocumentType::PARENTAL_CONSENT:
+ $name = "Autorisation parentale";
+ break;
+ case DocumentType::PHOTO_CONSENT:
+ $name = "Autorisation de droit à l'image";
+ break;
+ case DocumentType::SANITARY_PLUG:
+ $name = "Fiche sanitaire";
+ break;
+ case DocumentType::SCHOLARSHIP:
+ $name = "Notification de bourse";
+ break;
+ }
+ if ($type == DocumentType::MOTIVATION_LETTER)
+ $name = "Lettre de motivation de l'équipe $trigram";
+ else {
+ $surname = $user->getSurname();
+ $first_name = $user->getFirstName();
+ $name .= " de $first_name $surname";
+ }
+ }
+}
+else
+ require_once "server_files/404.php";
+
+$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), "$LOCAL_PATH/files/$id");
+if ($mime == "application/pdf")
+ $name .= ".pdf";
+elseif ($mime == "image/png")
+ $name .= ".png";
+else
+ $name = ".jpg";
+
+header("Content-Type: $mime");
+header("Content-Disposition: inline; filename=\"$name\"");
+
+readfile("$LOCAL_PATH/files/$id");
+
+exit();
\ No newline at end of file
diff --git a/server_files/model.php b/server_files/model.php
new file mode 100644
index 0000000..e650c24
--- /dev/null
+++ b/server_files/model.php
@@ -0,0 +1,267 @@
+getRole();
+
+ if ($user->getTeamId() !== null) {
+ $team = $_SESSION["team"] = Team::fromId($user->getTeamId());
+ $_SESSION["tournament"] = Tournament::fromId($team->getTournamentId());
+ }
+
+ if (isset($_GET["view-as-admin"])) {
+ if (isset($_SESSION["admin"])) {
+ $_SESSION["user_id"] = $_SESSION["admin"];
+ unset($_SESSION["admin"]);
+ }
+ header("Location: /");
+ exit();
+ }
+ }
+}
+
+function quitTeam($user_id = -1)
+{
+ global $DB, $URL_BASE;
+
+ header("Location: $URL_BASE");
+
+ /** @var User $user */
+ $user = $_SESSION["user"];
+ if ($user_id == -1)
+ $user_id = $user->getId();
+ else
+ $user = User::fromId($user_id);
+ $role = $user->getRole();
+
+ if ($role == Role::ADMIN || $role == Role::ORGANIZER)
+ return;
+
+ for ($i = 1; $i <= ($role == Role::ENCADRANT ? 3 : 6); ++$i)
+ /** @noinspection SqlResolve */
+ $DB->exec("UPDATE `teams` SET `" . strtolower(Role::getName($role)) . "_$i` = NULL WHERE `" . strtolower(Role::getName($role)) . "_$i` = $user_id;");
+ $user->setTeamId(null);
+ $DB->exec("UPDATE `teams` SET `encadrant_1` = `encadrant_2`, `encadrant_2` = NULL WHERE `encadrant_1` IS NULL;");
+ $DB->exec("UPDATE `teams` SET `encadrant_2` = `encadrant_3`, `encadrant_3` = NULL WHERE `encadrant_2` IS NULL;");
+ for ($i = 1; $i <= 5; ++$i) {
+ /** @noinspection SqlResolve */
+ $DB->exec("UPDATE `teams` SET `participant_$i` = `participant_" . strval($i + 1) . "`, `participant_" . strval($i + 1) . "` = NULL WHERE `participant_$i` IS NULL;");
+ }
+
+ $req = $DB->query("SELECT `file_id` FROM `documents` WHERE `user` = $user_id;");
+ while (($data = $req->fetch()) !== false)
+ unlink("$URL_BASE/files/" . $data["file_id"]);
+ $DB->exec("DELETE FROM `documents` WHERE `user` = $user_id;");
+
+ if ($DB->exec("DELETE FROM `teams` WHERE `encadrant_1` IS NULL AND `participant_1` IS NULL;") > 0) {
+ $team_id = $user->getTeamId();
+ $req = $DB->query("SELECT `file_id` FROM `solutions` WHERE `team` = $team_id;");
+ while (($data = $req->fetch()) !== false)
+ unlink("$URL_BASE/files/" . $data["file_id"]);
+ $DB->exec("DELETE FROM `solutions` WHERE `team` = $team_id;");
+
+ $req = $DB->query("SELECT `file_id` FROM `syntheses` WHERE `team` = $team_id;");
+ while (($data = $req->fetch()) !== false)
+ unlink("$URL_BASE/files/" . $data["file_id"]);
+ $DB->exec("DELETE FROM `syntheses` WHERE `team` = $team_id;");
+ }
+
+ $_SESSION["team"] = null;
+ unset($_SESSION["team"]);
+}
+
+function userExists($email)
+{
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("SELECT `id` FROM `users` WHERE `email` = ? AND `year` = '$YEAR';");
+ $req->execute([$email]);
+ return $req->fetch();
+}
+
+function teamExists($name)
+{
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("SELECT `id` FROM `teams` WHERE `name` = ? AND `year` = '$YEAR';");
+ $req->execute([$name]);
+ return $req->fetch();
+}
+
+function trigramExists($trigram)
+{
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("SELECT `id` FROM `teams` WHERE `trigram` = ? AND `year` = '$YEAR';");
+ $req->execute([$trigram]);
+ return $req->fetch();
+}
+
+function tournamentExists($name)
+{
+ global $DB, $YEAR;
+
+ $req = $DB->prepare("SELECT `id` FROM `tournaments` WHERE `name` = ? AND `year` = '$YEAR';");
+ $req->execute([$name]);
+ return $req->fetch();
+}
+
+function canValidate(Team $team, Tournament $tournament)
+{
+ global $DB, $YEAR;
+
+ $can_validate = $team->getValidationStatus() == ValidationStatus::NOT_READY;
+ $can_validate &= $team->getEncadrants()[0] != NULL;
+ $can_validate &= $team->getParticipants()[3] != NULL;
+
+ // Le TFJM² 2020 se déroulant en ligne, les papiers ne sont plus nécessaires
+/* for ($i = 1; $i <= 2; ++$i) {
+ if ($team->getEncadrants()[$i - 1] === NULL)
+ continue;
+
+ $req = $DB->prepare("SELECT COUNT(*) AS `version` FROM `documents` WHERE `user` = ? AND `tournament` = ? AND `type` = ?;");
+ $req->execute([$team->getEncadrants()[$i - 1], $tournament->getId(), "PHOTO_CONSENT"]);
+ $d = $req->fetch();
+ $can_validate &= $d["version"] > 0;
+ }*/
+
+
+ // Le TFJM² 2020 se déroulant en ligne, les papiers ne sont plus nécessaires
+/* for ($i = 1; $i <= 6; ++$i) {
+ if ($team->getParticipants()[$i] === NULL)
+ continue;
+
+ $req = $DB->prepare("SELECT COUNT(*) AS `version` FROM `documents` WHERE `user` = ? AND `tournament` = ? AND `type` = ?;");
+ $req->execute([$team->getParticipants()[$i], $tournament->getId(), "PHOTO_CONSENT"]);
+ $d = $req->fetch();
+ $can_validate &= $d["version"] > 0;
+
+ $birth_date = $DB->query("SELECT `birth_date` FROM `users` WHERE `id` = " . $team->getParticipants()[$i] . ";")->fetch()["birth_date"];
+ if ($birth_date > strval($YEAR - 18) . substr($tournament->getStartDate(), 4)) {
+ $req = $DB->prepare("SELECT COUNT(*) AS `version` FROM `documents` WHERE `user` = ? AND `tournament` = ? AND `type` = ?;");
+ $req->execute([$team->getParticipants()[$i], $tournament->getId(), "PARENTAL_CONSENT"]);
+ $d = $req->fetch();
+ $can_validate &= $d["version"] > 0;
+
+ $req = $DB->prepare("SELECT COUNT(*) AS `version` FROM `documents` WHERE `user` = ? AND `tournament` = ? AND `type` = ?;");
+ $req->execute([$team->getParticipants()[$i], $tournament->getId(), "SANITARY_PLUG"]);
+ $d = $req->fetch();
+ $can_validate &= $d["version"] > 0;
+ }
+ } */
+
+ // La lettre de motivation n'est plus nécessaire, mais existe toujours
+/* $req = $DB->prepare("SELECT COUNT(*) AS `version` FROM `documents` WHERE `team` = ? AND `tournament` = ? AND `type` = ?;");
+ $req->execute([$team->getId(), $tournament->getId(), "MOTIVATION_LETTER"]);
+ $d = $req->fetch();
+ $can_validate &= $d["version"] > 0;*/
+
+ $can_validate &= date("Y-m-d H:i:s") <= $tournament->getInscriptionDate();
+
+ return $can_validate;
+}
+
+function printDocuments($documents)
+{
+ if (sizeof($documents) == 0) {
+ echo "
" . $message . " ", $content);
+ }
+
+ self::sendMail($user->getEmail(), "Paiement pour le tournoi " . $tournament->getName() . " – TFJM² $YEAR", $content);
+ }
+}
diff --git a/server_files/services/mail_templates/add_organizer.html b/server_files/services/mail_templates/add_organizer.html
new file mode 100644
index 0000000..56ed2c5
--- /dev/null
+++ b/server_files/services/mail_templates/add_organizer.html
@@ -0,0 +1,21 @@
+
+
+
+
+ Organisateur du TFJM²
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous recevez ce message (envoyé automatiquement) car vous êtes organisateur d'un des tournois du TFJM2.
+Un compte organisateur vous a été créé par l'un des administrateurs. Un mot de passe aléatoire vous a été attribué, mais que vous
+devez changer pour des raisons de sécurité sur le lien suivant :
+{URL_BASE}/connexion/reinitialiser_mdp/{TOKEN}
+
+Une fois le mot de passe changé, vous pourrez vous connecter sur la plateforme.
+
+Merci beaucoup pour votre aide !
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/add_organizer_for_tournament.html b/server_files/services/mail_templates/add_organizer_for_tournament.html
new file mode 100644
index 0000000..ee83644
--- /dev/null
+++ b/server_files/services/mail_templates/add_organizer_for_tournament.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Organisateur du tournoi de {TOURNAMENT_NAME} – TFJM²
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous venez d'être promu organisateur du tournoi {TOURNAMENT_NAME} du TFJM2 {YEAR}.
+Ce message vous a été envoyé automatiquement. En cas de problème, merci de répondre à ce message.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/add_team.html b/server_files/services/mail_templates/add_team.html
new file mode 100644
index 0000000..ffd2b50
--- /dev/null
+++ b/server_files/services/mail_templates/add_team.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Nouvelle équipe TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous venez de créer l'équipe « {TEAM_NAME} » ({TRIGRAM}) pour le TFJM2 de {TOURNAMENT_NAME} et nous vous en remercions.
+Afin de permettre aux autres membres de votre équipe de vous rejoindre, veuillez leur transmettre le code d'accès :
+{ACCESS_CODE}
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/change_email_address.html b/server_files/services/mail_templates/change_email_address.html
new file mode 100644
index 0000000..0adf015
--- /dev/null
+++ b/server_files/services/mail_templates/change_email_address.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Changement d'adresse e-mail – TFJM²
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous venez de changer votre adresse e-mail. Veuillez désormais la confirmer en cliquant ici : {URL_BASE}/confirmer_mail/{TOKEN}
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/change_password.html b/server_files/services/mail_templates/change_password.html
new file mode 100644
index 0000000..91d2cf1
--- /dev/null
+++ b/server_files/services/mail_templates/change_password.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Mot de passe changé – TFJM²
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Nous vous informons que votre mot de passe vient d'être modifié. Si vous n'êtes pas à l'origine de cette manipulation,
+veuillez immédiatement vérifier vos accès à votre boîte mail et changer votre mot de passe sur la plateforme
+d'inscription.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/confirm_email.html b/server_files/services/mail_templates/confirm_email.html
new file mode 100644
index 0000000..ba75174
--- /dev/null
+++ b/server_files/services/mail_templates/confirm_email.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Inscription au TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous êtes inscrit au TFJM2 {YEAR} et nous vous en remercions.
+Pour valider votre adresse e-mail, veuillez cliquer sur le lien : {URL_BASE}/confirmer_mail/{TOKEN}
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/forgotten_password.html b/server_files/services/mail_templates/forgotten_password.html
new file mode 100644
index 0000000..18c187d
--- /dev/null
+++ b/server_files/services/mail_templates/forgotten_password.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+ Mot de passe oublié – TFJM²
+
+
+Bonjour,
+
+Vous avez indiqué avoir oublié votre mot de passe. Veuillez cliquer ici pour le réinitialiser : {URL_BASE}/connexion/reinitialiser_mdp/{TOKEN}
+
+Si vous n'êtes pas à l'origine de cette manipulation, vous pouvez ignorer ce message.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/join_team.html b/server_files/services/mail_templates/join_team.html
new file mode 100644
index 0000000..3e51307
--- /dev/null
+++ b/server_files/services/mail_templates/join_team.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Équipe rejointe – TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous venez de rejoindre l'équipe « {TEAM_NAME} » ({TRIGRAM}) pour le TFJM² de {TOURNAMENT_NAME} et nous vous en
+remercions.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/register.html b/server_files/services/mail_templates/register.html
new file mode 100644
index 0000000..0334c01
--- /dev/null
+++ b/server_files/services/mail_templates/register.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Inscription au TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Vous venez de vous inscrire au TFJM2 {YEAR} et nous vous en remercions.
+Pour valider votre adresse e-mail, veuillez cliquer sur le lien : {URL_BASE}/confirmer_mail/{TOKEN}
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/request_payment_validation.html b/server_files/services/mail_templates/request_payment_validation.html
new file mode 100644
index 0000000..8c23f21
--- /dev/null
+++ b/server_files/services/mail_templates/request_payment_validation.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Demande de validation de paiement pour le TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+{USER_FIRST_NAME} {USER_SURNAME} de l'équipe {TEAM_NAME} ({TRIGRAM}) annonce avoir réglé sa participation pour le tournoi {TOURNAMENT_NAME}.
+Les informations suivantes ont été communiquées :
+Équipe : {TEAM_NAME} ({TRIGRAM})
+Tournoi : {TOURNAMENT_NAME}
+Moyen de paiement : {PAYMENT_METHOD}
+Montant : {AMOUNT} €
+Informations sur le paiement : {PAYMENT_INFOS}
+
+Vous pouvez désormais vérifier ces informations, puis valider (ou non) le paiement sur
+la page associée à ce participant.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/request_validation.html b/server_files/services/mail_templates/request_validation.html
new file mode 100644
index 0000000..203d616
--- /dev/null
+++ b/server_files/services/mail_templates/request_validation.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Demande de validation - TFJM²
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+L'équipe « {TEAM_NAME} » ({TRIGRAM}) vient de demander à valider son équipe pour participer au tournoi {TOURNAMENT} du
+TFJM². Vous pouvez décider d'accepter ou de refuser l'équipe en vous rendant sur la page de l'équipe :
+{URL_BASE}/equipe/{TRIGRAM}
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/unvalidate_payment.html b/server_files/services/mail_templates/unvalidate_payment.html
new file mode 100644
index 0000000..c6e99ff
--- /dev/null
+++ b/server_files/services/mail_templates/unvalidate_payment.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Non-validation du paiement pour le TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Votre paiement pour le TFJM² {YEAR} a malheureusement été rejeté. Pour rappel, vous aviez fourni ces informations :
+Équipe : {TEAM_NAME} ({TRIGRAM})
+Tournoi : {TOURNAMENT_NAME}
+Moyen de paiement : {PAYMENT_METHOD}
+Montant : {AMOUNT} €
+Informations sur le paiement : {PAYMENT_INFOS}
+
+{MESSAGE}
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/unvalidate_team.html b/server_files/services/mail_templates/unvalidate_team.html
new file mode 100644
index 0000000..d9c5588
--- /dev/null
+++ b/server_files/services/mail_templates/unvalidate_team.html
@@ -0,0 +1,19 @@
+
+
+
+
+ Équipe non validée – TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Maleureusement, votre équipe « {TEAM_NAME} » ({TRIGRAM}) n'a pas été validée. Veuillez vérifier que vos autorisations sont correctes.
+{MESSAGE}
+
+N'hésitez pas à nous contacter à l'adresse contact@tfjm.org pour plus d'informations.
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/validate_payment.html b/server_files/services/mail_templates/validate_payment.html
new file mode 100644
index 0000000..afe94ba
--- /dev/null
+++ b/server_files/services/mail_templates/validate_payment.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Validation du paiement pour le TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Votre paiement pour le TFJM² {YEAR} a bien été validé. Pour rappel, vous aviez fourni ces informations :
+Équipe : {TEAM_NAME} ({TRIGRAM})
+Tournoi : {TOURNAMENT_NAME}
+Moyen de paiement : {PAYMENT_METHOD}
+Montant : {AMOUNT} €
+Informations sur le paiement : {PAYMENT_INFOS}
+
+{MESSAGE}
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/services/mail_templates/validate_team.html b/server_files/services/mail_templates/validate_team.html
new file mode 100644
index 0000000..0967488
--- /dev/null
+++ b/server_files/services/mail_templates/validate_team.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Équipe validée – TFJM² {YEAR}
+
+
+Bonjour {FIRST_NAME} {SURNAME},
+
+Félicitations ! Votre équipe « {TEAM_NAME} » ({TRIGRAM}) est désormais validée ! Vous êtes désormais apte à travailler sur
+vos problèmes et publier vos solutions sur la plateforme.
+{MESSAGE}
+
+Cordialement,
+
+Le comité national d'organisation du TFJM2
+
+
\ No newline at end of file
diff --git a/server_files/utils.php b/server_files/utils.php
new file mode 100644
index 0000000..2bc4ce5
--- /dev/null
+++ b/server_files/utils.php
@@ -0,0 +1,32 @@
+
+
+
+
Ajouter une équipe
+
+
+
+
+ Votre équipe a bien été créée ! Voici le code d'accès à transmettre aux autres membres de votre équipe :
+ = $new_team->access_code ?>
+
+ Tournoi de = $tournament->name ?> ajouté avec succès !
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server_files/views/connexion.php b/server_files/views/connexion.php
new file mode 100644
index 0000000..f59f4ac
--- /dev/null
+++ b/server_files/views/connexion.php
@@ -0,0 +1,65 @@
+Le mail de récupération de mot de passe a bien été envoyé.";
+ elseif (isset($reset_password) && isset($_POST["password"]))
+ echo "
Le mot de passe a bien été changé. Vous pouvez désormais vous connecter.