Repository: zestedesavoir/zds-site

Issue:
Element HTML avec la même Id sur la page d'un contenu publié
![image](https://user-images.githubusercontent.com/18501150/83149508-75fcee80-a0fa-11ea-8eff-e9d07da21002.png)

On a deux fois un élément avec la même id, l'id devrait être unique

Sur la prod : https://zestedesavoir.com/tutoriels/3324/introduction-a-la-visualisation-de-graphes-avec-visnetwork/ 


Discussion:
Précision: il faut être connecté avec les droits staff pour voir ce bug (ça vient du formulaire pour dépublier un contenu).
Le bug vient de la génération des formulaires par Django. Par défaut, il attribue des id aux champs en fonction de leur nom (voir [la doc dédiée](https://docs.djangoproject.com/en/3.0/ref/forms/api/#django.forms.Form.auto_id)). Or, certaines de nos pages ont plusieurs formulaires, dont certains ont des noms de champs identiques et se retrouvent donc avec le même id. Il faut donc configurer correctement les formulaires pour éviter que le nommage automatique se fasse de manière incontrôlée.

Pour ce faire, je vois deux solutions potentielles, que je ne sais pas départager :

- changer les configuration des auto_id directement dans les formulaires, ce qui me paraît un peu brutal et plus dur à maintenir (dans le sens ou il faudrait une convention ou s'assurer qu'il n'y a pas de doublons dans plein de fichiers), mais ça règle le problème à la source ;
- changer les configurations des auto_id dans les `get_context_data` des vues, ce qui permet d'avoir tous les préfixes sous les yeux pour une vue donnée et de gérer le problème des id seulement là où c'est nécessaire.




On peut aussi utiliser l'attribut `form_id` de [crispyforms](https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html#helper-attributes-you-can-set). Il me semble que ça définit aussi le préfixe de tous les ids des champs. 

Sinon je pense que ta première proposition est la bonne, si on met comme préfixe le nom de la classe du formulaire, ce sera unique sur la page, tant qu'un formulaire n'est utilisé qu'une fois sur chaque page (ce qui me semble être le cas).
Je viens de vérifier et `form_id` n'est apparemment utilisé que pour donner un `id` à la balise `form`. La plupart de nos formulaires ont déja un `form_id` défini et la duplication se fait malgré tout.

Resolving patch:
diff --git a/zds/tutorialv2/forms.py b/zds/tutorialv2/forms.py
--- a/zds/tutorialv2/forms.py
+++ b/zds/tutorialv2/forms.py
@@ -677,7 +677,9 @@ class AskValidationForm(forms.Form):
     text = forms.CharField(
         label="",
         required=False,
-        widget=forms.Textarea(attrs={"placeholder": _("Commentaire pour votre demande."), "rows": "3"}),
+        widget=forms.Textarea(
+            attrs={"placeholder": _("Commentaire pour votre demande."), "rows": "3", "id": "ask_validation_text"}
+        ),
     )
 
     version = forms.CharField(widget=forms.HiddenInput(), required=True)
@@ -810,7 +812,9 @@ class CancelValidationForm(forms.Form):
     text = forms.CharField(
         label="",
         required=True,
-        widget=forms.Textarea(attrs={"placeholder": _("Pourquoi annuler la validation ?"), "rows": "4"}),
+        widget=forms.Textarea(
+            attrs={"placeholder": _("Pourquoi annuler la validation ?"), "rows": "4", "id": "cancel_text"}
+        ),
     )
 
     def __init__(self, validation, *args, **kwargs):
@@ -856,7 +860,9 @@ def clean(self):
 class RejectValidationForm(forms.Form):
 
     text = forms.CharField(
-        label="", required=True, widget=forms.Textarea(attrs={"placeholder": _("Commentaire de rejet."), "rows": "6"})
+        label="",
+        required=True,
+        widget=forms.Textarea(attrs={"placeholder": _("Commentaire de rejet."), "rows": "6", "id": "reject_text"}),
     )
 
     def __init__(self, validation, *args, **kwargs):
@@ -912,7 +918,9 @@ class RevokeValidationForm(forms.Form):
     text = forms.CharField(
         label="",
         required=True,
-        widget=forms.Textarea(attrs={"placeholder": _("Pourquoi dépublier ce contenu ?"), "rows": "6"}),
+        widget=forms.Textarea(
+            attrs={"placeholder": _("Pourquoi dépublier ce contenu ?"), "rows": "6", "id": "up_text"}
+        ),
     )
 
     def __init__(self, content, *args, **kwargs):
@@ -1009,7 +1017,9 @@ def __init__(self, *args, **kwargs):
 class WarnTypoForm(forms.Form):
 
     text = forms.CharField(
-        label="", required=True, widget=forms.Textarea(attrs={"placeholder": _("Expliquez la faute"), "rows": "3"})
+        label="",
+        required=True,
+        widget=forms.Textarea(attrs={"placeholder": _("Expliquez la faute"), "rows": "3", "id": "warn_text"}),
     )
 
     target = forms.CharField(widget=forms.HiddenInput(), required=False)
@@ -1154,7 +1164,9 @@ class UnpublicationForm(forms.Form):
     text = forms.CharField(
         label="",
         required=True,
-        widget=forms.Textarea(attrs={"placeholder": _("Pourquoi dépublier ce contenu ?"), "rows": "6"}),
+        widget=forms.Textarea(
+            attrs={"placeholder": _("Pourquoi dépublier ce contenu ?"), "rows": "6", "id": "up_reason"}
+        ),
     )
 
     def __init__(self, content, *args, **kwargs):


Test patch:
diff --git a/zds/tutorialv2/tests/__init__.py b/zds/tutorialv2/tests/__init__.py
--- a/zds/tutorialv2/tests/__init__.py
+++ b/zds/tutorialv2/tests/__init__.py
@@ -79,7 +79,7 @@ def ask_validation(self):
         find_element = self.selenium.find_element_by_css_selector
         self.selenium.get(self.live_server_url + self.content.get_absolute_url())
         find_element('a[href="#ask-validation"]').click()
-        find_element("#id_text").send_keys("Coucou.")
+        find_element("#ask-validation textarea").send_keys("Coucou.")
         find_element('#ask-validation button[type="submit"]').click()
 
     def take_reservation(self):
@@ -93,7 +93,7 @@ def validate(self):
         self.selenium.get(self.live_server_url + self.content.get_absolute_url())
         validation = Validation.objects.filter(content=self.content).first()
         find_element('a[href="#valid-publish"]').click()
-        find_element("form#valid-publish #id_text").send_keys("Coucou.")
+        find_element("form#valid-publish textarea").send_keys("Coucou.")
         find_element(f'form[action="/validations/accepter/{validation.pk}/"] button').click()
 
     def wait_element_attribute_change(self, locator, attribute, initial_value, time):