Skip to content

Commit 2a910b4

Browse files
author
Alessio Fabiani
committed
Merge pull request #5287 from GeoNode/GNIP_71
[Fixes #5274] GNIP 71 - Layer Set Permissions Admin Dashboard (cherry picked from commit 16ffcdf)
1 parent 175e4fd commit 2a910b4

File tree

12 files changed

+205
-5
lines changed

12 files changed

+205
-5
lines changed
Loading
Loading

docs/admin/admin_panel/index.rst

+10
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,16 @@ By clicking over one Layer link, it will show a detail page allowing you to modi
693693

694694
.. note:: It is strongly recommended to always use the GeoNode :guilabel:`Metadata Wizard` or :guilabel:`Metadata Advanced` tools in order to edit the metadata info.
695695

696+
The ``Permissions`` can be changed also for multiple Layers at once throguh the :guilabel:`Set layers permissions` action.
697+
698+
.. figure:: img/set_layers_permissions_action.png
699+
:align: center
700+
701+
By clicking over one Layer link, it will show a detail page allowing you to modify the permissions for the selected resources.
702+
703+
.. figure:: img/set_layers_permissions_form.png
704+
:align: center
705+
696706
Manage the maps using the admin panel
697707
=====================================
698708

geonode/base/admin.py

+10
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ def metadata_batch_edit(modeladmin, request, queryset):
6262
metadata_batch_edit.short_description = 'Metadata batch edit'
6363

6464

65+
def set_batch_permissions(modeladmin, request, queryset):
66+
ids = ','.join([str(element.pk) for element in queryset])
67+
resource = queryset[0].class_name.lower()
68+
return HttpResponseRedirect(
69+
'/{}s/permissions/batch/{}/'.format(resource, ids))
70+
71+
72+
set_batch_permissions.short_description = 'Set permissions'
73+
74+
6575
class MediaTranslationAdmin(TranslationAdmin):
6676
class Media:
6777
js = (

geonode/base/forms.py

+26
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,32 @@ class BatchEditForm(forms.Form):
525525
keywords = forms.CharField(required=False)
526526

527527

528+
class BatchPermissionsForm(forms.Form):
529+
group = forms.ModelChoiceField(
530+
queryset=Group.objects.all(),
531+
required=False)
532+
user = forms.ModelChoiceField(
533+
queryset=get_user_model().objects.all(),
534+
required=False)
535+
permission_type = forms.MultipleChoiceField(
536+
required=True,
537+
widget=forms.CheckboxSelectMultiple,
538+
choices=(
539+
('r', 'Read'),
540+
('w', 'Write'),
541+
('d', 'Download'),
542+
),
543+
)
544+
mode = forms.ChoiceField(
545+
required=True,
546+
widget=forms.RadioSelect,
547+
choices=(
548+
('set', 'Set'),
549+
('unset', 'Unset'),
550+
),
551+
)
552+
553+
528554
class CuratedThumbnailForm(ModelForm):
529555

530556
class Meta:

geonode/base/templates/base/batch_edit.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ <h2 class="page-title">{% trans "Batch Edit" %}</h2>
1515
{% csrf_token %}
1616
{{ form|as_bootstrap }}
1717
<div>
18-
<a href="" class="btn btn-default">{% trans "Cancel" %}</a>
18+
<input class="btn" type="submit" name="cancel" value="{% trans "Cancel" %}" />
1919
<input class="btn btn-primary" type="submit" value="{% trans "Submit" %}" />
2020
</div>
2121
</form>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{% extends "geonode_base.html" %}
2+
{% load i18n %}
3+
{% load staticfiles %}
4+
{% load bootstrap_tags %}
5+
6+
{% block title %} {% trans "Set Permissions" %} - {{ block.super }} {% endblock %}
7+
8+
{% block body_class %}{% trans "set permissions" %}{% endblock %}
9+
10+
{% block body %}
11+
<div class="page-header">
12+
<h2 class="page-title">{% trans "Set Permissions" %}</h2>
13+
</div>
14+
<form action="/{{ model|lower }}s/permissions/batch/{{ ids }}/" method="post">
15+
{% csrf_token %}
16+
{{ form|as_bootstrap }}
17+
<div>
18+
<input class="btn" type="submit" name="cancel" value="{% trans "Cancel" %}" />
19+
<input class="btn btn-primary" type="submit" value="{% trans "Submit" %}" />
20+
</div>
21+
</form>
22+
{% endblock %}

geonode/base/views.py

+7
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ def batch_modify(request, ids, model):
4444
if model == 'Map':
4545
Resource = Map
4646
template = 'base/batch_edit.html'
47+
48+
if "cancel" in request.POST:
49+
return HttpResponseRedirect(
50+
'/admin/{model}s/{model}/'.format(model=model.lower())
51+
)
52+
4753
if request.method == 'POST':
4854
form = BatchEditForm(request.POST)
4955
if form.is_valid():
@@ -75,6 +81,7 @@ def batch_modify(request, ids, model):
7581
'model': model,
7682
}
7783
)
84+
7885
form = BatchEditForm()
7986
return render(
8087
request,

geonode/layers/admin.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from django.contrib import admin
2222

2323
from geonode.base.admin import MediaTranslationAdmin, ResourceBaseAdminForm
24-
from geonode.base.admin import metadata_batch_edit
24+
from geonode.base.admin import metadata_batch_edit, set_batch_permissions
2525
from geonode.layers.models import Layer, Attribute, Style
2626
from geonode.layers.models import LayerFile, UploadSession
2727

@@ -60,7 +60,7 @@ class LayerAdmin(MediaTranslationAdmin):
6060
readonly_fields = ('uuid', 'alternate', 'workspace')
6161
inlines = [AttributeInline]
6262
form = LayerAdminForm
63-
actions = [metadata_batch_edit]
63+
actions = [metadata_batch_edit, set_batch_permissions]
6464

6565

6666
class AttributeAdmin(admin.ModelAdmin):

geonode/layers/tests.py

+47
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,9 @@ def test_assign_change_layer_data_perm(self):
959959
self.assertIn('change_layer_data', perms['users'][user])
960960

961961
def test_batch_edit(self):
962+
"""
963+
Test batch editing of metadata fields.
964+
"""
962965
Model = Layer
963966
view = 'layer_batch_metadata'
964967
resources = Model.objects.all()[:3]
@@ -1044,6 +1047,50 @@ def test_batch_edit(self):
10441047
for word in resource.keywords.all():
10451048
self.assertTrue(word.name in keywords.split(','))
10461049

1050+
def test_batch_permissions(self):
1051+
"""
1052+
Test batch editing of test_batch_permissions.
1053+
"""
1054+
Model = Layer
1055+
view = 'layer_batch_permissions'
1056+
resources = Model.objects.all()[:3]
1057+
ids = ','.join([str(element.pk) for element in resources])
1058+
# test non-admin access
1059+
self.client.login(username="bobby", password="bob")
1060+
response = self.client.get(reverse(view, args=(ids,)))
1061+
self.assertTrue(response.status_code in (401, 403))
1062+
# test group permissions
1063+
group = Group.objects.first()
1064+
self.client.login(username='admin', password='admin')
1065+
response = self.client.post(
1066+
reverse(view, args=(ids,)),
1067+
data={
1068+
'group': group.pk,
1069+
'permission_type': ('r', ),
1070+
'mode': 'set'
1071+
},
1072+
)
1073+
self.assertEquals(response.status_code, 302)
1074+
resources = Model.objects.filter(id__in=[r.pk for r in resources])
1075+
for resource in resources:
1076+
perm_spec = resource.get_all_level_info()
1077+
self.assertTrue(group in perm_spec["groups"])
1078+
# test user permissions
1079+
user = get_user_model().objects.first()
1080+
response = self.client.post(
1081+
reverse(view, args=(ids,)),
1082+
data={
1083+
'user': user.pk,
1084+
'permission_type': ('r', ),
1085+
'mode': 'set'
1086+
},
1087+
)
1088+
self.assertEquals(response.status_code, 302)
1089+
resources = Model.objects.filter(id__in=[r.pk for r in resources])
1090+
for resource in resources:
1091+
perm_spec = resource.get_all_level_info()
1092+
self.assertTrue(user in perm_spec["users"])
1093+
10471094

10481095
class UnpublishedObjectTests(GeoNodeBaseTestSupport):
10491096

geonode/layers/urls.py

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@
6969
views.layer_feature_catalogue, name='layer_feature_catalogue'),
7070
url(r'^metadata/batch/(?P<ids>[^/]*)/$',
7171
views.layer_batch_metadata, name='layer_batch_metadata'),
72+
url(r'^permissions/batch/(?P<ids>[^/]*)/$',
73+
views.layer_batch_permissions, name='layer_batch_permissions'),
7274
]
7375

