1 Commits

Author SHA1 Message Date
a97c233ddb feature: ajout photo de profil 2026-04-30 13:21:16 +02:00
296 changed files with 568 additions and 717 deletions

8
.gitignore vendored
View File

@@ -1,7 +1,3 @@
db.sqlite3 db.sqlite3
venv/ venv/*
media/ media/*
staticfiles/
.env
migrations/
*.pyc

52
Jenkinsfile vendored
View File

@@ -1,52 +0,0 @@
pipeline
{
agent any
options {
// This is required if you want to clean before build
skipDefaultCheckout(true)
}
environment
{
SUDO_PASSWORD = credentials('sudo-password')
}
stages
{
stage ( 'checkout' )
{
steps
{
sh 'echo "Debut du pipeline"'
cleanWs()
checkout scm
}
}
stage ( 'Deploiement' )
{
when { branch 'main' }
steps {
sh '''
cd /var/www/sirh
echo $SUDO_PASSWORD | sudo -S chown -R jenkins:jenkins /var/www/sirh
git fetch origin main
git reset --hard origin/main
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt
python manage.py makemigrations
python manage.py migrate
echo $SUDO_PASSWORD | sudo -S rm -r staticfiles
python manage.py collectstatic --noinput
echo $SUDO_PASSWORD | sudo -S chown -R www-data:www-data /var/www/sirh
echo "Deploiement reussi"
echo $SUDO_PASSWORD | sudo -S supervisorctl restart sirh
'''
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -12,22 +12,21 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
import os import os
from pathlib import Path from pathlib import Path
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config('SECRET_KEY') SECRET_KEY = 'django-insecure--wdb9t(77rvyac$_q!n5gw86&0r(0&&j171v9h!-_$jahsza*5'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', default=True, cast=bool) DEBUG = False
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[]).split(',') ALLOWED_HOSTS = ["https://support.cerfig.org", "support.cerfig.org"]
#CSRF_TRUSTED_ORIGINS = config("CSRF_TRUSTED_ORIGINS", default=[]).split(",")
# Application definition # Application definition
@@ -41,9 +40,12 @@ INSTALLED_APPS = [
'gestion_employe', 'gestion_employe',
'gestion_conge', 'gestion_conge',
'gestion_projet', 'gestion_projet',
'gestion_salle' 'gestion_salle',
'simple_sso.sso_server'
] ]
LOGIN_URL = 'login'
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
@@ -66,7 +68,6 @@ TEMPLATES = [
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'contexte_universel.contexte_universel',
], ],
}, },
}, },
@@ -78,18 +79,25 @@ WSGI_APPLICATION = 'SIRH.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases # https://docs.djangoproject.com/en/5.2/ref/settings/#databases
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'sirh',
# 'USER': 'sirh',
# 'PASSWORD': 'sirh-cerfig',
# 'HOST': 'localhost',
# 'PORT': '3306',
# }
# }
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': config("DB_NAME"), 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'USER': config("DB_USER"),
'PASSWORD': config("DB_PASSWORD"),
'HOST': config("DB_HOST"),
'PORT': config("DB_PORT", cast=int),
} }
} }
# Password validation # Password validation
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
@@ -141,21 +149,11 @@ MEDIA_ROOT = BASE_DIR / "media"
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Connexion sso
# LOGIN_URL = '/client/'
LOGIN_URL = 'simple-sso-login'
SSO_PRIVATE_KEY = config('SSO_PRIVATE_KEY')
SSO_PUBLIC_KEY = config('SSO_PUBLIC_KEY')
SSO_SERVER = config('SSO_SERVER')
#SSO_LOGOUT_URL = 'http://test-authentification.cerfig.org:89/deconnexion'
# Configuration de l'email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'ssl0.ovh.net' EMAIL_HOST = 'ssl0.ovh.net'
EMAIL_PORT = 465 EMAIL_PORT = 465
EMAIL_USE_SSL = True EMAIL_USE_SSL = True
EMAIL_USE_TLS = False EMAIL_USE_TLS = False
EMAIL_HOST_USER = config("EMAIL_HOST_USER") EMAIL_HOST_USER = 'support.it@cerfig.org'
EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD") EMAIL_HOST_PASSWORD = 'Cerfig2025'
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER DEFAULT_FROM_EMAIL = EMAIL_HOST_USER

View File

@@ -1,39 +0,0 @@
# SGL/sso.py
from gestion_employe.models import Employe, Departement
from simple_sso.sso_client.client import Client
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
User = get_user_model()
class SIRHClient(Client):
def build_user(self, user_data):
"""Crée le User et son Profile avec les infos reçues du serveur."""
user_data_copy = user_data.copy()
print("Un petit test", user_data_copy)
user_data.pop("departement", None)
user_data.pop("chef_departement", None)
user_data.pop("groups", None)
user = super().build_user(user_data)
departement = None
if "departement" in user_data_copy:
departement = Departement.objects.get_or_create(
nom=user_data_copy.get("departement", ""))[0]
Employe.objects.update_or_create(
user = user,
defaults = {
"departement": departement,
"chef": user_data_copy.get("chef_departement", None),
}
)
for group_name in user_data_copy.get("groups", []):
group, _ = Group.objects.get_or_create(name=group_name)
user.groups.add(group)
return user

View File

@@ -15,9 +15,8 @@
<body> <body>
<div class="container-fluid"> <div class="container-fluid">
<div class="row flex-nowrap"> <div class="row flex-nowrap">
{% include 'parts/menu_application.html' %}
{% include 'parts/menu_principal.html' %} {% include 'parts/menu_principal.html' %}
<div class="col p-4"> <div class="col-9 p-4">
{% block 'contenu' %} {% endblock %} {% block 'contenu' %} {% endblock %}
</div> </div>
</div> </div>

View File

@@ -24,16 +24,15 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<div class="mb-3"> <div class="mb-3">
<label for="mail">Votre adresse email :</label> <label for="mail">Votre adresse email :</label>
<input type="text" name="mail" class="form-control" placeholder="Entrez votre e-mail" required> <input type="text" name="mail" class="form-control" placeholder="Entrez votre e-mail" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="mot_de_passe">Mot de passe</label> <label for="mot_de_passe">Mot de passe</label>
<input type="password" name="mot_de_passe" class="form-control" placeholder="Entrez votre mot de passe" required> <input type="password" name="mot_de_passe" class="form-control" placeholder="Entrez votre mot de passe" required>
<i class="bi bi-eye toggle-password" onclick="togglePassword()" <i class="bi bi-eye toggle-password" onclick="togglePassword()"
style="position:absolute; right:10px; top:38px; cursor:pointer;"></i> style="position:absolute; right:10px; top:38px; cursor:pointer;"></i>
</div> </div>
<button type="submit" class="btn btn-primary w-100">Se connecter</button> <button type="submit" class="btn btn-primary w-100">Se connecter</button>
</form> </form>

View File

@@ -1,8 +0,0 @@
<div class="col-1 bg-danger border d-flex flex-column vh-100 pt-5 sticky-top">
<a href="{{ SIRH_LIEN }}" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-people-fill fs-1 d-block"></i>SIRH
</a>
<a href="{{ SGL_LIEN }}" class="text-white text-center fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-building-gear fs-1 d-block"></i> SGL
</a>
</div>

View File

@@ -6,43 +6,51 @@
{% if user.employe.photo %} {% if user.employe.photo %}
<img src="{{ user.employe.photo.url }}" <img src="{{ user.employe.photo.url }}"
class="rounded-circle" class="rounded-circle"
width="100" width="80"
height="100" height="80"
style="object-fit:cover;"> style="object-fit:cover;">
{% else %} {% else %}
<i class="bi bi-person-circle text-white" style="font-size:60px;"></i> <i class="bi bi-person-circle text-white" style="font-size:60px;"></i>
{% endif %} {% endif %}
<div class="text-white mt-2 fw-bold fs-5"> <div class="text-white mt-2">
{{ user.first_name }} {{ user.last_name }} {{ user.username }}
</div> </div>
</div> </div>
<a href="{% url 'gestion_employe:mon-profil' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_employe:mon-profil' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-person-circle"></i> Mon profil <i class="bi bi-person-circle"></i> Mon profil
</a> </a>
{% if user|has_group:"ressource_humaine" or user|has_group:"direction" %} {% if user|has_group:"ressource_humaine" or user|has_group:"direction" %}
<a href="{% url 'gestion_employe:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_employe:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-speedometer2"></i> Tableau de bord <i class="bi bi-speedometer2"></i> Tableau de bord
</a> </a>
{% endif %} {% endif %}
{% if user|has_group:"ressource_humaine" or user|has_group:"direction" %} {% if user|has_group:"ressource_humaine" or user|has_group:"direction" %}
<a href="{% url 'gestion_projet:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_projet:index' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-folder"></i> Gestion des Projets <i class="bi bi-folder"></i> Gestion des Projets
</a> </a>
{% endif %} {% endif %}
<a href="{% url 'gestion_conges:conge' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_conges:conge' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-airplane"></i> Gestion des congés <i class="bi bi-airplane"></i> Gestion des congés
</a> </a>
{% if user|is_chef_projet %} {% if user|is_chef_projet %}
<a href="{% url 'gestion_projet:activites-projet' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_projet:activites-projet' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-list-task"></i> Suivi des Activités <i class="bi bi-list-task"></i> Suivi des Activités
</a> </a>
{% endif %} {% endif %}
<a href="{% url 'gestion_salle:reservation-salle' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <a href="{% url 'gestion_salle:reservation-salle' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-calendar-check"></i> Réservation <i class="bi bi-calendar-check"></i> Réservation
</a> </a>
{% comment %} {% url 'deconnexion' %} {% endcomment %} {% comment %} <a href="{% url 'rapport-rh' %}" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<a href="{% url 'deconnexion' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size: 1.2em;"> <i class="bi bi-graph-up"></i> Rapports et Statistiques
</a> {% endcomment %}
{% comment %} <a href="" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<i class="bi bi-person-gear"></i> Gestion des Utilisateurs
</a> {% endcomment %}
{% comment %} <a href="{% url 'gestion_employe:departement' %}" class="text-white fw-bold text-decoration-none mb-2" style="font-size:1.2em">
<i class="bi bi-gear"></i> Paramètres
</a> {% endcomment %}
<a href="{% url 'deconnexion' %}" class="text-white fw-bold text-decoration-none mb-4" style="font-size:1.4em">
<i class="bi bi-box-arrow-right"></i> Déconnexion <i class="bi bi-box-arrow-right"></i> Déconnexion
</a> </a>
</div> </div>

View File

@@ -16,29 +16,32 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
# from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from . import views # from simple_sso.sso_server.server import Server
from .sso import SIRHClient
client_sirh = SIRHClient( from . import views
settings.SSO_SERVER,
settings.SSO_PUBLIC_KEY, # server_sso = Server()
settings.SSO_PRIVATE_KEY
)
urlpatterns = [ urlpatterns = [
path(
'',
views.login_view,
name='index'
),
path('login/', path('login/',
views.connexion, views.login_view,
name='login' name='login'
), ),
path( path(
'deconnexion/', 'deconnexion/',
views.deconnexion, views.deconnexion_view,
name='deconnexion' name='deconnexion'
), ),
path( path(
'', 'employé/',
include("gestion_employe.urls") include("gestion_employe.urls")
), ),
path( path(
@@ -57,6 +60,8 @@ urlpatterns = [
'admin/', 'admin/',
admin.site.urls admin.site.urls
), ),
path("client/", include(client_sirh.get_urls())), # path(
] # 'sso',
# + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # include(server_sso.get_urls())
# )
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,16 +1,37 @@
from django.contrib.auth import logout from django.contrib.auth import authenticate, login, logout
from django.shortcuts import redirect from django.shortcuts import render, redirect
from django.urls import reverse_lazy from django.contrib import messages
from django.conf import settings def login_view(request):
"""
Gère la connexion des utilisateurs avec redirection selon le rôle et
vérification de l'acceptation de la politique d'utilisation.
"""
if request.method == 'POST':
email = request.POST.get('mail')
password = request.POST.get('mot_de_passe')
if not (email and password):
messages.error(request, "Veuillez remplir tous les champs.")
return render(request, 'login.html')
def connexion(request): user = authenticate(request, username=email, password=password)
"""Redirige vers le serveur SSO pour l'authentification."""
return redirect(reverse_lazy("simple-sso-login"))
def deconnexion(request): if user is None:
"""Gérez la déconnexion des utilisateurs.""" messages.error(request, "Nom d’utilisateur ou mot de passe incorrect.")
return render(request, 'login.html')
if not user.is_active:
messages.error(request, "Compte inactif. Contactez l'administrateur.")
return render(request, 'login.html')
login(request, user)
return redirect("gestion_conges:conge")
return render(request, 'login.html')
def deconnexion_view(request):
"""Gère la déconnexion de l'utilisateur."""
logout(request) logout(request)
#return redirect(settings.SSO_LOGOUT_URL) return redirect('login')
return redirect("http://authentification.cerfig.org/login")

View File

@@ -1,7 +0,0 @@
from decouple import config
def contexte_universel(request):
return {
"SIRH_LIEN": config("SIRH_LIEN"),
"SGL_LIEN": config("SGL_LIEN")
}

View File

@@ -1,5 +1,5 @@
from django.utils import timezone from django.utils import timezone
from gestion_employe.models import Contrat, Employe from gestion_employe.models import Contrat
from gestion_conge.models import Conge from gestion_conge.models import Conge
@@ -8,11 +8,6 @@ NOMBRE_PAGINATION = 8
DEBUT_RAPPEL = 60 DEBUT_RAPPEL = 60
DUREE_FIN_CONTRAT = 90 DUREE_FIN_CONTRAT = 90
try:
EMAIL_ASSISTANTE_DE_DIRECTION = list(Employe.objects.filter(fonction="assistant_direction").values_list('user__email', flat=True))
except:
pass
def solde_conge(employe): def solde_conge(employe):
"""Fonction de calcul du solde de congé restant l'employé""" """Fonction de calcul du solde de congé restant l'employé"""
contrat = Contrat.objects.filter(employe=employe, statut='actif').order_by('-date_debut').first() contrat = Contrat.objects.filter(employe=employe, statut='actif').order_by('-date_debut').first()
@@ -37,25 +32,3 @@ def solde_conge(employe):
"quota_annuel": QUOTA_CONGE_ANNUEL - jours_conges_valider, "quota_annuel": QUOTA_CONGE_ANNUEL - jours_conges_valider,
"nombre_jours_valide": jours_conges_valider "nombre_jours_valide": jours_conges_valider
} }
def envoyer_mail(sujet, message, destinataires):
"""Fonction d'envoi de mail"""
from django.core.mail import send_mail
from django.conf import settings
send_mail(
sujet,
message,
settings.EMAIL_HOST_USER,
destinataires,
fail_silently=False,
)
def destinataire_mail_demande_conges(employe):
"""Fonction de récupération des destinataires pour les mails de demande de congés"""
if employe.chef:
return EMAIL_ASSISTANTE_DE_DIRECTION
else:
if employe.departement:
chefs_departement = Employe.objects.filter(departement=employe.departement, chef=True)
return list(chefs_departement.values_list('user__email', flat=True))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,4 +1,4 @@
# Generated by Django 5.2.13 on 2026-06-16 11:06 # Generated by Django 5.2.13 on 2026-04-17 12:03
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models

Some files were not shown because too many files have changed in this diff Show More