Docfile commited on
Commit
5bd630d
·
verified ·
1 Parent(s): c2b48d3

Upload admin.py

Browse files
Files changed (1) hide show
  1. app/admin.py +27 -82
app/admin.py CHANGED
@@ -1,4 +1,3 @@
1
- # START OF FILE admin.py
2
  from flask import Blueprint
3
  from flask_admin.contrib.sqla import ModelView
4
  from flask_admin import BaseView, expose
@@ -6,120 +5,66 @@ from app import db, admin
6
  from app.models import Matiere, SousCategorie, Texte
7
  from flask_ckeditor import CKEditorField
8
  from wtforms import StringField, TextAreaField
9
- from bleach import clean, ALLOWED_TAGS, ALLOWED_ATTRIBUTES
10
- # Importer func pour les tris/filtres
11
- from sqlalchemy import func
12
 
13
 
14
  bp = Blueprint('custom_admin', __name__, url_prefix='/admin')
15
 
16
- # Définir les tags et attributs HTML autorisés
17
- # On ajoute les tags de base + titres, listes, blockquotes etc.
18
- # ALTERNATIVE (résultat en liste)
19
- ALLOWED_TAGS_EXTENDED = list(ALLOWED_TAGS) + [ # Convertit d'abord en liste
20
- 'p', 'br', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
21
- 'ul', 'ol', 'li', 'blockquote', 'pre', 'code',
22
- 'strong', 'em', 'u', 's', 'sub', 'sup', 'span'
23
- ]
24
- # Autoriser les attributs de base + 'style' pour certains éléments si nécessaire
25
- # ATTENTION : Autoriser 'style' peut ouvrir des failles si pas bien contrôlé.
26
- # Une alternative est de ne pas autoriser 'style' et d'utiliser des classes CSS.
27
- ALLOWED_ATTRIBUTES_EXTENDED = ALLOWED_ATTRIBUTES.copy() # Créer une copie pour modifier
28
- ALLOWED_ATTRIBUTES_EXTENDED['a'] = ['href', 'title', 'target'] # Autoriser target pour ouvrir dans une nouvelle fenetre
29
- ALLOWED_ATTRIBUTES_EXTENDED['span'] = ['style'] # Exemple: Autoriser style sur span
30
- ALLOWED_ATTRIBUTES_EXTENDED['p'] = ['style'] # Exemple: Autoriser style sur p
31
- # Ajoutez d'autres éléments et leurs attributs autorisés si besoin, ex: img: ['src', 'alt', 'title', 'width', 'height']
32
-
33
  def sanitize_html(html_content):
34
- """
35
- Nettoie le contenu HTML en autorisant un ensemble défini de balises et d'attributs.
36
- """
37
- if not html_content:
38
- return ""
39
-
40
- # Utiliser bleach avec les tags/attributs étendus
41
- # strip=True supprime les tags non autorisés au lieu de les échapper
42
- # strip_comments=True supprime les commentaires HTML
43
- cleaned_html = clean(
44
- html_content,
45
- tags=ALLOWED_TAGS_EXTENDED,
46
- attributes=ALLOWED_ATTRIBUTES_EXTENDED,
47
- strip=True,
48
- strip_comments=True
49
- )
50
- return cleaned_html
51
 
52
 
53
  class MatiereView(ModelView):
54
  column_list = ('nom', 'sous_categories') # Colonnes à afficher dans la liste
55
  form_columns = ('nom',)
56
- # Pour trier la liste déroulante des sous-catégories (si affichée ailleurs)
57
- column_sortable_list = ('nom',)
58
- column_searchable_list = ('nom',)
59
-
60
 
61
  class SousCategorieView(ModelView):
62
  column_list = ('nom', 'matiere')
63
  form_columns = ('nom', 'matiere')
64
- column_sortable_list = ('nom', ('matiere', 'matiere.nom')) # Tri par nom et par nom de matière
65
- column_searchable_list = ('nom', 'matiere.nom') # Recherche
66
- column_filters = ('matiere',) # Ajout de filtre par matière
67
-
68
- form_args = {
69
  'matiere': {
70
  'query_factory': lambda: Matiere.query.order_by(func.lower(Matiere.nom)) #Tri insensible à la casse
71
  }
72
  }
73
-
74
- # Ajout de 'func' manquant dans les imports
75
  def on_model_change(self, form, model, is_created):
76
  # Vérification de l'unicité (nom, matiere_id) *avant* l'insertion/mise à jour
77
- matiere_id = form.matiere.data.id if form.matiere.data else None
78
- if not matiere_id:
79
- raise ValueError("La matière est obligatoire.")
80
-
81
- nom_lower = func.lower(form.nom.data)
82
-
83
- query = SousCategorie.query.filter(
84
- func.lower(SousCategorie.nom) == nom_lower,
85
- SousCategorie.matiere_id == matiere_id
86
- )
87
-
88
- if not is_created: # Exclure l'enregistrement actuel lors de la mise à jour
89
- query = query.filter(SousCategorie.id != model.id)
90
-
91
- existing = query.first()
92
-
93
- if existing:
94
- raise ValueError(f"La sous-catégorie '{form.nom.data}' existe déjà pour la matière '{form.matiere.data.nom}'.")
95
 