7476
# -- Deprecated url routes for Geoserver authentication -- remove after GeoNode 2.1

geonode/layers/views.py

+78-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from celery.exceptions import TimeoutError
3232

3333
from django.contrib.gis.geos import GEOSGeometry
34+
from django.core.exceptions import PermissionDenied
3435
from django.template.response import TemplateResponse
3536
from requests import Request
3637
from itertools import chain
@@ -63,7 +64,7 @@
6364
from django.forms.utils import ErrorList
6465

6566
from geonode.base.auth import get_or_create_token
66-
from geonode.base.forms import CategoryForm, TKeywordForm
67+
from geonode.base.forms import CategoryForm, TKeywordForm, BatchPermissionsForm
6768
from geonode.base.views import batch_modify
6869
from geonode.base.models import (
6970
Thesaurus,
@@ -82,7 +83,8 @@
8283
from geonode.layers.utils import (
8384
file_upload,
8485
is_raster,
85-
is_vector)
86+
is_vector,
87+
set_layers_permissions)
8688

8789
from geonode.maps.models import Map
8890
from geonode.services.models import Service
@@ -1611,6 +1613,80 @@ def layer_batch_metadata(request, ids):
16111613
return batch_modify(request, ids, 'Layer')
16121614

16131615

1616+
def batch_permissions(request, ids, model):
1617+
Resource = None
1618+
if model == 'Layer':
1619+
Resource = Layer
1620+
if not Resource or not request.user.is_superuser:
1621+
raise PermissionDenied
1622+
1623+
template = 'base/batch_permissions.html'
1624+
1625+
if "cancel" in request.POST:
1626+
return HttpResponseRedirect(
1627+
'/admin/{model}s/{model}/'.format(model=model.lower())
1628+
)
1629+
1630+
if request.method == 'POST':
1631+
form = BatchPermissionsForm(request.POST)
1632+
if form.is_valid():
1633+
_data = form.cleaned_data
1634+
resources_names = []
1635+
for resource in Resource.objects.filter(id__in=ids.split(',')):
1636+
resources_names.append(resource.name)
1637+
users_usernames = [_data['user'].username, ] if _data['user'] else None
1638+
groups_names = [_data['group'].name, ] if _data['group'] else None
1639+
if users_usernames and 'AnonymousUser' in users_usernames and \
1640+
(not groups_names or 'anonymous' not in groups_names):
1641+
if not groups_names:
1642+
groups_names = []
1643+
groups_names.append('anonymous')
1644+
if groups_names and 'anonymous' in groups_names and \
1645+
(not users_usernames or 'AnonymousUser' not in users_usernames):
1646+
if not users_usernames:
1647+
users_usernames = []
1648+
users_usernames.append('AnonymousUser')
1649+
delete_flag = _data['mode'] == 'unset'
1650+
permissions_names = _data['permission_type']
1651+
if permissions_names:
1652+
for permissions_name in permissions_names:
1653+
set_layers_permissions(
1654+
permissions_name, resources_names, users_usernames, groups_names, delete_flag
1655+
)
1656+
return HttpResponseRedirect(
1657+
'/admin/{model}s/{model}/'.format(model=model.lower())
1658+
)
1659+
return render(
1660+
request,
1661+
template,
1662+
context={
1663+
'form': form,
1664+
'ids': ids,
1665+
'model': model,
1666+
}
1667+
)
1668+
1669+
form = BatchPermissionsForm(
1670+
{
1671+
'permission_type': ('r', ),
1672+
'mode': 'set'
1673+
})
1674+
return render(
1675+
request,
1676+
template,
1677+
context={
1678+
'form': form,
1679+
'ids': ids,
1680+
'model': model,
1681+
}
1682+
)
1683+
1684+
1685+
@login_required
1686+
def layer_batch_permissions(request, ids):
1687+
return batch_permissions(request, ids, 'Layer')
1688+
1689+
16141690
def layer_view_counter(layer_id, viewer):
16151691
_l = Layer.objects.get(id=layer_id)
16161692
_u = get_user_model().objects.get(username=viewer)

0 commit comments

Comments
 (0)