Ajout du mode maintenance avec modèle, vues, URL, contexte, et intégration des templates. Ajout de nouvelles fonctionnalités côté client, comme le basculement de thème et les interactions de navigation mobile.
This commit is contained in:
parent
acd9f42cea
commit
536f4e303f
12 changed files with 227 additions and 27 deletions
|
|
@ -1,5 +1,5 @@
|
|||
from django.contrib import admin
|
||||
from .models import SiteSettings, Visit
|
||||
from .models import SiteSettings, Visit, Maintenance
|
||||
|
||||
@admin.register(SiteSettings)
|
||||
class SiteSettingsAdmin(admin.ModelAdmin):
|
||||
|
|
@ -28,6 +28,10 @@ class SiteSettingsAdmin(admin.ModelAdmin):
|
|||
}),
|
||||
)
|
||||
|
||||
@admin.register(Maintenance)
|
||||
class MaintenanceAdmin(admin.ModelAdmin):
|
||||
list_display = ("name","start_date", "end_date")
|
||||
|
||||
|
||||
@admin.register(Visit)
|
||||
class VisitAdmin(admin.ModelAdmin):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
from .models import SiteSettings
|
||||
from django.utils.timesince import timesince
|
||||
|
||||
from .models import SiteSettings, Maintenance
|
||||
|
||||
def site_settings(request):
|
||||
# On récupère le premier objet, ou None s'il n'existe pas encore
|
||||
return {'settings': SiteSettings.objects.first()}
|
||||
|
||||
def site_maintenance(request):
|
||||
last = Maintenance.objects.last()
|
||||
start = last.start_date if last else None
|
||||
end = last.end_date if last else None
|
||||
delay = timesince(start, end) if start and end else None
|
||||
return {'maintenance': Maintenance.objects.last(), 'delay': delay}
|
||||
23
core/migrations/0006_maintenance.py
Normal file
23
core/migrations/0006_maintenance.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 6.0 on 2025-12-17 10:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0005_sitesettings_receive_emails_active'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Maintenance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_active', models.BooleanField(default=False)),
|
||||
('message', models.TextField(blank=True)),
|
||||
('start_date', models.DateTimeField(blank=True, null=True)),
|
||||
('end_date', models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
]
|
||||
18
core/migrations/0007_maintenance_name.py
Normal file
18
core/migrations/0007_maintenance_name.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 6.0 on 2025-12-17 10:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0006_maintenance'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='maintenance',
|
||||
name='name',
|
||||
field=models.CharField(blank=True, max_length=200),
|
||||
),
|
||||
]
|
||||
|
|
@ -35,6 +35,12 @@ class SiteSettings(models.Model):
|
|||
verbose_name = "Réglages du site"
|
||||
verbose_name_plural = "Réglages du site"
|
||||
|
||||
class Maintenance(models.Model):
|
||||
is_active = models.BooleanField(default=False)
|
||||
name = models.CharField(max_length=200, blank=True)
|
||||
message = models.TextField(blank=True)
|
||||
start_date = models.DateTimeField(blank=True, null=True)
|
||||
end_date = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
class Visit(models.Model):
|
||||
"""Enregistrement simplifié des visites (agrégées par jour et visiteur).
|
||||
|
|
|
|||
8
core/urls.py
Normal file
8
core/urls.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('update_database', views.update_database, name='update_database'),
|
||||
path('clear_cache', views.clear_cache, name='clear_cache'),
|
||||
path('regen_static_files', views.regen_static_files, name='regen_static_files')
|
||||
]
|
||||
|
|
@ -1,3 +1,20 @@
|
|||
from django.shortcuts import render
|
||||
from django.core.management import call_command
|
||||
from django.core.cache import cache
|
||||
import subprocess
|
||||
|
||||
# Create your views here.
|
||||
def update_database(request):
|
||||
call_command('makemigrations')
|
||||
call_command('migrate')
|
||||
message = "La base de données à bien été mise à jour !"
|
||||
return render(request, 'home.html', {'message': message})
|
||||
|
||||
def clear_cache(request):
|
||||
cache.clear()
|
||||
message = "Le cache à bien été effacé !"
|
||||
return render(request, 'home.html', {'message': message})
|
||||
|
||||
def regen_static_files(request):
|
||||
call_command('collectstatic', '--noinput')
|
||||
message = "Les fichiers statics ont bien été générés !"
|
||||
return render(request, 'home.html', {'message': message})
|
||||
|
|
@ -84,6 +84,7 @@ TEMPLATES = [
|
|||
|
||||
'devart.context_processor.app_version',
|
||||
'core.context_processor.site_settings',
|
||||
'core.context_processor.site_maintenance',
|
||||
'courses.context_processors.course_list',
|
||||
'blog.context_processor.posts_list',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -22,12 +22,13 @@ from django.http import HttpResponse
|
|||
from devart.sitemap import CourseSitemap, StaticViewSitemap
|
||||
from django.contrib.sitemaps.views import sitemap
|
||||
|
||||
# La vue pour le robots.txt
|
||||
def robots_txt(request):
|
||||
lines = [
|
||||
"User-agent: *",
|
||||
"Disallow: /admin/",
|
||||
"Disallow: /users/",
|
||||
"Disallow: /maintenance/",
|
||||
"Disallow: /core/",
|
||||
"Allow: /",
|
||||
"Sitemap: https://partirdezero.com/sitemap.xml", # On indique déjà où sera le plan
|
||||
]
|
||||
|
|
@ -39,6 +40,7 @@ sitemaps_dict = {
|
|||
}
|
||||
|
||||
urlpatterns = [
|
||||
path('core/', include('core.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('home.urls')),
|
||||
path('courses/', include('courses.urls')),
|
||||
|
|
|
|||
|
|
@ -13,3 +13,85 @@ function show(id) {
|
|||
let buttonChange = document.getElementById(id);
|
||||
buttonChange.onclick = function() { hide(id); };
|
||||
}
|
||||
|
||||
// Fonction pour supprimer le message d'alerte après 5 secondes
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let messages = document.querySelector('.flash_messages')
|
||||
if (messages) {
|
||||
setTimeout(function() {
|
||||
messages.style.opacity = 0;
|
||||
setTimeout(function() {
|
||||
messages.style.display = 'none';
|
||||
}, 1000);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Theme toggle setup
|
||||
var toggle = document.getElementById('themeToggle');
|
||||
var themeLink = document.getElementById('theme-css');
|
||||
function applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
if (theme === 'light') {
|
||||
if (themeLink) themeLink.href = themeLink.href.replace('colors_dark.css', 'colors_light.css');
|
||||
} else {
|
||||
if (themeLink) themeLink.href = themeLink.href.replace('colors_light.css', 'colors_dark.css');
|
||||
}
|
||||
var icon = toggle && toggle.querySelector('i');
|
||||
if (icon) {
|
||||
icon.classList.remove('fa-sun','fa-moon');
|
||||
icon.classList.add(theme === 'light' ? 'fa-moon' : 'fa-sun');
|
||||
}
|
||||
if (toggle) {
|
||||
toggle.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false');
|
||||
toggle.setAttribute('aria-label', theme === 'light' ? 'Activer le thème sombre' : 'Activer le thème clair');
|
||||
toggle.title = toggle.getAttribute('aria-label');
|
||||
}
|
||||
}
|
||||
try {
|
||||
var current = document.documentElement.getAttribute('data-theme') || 'dark';
|
||||
applyTheme(current);
|
||||
if (toggle) {
|
||||
toggle.addEventListener('click', function() {
|
||||
var next = (document.documentElement.getAttribute('data-theme') === 'light') ? 'dark' : 'light';
|
||||
localStorage.setItem('pdz-theme', next);
|
||||
applyTheme(next);
|
||||
});
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// Mobile nav toggle
|
||||
try {
|
||||
var navToggle = document.getElementById('navToggle');
|
||||
var primaryNav = document.getElementById('primaryNav');
|
||||
if (navToggle && primaryNav) {
|
||||
function setExpanded(expanded) {
|
||||
navToggle.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
||||
navToggle.setAttribute('aria-label', expanded ? 'Fermer le menu' : 'Ouvrir le menu');
|
||||
var icon = navToggle.querySelector('i');
|
||||
if (icon) {
|
||||
icon.classList.remove('fa-bars','fa-xmark');
|
||||
icon.classList.add(expanded ? 'fa-xmark' : 'fa-bars');
|
||||
}
|
||||
primaryNav.classList.toggle('is-open', expanded);
|
||||
}
|
||||
|
||||
navToggle.addEventListener('click', function() {
|
||||
var expanded = navToggle.getAttribute('aria-expanded') === 'true';
|
||||
setExpanded(!expanded);
|
||||
});
|
||||
|
||||
// Close menu when a link is clicked (on small screens)
|
||||
primaryNav.addEventListener('click', function(e) {
|
||||
var target = e.target;
|
||||
if (target.tagName === 'A' || target.closest('a')) {
|
||||
setExpanded(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Close on Escape
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') setExpanded(false);
|
||||
});
|
||||
}
|
||||
} catch(e) {}
|
||||
});
|
||||
|
|
@ -84,29 +84,33 @@
|
|||
<script defer>hljs.highlightAll();</script>
|
||||
</head>
|
||||
<body>
|
||||
{% now "n" as month %}
|
||||
{% if month == '12' %}
|
||||
<!-- Overlay neige discret, non interactif -->
|
||||
<div class="pdz-snow" aria-hidden="true"></div>
|
||||
{% endif %}
|
||||
{% block header %}
|
||||
{% include "partials/_header.html" %}
|
||||
{% endblock %}
|
||||
|
||||
<main>
|
||||
{% if messages %}
|
||||
<ul class="flash_messages">
|
||||
{% for message in messages %}
|
||||
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if maintenance.is_active == True %}
|
||||
{% include "maintenance.html" %}
|
||||
{% else %}
|
||||
{% now "n" as month %}
|
||||
{% if month == '12' %}
|
||||
<!-- Overlay neige discret, non interactif -->
|
||||
<div class="pdz-snow" aria-hidden="true"></div>
|
||||
{% endif %}
|
||||
{% block header %}
|
||||
{% include "partials/_header.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<main>
|
||||
{% if messages %}
|
||||
<ul class="flash_messages">
|
||||
{% for message in messages %}
|
||||
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% block footer %}
|
||||
{% include "partials/_footer.html" %}
|
||||
{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
{% block footer %}
|
||||
{% include "partials/_footer.html" %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
26
templates/maintenance.html
Normal file
26
templates/maintenance.html
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{% load comment_format %}
|
||||
<section>
|
||||
<h1>Maintenance : {{ maintenance.name }}</h1>
|
||||
{{ maintenance.message|comment_markdown }}
|
||||
<div class="text-right">Durée estimée : {{ delay }}</div>
|
||||
<div class="text-right">Début de la maintenance : {{ maintenance.start_date }}</div>
|
||||
<div class="text-right">Fin de la maintenance estimé : {{ maintenance.end_date }}</div>
|
||||
|
||||
{% if message %}
|
||||
<h2>{{ message }}</h2>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_superuser %}
|
||||
<div style="border-bottom: 2px solid white"></div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="{% url 'update_database' %}">Mettre à jour la base de données</a></li>
|
||||
<li><a href="">Nettoyer la base de données</a></li>
|
||||
<li><a href="{% url 'clear_cache' %}">Effacer le cache</a></li>
|
||||
<li><a href="{% url 'regen_static_files' %}">Régénérer les fichiers static</a></li>
|
||||
<div style="border-bottom: 2px solid white; margin: 5px;"></div>
|
||||
<li><a href="{% url 'admin:index' %}" class="btn btn-warning" target="_blank">Panel Admin</a> <a href="" class="btn btn-danger" target="_blank">Redemarrer le serveur Django</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
Loading…
Add table
Add a link
Reference in a new issue