Initial commit - mini cms final
This commit is contained in:
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/infrastructure" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
session_start();
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|
||||||
|
// ✅ Vérification que l'utilisateur est connecté
|
||||||
|
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$author_id = $_SESSION['user']['id'];
|
||||||
|
$message = '';
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$title = trim($_POST['title'] ?? '');
|
$title = trim($_POST['title'] ?? '');
|
||||||
$content = trim($_POST['content'] ?? '');
|
$content = trim($_POST['content'] ?? '');
|
||||||
$author_id = $_SESSION['user_id'] ?? null;
|
|
||||||
|
|
||||||
if (!$author_id) {
|
|
||||||
echo "❌ Erreur : utilisateur non connecté.";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$imageUrl = null;
|
$imageUrl = null;
|
||||||
|
|
||||||
global $s3Client, $bucketName;
|
global $s3Client, $bucketName;
|
||||||
|
|
||||||
|
// ✅ Upload de l'image sur MinIO
|
||||||
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
|
||||||
$fileName = time() . '_' . basename($_FILES['image']['name']);
|
$fileName = time() . '_' . basename($_FILES['image']['name']);
|
||||||
$tmpPath = $_FILES['image']['tmp_name'];
|
$tmpPath = $_FILES['image']['tmp_name'];
|
||||||
@@ -27,19 +32,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
]);
|
]);
|
||||||
$imageUrl = "http://localhost:9000/$bucketName/$fileName";
|
$imageUrl = "http://localhost:9000/$bucketName/$fileName";
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo "❌ Erreur upload MinIO : " . $e->getMessage();
|
$message = "❌ Erreur upload MinIO : " . $e->getMessage();
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ Insertion de l'article dans la base de données
|
||||||
|
if ($title && $content) {
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("INSERT INTO posts (title, content, image_url, user_id, date_creation)
|
$stmt = $pdo->prepare("
|
||||||
VALUES (?, ?, ?, ?, NOW())");
|
INSERT INTO posts (title, content, image_url, user_id, date_creation)
|
||||||
|
VALUES (?, ?, ?, ?, NOW())
|
||||||
|
");
|
||||||
$stmt->execute([$title, $content, $imageUrl, $author_id]);
|
$stmt->execute([$title, $content, $imageUrl, $author_id]);
|
||||||
header("Location: dashboard.php");
|
|
||||||
|
// ✅ Redirection vers la page d'accueil
|
||||||
|
header("Location: index.php");
|
||||||
exit;
|
exit;
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
echo "❌ Erreur base de données : " . $e->getMessage();
|
$message = "❌ Erreur base de données : " . $e->getMessage();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = "⚠️ Tous les champs doivent être remplis.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -140,6 +153,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>📝 Publier un nouvel article</h2>
|
<h2>📝 Publier un nouvel article</h2>
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<p style="color:red;"><?= htmlspecialchars($message) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
<label for="title">Titre :</label>
|
<label for="title">Titre :</label>
|
||||||
<input type="text" id="title" name="title" required>
|
<input type="text" id="title" name="title" required>
|
||||||
@@ -153,7 +170,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
<button type="submit">Publier l’article</button>
|
<button type="submit">Publier l’article</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<?php if ($_SESSION['user']['role'] === 'admin'): ?>
|
||||||
<a href="dashboard.php" class="retour">⬅ Retour au tableau de bord</a>
|
<a href="dashboard.php" class="retour">⬅ Retour au tableau de bord</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="index.php" class="retour">⬅ Retour à l’accueil</a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>🕒 <?= date('Y') ?> — Mini CMS by Aya 💖</footer>
|
<footer>🕒 <?= date('Y') ?> — Mini CMS by Aya 💖</footer>
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
use Aws\S3\S3Client;
|
use Aws\S3\S3Client;
|
||||||
|
|
||||||
// ✅ Connexion PDO
|
// ✅ Connexion PDO MySQL UTF-8 universelle
|
||||||
try {
|
try {
|
||||||
$pdo = new PDO(
|
$pdo = new PDO(
|
||||||
'mysql:host=mysql;dbname=forum_database;charset=utf8mb4',
|
'mysql:host=mysql;dbname=forum_database;charset=utf8mb4',
|
||||||
@@ -15,14 +14,15 @@ try {
|
|||||||
'mypassword',
|
'mypassword',
|
||||||
[
|
[
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
die("Erreur de connexion MySQL : " . $e->getMessage());
|
die("❌ Erreur de connexion MySQL : " . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Client MinIO
|
// ✅ Configuration du client MinIO (S3-compatible)
|
||||||
$s3Client = new S3Client([
|
$s3Client = new S3Client([
|
||||||
'version' => 'latest',
|
'version' => 'latest',
|
||||||
'region' => 'us-east-1',
|
'region' => 'us-east-1',
|
||||||
@@ -35,3 +35,36 @@ $s3Client = new S3Client([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$bucketName = 'bucketforum';
|
$bucketName = 'bucketforum';
|
||||||
|
|
||||||
|
// ✅ S’assurer que le bucket existe (création auto si absent)
|
||||||
|
try {
|
||||||
|
$buckets = $s3Client->listBuckets();
|
||||||
|
$bucketExists = false;
|
||||||
|
foreach ($buckets['Buckets'] as $bucket) {
|
||||||
|
if ($bucket['Name'] === $bucketName) {
|
||||||
|
$bucketExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$bucketExists) {
|
||||||
|
$s3Client->createBucket(['Bucket' => $bucketName]);
|
||||||
|
// Rendre le bucket public automatiquement
|
||||||
|
$policy = json_encode([
|
||||||
|
'Version' => '2012-10-17',
|
||||||
|
'Statement' => [[
|
||||||
|
'Effect' => 'Allow',
|
||||||
|
'Principal' => '*',
|
||||||
|
'Action' => ['s3:GetObject'],
|
||||||
|
'Resource' => "arn:aws:s3:::{$bucketName}/*"
|
||||||
|
]]
|
||||||
|
]);
|
||||||
|
$s3Client->putBucketPolicy([
|
||||||
|
'Bucket' => $bucketName,
|
||||||
|
'Policy' => $policy
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "⚠️ Impossible de vérifier/créer le bucket MinIO : " . $e->getMessage();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
// Démarre la session et le tampon proprement (évite les erreurs de headers)
|
|
||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
if (!ob_get_level()) ob_start();
|
if (!ob_get_level()) ob_start();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
@@ -42,40 +40,60 @@ if (!ob_get_level()) ob_start();
|
|||||||
color: #fff8f9;
|
color: #fff8f9;
|
||||||
text-shadow: 0 0 5px white;
|
text-shadow: 0 0 5px white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ✅ Zone cliquable agrandie et fluide */
|
||||||
.profile-menu {
|
.profile-menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 25px;
|
||||||
|
transition: background 0.3s;
|
||||||
}
|
}
|
||||||
|
.profile-menu:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.profile-pic {
|
.profile-pic {
|
||||||
width: 40px;
|
width: 42px;
|
||||||
height: 40px;
|
height: 42px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border: 2px solid white;
|
border: 2px solid white;
|
||||||
cursor: pointer;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
top: 55px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 3px 10px rgba(0,0,0,0.15);
|
box-shadow: 0 3px 12px rgba(0,0,0,0.15);
|
||||||
margin-top: 8px;
|
min-width: 180px;
|
||||||
min-width: 160px;
|
overflow: hidden;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown a {
|
.dropdown a {
|
||||||
color: #ff91a4;
|
color: #ff91a4;
|
||||||
padding: 10px 15px;
|
padding: 12px 16px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
|
transition: 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown a:hover {
|
.dropdown a:hover {
|
||||||
background: #ffe5ec;
|
background: #ffe5ec;
|
||||||
}
|
}
|
||||||
.profile-menu:hover .dropdown {
|
|
||||||
|
/* ✅ Ouverture plus fluide (hover ou clic sur toute la zone) */
|
||||||
|
.profile-menu:hover .dropdown,
|
||||||
|
.profile-menu:focus-within .dropdown {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -105,7 +123,7 @@ if (!ob_get_level()) ob_start();
|
|||||||
<a href="dashboard.php">Gestion</a>
|
<a href="dashboard.php">Gestion</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<a href="add_article.php">Publier un article</a>
|
<a href="add_article.php">Publier un article</a>
|
||||||
<div class="profile-menu">
|
<div class="profile-menu" tabindex="0">
|
||||||
<?php if (!empty($_SESSION['user']['profile_picture'])): ?>
|
<?php if (!empty($_SESSION['user']['profile_picture'])): ?>
|
||||||
<img src="<?= htmlspecialchars($_SESSION['user']['profile_picture']) ?>" alt="Profil" class="profile-pic">
|
<img src="<?= htmlspecialchars($_SESSION['user']['profile_picture']) ?>" alt="Profil" class="profile-pic">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@@ -117,7 +135,6 @@ if (!ob_get_level()) ob_start();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<!-- Boutons connexion / inscription -->
|
|
||||||
<a href="login.php">Connexion</a>
|
<a href="login.php">Connexion</a>
|
||||||
<a href="register.php">Inscription</a>
|
<a href="register.php">Inscription</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -1,59 +1,195 @@
|
|||||||
<?php
|
<?php
|
||||||
// /var/www/html/profile.php
|
|
||||||
session_start();
|
session_start();
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
include 'header.php';
|
|
||||||
|
|
||||||
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) {
|
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['id'])) {
|
||||||
header('Location: login.php'); exit;
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
$userId = (int)$_SESSION['user']['id'];
|
|
||||||
|
$userId = $_SESSION['user']['id'];
|
||||||
$message = '';
|
$message = '';
|
||||||
|
|
||||||
// POST handling (update or delete)
|
// ✅ Mise à jour de la bio et/ou de la photo
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$uploadDir = __DIR__ . '/uploads/profiles/';
|
|
||||||
if (!is_dir($uploadDir)) mkdir($uploadDir, 0777, true);
|
|
||||||
|
|
||||||
// delete?
|
|
||||||
if (isset($_POST['delete_picture'])) {
|
|
||||||
// remove file if any
|
|
||||||
if (!empty($_SESSION['user']['profile_picture']) && file_exists(__DIR__ . '/' . $_SESSION['user']['profile_picture'])) {
|
|
||||||
@unlink(__DIR__ . '/' . $_SESSION['user']['profile_picture']);
|
|
||||||
}
|
|
||||||
$stmt = $pdo->prepare("UPDATE utilisateurs SET profile_picture = NULL WHERE id = ?");
|
|
||||||
$stmt->execute([$userId]);
|
|
||||||
$_SESSION['user']['profile_picture'] = null;
|
|
||||||
header('Location: profile.php'); exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update (bio + optional file)
|
|
||||||
$bio = trim($_POST['bio'] ?? '');
|
$bio = trim($_POST['bio'] ?? '');
|
||||||
$profilePath = $_SESSION['user']['profile_picture'] ?? null;
|
$profilePictureUrl = $_SESSION['user']['profile_picture'] ?? null;
|
||||||
if (!empty($_FILES['profile_picture']['name'])) {
|
|
||||||
$fname = time() . '_' . preg_replace('/[^A-Za-z0-9_.-]/', '', basename($_FILES['profile_picture']['name']));
|
global $s3Client, $bucketName;
|
||||||
$target = $uploadDir . $fname;
|
|
||||||
if (in_array(strtolower(pathinfo($fname, PATHINFO_EXTENSION)), ['jpg','jpeg','png']) && move_uploaded_file($_FILES['profile_picture']['tmp_name'], $target)) {
|
// 📸 Upload d'une nouvelle image
|
||||||
$profilePath = 'uploads/profiles/' . $fname;
|
if (isset($_FILES['profile_picture']) && $_FILES['profile_picture']['error'] === UPLOAD_ERR_OK) {
|
||||||
} else {
|
$fileName = 'profile_' . $userId . '_' . time() . '_' . basename($_FILES['profile_picture']['name']);
|
||||||
$message = "❌ Problème lors de l'upload (format jpg/png uniquement).";
|
$tmpPath = $_FILES['profile_picture']['tmp_name'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$s3Client->putObject([
|
||||||
|
'Bucket' => $bucketName,
|
||||||
|
'Key' => $fileName,
|
||||||
|
'SourceFile' => $tmpPath,
|
||||||
|
'ACL' => 'public-read'
|
||||||
|
]);
|
||||||
|
$profilePictureUrl = "http://localhost:9000/$bucketName/$fileName";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$message = "❌ Erreur upload MinIO : " . $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// update DB
|
|
||||||
|
// 🧹 Suppression de la photo
|
||||||
|
if (isset($_POST['delete_picture'])) {
|
||||||
|
$profilePictureUrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$stmt = $pdo->prepare("UPDATE utilisateurs SET bio = ?, profile_picture = ? WHERE id = ?");
|
$stmt = $pdo->prepare("UPDATE utilisateurs SET bio = ?, profile_picture = ? WHERE id = ?");
|
||||||
$stmt->execute([$bio, $profilePath, $userId]);
|
$stmt->execute([$bio, $profilePictureUrl, $userId]);
|
||||||
|
|
||||||
// sync session
|
|
||||||
$_SESSION['user']['bio'] = $bio;
|
$_SESSION['user']['bio'] = $bio;
|
||||||
$_SESSION['user']['profile_picture'] = $profilePath;
|
$_SESSION['user']['profile_picture'] = $profilePictureUrl;
|
||||||
header('Location: profile.php'); exit;
|
|
||||||
|
$message = "✅ Profil mis à jour avec succès !";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$message = "❌ Erreur base de données : " . $e->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch fresh data
|
// ✅ Récupération des infos utilisateur
|
||||||
$stmt = $pdo->prepare("SELECT username, bio, profile_picture FROM utilisateurs WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT username, role, bio, profile_picture FROM utilisateurs WHERE id = ?");
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
$user = $stmt->fetch();
|
||||||
?>
|
|
||||||
<!-- include the HTML/CSS you prefer (same as earlier). -->
|
|
||||||
<?php include 'footer.php'; ?>
|
|
||||||
|
|
||||||
|
// ✅ Détermine la redirection correcte
|
||||||
|
if ($user['role'] === 'admin') {
|
||||||
|
$redirectPage = "dashboard.php"; // Admin → tableau de bord admin
|
||||||
|
} else {
|
||||||
|
$redirectPage = "index.php"; // Utilisateur → accueil public
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Profil de <?= htmlspecialchars($user['username']) ?></title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background: linear-gradient(120deg, #fff4f7, #ffe6ea);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: white;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0 6px 18px rgba(255, 100, 150, 0.2);
|
||||||
|
text-align: center;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: #ff69b4;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-picture {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 3px solid #ff69b4;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: linear-gradient(45deg, #ff69b4, #ffa07a);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin: 10px 0;
|
||||||
|
color: #ff69b4;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #ff69b4;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h2>Profil de <?= htmlspecialchars($user['username']) ?></h2>
|
||||||
|
|
||||||
|
<?php if (!empty($user['profile_picture'])): ?>
|
||||||
|
<img src="<?= htmlspecialchars($user['profile_picture']) ?>" class="profile-picture" alt="Photo de profil">
|
||||||
|
<?php else: ?>
|
||||||
|
<div style="font-size:60px;">👤</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p><strong>Rôle :</strong> <?= htmlspecialchars($user['role']) ?></p>
|
||||||
|
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
<label for="bio">Votre bio :</label>
|
||||||
|
<textarea name="bio" id="bio" rows="4"><?= htmlspecialchars($user['bio'] ?? '') ?></textarea>
|
||||||
|
|
||||||
|
<label for="profile_picture">Changer la photo :</label>
|
||||||
|
<input type="file" name="profile_picture" id="profile_picture" accept="image/png, image/jpeg">
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit" name="update_profile">Mettre à jour</button>
|
||||||
|
<button type="submit" name="delete_picture" style="background:#ddd;color:#333;">Supprimer la photo</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="message"><?= htmlspecialchars($message) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p style="margin-top:20px;">
|
||||||
|
<a href="<?= $redirectPage ?>">⬅ Retour au tableau de bord</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -10,13 +10,11 @@ if ($id <= 0) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch user
|
|
||||||
$stmt = $pdo->prepare("SELECT id, username, bio, profile_picture FROM utilisateurs WHERE id = ?");
|
$stmt = $pdo->prepare("SELECT id, username, bio, profile_picture FROM utilisateurs WHERE id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$u = $stmt->fetch(PDO::FETCH_ASSOC);
|
$u = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
if (!$u) { echo "Utilisateur introuvable."; exit; }
|
if (!$u) { echo "Utilisateur introuvable."; exit; }
|
||||||
|
|
||||||
// fetch posts by user
|
|
||||||
$stmt2 = $pdo->prepare("SELECT id, title, content, image_url, date_creation FROM posts WHERE user_id = ? ORDER BY date_creation DESC");
|
$stmt2 = $pdo->prepare("SELECT id, title, content, image_url, date_creation FROM posts WHERE user_id = ? ORDER BY date_creation DESC");
|
||||||
$stmt2->execute([$id]);
|
$stmt2->execute([$id]);
|
||||||
$posts = $stmt2->fetchAll(PDO::FETCH_ASSOC);
|
$posts = $stmt2->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
require_once "config.php";
|
require_once 'config.php';
|
||||||
include "header.php";
|
|
||||||
|
|
||||||
// Vérifier si le formulaire a été soumis
|
$message = '';
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|
||||||
$username = trim($_POST["username"]);
|
|
||||||
$password = password_hash($_POST["password"], PASSWORD_BCRYPT);
|
|
||||||
$role = "auteur"; // Par défaut, tout nouvel utilisateur est auteur
|
|
||||||
|
|
||||||
// Vérifier si le nom d'utilisateur existe déjà
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$stmt = $pdo->prepare("SELECT id FROM utilisateurs WHERE username = ?");
|
$username = trim($_POST['username'] ?? '');
|
||||||
$stmt->execute([$username]);
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
if ($stmt->rowCount() > 0) {
|
if ($username && $email && $password) {
|
||||||
echo "<p style='color:red;text-align:center;'>⚠️ Ce nom d'utilisateur existe déjà.</p>";
|
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
||||||
|
$role = 'auteur'; // ✅ tout nouvel utilisateur est un auteur
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO utilisateurs (username, email, password, role) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$username, $email, $hashedPassword, $role]);
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$message = "❌ Erreur lors de l'inscription : " . $e->getMessage();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Insérer le nouvel utilisateur
|
$message = "⚠️ Tous les champs sont obligatoires.";
|
||||||
$stmt = $pdo->prepare("INSERT INTO utilisateurs (username, password, role) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$username, $password, $role]);
|
|
||||||
|
|
||||||
echo "<p style='color:green;text-align:center;'>✅ Inscription réussie ! Vous pouvez maintenant vous connecter.</p>";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -29,70 +31,58 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Inscription - Mini CMS</title>
|
<title>Créer un compte ✨</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: linear-gradient(135deg, #ffb6c1, #ffa07a);
|
|
||||||
font-family: 'Poppins', sans-serif;
|
font-family: 'Poppins', sans-serif;
|
||||||
margin: 0;
|
background: linear-gradient(120deg, #ffe0ec, #fff6f8);
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||||
width: 400px;
|
width: 380px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
h2 {
|
input {
|
||||||
color: #ff69b4;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
input[type=text], input[type=password] {
|
|
||||||
width: 90%;
|
width: 90%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
|
border: 1px solid #ddd;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
background: linear-gradient(45deg, #ff69b4, #ffa07a);
|
background: linear-gradient(45deg, #ff69b4, #ffa07a);
|
||||||
color: white;
|
|
||||||
border: none;
|
border: none;
|
||||||
padding: 12px 25px;
|
color: white;
|
||||||
border-radius: 25px;
|
padding: 10px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #ff69b4;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
width: 95%;
|
||||||
}
|
}
|
||||||
a:hover {
|
button:hover { opacity: 0.9; }
|
||||||
text-decoration: underline;
|
.message { color: red; margin-bottom: 10px; }
|
||||||
}
|
a { color: #ff69b4; text-decoration: none; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>Créer un compte</h2>
|
<h2>Créer un compte ✨</h2>
|
||||||
|
<?php if ($message): ?><div class="message"><?= $message ?></div><?php endif; ?>
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<input type="text" name="username" placeholder="Nom d'utilisateur" required><br>
|
<input type="text" name="username" placeholder="Nom d'utilisateur" required><br>
|
||||||
|
<input type="email" name="email" placeholder="Email" required><br>
|
||||||
<input type="password" name="password" placeholder="Mot de passe" required><br>
|
<input type="password" name="password" placeholder="Mot de passe" required><br>
|
||||||
<button type="submit">S'inscrire</button>
|
<button type="submit">S'inscrire</button>
|
||||||
</form>
|
</form>
|
||||||
<p>Déjà un compte ? <a href="login.php">Se connecter</a></p>
|
<p>Déjà inscrit ? <a href="login.php">Connectez-vous</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
<?php include "footer.php"; ?>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* 🎨 — Thème global rose/orangé élégant */
|
|
||||||
body {
|
body {
|
||||||
font-family: "Poppins", sans-serif;
|
font-family: "Poppins", sans-serif;
|
||||||
background: #fffafc;
|
background: #fffafc;
|
||||||
@@ -7,7 +7,7 @@ body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 🌟 — En-tête du site */
|
|
||||||
header {
|
header {
|
||||||
background: linear-gradient(90deg, #ffa45b, #ff6fa7);
|
background: linear-gradient(90deg, #ffa45b, #ff6fa7);
|
||||||
color: white;
|
color: white;
|
||||||
@@ -26,7 +26,7 @@ header a:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 👤 — Auteur dans la liste d’articles */
|
|
||||||
.article-meta {
|
.article-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -51,7 +51,7 @@ header a:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 🧍♀️ — Page de profil publique (profile_view.php) */
|
|
||||||
.profile-view {
|
.profile-view {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 60px auto;
|
margin: 60px auto;
|
||||||
@@ -91,7 +91,7 @@ header a:hover {
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 📰 — Articles de l’auteur */
|
|
||||||
.user-articles h3 {
|
.user-articles h3 {
|
||||||
color: #ff6fa7;
|
color: #ff6fa7;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
@@ -140,7 +140,7 @@ header a:hover {
|
|||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 📱 — Responsive */
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
header {
|
header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/config.php'; // doit définir $s3Client et $bucketName
|
require_once __DIR__ . '/config.php'; // doit définir $s3Client et $bucketName
|
||||||
|
|
||||||
// crée un petit fichier temporaire
|
|
||||||
$tmp = '/tmp/test_minio_upload_'.time().'.txt';
|
$tmp = '/tmp/test_minio_upload_'.time().'.txt';
|
||||||
file_put_contents($tmp, "test upload ".time());
|
file_put_contents($tmp, "test upload ".time());
|
||||||
|
|
||||||
|
|||||||
0
infrastructure/README.md
Normal file
0
infrastructure/README.md
Normal file
@@ -12,10 +12,9 @@ services:
|
|||||||
- ../forum-project:/var/www/html
|
- ../forum-project:/var/www/html
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
|
||||||
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
|
- minio
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:8.1
|
image: mysql:8.1
|
||||||
@@ -33,6 +32,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
- ./setup-mysql:/docker-entrypoint-initdb.d
|
- ./setup-mysql:/docker-entrypoint-initdb.d
|
||||||
|
- ./setup-mysql/my.cnf:/etc/mysql/conf.d/my.cnf
|
||||||
|
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
@@ -64,5 +64,3 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
mysql_data:
|
mysql_data:
|
||||||
minio_data:
|
minio_data:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
29
infrastructure/setup-minio/init-minio.sh
Normal file → Executable file
29
infrastructure/setup-minio/init-minio.sh
Normal file → Executable file
@@ -1,13 +1,26 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
# Attendre que MinIO démarre
|
# Attendre que MinIO démarre
|
||||||
echo "Waiting for MinIO to start..."
|
echo "⏳ Attente du démarrage de MinIO..."
|
||||||
sleep 10
|
sleep 5
|
||||||
|
|
||||||
# Créer un alias pour MinIO
|
# Configuration du client MinIO (mc)
|
||||||
mc alias set localminio http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
|
echo "🔧 Configuration du client MinIO..."
|
||||||
|
mc alias set myminio http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD"
|
||||||
|
|
||||||
# Créer le bucket (ignore erreur si existe déjà)
|
# Création du bucket s’il n’existe pas déjà
|
||||||
mc mb localminio/bucketforum || true
|
echo "🪣 Vérification du bucket 'bucketforum'..."
|
||||||
|
if ! mc ls myminio | grep -q "bucketforum"; then
|
||||||
|
mc mb myminio/bucketforum
|
||||||
|
echo "✅ Bucket 'bucketforum' créé."
|
||||||
|
else
|
||||||
|
echo "ℹ️ Bucket 'bucketforum' déjà existant."
|
||||||
|
fi
|
||||||
|
|
||||||
# Définir une politique publique sur le bucket (optionnel)
|
# Rendre le bucket public
|
||||||
mc policy set public localminio/bucketforum
|
echo "🌍 Configuration de la politique publique..."
|
||||||
|
mc anonymous set public myminio/bucketforum
|
||||||
|
|
||||||
|
# Vérification du statut
|
||||||
|
echo "✅ Bucket 'bucketforum' est public et prêt à être utilisé."
|
||||||
|
|||||||
@@ -1,33 +1,41 @@
|
|||||||
|
-- encodage base
|
||||||
|
CREATE DATABASE IF NOT EXISTS forum_database
|
||||||
|
CHARACTER SET utf8mb4
|
||||||
|
COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
USE forum_database;
|
||||||
|
|
||||||
|
-- table utilisateurs
|
||||||
CREATE TABLE IF NOT EXISTS utilisateurs (
|
CREATE TABLE IF NOT EXISTS utilisateurs (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
username VARCHAR(50) NOT NULL UNIQUE,
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
password VARCHAR(255) NOT NULL,
|
password VARCHAR(255) NOT NULL,
|
||||||
email VARCHAR(100) UNIQUE,
|
email VARCHAR(100) UNIQUE,
|
||||||
|
role VARCHAR(50) DEFAULT 'user',
|
||||||
|
bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
|
||||||
|
profile_picture VARCHAR(255),
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- table posts
|
||||||
CREATE TABLE IF NOT EXISTS posts (
|
CREATE TABLE IF NOT EXISTS posts (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
title VARCHAR(100) NOT NULL,
|
title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||||
|
image_url VARCHAR(255),
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (user_id) REFERENCES utilisateurs(id)
|
date_creation DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
);
|
FOREIGN KEY (user_id) REFERENCES utilisateurs(id) ON DELETE CASCADE
|
||||||
|
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
INSERT INTO utilisateurs (username, password, email)
|
-- données initiales
|
||||||
|
INSERT INTO utilisateurs (username, password, email, role)
|
||||||
VALUES
|
VALUES
|
||||||
('aya', 'password123', 'ayae@example.com'),
|
('admin', '$2y$10$4U8a64xTrD/GQDnj8tCNsemA47765p.5gtflWWBKl6UGl2aj/uOlC', 'admin@example.com', 'admin'),
|
||||||
('tess', 'password456', 'tess@example.com');
|
('aya', '$2y$10$vhtGydzlL8AFMbRTeRVC2OtDkud47TZCHt98HcAjjGGVlLlm2Bn66', 'aya@example.com', 'user');
|
||||||
|
|
||||||
INSERT INTO posts (user_id, title, content)
|
INSERT INTO posts (user_id, title, content)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Bienvenue sur le forum', 'Ceci est le premier post !'),
|
(1, 'Bienvenue sur le Mini CMS', 'Ceci est un article de démonstration publié par l’administrateur !'),
|
||||||
(2, 'Deuxième post', 'Un autre test pour vérifier la base.');
|
(2, 'Un premier article test', 'Bienvenue dans notre CMS simplifié. Partagez vos idées ici !');
|
||||||
ALTER TABLE posts
|
|
||||||
DROP FOREIGN KEY posts_ibfk_1;
|
|
||||||
|
|
||||||
ALTER TABLE posts
|
|
||||||
ADD CONSTRAINT posts_ibfk_1
|
|
||||||
FOREIGN KEY (user_id) REFERENCES utilisateurs(id)
|
|
||||||
ON DELETE CASCADE;
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[mysqld]
|
||||||
|
character-set-server = utf8mb4
|
||||||
|
collation-server = utf8mb4_unicode_ci
|
||||||
|
skip-character-set-client-handshake
|
||||||
|
|
||||||
|
[client]
|
||||||
|
default-character-set = utf8mb4
|
||||||
|
|
||||||
|
[mysql]
|
||||||
|
default-character-set = utf8mb4
|
||||||
|
|||||||
Reference in New Issue
Block a user