Copie propre du code final de Folliow (projet d'équipe DWWM)
This commit is contained in:
commit
eed64cfeb6
67 changed files with 17897 additions and 0 deletions
21
.env.example
Normal file
21
.env.example
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# config BDD
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOSTNAME=localhost
|
||||
DB_DATABASE=projet_folliow
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
|
||||
# image path
|
||||
IMG_PROJECT_PATH = uploads/projects/
|
||||
IMG_USER_PATH = uploads/profiles/
|
||||
BASE_URL=http://localhost:8000/
|
||||
|
||||
# envoi d'e-mails (SMTP)
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=587
|
||||
SMTP_SECURE=tls
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
MAIL_FROM=
|
||||
MAIL_FROM_NAME=Folliow
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/templates_c/
|
||||
/vendor/
|
||||
.env
|
||||
uploads/*/*
|
||||
!uploads/*/.gitkeep
|
||||
38
README.md
Normal file
38
README.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Folliow
|
||||
|
||||
> **Ce dépôt est une copie propre du projet, pas le dépôt de travail.**
|
||||
>
|
||||
> Folliow a été développé en équipe de quatre pendant la formation DWWM (CCI Campus Colmar, 2026), dans un dépôt partagé qui reste privé. Son historique contient des éléments qui n'ont pas leur place en public : une clé d'envoi d'e-mails écrite en dur à un moment du projet (corrigée depuis, la configuration passe par le `.env`), les adresses e-mail personnelles des membres de l'équipe et la configuration de déploiement du serveur.
|
||||
>
|
||||
> Plutôt que de réécrire l'historique d'un dépôt commun, cette copie publie **le code final tel qu'il a été rendu**, sans historique, sans dépendances installées, sans images envoyées par les utilisateurs de la démo et sans configuration de déploiement. Le code lui-même n'a pas été retouché : les `@author` en tête de chaque fichier indiquent qui a écrit quoi.
|
||||
|
||||
Plateforme de portfolios à mi-chemin entre Behance et LinkedIn : chaque profil met en avant de vrais projets, avec leurs technologies, et on peut suivre les autres membres.
|
||||
|
||||
Démo en ligne : https://php.boulayoune.com · Présentation détaillée : https://yasser.boulayoune.com/realisations/projet-php/
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
- Inscription, connexion et gestion du profil
|
||||
- Création et édition de projets (description, technologies, images)
|
||||
- Fil de projets, recherche de profils et de projets
|
||||
- Abonnements entre utilisateurs
|
||||
- Partage d'un projet par e-mail
|
||||
- Espace d'administration (catégories, modération)
|
||||
|
||||
## Stack
|
||||
|
||||
- PHP 8 en MVC sans framework (contrôleurs, modèles, entités)
|
||||
- Smarty pour les gabarits
|
||||
- MySQL / MariaDB
|
||||
- PHPMailer pour l'envoi d'e-mails, vlucas/phpdotenv pour la configuration
|
||||
- HTML, CSS, JavaScript, Bootstrap
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
composer install
|
||||
cp .env.example .env # renseigner l'accès à la base (et le SMTP si besoin)
|
||||
mysql -u <utilisateur> -p < database/database.sql
|
||||
```
|
||||
|
||||
Puis servir le dossier avec Apache (ou `php -S localhost:8000`). La base fournie contient trois comptes de démonstration fictifs.
|
||||
0
assests/.gitkeep
Normal file
0
assests/.gitkeep
Normal file
178
assests/css/style.css
Normal file
178
assests/css/style.css
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 0.8rem 2rem;
|
||||
min-height: 64px;
|
||||
max-height: 64px;
|
||||
}
|
||||
.navbar .navbar-collapse {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #0d6efd !important;
|
||||
}
|
||||
|
||||
.navbar-brand span {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0 0.2rem;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
.nav-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
}
|
||||
.navbar-nav .nav-link:hover {
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.btn-signup {
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 25px;
|
||||
padding: 0.5rem 2rem;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-signup:hover {
|
||||
background-color: #0b5ed7;
|
||||
}
|
||||
|
||||
.notification-bell {
|
||||
font-size: 1.2rem;
|
||||
color: #6c757d;
|
||||
margin-right: 1rem;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.notification-bell:hover {
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
height: 30px;
|
||||
width: auto;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.logo{
|
||||
font-size: 5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.article-card {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.article-img {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.article-content {
|
||||
height: 20%;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.footer{
|
||||
color: white;
|
||||
background-color: #6A6ED4;
|
||||
}
|
||||
|
||||
.footer ul{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer a{
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.txt_title {
|
||||
color: rgb(51, 152, 217);
|
||||
}
|
||||
|
||||
/* On cible la card sans casser ses propriétés Bootstrap */
|
||||
.tiger-theme {
|
||||
background-color: #ff8c00 !important; /* Orange tigre */
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 30px,
|
||||
rgba(0, 0, 0, 0.15) 30px,
|
||||
rgba(0, 0, 0, 0.15) 60px
|
||||
) !important;
|
||||
border: 2px solid #000 !important;
|
||||
color: #000 !important;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Style du texte pour qu'il ressorte sur l'orange */
|
||||
.tiger-text {
|
||||
font-weight: 800 !important;
|
||||
text-transform: uppercase;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.tiger-date {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Bordure de l'image (n'affecte pas la taille de la card) */
|
||||
.tiger-border {
|
||||
box-shadow: 0 0 0 4px #000;
|
||||
}
|
||||
|
||||
/* Bouton style tigre */
|
||||
.btn-tiger {
|
||||
background-color: #000 !important;
|
||||
color: #ff8c00 !important;
|
||||
border: none !important;
|
||||
font-weight: bold !important;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.btn-tiger:hover {
|
||||
transform: scale(1.05);
|
||||
background-color: #222 !important;
|
||||
}
|
||||
11243
assests/css/styles.css
Normal file
11243
assests/css/styles.css
Normal file
File diff suppressed because it is too large
Load diff
BIN
assests/img/Group-49.ico
Normal file
BIN
assests/img/Group-49.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
89
assests/img/Logo-Wordmark.svg
Normal file
89
assests/img/Logo-Wordmark.svg
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
<svg width="2130" height="558" viewBox="0 0 2130 558" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M510.224 203.704C544.762 203.704 574.076 210.742 598.169 224.819C622.259 238.897 640.595 258.304 653.173 283.045C665.754 307.784 672.041 336.152 672.041 368.145V390.217C672.041 424.557 665.754 454.309 653.173 479.477C640.595 504.642 622.259 524.052 598.169 537.703C574.076 551.141 544.762 557.857 510.224 557.857C475.685 557.857 446.263 551.141 421.957 537.703C397.868 524.052 379.426 504.75 366.635 479.796C354.054 454.628 347.767 424.875 347.767 390.538V368.784C347.767 336.792 354.054 308.427 366.635 283.685C379.426 258.73 397.868 239.216 421.957 225.139C446.263 210.848 475.685 203.704 510.224 203.704ZM509.903 279.845C495.619 279.845 484.107 283.898 475.367 292.004C466.837 299.894 460.654 310.558 456.817 323.996C452.98 337.218 451.061 351.722 451.061 367.505V393.418C451.061 408.135 452.875 422.212 456.499 435.647C460.336 449.085 466.518 460.068 475.046 468.6C483.786 477.13 495.406 481.399 509.903 481.399C524.615 481.399 536.235 477.238 544.762 468.918C553.29 460.389 559.367 449.404 562.991 435.968C566.828 422.53 568.747 408.348 568.747 393.418V367.505C568.747 351.722 566.828 337.218 562.991 323.996C559.154 310.558 552.968 299.894 544.441 292.004C535.914 283.898 524.402 279.845 509.903 279.845ZM735.362 551.459V91.7314H837.378V551.459H735.362ZM917.648 551.459V91.7314H1019.66V551.459H917.648ZM1100.57 551.459V209.782H1202.59V551.459H1100.57ZM1096.42 123.723C1096.42 108.58 1101.85 96.5308 1112.73 87.5724C1123.6 78.6145 1136.6 74.1357 1151.74 74.1357C1166.88 74.1357 1179.78 78.6145 1190.44 87.5724C1201.31 96.5308 1206.75 108.58 1206.75 123.723C1206.75 138.867 1201.31 151.024 1190.44 160.195C1179.78 169.366 1166.88 173.951 1151.74 173.951C1136.6 173.951 1123.6 169.366 1112.73 160.195C1101.85 151.024 1096.42 138.867 1096.42 123.723ZM1429.33 203.704C1463.86 203.704 1493.18 210.742 1517.27 224.819C1541.36 238.897 1559.7 258.304 1572.28 283.045C1584.86 307.784 1591.15 336.152 1591.15 368.145V390.217C1591.15 424.557 1584.86 454.309 1572.28 479.477C1559.7 504.642 1541.36 524.052 1517.27 537.703C1493.18 551.141 1463.86 557.857 1429.33 557.857C1394.79 557.857 1365.36 551.141 1341.06 537.703C1316.97 524.052 1298.53 504.75 1285.74 479.796C1273.16 454.628 1266.87 424.875 1266.87 390.538V368.784C1266.87 336.792 1273.16 308.427 1285.74 283.685C1298.53 258.73 1316.97 239.216 1341.06 225.139C1365.36 210.848 1394.79 203.704 1429.33 203.704ZM1429 279.845C1414.72 279.845 1403.21 283.898 1394.47 292.004C1385.94 299.894 1379.76 310.558 1375.92 323.996C1372.08 337.218 1370.16 351.722 1370.16 367.505V393.418C1370.16 408.135 1371.98 422.212 1375.6 435.647C1379.44 449.085 1385.62 460.068 1394.15 468.6C1402.89 477.13 1414.51 481.399 1429 481.399C1443.72 481.399 1455.34 477.238 1463.86 468.918C1472.39 460.389 1478.47 449.404 1482.09 435.968C1485.93 422.53 1487.85 408.348 1487.85 393.418V367.505C1487.85 351.722 1485.93 337.218 1482.09 323.996C1478.26 310.558 1472.07 299.894 1463.54 292.004C1455.02 283.898 1443.5 279.845 1429 279.845ZM2130 209.782L2043.33 551.459H1935.25L1877.04 320.475H1873.52L1815.33 551.459H1706.28L1620.25 209.782H1723.23L1766.72 451.322H1770.87L1827.79 209.782H1923.41L1980.66 451.322H1984.81L2028.3 209.782H2130Z" fill="url(#paint0_linear_323_447)"/>
|
||||
<path d="M0 139.789L208.446 0H348.766L139.032 139.789H0Z" fill="url(#paint1_linear_323_447)"/>
|
||||
<path d="M139.137 139.548L348.755 0L313.88 139.548H139.137Z" fill="url(#paint2_linear_323_447)"/>
|
||||
<path d="M209.253 0L0.000366211 139.548L0.000366216 0H209.253Z" fill="url(#paint3_linear_323_447)"/>
|
||||
<path d="M0.000732422 418.644L0.000732613 279.096H140.987L0.000732422 418.644Z" fill="url(#paint4_linear_323_447)"/>
|
||||
<path d="M0 418.639L137.27 279.004L279.013 278.968L138.779 418.639H0Z" fill="url(#paint5_linear_323_447)"/>
|
||||
<path d="M278.909 279.096L244.683 418.644H138.754L278.909 279.096Z" fill="url(#paint6_linear_323_447)"/>
|
||||
<path d="M0 139.458H138.76V279.006L0 139.458Z" fill="url(#paint7_linear_323_447)"/>
|
||||
<path d="M0 418.452H138.76V558L0 418.452Z" fill="url(#paint8_linear_323_447)"/>
|
||||
<path d="M0 420.379L135.518 282.525L275.453 282.489L137.008 420.379H0Z" fill="url(#paint9_linear_323_447)"/>
|
||||
<path d="M0 145.086L205.787 7.08105H344.316L137.258 145.086H0Z" fill="url(#paint10_linear_323_447)"/>
|
||||
<path d="M137.362 144.849L344.305 7.08105L309.875 144.849H137.362Z" fill="url(#paint11_linear_323_447)"/>
|
||||
<path d="M206.584 7.08105L0.000732422 144.849L0.000732427 7.08105H206.584Z" fill="url(#paint12_linear_323_447)"/>
|
||||
<path d="M0.000732422 420.384L0.00073261 282.616H139.188L0.000732422 420.384Z" fill="url(#paint13_linear_323_447)"/>
|
||||
<path d="M275.351 282.616L241.561 420.384H136.983L275.351 282.616Z" fill="url(#paint14_linear_323_447)"/>
|
||||
<path d="M0 144.76H136.99V282.527L0 144.76Z" fill="url(#paint15_linear_323_447)"/>
|
||||
<path d="M0 420.193H136.99V557.961L0 420.193Z" fill="url(#paint16_linear_323_447)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_323_447" x1="1260.89" y1="1062.25" x2="2688.61" y2="-887.578" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0099FF"/>
|
||||
<stop offset="1" stop-color="#0000FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_323_447" x1="174.383" y1="0" x2="174.383" y2="139.789" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#91D3FF"/>
|
||||
<stop offset="1" stop-color="#0099FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_323_447" x1="223.477" y1="1.83571" x2="264.403" y2="228.183" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28DBFF"/>
|
||||
<stop offset="1" stop-color="#0000FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_323_447" x1="-175.546" y1="-231.311" x2="656.398" y2="312.107" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E1F7FF"/>
|
||||
<stop offset="1" stop-color="#0E9FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_323_447" x1="141.083" y1="306.265" x2="-193.82" y2="198.214" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#45B5FF"/>
|
||||
<stop offset="1" stop-color="#E1F7FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_323_447" x1="129.044" y1="268.506" x2="151.125" y2="424.265" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#95D6FF"/>
|
||||
<stop offset="1" stop-color="#0385FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_323_447" x1="182.521" y1="278.967" x2="208.823" y2="533.74" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#29DAFF"/>
|
||||
<stop offset="1" stop-color="#0000FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_323_447" x1="166.514" y1="313.303" x2="-66.2218" y2="39.4828" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0087E8"/>
|
||||
<stop offset="1" stop-color="#000091"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_323_447" x1="166.514" y1="592.297" x2="-66.2218" y2="318.477" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0087E8"/>
|
||||
<stop offset="1" stop-color="#000091"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_323_447" x1="127.397" y1="272.161" x2="149.197" y2="425.932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#95D6FF"/>
|
||||
<stop offset="1" stop-color="#0385FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_323_447" x1="172.158" y1="7.08105" x2="172.158" y2="145.086" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#91D3FF"/>
|
||||
<stop offset="1" stop-color="#0099FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_323_447" x1="220.625" y1="8.89334" x2="261.03" y2="232.352" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28DBFF"/>
|
||||
<stop offset="1" stop-color="#0000FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_323_447" x1="-173.306" y1="-221.278" x2="648.023" y2="315.206" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E1F7FF"/>
|
||||
<stop offset="1" stop-color="#0E9FFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_323_447" x1="191.449" y1="282.521" x2="-191.351" y2="202.773" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#45B5FF"/>
|
||||
<stop offset="1" stop-color="#E1F7FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_323_447" x1="180.192" y1="282.49" x2="206.158" y2="534.011" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#29DAFF"/>
|
||||
<stop offset="1" stop-color="#0000FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_323_447" x1="164.389" y1="316.386" x2="-65.3768" y2="46.0602" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0087E8"/>
|
||||
<stop offset="1" stop-color="#000091"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_323_447" x1="164.389" y1="591.82" x2="-65.3768" y2="321.494" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0087E8"/>
|
||||
<stop offset="1" stop-color="#000091"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
BIN
assests/img/default_avatar.jpg
Normal file
BIN
assests/img/default_avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
BIN
assests/img/error403.jpg
Normal file
BIN
assests/img/error403.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
assests/img/error404.webp
Normal file
BIN
assests/img/error404.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
assests/img/logo.png
Normal file
BIN
assests/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
26
assests/js/scripts.js
Normal file
26
assests/js/scripts.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*!
|
||||
* Start Bootstrap - SB Admin v7.0.7 (https://startbootstrap.com/template/sb-admin)
|
||||
* Copyright 2013-2023 Start Bootstrap
|
||||
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
|
||||
*/
|
||||
//
|
||||
// Scripts
|
||||
//
|
||||
|
||||
window.addEventListener('DOMContentLoaded', event => {
|
||||
|
||||
// Toggle the side navigation
|
||||
const sidebarToggle = document.body.querySelector('#sidebarToggle');
|
||||
if (sidebarToggle) {
|
||||
// Uncomment Below to persist sidebar toggle between refreshes
|
||||
// if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
|
||||
// document.body.classList.toggle('sb-sidenav-toggled');
|
||||
// }
|
||||
sidebarToggle.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
document.body.classList.toggle('sb-sidenav-toggled');
|
||||
localStorage.setItem('sb|sidebar-toggle', document.body.classList.contains('sb-sidenav-toggled'));
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
11
assests/styles.css
Normal file
11
assests/styles.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* Style général appliqué à toute la page */
|
||||
body{
|
||||
/* Couleur de fond claire pour améliorer la lisibilité */
|
||||
background: #f7f8fb;
|
||||
}
|
||||
|
||||
/* Style de base des cartes Bootstrap */
|
||||
.card{
|
||||
/* Fond blanc pour mettre en valeur le contenu */
|
||||
background: #fff;
|
||||
}
|
||||
22
composer.json
Normal file
22
composer.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "groupe3/projetphp",
|
||||
"description": "le projet php de ces mort",
|
||||
"require": {
|
||||
"smarty/smarty": "^5.7",
|
||||
"phpmailer/phpmailer": "^7.0",
|
||||
"vlucas/phpdotenv": "^5.6"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Controllers\\": "controllers/",
|
||||
"Entities\\": "entities/",
|
||||
"Models\\": "models/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Le Meilleur groupe",
|
||||
"email": "pasdemail@meilleurgroupe.ccicampus"
|
||||
}
|
||||
]
|
||||
}
|
||||
650
composer.lock
generated
Normal file
650
composer.lock
generated
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d2f573a5d0618ae6d7627bef30d78eb9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
"version": "v1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GrahamCampbell/Result-Type.git",
|
||||
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
|
||||
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"phpoption/phpoption": "^1.9.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GrahamCampbell\\ResultType\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
}
|
||||
],
|
||||
"description": "An Implementation Of The Result Type",
|
||||
"keywords": [
|
||||
"Graham Campbell",
|
||||
"GrahamCampbell",
|
||||
"Result Type",
|
||||
"Result-Type",
|
||||
"result"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
|
||||
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-27T19:43:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v7.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||
"reference": "ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-hash": "*",
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||
"phpcompatibility/php-compatibility": "^10.0.0@dev",
|
||||
"squizlabs/php_codesniffer": "^3.13.5",
|
||||
"yoast/phpunit-polyfills": "^1.0.4"
|
||||
},
|
||||
"suggest": {
|
||||
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||
"directorytree/imapengine": "For uploading sent messages via IMAP, see gmail example",
|
||||
"ext-imap": "Needed to support advanced email address parsing according to RFC822",
|
||||
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||
"psr/log": "For optional PSR-3 debug logging",
|
||||
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPMailer\\PHPMailer\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-only"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marcus Bointon",
|
||||
"email": "phpmailer@synchromedia.co.uk"
|
||||
},
|
||||
{
|
||||
"name": "Jim Jagielski",
|
||||
"email": "jimjag@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Andy Prevost",
|
||||
"email": "codeworxtech@users.sourceforge.net"
|
||||
},
|
||||
{
|
||||
"name": "Brent R. Matzelle"
|
||||
}
|
||||
],
|
||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||
"source": "https://github.com/PHPMailer/PHPMailer/tree/v7.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Synchro",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-09T18:02:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/schmittjoh/php-option.git",
|
||||
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
|
||||
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.9-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOption\\": "src/PhpOption/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Johannes M. Schmitt",
|
||||
"email": "schmittjoh@gmail.com",
|
||||
"homepage": "https://github.com/schmittjoh"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
}
|
||||
],
|
||||
"description": "Option Type for PHP",
|
||||
"keywords": [
|
||||
"language",
|
||||
"option",
|
||||
"php",
|
||||
"type"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/schmittjoh/php-option/issues",
|
||||
"source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-27T19:41:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "smarty/smarty",
|
||||
"version": "v5.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/smarty-php/smarty.git",
|
||||
"reference": "78d259d3b971c59a0cd719c270cc5cbb740c36a7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/78d259d3b971c59a0cd719c270cc5cbb740c36a7",
|
||||
"reference": "78d259d3b971c59a0cd719c270cc5cbb740c36a7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"symfony/polyfill-mbstring": "^1.27"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5 || ^7.5",
|
||||
"smarty/smarty-lexer": "^4.0.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Smarty\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Monte Ohrt",
|
||||
"email": "monte@ohrt.com"
|
||||
},
|
||||
{
|
||||
"name": "Uwe Tews",
|
||||
"email": "uwe.tews@googlemail.com"
|
||||
},
|
||||
{
|
||||
"name": "Rodney Rehm",
|
||||
"email": "rodney.rehm@medialize.de"
|
||||
},
|
||||
{
|
||||
"name": "Simon Wisselink",
|
||||
"homepage": "https://www.iwink.nl/"
|
||||
}
|
||||
],
|
||||
"description": "Smarty - the compiling PHP template engine",
|
||||
"homepage": "https://smarty-php.github.io/smarty/",
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://github.com/smarty-php/smarty/discussions",
|
||||
"issues": "https://github.com/smarty-php/smarty/issues",
|
||||
"source": "https://github.com/smarty-php/smarty/tree/v5.8.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/wisskid",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-02-15T14:27:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-iconv": "*",
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-23T08:48:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
"version": "v1.33.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Php80\\": ""
|
||||
},
|
||||
"classmap": [
|
||||
"Resources/stubs"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ion Bazan",
|
||||
"email": "ion.bazan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-02T08:10:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v5.6.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "955e7815d677a3eaa7075231212f2110983adecc"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
|
||||
"reference": "955e7815d677a3eaa7075231212f2110983adecc",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pcre": "*",
|
||||
"graham-campbell/result-type": "^1.1.4",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"phpoption/phpoption": "^1.9.5",
|
||||
"symfony/polyfill-ctype": "^1.26",
|
||||
"symfony/polyfill-mbstring": "^1.26",
|
||||
"symfony/polyfill-php80": "^1.26"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.2",
|
||||
"ext-filter": "*",
|
||||
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-filter": "Required to use the boolean validator."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "5.6-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dotenv\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "hello@gjcampbell.co.uk",
|
||||
"homepage": "https://github.com/GrahamCampbell"
|
||||
},
|
||||
{
|
||||
"name": "Vance Lucas",
|
||||
"email": "vance@vancelucas.com",
|
||||
"homepage": "https://github.com/vlucas"
|
||||
}
|
||||
],
|
||||
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
"environment"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/vlucas/phpdotenv/issues",
|
||||
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-27T19:49:13+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
0
controllers/.gitkeep
Normal file
0
controllers/.gitkeep
Normal file
105
controllers/AdminCtrl.php
Normal file
105
controllers/AdminCtrl.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
namespace Controllers;
|
||||
|
||||
use Models\CategoryModel;
|
||||
use Models\UserModel;
|
||||
use Models\AuthorisationModel;
|
||||
|
||||
use Entities\Category;
|
||||
use Entities\Authorisation;
|
||||
use Entities\User;
|
||||
|
||||
/**
|
||||
* Le controller de la partie accessible uniquement par l'admin
|
||||
* @author Laura
|
||||
*/
|
||||
|
||||
class AdminCtrl extends MotherCtrl{
|
||||
/**
|
||||
* Page Admin
|
||||
*/
|
||||
public function admin(){
|
||||
|
||||
if (!isset($_SESSION['user']) && ($_SESSION['user']['user_status'] != 1 )){
|
||||
$error = new \Controllers\ErrorCtrl();
|
||||
return $error->error_403();
|
||||
}
|
||||
|
||||
$objCategoryModel = new CategoryModel;
|
||||
|
||||
if (!empty($_POST['new_category'])) {
|
||||
$newCat = new Category();
|
||||
$newCat->setName($_POST['new_category']);
|
||||
$objCategoryModel->insertCategory($newCat);
|
||||
$_SESSION['success'] = "La catégorie a bien été ajoutée";
|
||||
header('Location: '.$_ENV['BASE_URL'].'/admin/admin');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!empty($_POST['id_to_edit']) && !empty($_POST['new_name'])) {
|
||||
$editCat = new Category();
|
||||
$editCat->setId($_POST['id_to_edit']);
|
||||
$editCat->setName($_POST['new_name']);
|
||||
$objCategoryModel->editCategory($editCat);
|
||||
$_SESSION['success'] = "La catégorie a bien été modifiée";
|
||||
header('Location: '.$_ENV['BASE_URL'].'/admin/admin');
|
||||
exit;
|
||||
}
|
||||
|
||||
$arrCategory = $objCategoryModel->findAllCategory();
|
||||
$arrCategoryToDisplay = array();
|
||||
|
||||
foreach($arrCategory as $arrDetCategory){
|
||||
$objCategory = new Category;
|
||||
$objCategory->hydrate($arrDetCategory);
|
||||
$arrCategoryToDisplay[] = $objCategory;
|
||||
}
|
||||
|
||||
$objUserModel = new UserModel;
|
||||
|
||||
if (!empty($_POST['action'])) {
|
||||
$intUserId = (int)$_POST['user_id'];
|
||||
|
||||
if ($intUserId > 0) {
|
||||
if ($_POST['action'] === 'update_status' && !empty($_POST['new_status'])) {
|
||||
$objUser = new User();
|
||||
$objUser->setId($intUserId);
|
||||
$objUser->setStatus((int)$_POST['new_status']);
|
||||
if ($objUserModel->editStatus($objUser)) {
|
||||
$_SESSION['success'] = "Le statut a bien été modifié !";
|
||||
}
|
||||
}
|
||||
elseif ($_POST['action'] === 'delete_user') {
|
||||
$objUserModel->delete_soft($intUserId);
|
||||
$_SESSION['success'] = "L'utilisateur a été supprimé.";
|
||||
}
|
||||
header('Location: '.$_ENV['BASE_URL'].'/admin/admin');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$arrUser = $objUserModel->findAllUsers();
|
||||
$arrUserToDisplay = array();
|
||||
|
||||
foreach($arrUser as $arrDetUser){
|
||||
$objUser = new User;
|
||||
$objUser->hydrate($arrDetUser);
|
||||
$arrUserToDisplay[] = $objUser;
|
||||
}
|
||||
|
||||
$objAuthorisationModel = new AuthorisationModel;
|
||||
$arrAuthorisation = $objAuthorisationModel->findAllAuthorisation();
|
||||
$arrAuthorisationToDisplay = array();
|
||||
|
||||
foreach($arrAuthorisation as $arrDetAuthorisation){
|
||||
$objAuthorisation = new Authorisation;
|
||||
$objAuthorisation->hydrate($arrDetAuthorisation);
|
||||
$arrAuthorisationToDisplay[] = $objAuthorisation;
|
||||
}
|
||||
|
||||
$this->_arrData['arrCategoryToDisplay'] = $arrCategoryToDisplay;
|
||||
$this->_arrData['arrUserToDisplay'] = $arrUserToDisplay;
|
||||
$this->_arrData['arrAuthorisationToDisplay'] = $arrAuthorisationToDisplay;
|
||||
$this->_display("admin");
|
||||
}
|
||||
}
|
||||
28
controllers/ErrorCtrl.php
Normal file
28
controllers/ErrorCtrl.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
namespace Controllers;
|
||||
|
||||
/**
|
||||
* Le contrôleur des erreurs
|
||||
* @author Laura
|
||||
*/
|
||||
class ErrorCtrl extends MotherCtrl{
|
||||
|
||||
/**
|
||||
* Page erreur 404
|
||||
*/
|
||||
public function error_404(){
|
||||
http_response_code(404);
|
||||
$this->_display("error_404");
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Page erreur 403
|
||||
*/
|
||||
public function error_403(){
|
||||
http_response_code(403);
|
||||
$this->_display("error_403");
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
||||
52
controllers/MotherCtrl.php
Normal file
52
controllers/MotherCtrl.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace Controllers;
|
||||
|
||||
use Smarty\Smarty;
|
||||
|
||||
/**
|
||||
* Controller Mère qui permet de gèrer l'affichage des pages
|
||||
* @author Yasser
|
||||
*/
|
||||
|
||||
class MotherCtrl {
|
||||
|
||||
protected array $_arrData = array(); // ou = []
|
||||
|
||||
/**
|
||||
* Méthode d'affichage des pages
|
||||
* @param string $strView Le fichier a afficher
|
||||
* @param bool $boolDisplay Booléen pour afficher web ou mail
|
||||
* @return page|object affiche la page ou retourne l'objet pour le mail
|
||||
*/
|
||||
protected function _display($strView, bool $boolDisplay = true){
|
||||
|
||||
$objSmarty = new Smarty();
|
||||
$objSmarty->registerPlugin('modifier', 'vardump', 'var_dump');
|
||||
$objSmarty->registerPlugin('modifier', 'file_exists', 'file_exists');
|
||||
$objSmarty->registerPlugin('modifier', 'stripos', 'stripos');
|
||||
$objSmarty->caching = false;
|
||||
|
||||
$objSmarty->force_compile = true;
|
||||
|
||||
$objSmarty->compile_check = true;
|
||||
|
||||
foreach($this->_arrData as $key=>$value){
|
||||
$objSmarty->assign($key, $value);
|
||||
}
|
||||
|
||||
$objSmarty->assign("success_message", $_SESSION['success']??'');
|
||||
unset($_SESSION['success']);
|
||||
|
||||
if (isset($_SESSION['error'])){
|
||||
$objSmarty->assign("arrError", array($_SESSION['error']));
|
||||
unset($_SESSION['error']);
|
||||
}
|
||||
|
||||
if ($boolDisplay){
|
||||
$objSmarty->display("views/".$strView.".tpl");
|
||||
}else{
|
||||
return $objSmarty->fetch("views/".$strView.".tpl");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
39
controllers/PageCtrl.php
Normal file
39
controllers/PageCtrl.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Controllers;
|
||||
|
||||
|
||||
/**
|
||||
* Le controller des pages annexes
|
||||
* @author Laura & Besnik
|
||||
*/
|
||||
|
||||
class PageCtrl extends MotherCtrl{
|
||||
|
||||
|
||||
/**
|
||||
* Page aide utilisateur
|
||||
*/
|
||||
public function help(){
|
||||
|
||||
$this->_display("help");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Page mentions légales
|
||||
*/
|
||||
public function mentions(){
|
||||
// Afficher
|
||||
$this->_display("mentions");
|
||||
}
|
||||
|
||||
/**
|
||||
* Page à propos
|
||||
*/
|
||||
public function about(){
|
||||
// Afficher
|
||||
$this->_display("about");
|
||||
}
|
||||
|
||||
}
|
||||
554
controllers/ProjectCtrl.php
Normal file
554
controllers/ProjectCtrl.php
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
<?php
|
||||
|
||||
namespace Controllers;
|
||||
|
||||
|
||||
|
||||
use Models\ProjectModel;
|
||||
use Models\CategoryModel;
|
||||
use Models\ImageModel;
|
||||
use Models\UserModel;
|
||||
|
||||
|
||||
use Entities\Project;
|
||||
use Entities\Category;
|
||||
use Entities\Authorisation;
|
||||
use Entities\User;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
|
||||
/**
|
||||
* Le controler des Project
|
||||
* @author Yasser, Laura, Besnik & Guillaume
|
||||
*/
|
||||
|
||||
class ProjectCtrl extends MotherCtrl{
|
||||
|
||||
/**
|
||||
* Fonction d'affichage de la page d'acceuil
|
||||
*/
|
||||
public function home(){
|
||||
|
||||
|
||||
if (!isset($_SESSION['user']) && isset($_COOKIE['remember_me'])) {
|
||||
|
||||
$token_du_cookie = $_COOKIE['remember_me'];
|
||||
$hash_a_verifier = hash('sha256', $token_du_cookie);
|
||||
|
||||
$objUserModel = new UserModel;
|
||||
$row = $objUserModel->getTokenUser($hash_a_verifier);
|
||||
|
||||
if ($row) {
|
||||
$user = $objUserModel->findUserById($row['token_user_id']);
|
||||
|
||||
if ($user) {
|
||||
$_SESSION['user'] = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$intCategory = 0;
|
||||
if (!empty($_GET['filter_cat'])) {
|
||||
$intCategory = (int) $_GET['filter_cat'];
|
||||
}
|
||||
|
||||
$boolOld = false;
|
||||
if (!empty($_GET['filter_old']) && $_GET['filter_old'] == 'true') {
|
||||
$boolOld = true;
|
||||
}
|
||||
|
||||
$objProjectModel = new ProjectModel;
|
||||
$arrProject = $objProjectModel->findAll(0,'',0,0,'','','',$intCategory,$boolOld);
|
||||
$arrProjectToDisplay = array();
|
||||
foreach($arrProject as $arrDetProject){
|
||||
$objProject = new Project;
|
||||
$objProject->hydrate($arrDetProject);
|
||||
$arrProjectToDisplay[] = $objProject;
|
||||
}
|
||||
|
||||
$this->_arrData['arrProjectToDisplay'] = $arrProjectToDisplay;
|
||||
$this->_display("home");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction d'affichage de la barre de recherche
|
||||
*/
|
||||
public function search(){
|
||||
|
||||
$strKeywords = $_POST['keywords']??"";
|
||||
$intAuthor = $_POST['author']??0;
|
||||
$intPeriod = $_POST['period']??0;
|
||||
$strDate = $_POST['date']??'';
|
||||
$strStartDate = $_POST['startdate']??'';
|
||||
$strEndDate = $_POST['enddate']??'';
|
||||
$intCategory = $_POST['category']??0;
|
||||
|
||||
$objProjectModel = new ProjectModel;
|
||||
$arrProject = $objProjectModel->findAll(intAuthor:$intAuthor, intPeriod:$intPeriod, strDate:$strDate,
|
||||
strKeywords:$strKeywords, strStartDate:$strStartDate, strEndDate:$strEndDate, intCategory:$intCategory);
|
||||
$arrProjectToDisplay = array();
|
||||
|
||||
foreach($arrProject as $arrDetProject){
|
||||
$objProject = new Project;
|
||||
$objProject->hydrate($arrDetProject);
|
||||
$arrProjectToDisplay[] = $objProject;
|
||||
}
|
||||
|
||||
$objUserModel = new UserModel;
|
||||
$arrUser = $objUserModel->findAllUsers();
|
||||
|
||||
$objCategoryModel = new CategoryModel;
|
||||
$arrCategory = $objCategoryModel->findAllCategory();
|
||||
|
||||
$this->_arrData['arrProjectToDisplay'] = $arrProjectToDisplay;
|
||||
$this->_arrData['arrCategory'] = $arrCategory;
|
||||
$this->_arrData['arrProject'] = $arrProject;
|
||||
$this->_arrData['arrUser'] = $arrUser;
|
||||
$this->_arrData['strKeywords'] = $strKeywords;
|
||||
$this->_arrData['intAuthor'] = $intAuthor;
|
||||
$this->_arrData['intPeriod'] = $intPeriod;
|
||||
$this->_arrData['strDate'] = $strDate;
|
||||
$this->_arrData['strStartDate'] = $strStartDate;
|
||||
$this->_arrData['strEndDate'] = $strEndDate;
|
||||
$this->_arrData['intCategory'] = $intCategory;
|
||||
|
||||
$this->_display("search");
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction d'affichage de la page projet
|
||||
*/
|
||||
public function addedit_project() {
|
||||
if (!isset($_SESSION['user'])){
|
||||
$error = new \Controllers\ErrorCtrl();
|
||||
return $error->error_403();
|
||||
}
|
||||
|
||||
$objProject = new Project;
|
||||
$objProjectModel = new ProjectModel;
|
||||
$objCategoryModel = new CategoryModel;
|
||||
|
||||
if (isset($_GET['id'])){
|
||||
$arrProject = $objProjectModel->findOne($_GET['id']);
|
||||
if($_SESSION['user']['user_id'] != $arrProject['project_user_id']){
|
||||
$error = new \Controllers\ErrorCtrl();
|
||||
return $error->error_403();
|
||||
}
|
||||
$objProject->hydrate($arrProject);
|
||||
$this->_arrData['arrImages'] = $objProjectModel->getImagesByProjectId($objProject->getId());
|
||||
}
|
||||
|
||||
$arrError = [];
|
||||
if (count($_POST) > 0) {
|
||||
|
||||
$objProject->hydrate($_POST);
|
||||
if ($objProject->getTitle() == ""){
|
||||
$arrError['title'] = "Le titre est obligatoire";
|
||||
}
|
||||
|
||||
if ($objProject->getDescription() == ""){
|
||||
$arrError['description'] = "La description est obligatoire";
|
||||
}
|
||||
|
||||
if ($objProject->getContent() == ""){
|
||||
$arrError['content'] = "Le contenu est obligatoire";
|
||||
}
|
||||
|
||||
$arrTypeAllowed = array('image/jpeg', 'image/png', 'image/webp');
|
||||
if ($_FILES['thumbnail']['error'] != 4){
|
||||
if (!in_array($_FILES['thumbnail']['type'], $arrTypeAllowed)){
|
||||
$arrError['thumbnail'] = "Le type de fichier n'est pas autorisé";
|
||||
}else{
|
||||
switch ($_FILES['thumbnail']['error']){
|
||||
case 0 :
|
||||
$strImageName = uniqid().".webp";
|
||||
$strOldImg = $objProject->getThumbnail();
|
||||
$objProject->setThumbnail($strImageName);
|
||||
break;
|
||||
case 1 :
|
||||
$arrError['thumbnail'] = "Le fichier est trop volumineux";
|
||||
break;
|
||||
case 2 :
|
||||
$arrError['thumbnail'] = "Le fichier est trop volumineux";
|
||||
break;
|
||||
case 3 :
|
||||
$arrError['thumbnail'] = "Le fichier a été partiellement téléchargé";
|
||||
break;
|
||||
case 6 :
|
||||
$arrError['thumbnail'] = "Le répertoire temporaire est manquant";
|
||||
break;
|
||||
default :
|
||||
$arrError['thumbnail'] = "Erreur sur l'image";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (is_null($objProject->getThumbnail())){
|
||||
$arrError['thumbnail'] = "L'image est obligatoire";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($arrError) == 0){
|
||||
|
||||
$boolImageOk = true;
|
||||
if (isset($strImageName)){
|
||||
$strDest = $_ENV['IMG_PROJECT_PATH'].$strImageName;
|
||||
$strSource = $_FILES['thumbnail']['tmp_name'];
|
||||
list($intWidth, $intHeight) = getimagesize($strSource);
|
||||
|
||||
$intDestWidth = 1280; $intDestHeight = 720;
|
||||
$fltDestRatio = $intDestWidth / $intDestHeight;
|
||||
$fltSourceRatio = $intWidth / $intHeight;
|
||||
|
||||
if ($fltSourceRatio > $fltDestRatio) {
|
||||
$intCropHeight = $intHeight;
|
||||
$intCropWidth = (int)round($intHeight * $fltDestRatio);
|
||||
$intCropX = (int)(($intWidth - $intCropWidth) / 2);
|
||||
$intCropY = 0;
|
||||
} else {
|
||||
$intCropWidth = $intWidth;
|
||||
$intCropHeight = (int)round($intWidth / $fltDestRatio);
|
||||
$intCropX = 0;
|
||||
$intCropY = (int)(($intHeight - $intCropHeight) / 2);
|
||||
}
|
||||
|
||||
$objDest = imagecreatetruecolor($intDestWidth, $intDestHeight);
|
||||
switch ($_FILES['thumbnail']['type']) {
|
||||
case 'image/jpeg' :
|
||||
$objSource = imagecreatefromjpeg($strSource);
|
||||
break;
|
||||
case 'image/png' :
|
||||
$objSource = imagecreatefrompng($strSource);
|
||||
break;
|
||||
case 'image/webp' :
|
||||
$objSource = imagecreatefromwebp($strSource);
|
||||
break;
|
||||
}
|
||||
|
||||
imagecopyresampled($objDest, $objSource, 0, 0, $intCropX, $intCropY, $intDestWidth, $intDestHeight, $intCropWidth, $intCropHeight);
|
||||
$boolImageOk = imagewebp($objDest, $strDest);
|
||||
|
||||
imagedestroy($objDest);
|
||||
imagedestroy($objSource);
|
||||
}
|
||||
|
||||
if ($boolImageOk){
|
||||
if (!isset($_GET['id'])){
|
||||
$objProject->setUser_id($_SESSION['user']['user_id']);
|
||||
$boolOk = $objProjectModel->insert($objProject);
|
||||
} else {
|
||||
$boolOk = $objProjectModel->updateProject($objProject);
|
||||
}
|
||||
|
||||
if ($boolOk){
|
||||
if (isset($_FILES['imageProject']) && $_FILES['imageProject']['error'][0] != 4) {
|
||||
$files = $_FILES['imageProject'];
|
||||
$maxPhotos = 20;
|
||||
|
||||
$currentImages = $objProjectModel->getImagesByProjectId($objProject->getId());
|
||||
$totalExisting = count($currentImages);
|
||||
$remainingSlots = $maxPhotos - $totalExisting;
|
||||
|
||||
if ($remainingSlots <= 0) {
|
||||
$_SESSION['error'] = "Limite de $maxPhotos photos atteinte. Supprimez-en pour en ajouter de nouvelles.";
|
||||
} else {
|
||||
$uploadedCount = 0;
|
||||
|
||||
foreach ($files['name'] as $key => $name) {
|
||||
if ($uploadedCount >= $remainingSlots) break;
|
||||
|
||||
if ($files['error'][$key] === 0 && in_array($files['type'][$key], $arrTypeAllowed)) {
|
||||
|
||||
$galleryName = uniqid() . "_gallery.webp";
|
||||
$strDestGallery = $_ENV['IMG_PROJECT_PATH'].$galleryName;
|
||||
$strSourceGallery = $files['tmp_name'][$key];
|
||||
|
||||
list($intW, $intH) = getimagesize($strSourceGallery);
|
||||
|
||||
$intDestW = 150; $intDestH = 150;
|
||||
$fltDestR = $intDestW / $intDestH;
|
||||
$fltSourceR = $intW / $intH;
|
||||
|
||||
if ($fltSourceR > $fltDestR) {
|
||||
$intCropH = $intH;
|
||||
$intCropW = (int)round($intH * $fltDestR);
|
||||
$intCropX = (int)(($intW - $intCropW) / 2);
|
||||
$intCropY = 0;
|
||||
} else {
|
||||
$intCropW = $intW;
|
||||
$intCropH = (int)round($intW / $fltDestR);
|
||||
$intCropX = 0;
|
||||
$intCropY = (int)(($intH - $intCropH) / 2);
|
||||
}
|
||||
|
||||
$objDestGallery = imagecreatetruecolor($intDestW, $intDestH);
|
||||
|
||||
switch ($files['type'][$key]) {
|
||||
case 'image/jpeg' :
|
||||
$objSourceGallery = imagecreatefromjpeg($strSourceGallery);
|
||||
break;
|
||||
case 'image/png' :
|
||||
$objSourceGallery = imagecreatefrompng($strSourceGallery);
|
||||
break;
|
||||
case 'image/webp' :
|
||||
$objSourceGallery = imagecreatefromwebp($strSourceGallery);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($objSourceGallery) {
|
||||
imagecopyresampled($objDestGallery, $objSourceGallery, 0, 0, $intCropX, $intCropY, $intDestW, $intDestH, $intCropW, $intCropH);
|
||||
|
||||
if (imagewebp($objDestGallery, $strDestGallery)) {
|
||||
$objProjectModel->addImageInProject($galleryName, $objProject->getId());
|
||||
$uploadedCount++;
|
||||
}
|
||||
|
||||
imagedestroy($objDestGallery);
|
||||
imagedestroy($objSourceGallery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($uploadedCount > 0) {
|
||||
$_SESSION['success'] = "$uploadedCount image(s) ajoutée(s) à la galerie !";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($strOldImg) && !empty($strOldImg) && isset($strImageName)){
|
||||
$strOldFile = $_ENV['IMG_PROJECT_PATH'].$strOldImg;
|
||||
if (file_exists($strOldFile)) unlink($strOldFile);
|
||||
}
|
||||
|
||||
$_SESSION['success'] = (!isset($_GET['id'])) ? "Le projet a bien été créé" : "Le projet a bien été modifié";
|
||||
header('Location:'.$_ENV['BASE_URL']);
|
||||
exit;
|
||||
} else {
|
||||
$arrError[] = "Erreur lors de l'enregistrement en base de données";
|
||||
}
|
||||
} else {
|
||||
$arrError['thumbnail'] = "Erreur dans le traitement de l'image";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_arrData['arrCategory'] = $objCategoryModel->findAllCategory();
|
||||
$this->_arrData['objProject'] = $objProject;
|
||||
$this->_arrData['arrError'] = $arrError;
|
||||
|
||||
if ($objProject->getId()) {
|
||||
$this->_arrData['arrImages'] = $objProjectModel->getImagesByProjectId($objProject->getId());
|
||||
} else {
|
||||
$this->_arrData['arrImages'] = [];
|
||||
}
|
||||
|
||||
$this->_display('addedit_project');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fonction d'affichage d'un projet
|
||||
*/
|
||||
public function display() {
|
||||
$intId = $_GET['id'] ?? null;
|
||||
|
||||
if ($intId) {
|
||||
$objProjectModel = new ProjectModel();
|
||||
$arrProject = $objProjectModel->findOne((int)$intId);
|
||||
|
||||
|
||||
if ($arrProject) {
|
||||
$arrImages = $objProjectModel->getImagesByProjectId((int)$intId);
|
||||
$objProject = new Project();
|
||||
$objProject->hydrate($arrProject);
|
||||
|
||||
$this->_arrData["arrProject"] = $arrProject;
|
||||
$this->_arrData["objProject"] = $objProject;
|
||||
$this->_arrData["arrImages"] = $arrImages;
|
||||
|
||||
$this->_display("project_display");
|
||||
|
||||
} else {
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/home');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/home');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction d'envoi d'email
|
||||
*/
|
||||
public function sendEmail(){
|
||||
if (count($_POST) > 0) {
|
||||
|
||||
$projectId = (int)($_POST['project_id'] ?? 0);
|
||||
$toEmail = trim($_POST['to_email'] ?? '');
|
||||
|
||||
$objProjectModel = new ProjectModel();
|
||||
$arrProject = $objProjectModel->findOne($projectId);
|
||||
|
||||
if (!$arrProject) {
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/home');
|
||||
exit;
|
||||
}
|
||||
|
||||
$objProject = new Project();
|
||||
$objProject->hydrate($arrProject);
|
||||
|
||||
$objMail = new PHPMailer();
|
||||
$objMail->IsSMTP();
|
||||
$objMail->Mailer = "smtp";
|
||||
$objMail->CharSet = PHPMailer::CHARSET_UTF8;
|
||||
$objMail->SMTPDebug = 0;
|
||||
|
||||
$objMail->SMTPAuth = true;
|
||||
$objMail->SMTPSecure = $_ENV['SMTP_SECURE'];
|
||||
$objMail->Port = $_ENV['SMTP_PORT'];
|
||||
$objMail->Host = $_ENV['SMTP_HOST'];
|
||||
$objMail->Username = $_ENV['SMTP_USERNAME'];
|
||||
$objMail->Password = $_ENV['SMTP_PASSWORD'];
|
||||
|
||||
$objMail->SMTPOptions = [
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$objMail->IsHTML(true);
|
||||
|
||||
$objMail->setFrom($_ENV['MAIL_FROM'], $_ENV['MAIL_FROM_NAME']);
|
||||
$objMail->addAddress($toEmail);
|
||||
|
||||
$objMail->Subject = "Projet : " . $objProject->getTitle();
|
||||
|
||||
$url = $_ENV['BASE_URL'].'/project/display/'.$projectId;
|
||||
|
||||
$this->_arrData['projectTitle'] = $objProject->getTitle();
|
||||
$this->_arrData['projectDescription'] = $objProject->getDescription();
|
||||
$this->_arrData['projectUrl'] = $url;
|
||||
|
||||
$objMail->Body = $this->_display("mail_message", false);
|
||||
|
||||
if ($objMail->Send()) {
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/display/'.$projectId.'?mail=ok');
|
||||
} else {
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/display/'.$projectId.'?mail=fail');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Location: '.$_ENV['BASE_URL'].'/project/home');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de modération de projet = accepté
|
||||
*/
|
||||
public function accept(){
|
||||
|
||||
$intId = $_GET['id'];
|
||||
|
||||
$objProjectModel = new ProjectModel;
|
||||
$objProjectModel->accept($intId);
|
||||
|
||||
header('Location: '.$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de modération de projet = refusé
|
||||
*/
|
||||
public function refuse(){
|
||||
|
||||
$intId = $_GET['id'];
|
||||
|
||||
$objProjectModel = new ProjectModel;
|
||||
$objProjectModel->refuse($intId);
|
||||
|
||||
header('Location: '.$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de suppression de projet
|
||||
*/
|
||||
public function delete(){
|
||||
|
||||
$objProjectModel = new ProjectModel();
|
||||
$intId = $_GET['id'] ?? null;
|
||||
$project = $objProjectModel->findOne($intId);
|
||||
if (
|
||||
!isset($_SESSION['user']) ||
|
||||
!$project &&
|
||||
($_SESSION['user']['user_status'] != 2 || $project['project_user_id'] != $_SESSION['user']['user_id'])
|
||||
) {
|
||||
header('Location: '.$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
if ($intId) {
|
||||
if ($objProjectModel->delete_soft_project((int) $intId)) {
|
||||
$_SESSION['success'] = "Le projet a été archivé avec succès.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Une erreur est survenue lors de l'archivage.";
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: '.$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de changement de statut (Approuvé, Refusé, En attente)
|
||||
*/
|
||||
public function change_image_status() {
|
||||
if (isset($_GET['id_img']) && isset($_GET['status'])) {
|
||||
$idImg = (int)$_GET['id_img'];
|
||||
$status = $_GET['status'];
|
||||
|
||||
$objProjectModel = new ProjectModel();
|
||||
|
||||
if ($objProjectModel->updateImageStatus($idImg, $status)) {
|
||||
$_SESSION['success'] = "Le statut de l'image est désormais : " . ucfirst($status);
|
||||
}
|
||||
}
|
||||
|
||||
$urlRedirect = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "index.php";
|
||||
header('Location: ' . $urlRedirect);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fonction de validation de l'image de projet
|
||||
*/
|
||||
public function delete_image() {
|
||||
|
||||
$idImg = $_GET['id'];
|
||||
$objProjectModel = new ProjectModel();
|
||||
|
||||
$image = $objProjectModel->findImage($idImg);
|
||||
if ($image) {
|
||||
$filePath = $_ENV['IMG_PROJECT_PATH'] . $image['image_name'];
|
||||
if (file_exists($filePath)) unlink($filePath);
|
||||
|
||||
$objProjectModel->deleteImage($idImg);
|
||||
$_SESSION['success'] = "Image supprimée !";
|
||||
}
|
||||
|
||||
$url = (isset($_SERVER['HTTP_REFERER'])) ? $_SERVER['HTTP_REFERER'] : "index.php";
|
||||
header('Location: ' . $url);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
334
controllers/UserCtrl.php
Normal file
334
controllers/UserCtrl.php
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
<?php
|
||||
|
||||
namespace Controllers;
|
||||
|
||||
use Models\UserModel;
|
||||
use Models\ProjectModel;
|
||||
|
||||
use Entities\User;
|
||||
use Entities\Project;
|
||||
|
||||
/**
|
||||
* Le controller de la page d'aide utilisateur
|
||||
* @author Guillaume & Besnik & Yasser
|
||||
*/
|
||||
|
||||
class UserCtrl extends MotherCtrl {
|
||||
/**
|
||||
* Page Login
|
||||
*/
|
||||
public function login(){
|
||||
|
||||
$strMail = $_POST['user_mail']??"";
|
||||
$strPwd = $_POST['user_password']??"";
|
||||
|
||||
$arrError = [];
|
||||
if (count($_POST) > 0) {
|
||||
if ($strMail == ""){
|
||||
$arrError['mail'] = "Le mail est obligatoire";
|
||||
}
|
||||
if ($strPwd == ""){
|
||||
$arrError['pwd'] = "Le mot de passe est obligatoire";
|
||||
}
|
||||
|
||||
if (count($arrError) == 0){
|
||||
$objUserModel = new UserModel;
|
||||
$arrResult = $objUserModel->verifUser($strMail, $strPwd);
|
||||
if ($arrResult === false){
|
||||
$arrError[] = "Mail ou mot de passe invalide";
|
||||
}else{
|
||||
$_SESSION['user'] = $arrResult;
|
||||
$_SESSION['success'] = "Bienvenue, vous êtes bien connecté";
|
||||
if (isset($_POST['remember_me'])) {
|
||||
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$token_hash = hash('sha256', $token);
|
||||
$objUserModel->remember($_SESSION['user']['user_id'],$token_hash);
|
||||
|
||||
setcookie('remember_me', $token, time() + (15*24*60*60), "/", "", true, true);
|
||||
|
||||
}
|
||||
header("Location:".$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->_arrData['arrError'] = $arrError;
|
||||
$this->_display("login");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction pour ce déconnecter
|
||||
*/
|
||||
public function logout(){
|
||||
|
||||
if (isset($_COOKIE['remember_me'])) {
|
||||
$hash = hash('sha256', $_COOKIE['remember_me']);
|
||||
|
||||
$objUserModel = new UserModel;
|
||||
$objUserModel->deleteToken($hash);
|
||||
|
||||
setcookie('remember_me', '', time() - 3600, "/");
|
||||
}
|
||||
unset($_SESSION['user']);
|
||||
|
||||
$_SESSION['success'] = "Vous êtes bien déconnecté";
|
||||
|
||||
header("Location:".$_ENV['BASE_URL']);
|
||||
exit;
|
||||
|
||||
}
|
||||
/**
|
||||
* Fonction d'inscription d'un utilisateur
|
||||
* Effectue les validations du formulaire,
|
||||
* vérifie l'unicité du mail et du pseudo, puis insère l'utilisateur en base de données
|
||||
* @return void
|
||||
*/
|
||||
public function signup(){
|
||||
|
||||
$objUser = new User();
|
||||
$strPwdConfirm = $_POST['pwd_confirm'] ?? "";
|
||||
|
||||
$objUser->hydrate($_POST);
|
||||
|
||||
$arrError = [];
|
||||
|
||||
if (!empty($_POST)) {
|
||||
|
||||
$arrError = array_merge($this->_verifInfos($objUser),
|
||||
$this->_verifPwd($objUser, $strPwdConfirm));
|
||||
|
||||
if (count($arrError) === 0) {
|
||||
$objUserModel = new UserModel();
|
||||
if ($objUserModel->mailExists($objUser->getMail())) {
|
||||
$arrError['user_mail'] = "Impossible de créer le compte avec ces informations";
|
||||
}
|
||||
if ($objUserModel->pseudoExists($objUser->getPseudo())) {
|
||||
$arrError['user_pseudo'] = "Ce pseudo existe déjà";
|
||||
}
|
||||
if (count($arrError) === 0) {
|
||||
$boolInsert = $objUserModel->insert($objUser);
|
||||
|
||||
if ($boolInsert === true) {
|
||||
$_SESSION['success'] = "Compte créé avec succès";
|
||||
header("Location:".$_ENV['BASE_URL']."/user/login");
|
||||
exit;
|
||||
} else {
|
||||
$arrError['global'] = "Erreur lors de l'ajout";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_arrData['objUser'] = $objUser;
|
||||
$this->_arrData['arrError'] = $arrError;
|
||||
$this->_display("signup");
|
||||
}
|
||||
|
||||
|
||||
protected function _verifInfos(User $objUser): array {
|
||||
|
||||
$arrError = [];
|
||||
|
||||
if (trim($objUser->getName()) === "") {
|
||||
$arrError['user_name'] = "Le nom est obligatoire";
|
||||
}
|
||||
|
||||
if (trim($objUser->getFirstname()) === "") {
|
||||
$arrError['user_firstname'] = "Le prénom est obligatoire";
|
||||
}
|
||||
|
||||
if (trim($objUser->getMail()) === "") {
|
||||
$arrError['user_mail'] = "L'adresse e-mail est obligatoire";
|
||||
} elseif (!filter_var($objUser->getMail(), FILTER_VALIDATE_EMAIL)) {
|
||||
$arrError['user_mail'] = "Le format de l'adresse e-mail est invalide";
|
||||
}
|
||||
|
||||
if (trim($objUser->getPseudo()) === "") {
|
||||
$arrError['user_pseudo'] = "Le pseudo est obligatoire";
|
||||
}
|
||||
|
||||
return $arrError;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vérification du mot de passe
|
||||
*/
|
||||
protected function _verifPwd(User $objUser, string $strPwdConfirm): array {
|
||||
|
||||
$arrError = [];
|
||||
|
||||
$strRegex = "/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{15,}$/";
|
||||
|
||||
if ($objUser->getPwd() == ""){
|
||||
$arrError['user_password'] = "Le mot de passe est obligatoire";
|
||||
}
|
||||
else if (!preg_match($strRegex, $objUser->getPwd())){
|
||||
$arrError['user_password'] = "Le mot de passe ne respecte pas les critères";
|
||||
}
|
||||
else if ($objUser->getPwd() != $strPwdConfirm){
|
||||
$arrError['pwd_confirm'] = "La confirmation du mot de passe ne correspond pas";
|
||||
}
|
||||
|
||||
return $arrError;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* le controlleur affichage de la page user
|
||||
*/
|
||||
public function user(){
|
||||
|
||||
$strPseudo = $_GET['pseudo']??'';
|
||||
|
||||
$objUserModel = new UserModel;
|
||||
$arrUserData = $objUserModel->findUserByPseudo($strPseudo);
|
||||
|
||||
if ($arrUserData === false) {
|
||||
header("Location: ".$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$objUser = new User;
|
||||
$objUser->hydrate($arrUserData);
|
||||
$objProjectModel = new ProjectModel;
|
||||
$arrProjects = $objProjectModel->findAll(0,'',$objUser->getId());
|
||||
|
||||
$arrProjectToDisplay = array();
|
||||
foreach($arrProjects as $projectData) {
|
||||
$objProject = new Project();
|
||||
$objProject->hydrate($projectData);
|
||||
$arrProjectToDisplay[] = $objProject;
|
||||
}
|
||||
|
||||
$this->_arrData['user'] = $objUser;
|
||||
$this->_arrData['arrProjectToDisplay'] = $arrProjectToDisplay;
|
||||
$this->_display("user");
|
||||
}
|
||||
|
||||
/**
|
||||
* le controlleur de la modification d'un user
|
||||
*/
|
||||
public function edit(){
|
||||
if(!isset($_SESSION['user'])){
|
||||
header("Location: ".$_ENV['BASE_URL']);
|
||||
exit;
|
||||
}
|
||||
$objUserModel = new UserModel;
|
||||
$arrError = [];
|
||||
$objUser = new User;
|
||||
$arrUserData = $objUserModel->findUserById($_SESSION['user']['user_id']);
|
||||
$objUser->hydrate($arrUserData);
|
||||
if (!empty($_POST)) {
|
||||
if ($objUserModel->mailExists($_POST['user_mail']) && ($_POST['user_mail'] != $objUser->getMail())) {
|
||||
|
||||
$arrError['user_mail'] = "Ce mail est déjà associé";
|
||||
} else {
|
||||
if ($objUserModel->pseudoExists($_POST['user_pseudo']) && ($_POST['user_pseudo'] != $objUser->getPseudo())){
|
||||
$arrError['user_pseudo'] = "Ce pseudo est déjà utiliser";
|
||||
}else{
|
||||
$objUser->hydrate($_POST);
|
||||
$objUser->setId($_SESSION['user']['user_id']);
|
||||
|
||||
$arrTypeAllowed = array('image/jpeg', 'image/png', 'image/webp');
|
||||
$boolImageOk = true;
|
||||
|
||||
if ($_FILES['image']['error'] != 4) {
|
||||
if (!in_array($_FILES['image']['type'], $arrTypeAllowed)) {
|
||||
$arrError['image'] = "Le type de fichier n'est pas autorisé";
|
||||
} else {
|
||||
switch ($_FILES['image']['error']) {
|
||||
case 0:
|
||||
$strImageName = uniqid() . ".webp";
|
||||
$strOldImg = $objUser->getImage();
|
||||
$objUser->setImage($strImageName);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
$arrError['image'] = "Le fichier est trop volumineux";
|
||||
break;
|
||||
case 3:
|
||||
$arrError['image'] = "Le fichier a été partiellement téléchargé";
|
||||
break;
|
||||
case 6:
|
||||
$arrError['image'] = "Le répertoire temporaire est manquant";
|
||||
break;
|
||||
default:
|
||||
$arrError['image'] = "Erreur sur l'image";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (count($arrError) == 0 && isset($strImageName)) {
|
||||
$strDest = $_ENV['IMG_USER_PATH'] . $strImageName;
|
||||
$strSource = $_FILES['image']['tmp_name'];
|
||||
list($intWidth, $intHeight) = getimagesize($strSource);
|
||||
|
||||
$intDestWidth = 200; $intDestHeight = 200;
|
||||
$fltDestRatio = $intDestWidth / $intDestHeight;
|
||||
$fltSourceRatio = $intWidth / $intHeight;
|
||||
|
||||
if ($fltSourceRatio > $fltDestRatio) {
|
||||
$intCropHeight = $intHeight;
|
||||
$intCropWidth = (int)round($intHeight * $fltDestRatio);
|
||||
$intCropX = (int)(($intWidth - $intCropWidth) / 2);
|
||||
$intCropY = 0;
|
||||
} else {
|
||||
$intCropWidth = $intWidth;
|
||||
$intCropHeight = (int)round($intWidth / $fltDestRatio);
|
||||
$intCropX = 0;
|
||||
$intCropY = (int)(($intHeight - $intCropHeight) / 2);
|
||||
}
|
||||
|
||||
$objDest = imagecreatetruecolor($intDestWidth, $intDestHeight);
|
||||
switch ($_FILES['image']['type']) {
|
||||
case 'image/jpeg': $objSource = imagecreatefromjpeg($strSource); break;
|
||||
case 'image/png': $objSource = imagecreatefrompng($strSource); break;
|
||||
case 'image/webp': $objSource = imagecreatefromwebp($strSource); break;
|
||||
}
|
||||
|
||||
imagecopyresampled($objDest, $objSource, 0, 0, $intCropX, $intCropY, $intDestWidth, $intDestHeight, $intCropWidth, $intCropHeight);
|
||||
$boolImageOk = imagewebp($objDest, $strDest);
|
||||
imagedestroy($objDest);
|
||||
imagedestroy($objSource);
|
||||
}
|
||||
|
||||
|
||||
if ($_POST['delete_image'] === '1') {
|
||||
$strOldImg = $objUser->getImage();
|
||||
if (!empty($strOldImg) && $strOldImg !== 'images.jpg') {
|
||||
$strOldFile = $_ENV['IMG_USER_PATH'] . $strOldImg;
|
||||
if (file_exists($strOldFile)) unlink($strOldFile);
|
||||
}
|
||||
$objUser->setImage('images.jpg');
|
||||
}
|
||||
$boolInsert = $objUserModel->update($objUser);
|
||||
|
||||
if ($boolInsert === true) {
|
||||
if (isset($strOldImg) && !empty($strOldImg) && $strOldImg !== 'images.jpg' && isset($strImageName)) {
|
||||
$strOldFile = $_ENV['IMG_USER_PATH'] . $strOldImg;
|
||||
if (file_exists($strOldFile)) unlink($strOldFile);
|
||||
}
|
||||
$arrNewInfo = $objUserModel->findUserByPseudo($objUser->getPseudo());
|
||||
$_SESSION['user'] = $arrNewInfo;
|
||||
$_SESSION['success'] = "Compte modifier avec succès";
|
||||
header('Location:'.$_ENV['BASE_URL'].'/user/user/'.$objUser->getPseudo());
|
||||
exit;
|
||||
} else {
|
||||
$arrError['global'] = "Erreur lors de l'update";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_arrData["arrError"] = $arrError;
|
||||
$this->_arrData['objUser'] = $objUser;
|
||||
$this->_display("useredit");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/* Smarty version 5.7.0, created on 2026-02-06 08:21:14
|
||||
from 'file:views/home.tpl' */
|
||||
|
||||
/* @var \Smarty\Template $_smarty_tpl */
|
||||
if ($_smarty_tpl->getCompiled()->isFresh($_smarty_tpl, array (
|
||||
'version' => '5.7.0',
|
||||
'unifunc' => 'content_6985a47abcda40_19846426',
|
||||
'has_nocache_code' => false,
|
||||
'file_dependency' =>
|
||||
array (
|
||||
'299f75d7692a19207f23bf5795a2721f507c3fc6' =>
|
||||
array (
|
||||
0 => 'views/home.tpl',
|
||||
1 => 1770365364,
|
||||
2 => 'file',
|
||||
),
|
||||
),
|
||||
'includes' =>
|
||||
array (
|
||||
'file:views/_partial/preview.tpl' => 1,
|
||||
),
|
||||
))) {
|
||||
function content_6985a47abcda40_19846426 (\Smarty\Template $_smarty_tpl) {
|
||||
$_smarty_current_dir = 'C:\\wamp64\\www\\projetphp\\views';
|
||||
$_smarty_tpl->getInheritance()->init($_smarty_tpl, false);
|
||||
?>
|
||||
|
||||
<?php
|
||||
$_smarty_tpl->getInheritance()->instanceBlock($_smarty_tpl, 'Block_3150142016985a47abb6c29_00747518', "content");
|
||||
?>
|
||||
|
||||
<?php }
|
||||
/* {block "content"} */
|
||||
class Block_3150142016985a47abb6c29_00747518 extends \Smarty\Runtime\Block
|
||||
{
|
||||
public function callBlock(\Smarty\Template $_smarty_tpl) {
|
||||
$_smarty_current_dir = 'C:\\wamp64\\www\\projetphp\\views';
|
||||
?>
|
||||
|
||||
<section class="container mt-5 p-5 d-flex flex-column align-items-center text-center">
|
||||
<h1 class="logo">Folliow</h1>
|
||||
<h2>Là où les talents rencontrent leur avenir</h2>
|
||||
<p class="col-6">Une plateforme de portfolio adapté à vos besoins et aux besoins des entreprises.
|
||||
Créer un portfolio réellement pertinent aux exigences du marché et rentrez
|
||||
directement en contact avec les entreprises.</p>
|
||||
</section>
|
||||
|
||||
<section class="container" aria-label="Articles récents">
|
||||
<h2 class="visually-hidden">Les 4 derniers articles</h2>
|
||||
<div class="row mb-2">
|
||||
|
||||
<?php
|
||||
$_from = $_smarty_tpl->getSmarty()->getRuntime('Foreach')->init($_smarty_tpl, $_smarty_tpl->getValue('arrProjectToDisplay'), 'objProject');
|
||||
$foreach0DoElse = true;
|
||||
foreach ($_from ?? [] as $_smarty_tpl->getVariable('objProject')->value) {
|
||||
$foreach0DoElse = false;
|
||||
?>
|
||||
<?php $_smarty_tpl->renderSubTemplate("file:views/_partial/preview.tpl", $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, 0, $_smarty_tpl->cache_lifetime, array(), (int) 0, $_smarty_current_dir);
|
||||
?>
|
||||
<?php
|
||||
}
|
||||
$_smarty_tpl->getSmarty()->getRuntime('Foreach')->restore($_smarty_tpl, 1);?>
|
||||
</section>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
/* {/block "content"} */
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/* Smarty version 5.7.0, created on 2026-02-06 08:21:14
|
||||
from 'file:views/_partial/preview.tpl' */
|
||||
|
||||
/* @var \Smarty\Template $_smarty_tpl */
|
||||
if ($_smarty_tpl->getCompiled()->isFresh($_smarty_tpl, array (
|
||||
'version' => '5.7.0',
|
||||
'unifunc' => 'content_6985a47ad585d5_25749521',
|
||||
'has_nocache_code' => false,
|
||||
'file_dependency' =>
|
||||
array (
|
||||
'c724044e55872f26030b02de6dcd14dc34a20b16' =>
|
||||
array (
|
||||
0 => 'views/_partial/preview.tpl',
|
||||
1 => 1770365554,
|
||||
2 => 'file',
|
||||
),
|
||||
),
|
||||
'includes' =>
|
||||
array (
|
||||
),
|
||||
))) {
|
||||
function content_6985a47ad585d5_25749521 (\Smarty\Template $_smarty_tpl) {
|
||||
$_smarty_current_dir = 'C:\\wamp64\\www\\projetphp\\views\\_partial';
|
||||
?><article class="col-md-3 mb-4">
|
||||
<div class="card h-100 shadow-sm article-card">
|
||||
|
||||
<div class="ratio ratio-16x9">
|
||||
<img src=".<?php echo $_smarty_tpl->getValue('objProject')->getThumbnail();?>
|
||||
"
|
||||
class="w-100 h-100 object-fit-cover"
|
||||
alt=""
|
||||
loading="lazy">
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
|
||||
<img src=".<?php echo $_smarty_tpl->getValue('objProject')->getUser_image();?>
|
||||
"
|
||||
class="rounded-circle flex-shrink-0 mt-2 ml-5"
|
||||
style="width: 48px; height: 48px; object-fit: cover;"
|
||||
alt="Photo de profil">
|
||||
|
||||
<div class="flex-grow-1 card-body p-3">
|
||||
<h3 class="h6 mb-1"><?php echo $_smarty_tpl->getValue('objProject')->getTitle();?>
|
||||
</h3>
|
||||
|
||||
<small class="text-body-secondary d-block mb-1">
|
||||
<time><?php echo $_smarty_tpl->getValue('objProject')->getCreation_date();?>
|
||||
</time>
|
||||
– <?php echo $_smarty_tpl->getValue('objProject')->getCreatorname();?>
|
||||
|
||||
</small>
|
||||
|
||||
<a href="?id=<?php echo $_smarty_tpl->getValue('objProject')->getId();?>
|
||||
"
|
||||
class="stretched-link small">
|
||||
Lire la suite →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</article><?php }
|
||||
}
|
||||
0
database/.gitkeep
Normal file
0
database/.gitkeep
Normal file
253
database/database.sql
Normal file
253
database/database.sql
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
CREATE DATABASE IF NOT EXISTS `projet_folliow`
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
USE `projet_folliow`;
|
||||
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
-- MariaDB dump 10.19 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: projet_folliow
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.11.14-MariaDB-0+deb12u2
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `authorisation`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `authorisation`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `authorisation` (
|
||||
`authorisation_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique des autorisations',
|
||||
`authorisation_name` varchar(30) NOT NULL COMMENT 'Administrateur, modérateur, utilisateur',
|
||||
PRIMARY KEY (`authorisation_id`),
|
||||
UNIQUE KEY `uk_authorisation_name` (`authorisation_name`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `authorisation`
|
||||
--
|
||||
|
||||
LOCK TABLES `authorisation` WRITE;
|
||||
/*!40000 ALTER TABLE `authorisation` DISABLE KEYS */;
|
||||
INSERT INTO `authorisation` VALUES
|
||||
(1,'Administrateur'),
|
||||
(2,'Modérateur'),
|
||||
(3,'Utilisateur');
|
||||
/*!40000 ALTER TABLE `authorisation` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `category`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `category`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `category` (
|
||||
`category_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique des catégorie du projet',
|
||||
`category_name` varchar(150) NOT NULL COMMENT 'Nom de chaque catégorie',
|
||||
`category_parent` int(10) unsigned DEFAULT NULL COMMENT 'ID de la catégorie parente (NULL = catégorie principale)',
|
||||
PRIMARY KEY (`category_id`),
|
||||
UNIQUE KEY `uk_category_name_parent` (`category_name`,`category_parent`),
|
||||
KEY `fk_category_parent` (`category_parent`),
|
||||
CONSTRAINT `fk_category_parent` FOREIGN KEY (`category_parent`) REFERENCES `category` (`category_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `category`
|
||||
--
|
||||
|
||||
LOCK TABLES `category` WRITE;
|
||||
/*!40000 ALTER TABLE `category` DISABLE KEYS */;
|
||||
INSERT INTO `category` VALUES
|
||||
(32,'3D',4),
|
||||
(34,'Aquarelle',4),
|
||||
(5,'Architecture',NULL),
|
||||
(36,'Architecture d\'Intérieur',5),
|
||||
(35,'Architecture Moderne',5),
|
||||
(24,'Backend',2),
|
||||
(33,'Bande Dessinée',4),
|
||||
(1,'Design',NULL),
|
||||
(2,'Développement Web',NULL),
|
||||
(31,'Digital Art',4),
|
||||
(30,'Événementiel',3),
|
||||
(23,'Frontend',2),
|
||||
(25,'Full-Stack',2),
|
||||
(4,'Illustration',NULL),
|
||||
(20,'Logo & Identité Visuelle',1),
|
||||
(39,'Marketing',NULL),
|
||||
(46,'Marketing',NULL),
|
||||
(26,'Mobile',2),
|
||||
(21,'Motion Design',1),
|
||||
(38,'Patrimoine',5),
|
||||
(28,'Paysage',3),
|
||||
(3,'Photographie',NULL),
|
||||
(27,'Portrait',3),
|
||||
(22,'Print Design',1),
|
||||
(40,'Rédaction',NULL),
|
||||
(47,'Rédaction',NULL),
|
||||
(29,'Sport',3),
|
||||
(19,'UI/UX Design',1),
|
||||
(37,'Urbanisme',5),
|
||||
(41,'Vidéo',NULL),
|
||||
(48,'Vidéo',NULL);
|
||||
/*!40000 ALTER TABLE `category` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `image`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `image`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `image` (
|
||||
`image_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'identifiant unique des images',
|
||||
`image_name` varchar(150) NOT NULL COMMENT 'Chemin de l''image enregistrée',
|
||||
`image_alt` varchar(255) DEFAULT NULL COMMENT 'Alt de l''image',
|
||||
`image_status` varchar(50) NOT NULL DEFAULT 'en_attente' COMMENT 'Statut de modération de l''image',
|
||||
`image_project` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant du projet où se trouve l''image',
|
||||
PRIMARY KEY (`image_id`),
|
||||
KEY `fk_image_project` (`image_project`),
|
||||
CONSTRAINT `fk_image_project` FOREIGN KEY (`image_project`) REFERENCES `project` (`project_id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `chk_image_status` CHECK (`image_status` in ('en_attente','approuvé','rejeté'))
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `image`
|
||||
--
|
||||
|
||||
LOCK TABLES `image` WRITE;
|
||||
/*!40000 ALTER TABLE `image` DISABLE KEYS */;
|
||||
INSERT INTO `image` VALUES
|
||||
(1,'/uploads/projects/ecommerce-01.jpg','Page d\'accueil du site e-commerce refondu','approuvé',1),
|
||||
(2,'/uploads/projects/ecommerce-02.jpg','Interface mobile du processus de commande','approuvé',1),
|
||||
(3,'/uploads/projects/taskmanager-01.jpg','Vue kanban de l\'application','approuvé',2),
|
||||
(4,'/uploads/projects/urban-01.jpg','Architecture moderne à La Défense','en_attente',3);
|
||||
/*!40000 ALTER TABLE `image` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `project`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `project`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `project` (
|
||||
`project_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique du projet',
|
||||
`project_title` varchar(150) NOT NULL COMMENT 'Titre du projet',
|
||||
`project_description` text DEFAULT NULL COMMENT 'Description du projet',
|
||||
`project_thumbnail` varchar(150) DEFAULT NULL COMMENT 'Image miniature du projet',
|
||||
`project_content` text DEFAULT NULL COMMENT 'Contenu du projet',
|
||||
`project_creation_date` date NOT NULL COMMENT 'Date de création du projet',
|
||||
`project_status` varchar(30) NOT NULL DEFAULT 'brouillon' COMMENT 'Statut du projet : brouillon, publié, en_attente',
|
||||
`project_user` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant de l''user',
|
||||
`project_category` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant de la catégorie',
|
||||
|
||||
PRIMARY KEY (`project_id`),
|
||||
KEY `fk_project_user` (`project_user`),
|
||||
KEY `fk_project_category` (`project_category`),
|
||||
KEY `idx_project_status` (`project_status`),
|
||||
KEY `idx_project_creation_date` (`project_creation_date`),
|
||||
|
||||
CONSTRAINT `fk_project_category`
|
||||
FOREIGN KEY (`project_category`)
|
||||
REFERENCES `category` (`category_id`)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE,
|
||||
|
||||
CONSTRAINT `fk_project_user`
|
||||
FOREIGN KEY (`project_user`)
|
||||
REFERENCES `users` (`user_id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB
|
||||
DEFAULT CHARSET=utf8mb4
|
||||
COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `project`
|
||||
--
|
||||
|
||||
LOCK TABLES `project` WRITE;
|
||||
/*!40000 ALTER TABLE `project` DISABLE KEYS */;
|
||||
INSERT INTO `project` VALUES
|
||||
(1,'Refonte du site e-commerce','Refonte complète de l\'interface utilisateur d\'une boutique en ligne avec focus sur l\'expérience mobile','/uploads/projects/ecommerce-thumb.jpg','Ce projet visait à moderniser entièrement l\'interface d\'un site e-commerce existant. Les principales améliorations incluent une navigation simplifiée, un processus de commande optimisé et une interface responsive.','2025-12-01','publié',1,1),
|
||||
(2,'Application de gestion de tâches','Développement d\'une application web pour la gestion collaborative de projets','/uploads/projects/taskmanager-thumb.jpg','Application web développée en React et Node.js permettant aux équipes de gérer leurs projets de manière collaborative. Fonctionnalités : kanban, calendrier, notifications en temps réel.','2025-12-15','publié',2,2),
|
||||
(3,'Série photo urbaine','Collection de photographies capturant l\'architecture moderne de Paris','/uploads/projects/urban-thumb.jpg','Série de 30 photographies prises dans différents quartiers de Paris, mettant en valeur le contraste entre architecture classique et moderne. Travail sur la lumière naturelle et les perspectives.','2026-01-05','en_attente',3,3);
|
||||
/*!40000 ALTER TABLE `project` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `users`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `users` (
|
||||
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique de l''utilisateur',
|
||||
`user_name` varchar(100) NOT NULL COMMENT 'Nom de l''utilisateur',
|
||||
`user_firstname` varchar(100) NOT NULL COMMENT 'Prénom de l''utilisateur',
|
||||
`user_pseudo` varchar(30) NOT NULL COMMENT 'Pseudo de l''utilisateur',
|
||||
`user_image` varchar(150) DEFAULT NULL COMMENT 'Photo de profil de l''utilisateur',
|
||||
`user_mail` varchar(100) NOT NULL COMMENT 'Mail de l''utilisateur',
|
||||
`user_password` varchar(255) NOT NULL COMMENT 'Mot de passe de l''utilisateur',
|
||||
`user_phone` varchar(20) DEFAULT NULL COMMENT 'Téléphone de l''utilisateur',
|
||||
`user_work` varchar(50) DEFAULT NULL COMMENT 'Profession de l''utilisateur',
|
||||
`user_birth` date DEFAULT NULL COMMENT 'Date de naissance de l''utilisateur',
|
||||
`user_location` varchar(150) DEFAULT NULL COMMENT 'Localisation de l''utilisateur',
|
||||
`user_description` varchar(255) DEFAULT NULL COMMENT 'Phrase d''accroche de l''utilisateur',
|
||||
`user_account_creation` datetime NOT NULL DEFAULT current_timestamp() COMMENT 'Date et heure de création du compte',
|
||||
`user_status` int(10) unsigned DEFAULT NULL COMMENT 'identifiant de niveau d''autorisation',
|
||||
PRIMARY KEY (`user_id`),
|
||||
UNIQUE KEY `uk_user_mail` (`user_mail`),
|
||||
UNIQUE KEY `uk_user_pseudo` (`user_pseudo`),
|
||||
KEY `fk_users_authorisation` (`user_status`),
|
||||
KEY `idx_user_mail` (`user_mail`),
|
||||
CONSTRAINT `fk_users_authorisation` FOREIGN KEY (`user_status`) REFERENCES `authorisation` (`authorisation_id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `users`
|
||||
--
|
||||
|
||||
LOCK TABLES `users` WRITE;
|
||||
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||
INSERT INTO `users` VALUES
|
||||
(1,'Dupont','Marie','marie_design','/uploads/users/marie.jpg','marie.dupont@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0612345678','Designer UI/UX','1995-03-15','Paris, France','Passionnée de design et d\'expérience utilisateur','2024-01-15 10:30:00',1),
|
||||
(2,'Martin','Thomas','thomas_dev','/uploads/users/thomas.jpg','thomas.martin@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0623456789','Développeur Full-Stack','1992-07-22','Lyon, France','Créateur d\'applications web modernes','2024-02-20 14:45:00',2),
|
||||
(3,'Dubois','Sophie','sophie_photo','/uploads/users/sophie.jpg','sophie.dubois@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0634567890','Photographe','1998-11-08','Marseille, France','Capturer l\'instant présent est ma passion','2024-03-10 09:15:00',3);
|
||||
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2026-01-09 17:33:08
|
||||
53
entities/Authorisation.php
Normal file
53
entities/Authorisation.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
namespace Entities;
|
||||
|
||||
|
||||
/**
|
||||
* Classe d'un objet Authorisation
|
||||
* @author Laura
|
||||
*/
|
||||
class Authorisation extends Mother{
|
||||
|
||||
private int $_id;
|
||||
private string $_name = '';
|
||||
|
||||
/**
|
||||
* le constructeur de la table authorisation
|
||||
*/
|
||||
public function __construct(){
|
||||
$this->_prefix = 'authorisation_';
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'id du statut d'un utilisateur
|
||||
* @return int l'id du statut
|
||||
*/
|
||||
public function getId():int{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id du statut d'un utilisateur
|
||||
* @param int le nouvelle id du statut
|
||||
*/
|
||||
public function setId(int $id){
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'intitulé du statut d'un utilisateur
|
||||
* @return string l'intitulé du statut
|
||||
*/
|
||||
public function getName():string{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'intitulé du statut d'un utilisateur
|
||||
* @return string le nouvel intitulé du statut
|
||||
*/
|
||||
public function setName(string $name){
|
||||
$this->_name = $name;
|
||||
}
|
||||
|
||||
}
|
||||
81
entities/Category.php
Normal file
81
entities/Category.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Entities;
|
||||
|
||||
/**
|
||||
* Classe d'un objet Category
|
||||
* @author Laura
|
||||
*/
|
||||
|
||||
class Category extends Mother{
|
||||
|
||||
private ?int $_id = null;
|
||||
protected string $_name = '';
|
||||
protected ?int $_parent = null;
|
||||
|
||||
/**
|
||||
* le constructeur de la table category
|
||||
*/
|
||||
public function __construct(){
|
||||
$this->_prefix = 'category_';
|
||||
}
|
||||
|
||||
// Méthode Getter et Setter
|
||||
|
||||
/**
|
||||
* Récuperation de l'id de la catégorie
|
||||
* @return int l'id de la catégorie
|
||||
*/
|
||||
public function getId(): ?int{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id de la catégorie
|
||||
* @param int le nouvelle id
|
||||
*/
|
||||
public function setId($id){
|
||||
if (empty($id)) {
|
||||
$this->_id = null;
|
||||
} else {
|
||||
$this->_id = (int) $id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du nom de la catégorie
|
||||
* @return string nom de la catégorie
|
||||
*/
|
||||
public function getName(){
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom de la catégorie
|
||||
* @param string le nouveau nom de la catégorie
|
||||
*/
|
||||
public function setName($name){
|
||||
$this->_name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du nom du parent de la catégorie
|
||||
* @return int nom de la catégorie
|
||||
*/
|
||||
public function getParent():?int{
|
||||
return $this->_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom du parent de la catégorie
|
||||
* @param int le nouveau nom de la catégorie
|
||||
*/
|
||||
public function setParent($parent){
|
||||
if (empty($parent)) {
|
||||
$this->_parent = null;
|
||||
} else {
|
||||
$this->_parent = (int) $parent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
92
entities/Image.php
Normal file
92
entities/Image.php
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Entities;
|
||||
|
||||
|
||||
/**
|
||||
* Classe d'un objet Projet
|
||||
* @author Laura
|
||||
*/
|
||||
class Image extends Mother{
|
||||
|
||||
private int $_id;
|
||||
private string $_name = '';
|
||||
private string $_alt = '';
|
||||
private string $_status ='';
|
||||
private int $_project = 0;
|
||||
|
||||
|
||||
/**
|
||||
* le constructeur de la table image
|
||||
*/
|
||||
public function __construct(){
|
||||
$this->_prefix = 'image_';
|
||||
}
|
||||
|
||||
// Méthode Getter et Setter
|
||||
|
||||
/**
|
||||
* Récuperation de l'id de l'image
|
||||
* @return int l'id de l'image
|
||||
*/
|
||||
public function getId():int{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id de l'image
|
||||
* @param int le nouvelle id
|
||||
*/
|
||||
public function setId($id){
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du nom de l'image
|
||||
* @return string nom de l'image
|
||||
*/
|
||||
public function getName(){
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom de l'image
|
||||
* @param string le nouveau nom de l'image
|
||||
*/
|
||||
public function setName($name){
|
||||
$this->_name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'alt
|
||||
* @return string contenu de l'alt
|
||||
*/
|
||||
public function getAlt(){
|
||||
return $this->_alt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'alt
|
||||
* @param string le nouveau contenu de l'alt
|
||||
*/
|
||||
public function setAlt($alt){
|
||||
$this->_alt = $alt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du statut de la photo
|
||||
* @return string du statut
|
||||
*/
|
||||
public function getStatus(){
|
||||
return $this->_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du statut de la photo
|
||||
* @param string le nouveau statut de la photo
|
||||
*/
|
||||
public function setStatus($status){
|
||||
$this->_status = $status;
|
||||
}
|
||||
|
||||
}
|
||||
31
entities/Mother.php
Normal file
31
entities/Mother.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Entities;
|
||||
|
||||
/**
|
||||
* Classe d'un Mere de tout objet
|
||||
* @author Yass & Laura
|
||||
*/
|
||||
|
||||
class Mother{
|
||||
|
||||
protected string $_prefix = '';
|
||||
|
||||
|
||||
public function hydrate(array $arrData){
|
||||
foreach($arrData as $key=>$value){
|
||||
$strMethodName = "set".ucfirst(str_replace($this->_prefix,'',$key));
|
||||
if (method_exists($this,$strMethodName)){
|
||||
$this->$strMethodName($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function nettoyer(string $strText){
|
||||
$strText = trim($strText);
|
||||
$strText = str_replace("<script>", "", $strText);
|
||||
$strText = str_replace("</script>", "", $strText);
|
||||
return $strText;
|
||||
}
|
||||
|
||||
}
|
||||
240
entities/Project.php
Normal file
240
entities/Project.php
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
<?php
|
||||
namespace Entities;
|
||||
|
||||
use \DateTime;
|
||||
use \IntlDateFormatter;
|
||||
|
||||
/**
|
||||
* Classe d'un objet Projet
|
||||
* @author Yass
|
||||
*/
|
||||
|
||||
class Project extends Mother{
|
||||
|
||||
private ?int $_id = null;
|
||||
private string $_title = "";
|
||||
private string $_description = "";
|
||||
private ?string $_thumbnail = null;
|
||||
private string $_content = "";
|
||||
private string $_creation_date;
|
||||
private string $_status = "en_attente";
|
||||
private int $_user;
|
||||
private int $_category = 0;
|
||||
private string $_creatorname;
|
||||
private ?string $_user_image = null;
|
||||
private ?string $_project_deleted_at = null;
|
||||
|
||||
|
||||
/**
|
||||
* le constructeur de la table project
|
||||
*/
|
||||
public function __construct(){
|
||||
$this->_prefix = 'project_';
|
||||
}
|
||||
|
||||
// Méthode Getter et Setter
|
||||
|
||||
/**
|
||||
* Récuperation de l'id du projet
|
||||
* @return int l'id du projet
|
||||
*/
|
||||
public function getId():?int{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id du projet
|
||||
* @param int le nouvelle id
|
||||
*/
|
||||
public function setId(int $id){
|
||||
$this->_id = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du titre
|
||||
* @return string tite du projet
|
||||
*/
|
||||
public function getTitle(){
|
||||
return $this->nettoyer($this->_title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du titre
|
||||
* @param string le nouveau titre
|
||||
*/
|
||||
public function setTitle($title){
|
||||
$this->_title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la description
|
||||
* @return string description du projet
|
||||
*/
|
||||
public function getDescription(){
|
||||
return $this->nettoyer($this->_description) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la description
|
||||
* @param string la nouvelle description
|
||||
*/
|
||||
public function setDescription($description){
|
||||
$this->_description = $description;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Récuperation de l'image
|
||||
* @return string chemin vers l'image
|
||||
*/
|
||||
public function getThumbnail(){
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'image
|
||||
* @param string chemin vers nouvelle image
|
||||
*/
|
||||
public function setThumbnail($thumbnail){
|
||||
$this->_thumbnail = $thumbnail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du contenu
|
||||
* @return string contenu du projet
|
||||
*/
|
||||
public function getContent(){
|
||||
return $this->nettoyer($this->_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du contenu
|
||||
* @param string le nouveau contenu
|
||||
*/
|
||||
public function setContent($content){
|
||||
$this->_content = $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération de la date de création
|
||||
* @param string lang de formatage de la date (par défaut = "fr_FR")
|
||||
* @return string date de création formatter
|
||||
*/
|
||||
public function getCreation_date(string $strFormat = "fr_FR"){
|
||||
$objDate = new DateTime($this->_creation_date);
|
||||
|
||||
$objDateFormatter = new IntlDateFormatter(
|
||||
$strFormat,
|
||||
IntlDateFormatter::LONG,
|
||||
IntlDateFormatter::NONE,
|
||||
|
||||
);
|
||||
$strFormat = $objDateFormatter->format($objDate);
|
||||
return $strFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la date de création
|
||||
* @param string la nouvelle date de création
|
||||
*/
|
||||
public function setCreation_date($creation_date){
|
||||
$this->_creation_date = $creation_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération du statut
|
||||
* @return string statut
|
||||
*/
|
||||
public function getStatus(){
|
||||
return $this->_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du statut
|
||||
* @param string le nouveau statut
|
||||
*/
|
||||
public function setStatus($status){
|
||||
$this->_status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération de l'utilisateur
|
||||
* @return int id de l'utilisateur
|
||||
*/
|
||||
public function getUser_id(){
|
||||
return $this->_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'utilisateur
|
||||
* @param int id de l'utilisateur
|
||||
*/
|
||||
public function setUser_id($user){
|
||||
$this->_user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération de la catégorie
|
||||
* @return int id de la catégorie
|
||||
*/
|
||||
public function getCategory():?int{
|
||||
return $this->_category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la catégorie
|
||||
* @param int id de la catégorie
|
||||
*/
|
||||
public function setCategory($category){
|
||||
$this->_category = $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération du nom du créateur
|
||||
* @return string nom du créateur
|
||||
*/
|
||||
public function getCreatorName(){
|
||||
return $this->_creatorname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom du créateur
|
||||
* @param string le nom du créateur
|
||||
*/
|
||||
public function setCreatorName($creatorname){
|
||||
$this->_creatorname = $creatorname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération du chemin photo profil
|
||||
* @return string nom du chemin photo profil
|
||||
*/
|
||||
public function getUser_image(){
|
||||
return $this->_user_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du chemin photo profil
|
||||
* @param string chemin photo profil
|
||||
*/
|
||||
public function setUser_image($user_image){
|
||||
$this->_user_image = $user_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupération de la date du projet supprimer
|
||||
* @return string date du projet
|
||||
*/
|
||||
public function getDeleted_at(){
|
||||
return $this->_project_deleted_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la date de suppression de projet
|
||||
* @param string date du projet
|
||||
*/
|
||||
public function setDeleted_at($project_deleted_at){
|
||||
$this->_project_deleted_at = $project_deleted_at;
|
||||
}
|
||||
}
|
||||
263
entities/User.php
Normal file
263
entities/User.php
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
<?php
|
||||
namespace Entities;
|
||||
|
||||
|
||||
/**
|
||||
* Classe d'un objet User
|
||||
* @author Besnik & Laura
|
||||
*/
|
||||
class User extends Mother{
|
||||
private int $_id;
|
||||
private string $_name = '';
|
||||
private string $_firstname = '';
|
||||
private string $_pseudo = '';
|
||||
private ?string $_image = null;
|
||||
private string $_mail = '';
|
||||
private string $_pwd;
|
||||
private string $_phone = '';
|
||||
private string $_work = '';
|
||||
private ?string $_birth = null;
|
||||
private string $_location = '';
|
||||
private string $_description = '';
|
||||
private string $_account_creation = '';
|
||||
private int $_status;
|
||||
|
||||
/**
|
||||
* le constructeur de la table user
|
||||
*/
|
||||
public function __construct(){
|
||||
$this->_prefix = 'user_';
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'id d'un utilisateur
|
||||
* @return int l'id de l'utilisateur
|
||||
*/
|
||||
public function getId():int{
|
||||
return $this->_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id d'un utilisateur
|
||||
* @param int le nouvelle id de l'utilisateur
|
||||
*/
|
||||
public function setId(int $id){
|
||||
$this->_id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du nom d'un utilisateur
|
||||
* @return string le nom de l'utilisateur
|
||||
*/
|
||||
public function getName():string{
|
||||
return $this->nettoyer($this->_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom d'un utilisateur
|
||||
* @return string le nouveau nom de l'utilisateur
|
||||
*/
|
||||
public function setName(string $name){
|
||||
$this->_name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du prénom d'un utilisateur
|
||||
* @return string le prénom de l'utilisateur
|
||||
*/
|
||||
public function getFirstname():string{
|
||||
return $this->nettoyer($this->_firstname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du prénom d'un utilisateur
|
||||
* @return string le nouveau prénom de l'utilisateur
|
||||
*/
|
||||
public function setFirstname(string $firstname){
|
||||
$this->_firstname = $firstname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du pseudo unique d'un utilisateur
|
||||
* @return string le pseudo de l'utilisateur
|
||||
*/
|
||||
public function getPseudo():string{
|
||||
return $this->nettoyer($this->_pseudo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du pseudo unique d'un utilisateur
|
||||
* @return string le nouveau pseudo de l'utilisateur
|
||||
*/
|
||||
public function setPseudo(string $pseudo){
|
||||
$this->_pseudo = str_replace(" ","_",$pseudo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du nom de l'image d'un utilisateur
|
||||
* @return string du nom de l'image de l'utilisateur
|
||||
*/
|
||||
public function getImage():?string{
|
||||
return $this->_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du nom de l'image d'un utilisateur
|
||||
* @return string le nouveau nom de l'image de l'utilisateur
|
||||
*/
|
||||
public function setImage(?string $image){
|
||||
$this->_image = $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'email d'un utilisateur
|
||||
* @return string l'email de l'utilisateur
|
||||
*/
|
||||
public function getMail():string{
|
||||
return $this->_mail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'email d'un utilisateur
|
||||
* @return string le nouvel email de l'utilisateur
|
||||
*/
|
||||
public function setMail(string $mail){
|
||||
$this->_mail = strtolower($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du mot de passe d'un utilisateur
|
||||
* @return string le mot de passe de l'utilisateur
|
||||
*/
|
||||
public function getPwd():string{
|
||||
return $this->_pwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du mot de passe haché d'un utilisateur
|
||||
* @return string le mot de passe haché de l'utilisateur
|
||||
*/
|
||||
public function getPwdHash():string{
|
||||
return password_hash($this->_pwd, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du mot de passe d'un utilisateur
|
||||
* @return string le nouveau mot de passe de l'utilisateur
|
||||
*/
|
||||
public function setPwd(string $pwd){
|
||||
$this->_pwd = $pwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation du numéro de téléphone d'un utilisateur
|
||||
* @return string le numéro de téléphone de l'utilisateur
|
||||
*/
|
||||
public function getPhone():string{
|
||||
return $this->nettoyer($this->_phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour du numéro de téléphone d'un utilisateur
|
||||
* @return string le nouveau numéro de téléphone de l'utilisateur
|
||||
*/
|
||||
public function setPhone(string $phone){
|
||||
$this->_phone = $phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la profession d'un utilisateur
|
||||
* @return string la profession de l'utilisateur
|
||||
*/
|
||||
public function getWork():string{
|
||||
return $this->nettoyer($this->_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la profession d'un utilisateur
|
||||
* @return string la nouvelle profession de l'utilisateur
|
||||
*/
|
||||
public function setWork(string $work){
|
||||
$this->_work = $work;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la date de naissance d'un utilisateur
|
||||
* @return string la date de naissance de l'utilisateur
|
||||
*/
|
||||
public function getBirth():?string{
|
||||
return $this->_birth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la date de naissance d'un utilisateur
|
||||
* @return string la nouvelle date de naissance de l'utilisateur
|
||||
*/
|
||||
public function setBirth(?string $birth){
|
||||
$this->_birth = $birth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la localisation d'un utilisateur
|
||||
* @return string la localisation de l'utilisateur
|
||||
*/
|
||||
public function getLocation():string{
|
||||
return $this->nettoyer($this->_location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la localisation d'un utilisateur
|
||||
* @return string la nouvelle localisation de l'utilisateur
|
||||
*/
|
||||
public function setLocation(string $location){
|
||||
$this->_location = $location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la description d'un utilisateur
|
||||
* @return string la description de l'utilisateur
|
||||
*/
|
||||
public function getDescription():string{
|
||||
return $this->nettoyer($this->_description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la description d'un utilisateur
|
||||
* @return string la nouvelle description de l'utilisateur
|
||||
*/
|
||||
public function setDescription(string $description){
|
||||
$this->_description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de la date de création d'un utilisateur
|
||||
* @return string la date de création de l'utilisateur
|
||||
*/
|
||||
public function getAccountCreation():string{
|
||||
return $this->_account_creation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la date de création d'un utilisateur
|
||||
* @return string la nouvelle date de création de l'utilisateur
|
||||
*/
|
||||
public function setAccountCreation(string $account_creation){
|
||||
$this->_account_creation = $account_creation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récuperation de l'id du statut d'un utilisateur
|
||||
* @return int l'id du statut de l'utilisateur
|
||||
*/
|
||||
public function getStatus():int{
|
||||
return $this->_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de l'id du statut d'un utilisateur
|
||||
* @return int le nouvel id du statut de l'utilisateur
|
||||
*/
|
||||
public function setStatus(int $status){
|
||||
$this->_status = $status;
|
||||
}
|
||||
}
|
||||
32
index.php
Normal file
32
index.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
session_start();
|
||||
require(__DIR__ . "/vendor/autoload.php");
|
||||
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->safeLoad();
|
||||
|
||||
$strCtrl = preg_replace('/[^a-zA-Z]/', '', $_GET['ctrl'] ?? 'project');
|
||||
$strMethod = preg_replace('/[^a-zA-Z_]/', '', $_GET['action'] ?? 'home');
|
||||
|
||||
$strClassName = "Controllers\\" . ucfirst($strCtrl) . "Ctrl";
|
||||
|
||||
$boolError = false;
|
||||
|
||||
if (class_exists($strClassName)) {
|
||||
$objController = new $strClassName();
|
||||
|
||||
if (method_exists($objController, $strMethod)) {
|
||||
$objController->$strMethod();
|
||||
} else {
|
||||
$boolError = true;
|
||||
}
|
||||
} else {
|
||||
$boolError = true;
|
||||
}
|
||||
|
||||
if ($boolError) {
|
||||
http_response_code(404); // ← important pour le SEO et les navigateurs
|
||||
$error = new \Controllers\ErrorCtrl();
|
||||
$error->error_404(); // ← pas de "return", juste l'appel
|
||||
exit; // ← stoppe l'exécution après l'affichage
|
||||
}
|
||||
0
models/.gitkeep
Normal file
0
models/.gitkeep
Normal file
30
models/AuthorisationModel.php
Normal file
30
models/AuthorisationModel.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
use \PDO;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Traitement des requêtes pour le status de l'utilisateur
|
||||
* @author : Laura
|
||||
*/
|
||||
|
||||
class AuthorisationModel extends MotherModel{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* fonction de récupération des infos d'authorisation
|
||||
* @return array
|
||||
*/
|
||||
public function findAllAuthorisation():array{
|
||||
|
||||
$strRq = "SELECT *
|
||||
FROM authorisation";
|
||||
|
||||
return $this->_db->query($strRq)->fetchAll();
|
||||
}
|
||||
|
||||
}
|
||||
93
models/CategoryModel.php
Normal file
93
models/CategoryModel.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
use \PDO;
|
||||
|
||||
|
||||
/**
|
||||
* Traitement des requêtes pour les catégories
|
||||
* @author : Laura
|
||||
*/
|
||||
|
||||
class CategoryModel extends MotherModel{
|
||||
|
||||
/**
|
||||
* Fonction de récupération des catégories
|
||||
* @param int $intLimit
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function findAllCategory(int $intLimit=0):array{
|
||||
|
||||
$strRq = "SELECT category.*
|
||||
FROM category";
|
||||
|
||||
if ($intLimit > 0){
|
||||
$strRq .= " LIMIT :limit";
|
||||
}
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
if ($intLimit > 0){
|
||||
$rqPrep->bindValue(":limit", $intLimit, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$rqPrep->execute();
|
||||
|
||||
return $rqPrep->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* fonction d'insertion d'une nouvelle catégorie dans la bdd
|
||||
* @param object $objCategory l'objet catégorie
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function insertCategory(object $objCategory):bool{
|
||||
|
||||
$strRq = "INSERT INTO category (category_name)
|
||||
VALUES (:name)";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":name", $objCategory->getName(), PDO::PARAM_STR);
|
||||
|
||||
return $rqPrep->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fonction de suppression d'une catégorie dans la bdd
|
||||
* @param object $objCategory l'objet catégorie
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function deleteCategory(object $objCategory):bool{
|
||||
|
||||
$strRq = "DELETE FROM category
|
||||
WHERE category_id= :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":id", $objCategory->getId(), PDO::PARAM_INT);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* fonction de modification d'une catégorie dans la bdd
|
||||
* @param object $objCategory l'objet catégorie
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function editCategory(object $objCategory):bool{
|
||||
|
||||
$strRq = "UPDATE category
|
||||
SET category_name = :name
|
||||
WHERE category_id = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":id", $objCategory->getId(), PDO::PARAM_INT);
|
||||
$rqPrep->bindValue(":name", $objCategory->getName(), PDO::PARAM_STR);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
}
|
||||
|
||||
39
models/ImageModel.php
Normal file
39
models/ImageModel.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
|
||||
use \PDO;
|
||||
|
||||
|
||||
/**
|
||||
* Traitement de la requête pour les images
|
||||
* @author Laura
|
||||
*/
|
||||
|
||||
class ImageModel extends MotherModel{
|
||||
|
||||
/**
|
||||
* Fonction de récupération des images
|
||||
* @param int $intLimit
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function findAllImage(int $intLimit=0):array{
|
||||
|
||||
$strRq = "SELECT image.*
|
||||
FROM image";
|
||||
|
||||
if ($intLimit > 0){
|
||||
$strRq .= " LIMIT :limit";
|
||||
}
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
if ($intLimit > 0){
|
||||
$rqPrep->bindValue(":limit", $intLimit, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$rqPrep->execute();
|
||||
|
||||
return $rqPrep->fetchAll();
|
||||
}
|
||||
}
|
||||
24
models/MotherModel.php
Normal file
24
models/MotherModel.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
|
||||
use \PDO;
|
||||
|
||||
class MotherModel {
|
||||
|
||||
protected $_db;
|
||||
|
||||
public function __construct(){
|
||||
try{
|
||||
$this->_db = new PDO(
|
||||
"mysql:host=".$_ENV['DB_HOSTNAME'].";dbname=".$_ENV['DB_DATABASE'],
|
||||
$_ENV['DB_USERNAME'],
|
||||
$_ENV['DB_PASSWORD'],
|
||||
array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC)
|
||||
);
|
||||
$this->_db->exec("SET CHARACTER SET utf8");
|
||||
$this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch(PDOException$e) {
|
||||
echo "Échec : " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
320
models/ProjectModel.php
Normal file
320
models/ProjectModel.php
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
|
||||
use \PDO;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Traitement des requêtes pour les projets
|
||||
* @author Laura & Guillaume
|
||||
*/
|
||||
|
||||
class ProjectModel extends MotherModel{
|
||||
|
||||
|
||||
/**
|
||||
* Fonction de recherche des projets
|
||||
* @param type string, int et bool
|
||||
* @return array
|
||||
*/
|
||||
public function findAll(int $intLimit=0, string $strKeywords='', int $intAuthor=0,
|
||||
int $intPeriod=0, string $strDate='', string $strStartDate='',
|
||||
string $strEndDate='', int $intCategory=0, bool $boolOlderThan6Months=false): array {
|
||||
|
||||
|
||||
$strRq = "SELECT project.*,
|
||||
user_pseudo AS 'project_creatorname',
|
||||
user_image
|
||||
FROM project
|
||||
INNER JOIN users ON user_id = project_user_id
|
||||
WHERE 1=1";
|
||||
|
||||
if ($strKeywords != '') {
|
||||
$strRq .= " AND (project_title LIKE :keywords OR project_content LIKE :keywords)";
|
||||
}
|
||||
|
||||
if ($intAuthor > 0){
|
||||
$strRq .= " AND project_user_id = :author";
|
||||
}
|
||||
|
||||
if ($intCategory > 0){
|
||||
$strRq .= " AND project_category = :category";
|
||||
}
|
||||
|
||||
if ($boolOlderThan6Months === true) {
|
||||
$strRq .= " AND project_creation_date <= DATE_SUB(NOW(), INTERVAL 6 MONTH)";
|
||||
}
|
||||
|
||||
if ($intPeriod == 0){
|
||||
if ($strDate != ''){
|
||||
$strRq .= " AND project_creation_date = :date_exacte";
|
||||
}
|
||||
} else {
|
||||
if ($strStartDate != '' && $strEndDate != ''){
|
||||
$strRq .= " AND project_creation_date BETWEEN :date_debut AND :date_fin";
|
||||
} else {
|
||||
if ($strStartDate != ''){
|
||||
$strRq .= " AND project_creation_date >= :date_debut";
|
||||
} else if ($strEndDate != ''){
|
||||
$strRq .= " AND project_creation_date <= :date_fin";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$strRq .= " ORDER BY project_creation_date DESC";
|
||||
|
||||
if ($intLimit > 0){
|
||||
$strRq .= " LIMIT :limit";
|
||||
}
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
if ($strKeywords != '') {
|
||||
$rqPrep->bindValue(':keywords', '%' . $strKeywords . '%', PDO::PARAM_STR);
|
||||
}
|
||||
if ($intAuthor > 0){
|
||||
$rqPrep->bindValue(':author', $intAuthor, PDO::PARAM_INT);
|
||||
}
|
||||
if ($intCategory > 0){
|
||||
$rqPrep->bindValue(':category', $intCategory, PDO::PARAM_INT);
|
||||
}
|
||||
if ($intPeriod == 0){
|
||||
if ($strDate != ''){
|
||||
$rqPrep->bindValue(':date_exacte', $strDate, PDO::PARAM_STR);
|
||||
}
|
||||
} else {
|
||||
if ($strStartDate != '' && $strEndDate != ''){
|
||||
$rqPrep->bindValue(':date_debut', $strStartDate, PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':date_fin', $strEndDate, PDO::PARAM_STR);
|
||||
} else {
|
||||
if ($strStartDate != ''){
|
||||
$rqPrep->bindValue(':date_debut', $strStartDate, PDO::PARAM_STR);
|
||||
} else if ($strEndDate != ''){
|
||||
$rqPrep->bindValue(':date_fin', $strEndDate, PDO::PARAM_STR);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($intLimit > 0){
|
||||
$rqPrep->bindValue(':limit', $intLimit, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
$rqPrep->execute();
|
||||
return $rqPrep->fetchAll();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fonction d'insertion d'un nouveau projet dans la bdd
|
||||
* @param object $objProject l'objet projet
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function insert(object $objProject):bool{
|
||||
|
||||
$strRq = "INSERT INTO project (project_title, project_description, project_thumbnail, project_content, project_status, project_creation_date, project_user_id, project_category)
|
||||
VALUES (:title, :description, :thumbnail, :content, :status, DATE(NOW()), :project_user_id, :project_category)";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":title", $objProject->getTitle(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":description", $objProject->getDescription(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":thumbnail", $objProject->getThumbnail(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":content", $objProject->getContent(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":status", $objProject->getStatus(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":project_user_id", $objProject->getUser_id(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":project_category", $objProject->getCategory(), PDO::PARAM_STR);
|
||||
|
||||
$boolOk = $rqPrep->execute();
|
||||
|
||||
if ($boolOk) {
|
||||
$objProject->setId($this->_db->lastInsertId());
|
||||
}
|
||||
|
||||
return $boolOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de recherche d'un seul projet
|
||||
* @param int $intId
|
||||
* @return array|bool
|
||||
*/
|
||||
public function findOne(int $intId) :array|bool{
|
||||
$strRq = "SELECT project.*,
|
||||
users.user_pseudo AS 'project_creatorname',
|
||||
users.user_image,
|
||||
users.user_mail,
|
||||
category.category_name
|
||||
FROM project
|
||||
INNER JOIN users ON users.user_id = project.project_user_id
|
||||
LEFT JOIN category ON category.category_id = project.project_category
|
||||
WHERE project.project_id = :id
|
||||
AND project.project_deleted_at IS NULL";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $intId, PDO::PARAM_INT);
|
||||
$rqPrep->execute();
|
||||
|
||||
return $rqPrep->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de changement de status (accepter) d'un projet en BDD
|
||||
* @param int $id l'id du projet
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function accept(int $id){
|
||||
|
||||
$strRq = "UPDATE project
|
||||
SET project_status = 'publié',
|
||||
project_edit_date = NOW()
|
||||
WHERE project_id =".$id;
|
||||
|
||||
return $this->_db->query($strRq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de changement de status (refusé) d'un projet en BDD
|
||||
* @param int $id l'id du projet
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function refuse(int $id){
|
||||
|
||||
$strRq = "UPDATE project
|
||||
SET project_status = 'refusé',
|
||||
project_edit_date = NOW()
|
||||
WHERE project_id =".$id;
|
||||
|
||||
return $this->_db->query($strRq);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de suppression d'un projet en BDD
|
||||
* @param int $id l'id du projet
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function delete_soft_project(int $intId): bool {
|
||||
$strRq = "UPDATE project
|
||||
SET project_deleted_at = NOW(),
|
||||
project_edit_date = NOW(),
|
||||
project_status = 'supprimé'
|
||||
WHERE project_id = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $intId, PDO::PARAM_INT);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de mise à jour d'un projet en BDD
|
||||
* @param object $objProject L'objet utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function updateProject(object $objProject):bool{
|
||||
|
||||
$strRq = "UPDATE project
|
||||
SET project_title = :title,
|
||||
project_description = :description,
|
||||
project_content = :content,
|
||||
project_thumbnail = :thumbnail,
|
||||
project_status = 'en_attente',
|
||||
project_edit_date = NOW()
|
||||
WHERE project_id = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":title", $objProject->getTitle(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":description", $objProject->getDescription(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":content", $objProject->getContent(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":thumbnail", $objProject->getThumbnail(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":id", $objProject->getId(), PDO::PARAM_INT);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de récupération d'image d'un projet en BDD
|
||||
* @param int $objProject L'Id du projet choisit
|
||||
* @return array Un tableau avec les informations de la bdd
|
||||
*/
|
||||
public function getImagesByProjectId(int $projectId): array {
|
||||
$strRq = "SELECT image_id, image_name, image_alt, image_status
|
||||
FROM image
|
||||
WHERE image_project = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $projectId, PDO::PARAM_INT);
|
||||
$rqPrep->execute();
|
||||
|
||||
return $rqPrep->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de récupération d'image d'un projet en BDD
|
||||
* @param int $id L'Id de l'image choisit
|
||||
* @return array Un tableau avec les informations de la bdd
|
||||
*/
|
||||
public function deleteImage(int $id): bool {
|
||||
$strRq = "DELETE FROM image WHERE image_id = :id";
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de modifications de status de l'image d'un projet en BDD
|
||||
* @param int $id L'Id de l'image choisit, string $status le status choisit
|
||||
* @return array Un tableau avec les informations de la bdd
|
||||
*/
|
||||
public function updateImageStatus(int $id, string $status): bool {
|
||||
$strRq = "UPDATE image
|
||||
SET image_status = :status
|
||||
WHERE image_id = :id";
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(':status', $status, PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de récupération d'image d'un projet en BDD
|
||||
* @param int $id L'Id de l'image choisit
|
||||
* @return array Un tableau avec les informations de la bdd
|
||||
*/
|
||||
public function findImage(int $id): array|bool {
|
||||
$strRq = "SELECT * FROM image
|
||||
WHERE image_id = :id";
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$rqPrep->execute();
|
||||
return $rqPrep->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ajoute une image liée à un projet dans la table 'image'
|
||||
* @param string $fileName Nom du fichier image
|
||||
* @param int $projectId ID du projet parent
|
||||
* @param string $alt Texte alternatif
|
||||
* @return bool
|
||||
*/
|
||||
public function addImageInProject(string $fileName, int $projectId, string $alt = "Image projet"): bool {
|
||||
$strRq = "INSERT INTO image (
|
||||
image_name,
|
||||
image_alt,
|
||||
image_status,
|
||||
image_project
|
||||
)
|
||||
VALUES (:name, :alt, :status, :project)";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":name", $fileName, PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":alt", $alt, PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":status", "en_attente", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":project", $projectId, PDO::PARAM_INT);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
}
|
||||
262
models/UserModel.php
Normal file
262
models/UserModel.php
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
namespace Models;
|
||||
|
||||
use \PDO;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Traitement des requêtes pour les utilisateurs
|
||||
* @author : Yasser, Guillaume & Besnik
|
||||
*/
|
||||
|
||||
class UserModel extends MotherModel{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de recherche des utilisateurs et leur niveau d'autorisation
|
||||
* @return array
|
||||
*/
|
||||
public function findAllUsers():array{
|
||||
$strRq = "SELECT user_id, user_firstname, user_name, user_image, user_status, authorisation_name, user_pseudo
|
||||
FROM users INNER JOIN authorisation ON authorisation.authorisation_id = users.user_status
|
||||
WHERE user_deleted_at IS NULL";
|
||||
return $this->_db->query($strRq)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de vérification des utilisateurs
|
||||
* @param string $strMail
|
||||
* @param string $strPwd
|
||||
* @return array|bool
|
||||
*/
|
||||
public function verifUser(string $strMail, string $strPwd):array|bool{
|
||||
|
||||
$strRq = "SELECT user_id, user_name, user_firstname, user_password, user_image, user_status, authorisation_name, user_pseudo
|
||||
FROM users INNER JOIN authorisation ON authorisation.authorisation_id = users.user_status
|
||||
WHERE user_mail = :mail";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":mail", $strMail, PDO::PARAM_STR);
|
||||
$rqPrep->execute();
|
||||
|
||||
|
||||
|
||||
$arrUser = $rqPrep->fetch();
|
||||
if ($arrUser === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($arrUser['user_password'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!password_verify($strPwd, $arrUser['user_password'])) {
|
||||
return false;
|
||||
}
|
||||
unset($arrUser['user_password']);
|
||||
return $arrUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction d'insertion d'un utilisateur en BDD
|
||||
* @param object $objUser L'objet utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function insert(object $objUser):bool{
|
||||
|
||||
$strRq = "INSERT INTO users (user_name, user_firstname, user_pseudo, user_mail, user_password, user_phone, user_work, user_location, user_description)
|
||||
VALUES (:name, :firstname, :pseudo,:mail, :pwd, :phone, :work, :location,:description)";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":name", $objUser->getName(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":firstname", $objUser->getFirstname(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":pseudo", $objUser->getPseudo(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":mail", $objUser->getMail(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":pwd", $objUser->getPwdHash(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':phone', $objUser->getPhone() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':work', $objUser->getWork() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':location', $objUser->getLocation() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':description', $objUser->getDescription() ?? "", PDO::PARAM_STR);
|
||||
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour sauvegarder l'utilisateur lorsqu'il veut être souvenue
|
||||
* @param int $userId l'id de l'utilisateur a se rappeler
|
||||
* @param string $token Token hashé unique lié à l'utilisateur à se rappeler
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function remember(int $userId, string $token):bool{
|
||||
$strRq = "INSERT INTO tokens (token_user_id, token_hash, token_created_at, token_expire_at) VALUES (:id, :token, NOW(), :exp)";
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $userId, PDO::PARAM_INT);
|
||||
$rqPrep->bindValue(":token", $token, PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":exp",
|
||||
date('Y-m-d H:i:s', time() + (15*24*60*60))
|
||||
, PDO::PARAM_STR);
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour récupperer l'utilisateur par rapport au cookie enregistrer
|
||||
* @param string $hash Token hashé unique lié à l'utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
|
||||
public function getTokenUser(string $hash){
|
||||
$strRq = $this->_db->prepare("SELECT token_user_id FROM tokens WHERE token_hash = :hash AND token_expire_at > NOW()");
|
||||
$strRq->execute(['hash' => $hash]);
|
||||
return $strRq->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Méthode pour supprimer le token lors de la déconnexion
|
||||
* @param string $hash Token hashé unique lié à l'utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function deleteToken(string $hash){
|
||||
$strRq = $this->_db->prepare("DELETE FROM tokens WHERE token_hash = :hash");
|
||||
return $strRq->execute(['hash' => $hash]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Méthode pour mettre a jour l'utilisateur
|
||||
* @param object $objUser L'objet user a mettre à jour
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function update(object $objUser):bool{
|
||||
$strRq = "UPDATE users SET
|
||||
user_name = :name,
|
||||
user_firstname = :firstname,
|
||||
user_pseudo = :pseudo,
|
||||
user_mail = :mail,
|
||||
user_phone = :phone,
|
||||
user_work = :work,
|
||||
user_location = :location,
|
||||
user_description = :description,
|
||||
user_image = :image
|
||||
WHERE user_id = :id";
|
||||
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
|
||||
$rqPrep->bindValue(":id", $objUser->getId(), PDO::PARAM_INT);
|
||||
$rqPrep->bindValue(":name", $objUser->getName(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":firstname", $objUser->getFirstname(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":pseudo", $objUser->getPseudo(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(":mail", $objUser->getMail(), PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':phone', $objUser->getPhone() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':work', $objUser->getWork() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':location', $objUser->getLocation() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':description', $objUser->getDescription() ?? "", PDO::PARAM_STR);
|
||||
$rqPrep->bindValue(':image', $objUser->getImage() ?? "", PDO::PARAM_STR);
|
||||
|
||||
return $rqPrep->execute();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de vérification de mail
|
||||
* @param string $mail
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function mailExists(string $mail): bool{
|
||||
|
||||
$rq = $this->_db->prepare("SELECT 1 FROM users WHERE user_mail = :mail LIMIT 1");
|
||||
$rq->bindValue(":mail", $mail);
|
||||
$rq->execute();
|
||||
|
||||
return (bool)$rq->fetchColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction de changement de status d'un utilisateur
|
||||
* @param object $objUser L'objet utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
|
||||
public function editStatus(object $objUser):bool{
|
||||
|
||||
$strRq = "UPDATE users
|
||||
SET user_status = :status
|
||||
WHERE user_id = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $objUser->getId(), PDO::PARAM_INT);
|
||||
$rqPrep->bindValue(":status", $objUser->getStatus(), PDO::PARAM_INT);
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fonction permettant de supprimer un utilisateur avec une date de suppression
|
||||
* @param int $intId L'identifiant de l'utilisateur
|
||||
* @return bool Est-ce que la requête s'est bien passée
|
||||
*/
|
||||
public function delete_soft(int $intId):bool{
|
||||
|
||||
$strRq = "UPDATE users
|
||||
SET user_deleted_at = NOW()
|
||||
WHERE user_id = :id";
|
||||
|
||||
$rqPrep = $this->_db->prepare($strRq);
|
||||
$rqPrep->bindValue(":id", $intId, PDO::PARAM_INT);
|
||||
return $rqPrep->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les informations d'un utilisateur par son ID
|
||||
* @param int $intId L'identifiant de l'utilisateur
|
||||
* @return array Tableau associatif (ou false si pas trouvé)
|
||||
*/
|
||||
public function findUserById(int $intId): array|bool {
|
||||
|
||||
$strRq = "SELECT user_id,user_status ,user_image ,user_name, user_firstname, user_pseudo, user_mail, user_phone, user_work, user_location, user_description, authorisation_name
|
||||
FROM users INNER JOIN authorisation ON authorisation.authorisation_id = users.user_status
|
||||
WHERE user_id = :id";
|
||||
|
||||
$prep = $this->_db->prepare($strRq);
|
||||
$prep->bindValue(':id', $intId, PDO::PARAM_INT);
|
||||
$prep->execute();
|
||||
|
||||
return $prep->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les informations d'un utilisateur par son ID
|
||||
* @param string $strPseudo Pseudo de l'utilisateur
|
||||
* @return array Tableau associatif (ou false si pas trouvé)
|
||||
*/
|
||||
public function findUserByPseudo(string $strPseudo): array|bool {
|
||||
|
||||
$strRq = "SELECT user_id,user_image, user_status ,user_name, user_firstname, user_pseudo, user_mail, user_phone, user_work, user_location, user_description, authorisation_name
|
||||
FROM users INNER JOIN authorisation ON authorisation.authorisation_id = users.user_status
|
||||
WHERE user_pseudo = :pseudo";
|
||||
|
||||
$prep = $this->_db->prepare($strRq);
|
||||
$prep->bindValue(':pseudo', $strPseudo, PDO::PARAM_STR);
|
||||
$prep->execute();
|
||||
|
||||
return $prep->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifie sur le pseudo entré n'est pas déjà utilisé
|
||||
* @param string $pseudo Pseudo a verifié
|
||||
* @return bool Le pseudo existe ou pas
|
||||
*/
|
||||
public function pseudoExists(string $pseudo): bool{
|
||||
|
||||
$rq = $this->_db->prepare("SELECT 1 FROM users WHERE user_pseudo = :pseudo LIMIT 1");
|
||||
$rq->bindValue(":pseudo", $pseudo, PDO::PARAM_STR);
|
||||
$rq->execute();
|
||||
|
||||
return (bool)$rq->fetchColumn();
|
||||
}
|
||||
}
|
||||
0
uploads/profiles/.gitkeep
Normal file
0
uploads/profiles/.gitkeep
Normal file
0
uploads/projects/.gitkeep
Normal file
0
uploads/projects/.gitkeep
Normal file
0
views/.gitkeep
Normal file
0
views/.gitkeep
Normal file
112
views/_partial/apigeo.tpl
Normal file
112
views/_partial/apigeo.tpl
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<!-- Script d'autocomplétion ville/département — API Géo (gouvernement français) -->
|
||||
<script>
|
||||
(function () {
|
||||
const input = document.getElementById('user_location');
|
||||
const suggestions = document.getElementById('location-suggestions');
|
||||
let debounceTimer = null;
|
||||
|
||||
// Ferme la liste si on clique ailleurs
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!input.contains(e.target) && !suggestions.contains(e.target)) {
|
||||
hideSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('input', function () {
|
||||
const query = this.value.trim();
|
||||
|
||||
clearTimeout(debounceTimer);
|
||||
|
||||
if (query.length < 2) {
|
||||
hideSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Délai de 300 ms pour éviter trop d'appels API
|
||||
debounceTimer = setTimeout(function () {
|
||||
fetchCities(query);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Navigation clavier dans la liste
|
||||
input.addEventListener('keydown', function (e) {
|
||||
const items = suggestions.querySelectorAll('.list-group-item');
|
||||
const active = suggestions.querySelector('.list-group-item.active');
|
||||
let index = Array.from(items).indexOf(active);
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (index < items.length - 1) setActive(items, index + 1);
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (index > 0) setActive(items, index - 1);
|
||||
} else if (e.key === 'Enter' && active) {
|
||||
e.preventDefault();
|
||||
selectItem(active.dataset.value);
|
||||
} else if (e.key === 'Escape') {
|
||||
hideSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
function fetchCities(query) {
|
||||
// On cherche par nom de commune, on récupère aussi le département
|
||||
const url = 'https://geo.api.gouv.fr/communes?nom=' + encodeURIComponent(query)
|
||||
+ '&fields=nom,departement&boost=population&limit=8';
|
||||
|
||||
fetch(url)
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (data) { renderSuggestions(data); })
|
||||
.catch(function () { hideSuggestions(); });
|
||||
}
|
||||
|
||||
function renderSuggestions(cities) {
|
||||
suggestions.innerHTML = '';
|
||||
|
||||
if (!cities || cities.length === 0) {
|
||||
hideSuggestions();
|
||||
return;
|
||||
}
|
||||
|
||||
cities.forEach(function (city) {
|
||||
const dept = city.departement
|
||||
? city.departement.nom + ' (' + city.departement.code + ')'
|
||||
: '';
|
||||
const label = city.nom + (dept ? ' — ' + dept : '');
|
||||
// Valeur stockée : "Ville (Département)"
|
||||
const value = city.nom + (city.departement ? ' (' + city.departement.nom + ')' : '');
|
||||
|
||||
const li = document.createElement('li');
|
||||
li.className = 'list-group-item list-group-item-action py-2 px-3';
|
||||
li.style.cursor = 'pointer';
|
||||
li.dataset.value = value;
|
||||
li.textContent = label;
|
||||
|
||||
li.addEventListener('mousedown', function (e) {
|
||||
// mousedown avant blur pour éviter que la liste disparaisse avant le clic
|
||||
e.preventDefault();
|
||||
selectItem(value);
|
||||
});
|
||||
|
||||
suggestions.appendChild(li);
|
||||
});
|
||||
|
||||
suggestions.style.display = 'block';
|
||||
}
|
||||
|
||||
function selectItem(value) {
|
||||
input.value = value;
|
||||
hideSuggestions();
|
||||
}
|
||||
|
||||
function hideSuggestions() {
|
||||
suggestions.style.display = 'none';
|
||||
suggestions.innerHTML = '';
|
||||
}
|
||||
|
||||
function setActive(items, index) {
|
||||
items.forEach(function (item) { item.classList.remove('active'); });
|
||||
items[index].classList.add('active');
|
||||
input.value = items[index].dataset.value;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
31
views/_partial/delphoto.tpl
Normal file
31
views/_partial/delphoto.tpl
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
const defaultAvatar = "{$smarty.env.IMG_USER_PATH}images.jpg";
|
||||
|
||||
// Suppression du listener précédent avant d'en ajouter un nouveau
|
||||
const inputImage = document.getElementById('image');
|
||||
const btnDelete = document.getElementById('btn-delete-image');
|
||||
const previewAvatar = document.getElementById('preview-avatar');
|
||||
const deleteInput = document.getElementById('delete_image');
|
||||
|
||||
inputImage?.addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
previewAvatar.src = e.target.result;
|
||||
previewAvatar.style.opacity = '1';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
deleteInput.value = '0';
|
||||
if (btnDelete) {
|
||||
btnDelete.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
btnDelete?.addEventListener('click', function () {
|
||||
deleteInput.value = '1';
|
||||
previewAvatar.src = defaultAvatar;
|
||||
previewAvatar.style.opacity = '0.5';
|
||||
this.disabled = true;
|
||||
});
|
||||
</script>
|
||||
23
views/_partial/footer.tpl
Normal file
23
views/_partial/footer.tpl
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
<footer class="py-3 mt-3 text-center" >
|
||||
<p>Créé par YaLaGuBe</a></p>
|
||||
<nav aria-label="Navigation pied de page">
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item">
|
||||
<a href="{$smarty.env.BASE_URL}/page/about">A propos</a>
|
||||
</li>
|
||||
<li class="list-inline-item" aria-hidden="true">|</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="{$smarty.env.BASE_URL}/page/help">Aide et support</a>
|
||||
</li>
|
||||
<li class="list-inline-item" aria-hidden="true">|</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="{$smarty.env.BASE_URL}/page/mentions">Mentions légales</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="{$smarty.env.BASE_URL}/assests/js/scripts.js"></script>
|
||||
|
||||
|
||||
110
views/_partial/header.tpl
Normal file
110
views/_partial/header.tpl
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="{$smarty.env.BASE_URL}/assests/css/style.css">
|
||||
<link rel="shortcut icon" href="{$smarty.env.BASE_URL}/assests/img/Group-49.ico" type="image/x-icon">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
|
||||
<title>Folliow{block name="title"}{/block}</title>
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Logo -->
|
||||
<a class="navbar-brand d-flex align-items-center" href="{$smarty.env.BASE_URL}">
|
||||
<img src="{$smarty.env.BASE_URL}/assests/img/logo.png" alt="Logo" class="logo-image">
|
||||
</a>
|
||||
|
||||
<!-- Bouton hamburger mobile -->
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<!-- Liens de navigation -->
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
|
||||
<!-- Liens gauche -->
|
||||
<ul class="navbar-nav me-auto mb-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/page/about">À propos</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/project/search">Rechercher</a>
|
||||
</li>
|
||||
{if isset($smarty.session.user)}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/project/addedit_project">Ajouter un projet</a>
|
||||
</li>
|
||||
{if $smarty.session.user.authorisation_name == "Administrateur"}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/admin/admin">Admin</a>
|
||||
</li>
|
||||
{/if}
|
||||
{/if}
|
||||
</ul>
|
||||
|
||||
<!-- Liens droite (connexion / profil) -->
|
||||
<nav aria-label="Connexion utilisateur">
|
||||
{if !isset($smarty.session.user)}
|
||||
<ul class="navbar-nav d-flex flex-row align-items-center gap-1 mb-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/user/signup"
|
||||
title="Créer un compte" aria-label="Créer un compte">
|
||||
S'inscrire
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/user/login"
|
||||
title="Se connecter" aria-label="Se connecter">
|
||||
Se connecter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{else}
|
||||
<ul class="navbar-nav d-flex flex-row align-items-center gap-2 mb-0">
|
||||
<li class="nav-item d-flex align-items-center">
|
||||
<a class="nav-link p-0" href="{$smarty.env.BASE_URL}/user/user/{$smarty.session.user.user_pseudo}"
|
||||
title="Modifier mon compte" aria-label="Modifier mon compte">
|
||||
<img
|
||||
src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_USER_PATH}{if ($smarty.env.IMG_USER_PATH|cat:($smarty.session.user.user_image))|file_exists}{$smarty.session.user.user_image}{else}images.jpg{/if}"
|
||||
class="nav-avatar"
|
||||
alt="Photo de profil"
|
||||
>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item d-flex align-items-center">
|
||||
<a class="nav-link" href="{$smarty.env.BASE_URL}/user/logout"
|
||||
title="Se déconnecter" aria-label="Se déconnecter">
|
||||
Se déconnecter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{include file="views/_partial/messages.tpl"}
|
||||
|
||||
|
||||
<main id="main-content" class="container my-4" {block name="display"}{/block}>
|
||||
<section class="p-4 mb-4 text-center txt_title" aria-labelledby="page-title">
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<h2 id="page-title" class="display-5 fw-semibold mb-3">
|
||||
{block name="h2"}{/block}
|
||||
</h2>
|
||||
<p class="lead mb-2">
|
||||
{block name="p"}{/block}
|
||||
</p>
|
||||
<p class="text-muted small mb-0">
|
||||
{block name="date_maj"}{/block}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
14
views/_partial/messages.tpl
Normal file
14
views/_partial/messages.tpl
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
{if ($success_message != '')}
|
||||
<div class="alert alert-success">
|
||||
<p>{$success_message}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if (isset($arrError) && count($arrError) > 0) }
|
||||
<div class="alert alert-danger">
|
||||
{foreach $arrError as $strError}
|
||||
<p>{$strError}</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
111
views/_partial/preview.tpl
Normal file
111
views/_partial/preview.tpl
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<article class="col-md-3 mb-5 {if isset($smarty.session.user) and $smarty.session.user.user_status == 2} pb-5 {/if}" style="border-radius: 100px ;">
|
||||
<div class="card h-100 shadow article-card rounded-4" style="border-width: 2px; overflow: hidden;">
|
||||
|
||||
|
||||
<div class="ratio ratio-4x3">
|
||||
<a href="{$smarty.env.BASE_URL}/project/display/{$objProject->getId()}">
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_PROJECT_PATH}{if ($smarty.env.IMG_PROJECT_PATH|cat:$objProject->getThumbnail())|file_exists}{$objProject->getThumbnail()}{else}{math equation="rand(1,4)"}.jpg{/if}"
|
||||
class="w-100 h-100 object-fit-cover"
|
||||
{if $objProject->getCreatorName()|stripos:'minou' !== false}
|
||||
style =" box-shadow: 0 0 0 4px #000; "{/if}
|
||||
alt=""
|
||||
loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-body p-3 bg-light" {if $objProject->getCreatorName()|stripos:'minou' !== false}style=" background-color: #ff8c00 !important; /* Orange tigre */
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 30px,
|
||||
rgba(0, 0, 0, 0.15) 30px,
|
||||
rgba(0, 0, 0, 0.15) 60px
|
||||
) !important;
|
||||
color: #000 !important;
|
||||
position: relative;
|
||||
z-index: 1;"{/if}>
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<a href="{$smarty.env.BASE_URL}/user/user/{$objProject->getCreatorname()}">
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_USER_PATH}{if ($smarty.env.IMG_USER_PATH|cat:($objProject->getUser_image()))|file_exists}{$objProject->getUser_image()}{else}images.jpg{/if}"
|
||||
class="rounded-circle flex-shrink-0 border border-2 border-white"
|
||||
style="width: 64px; height: 64px; object-fit: cover; margin-top: 8px;{if $objProject->getCreatorName()|stripos:'minou' !== false}
|
||||
box-shadow: 0 0 0 4px #000; {/if}"
|
||||
alt="Photo de profil">
|
||||
</a>
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="h6 fw-bold mb-2 mt-1">{$objProject->getTitle()}</h3>
|
||||
<p class="small text-muted mb-1">
|
||||
<i class="bi bi-person"></i>
|
||||
<a href="{$smarty.env.BASE_URL}/user/user/{$objProject->getCreatorname()}"
|
||||
class="text-decoration-none text-muted"
|
||||
style="position: relative; z-index: 2;
|
||||
{if $objProject->getCreatorName()|stripos:'minou' !== false}font-weight: 800 !important;
|
||||
text-transform: uppercase;
|
||||
color: #000 !important;{/if}">
|
||||
{$objProject->getCreatorname()}
|
||||
</a>
|
||||
</p>
|
||||
<p class="small text-muted mb-0">
|
||||
<i class="bi bi-geo-alt">Colmar</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mt-3">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-clock"><time>{$objProject->getCreation_date()}</time></i>
|
||||
</small>
|
||||
{if $objProject->getStatus() != "refusé" && $objProject->getStatus() != "supprimé"}
|
||||
<a href="{$smarty.env.BASE_URL}/project/display/{$objProject->getId()}"
|
||||
class="btn btn-sm btn-primary" {if $objProject->getCreatorName()|stripos:'minou' !== false}style="
|
||||
background-color: #000 !important;
|
||||
color: #ff8c00 !important;
|
||||
border: none !important;
|
||||
font-weight: bold !important;
|
||||
transition: transform 0.2s;"{/if}>
|
||||
Voir →
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{if isset($smarty.session.user)}
|
||||
{if ($smarty.session.user.user_id == $objProject->getUser_id()) && $objProject->getStatus() == "en_attente" && $objProject->getStatus() == "accepté"}
|
||||
<a href="{$smarty.env.BASE_URL}/project/addedit_project/{$objProject->getId()}"
|
||||
class="btn btn-sm btn-outline-secondary mt-2 w-100"
|
||||
style="position: relative; z-index: 2;">
|
||||
Editer
|
||||
</a>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{if isset($smarty.session.user)
|
||||
and $smarty.session.user.user_status == 2
|
||||
and $objProject->getStatus() == "en_attente"}
|
||||
<div class="card-footer bg-white border-top-0 p-2">
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-sm btn-success flex-fill"
|
||||
href="{$smarty.env.BASE_URL}/project/accept/{$objProject->getId()}"
|
||||
name="toPublished">Accepter</a>
|
||||
<a class="btn btn-sm btn-warning flex-fill"
|
||||
href="{$smarty.env.BASE_URL}/project/refuse/{$objProject->getId()}"
|
||||
name="toRefused">Refuser</a>
|
||||
<a class="btn btn-sm btn-danger flex-fill"
|
||||
href="{$smarty.env.BASE_URL}/project/delete/{$objProject->getId()}"
|
||||
name="toDelete"
|
||||
onclick="return confirm('Attention ! Êtes-vous sûr de vouloir supprimer ce projet ? Cette action est irréversible.');">
|
||||
Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
{elseif $objProject->getStatus() == "refusé"}
|
||||
<div class="card-footer bg-white border-top-0 p-2">
|
||||
<p class="text-danger fw-bold text-center mb-0 small">Projet refusé</p>
|
||||
</div>
|
||||
{elseif $objProject->getStatus() == "supprimé"}
|
||||
<div class="card-footer bg-white border-top-0 p-2">
|
||||
<p class="text-danger fw-bold text-center mb-0 small">Projet archivé</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</article>
|
||||
197
views/about.tpl
Normal file
197
views/about.tpl
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="title" append} - À propos{/block}
|
||||
|
||||
{block name="h2"}À propos{/block}
|
||||
{block name="p"}Plateforme de partage de portfolio – Projet pédagogique{/block}
|
||||
|
||||
{block name="content"}
|
||||
|
||||
<section aria-label="À propos de FOLLIOW">
|
||||
|
||||
<div class="row g-5">
|
||||
|
||||
<!-- CONTENU PRINCIPAL -->
|
||||
<div class="col-lg-8">
|
||||
|
||||
<!-- INTRO -->
|
||||
<div class="mb-5">
|
||||
<h3 class="h4 mb-3">
|
||||
<i class="bi bi-lightbulb me-2 text-primary"></i>
|
||||
Présentation du projet
|
||||
</h3>
|
||||
|
||||
<p class="lead fs-4">
|
||||
<strong>FOLLIOW</strong> est un réseau social dédié aux portfolios numériques.
|
||||
</p>
|
||||
|
||||
<p class="fs-5">
|
||||
La plateforme permet à chacun de créer un profil personnalisé, de mettre en avant ses projets
|
||||
et de valoriser ses compétences à travers une présentation claire et professionnelle.
|
||||
</p>
|
||||
|
||||
<p class="fs-5">
|
||||
Les recruteurs et visiteurs peuvent rechercher des profils, consulter les réalisations
|
||||
et prendre contact avec les utilisateurs qui les intéressent.
|
||||
</p>
|
||||
|
||||
<p class="fs-5">
|
||||
Ce projet a été développé dans un cadre pédagogique afin de concevoir une application web
|
||||
complète simulant un environnement professionnel réel.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- FONCTIONNALITÉS -->
|
||||
<div class="mb-5">
|
||||
<h3 class="h4 mb-3">
|
||||
<i class="bi bi-gear me-2 text-primary"></i>
|
||||
Fonctionnalités principales
|
||||
</h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<ul class="list-unstyled fs-5">
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-2"></i>
|
||||
Création et gestion de projets
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-2"></i>
|
||||
Affichage dynamique des contenus
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-2"></i>
|
||||
Gestion des utilisateurs
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<ul class="list-unstyled fs-5">
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-2"></i>
|
||||
Partage d’un projet par email
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-check-circle text-success me-2"></i>
|
||||
Interface responsive et accessible
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OBJECTIFS -->
|
||||
<div class="mb-5">
|
||||
<h3 class="h4 mb-3">
|
||||
<i class="bi bi-bullseye me-2 text-primary"></i>
|
||||
Objectifs pédagogiques
|
||||
</h3>
|
||||
|
||||
<p class="fs-5">
|
||||
L’objectif principal de FOLLIOW est de démontrer la capacité à concevoir
|
||||
une application web complète, structurée et sécurisée.
|
||||
</p>
|
||||
|
||||
<ul class="fs-5">
|
||||
<li>Structuration d’un projet en architecture MVC</li>
|
||||
<li>Manipulation de bases de données relationnelles</li>
|
||||
<li>Validation et sécurisation des données</li>
|
||||
<li>Implémentation d’un système d’envoi d’emails</li>
|
||||
<li>Respect des bonnes pratiques (RGPD, accessibilité, organisation du code)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- TECHNOLOGIES -->
|
||||
<div>
|
||||
<h3 class="h4 mb-4">
|
||||
<i class="bi bi-code-slash me-2 text-primary"></i>
|
||||
Technologies utilisées
|
||||
</h3>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<strong class="fs-5">Backend</strong>
|
||||
<p class="small text-muted mb-0">
|
||||
PHP orienté objet – Architecture MVC – MySQL
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<strong class="fs-5">Frontend</strong>
|
||||
<p class="small text-muted mb-0">
|
||||
HTML5 – CSS3 – Bootstrap – Smarty
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<strong class="fs-5">Emails</strong>
|
||||
<p class="small text-muted mb-0">
|
||||
PHPMailer – SMTP – Brevo
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="border rounded p-3 h-100">
|
||||
<strong class="fs-5">Sécurité</strong>
|
||||
<p class="small text-muted mb-0">
|
||||
Validation des données – Protection des formulaires – Gestion des sessions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
<div class="col-lg-4">
|
||||
|
||||
<div class="sticky-top" style="top: 90px;">
|
||||
|
||||
<div class="border rounded p-4 mb-4">
|
||||
<h4 class="h5 mb-3">
|
||||
<i class="bi bi-mortarboard me-2 text-primary"></i>
|
||||
Projet pédagogique
|
||||
</h4>
|
||||
|
||||
<p class="fs-5 mb-0">
|
||||
FOLLIOW a été développé dans le cadre du module PROJET WEB 2,
|
||||
consacré à la conception d’une application web dynamique en PHP orienté objet (architecture MVC),
|
||||
au sein d’une formation en Développement Web et Web Mobile à CCI Campus Colmar.
|
||||
Il s’agit d’une plateforme démonstrative réalisée à des fins pédagogiques.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-4">
|
||||
<h4 class="h5 mb-3">
|
||||
<i class="bi bi-envelope me-2 text-primary"></i>
|
||||
Contact
|
||||
</h4>
|
||||
|
||||
<p class="fs-5 mb-2">
|
||||
Pour toute question :
|
||||
</p>
|
||||
|
||||
<a href="mailto:projet.folliow@hotmail.com" class="fs-5 text-decoration-none">
|
||||
projet.folliow@hotmail.com
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
{/block}
|
||||
105
views/addedit_project.tpl
Normal file
105
views/addedit_project.tpl
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="h2"}Ajouter un projet{/block}
|
||||
{block name="p"}Ajouter un projet à votre portfolio{/block}
|
||||
|
||||
|
||||
{block name="content"}
|
||||
<section class="container mt-4">
|
||||
{if isset($smarty.session.user)}
|
||||
<form class="m-2" method="POST" enctype="multipart/form-data" onsubmit="return confirm('Êtes-vous certain de vouloir poster votre projet ?')">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Titre</label>
|
||||
<input type="text" name="title" class="form-control {if (isset($arrError['title'])) } is-invalid {/if} " value="{if $objProject}{$objProject->getTitle()}{/if}" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description (courte)</label>
|
||||
<textarea name="description" class="form-control {if (isset($arrError['descProject'])) } is-invalid {/if} " rows="2">{if $objProject}{$objProject->getDescription()}{/if}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Texte complet du Portfolio</label>
|
||||
<textarea name="content" class="form-control {if (isset($arrError['content'])) } is-invalid {/if}" rows="5">{if $objProject}{$objProject->getContent()}{/if}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="col-6 mb-3">
|
||||
<fieldset class="border p-2 rounded">
|
||||
<legend class="form-label h6">Catégorie</legend>
|
||||
<select class="form-select" id="category" name="category">
|
||||
<option value="0" {if $objProject->getCategory() == 0}selected{/if}>Toutes les catégories</option>
|
||||
{foreach $arrCategory as $arrDetCategory}
|
||||
<option value="{$arrDetCategory['category_id']}"
|
||||
{if $objProject->getCategory() == $arrDetCategory['category_id']}selected{/if}>
|
||||
{$arrDetCategory['category_name']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="h5">Miniature de votre projet</label>
|
||||
<input name="thumbnail" class="form-control" type="file">
|
||||
{if $objProject && $objProject->getId()}
|
||||
<label class="h5">Miniature actuelle :</label>
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_PROJECT_PATH}{$objProject->getThumbnail()}" alt="Miniature">
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label h5">Galerie de photos (Max 20)</label>
|
||||
{assign var="nbActuel" value=$arrImages|@count}
|
||||
{assign var="disponible" value=20 - $nbActuel}
|
||||
|
||||
{if $disponible > 0}
|
||||
<p class="small text-muted">Il vous reste <strong>{$disponible}</strong> emplacement(s) libre(s).</p>
|
||||
<input type="file" name="imageProject[]" multiple class="form-control" accept="image/*">
|
||||
{else}
|
||||
<div class="alert alert-warning p-2">
|
||||
<i class="fas fa-exclamation-triangle"></i> Quota de 20 photos atteint.
|
||||
</div>
|
||||
<input type="file" disabled class="form-control">
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<label class="h5">Galerie du projet</label>
|
||||
{foreach $arrImages as $image}
|
||||
<div class="col-md-3 mb-3 text-center">
|
||||
<div class="card shadow-sm h-100 border-{if $image.image_status == 'approuvé'}success{elseif $image.image_status == 'refusé'}danger{else}warning{/if}">
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_PROJECT_PATH}{$image.image_name}"
|
||||
class="card-img-top img-thumbnail"
|
||||
alt="{$image.image_alt}"
|
||||
style="height: 150px; object-fit: cover;">
|
||||
|
||||
<div class="card-body p-2">
|
||||
<span class="badge {if $image.image_status == 'approuvé'}bg-success{elseif $image.image_status == 'refusé'}bg-danger{else}bg-warning text-dark{/if}">
|
||||
{$image.image_status|replace:'_':' '}
|
||||
</span>
|
||||
|
||||
<div class="mt-2 d-flex flex-column gap-1">
|
||||
<a href="{$smarty.env.BASE_URL}/project/delete_image/{$image.image_id}"
|
||||
class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Supprimer définitivement cette image ?')">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{foreachelse}
|
||||
<p class="text-muted italic">Aucune photo dans la galerie pour le moment.</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-md" name="sendProject">Envoyer</button>
|
||||
</div>
|
||||
</form>
|
||||
{else}
|
||||
<div class="alert alert-danger mt-5">
|
||||
<p class="mb-0">Vous devez vous connecter pour accéder à cette fonctionnalité</p>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/block}
|
||||
109
views/admin.tpl
Normal file
109
views/admin.tpl
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="h2"}Admin{/block}
|
||||
{block name="p"}Tableau de bord administrateur{/block}
|
||||
|
||||
{block name="content"}
|
||||
|
||||
<div class="container-fluid px-4 py-4">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0 fw-bold">Dashboard</h1>
|
||||
<span class="text-muted small">Connecté : <strong>{$smarty.session.user.user_name} {$smarty.session.user.user_firstname}</strong></span>
|
||||
</div>
|
||||
|
||||
<!-- Gestion utilisateurs -->
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-primary text-white fw-semibold">
|
||||
👤 Gestion des utilisateurs
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small mb-3">Modifier le statut ou supprimer un compte utilisateur.</p>
|
||||
<form method="POST">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold">Utilisateur</label>
|
||||
<select class="form-select" name="user_id">
|
||||
<option value="0">Choisir un utilisateur…</option>
|
||||
{foreach from=$arrUserToDisplay item=user}
|
||||
<option value="{$user->getId()}">{$user->getName()} {$user->getFirstname()}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-semibold">Nouveau statut</label>
|
||||
<select class="form-select" name="new_status">
|
||||
<option value="0">Sélectionner un statut…</option>
|
||||
{foreach from=$arrAuthorisationToDisplay item=arrDetAuthorisation}
|
||||
<option value="{$arrDetAuthorisation->getId()}">{$arrDetAuthorisation->getName()}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex gap-2">
|
||||
<button type="submit" name="action" value="update_status" class="btn btn-success w-100">
|
||||
✓ Valider
|
||||
</button>
|
||||
<button type="submit" name="action" value="delete_user" class="btn btn-danger w-100"
|
||||
onclick="return confirm('Êtes-vous sûr de vouloir supprimer cet utilisateur ? Cette action est irréversible.');">
|
||||
🗑 Supprimer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gestion des catégories -->
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Modifier une catégorie -->
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-secondary text-white fw-semibold">
|
||||
✏️ Modifier une catégorie
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small mb-3">Renommer une catégorie existante.</p>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Catégorie à modifier</label>
|
||||
<select class="form-select" name="id_to_edit">
|
||||
<option value="0">Choisir une catégorie…</option>
|
||||
{foreach from=$arrCategoryToDisplay item=category}
|
||||
<option value="{$category->getId()}">{$category->getName()}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Nouveau nom</label>
|
||||
<input type="text" class="form-control" name="new_name" placeholder="Nouveau nom de la catégorie">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">✓ Renommer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Créer une catégorie -->
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-secondary text-white fw-semibold">
|
||||
➕ Créer une catégorie
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small mb-3">Ajouter une nouvelle catégorie au catalogue.</p>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">Nom de la catégorie</label>
|
||||
<input type="text" class="form-control" name="new_category" placeholder="Nom de la nouvelle catégorie">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">➕ Créer</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/block}
|
||||
9
views/error_403.tpl
Normal file
9
views/error_403.tpl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="text-center">
|
||||
<h1 class="display-1">Erreur 403</h1>
|
||||
<h2 class="display-3">Vous n'êtes pas autorisé à accéder à cette page !</h2>
|
||||
<img class="img-fluid" src="{$smarty.env.BASE_URL}/assests/img/error403.jpg">
|
||||
</div>
|
||||
{/block}
|
||||
9
views/error_404.tpl
Normal file
9
views/error_404.tpl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="content"}
|
||||
<div class="text-center">
|
||||
<h1 class="display-1">Erreur 404</h1>
|
||||
<h2 class="display-3">Cette page n'existe pas !</h2>
|
||||
<img class="img-fluid" src="{$smarty.env.BASE_URL}/assests/img/error404.webp">
|
||||
</div>
|
||||
{/block}
|
||||
245
views/help.tpl
Normal file
245
views/help.tpl
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="h2"}Bienvenue sur l'aide & support de Folliow !{/block}
|
||||
{block name="p"}Ici, vous trouverez de l'aide pour naviguer sur notre site.{/block}
|
||||
|
||||
{block name="content"}
|
||||
|
||||
<section class="container">
|
||||
{if isset($smarty.session.user)}
|
||||
|
||||
{if $smarty.session.user.user_status == 3}
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Comment créer un nouveau projet ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Un haut, dans votre barre de navigation, vous avez un lien pour ajouter un nouveau projet.
|
||||
Il faudra remplir votre projet avec un titre, une description, des images (jusqu'à 20 par projet) et une image de présentation qui apparaitra en illustration dans la liste des projets.<br>
|
||||
Plus votre projet est rempli, plus il sera intéressant pour les autres profils, alors n'hésitez pas à bien le remplir !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Comment modifier mon projet ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Une fois votre projet créé, vous avez la possibilité de modifier son contenu. Si vous êtes bien connecté à votre compte, vous pouvez aller sur votre profil et dans la liste de vos projets,
|
||||
il vous suffira de cliquer sur "éditer" pour voir toutes les informations apparaitres et changer directement ce que vous souhaitez modifier.
|
||||
N'oubliez pas de valider vos modifications !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Comment modifier mes informations personnelles ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Si vous êtes bien connecté à votre compte, vous pouvez cliquer sur votre photo de profil en haut à droite de votre écran pour accéder à votre profil.
|
||||
Vous pourrez ensuite modifier vos informations directement depuis votre profil !
|
||||
Simple comme "bonjour" :) </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
Nous contacter
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Si vous avez des questions ? Un problème avec votre compte ?
|
||||
Vous souhaitez supprimer toutes vos données ou débloquer votre compte suite à une modération ? N'hésitez pas à nous écrire à <a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com </a> !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{elseif $smarty.session.user.user_status == 2}
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Comment vérifier le statut d'un projet ou d'une image ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Bienvenue dans notre team de modérateurs ! Vous avez maintenant accès à de nouvelles fonctionnalités.
|
||||
Vous verez le statut de chaque projet et image en dessous de chacun. Si rien n'est mentionné, celà signifie que le projet est déjà approuvé !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Comment modérer du contenu ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Très bonne question ! En plus du statut du projet ou de l'image, vous avez également accès à 3 boutons : approuver, refuser et supprimer.
|
||||
Si le projet est valide à nos conditions d'utilisation de Folliow, vous pouvez l'approuver. Si vous pensez qu'il faut légèrement modifier ou adapter, vous pouvez le refuser et l'utilisateur pourra modifier son projet.</p>
|
||||
<p>Et si le projet enfreint nos règles d'utilisation, vous pouvez supprimer le projet de l'utilisateur. Attention, cette action est définitive.
|
||||
N'hésitez pas à signaler un utilisateur à l'administrateur en cas de besoin !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Comment modérer une image ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
De la même manière que vous pouvez modérer un projet ! Dans la liste des images, vous aurez la possibilité de refuser, approuver ou bien supprimer une image.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
Nous contacter
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Si vous avez des questions ? Une fonctionnalité que vous avez du mal à comprendre ? Des suggestions d'amélioration ? N'hésitez pas à nous écrire à <a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com </a>! </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{elseif $smarty.session.user.user_status == 1}
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Comment accéder à mon espace administrateur ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Rien de plus simple ! Si vous êtes connecté à votre compte, un lien <a href="{$smarty.env.BASE_URL}/admin/admin">"admin"</a> sera visible dans votre barre de navigation.
|
||||
Cliquez dessus et vous voici dans l'antre réservée à nos fabuleux administrateurs !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Comment gérer un utilisateur ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>La première fonctionnalité accessible sur votre Dashbord est la maintenance utilisateur. Vous pouvez dans le menu déroulant seléctionner l'utilisateur concerné par votre action.
|
||||
Un nouveau modérateurs rejoint l'équipe ? Il suffira ensuite de choisir dans le menu déroulant, son nouveau statut ! N'oubliez pas de valider la modification !</p>
|
||||
<p>Oh, oh... Quelqu'un n'a pas respecté nos règles de modération et vous devez maintenant supprimer cette utilisateur... Après l'avoir séléctionné, vous pouvez appuyer sur le bouton "supprimer l'utilisateur".
|
||||
Nous vous demanderons une confirmation avant de finaliser la suppression. Attention ! Cette action est définitive et l'utilisateur perdra tous ses projets !</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Comment modifier ou créer une catégorie de projet ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Voici la seconde partie de votre Dashbord. Si celà vous semble judicieux ou suite à des suggestions d'utilisateurs, vous pouvez modifier l'intitulé d'une catégorie.
|
||||
Dans le menu déroulant, choisissez la catégorie concerncée, puis écrivez son nouveau nom dans le champ "nouveau nom de la catégorie". N'oubliez pas de valider le changement !</p>
|
||||
<p>Et si vous souhaitez créer une nouvelle catégorie, écrivez son intitulé dans le champ "nom de la nouvelle catégorie" et validez la création. Votre nouvelle catégorie apparait déjà dans le menu déroulant !</p>
|
||||
<p>La suppression d'une catégorie n'est pas possible, car beaucoup de projet se retrouveraient orphelins :( Mais avec la modification et la création, vous avez les outils nécessaire pour organiser le classement de tous nos beaux projets.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
Nous contacter
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p> Vous avez des questions ? Un problème avec votre espace administrateur ou une suggestion d'amélioration ? N'hésitez pas à nous écrire à <a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com </a> !<p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{else}
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
Qu'est-ce que Folliow ?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Folliow est un réseau social de portfolio. Il vous permet de rassembler et mettre en avant tous les projets que vous avez effectué !
|
||||
Et si vous êtes rectruteur à la recherche de talents, vous pourrez rechercher les profils en fonction de vos besoins, partager leur contenus et les contacter si vous souhaitez collaborer avec eux.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Comment créer un compte
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>En haut à droite de votre écran, vous avez le lien <a href="{$smarty.env.BASE_URL}/user/signin">"s'inscrire"</a>. Nous demandons certaines informations obligatoires pour l'identification,
|
||||
ensuite à vous de remplir les informations complémentaires pour alimenter votre profil et permettre aux autres utilisateurs de mieux vous connaitre et vous contacter.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
Partager un projet
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Lorsque vous consultez un projet, vous pouvez remplir le champ "email" avec l'email de la personne à qui vous souhaitez partager le projet.
|
||||
Votre contact recevra en moins de 5 minutes, un mail de notre part avec le lien vers le projet en question ! Pratique, n'est-ce pas ?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
|
||||
Nous contacter
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<p>Si vous avez des questions, n'hésitez pas à nous écrire à <a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com </a>!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
{/block}
|
||||
43
views/home.tpl
Normal file
43
views/home.tpl
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
{block name="display" }style="display:none;"{/block}
|
||||
{block name="content"}
|
||||
<section class="container mt-5 p-5 d-flex flex-column align-items-center text-center">
|
||||
<h1 class="logo">Folliow</h1>
|
||||
<h2>Là où les talents rencontrent leur avenir</h2>
|
||||
<p class="col-6">Une plateforme de portfolio adapté à vos besoins et aux besoins des entreprises.
|
||||
Créer un portfolio réellement pertinent aux exigences du marché et rentrez
|
||||
directement en contact avec les entreprises.</p>
|
||||
</section>
|
||||
|
||||
<section class="container mt-5 p-5 d-flex flex-column align-items-center text-center">
|
||||
<div class="mb-4">
|
||||
<form method="GET" action="{$smarty.env.BASE_URL}">
|
||||
<button type="submit" name="filter_cat" value="1" class="btn btn-primary">Design</button>
|
||||
<button type="submit" name="filter_cat" value="2" class="btn btn-primary">Développement Web</button>
|
||||
<button type="submit" name="filter_old" value="true" class="btn btn-primary">Plus de 6 mois</button>
|
||||
<a href="{$smarty.env.BASE_URL}/project/home" class="btn btn-primary">Tout</a>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="container-fluid" aria-label="Articles récents">
|
||||
<h2 class="visually-hidden">Les 4 derniers articles</h2>
|
||||
<div class="row mb-2">
|
||||
|
||||
{foreach from=$arrProjectToDisplay item=objProject}
|
||||
|
||||
{assign var="isAuthor" value=(isset($smarty.session.user) && $smarty.session.user.user_id == $objProject->getUser_id())}
|
||||
{assign var="isModerator" value=(isset($smarty.session.user) && $smarty.session.user.user_status == 2)}
|
||||
{assign var="isDeleted" value=($objProject->getDeleted_at() !== null)}
|
||||
{assign var="isRefused" value=($objProject->getStatus() == "refusé")}
|
||||
|
||||
{if $isModerator
|
||||
|| ($isAuthor && !$isDeleted)
|
||||
|| (!$isRefused && !$isDeleted)}
|
||||
|
||||
{include file="views/_partial/preview.tpl"}
|
||||
{/if}
|
||||
|
||||
{/foreach}
|
||||
</section>
|
||||
{/block}
|
||||
8
views/layout.tpl
Normal file
8
views/layout.tpl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{include file="views/_partial/header.tpl"}
|
||||
|
||||
<main class="container mb-4 mt-1 flex-grow-1 d-flex flex-column align-items-center justify-content-center">
|
||||
{block name="content"}
|
||||
{/block}
|
||||
</main>
|
||||
|
||||
{include file="views/_partial/footer.tpl"}
|
||||
89
views/login.tpl
Normal file
89
views/login.tpl
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
{block name="title" append} - Connexion{/block}
|
||||
|
||||
{block name="display" }style="display:none;"{/block}
|
||||
{block name="content"}
|
||||
<section aria-label="Se connecter">
|
||||
|
||||
<div class="py-5">
|
||||
<!-- Centrage horizontal du formulaire -->
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-md-8 col-lg-5">
|
||||
|
||||
<!-- Carte contenant le formulaire de connexion -->
|
||||
<div class="card shadow-sm border-0 rounded-4 p-4 p-lg-5">
|
||||
|
||||
<!-- Titre principal -->
|
||||
<h1 class="h3 fw-bold mb-1">Connexion</h1>
|
||||
|
||||
<!-- Texte descriptif -->
|
||||
<p class="text-secondary mb-4">
|
||||
Connectez-vous à votre compte.
|
||||
</p>
|
||||
|
||||
<!-- Formulaire de connexion -->
|
||||
<!-- Le traitement sera effectué en PHP via la méthode POST -->
|
||||
<form method="POST">
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- Champ : adresse e-mail de l'utilisateur -->
|
||||
<div class="col-12">
|
||||
<label for="user_mail" class="form-label">
|
||||
Adresse e-mail
|
||||
</label>
|
||||
<input
|
||||
value="{$strMail|default:''}"
|
||||
type="email"
|
||||
class="form-control {if isset($arrError.user_mail)}is-invalid{/if}"
|
||||
id="user_mail"
|
||||
name="user_mail"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ : mot de passe -->
|
||||
<div class="col-12">
|
||||
<label for="user_password" class="form-label">
|
||||
Mot de passe
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control {if isset($arrError.user_password)}is-invalid{/if}"
|
||||
id="user_password"
|
||||
name="user_password"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
<div class="form-check col-12">
|
||||
<input type="checkbox" class="form-check-input" id="remember_me" name="remember_me">
|
||||
<label class="form-check-label" for="remember_me">Se souvenir de moi</label>
|
||||
</div>
|
||||
|
||||
<!-- Bouton de soumission du formulaire -->
|
||||
<div class="col-12 d-grid mt-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg rounded-3">
|
||||
Se connecter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Lien vers la page d'inscription -->
|
||||
<div class="col-12 text-center">
|
||||
<small class="text-secondary">
|
||||
Pas encore de compte ?
|
||||
<a href="{$smarty.env.BASE_URL}/user/signup" class="link-primary">
|
||||
Créer un compte
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
{/block}
|
||||
27
views/mail_message.tpl
Normal file
27
views/mail_message.tpl
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<p>Bonjour,</p>
|
||||
|
||||
<p>
|
||||
Un projet a été partagé avec vous via la plateforme <strong>Folliow</strong>.
|
||||
</p>
|
||||
|
||||
<h3>{$projectTitle}</h3>
|
||||
|
||||
<p>
|
||||
{$projectDescription}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Vous pouvez consulter le projet en cliquant sur le lien ci-dessous :
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="{$projectUrl}">
|
||||
{$projectUrl}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Cordialement,<br>
|
||||
L’équipe Folliow
|
||||
</p>
|
||||
<img src="{$smarty.env.BASE_URL}/assests/img/logo.png" style="height : 30px;">
|
||||
287
views/mentions.tpl
Normal file
287
views/mentions.tpl
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="title" append} - Mentions légales{/block}
|
||||
|
||||
{block name="h2"}Mentions légales{/block}
|
||||
{block name="p"}Informations légales et politique de confidentialité{/block}
|
||||
|
||||
{block name="date_maj"}
|
||||
<p class="text-muted small mb-0">
|
||||
<i class="bi bi-calendar-event me-2" aria-hidden="true"></i>
|
||||
Dernière mise à jour : <time datetime="2026-03-05">5 mars 2026</time>
|
||||
</p>
|
||||
{/block}
|
||||
|
||||
{block name="js_footer" append}
|
||||
<script>
|
||||
{literal}
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
target.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
{/literal}
|
||||
</script>
|
||||
{/block}
|
||||
|
||||
{block name="content"}
|
||||
<section aria-label="Mentions légales">
|
||||
<h2 class="visually-hidden">Mentions légales</h2>
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="col-md-12">
|
||||
|
||||
<!-- Table des matières -->
|
||||
<nav class="table-of-contents bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="toc-heading">
|
||||
<h3 id="toc-heading" class="h5 mb-3">
|
||||
<i class="bi bi-list-ul me-2" aria-hidden="true"></i>
|
||||
Sommaire
|
||||
</h3>
|
||||
<ul class="list-unstyled ms-3">
|
||||
<li class="mb-2"><a href="#editeur">1. Éditeur du site</a></li>
|
||||
<li class="mb-2"><a href="#hebergeur">2. Hébergement</a></li>
|
||||
<li class="mb-2"><a href="#propriete">3. Propriété intellectuelle</a></li>
|
||||
<li class="mb-2"><a href="#rgpd">4. Protection des données personnelles (RGPD)</a></li>
|
||||
<li class="mb-2"><a href="#cookies">5. Cookies</a></li>
|
||||
<li class="mb-2"><a href="#responsabilite">6. Limitation de responsabilité</a></li>
|
||||
<li class="mb-2"><a href="#liens">7. Liens hypertextes</a></li>
|
||||
<li class="mb-2"><a href="#droit">8. Droit applicable</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Section 1 : Éditeur -->
|
||||
<section id="editeur" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="editeur-heading">
|
||||
<h3 id="editeur-heading" class="h4">
|
||||
<i class="bi bi-building me-2 text-primary" aria-hidden="true"></i>
|
||||
1. Éditeur du site
|
||||
</h3>
|
||||
|
||||
<p class="text-muted">
|
||||
FOLLIOW est un <strong>projet pédagogique</strong> réalisé dans le cadre d’un exercice de formation.
|
||||
</p>
|
||||
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Nom du projet</dt>
|
||||
<dd class="col-sm-9">FOLLIOW</dd>
|
||||
|
||||
<dt class="col-sm-3">Nature</dt>
|
||||
<dd class="col-sm-9">Projet scolaire / démonstration technique</dd>
|
||||
|
||||
<dt class="col-sm-3">Établissement</dt>
|
||||
<dd class="col-sm-9">FOLLIOW</dd>
|
||||
|
||||
<dt class="col-sm-3">Forme juridique</dt>
|
||||
<dd class="col-sm-9">Non applicable (projet pédagogique)</dd>
|
||||
|
||||
<dt class="col-sm-3">Capital social</dt>
|
||||
<dd class="col-sm-9">Non applicable</dd>
|
||||
|
||||
<dt class="col-sm-3">Adresse</dt>
|
||||
<dd class="col-sm-9">4 Rue du Rhin, 68000 Colmar</dd>
|
||||
|
||||
<dt class="col-sm-3">Téléphone</dt>
|
||||
<dd class="col-sm-9">03 68 67 20 00</dd>
|
||||
|
||||
<dt class="col-sm-3">Email</dt>
|
||||
<dd class="col-sm-9">
|
||||
<a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com</a>
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-3">Directeur de publication</dt>
|
||||
<dd class="col-sm-9">L’équipe du projet FOLLIOW</dd>
|
||||
|
||||
<dt class="col-sm-3">SIRET</dt>
|
||||
<dd class="col-sm-9">Non applicable (projet scolaire)</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<!-- Section 2 : Hébergeur -->
|
||||
<section id="hebergeur" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="hebergeur-heading">
|
||||
<h3 id="hebergeur-heading" class="h4">
|
||||
<i class="bi bi-hdd-network me-2 text-primary" aria-hidden="true"></i>
|
||||
2. Hébergement
|
||||
</h3>
|
||||
|
||||
<p>Le site est hébergé sur une infrastructure pédagogique mise à disposition pour le projet :</p>
|
||||
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Hébergeur</dt>
|
||||
<dd class="col-sm-9">Yass Le Goat en faite (et o2switch)<dd>
|
||||
|
||||
<dt class="col-sm-3">Domaine / accès</dt>
|
||||
<dd class="col-sm-9">php.boulayoune.com</dd>
|
||||
|
||||
<dt class="col-sm-3">Adresse</dt>
|
||||
<dd class="col-sm-9">REDACTED FOR PRIVACY</dd>
|
||||
|
||||
<dt class="col-sm-3">Téléphone</dt>
|
||||
<dd class="col-sm-9">REDACTED FOR PRIVACY</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<!-- Section 3 : Propriété intellectuelle -->
|
||||
<section id="propriete" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="propriete-heading">
|
||||
<h3 id="propriete-heading" class="h4">
|
||||
<i class="bi bi-c-circle me-2 text-primary" aria-hidden="true"></i>
|
||||
3. Propriété intellectuelle
|
||||
</h3>
|
||||
<p>
|
||||
Sauf mention contraire, l’ensemble des contenus présents sur FOLLIOW (textes, visuels, logo, éléments d’interface)
|
||||
est utilisé dans le cadre du projet et reste la propriété de leurs auteurs respectifs.
|
||||
</p>
|
||||
<p>
|
||||
Toute reproduction ou réutilisation à des fins commerciales est interdite sans autorisation préalable.
|
||||
</p>
|
||||
<p>
|
||||
Les éventuels contenus tiers (images, icônes, bibliothèques) restent soumis à leurs licences d’origine.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 4 : RGPD -->
|
||||
<section id="rgpd" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="rgpd-heading">
|
||||
<h3 id="rgpd-heading" class="h4">
|
||||
<i class="bi bi-shield-lock me-2 text-primary" aria-hidden="true"></i>
|
||||
4. Protection des données personnelles (RGPD)
|
||||
</h3>
|
||||
|
||||
<h4 class="h5 mt-4">4.1 Responsable du traitement</h4>
|
||||
<p>
|
||||
Le responsable du traitement est l’équipe du projet FOLLIOW (projet pédagogique).
|
||||
</p>
|
||||
|
||||
<h4 class="h5 mt-4">4.2 Données collectées</h4>
|
||||
<p>Selon l’utilisation du site, les données pouvant être collectées sont :</p>
|
||||
<ul>
|
||||
<li>Nom / Prenom </li>
|
||||
<li>Pseudo </li>
|
||||
<li>Adresse email (inscription, connexion, partage de projet par email)</li>
|
||||
<li>Contenus déposés par l’utilisateur (projets, descriptions, images)</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="h5 mt-4">4.3 Finalités du traitement</h4>
|
||||
<ul>
|
||||
<li>Création et gestion de compte</li>
|
||||
<li>Publication et affichage de projets</li>
|
||||
<li>Partage d’un projet par email à la demande de l’utilisateur</li>
|
||||
<li>Sécurisation du site et prévention des abus</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="h5 mt-4">4.4 Durée de conservation</h4>
|
||||
<p>
|
||||
Dans le cadre de ce projet pédagogique, les données sont conservées pendant la durée du projet et des évaluations,
|
||||
puis supprimées ou anonymisées, sauf obligation légale contraire.
|
||||
</p>
|
||||
|
||||
<h4 class="h5 mt-4">4.5 Vos droits</h4>
|
||||
<p>Conformément au RGPD, vous disposez de droits d’accès, de rectification, d’effacement et d’opposition.</p>
|
||||
<p>
|
||||
Pour exercer ces droits, contactez-nous à :
|
||||
<a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com</a>
|
||||
</p>
|
||||
<p>
|
||||
Vous pouvez également introduire une réclamation auprès de la CNIL
|
||||
(<a href="https://www.cnil.fr" target="_blank" rel="noopener">www.cnil.fr</a>).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 5 : Cookies -->
|
||||
<section id="cookies" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="cookies-heading">
|
||||
<h3 id="cookies-heading" class="h4">
|
||||
<i class="bi bi-cookie me-2 text-primary" aria-hidden="true"></i>
|
||||
5. Cookies
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
FOLLIOW peut utiliser des cookies <strong>strictement nécessaires</strong> au fonctionnement du site
|
||||
(ex : session de connexion). Aucun cookie publicitaire n’est utilisé.
|
||||
</p>
|
||||
|
||||
<h4 class="h5 mt-4">5.1 Qu’est-ce qu’un cookie ?</h4>
|
||||
<p>
|
||||
Un cookie est un petit fichier texte déposé sur votre appareil lors de la visite d’un site.
|
||||
Il permet notamment de conserver une session ou des préférences.
|
||||
</p>
|
||||
|
||||
<h4 class="h5 mt-4">5.2 Types de cookies utilisés</h4>
|
||||
<ul>
|
||||
<li><strong>Cookies techniques</strong> : indispensables (session, sécurité)</li>
|
||||
<li><strong>Cookies de préférence</strong> : éventuels (langue, affichage) si implémentés</li>
|
||||
</ul>
|
||||
|
||||
<h4 class="h5 mt-4">5.3 Gestion des cookies</h4>
|
||||
<p>
|
||||
Vous pouvez configurer votre navigateur pour refuser les cookies. Certaines fonctionnalités du site
|
||||
peuvent alors ne pas fonctionner correctement.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 6 : Limitation de responsabilité -->
|
||||
<section id="responsabilite" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="responsabilite-heading">
|
||||
<h3 id="responsabilite-heading" class="h4">
|
||||
<i class="bi bi-exclamation-triangle me-2 text-primary" aria-hidden="true"></i>
|
||||
6. Limitation de responsabilité
|
||||
</h3>
|
||||
<p>
|
||||
Les informations présentées sur FOLLIOW sont fournies à titre démonstratif dans le cadre d’un projet pédagogique.
|
||||
L’équipe s’efforce de maintenir le site accessible, sans garantie d’absence d’erreurs ou d’interruptions.
|
||||
</p>
|
||||
<p>
|
||||
L’équipe FOLLIOW ne pourra être tenue responsable des dommages directs ou indirects résultant de l’utilisation du site,
|
||||
notamment en cas d’indisponibilité ou de perte de données.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 7 : Liens hypertextes -->
|
||||
<section id="liens" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="liens-heading">
|
||||
<h3 id="liens-heading" class="h4">
|
||||
<i class="bi bi-link-45deg me-2 text-primary" aria-hidden="true"></i>
|
||||
7. Liens hypertextes
|
||||
</h3>
|
||||
<p>
|
||||
Le site peut contenir des liens vers des sites tiers. FOLLIOW n’exerce aucun contrôle sur ces sites et décline toute
|
||||
responsabilité quant à leur contenu ou leur disponibilité.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Section 8 : Droit applicable -->
|
||||
<section id="droit" class="legal-section bg-light rounded shadow-sm p-4 mb-4" aria-labelledby="droit-heading">
|
||||
<h3 id="droit-heading" class="h4">
|
||||
<i class="bi bi-hammer me-2 text-primary" aria-hidden="true"></i>
|
||||
8. Droit applicable et juridiction compétente
|
||||
</h3>
|
||||
<p>
|
||||
Les présentes mentions légales sont régies par le droit français. En cas de litige, et à défaut d’accord amiable,
|
||||
les tribunaux français seront seuls compétents.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Contact -->
|
||||
<section class="mt-5 p-4 bg-light rounded shadow-sm" aria-labelledby="contact-legal">
|
||||
<h3 id="contact-legal" class="h4 mb-3">
|
||||
<i class="bi bi-envelope me-2 text-primary" aria-hidden="true"></i>
|
||||
Questions ou réclamations
|
||||
</h3>
|
||||
<p>
|
||||
Pour toute question concernant ces mentions légales ou pour exercer vos droits, vous pouvez nous contacter :
|
||||
</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">
|
||||
<i class="bi bi-envelope me-2" aria-hidden="true"></i>
|
||||
Par email : <a href="mailto:projet.folliow@hotmail.com">projet.folliow@hotmail.com</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/block}
|
||||
75
views/project.tpl
Normal file
75
views/project.tpl
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="content"}
|
||||
<body>
|
||||
<section class="container mt-5 p-5 d-flex flex-column align-items-center">
|
||||
<div>
|
||||
<h2>Alimenter votre projet</h2>
|
||||
<form method="post">
|
||||
<button type="submit" class="btn btn-primary btn-lg" name="showForm">+</button>
|
||||
{if isset($smarty.post.toContinue)}
|
||||
<button type="submit" class="btn btn-warning btn-lg" name="showFormContinue">Reprendre</button>
|
||||
{/if}
|
||||
</form>
|
||||
|
||||
{* Affichage d'un formulaire en cas d'appuie sur le bouton "+" *}
|
||||
{if isset($smarty.post.showForm) || isset($smarty.post.showFormContinue)}
|
||||
{if isset($smarty.session.user)}
|
||||
<form class="m-2" method="post" enctype="multipart/form-data" onsubmit="return confirm('Voulez-vous vraiment envoyer le formulaire ?')">
|
||||
<div>
|
||||
<label>Titre</label>
|
||||
<input type="text" name="titleProject">
|
||||
</div>
|
||||
<div>
|
||||
<label>Description</label>
|
||||
<input type="text" name="descProject">
|
||||
</div>
|
||||
<div>
|
||||
<label>Texte Portfolio</label>
|
||||
<input type="text" name="textProject">
|
||||
</div>
|
||||
<div>
|
||||
<h3>Image thumbnail</h3>
|
||||
<input name="imageThumbnail" class="form-control" type="file">
|
||||
</div>
|
||||
<div>
|
||||
<h3>Image du projet</h3>
|
||||
<input name="imageProject" class="form-control" type="file">
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-primary btn-md" name="sendMessage">Envoyer</button>
|
||||
<button type="submit" class="btn btn-warning btn-md" name="toContinue">Remettre à plus tard</button>
|
||||
</div>
|
||||
</form>
|
||||
{else}
|
||||
<div class="border rounded mt-5">
|
||||
<p class="text-danger p-2">Vous devez vous connecter pour accéder à cette fonctionnalité</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h3>Description</h3>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Photos behind the scene</h3>
|
||||
<div class="row mb-2">
|
||||
<img>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Other projects</h3>
|
||||
<div class="row mb-2">
|
||||
{foreach $arrProjectToDisplay as $objProject}
|
||||
{include file="../app/views/partials/preview.tpl"}
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section>
|
||||
</body>
|
||||
{/block}
|
||||
178
views/project_display.tpl
Normal file
178
views/project_display.tpl
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="title" append} - {$objProject->getTitle()}{/block}
|
||||
|
||||
{block name="h2"}Projet de {$objProject->getCreatorName()}{/block}
|
||||
|
||||
{block name="p"}Portfolio créatif partagé sur FOLLIOW{/block}
|
||||
|
||||
{block name="content"}
|
||||
|
||||
<!-- Message après l'envoi d'email -->
|
||||
{if isset($smarty.get.mail) && $smarty.get.mail === 'ok'}
|
||||
<div class="alert alert-success">Email envoyé avec succès.</div>
|
||||
{elseif isset($smarty.get.mail) && $smarty.get.mail === 'fail'}
|
||||
<div class="alert alert-danger">Erreur lors de l'envoi de l'email.</div>
|
||||
{/if}
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Contenu principal du projet -->
|
||||
<div class="col-lg-8">
|
||||
|
||||
<h1 class="fw-bold">{$objProject->getTitle()}</h1>
|
||||
|
||||
<p class="text-muted">
|
||||
{$arrProject.category_name ?? 'Général'}
|
||||
</p>
|
||||
|
||||
<div class="mb-4 shadow-sm {if !($smarty.env.IMG_PROJECT_PATH|cat:$objProject->getThumbnail())|file_exists}d-none{/if}" >
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_PROJECT_PATH}{$objProject->getThumbnail()}"
|
||||
class="img-fluid rounded w-100">
|
||||
</div>
|
||||
|
||||
<div class="bg-light p-4 rounded mb-4">
|
||||
<h4 class="border-bottom pb-2">Description</h4>
|
||||
<p class="lead">{$objProject->getDescription()}</p>
|
||||
|
||||
<div class="mt-4" style="white-space: pre-wrap; overflow-wrap: break-word;">
|
||||
{$objProject->getContent()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Images du projet -->
|
||||
<section id="galerie-projet">
|
||||
<h2>Galerie du projet</h2>
|
||||
<div class="row">
|
||||
{foreach $arrImages as $image}
|
||||
|
||||
{* On affiche l'image si elle est approuvée OU si l'utlilisateur possède le projet OU si l'utlilisateur est Modérateur ET que l'image est disponible sur le serveur*}
|
||||
{if (($image.image_status == 'approuvé') ||
|
||||
(isset($smarty.session.user) && $smarty.session.user.user_status == 2) ||
|
||||
(isset($smarty.session.user) && $smarty.session.user.user_id == $objProject->getUser_id())) && (($smarty.env.IMG_PROJECT_PATH|cat:$objProject->getThumbnail())|file_exists)}
|
||||
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card {if $image.image_status != 'approuvé'}border-warning shadow-none opacity-75{/if}">
|
||||
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_PROJECT_PATH}{$image.image_name}" class="card-img-top" alt="{$image.image_alt}">
|
||||
|
||||
{* Visible uniquement par le modérateur *}
|
||||
{if isset($smarty.session.user.user_status) && $smarty.session.user.user_status == 2}
|
||||
<div class="moderator-tools border-top pt-2 mt-2">
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{$smarty.env.BASE_URL}/project/change_image_status/{$image.image_id}?status=approuvé"
|
||||
class="btn btn-sm btn-success">Valider</a>
|
||||
|
||||
<a href="{$smarty.env.BASE_URL}/project/delete_image/{$image.image_id}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
onclick="return confirm('Supprimer définitivement ?')">Supprimer</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{foreachelse}
|
||||
<p>Aucune image disponible pour ce projet.</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Formulaire qui envoie la demande au contrôleur (sendEmail) -->
|
||||
<div class="card shadow-sm p-4 mb-5">
|
||||
<form method="post" action="{$smarty.env.BASE_URL}/project/sendEmail">
|
||||
|
||||
<input type="hidden" name="project_id"
|
||||
value="{$objProject->getId()}">
|
||||
|
||||
<input type="email" name="to_email"
|
||||
class="form-control mb-3"
|
||||
placeholder="Adresse email" required>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
Envoyer par email
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Sidebar : informations du créateur -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card text-center shadow-sm p-4" {if $objProject->getCreatorName()|stripos:'minou' !== false}style=" background-color: #ff8c00 !important; /* Orange tigre */
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 30px,
|
||||
rgba(0, 0, 0, 0.15) 30px,
|
||||
rgba(0, 0, 0, 0.15) 60px
|
||||
) !important;
|
||||
border: 2px solid #000 !important;
|
||||
color: #000 !important;
|
||||
position: relative;
|
||||
z-index: 1;"{/if}>
|
||||
|
||||
<a href="{$smarty.env.BASE_URL}/user/user/{$objProject->getCreatorName()}" class="text-decoration-none text-dark">
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_USER_PATH}{if ($smarty.env.IMG_USER_PATH|cat:($objProject->getUser_image()))|file_exists}{$objProject->getUser_image()}{else}images.jpg{/if}"
|
||||
class="rounded-circle mb-3 mx-auto "
|
||||
style="width:100px;height:100px;object-fit:cover;{if $objProject->getCreatorName()|stripos:'minou' !== false}
|
||||
box-shadow: 0 0 0 4px #000; {/if}">
|
||||
</a>
|
||||
|
||||
<h5 {if $objProject->getCreatorName()|stripos:'minou' !== false}style="font-weight: 800 !important;
|
||||
text-transform: uppercase;
|
||||
color: #000 !important;"{/if}>
|
||||
{$objProject->getCreatorName()}
|
||||
</h5>
|
||||
|
||||
<p class="text-muted small">
|
||||
Publié le {$objProject->getCreation_date()}
|
||||
</p>
|
||||
|
||||
<a href="mailto:{$arrProject['user_mail']}" class="btn btn-primary" {if $objProject->getCreatorName()|stripos:'minou' !== false}style="
|
||||
background-color: #000 !important;
|
||||
color: #ff8c00 !important;
|
||||
border: none !important;
|
||||
font-weight: bold !important;
|
||||
transition: transform 0.2s;"{/if}>
|
||||
Contacter le talent
|
||||
</a>
|
||||
|
||||
{if $objProject->getCreatorName()|stripos:"minou" !== false}
|
||||
<div class="small mt-2">MINOU NE MEURT JAMAIS ! 🐯</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{*Controle de l'utilisateur ainsi que du status du projet + Suppression disponible pour l'utilisateur possédant le projet*}
|
||||
{if isset($smarty.session.user)}
|
||||
{if ($smarty.session.user.user_status == 2 || $smarty.session.user.user_id == $objProject->getUser_id())}
|
||||
<div class="border rounded text-center">
|
||||
<a class="btn btn-sm btn-danger flex-fill"
|
||||
href="{$smarty.env.BASE_URL}/project/delete/{$objProject->getId()}"
|
||||
name="toDelete"
|
||||
onclick="return confirm('Attention ! Êtes-vous sûr de vouloir supprimer ce projet ? Cette action est irréversible.');">
|
||||
Supprimer</a>
|
||||
{/if}
|
||||
|
||||
|
||||
{if $smarty.session.user.user_id == $objProject->getUser_id()}
|
||||
<a class="btn btn-sm m-1 btn-warning" href="{$smarty.env.BASE_URL}/project/addedit_project/{$objProject->getId()}" name="addedit">Modifier le projet</a>
|
||||
{/if}
|
||||
|
||||
{*Conditions permettant au Modérateur de modifier le status d'un projet*}
|
||||
{if $smarty.session.user.user_status == 2 && $objProject->getStatus() == "en_attente"}
|
||||
<a class="btn btn-sm m-1 btn-success" href="{$smarty.env.BASE_URL}/project/accept/{$objProject->getId()}" name="toPublished">Accepter</a>
|
||||
<a class="btn btn-sm m-1 btn-warning" href="{$smarty.env.BASE_URL}/project/refuse/{$objProject->getId()}" name="toRefused">Refuser</a>
|
||||
</div>
|
||||
{elseif $projectStatus == "refusé"}
|
||||
<p class="text-danger fw-bold">Portfolio refusé</p>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{/block}
|
||||
195
views/search.tpl
Normal file
195
views/search.tpl
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
{block name="title" append} - Rechercher{/block}
|
||||
|
||||
{block name="h2"}Rechercher{/block}
|
||||
{block name="p"}Recherche de projets par mot clés, date, auteur et catégorie.{/block}
|
||||
|
||||
|
||||
{block name="content"}
|
||||
<section aria-label="Blog">
|
||||
<h2 class="visually-hidden">Rechercher parmi les projets</h2>
|
||||
<div class="row mb-2">
|
||||
<section class="mb-5" aria-labelledby="search-heading">
|
||||
<form name="formSearch" method="post" action="" class="border rounded p-4 bg-light">
|
||||
<h3 id="search-heading" class="h4 mb-4">
|
||||
<i class="fas fa-search me-2" aria-hidden="true"></i>
|
||||
Rechercher des projets
|
||||
</h3>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="keywords" class="form-label">Mots-clés</label>
|
||||
<input
|
||||
value="{$strKeywords}" type="text" class="form-control" id="keywords" name="keywords" placeholder="Design, IA, back-end" aria-describedby="keywords-help">
|
||||
<small id="keywords-help" class="form-text text-muted">
|
||||
Recherchez dans les titres et contenus
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="author" class="form-label">Auteurs</label>
|
||||
<select class="form-select" id="author" name="author">
|
||||
<option value="0" {if $intAuthor == 0}selected{/if}>Tous les auteurs</option>
|
||||
{foreach $arrUser as $arrDetUser}
|
||||
<option value="{$arrDetUser['user_id']}"
|
||||
{if $intAuthor == $arrDetUser['user_id']}selected{/if}
|
||||
>
|
||||
{$arrDetUser['user_firstname']} {$arrDetUser['user_name']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<div class="col-6">
|
||||
<fieldset>
|
||||
<legend class="form-label">Recherche par date</legend>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="period"
|
||||
id="period-exact"
|
||||
value="0"
|
||||
{if $intPeriod == 0}checked{/if}
|
||||
aria-controls="date-exact date-range">
|
||||
<label class="form-check-label" for="period-exact">
|
||||
Date exacte
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="period"
|
||||
id="period-range"
|
||||
value="1"
|
||||
{if $intPeriod == 1}checked{/if}
|
||||
aria-controls="date-exact date-range">
|
||||
<label class="form-check-label" for="period-range">
|
||||
Période
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12" id="date-exact">
|
||||
<label for="date" class="form-label">Date</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
id="date"
|
||||
name="date"
|
||||
aria-describedby="date-help"
|
||||
value="{$strDate}" >
|
||||
<small id="date-help" class="form-text text-muted">
|
||||
Format: JJ/MM/AAAA
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div id="date-range" style="display: none;">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="startdate" class="form-label">Date de début</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
id="startdate"
|
||||
name="startdate"
|
||||
value="{$strStartDate}" >
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="enddate" class="form-label">Date de fin</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
id="enddate"
|
||||
name="enddate"
|
||||
value="{$strEndDate}" >
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<fieldset>
|
||||
<legend class="form-label">Recherche par catégories</legend>
|
||||
<div class="col-md-6">
|
||||
<label for="author" class="form-label">Catégorie</label>
|
||||
<select class="form-select" id="category" name="category">
|
||||
<option value="0" {if $intCategory == 0}selected{/if}>Toutes les catégories</option>
|
||||
{foreach $arrCategory as $arrDetCategory}
|
||||
<option value="{$arrDetCategory['category_id']}"
|
||||
{if $intCategory == $arrDetCategory['category_id']}selected{/if}
|
||||
>
|
||||
{$arrDetCategory['category_name']}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-search me-2" aria-hidden="true"></i>
|
||||
Rechercher
|
||||
</button>
|
||||
<a href="{$smarty.env.BASE_URL}/project/search" class="btn btn-secondary ms-2">
|
||||
<i class="fas fa-redo me-2" aria-hidden="true"></i>
|
||||
Réinitialiser
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="articles-heading">
|
||||
<h3 id="articles-heading" class="visually-hidden">Liste des projets</h3>
|
||||
<div class="row mb-2">
|
||||
{if count($arrProject) == 0}
|
||||
<div class="alert alert-warning">
|
||||
<p>Pas de résultats</p>
|
||||
</div>
|
||||
{/if}
|
||||
{foreach from=$arrProjectToDisplay item=objProject}
|
||||
|
||||
{assign var="isAuthor" value=(isset($smarty.session.user) && $smarty.session.user.user_id == $objProject->getUser_id())}
|
||||
{assign var="isModerator" value=(isset($smarty.session.user) && $smarty.session.user.user_status == 2)}
|
||||
{assign var="isDeleted" value=($objProject->getDeleted_at() !== null)}
|
||||
{assign var="isRefused" value=($objProject->getStatus() == "refusé")}
|
||||
|
||||
{if $isModerator
|
||||
|| ($isAuthor && !$isDeleted)
|
||||
|| (!$isRefused && !$isDeleted)}
|
||||
|
||||
{include file="views/_partial/preview.tpl"}
|
||||
{/if}
|
||||
|
||||
{/foreach}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
const periodRadios = document.querySelectorAll('input[name="period"]');
|
||||
const dateExact = document.getElementById('date-exact');
|
||||
const dateRange = document.getElementById('date-range');
|
||||
|
||||
function toggleDateFields() {
|
||||
const selectedPeriod = document.querySelector('input[name="period"]:checked').value;
|
||||
|
||||
if (selectedPeriod === '0') {
|
||||
dateExact.style.display = 'block';
|
||||
dateRange.style.display = 'none';
|
||||
} else {
|
||||
dateExact.style.display = 'none';
|
||||
dateRange.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
periodRadios.forEach(radio => {
|
||||
radio.addEventListener('change', toggleDateFields);
|
||||
});
|
||||
toggleDateFields();
|
||||
</script>
|
||||
{/block}
|
||||
278
views/signup.tpl
Normal file
278
views/signup.tpl
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
{block name="title" append} - Inscription{/block}
|
||||
|
||||
{block name="display" }style="display:none;"{/block}
|
||||
{block name="content"}
|
||||
<!-- Page : Inscription -->
|
||||
<div class="py-5">
|
||||
|
||||
<!-- Centrage horizontal du formulaire -->
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-md-10 col-lg-6">
|
||||
|
||||
<!-- Carte contenant le formulaire d'inscription -->
|
||||
<div class="card shadow-sm border-0 rounded-4 p-4 p-lg-5">
|
||||
|
||||
<!-- Titre principal de la page -->
|
||||
<h1 class="h3 fw-bold mb-1">Inscription</h1>
|
||||
|
||||
<!-- Texte descriptif -->
|
||||
<p class="text-secondary mb-4">
|
||||
Créez votre compte utilisateur.
|
||||
</p>
|
||||
|
||||
<!-- Formulaire d'inscription -->
|
||||
<!-- Les données seront traitées côté serveur en PHP via la méthode POST -->
|
||||
<form method="POST">
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- Champ : prénom de l'utilisateur -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="user_firstname">
|
||||
Prénom *
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_firstname"
|
||||
name="user_firstname"
|
||||
value="{$objUser->getFirstname()|default:''}"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ : nom de l'utilisateur -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="user_name">
|
||||
Nom *
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_name"
|
||||
name="user_name"
|
||||
value="{$objUser->getName()|default:''}"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ : pseudo -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_pseudo">
|
||||
Pseudo *
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">@</span>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_pseudo"
|
||||
name="user_pseudo"
|
||||
value="{$objUser->getPseudo()|default:''}"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Champ : adresse e-mail -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_mail">
|
||||
Adresse e-mail *
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="email"
|
||||
id="user_mail"
|
||||
name="user_mail"
|
||||
value="{$objUser->getMail()|default:''}"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ : mot de passe -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_password">
|
||||
Mot de passe *
|
||||
</label>
|
||||
<div class="position-relative">
|
||||
<input
|
||||
class="form-control pe-5"
|
||||
type="password"
|
||||
id="user_pwd"
|
||||
name="user_pwd"
|
||||
required
|
||||
>
|
||||
<button
|
||||
class="btn position-absolute top-50 end-0 translate-middle-y me-2 p-0 border-0 bg-transparent text-muted"
|
||||
type="button"
|
||||
onclick="togglePassword('user_pwd', this)"
|
||||
style="z-index: 10;"
|
||||
>
|
||||
<i class="fa-solid fa-eye-slash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2 small text-muted">
|
||||
<p class="mb-1">Le mot de passe doit contenir :</p>
|
||||
<ul class="mb-0 ps-3">
|
||||
<li>Au moins <strong>15 caractères</strong></li>
|
||||
<li>Au moins <strong>une lettre majuscule</strong></li>
|
||||
<li>Au moins <strong>une lettre minuscule</strong></li>
|
||||
<li>Au moins <strong>un chiffre</strong></li>
|
||||
<li>Au moins <strong>un caractère spécial</strong> (# ? ! @ $ % ^ & * -)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Champ : confirmer le mot de passe -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="pwd_confirm">
|
||||
Confirmer le mot de passe *
|
||||
</label>
|
||||
<div class="position-relative">
|
||||
<input
|
||||
class="form-control"
|
||||
type="password"
|
||||
id="pwd_confirm"
|
||||
name="pwd_confirm"
|
||||
required
|
||||
><button
|
||||
class="btn position-absolute top-50 end-0 translate-middle-y me-2 p-0 border-0 bg-transparent text-muted"
|
||||
type="button"
|
||||
onclick="togglePassword('pwd_confirm', this)"
|
||||
style="z-index: 10;"
|
||||
>
|
||||
<i class="fa-solid fa-eye-slash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Champ optionnel : numéro de téléphone -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_phone">
|
||||
Téléphone
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_phone"
|
||||
name="user_phone"
|
||||
value="{$objUser->getPhone()|default:''}"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ optionnel : profession de l'utilisateur -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_work">
|
||||
Profession
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_work"
|
||||
name="user_work"
|
||||
value="{$objUser->getWork()|default:''}"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Champ optionnel : localisation de l'utilisateur -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_location">
|
||||
Localisation
|
||||
</label>
|
||||
<div class="position-relative">
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_location"
|
||||
name="user_location"
|
||||
value="{$objUser->getLocation()|default:''}"
|
||||
autocomplete="off"
|
||||
placeholder="Ex : Paris, Lyon..."
|
||||
>
|
||||
<!-- Liste déroulante des suggestions -->
|
||||
<ul
|
||||
id="location-suggestions"
|
||||
class="list-group position-absolute w-100 shadow-sm"
|
||||
style="z-index: 1000; display: none; max-height: 220px; overflow-y: auto; top: 100%; left: 0;"
|
||||
></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Champ optionnel : phrase d'accroche -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_description">
|
||||
Phrase d'accroche
|
||||
</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
id="user_description"
|
||||
name="user_description"
|
||||
rows="3"
|
||||
>{$objUser->getDescription()|default:''}</textarea>
|
||||
</div>
|
||||
|
||||
<!-- Bouton de soumission du formulaire -->
|
||||
<div class="col-12 d-grid mt-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg rounded-3">
|
||||
Créer mon compte
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Lien vers la page de connexion -->
|
||||
<div class="col-12 text-center">
|
||||
<small class="text-secondary">
|
||||
Déjà un compte ?
|
||||
<a class="link-primary" href="{$smarty.env.BASE_URL}/user/login">
|
||||
Se connecter
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
const pwd = document.getElementById('user_pwd');
|
||||
|
||||
const rules = {
|
||||
length: { el: null, test: v => v.length >= 15 },
|
||||
upper: { el: null, test: v => /[A-Z]/.test(v) },
|
||||
lower: { el: null, test: v => /[a-z]/.test(v) },
|
||||
digit: { el: null, test: v => /[0-9]/.test(v) },
|
||||
special: { el: null, test: v => /[#?!@$%^&*\-]/.test(v) }
|
||||
};
|
||||
|
||||
// Associe chaque règle à son <li> dans l'ordre du DOM
|
||||
const items = pwd.closest('.col-12').querySelectorAll('ul li');
|
||||
const keys = Object.keys(rules);
|
||||
items.forEach((li, i) => rules[keys[i]].el = li);
|
||||
|
||||
pwd.addEventListener('input', () => {
|
||||
const v = pwd.value;
|
||||
keys.forEach(key => {
|
||||
const { el, test } = rules[key];
|
||||
el.classList.toggle('text-success', test(v));
|
||||
el.classList.toggle('text-danger', !test(v));
|
||||
});
|
||||
});
|
||||
|
||||
function togglePassword(fieldId, btn) {
|
||||
const input = document.getElementById(fieldId);
|
||||
const icon = btn.querySelector('svg');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.classList.replace('fa-eye-slash', 'fa-eye');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.classList.replace('fa-eye', 'fa-eye-slash');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{include file="views/_partial/apigeo.tpl"}
|
||||
{/block}
|
||||
88
views/user.tpl
Normal file
88
views/user.tpl
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="content"}
|
||||
|
||||
{assign var="isMinou" value=($user->getPseudo()|stripos:'minou' !== false)}
|
||||
|
||||
<div class="w-100 align-self-start">
|
||||
|
||||
<!-- Profil -->
|
||||
<section class="mb-5 p-4 rounded-3 {if $isMinou}border border-dark border-2{/if}"
|
||||
{if $isMinou}style="background-color:#ff8c00; background-image:repeating-linear-gradient(45deg,transparent,transparent 30px,rgba(0,0,0,.15) 30px,rgba(0,0,0,.15) 60px);"{/if}>
|
||||
|
||||
<div class="row align-items-center g-4">
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="col-md-3 text-center">
|
||||
<img src="{$smarty.env.BASE_URL}/{$smarty.env.IMG_USER_PATH}{if ($smarty.env.IMG_USER_PATH|cat:($user->getImage()))|file_exists}{$user->getImage()}{else}images.jpg{/if}"
|
||||
alt="Avatar de {$user->getPseudo()}"
|
||||
class="rounded-circle border border-3 border-white shadow"
|
||||
style="width:180px; height:180px; object-fit:cover; {if $isMinou}outline:4px solid #000;{/if}">
|
||||
</div>
|
||||
|
||||
<!-- Infos -->
|
||||
<div class="col-md-9">
|
||||
<h1 class="fw-bold mb-1 {if $isMinou}text-uppercase text-dark{/if}">
|
||||
{$user->getPseudo()}
|
||||
</h1>
|
||||
<p class="text-muted mb-3">{$user->getMail()}</p>
|
||||
|
||||
{if $user->getWork()}
|
||||
<div class="d-flex align-items-center gap-2 mb-2">
|
||||
<i class="fa-solid fa-briefcase text-secondary"></i>
|
||||
<span>{$user->getWork()}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if $user->getLocation()}
|
||||
<div class="d-flex align-items-center gap-2 mb-2">
|
||||
<i class="fa-solid fa-location-dot text-secondary"></i>
|
||||
<span>{$user->getLocation()}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if $user->getDescription()}
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<i class="fa-regular fa-note-sticky text-secondary"></i>
|
||||
<span>{$user->getDescription()}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if isset($smarty.session.user) && $smarty.session.user.user_id == $user->getId()}
|
||||
<a href="{$smarty.env.BASE_URL}/user/edit"
|
||||
class="btn btn-sm {if $isMinou}btn-dark{else}btn-primary{/if}">
|
||||
✏️ Modifier mon profil
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Projets -->
|
||||
<section>
|
||||
<h2 class="mb-4 border-bottom pb-2">Les projets de {$user->getPseudo()}</h2>
|
||||
|
||||
<div class="row g-4">
|
||||
{if count($arrProjectToDisplay) > 0}
|
||||
{foreach $arrProjectToDisplay as $objProject}
|
||||
{assign var="isAuthor" value=(isset($smarty.session.user) && $smarty.session.user.user_id == $objProject->getUser_id())}
|
||||
{assign var="isModerator" value=(isset($smarty.session.user) && $smarty.session.user.user_status == 2)}
|
||||
{assign var="isDeleted" value=($objProject->getDeleted_at() !== null)}
|
||||
{assign var="isRefused" value=($objProject->getStatus() == "refusé")}
|
||||
|
||||
{if $isModerator || ($isAuthor && !$isDeleted) || (!$isRefused && !$isDeleted)}
|
||||
{include file="views/_partial/preview.tpl"}
|
||||
{/if}
|
||||
{/foreach}
|
||||
{else}
|
||||
<div class="col-12">
|
||||
<p class="alert alert-info">Cet utilisateur n'a pas encore publié de projets.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
{/block}
|
||||
211
views/useredit.tpl
Normal file
211
views/useredit.tpl
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
{extends file="views/layout.tpl"}
|
||||
|
||||
{block name="content"}
|
||||
<!-- Page : Inscription -->
|
||||
|
||||
|
||||
<main class="container py-5">
|
||||
|
||||
<!-- Centrage horizontal du formulaire -->
|
||||
<div class="row justify-content-center position-relative">
|
||||
<div class="col-12 col-md-10 col-lg-6">
|
||||
|
||||
<!-- Carte contenant le formulaire d'inscription -->
|
||||
<div class="card shadow-sm border-0 rounded-4 p-4 p-lg-5">
|
||||
|
||||
<!-- Titre principal de la page -->
|
||||
<h1 class="h3 fw-bold mb-1">Modification du profil</h1>
|
||||
|
||||
{if (isset($arrError) && count($arrError) > 0) }
|
||||
<div class="alert alert-danger">
|
||||
{foreach $arrError as $strError}
|
||||
<p>{$strError}</p>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Formulaire d'inscription -->
|
||||
<!-- Les données seront traitées côté serveur en PHP via la méthode POST -->
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="user_firstname">
|
||||
Prénom
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_firstname"
|
||||
name="user_firstname"
|
||||
value="{$objUser->getFirstname()}"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="user_name">
|
||||
Nom
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_name"
|
||||
name="user_name"
|
||||
value="{$objUser->getName()}"
|
||||
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_pseudo">
|
||||
Pseudo
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">@</span>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_pseudo"
|
||||
name="user_pseudo"
|
||||
value="{$objUser->getPseudo()}"
|
||||
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="image">Photo de profil</label>
|
||||
|
||||
<div class="d-flex align-items-center gap-4 mb-3">
|
||||
<!-- Avatar preview -->
|
||||
<img
|
||||
id="preview-avatar"
|
||||
src="{$smarty.env.BASE_URL}/{if $objUser->getImage()}{$smarty.env.IMG_USER_PATH}{$objUser->getImage()}{else}assests/img/images.jpg{/if}"
|
||||
alt="Photo de profil"
|
||||
class="rounded-circle border"
|
||||
style="width:80px; height:80px; object-fit:cover;"
|
||||
>
|
||||
|
||||
<!-- Actions à droite de l'avatar -->
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<label for="image" class="btn btn-outline-secondary btn-sm mb-0">
|
||||
<i class="fa-solid fa-upload me-1"></i> Changer la photo
|
||||
</label>
|
||||
{if $objUser->getImage() != "images.jpg"}
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" id="btn-delete-image">
|
||||
<i class="fa-solid fa-trash me-1"></i> Supprimer la photo
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Input file caché, déclenché par le label -->
|
||||
<input
|
||||
class="d-none"
|
||||
type="file"
|
||||
id="image"
|
||||
name="image"
|
||||
accept="image/jpeg, image/png, image/webp"
|
||||
>
|
||||
<input type="hidden" name="delete_image" id="delete_image" value="0">
|
||||
<div class="form-text">Formats acceptés : JPEG, PNG, WEBP. Laisser vide pour ne pas changer.</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_mail">
|
||||
Adresse e-mail
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="email"
|
||||
id="user_mail"
|
||||
name="user_mail"
|
||||
value="{$objUser->getMail()}"
|
||||
|
||||
>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_phone">
|
||||
Téléphone
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_phone"
|
||||
name="user_phone"
|
||||
value="{$objUser->getPhone()}"
|
||||
>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_work">
|
||||
Profession
|
||||
</label>
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_work"
|
||||
name="user_work"
|
||||
value="{$objUser->getWork()}"
|
||||
>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Champ optionnel : localisation de l'utilisateur -->
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_location">
|
||||
Localisation
|
||||
</label>
|
||||
<div class="position-relative">
|
||||
<input
|
||||
class="form-control"
|
||||
type="text"
|
||||
id="user_location"
|
||||
name="user_location"
|
||||
value="{$objUser->getLocation()}"
|
||||
autocomplete="off"
|
||||
placeholder="Ex : Paris, Lyon..."
|
||||
>
|
||||
<!-- Liste déroulante des suggestions -->
|
||||
<ul
|
||||
id="location-suggestions"
|
||||
class="list-group position-absolute w-100 shadow-sm"
|
||||
style="z-index: 1000; display: none; max-height: 220px; overflow-y: auto; top: 100%; left: 0;"
|
||||
></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="user_description">
|
||||
Phrase d'accroche
|
||||
</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
id="user_description"
|
||||
name="user_description"
|
||||
rows="3"
|
||||
>
|
||||
{$objUser->getDescription()}
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="col-12 d-grid mt-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg rounded-3">
|
||||
Confirmer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{include file="views/_partial/delphoto.tpl"}
|
||||
{include file="views/_partial/apigeo.tpl"}
|
||||
{/block}
|
||||
Loading…
Add table
Add a link
Reference in a new issue