96
 
97
  class TexteView(ModelView):
98
- column_list = ('titre', 'sous_categorie', 'auteur') # Ajout de l'auteur dans la liste
99
- form_columns = ('titre', 'contenu', 'sous_categorie', 'auteur') # Ajout de l'auteur dans le formulaire
100
  form_overrides = dict(contenu=CKEditorField)
101
-
102
- column_sortable_list = ('titre', ('sous_categorie', 'sous_categorie.nom'),) # Tri
103
- column_searchable_list = ('titre', 'contenu', , 'sous_categorie.nom') # Recherche
104
- column_filters = ('sous_categorie') # Filtres
105
-
106
  form_args = {
107
  'sous_categorie': {
108
  'query_factory': lambda: SousCategorie.query.join(Matiere).order_by(func.lower(Matiere.nom), func.lower(SousCategorie.nom))
109
- },
110
-
111
  }
112
 
113
  def on_model_change(self, form, model, is_created):
114
- # Appliquer la sanitization améliorée
115
  model.contenu = sanitize_html(form.contenu.data)
116
- # Optionnel: Mettre une valeur par défaut si l'auteur est vide
117
- if not model.auteur:
118
- model.auteur = "Anonyme" # Ou None si vous préférez
119
 
120
 
121
  admin.add_view(MatiereView(Matiere, db.session))
122
  admin.add_view(SousCategorieView(SousCategorie, db.session))
123
  admin.add_view(TexteView(Texte, db.session))
124
-
125
- # --- END OF FILE admin.py ---
 
 
1
  from flask import Blueprint
2
  from flask_admin.contrib.sqla import ModelView
3
  from flask_admin import BaseView, expose
 
5
  from app.models import Matiere, SousCategorie, Texte
6
  from flask_ckeditor import CKEditorField
7
  from wtforms import StringField, TextAreaField
8
+ from bleach import clean
9
+ from bs4 import BeautifulSoup
 
10
 
11
 
12
  bp = Blueprint('custom_admin', __name__, url_prefix='/admin')
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def sanitize_html(html_content):
15
+ # TRÈS PERMISSIF - UNIQUEMENT POUR LE TEST
16
+ return clean(html_content, tags=[], attributes={}, strip=False)
17
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
 
20
  class MatiereView(ModelView):
21
  column_list = ('nom', 'sous_categories') # Colonnes à afficher dans la liste
22
  form_columns = ('nom',)
 
 
 
 
23
 
24
  class SousCategorieView(ModelView):
25
  column_list = ('nom', 'matiere')
26
  form_columns = ('nom', 'matiere')
27
+ #form_overrides = dict(nom=StringField)
28
+ form_args = { # Amélioration de la sélection de la matière
 
 
 
29
  'matiere': {
30
  'query_factory': lambda: Matiere.query.order_by(func.lower(Matiere.nom)) #Tri insensible à la casse
31
  }
32
  }
 
 
33
  def on_model_change(self, form, model, is_created):
34
  # Vérification de l'unicité (nom, matiere_id) *avant* l'insertion/mise à jour
35
+ if is_created:
36
+ existing = SousCategorie.query.filter(
37
+ func.lower(SousCategorie.nom) == func.lower(form.nom.data),
38
+ SousCategorie.matiere_id == form.matiere.data.id
39
+ ).first()
40
+ if existing:
41
+ raise ValueError("Cette sous-catégorie existe déjà pour cette matière.")
42
+ else: #Mise à jour
43
+ existing = SousCategorie.query.filter(
44
+ func.lower(SousCategorie.nom) == func.lower(form.nom.data),
45
+ SousCategorie.matiere_id == form.matiere.data.id,
46
+ SousCategorie.id != model.id
47
+ ).first()
48
+
49
+ if existing:
50
+ raise ValueError("Cette sous-catégorie existe déjà pour cette matière.")
 
 
51
 
52
 
53
  class TexteView(ModelView):
54
+ column_list = ('titre', 'sous_categorie')
55
+ form_columns = ('titre', 'contenu', 'sous_categorie')
56
  form_overrides = dict(contenu=CKEditorField)
 
 
 
 
 
57
  form_args = {
58
  'sous_categorie': {
59
  'query_factory': lambda: SousCategorie.query.join(Matiere).order_by(func.lower(Matiere.nom), func.lower(SousCategorie.nom))
60
+ }
 
61
  }
62
 
63
  def on_model_change(self, form, model, is_created):
 
64
  model.contenu = sanitize_html(form.contenu.data)
65
+
 
 
66
 
67
 
68
  admin.add_view(MatiereView(Matiere, db.session))
69
  admin.add_view(SousCategorieView(SousCategorie, db.session))
70
  admin.add_view(TexteView(Texte, db.session))