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
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