first commit

This commit is contained in:
julien
2026-03-16 01:47:07 +01:00
commit 8f7e61bda0
185 changed files with 27731 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
{% extends "layout.twig" %}
{% block title %}Mon compte Changer le mot de passe{% endblock %}
{% block content %}
<div class="form-container form-container--narrow">
<div class="form-container__panel">
<div class="form-container__header">
<h2 class="form-container__title">Changer le mot de passe</h2>
</div>
{% if error %}
<div class="alert alert--danger">{{ error }}</div>
{% endif %}
{% if success %}
<div class="alert alert--success">{{ success }}</div>
{% endif %}
<form method="post" action="/account/password" class="form-container__form">
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<p class="form-container__field">
<label for="current_password" class="form-container__label">
<span>Mot de passe actuel</span>
<input type="password" id="current_password" name="current_password" required autofocus
class="form-container__input">
</label>
</p>
<p class="form-container__field">
<label for="new_password" class="form-container__label">
<span>Nouveau mot de passe</span>
<input type="password" id="new_password" name="new_password" required minlength="8"
class="form-container__input">
</label>
<small class="form-container__hint">Minimum 8 caractères</small>
</p>
<p class="form-container__field">
<label for="new_password_confirm" class="form-container__label">
<span>Confirmer le nouveau mot de passe</span>
<input type="password" id="new_password_confirm" name="new_password_confirm" required minlength="8"
class="form-container__input">
</label>
</p>
<div class="form-container__actions">
<div class="form-container__action">
<button type="submit" class="btn btn--primary btn--lg btn--full">Mettre à jour</button>
</div>
<div class="form-container__action">
<a href="{{ back_url }}" class="btn btn--secondary btn--lg btn--full">Annuler</a>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,50 @@
{% extends "layout.twig" %}
{% block title %}Connexion Slim Blog{% endblock %}
{% block content %}
<div class="form-container form-container--narrow">
<div class="form-container__panel">
<div class="form-container__header">
<h2 class="form-container__title">Connexion</h2>
</div>
{% if error %}
<div class="alert alert--danger">{{ error }}</div>
{% endif %}
{% if success %}
<div class="alert alert--success">{{ success }}</div>
{% endif %}
<form method="post" action="/auth/login" class="form-container__form">
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<p class="form-container__field">
<label for="username" class="form-container__label">
<span>Nom d'utilisateur</span>
<input type="text" id="username" name="username" required autofocus class="form-container__input">
</label>
</p>
<p class="form-container__field">
<label for="password" class="form-container__label">
<span>Mot de passe</span>
<input type="password" id="password" name="password" required class="form-container__input">
</label>
</p>
<div class="form-container__actions">
<div class="form-container__action">
<button type="submit" class="btn btn--primary btn--lg btn--full">Se connecter</button>
</div>
</div>
</form>
<div class="form-container__footer">
<a href="/password/forgot">Mot de passe oublié ?</a>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,44 @@
{% extends "layout.twig" %}
{% block title %}Mot de passe oublié Slim Blog{% endblock %}
{% block content %}
<div class="form-container form-container--narrow">
<div class="form-container__panel">
<div class="form-container__header">
<h2 class="form-container__title">Mot de passe oublié</h2>
<p class="form-container__intro">Saisissez votre adresse email. Si elle est associée à un compte, vous recevrez un lien de réinitialisation.</p>
</div>
{% if error %}
<div class="alert alert--danger">{{ error }}</div>
{% endif %}
{% if success %}
<div class="alert alert--success">{{ success }}</div>
{% endif %}
<form method="post" action="/password/forgot" class="form-container__form">
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<p class="form-container__field">
<label for="email" class="form-container__label">
<span>Adresse email</span>
<input type="email" id="email" name="email" required autofocus class="form-container__input">
</label>
</p>
<div class="form-container__actions">
<div class="form-container__action">
<button type="submit" class="btn btn--primary btn--lg btn--full">Envoyer le lien</button>
</div>
</div>
</form>
<div class="form-container__footer">
<a href="/auth/login">← Retour à la connexion</a>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,46 @@
{% extends "layout.twig" %}
{% block title %}Réinitialisation du mot de passe Slim Blog{% endblock %}
{% block content %}
<div class="form-container form-container--narrow">
<div class="form-container__panel">
<div class="form-container__header">
<h2 class="form-container__title">Nouveau mot de passe</h2>
</div>
{% if error %}
<div class="alert alert--danger">{{ error }}</div>
{% endif %}
<form method="post" action="/password/reset" class="form-container__form">
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<input type="hidden" name="token" value="{{ token }}">
<p class="form-container__field">
<label for="new_password" class="form-container__label">
<span>Nouveau mot de passe</span>
<input type="password" id="new_password" name="new_password"
required minlength="8" autofocus class="form-container__input">
</label>
<small class="form-container__hint">Minimum 8 caractères</small>
</p>
<p class="form-container__field">
<label for="new_password_confirm" class="form-container__label">
<span>Confirmer le mot de passe</span>
<input type="password" id="new_password_confirm" name="new_password_confirm"
required minlength="8" class="form-container__input">
</label>
</p>
<div class="form-container__actions">
<div class="form-container__action">
<button type="submit" class="btn btn--primary btn--lg btn--full">Réinitialiser</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

11
views/pages/error.twig Normal file
View File

