From ef8bbdfde1daf7b4782bd11cb50c7db05ac943e6 Mon Sep 17 00:00:00 2001 From: Dorian Lesbre Date: Thu, 24 Dec 2020 17:38:07 +0100 Subject: [PATCH 01/14] Changed email from cof-geek to klub-dev --- shared/templates/wiki/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/templates/wiki/base.html b/shared/templates/wiki/base.html index 975c37e..86f1599 100644 --- a/shared/templates/wiki/base.html +++ b/shared/templates/wiki/base.html @@ -62,5 +62,5 @@ {% block wiki_footer_logo %} -

Wiki maintenu par les élèves de l'École normale supérieure.
En cas de pépin contacter cof-geek [chez] ens [point] fr

+

Wiki maintenu par les élèves de l'École normale supérieure.
En cas de pépin contacter klub-dev [chez] ens [point] fr

{% endblock %} From 69d34e5a44e42761ca2cb4157895bea85a28ed0f Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Thu, 28 Jan 2021 15:24:41 +0100 Subject: [PATCH 02/14] Corrige le look de la navbar du wiki --- shared/templates/wiki/base.html | 77 +++++++++++++++------------------ 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/shared/templates/wiki/base.html b/shared/templates/wiki/base.html index 86f1599..4188a8c 100644 --- a/shared/templates/wiki/base.html +++ b/shared/templates/wiki/base.html @@ -6,57 +6,50 @@ {% block wiki_header_branding %} - Wiki  ENS + Wiki  ENS {% endblock %} {% block wiki_header_navlinks %} - {% endblock %} diff --git a/wiki_groups/forms.py b/wiki_groups/forms.py new file mode 100644 index 0000000..8c397fc --- /dev/null +++ b/wiki_groups/forms.py @@ -0,0 +1,52 @@ +from django import forms +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group + +from wiki_groups.models import WikiGroup + +User = get_user_model() + + +class SelectUserForm(forms.Form): + user = forms.CharField(max_length=150) + + def clean_user(self): + user = User.objects.filter(username=self.cleaned_data["user"]).first() + + if user is None: + self.add_error( + "user", "Aucune utilisatrice ou utilisateur avec ce login n'existe." + ) + + return user + + +class SelectGroupForm(forms.Form): + group = forms.CharField(max_length=150) + + def clean_group(self): + group = WikiGroup.objects.filter( + django_group__name=self.cleaned_data["group"] + ).first() + + if group is None: + self.add_error("group", "Aucun groupe avec ce nom n'existe.") + + return group + + +class CreateGroupForm(forms.Form): + group = forms.CharField(max_length=150) + + def clean_group(self): + name = self.cleaned_data["group"] + + django_group, created = Group.objects.get_or_create(name=name) + + if hasattr(django_group, "wikigroup"): + self.add_error("group", "Un groupe avec ce nom existe déjà.") + return None + + group = WikiGroup.objects.create(django_group=django_group) + + return group diff --git a/wiki_groups/migrations/0002_wikigroup_managers.py b/wiki_groups/migrations/0002_wikigroup_managers.py new file mode 100644 index 0000000..8ae5dd3 --- /dev/null +++ b/wiki_groups/migrations/0002_wikigroup_managers.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.19 on 2021-07-23 09:01 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('wiki_groups', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='wikigroup', + name='managers', + field=models.ManyToManyField(blank=True, related_name='managed_groups', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/wiki_groups/models.py b/wiki_groups/models.py index 038f303..79444a9 100644 --- a/wiki_groups/models.py +++ b/wiki_groups/models.py @@ -1,12 +1,14 @@ -from django.db import models -from django.contrib.auth.models import Group as DjangoGroup from django.contrib.auth import get_user_model -from django.db.models.signals import post_save, m2m_changed +from django.contrib.auth.models import Group as DjangoGroup +from django.db import models +from django.db.models.signals import m2m_changed, post_save from django.dispatch import receiver +User = get_user_model() + class WikiGroup(models.Model): - """ A structured user group, used to grant permissions on sub-wikis + """A structured user group, used to grant permissions on sub-wikis This model contains a structured group of users, in the sense that a group contains both users and other groups, allowing a DAG group structure. @@ -20,8 +22,8 @@ class WikiGroup(models.Model): """ class CyclicStructureException(Exception): - """ Exception raised when a new edge introduces a cycle in the groups' - structure """ + """Exception raised when a new edge introduces a cycle in the groups' + structure""" def __init__(self, from_group, to_group): self.from_group = from_group @@ -40,22 +42,48 @@ class WikiGroup(models.Model): related_name="included_in_groups", blank=True, ) - users = models.ManyToManyField(get_user_model(), blank=True) + users = models.ManyToManyField(User, blank=True) + managers = models.ManyToManyField(User, related_name="managed_groups", blank=True) def __str__(self): return str(self.django_group) def get_all_users(self): - """ Get the queryset of all the users in this group, including recursively - included users """ + """Get the queryset of all the users in this group, including recursively + included users""" users_set = self.users.all() for subgroup in self.includes_groups.all(): users_set = users_set.union(subgroup.get_all_users()) return users_set + def is_manager(self, user): + """Checks wether the user is a manager of this group or any subgroup""" + + # Base case: the user is an explicit manager + if user in self.managers.all(): + return True + + for subgroup in self.includes_groups.all(): + # If the user is a manager of a subgroup + if subgroup.is_manager(user): + return True + + return False + + def get_all_groups(self): + """Returns the set of metagroups i.e. self plus the list of groups including + this group recursively""" + + groups = {self} + + for metagroup in self.included_in_groups.all(): + groups |= metagroup.get_all_groups() + + return groups + def propagate_update(self, already_notified=None): - """ Commits itself to the Django group, and calls this method on every group in - `included_in_groups` """ + """Commits itself to the Django group, and calls this method on every group in + `included_in_groups`""" # Check that we did not already propagate the update signal to this group if already_notified is None: @@ -69,20 +97,20 @@ class WikiGroup(models.Model): metagroup.propagate_update(already_notified=already_notified) def commit_to_django_group(self): - """ Writes this model's data to the related Django group """ + """Writes this model's data to the related Django group""" self.django_group.user_set.set(self.get_all_users()) def group_in_cycle(self, with_children): - """ Checks whether this group would be in a group cycle if it had + """Checks whether this group would be in a group cycle if it had `with_children` as child nodes. This assumes that the graph currently stored is acyclic. Returns `None` if no cycle is found, else retuns a child from `with_children` - causing the cycle to appear. """ + causing the cycle to appear.""" def do_dfs(cur_node, visited_nodes): - """ DFS to check whether we find `self` again """ + """DFS to check whether we find `self` again""" if cur_node.pk in visited_nodes: return False if cur_node.pk == self.pk: @@ -103,7 +131,7 @@ class WikiGroup(models.Model): @receiver(post_save, sender=WikiGroup, dispatch_uid="on_wiki_group_changed") def on_wiki_group_changed(sender, instance, **kwargs): - """ Commit the related WikiGroups to Django Group upon model change """ + """Commit the related WikiGroups to Django Group upon model change""" instance.propagate_update() @@ -113,8 +141,8 @@ def on_wiki_group_changed(sender, instance, **kwargs): dispatch_uid="on_wiki_group_includes_changed", ) def on_wiki_group_includes_changed(sender, instance, action, **kwargs): - """ Commit the related WikiGroups to Django Group upon change of the set of - included other groups """ + """Commit the related WikiGroups to Django Group upon change of the set of + included other groups""" if action in ["post_add", "post_remove", "post_clear"]: instance.propagate_update() @@ -125,7 +153,7 @@ def on_wiki_group_includes_changed(sender, instance, action, **kwargs): dispatch_uid="on_wiki_group_users_changed", ) def on_wiki_group_users_changed(sender, instance, action, **kwargs): - """ Commit the related WikiGroups to Django Group upon change of included users """ + """Commit the related WikiGroups to Django Group upon change of included users""" if action in ["post_add", "post_remove", "post_clear"]: instance.propagate_update() @@ -136,7 +164,7 @@ def on_wiki_group_users_changed(sender, instance, action, **kwargs): dispatch_uid="on_wiki_group_includes_check_acyclic", ) def on_wiki_group_includes_check_acyclic(sender, instance, action, pk_set, **kwargs): - """ Checks the acyclicity of the groups' graph before committing new edges. + """Checks the acyclicity of the groups' graph before committing new edges. PLEASE NOTE that this check is only a fallback, and that forms should validate the acyclicity before committing anything. diff --git a/wiki_groups/templates/wiki_groups/admin.html b/wiki_groups/templates/wiki_groups/admin.html new file mode 100644 index 0000000..b1154b5 --- /dev/null +++ b/wiki_groups/templates/wiki_groups/admin.html @@ -0,0 +1,210 @@ +{% extends "wiki/base.html" %} +{% load sekizai_tags %} + +{% block wiki_site_title %}Groupes administrés - WikiENS{% endblock %} + +{% block wiki_contents %} +

Gestion du groupe « {{ wikigroup }} »

+
+ +
+
+ +
+

Liste des membres

+
+ +
+
+
+ {% csrf_token %} +
+ +
+ +
+ {% if errors.user %} + {% for msg in errors.user.msg %} +
{{ msg }}
+ {% endfor %} + {% endif %} +
+ Entrer le login (clipper) de la personne à ajouter +
+
+ + {% for user in wikigroup.users.all %} +
+ {{ user }} +
+ {% endfor %} +
+
+ +
+

Liste des groupes inclus

+
+ +
+
+
+ {% csrf_token %} +
+ +
+ +
+ {% if errors.group_add %} + {% for msg in errors.group_add.msg %} +
{{ msg }}
+ {% endfor %} + {% endif %} +
+ Entrer le nom du groupe à ajouter +
+
+ + {% for group in wikigroup.includes_groups.all %} +
+ {{ group }} +
+ {% endfor %} + +
+
+ {% csrf_token %} +
+ +
+ +
+ {% if errors.group_create %} + {% for msg in errors.group_create.msg %} +
{{ msg }}
+ {% endfor %} + {% endif %} +
+ Entrer le nom du groupe à créer +
+
+
+
+
+
+ +
+
+

Liste des gestionnaires

+
+ +
+
+
+ {% csrf_token %} +
+ +
+ +
+ {% if errors.manager %} + {% for msg in errors.manager.msg %} +
{{ msg }}
+ {% endfor %} + {% endif %} +
+ Entrer le login (clipper) de la personne à ajouter +
+
+ + {% for user in wikigroup.managers.all %} +
+ {{ user }} +
+ {% endfor %} +
+
+ + {% if request.user.is_staff %} +
+

Supprimer ce groupe

+
+ +
+

Attention, cette action est irréversible.

+
+
+ + + {% endif %} +
+
+ +{# Confirmation modal #} + + +{% addtoblock "js" %} + +{% endaddtoblock %} + +{% endblock %} diff --git a/wiki_groups/templates/wiki_groups/managed.html b/wiki_groups/templates/wiki_groups/managed.html new file mode 100644 index 0000000..4935b0e --- /dev/null +++ b/wiki_groups/templates/wiki_groups/managed.html @@ -0,0 +1,18 @@ +{% extends "wiki/base.html" %} + +{% block wiki_site_title %}Groupes administrés - WikiENS{% endblock %} + +{% block wiki_contents %} +

Liste des groupes administrés

+
+ +{% if wikigroups %} +
+ {% for group in wikigroups %} + {{ group }} + {% endfor %} +
+{% endif %} + + +{% endblock %} diff --git a/wiki_groups/urls.py b/wiki_groups/urls.py index 851a62d..b9c4f44 100644 --- a/wiki_groups/urls.py +++ b/wiki_groups/urls.py @@ -1,9 +1,31 @@ from django.urls import path -from wiki_groups import views +from wiki_groups import views app_name = "wiki_groups" urlpatterns = [ path("dot_graph", views.dot_graph, name="dot_graph"), path("graph", views.graph, name="graph"), + path("managed", views.ManagedGroupsView.as_view(), name="managed-groups"), + path("", views.WikiGroupView.as_view(), name="admin-group"), + path( + "/rm-user/", + views.RemoveUserView.as_view(), + name="remove-user", + ), + path( + "/rm-manager/", + views.RemoveManagerView.as_view(), + name="remove-manager", + ), + path( + "/rm-group/", + views.RemoveGroupView.as_view(), + name="remove-group", + ), + path("/add-user", views.AddUserView.as_view(), name="add-user"), + path("/add-manager", views.AddManagerView.as_view(), name="add-manager"), + path("/add-group", views.AddGroupView.as_view(), name="add-group"), + path("/create-group", views.CreateGroupView.as_view(), name="create-group"), + path("/delete", views.DeleteGroupView.as_view(), name="delete-group"), ] diff --git a/wiki_groups/views.py b/wiki_groups/views.py index c5200cd..2de792e 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -1,8 +1,16 @@ -from django.http import HttpResponse -from django.views.generic import TemplateView +from django.contrib.auth import get_user_model +from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin +from django.http import HttpResponse, HttpResponseRedirect +from django.urls import reverse, reverse_lazy +from django.views.generic import DetailView, ListView, RedirectView, TemplateView +from django.views.generic.detail import SingleObjectMixin +from django.views.generic.edit import BaseFormView +from wiki_groups.forms import CreateGroupForm, SelectGroupForm, SelectUserForm from wiki_groups.models import WikiGroup +User = get_user_model() + def dot_graph(request): response = HttpResponse(content_type="text/vnd.graphviz") @@ -23,3 +31,182 @@ def dot_graph(request): graph = TemplateView.as_view(template_name="wiki_groups/graph.html") + + +class WikiGroupMixin(SingleObjectMixin, UserPassesTestMixin): + """Restricts the view to a manager of the wikigroup""" + + model = WikiGroup + + def get_queryset(self): + return super().get_queryset().select_related("django_group") + + def test_func(self): + self.object = self.get_object() + return self.request.user.is_staff or self.object.is_manager(self.request.user) + + +class StaffMixin(UserPassesTestMixin): + """Restricts the view to a staff member""" + + def test_func(self): + return self.request.user.is_staff + + +class ManagedGroupsView(LoginRequiredMixin, ListView): + model = WikiGroup + template_name = "wiki_groups/managed.html" + + def get_queryset(self): + if self.request.user.is_staff: + return WikiGroup.objects.select_related("django_group") + return self.request.user.managed_groups.select_related("django_group") + + def get_context_data(self, **kwargs): + ctx = super().get_context_data(**kwargs) + ctx["wikigroups"] = set() + + for group in ctx["wikigroup_list"]: + ctx["wikigroups"] |= group.get_all_groups() + + return ctx + + +class WikiGroupView(WikiGroupMixin, DetailView): + template_name = "wiki_groups/admin.html" + + def get_context_data(self, **kwargs): + kwargs.update({"errors": self.request.session.pop("wiki_form_errors", None)}) + return super().get_context_data(**kwargs) + + +class RemoveUserView(WikiGroupMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) + + def get(self, request, *args, **kwargs): + user = User.objects.get(pk=kwargs["user_pk"]) + self.object.users.remove(user) + + return super().get(request, *args, **kwargs) + + +class RemoveManagerView(WikiGroupMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) + + def get(self, request, *args, **kwargs): + user = User.objects.get(pk=kwargs["user_pk"]) + self.object.managers.remove(user) + + return super().get(request, *args, **kwargs) + + +class RemoveGroupView(WikiGroupMixin, RedirectView): + def get_redirect_url(self, *args, **kwargs): + return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) + + def get(self, request, *args, **kwargs): + group = WikiGroup.objects.get(pk=kwargs["group_pk"]) + self.object.includes_groups.remove(group) + + return super().get(request, *args, **kwargs) + + +class AddUserView(WikiGroupMixin, BaseFormView): + form_class = SelectUserForm + + def get_success_url(self): + return reverse("wiki_groups:admin-group", args=[self.object.pk]) + + def form_valid(self, form): + self.object.users.add(form.cleaned_data["user"]) + + return super().form_valid(form) + + def form_invalid(self, form): + self.request.session["wiki_form_errors"] = { + "user": {"value": form["user"].value(), "msg": form.errors["user"]} + } + return HttpResponseRedirect(self.get_success_url()) + + +class AddManagerView(WikiGroupMixin, BaseFormView): + form_class = SelectUserForm + + def get_success_url(self): + return reverse("wiki_groups:admin-group", args=[self.object.pk]) + + def form_valid(self, form): + self.object.managers.add(form.cleaned_data["user"]) + + return super().form_valid(form) + + def form_invalid(self, form): + self.request.session["wiki_form_errors"] = { + "manager": {"value": form["user"].value(), "msg": form.errors["user"]} + } + return HttpResponseRedirect(self.get_success_url()) + + +class AddGroupView(WikiGroupMixin, BaseFormView): + form_class = SelectGroupForm + + def get_success_url(self): + return reverse("wiki_groups:admin-group", args=[self.object.pk]) + + def form_valid(self, form): + subgroup = form.cleaned_data["group"] + group = self.object + + if group.group_in_cycle(list(group.includes_groups.all()) + [subgroup]): + form.add_error("group", "Ajout impossible sous peine de créer un cycle") + return self.form_invalid(form) + + group.includes_groups.add(subgroup) + return super().form_valid(form) + + def form_invalid(self, form): + self.request.session["wiki_form_errors"] = { + "group_add": {"value": form["group"].value(), "msg": form.errors["group"]} + } + return HttpResponseRedirect(self.get_success_url()) + + +class CreateGroupView(WikiGroupMixin, BaseFormView): + form_class = CreateGroupForm + + def get_success_url(self): + return reverse("wiki_groups:admin-group", args=[self.object.pk]) + + def form_valid(self, form): + new_group = form.cleaned_data["group"] + + self.object.includes_groups.add(new_group) + new_group.managers.add(self.request.user) + + return super().form_valid(form) + + def form_invalid(self, form): + self.request.session["wiki_form_errors"] = { + "group_create": { + "value": form["group"].value(), + "msg": form.errors["group"], + } + } + return HttpResponseRedirect(self.get_success_url()) + + +class DeleteGroupView(SingleObjectMixin, StaffMixin, RedirectView): + model = WikiGroup + url = reverse_lazy("wiki_groups:managed-groups") + + def get(self, request, *args, **kwargs): + group = self.get_object() + # On enlève les membres pour répercuter les changements + group.users.clear() + + # On utilise la propagation de la suppression django_group -> wiki_group + group.django_group.delete() + + return super().get(request, *args, **kwargs) From f257209f8fecfe22fd9e2f8c5f3a25b6691f07aa Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Mon, 11 Oct 2021 15:34:24 +0200 Subject: [PATCH 05/14] JS update --- wiki_groups/templates/wiki_groups/admin.html | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/wiki_groups/templates/wiki_groups/admin.html b/wiki_groups/templates/wiki_groups/admin.html index b1154b5..85ec40f 100644 --- a/wiki_groups/templates/wiki_groups/admin.html +++ b/wiki_groups/templates/wiki_groups/admin.html @@ -193,15 +193,12 @@ {% addtoblock "js" %} From 9c02e1e9027916e5b9c44aadcbd171590e5f4d71 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 18:53:31 +0200 Subject: [PATCH 06/14] Use already_notified in get_all_groups --- wiki_groups/models.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/wiki_groups/models.py b/wiki_groups/models.py index 79444a9..55ae155 100644 --- a/wiki_groups/models.py +++ b/wiki_groups/models.py @@ -70,14 +70,20 @@ class WikiGroup(models.Model): return False - def get_all_groups(self): + def get_all_groups(self, already_notified=None): """Returns the set of metagroups i.e. self plus the list of groups including this group recursively""" + if already_notified is None: + already_notified = set() + elif self.pk in already_notified: + return set() + already_notified.add(self.pk) + groups = {self} for metagroup in self.included_in_groups.all(): - groups |= metagroup.get_all_groups() + groups |= metagroup.get_all_groups(already_notified=already_notified) return groups From 3da477729af2de93e819226b9674a740ede2e2f9 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 18:57:38 +0200 Subject: [PATCH 07/14] Better docstring --- wiki_groups/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wiki_groups/views.py b/wiki_groups/views.py index 2de792e..d7076f6 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -34,7 +34,10 @@ graph = TemplateView.as_view(template_name="wiki_groups/graph.html") class WikiGroupMixin(SingleObjectMixin, UserPassesTestMixin): - """Restricts the view to a manager of the wikigroup""" + """ + Restricts the view to a manager of the wikigroup, and selects automatically + the required WikiGroup with its Django group + """ model = WikiGroup From d5c1e2225aa458a5bf9e34c183874e81f406df31 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 19:06:23 +0200 Subject: [PATCH 08/14] On remplace les Querysets par ce qu'on veut --- wiki_groups/templates/wiki_groups/managed.html | 4 ++-- wiki_groups/views.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/wiki_groups/templates/wiki_groups/managed.html b/wiki_groups/templates/wiki_groups/managed.html index 4935b0e..2a4b3e8 100644 --- a/wiki_groups/templates/wiki_groups/managed.html +++ b/wiki_groups/templates/wiki_groups/managed.html @@ -6,9 +6,9 @@

Liste des groupes administrés


-{% if wikigroups %} +{% if wikigroup_list %}
- {% for group in wikigroups %} + {% for group in wikigroup_list %} {{ group }} {% endfor %}
diff --git a/wiki_groups/views.py b/wiki_groups/views.py index d7076f6..afda14f 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -67,10 +67,13 @@ class ManagedGroupsView(LoginRequiredMixin, ListView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["wikigroups"] = set() + wikigroups = set() for group in ctx["wikigroup_list"]: - ctx["wikigroups"] |= group.get_all_groups() + wikigroups |= group.get_all_groups() + + ctx["wikigroup_list"] = wikigroups + ctx["object_list"] = wikigroups return ctx From 93792f5f460f2ec87e30d126ba9e29ac1cea1693 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 19:17:06 +0200 Subject: [PATCH 09/14] =?UTF-8?q?On=20=C3=A9vite=20les=20erreurs=20si=20pa?= =?UTF-8?q?s=20la=20bonne=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wiki_groups/views.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/wiki_groups/views.py b/wiki_groups/views.py index afda14f..e2055f1 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -91,8 +91,10 @@ class RemoveUserView(WikiGroupMixin, RedirectView): return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) def get(self, request, *args, **kwargs): - user = User.objects.get(pk=kwargs["user_pk"]) - self.object.users.remove(user) + user = User.objects.filter(pk=kwargs["user_pk"]).first() + + if user is not None: + self.object.users.remove(user) return super().get(request, *args, **kwargs) @@ -102,7 +104,11 @@ class RemoveManagerView(WikiGroupMixin, RedirectView): return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) def get(self, request, *args, **kwargs): - user = User.objects.get(pk=kwargs["user_pk"]) + user = User.objects.filter(pk=kwargs["user_pk"]).first() + + if user is not None: + self.object.users.remove(user) + self.object.managers.remove(user) return super().get(request, *args, **kwargs) @@ -113,8 +119,10 @@ class RemoveGroupView(WikiGroupMixin, RedirectView): return reverse("wiki_groups:admin-group", args=[kwargs["pk"]]) def get(self, request, *args, **kwargs): - group = WikiGroup.objects.get(pk=kwargs["group_pk"]) - self.object.includes_groups.remove(group) + group = WikiGroup.objects.filter(pk=kwargs["group_pk"]).first() + + if group is not None: + self.object.includes_groups.remove(group) return super().get(request, *args, **kwargs) From e8830540eccab402950e208c18f227e3e43512d3 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 19:20:29 +0200 Subject: [PATCH 10/14] Meilleur message d'erreur --- wiki_groups/views.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wiki_groups/views.py b/wiki_groups/views.py index e2055f1..b43e92f 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -174,7 +174,13 @@ class AddGroupView(WikiGroupMixin, BaseFormView): group = self.object if group.group_in_cycle(list(group.includes_groups.all()) + [subgroup]): - form.add_error("group", "Ajout impossible sous peine de créer un cycle") + form.add_error( + "group", + ( + "Ajout impossible sous peine de créer un cycle " + f"({group} est inclus dans {subgroup})" + ), + ) return self.form_invalid(form) group.includes_groups.add(subgroup) From 66cb88721f27288e204fef82715f727919180332 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 19:29:30 +0200 Subject: [PATCH 11/14] Docstring --- wiki_groups/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wiki_groups/views.py b/wiki_groups/views.py index b43e92f..f580963 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -164,6 +164,8 @@ class AddManagerView(WikiGroupMixin, BaseFormView): class AddGroupView(WikiGroupMixin, BaseFormView): + """Adds an existing metagroup to this group""" + form_class = SelectGroupForm def get_success_url(self): @@ -194,6 +196,8 @@ class AddGroupView(WikiGroupMixin, BaseFormView): class CreateGroupView(WikiGroupMixin, BaseFormView): + """Creates a metagroup included in this group""" + form_class = CreateGroupForm def get_success_url(self): From c771f1ad490641dcae3dc037700acf9cdd1c5be7 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 19:38:00 +0200 Subject: [PATCH 12/14] Placeholder --- wiki_groups/templates/wiki_groups/admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki_groups/templates/wiki_groups/admin.html b/wiki_groups/templates/wiki_groups/admin.html index 85ec40f..590fad7 100644 --- a/wiki_groups/templates/wiki_groups/admin.html +++ b/wiki_groups/templates/wiki_groups/admin.html @@ -51,7 +51,7 @@
{% csrf_token %}
- +
From cc50d540c34e447bbf315d454eea5ad0c961f352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20P=C3=A9pin?= Date: Wed, 6 Jan 2021 20:17:25 +0100 Subject: [PATCH 13/14] Display the list of groups at /_groups/ --- .../templates/wiki_groups/{graph.html => list.html} | 10 ++++++++++ wiki_groups/urls.py | 2 +- wiki_groups/views.py | 10 +++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) rename wiki_groups/templates/wiki_groups/{graph.html => list.html} (87%) diff --git a/wiki_groups/templates/wiki_groups/graph.html b/wiki_groups/templates/wiki_groups/list.html similarity index 87% rename from wiki_groups/templates/wiki_groups/graph.html rename to wiki_groups/templates/wiki_groups/list.html index f2cfea7..b20c4e8 100644 --- a/wiki_groups/templates/wiki_groups/graph.html +++ b/wiki_groups/templates/wiki_groups/list.html @@ -4,6 +4,16 @@ {% block wiki_site_title %}Groupes - WikiENS{% endblock %} {% block wiki_contents %} +

