diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index be3a9a0..e745855 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,8 +22,16 @@ jobs: script: | echo "➡️ Connexion réussie !" cd /var/www/projet_php - sudo git fetch origin main - sudo git reset --hard origin/main + + echo "➡️ Mise à jour du code..." + git fetch origin main + git reset --hard origin/main + + echo "➡️ Correction des permissions et nettoyage..." sudo chown -R yass:www-data /var/www/projet_php sudo chmod -R 775 /var/www/projet_php/templates_c - sudo rm -rf /var/www/projet_php/templates_c/* \ No newline at end of file + sudo rm -rf /var/www/projet_php/templates_c/* + sudo chmod -R 775 /var/www/projet_php/uploads/projects + sudo chmod -R 775 /var/www/projet_php/uploads/profiles + + echo "✅ Déploiement terminé ! (Shin-en no Egotisu)" diff --git a/README.md b/README.md deleted file mode 100644 index ff24f70..0000000 --- a/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Folliow – Projet PHP - -A platform for sharing portfolios and projects, designed as a mix between Behance and LinkedIn. Folliow focuses on highlighting real projects while making it easy to connect with other users. - -This project was developed as part of the **DWWM (Développeur Web et Web Mobile)** training. - ---- - -## Features - -- User authentication and profile management -- Portfolio and project creation -- Project showcase with descriptions and technologies -- User connections / follow system -- Project feed and discovery -- Profile and project search - ---- - -## Tech Stack - -**Client:** HTML, CSS, JavaScript -**Server:** PHP (MVC architecture) -**Database:** MySQL -**Web Server:** Apache - ---- - -## Project Structure - -```text -folliow/ -├── app/ -│ ├── controllers/ -│ ├── models/ -│ └── views/ -├── public/ -│ ├── assets/ -│ └── index.php -├── config/ -│ └── database.php -├── sql/ -│ └── folliow.sql -└── README.md -``` - ---- - -## Installation - -Clone the project - -```bash -git clone https://github.com/Yasder5/projet_php.git -``` - -Go to the project directory - -```bash -cd projet_php -``` - -Import the database - -- Use the SQL file located in the `sql/` directory - -Configure database access - -- Update credentials in `config/database.php` - -Run the project - -- Use a local server (XAMPP, WAMP, or Apache on Linux) - ---- - -## Learning Objectives - -- Build a complete PHP web application -- Apply MVC architecture -- Manage a relational database -- Design a user-oriented portfolio platform - ---- - -## Future Improvements - -- Private messaging -- Likes and comments on projects -- Tags and categories -- Improved responsive design -- Advanced authentication and roles - ---- - -## License - -This project is for educational purposes. diff --git a/assests/img/error403.jpg b/assests/img/error403.jpg new file mode 100644 index 0000000..ddb9376 Binary files /dev/null and b/assests/img/error403.jpg differ diff --git a/assests/img/error404.webp b/assests/img/error404.webp new file mode 100644 index 0000000..1fb29eb Binary files /dev/null and b/assests/img/error404.webp differ diff --git a/controllers/admin_controller.php b/controllers/admin_controller.php index 5fd9e56..b98252b 100644 --- a/controllers/admin_controller.php +++ b/controllers/admin_controller.php @@ -25,7 +25,6 @@ header("Location:index.php?ctrl=error&action=error_403"); exit; } - var_dump($_SESSION); //gestion de l'user $objCategoryModel = new CategoryModel; @@ -44,6 +43,7 @@ $editCat->setId($_POST['id_to_edit']); $editCat->setName($_POST['new_name']); $objCategoryModel->editCategory($editCat); + $_SESSION['success'] = "La catégorie a bien été modifiée"; header('Location: index.php?ctrl=admin&action=admin'); exit; } diff --git a/controllers/error_controller.php b/controllers/error_controller.php new file mode 100644 index 0000000..4ebead6 --- /dev/null +++ b/controllers/error_controller.php @@ -0,0 +1,24 @@ +_display("error_404"); + } + + /** + * Page erreur 403 + */ + public function error_403(){ + $this->_display("error_403"); + } + + } \ No newline at end of file diff --git a/controllers/project_controller.php b/controllers/project_controller.php index 8e33131..ae44662 100644 --- a/controllers/project_controller.php +++ b/controllers/project_controller.php @@ -25,6 +25,27 @@ public function home(){ + if (!isset($_SESSION['user']) && isset($_COOKIE['remember_me'])) { + + $token_du_cookie = $_COOKIE['remember_me']; + $hash_a_verifier = hash('sha256', $token_du_cookie); + + $objUserModel = new UserModel; + // 2. On cherche le jeton dans TA table "tokens" (avec token_user_id) + $row = $objUserModel->getTokenUser($hash_a_verifier); + + if ($row) { + // 3. Jeton trouvé ! On récupère les infos de l'utilisateur + $user = $objUserModel->findUserById($row['token_user_id']); + + if ($user) { + // 4. On recrée la session comme lors d'un login normal + $_SESSION['user'] = $user; + } + } + } + + $intCategory = 0; if (!empty($_GET['filter_cat'])) { $intCategory = (int) $_GET['filter_cat']; diff --git a/controllers/user_controller.php b/controllers/user_controller.php index d981df7..eafccef 100644 --- a/controllers/user_controller.php +++ b/controllers/user_controller.php @@ -36,7 +36,15 @@ class UserCtrl extends MotherCtrl { // Ajoute l'utilisateur en session $_SESSION['user'] = $arrResult; $_SESSION['success'] = "Bienvenue, vous êtes bien connecté"; - + if (isset($_POST['remember_me'])) { + + $token = bin2hex(random_bytes(32)); + $token_hash = hash('sha256', $token); + $objUserModel->remember($_SESSION['user']['user_id'],$token_hash); + + setcookie('remember_me', $token, time() + (15*24*60*60), "/", "", true, true); + + } header("Location:index.php"); exit; } @@ -49,11 +57,15 @@ class UserCtrl extends MotherCtrl { public function logout(){ - session_start(); - /*session_destroy(); - session_start();*/ + + if (isset($_COOKIE['remember_me'])) { + $hash = hash('sha256', $_COOKIE['remember_me']); + + $objUserModel = new UserModel; + $objUserModel->deleteToken($hash); - // on supprime l'utilisateur en session + setcookie('remember_me', '', time() - 3600, "/"); + } unset($_SESSION['user']); $_SESSION['success'] = "Vous êtes bien déconnecté"; @@ -62,8 +74,14 @@ class UserCtrl extends MotherCtrl { exit; } - - public function signin(){ + /** + * Fonction d'inscription d'un utilisateur + * Effectue les validations du formulaire, + * vérifie l'unicité du mail et du pseudo, + * puis insère l'utilisateur en base de données + * @return void + */ + public function signup(){ // Entité pour réafficher les valeurs dans le formulaire $objUser = new User(); @@ -101,33 +119,40 @@ class UserCtrl extends MotherCtrl { } if (trim($objUser->getMail()) === "") { - $arrError['user_mail'] = "Le mail est obligatoire"; + $arrError['user_mail'] = "L'adresse e-mail est obligatoire"; } elseif (!filter_var($objUser->getMail(), FILTER_VALIDATE_EMAIL)) { - $arrError['user_mail'] = "Le format du mail n'est pas correct"; + $arrError['user_mail'] = "Le format de l'adresse e-mail est invalide"; } if (trim($objUser->getPseudo()) === "") { $arrError['user_pseudo'] = "Le pseudo est obligatoire"; } - $strRegex = "/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{16,}$/"; + $strRegex = "/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{15,}$/"; if ($objUser->getPwd() == ""){ $arrError['user_password'] = "Le mot de passe est obligatoire"; }else if (!preg_match($strRegex, $objUser->getPwd())){ - $arrError['user_password'] = "Le mot de passe ne correspond pas aux règles"; + $arrError['user_password'] = "Le mot de passe ne respecte pas les critères"; }else if($objUser->getPwd() != $strPwdConfirm){ - $arrError['pwd_confirm'] = "Le mot de passe et sa confirmation ne sont pas identiques"; + $arrError['pwd_confirm'] = "La confirmation du mot de passe ne correspond pas"; } - // Si pas d'erreurs => insertion if (count($arrError) === 0) { $objUserModel = new UserModel(); + // Vérif mail if ($objUserModel->mailExists($objUser->getMail())) { + $arrError['user_mail'] = "Impossible de créer le compte avec ces informations"; + } - $arrError['user_mail'] = "Ce mail existe déjà"; - } else { + // Vérif pseudo + if ($objUserModel->pseudoExists($objUser->getPseudo())) { + $arrError['user_pseudo'] = "Ce pseudo existe déjà"; + } + + // Si aucune erreur => insert + if (count($arrError) === 0) { $boolInsert = $objUserModel->insert($objUser); if ($boolInsert === true) { @@ -142,172 +167,177 @@ class UserCtrl extends MotherCtrl { } // Affichage de la vue inscription - $this->_arrData["arrError"] = $arrError; - $this->_display("inscription"); - } - - /** - * le controlleur affichage de la page user - */ - public function user(){ - - /**$intId = isset($_GET['id']) ? (int)$_GET['id'] : 0; - - if ($intId <= 0) { - header("Location: index.php"); - exit; - } - - //affichage info utilisateur - $objUserModel = new UserModel; - $arrUserData = $objUserModel->findUserById($intId); - - if ($arrUserData === false) { - header("Location: index.php"); - exit; - }*/ - - $strPseudo = $_GET['pseudo']??''; - - $objUserModel = new UserModel; - $arrUserData = $objUserModel->findUserByPseudo($strPseudo); - - if ($arrUserData === false) { - header("Location: index.php"); - exit; - } - - $objUser = new User; - $objUser->hydrate($arrUserData); - - //affichage projet de l'utilisateur - $objProjectModel = new ProjectModel; - $arrProjects = $objProjectModel->findAll(0,'',$objUser->getId()); - - $arrProjectToDisplay = array(); - foreach($arrProjects as $projectData) { - $objProject = new Project(); - $objProject->hydrate($projectData); - $arrProjectToDisplay[] = $objProject; - } - - $this->_arrData['user'] = $objUser; - $this->_arrData['arrProjectToDisplay'] = $arrProjectToDisplay; - $this->_display("user"); - } - - public function edit(){ - if(!isset($_SESSION['user'])){ - header("Location: index.php"); - exit; + $this->_arrData['objUser'] = $objUser; + $this->_arrData['arrError'] = $arrError; + $this->_display("signup"); } + + - $objUserModel = new UserModel; - $arrError = []; - $objUser = new User; - $arrUserData = $objUserModel->findUserById($_SESSION['user']['user_id']); - $objUser->hydrate($arrUserData); - if (!empty($_POST)) { - if ($objUserModel->mailExists($_POST['user_mail']) && ($_POST['user_mail'] != $objUser->getMail())) { - - $arrError['user_mail'] = "Ce mail est déjà associé"; - } else { - if ($objUserModel->pseudoExists($_POST['user_pseudo']) && ($_POST['user_pseudo'] != $objUser->getPseudo())){ - $arrError['user_pseudo'] = "Ce pseudo est déjà utiliser"; - }else{ - $objUser->hydrate($_POST); - $objUser->setId($_SESSION['user']['user_id']); - - // Vérification de l'image - $arrTypeAllowed = array('image/jpeg', 'image/png', 'image/webp'); - $boolImageOk = true; - - if ($_FILES['image']['error'] != 4) { - if (!in_array($_FILES['image']['type'], $arrTypeAllowed)) { - $arrError['image'] = "Le type de fichier n'est pas autorisé"; - } else { - switch ($_FILES['image']['error']) { - case 0: - $strImageName = uniqid() . ".webp"; - $strOldImg = $objUser->getImage(); - $objUser->setImage($strImageName); - break; - case 1: - case 2: - $arrError['image'] = "Le fichier est trop volumineux"; - break; - case 3: - $arrError['image'] = "Le fichier a été partiellement téléchargé"; - break; - case 6: - $arrError['image'] = "Le répertoire temporaire est manquant"; - break; - default: - $arrError['image'] = "Erreur sur l'image"; - break; - } - } - } - - // Traitement de l'image si pas d'erreur - if (count($arrError) == 0 && isset($strImageName)) { - $strDest = $_ENV['IMG_USER_PATH'] . $strImageName; - $strSource = $_FILES['image']['tmp_name']; - list($intWidth, $intHeight) = getimagesize($strSource); - - $intDestWidth = 200; $intDestHeight = 200; - $fltDestRatio = $intDestWidth / $intDestHeight; - $fltSourceRatio = $intWidth / $intHeight; - - if ($fltSourceRatio > $fltDestRatio) { - $intCropHeight = $intHeight; - $intCropWidth = (int)round($intHeight * $fltDestRatio); - $intCropX = (int)(($intWidth - $intCropWidth) / 2); - $intCropY = 0; - } else { - $intCropWidth = $intWidth; - $intCropHeight = (int)round($intWidth / $fltDestRatio); - $intCropX = 0; - $intCropY = (int)(($intHeight - $intCropHeight) / 2); - } - - $objDest = imagecreatetruecolor($intDestWidth, $intDestHeight); - switch ($_FILES['image']['type']) { - case 'image/jpeg': $objSource = imagecreatefromjpeg($strSource); break; - case 'image/png': $objSource = imagecreatefrompng($strSource); break; - case 'image/webp': $objSource = imagecreatefromwebp($strSource); break; - } - - imagecopyresampled($objDest, $objSource, 0, 0, $intCropX, $intCropY, $intDestWidth, $intDestHeight, $intCropWidth, $intCropHeight); - $boolImageOk = imagewebp($objDest, $strDest); - imagedestroy($objDest); - imagedestroy($objSource); - } - $boolInsert = $objUserModel->update($objUser); + /** + * le controlleur affichage de la page user + */ + public function user(){ - if ($boolInsert === true) { - if (isset($strOldImg) && !empty($strOldImg) && isset($strImageName)) { - $strOldFile = $_ENV['IMG_USER_PATH'] . $strOldImg; - if (file_exists($strOldFile)) unlink($strOldFile); - } - $arrNewInfo = $objUserModel->findUserByPseudo($objUser->getPseudo()); - $_SESSION['user'] = $arrNewInfo; - $_SESSION['success'] = "Compte modifier avec succès"; - header("Location:?ctrl=user&action=user&pseudo=".$objUser->getPseudo()); - exit; - } else { - $arrError['global'] = "Erreur lors de l'update"; - } - } - } + /**$intId = isset($_GET['id']) ? (int)$_GET['id'] : 0; + + if ($intId <= 0) { + header("Location: index.php"); + exit; + } + + //affichage info utilisateur + $objUserModel = new UserModel; + $arrUserData = $objUserModel->findUserById($intId); + + if ($arrUserData === false) { + header("Location: index.php"); + exit; + }*/ + + $strPseudo = $_GET['pseudo']??''; + + $objUserModel = new UserModel; + $arrUserData = $objUserModel->findUserByPseudo($strPseudo); + + if ($arrUserData === false) { + header("Location: index.php"); + exit; + } + + $objUser = new User; + $objUser->hydrate($arrUserData); + + //affichage projet de l'utilisateur + $objProjectModel = new ProjectModel; + $arrProjects = $objProjectModel->findAll(0,'',$objUser->getId()); + + $arrProjectToDisplay = array(); + foreach($arrProjects as $projectData) { + $objProject = new Project(); + $objProject->hydrate($projectData); + $arrProjectToDisplay[] = $objProject; + } + + $this->_arrData['user'] = $objUser; + $this->_arrData['arrProjectToDisplay'] = $arrProjectToDisplay; + $this->_display("user"); } - $this->_arrData["arrError"] = $arrError; - $this->_arrData['objUser'] = $objUser; - $this->_display("useredit"); + public function edit(){ + if(!isset($_SESSION['user'])){ + header("Location: index.php"); + exit; + } + + $objUserModel = new UserModel; + $arrError = []; + $objUser = new User; + $arrUserData = $objUserModel->findUserById($_SESSION['user']['user_id']); + $objUser->hydrate($arrUserData); + if (!empty($_POST)) { + if ($objUserModel->mailExists($_POST['user_mail']) && ($_POST['user_mail'] != $objUser->getMail())) { - } - + $arrError['user_mail'] = "Ce mail est déjà associé"; + } else { + if ($objUserModel->pseudoExists($_POST['user_pseudo']) && ($_POST['user_pseudo'] != $objUser->getPseudo())){ + $arrError['user_pseudo'] = "Ce pseudo est déjà utiliser"; + }else{ + $objUser->hydrate($_POST); + $objUser->setId($_SESSION['user']['user_id']); + + // Vérification de l'image + $arrTypeAllowed = array('image/jpeg', 'image/png', 'image/webp'); + $boolImageOk = true; + + if ($_FILES['image']['error'] != 4) { + if (!in_array($_FILES['image']['type'], $arrTypeAllowed)) { + $arrError['image'] = "Le type de fichier n'est pas autorisé"; + } else { + switch ($_FILES['image']['error']) { + case 0: + $strImageName = uniqid() . ".webp"; + $strOldImg = $objUser->getImage(); + $objUser->setImage($strImageName); + break; + case 1: + case 2: + $arrError['image'] = "Le fichier est trop volumineux"; + break; + case 3: + $arrError['image'] = "Le fichier a été partiellement téléchargé"; + break; + case 6: + $arrError['image'] = "Le répertoire temporaire est manquant"; + break; + default: + $arrError['image'] = "Erreur sur l'image"; + break; + } + } + } + + // Traitement de l'image si pas d'erreur + if (count($arrError) == 0 && isset($strImageName)) { + $strDest = $_ENV['IMG_USER_PATH'] . $strImageName; + $strSource = $_FILES['image']['tmp_name']; + list($intWidth, $intHeight) = getimagesize($strSource); + + $intDestWidth = 200; $intDestHeight = 200; + $fltDestRatio = $intDestWidth / $intDestHeight; + $fltSourceRatio = $intWidth / $intHeight; + + if ($fltSourceRatio > $fltDestRatio) { + $intCropHeight = $intHeight; + $intCropWidth = (int)round($intHeight * $fltDestRatio); + $intCropX = (int)(($intWidth - $intCropWidth) / 2); + $intCropY = 0; + } else { + $intCropWidth = $intWidth; + $intCropHeight = (int)round($intWidth / $fltDestRatio); + $intCropX = 0; + $intCropY = (int)(($intHeight - $intCropHeight) / 2); + } + + $objDest = imagecreatetruecolor($intDestWidth, $intDestHeight); + switch ($_FILES['image']['type']) { + case 'image/jpeg': $objSource = imagecreatefromjpeg($strSource); break; + case 'image/png': $objSource = imagecreatefrompng($strSource); break; + case 'image/webp': $objSource = imagecreatefromwebp($strSource); break; + } + + imagecopyresampled($objDest, $objSource, 0, 0, $intCropX, $intCropY, $intDestWidth, $intDestHeight, $intCropWidth, $intCropHeight); + $boolImageOk = imagewebp($objDest, $strDest); + imagedestroy($objDest); + imagedestroy($objSource); + } + + + $boolInsert = $objUserModel->update($objUser); + + if ($boolInsert === true) { + if (isset($strOldImg) && !empty($strOldImg) && isset($strImageName)) { + $strOldFile = $_ENV['IMG_USER_PATH'] . $strOldImg; + if (file_exists($strOldFile)) unlink($strOldFile); + } + $arrNewInfo = $objUserModel->findUserByPseudo($objUser->getPseudo()); + $_SESSION['user'] = $arrNewInfo; + $_SESSION['success'] = "Compte modifier avec succès"; + header("Location:?ctrl=user&action=user&pseudo=".$objUser->getPseudo()); + exit; + } else { + $arrError['global'] = "Erreur lors de l'update"; + } + } + } + } + + $this->_arrData["arrError"] = $arrError; + $this->_arrData['objUser'] = $objUser; + $this->_display("useredit"); + + } + } diff --git a/database/.gitkeep b/database/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/database/database.sql b/database/database.sql deleted file mode 100644 index 649db7d..0000000 --- a/database/database.sql +++ /dev/null @@ -1,253 +0,0 @@ -CREATE DATABASE IF NOT EXISTS `projet_folliow` - DEFAULT CHARACTER SET utf8mb4 - COLLATE utf8mb4_unicode_ci; - -USE `projet_folliow`; - -/*M!999999\- enable the sandbox mode */ --- MariaDB dump 10.19 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) --- --- Host: localhost Database: projet_folliow --- ------------------------------------------------------ --- Server version 10.11.14-MariaDB-0+deb12u2 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8mb4 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `authorisation` --- - -DROP TABLE IF EXISTS `authorisation`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8mb4 */; -CREATE TABLE `authorisation` ( - `authorisation_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique des autorisations', - `authorisation_name` varchar(30) NOT NULL COMMENT 'Administrateur, modérateur, utilisateur', - PRIMARY KEY (`authorisation_id`), - UNIQUE KEY `uk_authorisation_name` (`authorisation_name`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `authorisation` --- - -LOCK TABLES `authorisation` WRITE; -/*!40000 ALTER TABLE `authorisation` DISABLE KEYS */; -INSERT INTO `authorisation` VALUES -(1,'Administrateur'), -(2,'Modérateur'), -(3,'Utilisateur'); -/*!40000 ALTER TABLE `authorisation` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `category` --- - -DROP TABLE IF EXISTS `category`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8mb4 */; -CREATE TABLE `category` ( - `category_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique des catégorie du projet', - `category_name` varchar(150) NOT NULL COMMENT 'Nom de chaque catégorie', - `category_parent` int(10) unsigned DEFAULT NULL COMMENT 'ID de la catégorie parente (NULL = catégorie principale)', - PRIMARY KEY (`category_id`), - UNIQUE KEY `uk_category_name_parent` (`category_name`,`category_parent`), - KEY `fk_category_parent` (`category_parent`), - CONSTRAINT `fk_category_parent` FOREIGN KEY (`category_parent`) REFERENCES `category` (`category_id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=54 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `category` --- - -LOCK TABLES `category` WRITE; -/*!40000 ALTER TABLE `category` DISABLE KEYS */; -INSERT INTO `category` VALUES -(32,'3D',4), -(34,'Aquarelle',4), -(5,'Architecture',NULL), -(36,'Architecture d\'Intérieur',5), -(35,'Architecture Moderne',5), -(24,'Backend',2), -(33,'Bande Dessinée',4), -(1,'Design',NULL), -(2,'Développement Web',NULL), -(31,'Digital Art',4), -(30,'Événementiel',3), -(23,'Frontend',2), -(25,'Full-Stack',2), -(4,'Illustration',NULL), -(20,'Logo & Identité Visuelle',1), -(39,'Marketing',NULL), -(46,'Marketing',NULL), -(26,'Mobile',2), -(21,'Motion Design',1), -(38,'Patrimoine',5), -(28,'Paysage',3), -(3,'Photographie',NULL), -(27,'Portrait',3), -(22,'Print Design',1), -(40,'Rédaction',NULL), -(47,'Rédaction',NULL), -(29,'Sport',3), -(19,'UI/UX Design',1), -(37,'Urbanisme',5), -(41,'Vidéo',NULL), -(48,'Vidéo',NULL); -/*!40000 ALTER TABLE `category` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `image` --- - -DROP TABLE IF EXISTS `image`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8mb4 */; -CREATE TABLE `image` ( - `image_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'identifiant unique des images', - `image_name` varchar(150) NOT NULL COMMENT 'Chemin de l''image enregistrée', - `image_alt` varchar(255) DEFAULT NULL COMMENT 'Alt de l''image', - `image_status` varchar(50) NOT NULL DEFAULT 'en_attente' COMMENT 'Statut de modération de l''image', - `image_project` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant du projet où se trouve l''image', - PRIMARY KEY (`image_id`), - KEY `fk_image_project` (`image_project`), - CONSTRAINT `fk_image_project` FOREIGN KEY (`image_project`) REFERENCES `project` (`project_id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `chk_image_status` CHECK (`image_status` in ('en_attente','approuvé','rejeté')) -) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `image` --- - -LOCK TABLES `image` WRITE; -/*!40000 ALTER TABLE `image` DISABLE KEYS */; -INSERT INTO `image` VALUES -(1,'/uploads/projects/ecommerce-01.jpg','Page d\'accueil du site e-commerce refondu','approuvé',1), -(2,'/uploads/projects/ecommerce-02.jpg','Interface mobile du processus de commande','approuvé',1), -(3,'/uploads/projects/taskmanager-01.jpg','Vue kanban de l\'application','approuvé',2), -(4,'/uploads/projects/urban-01.jpg','Architecture moderne à La Défense','en_attente',3); -/*!40000 ALTER TABLE `image` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `project` --- - -DROP TABLE IF EXISTS `project`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8mb4 */; -CREATE TABLE `project` ( - `project_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique du projet', - `project_title` varchar(150) NOT NULL COMMENT 'Titre du projet', - `project_description` text DEFAULT NULL COMMENT 'Description du projet', - `project_thumbnail` varchar(150) DEFAULT NULL COMMENT 'Image miniature du projet', - `project_content` text DEFAULT NULL COMMENT 'Contenu du projet', - `project_creation_date` date NOT NULL COMMENT 'Date de création du projet', - `project_status` varchar(30) NOT NULL DEFAULT 'brouillon' COMMENT 'Statut du projet : brouillon, publié, en_attente', - `project_user` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant de l''user', - `project_category` int(10) unsigned DEFAULT NULL COMMENT 'Identifiant de la catégorie', - - PRIMARY KEY (`project_id`), - KEY `fk_project_user` (`project_user`), - KEY `fk_project_category` (`project_category`), - KEY `idx_project_status` (`project_status`), - KEY `idx_project_creation_date` (`project_creation_date`), - - CONSTRAINT `fk_project_category` - FOREIGN KEY (`project_category`) - REFERENCES `category` (`category_id`) - ON DELETE SET NULL - ON UPDATE CASCADE, - - CONSTRAINT `fk_project_user` - FOREIGN KEY (`project_user`) - REFERENCES `users` (`user_id`) - ON DELETE CASCADE - ON UPDATE CASCADE -) ENGINE=InnoDB -DEFAULT CHARSET=utf8mb4 -COLLATE=utf8mb4_unicode_ci; - -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `project` --- - -LOCK TABLES `project` WRITE; -/*!40000 ALTER TABLE `project` DISABLE KEYS */; -INSERT INTO `project` VALUES -(1,'Refonte du site e-commerce','Refonte complète de l\'interface utilisateur d\'une boutique en ligne avec focus sur l\'expérience mobile','/uploads/projects/ecommerce-thumb.jpg','Ce projet visait à moderniser entièrement l\'interface d\'un site e-commerce existant. Les principales améliorations incluent une navigation simplifiée, un processus de commande optimisé et une interface responsive.','2025-12-01','publié',1,1), -(2,'Application de gestion de tâches','Développement d\'une application web pour la gestion collaborative de projets','/uploads/projects/taskmanager-thumb.jpg','Application web développée en React et Node.js permettant aux équipes de gérer leurs projets de manière collaborative. Fonctionnalités : kanban, calendrier, notifications en temps réel.','2025-12-15','publié',2,2), -(3,'Série photo urbaine','Collection de photographies capturant l\'architecture moderne de Paris','/uploads/projects/urban-thumb.jpg','Série de 30 photographies prises dans différents quartiers de Paris, mettant en valeur le contraste entre architecture classique et moderne. Travail sur la lumière naturelle et les perspectives.','2026-01-05','en_attente',3,3); -/*!40000 ALTER TABLE `project` ENABLE KEYS */; -UNLOCK TABLES; - --- --- Table structure for table `users` --- - -DROP TABLE IF EXISTS `users`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8mb4 */; -CREATE TABLE `users` ( - `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique de l''utilisateur', - `user_name` varchar(100) NOT NULL COMMENT 'Nom de l''utilisateur', - `user_firstname` varchar(100) NOT NULL COMMENT 'Prénom de l''utilisateur', - `user_pseudo` varchar(30) NOT NULL COMMENT 'Pseudo de l''utilisateur', - `user_image` varchar(150) DEFAULT NULL COMMENT 'Photo de profil de l''utilisateur', - `user_mail` varchar(100) NOT NULL COMMENT 'Mail de l''utilisateur', - `user_password` varchar(255) NOT NULL COMMENT 'Mot de passe de l''utilisateur', - `user_phone` varchar(20) DEFAULT NULL COMMENT 'Téléphone de l''utilisateur', - `user_work` varchar(50) DEFAULT NULL COMMENT 'Profession de l''utilisateur', - `user_birth` date DEFAULT NULL COMMENT 'Date de naissance de l''utilisateur', - `user_location` varchar(150) DEFAULT NULL COMMENT 'Localisation de l''utilisateur', - `user_description` varchar(255) DEFAULT NULL COMMENT 'Phrase d''accroche de l''utilisateur', - `user_account_creation` datetime NOT NULL DEFAULT current_timestamp() COMMENT 'Date et heure de création du compte', - `user_status` int(10) unsigned DEFAULT NULL COMMENT 'identifiant de niveau d''autorisation', - PRIMARY KEY (`user_id`), - UNIQUE KEY `uk_user_mail` (`user_mail`), - UNIQUE KEY `uk_user_pseudo` (`user_pseudo`), - KEY `fk_users_authorisation` (`user_status`), - KEY `idx_user_mail` (`user_mail`), - CONSTRAINT `fk_users_authorisation` FOREIGN KEY (`user_status`) REFERENCES `authorisation` (`authorisation_id`) ON DELETE SET NULL ON UPDATE CASCADE -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `users` --- - -LOCK TABLES `users` WRITE; -/*!40000 ALTER TABLE `users` DISABLE KEYS */; -INSERT INTO `users` VALUES -(1,'Dupont','Marie','marie_design','/uploads/users/marie.jpg','marie.dupont@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0612345678','Designer UI/UX','1995-03-15','Paris, France','Passionnée de design et d\'expérience utilisateur','2024-01-15 10:30:00',1), -(2,'Martin','Thomas','thomas_dev','/uploads/users/thomas.jpg','thomas.martin@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0623456789','Développeur Full-Stack','1992-07-22','Lyon, France','Créateur d\'applications web modernes','2024-02-20 14:45:00',2), -(3,'Dubois','Sophie','sophie_photo','/uploads/users/sophie.jpg','sophie.dubois@example.com','$2y$10$T5kWRD3NBqXKWtQqrBG3We1Qpq2Odum0/xYAoT2SaCclG7h2Y0Gvq','0634567890','Photographe','1998-11-08','Marseille, France','Capturer l\'instant présent est ma passion','2024-03-10 09:15:00',3); -/*!40000 ALTER TABLE `users` ENABLE KEYS */; -UNLOCK TABLES; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2026-01-09 17:33:08 diff --git a/env b/env index af95f41..1ba2754 100644 --- a/env +++ b/env @@ -4,4 +4,8 @@ DB_CONNECTION=mysql DB_HOSTNAME=boulayoune.com DB_DATABASE=projet_folliow DB_USERNAME= -DB_PASSWORD= \ No newline at end of file +DB_PASSWORD= + +# image path +IMG_PROJECT_PATH = uploads/projects/ +IMG_USER_PATH = uploads/profiles/ \ No newline at end of file diff --git a/index.php b/index.php index 320007f..4013681 100644 --- a/index.php +++ b/index.php @@ -12,7 +12,7 @@ $boolError = false; $strFileName = "./controllers/".$strCtrl."_controller.php"; - + if(file_exists($strFileName)){ require($strFileName); $strClassName = ucfirst($strCtrl)."Ctrl"; @@ -31,6 +31,6 @@ } if($boolError){ - echo "error 404 - la page elle existe pas "; + header("Location:index.php?ctrl=error&action=error_404"); } diff --git a/models/mother_model.php b/models/mother_model.php index c3f088c..7758599 100644 --- a/models/mother_model.php +++ b/models/mother_model.php @@ -23,14 +23,14 @@ } /** - Pour passer sur le serveur de YASS: + *Pour passer sur le serveur de YASS: *"mysql:host=boulayoune.com;dbname=projet_folliow", // Serveur et BDD "projet_user", //Nom d'utilisateur de la base de données "F0lliowRules!",// Mot de passe de la base de données Site pour BDD: https://phpmyadmin.boulayoune.com/index.php?route=/sql&pos=0&db=projet_folliow&table=project - Pour passer en local: - "mysql:host=localhost;dbname=projet_folliow", // Serveur et BDD - "root", //Nom d'utilisateur de la base de données - "",// Mot de passe de la base de données + *Pour passer en local: + *"mysql:host=localhost;dbname=projet_folliow", // Serveur et BDD + *"root", //Nom d'utilisateur de la base de données + *"",// Mot de passe de la base de données */ \ No newline at end of file diff --git a/models/project_model.php b/models/project_model.php index 73e8a87..04bd1a6 100644 --- a/models/project_model.php +++ b/models/project_model.php @@ -16,72 +16,90 @@ * @return array */ public function findAll(int $intLimit=0, string $strKeywords='', int $intAuthor=0, - int $intPeriod=0, string $strDate='', string $strStartDate='', - string $strEndDate='', int $intCategory=0, bool $bool6Months=false):array{ + int $intPeriod=0, string $strDate='', string $strStartDate='', + string $strEndDate='', int $intCategory=0, bool $boolOlderThan6Months=false): array { + $strRq = "SELECT project.*, user_pseudo AS 'project_creatorname', user_image FROM project - INNER JOIN users ON user_id = project_user_id"; - - $strWhere = " WHERE "; + INNER JOIN users ON user_id = project_user_id + WHERE 1=1"; - // Recherche par mot clé avec quote pour éviter bug du ' if ($strKeywords != '') { - - $strSafeKeywords = $this->_db->quote("%" . $strKeywords . "%"); - - $strRq .= " WHERE (project_title LIKE ".$strSafeKeywords." - OR project_content LIKE ".$strSafeKeywords.") "; - - //$boolWhere = true; - $strWhere = " AND "; + $strRq .= " AND (project_title LIKE :keywords OR project_content LIKE :keywords)"; } - - // Recherche par auteur + if ($intAuthor > 0){ - $strRq .= $strWhere." user_id = ".$intAuthor; - $strWhere = " AND "; + $strRq .= " AND project_user_id = :author"; } - // Recherche par catégorie if ($intCategory > 0){ - $strRq .= $strWhere." project_category = ".$intCategory; - $strWhere = " AND "; + $strRq .= " AND project_category = :category"; } - //recherche par ancienneté - if ($bool6Months === true) { - $strRq .= $strWhere . " project_creation_date <= DATE_SUB(NOW(), INTERVAL 6 MONTH) "; - $strWhere = " AND "; + if ($boolOlderThan6Months === true) { + $strRq .= " AND project_creation_date <= DATE_SUB(NOW(), INTERVAL 6 MONTH)"; } - - // Recherche par dates + if ($intPeriod == 0){ if ($strDate != ''){ - $strRq .= $strWhere." project_creation_date = '".$strDate."'"; + $strRq .= " AND project_creation_date = :date_exacte"; } - }else{ + } else { if ($strStartDate != '' && $strEndDate != ''){ - $strRq .= $strWhere." project_creation_date BETWEEN '".$strStartDate."' AND '".$strEndDate."'"; - }else{ + $strRq .= " AND project_creation_date BETWEEN :date_debut AND :date_fin"; + } else { if ($strStartDate != ''){ - $strRq .= $strWhere." project_creation_date >= '".$strStartDate."'"; - }else if ($strEndDate != ''){ - $strRq .= $strWhere." project_creation_date <= '".$strEndDate."'"; + $strRq .= " AND project_creation_date >= :date_debut"; + } else if ($strEndDate != ''){ + $strRq .= " AND project_creation_date <= :date_fin"; } } } - + $strRq .= " ORDER BY project_creation_date DESC"; if ($intLimit > 0){ - $strRq .= " LIMIT ".$intLimit; + $strRq .= " LIMIT :limit"; } - return $this->_db->query($strRq)->fetchAll(); + $rqPrep = $this->_db->prepare($strRq); + + if ($strKeywords != '') { + $rqPrep->bindValue(':keywords', '%' . $strKeywords . '%', PDO::PARAM_STR); + } + if ($intAuthor > 0){ + $rqPrep->bindValue(':author', $intAuthor, PDO::PARAM_INT); + } + if ($intCategory > 0){ + $rqPrep->bindValue(':category', $intCategory, PDO::PARAM_INT); + } + if ($intPeriod == 0){ + if ($strDate != ''){ + $rqPrep->bindValue(':date_exacte', $strDate, PDO::PARAM_STR); + } + } else { + if ($strStartDate != '' && $strEndDate != ''){ + $rqPrep->bindValue(':date_debut', $strStartDate, PDO::PARAM_STR); + $rqPrep->bindValue(':date_fin', $strEndDate, PDO::PARAM_STR); + } else { + if ($strStartDate != ''){ + $rqPrep->bindValue(':date_debut', $strStartDate, PDO::PARAM_STR); + } else if ($strEndDate != ''){ + $rqPrep->bindValue(':date_fin', $strEndDate, PDO::PARAM_STR); + } + } + } + if ($intLimit > 0){ + $rqPrep->bindValue(':limit', $intLimit, PDO::PARAM_INT); + } + + $rqPrep->execute(); + return $rqPrep->fetchAll(); } + /** * Fonction d'insertion d'un nouveau projet dans la bdd @@ -122,7 +140,7 @@ */ public function findOne(int $intId) :array{ $strRq = "SELECT project.*, - CONCAT(users.user_firstname, ' ', users.user_name) AS 'project_creatorname', + users.user_pseudo AS 'project_creatorname', users.user_image, category.category_name FROM project @@ -145,12 +163,10 @@ */ public function accept(int $id){ - //SQL pour changer le status en accept $strRq = "UPDATE project SET project_status= 'publié' WHERE project_id =".$id; - //retourne la commande return $this->_db->query($strRq); } diff --git a/models/user_model.php b/models/user_model.php index f17b9a3..9c6c7ce 100644 --- a/models/user_model.php +++ b/models/user_model.php @@ -70,6 +70,28 @@ return $rqPrep->execute(); } + public function remember(int $userId, string $token):bool{ + $strRq = "INSERT INTO tokens (token_user_id, token_hash, token_created_at, token_expire_at) VALUES (:id, :token, NOW(), :exp)"; + $rqPrep = $this->_db->prepare($strRq); + $rqPrep->bindValue(":id", $userId, PDO::PARAM_INT); + $rqPrep->bindValue(":token", $token, PDO::PARAM_STR); + $rqPrep->bindValue(":exp", + //pour faire que le cookies soit valable 15 jours + date('Y-m-d H:i:s', time() + (15*24*60*60)) + , PDO::PARAM_STR); + return $rqPrep->execute(); + + } + public function getTokenUser(string $hash){ + $strRq = $this->_db->prepare("SELECT token_user_id FROM tokens WHERE token_hash = :hash AND token_expire_at > NOW()"); + $strRq->execute(['hash' => $hash]); + return $strRq->fetch(); + } + public function deleteToken(string $hash){ + $strRq = $this->_db->prepare("DELETE FROM tokens WHERE token_hash = :hash"); + return $strRq->execute(['hash' => $hash]); + } + public function update(object $objUser):bool{ $strRq = "UPDATE users SET user_name = :name, diff --git a/sfq.txt b/sfq.txt deleted file mode 100644 index e69de29..0000000 diff --git a/uploads/profiles/thomas.jpg b/uploads/profiles/thomas.jpg deleted file mode 100644 index 051578c..0000000 Binary files a/uploads/profiles/thomas.jpg and /dev/null differ diff --git a/views/_partial/footer.tpl b/views/_partial/footer.tpl index 9eaa238..bde1b20 100644 --- a/views/_partial/footer.tpl +++ b/views/_partial/footer.tpl @@ -5,7 +5,7 @@
  • Découvrir
  • Customisation
  • Emploi -
  • A propos +
  • A propos
    @@ -19,7 +19,7 @@
    @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/views/_partial/header.tpl b/views/_partial/header.tpl index 395bd39..06911af 100644 --- a/views/_partial/header.tpl +++ b/views/_partial/header.tpl @@ -7,7 +7,7 @@ - Folliow + Folliow{block name="title"}{/block}