mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
wip: add very basic CRUD for groups
This commit is contained in:
parent
a3e972bf5d
commit
9b2a99019b
11 changed files with 199 additions and 18 deletions
|
@ -1,6 +1,7 @@
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
@ -60,3 +61,14 @@ def can_view_map(view_func):
|
||||||
return view_func(request, *args, **kwargs)
|
return view_func(request, *args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def group_members_only(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
group = get_object_or_404(Group, pk=kwargs["pk"])
|
||||||
|
if group not in request.user.groups.all():
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
return view_func(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.gis.geos import Point
|
from django.contrib.gis.geos import Point
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
|
@ -110,3 +111,13 @@ class UserProfileForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ("username", "first_name", "last_name")
|
fields = ("username", "first_name", "last_name")
|
||||||
|
|
||||||
|
|
||||||
|
class GroupForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Group
|
||||||
|
fields = ["name", "members"]
|
||||||
|
|
||||||
|
members = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=User.objects.all(), widget=forms.CheckboxSelectMultiple
|
||||||
|
)
|
||||||
|
|
19
umap/templates/auth/group_confirm_delete.html
Normal file
19
umap/templates/auth/group_confirm_delete.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
{% include "umap/dashboard_menu.html" with selected="groups" %}
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="row">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
Are you sure you want to delete "{{ object }}"?
|
||||||
|
</p>
|
||||||
|
{{ form }}
|
||||||
|
<input type="submit" value="Confirm">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock maincontent %}
|
22
umap/templates/auth/group_detail.html
Normal file
22
umap/templates/auth/group_detail.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
<div class="col wide">
|
||||||
|
<h2 class="section">
|
||||||
|
{% blocktrans %}Browse {{ current_group }}'s maps{% endblocktrans %}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="map_list row">
|
||||||
|
{% if maps %}
|
||||||
|
{% include "umap/map_list.html" %}
|
||||||
|
{% else %}
|
||||||
|
<div>
|
||||||
|
{% blocktrans %}{{ current_group }} has no public maps.{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock maincontent %}
|
28
umap/templates/auth/group_form.html
Normal file
28
umap/templates/auth/group_form.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
{% include "umap/dashboard_menu.html" with selected="groups" %}
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="row">
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<ul class="form-errors">
|
||||||
|
{% for error in form.non_field_errors %}
|
||||||
|
<li>
|
||||||
|
{{ error }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<form id="group_form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<input type="submit" value="{% trans 'Save' %}" />
|
||||||
|
</form>
|
||||||
|
{% if group.user_set.count == 1 %}
|
||||||
|
<a href="{% url 'group_delete' group.pk %}">{% trans "Delete this group" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock maincontent %}
|
|
@ -3,12 +3,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block maincontent %}
|
{% block maincontent %}
|
||||||
<div class="row">
|
{% include "umap/dashboard_menu.html" with selected="profile" %}
|
||||||
<h2 class="section tabs">
|
|
||||||
<a href="{% url "user_dashboard" %}">{% trans "My Maps" %}</a>
|
|
||||||
<a class="selected" href="{% url 'user_profile' %}">{% trans "My Profile" %}</a>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if form.non_field_errors %}
|
{% if form.non_field_errors %}
|
||||||
|
|
15
umap/templates/umap/dashboard_menu.html
Normal file
15
umap/templates/umap/dashboard_menu.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="section tabs">
|
||||||
|
{% if selected == "maps" %}
|
||||||
|
<a class="selected" href="{% url 'user_dashboard' %}">{% blocktranslate with count=maps.paginator.count %}My Maps ({{ count }}){% endblocktranslate %}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'user_dashboard' %}">{% trans "My Maps" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
<a {% if selected == "profile" %}class="selected"{% endif %}
|
||||||
|
href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
|
||||||
|
<a {% if selected == "groups" %}class="selected"{% endif %}
|
||||||
|
href="{% url 'user_groups' %}">{% trans "My teams" %}</a>
|
||||||
|
</h2>
|
||||||
|
</div>
|
|
@ -7,13 +7,7 @@
|
||||||
{% endblock head_title %}
|
{% endblock head_title %}
|
||||||
{% block maincontent %}
|
{% block maincontent %}
|
||||||
{% trans "Search my maps" as placeholder %}
|
{% trans "Search my maps" as placeholder %}
|
||||||
<div class="row">
|
{% include "umap/dashboard_menu.html" with selected="maps" %}
|
||||||
<h2 class="section tabs">
|
|
||||||
<a class="selected" href="{% url 'user_dashboard' %}">{% blocktranslate with count=maps.paginator.count %}My Maps ({{ count }}){% endblocktranslate %}
|
|
||||||
</a>
|
|
||||||
<a href="{% url 'user_profile' %}">{% trans "My profile" %}</a>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="table-header">
|
<div class="table-header">
|
||||||
|
|
19
umap/templates/umap/user_groups.html
Normal file
19
umap/templates/umap/user_groups.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends "umap/content.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block maincontent %}
|
||||||
|
{% include "umap/dashboard_menu.html" with selected="groups" %}
|
||||||
|
<div class="wrapper">
|
||||||
|
<div class="row">
|
||||||
|
<ul>
|
||||||
|
{% for group in groups %}
|
||||||
|
<li>
|
||||||
|
{{ group }} <a href="{% url 'group_update' group.pk %}">({% trans "Edit" %})</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<a href="{% url 'group_new' %}">{% trans "New team" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
18
umap/urls.py
18
umap/urls.py
|
@ -15,6 +15,7 @@ from . import views
|
||||||
from .decorators import (
|
from .decorators import (
|
||||||
can_edit_map,
|
can_edit_map,
|
||||||
can_view_map,
|
can_view_map,
|
||||||
|
group_members_only,
|
||||||
login_required_if_not_anonymous_allowed,
|
login_required_if_not_anonymous_allowed,
|
||||||
)
|
)
|
||||||
from .utils import decorated_patterns
|
from .utils import decorated_patterns
|
||||||
|
@ -96,8 +97,8 @@ i18n_urls += decorated_patterns(
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
[ensure_csrf_cookie],
|
[ensure_csrf_cookie],
|
||||||
re_path(r"^map/$", views.MapPreview.as_view(), name="map_preview"),
|
path("map/", views.MapPreview.as_view(), name="map_preview"),
|
||||||
re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"),
|
path("map/new/", views.MapNew.as_view(), name="map_new"),
|
||||||
)
|
)
|
||||||
i18n_urls += decorated_patterns(
|
i18n_urls += decorated_patterns(
|
||||||
[login_required_if_not_anonymous_allowed, never_cache],
|
[login_required_if_not_anonymous_allowed, never_cache],
|
||||||
|
@ -110,9 +111,16 @@ i18n_urls += decorated_patterns(
|
||||||
views.ToggleMapStarStatus.as_view(),
|
views.ToggleMapStarStatus.as_view(),
|
||||||
name="map_star",
|
name="map_star",
|
||||||
),
|
),
|
||||||
re_path(r"^me$", views.user_dashboard, name="user_dashboard"),
|
path("me", views.user_dashboard, name="user_dashboard"),
|
||||||
re_path(r"^me/profile$", views.user_profile, name="user_profile"),
|
path("me/profile", views.user_profile, name="user_profile"),
|
||||||
re_path(r"^me/download$", views.user_download, name="user_download"),
|
path("me/download", views.user_download, name="user_download"),
|
||||||
|
path("me/groups", views.UserGroups.as_view(), name="user_groups"),
|
||||||
|
path("group/create/", views.GroupNew.as_view(), name="group_new"),
|
||||||
|
)
|
||||||
|
i18n_urls += decorated_patterns(
|
||||||
|
[login_required, group_members_only],
|
||||||
|
path("group/<int:pk>/edit/", views.GroupUpdate.as_view(), name="group_update"),
|
||||||
|
path("group/<int:pk>/delete/", views.GroupDelete.as_view(), name="group_delete"),
|
||||||
)
|
)
|
||||||
map_urls = [
|
map_urls = [
|
||||||
re_path(
|
re_path(
|
||||||
|
|
|
@ -61,6 +61,7 @@ from .forms import (
|
||||||
DataLayerForm,
|
DataLayerForm,
|
||||||
DataLayerPermissionsForm,
|
DataLayerPermissionsForm,
|
||||||
FlatErrorList,
|
FlatErrorList,
|
||||||
|
GroupForm,
|
||||||
MapSettingsForm,
|
MapSettingsForm,
|
||||||
SendLinkForm,
|
SendLinkForm,
|
||||||
UpdateMapPermissionsForm,
|
UpdateMapPermissionsForm,
|
||||||
|
@ -189,6 +190,63 @@ class About(Home):
|
||||||
about = About.as_view()
|
about = About.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class GroupNew(CreateView):
|
||||||
|
model = Group
|
||||||
|
fields = ["name"]
|
||||||
|
success_url = reverse_lazy("user_groups")
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
response = super().form_valid(form)
|
||||||
|
self.request.user.groups.add(self.object)
|
||||||
|
self.request.user.save()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class GroupUpdate(UpdateView):
|
||||||
|
model = Group
|
||||||
|
form_class = GroupForm
|
||||||
|
success_url = reverse_lazy("user_groups")
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initial = super().get_initial()
|
||||||
|
initial["members"] = self.object.user_set.all()
|
||||||
|
return initial
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
for user in form.cleaned_data["members"]:
|
||||||
|
user.groups.add(self.object)
|
||||||
|
user.save()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class GroupDelete(DeleteView):
|
||||||
|
model = Group
|
||||||
|
success_url = reverse_lazy("user_groups")
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
if self.object.user_set.count() > 1:
|
||||||
|
return HttpResponseBadRequest(
|
||||||
|
_("Cannot delete a group with more than one member")
|
||||||
|
)
|
||||||
|
messages.info(
|
||||||
|
self.request,
|
||||||
|
_("Group “%(name)s” has been deleted") % {"name": self.object.name},
|
||||||
|
)
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class UserGroups(DetailView):
|
||||||
|
model = User
|
||||||
|
template_name = "umap/user_groups.html"
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return self.get_queryset().get(pk=self.request.user.pk)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs.update({"groups": self.object.groups.all()})
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(UpdateView):
|
class UserProfile(UpdateView):
|
||||||
model = User
|
model = User
|
||||||
form_class = UserProfileForm
|
form_class = UserProfileForm
|
||||||
|
|
Loading…
Reference in a new issue