@@ -0,0 +1,11 @@
{% extends "layout.twig" %}
{% block title %}{{ status }} Slim Blog{% endblock %}
{% block content %}
<div class="error-page">
<h2 class="error-page__code">{{ status }}</h2>
<p class="error-page__message">{{ message }}</p>
<p><a href="/">← Retour à l'accueil</a></p>
</div>
{% endblock %}

94
views/pages/home.twig Normal file
View File

@@ -0,0 +1,94 @@
{% extends "layout.twig" %}
{% block title %}Slim Blog{% endblock %}
{% block meta %}
<meta name="description" content="{% if activeCategory %}Articles de la catégorie {{ activeCategory.name }}{% endif %}Slim Blog, un blog propulsé par Slim 4.">
<meta property="og:type" content="website">
<meta property="og:title" content="Slim Blog">
<meta property="og:description" content="Un blog propulsé par Slim 4.">
<meta property="og:url" content="{{ app_url }}/">
{% endblock %}
{% block content %}
<form method="get" action="/" class="search-bar">
{% if activeCategory %}
<input type="hidden" name="categorie" value="{{ activeCategory.slug }}">
{% endif %}
<input type="search" name="q" value="{{ searchQuery }}"
placeholder="Rechercher un article…" class="search-bar__input" aria-label="Recherche">
<button type="submit" class="search-bar__btn">Rechercher</button>
{% if searchQuery %}
<a href="/{% if activeCategory %}?categorie={{ activeCategory.slug }}{% endif %}" class="search-bar__reset">✕</a>
{% endif %}
</form>
{% if searchQuery %}
<p class="search-bar__info">
{% if posts is not empty %}
{{ posts|length }} résultat{{ posts|length > 1 ? 's' : '' }} pour « {{ searchQuery }} »
{% else %}
Aucun résultat pour « {{ searchQuery }} »
{% endif %}
</p>
{% endif %}
{% if categories is not empty %}
<nav class="category-filter">
<a href="/"
class="category-filter__item{% if activeCategory is null %} category-filter__item--active{% endif %}">
Tous
</a>
{% for category in categories %}
<a href="/?categorie={{ category.slug }}"
class="category-filter__item{% if activeCategory and activeCategory.id == category.id %} category-filter__item--active{% endif %}">
{{ category.name }}
</a>
{% endfor %}
</nav>
{% endif %}
<div class="card-list card-list--contained">
{% for post in posts %}
{% set thumb = post_thumbnail(post) %}
<article class="card">
<a href="{{ post_url(post) }}" class="card__thumb-link" tabindex="-1" aria-hidden="true">
{% if thumb %}
<img class="card__thumb" src="{{ thumb }}" alt="">
{% else %}
<span class="card__initials" aria-hidden="true">{{ post_initials(post) }}</span>
{% endif %}
</a>
<div class="card__content">
<div class="card__body">
<h2 class="card__title">
<a href="{{ post_url(post) }}" class="card__title-link">{{ post.title }}</a>
</h2>
<div class="card__meta">
<small>
Publié le {{ post.createdAt|date("d/m/Y à H:i") }}
par <strong>{{ post.authorUsername ?? 'inconnu' }}</strong>
</small>
{% if post.categoryName %}
<a href="/?categorie={{ post.categorySlug }}" class="badge badge--category">{{ post.categoryName }}</a>
{% endif %}
</div>
<p class="card__excerpt">{{ post_excerpt(post) }}</p>
</div>
<div class="card__actions">
<a href="{{ post_url(post) }}" class="card__actions-link">Lire la suite →</a>
</div>
</div>
</article>
{% else %}
<p>Aucun article publié{% if searchQuery %} pour « {{ searchQuery }} »{% elseif activeCategory %} dans cette catégorie{% endif %}.</p>
{% endfor %}
</div>
{% endblock %}

View File

@@ -0,0 +1,48 @@
{% extends "layout.twig" %}
{% block title %}{{ post.title }} Slim Blog{% endblock %}
{% block meta %}
{% set excerpt = post_excerpt(post, 160) %}
{% set thumb = post_thumbnail(post) %}
<meta name="description" content="{{ excerpt }}">
<meta property="og:type" content="article">
<meta property="og:title" content="{{ post.title }}">
<meta property="og:description" content="{{ excerpt }}">
<meta property="og:url" content="{{ app_url }}{{ post_url(post) }}">
{% if thumb %}
<meta property="og:image" content="{{ app_url }}{{ thumb }}">
{% endif %}
{% endblock %}
{% block content %}
<article class="post">
<h1>{{ post.title }}</h1>
<div class="post__meta">
<small>
Publié le {{ post.createdAt|date("d/m/Y à H:i") }}
par <strong>{{ post.authorUsername ?? 'inconnu' }}</strong>
</small>
{% if post.categoryName %}
<a href="/?categorie={{ post.categorySlug }}" class="badge badge--category">{{ post.categoryName }}</a>
{% endif %}
</div>
{% if post.updatedAt != post.createdAt %}
<div class="post__updated">
<small><em>Mis à jour le {{ post.updatedAt|date("d/m/Y à H:i") }}</em></small>
</div>
{% endif %}
<div class="post__content">
{# Le contenu est déjà sanitisé par HtmlSanitizer via PostService #}
{{ post.content|raw }}
</div>
<hr>
<p>
<a href="/">← Retour aux articles</a>
</p>
</article>
{% endblock %}