Liste des groupes du wiki

+ +
+ +
    + {% for id, name in groups %} +
  • {{name}}
  • + {% endfor %} +
+

Graphe des groupes du wiki


diff --git a/wiki_groups/urls.py b/wiki_groups/urls.py index b9c4f44..8414869 100644 --- a/wiki_groups/urls.py +++ b/wiki_groups/urls.py @@ -4,8 +4,8 @@ from wiki_groups import views app_name = "wiki_groups" urlpatterns = [ + path("", views.GroupList.as_view(), name="list"), path("dot_graph", views.dot_graph, name="dot_graph"), - path("graph", views.graph, name="graph"), path("managed", views.ManagedGroupsView.as_view(), name="managed-groups"), path("", views.WikiGroupView.as_view(), name="admin-group"), path( diff --git a/wiki_groups/views.py b/wiki_groups/views.py index f580963..bba5db7 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -30,7 +30,15 @@ def dot_graph(request): return response -graph = TemplateView.as_view(template_name="wiki_groups/graph.html") +class GroupList(ListView): + template_name = "wiki_groups/list.html" + model = WikiGroup + context_object_name = "groups" + + def get_queryset(self): + return WikiGroup.objects.values_list("id", "django_group__name").order_by( + "django_group__name" + ) class WikiGroupMixin(SingleObjectMixin, UserPassesTestMixin): From 29c5abbc1af6e433f4345da51f22c4b277f601d0 Mon Sep 17 00:00:00 2001 From: Tom Hubrecht Date: Tue, 26 Oct 2021 20:01:58 +0200 Subject: [PATCH 14/14] On affiche la liste des managers --- wiki_groups/templates/wiki_groups/list.html | 67 +++++++++++---------- wiki_groups/views.py | 5 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/wiki_groups/templates/wiki_groups/list.html b/wiki_groups/templates/wiki_groups/list.html index b20c4e8..ecf293e 100644 --- a/wiki_groups/templates/wiki_groups/list.html +++ b/wiki_groups/templates/wiki_groups/list.html @@ -4,44 +4,49 @@ {% block wiki_site_title %}Groupes - WikiENS{% endblock %} {% block wiki_contents %} -

Liste des groupes du wiki

+

+ Liste des groupes du wiki + {% if request.user.is_staff or request.user.managed_groups.exists %} + + Liste des groupes gérés + + {% endif %} +

+
-
+
    + {% for group in wikigroup_list %} +
  • {{ group.django_group.name }} {% if group.managers.exists %}(Géré par {% for m in group.managers.all %}{{ m }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}
  • + {% endfor %} +
-
    - {% for id, name in groups %} -
  • {{name}}
  • - {% endfor %} -
+

Graphe des groupes du wiki

+
-

Graphe des groupes du wiki

+
+
-
+

+ Les flèches représentent l'inclusion : A -> B signifie que + le groupe A est contenu dans le groupe B. +

-
+ + + - - + } + }; + xhttp.open('GET', '{% url 'wiki_groups:dot_graph' %}', true); + xhttp.send(); + + {% endblock %} diff --git a/wiki_groups/views.py b/wiki_groups/views.py index bba5db7..db751b9 100644 --- a/wiki_groups/views.py +++ b/wiki_groups/views.py @@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse, reverse_lazy -from django.views.generic import DetailView, ListView, RedirectView, TemplateView +from django.views.generic import DetailView, ListView, RedirectView from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import BaseFormView @@ -33,10 +33,9 @@ def dot_graph(request): class GroupList(ListView): template_name = "wiki_groups/list.html" model = WikiGroup - context_object_name = "groups" def get_queryset(self): - return WikiGroup.objects.values_list("id", "django_group__name").order_by( + return WikiGroup.objects.select_related("django_group").order_by( "django_group__name" )