Skip to content

Commit f554424

Browse files
Alessio Fabianit-bookpetrus7gannebamm
authored
[Issue GeoNode#6684] GNIP-78: GeoNode generic "Apps" model to include pluggable entities into the framework (GeoNode#6713)
* [Hardening] - Recenet Activity List for Documents error when actor is None * [Frontend] Monitoring: Bump "node-sass" to version 4.14.1 * [Frontend] Bump jquery to version 3.5.1 * [Fixes: GeoNode#6519] Bump jquery to 3.5.1 (GeoNode#6526) (cherry picked from commit e532813) # Conflicts: # geonode/static/lib/css/assets.min.css # geonode/static/lib/css/bootstrap-select.css # geonode/static/lib/css/bootstrap-table.css # geonode/static/lib/js/assets.min.js # geonode/static/lib/js/bootstrap-select.js # geonode/static/lib/js/bootstrap-table.js # geonode/static/lib/js/leaflet-plugins.min.js # geonode/static/lib/js/leaflet.js # geonode/static/lib/js/moment-timezone-with-data.js # geonode/static/lib/js/underscore.js * Merge branch 'master' of https://github.com/GeoNode/geonode into rest_api_v2_proof_of_concept # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit. * [Hardening] Re-create the map thumbnail only if it is missing * Fixes error with GDAL 3.0.4 due to a breaking change on GDAL (https://code.djangoproject.com/ticket/30645) * Fixes error with GDAL 3.0.4 due to a breaking change on GDAL (https://code.djangoproject.com/ticket/30645) * - Introducing the concept of "GeoNode App" Resource Base * [GeoApps] Add "Create new" Button to the apps list page * [GeoApps] Hooking Resources List pages * [GeoApps] Hooking GeoApp List page * [GeoApps] Hooking GeoApp rest v2 API serializers fixes * [GeoApps] Fix resourcebase_api polymorphic ctype filter * [GeoApps] REST API v2 "geostories" endpoints optimizations * [REST APIs V2] Make use of the new "bbox_polygon" field based on GeoDjango * [Fixes RemoteServices bbox parse] Merge branch 'search-by-extent' of https://github.com/mtnorthcott/geonode * [Fix migrations] Merge branch 'search-by-extent' of https://github.com/mtnorthcott/geonode * [Fix migrations] Merge branch 'search-by-extent' of https://github.com/mtnorthcott/geonode * [GeoApps] Adding "geoapp_edit" page * [GeoApps] Adding "geoapp_edit" page context * [GeoApps] Adding security info (access_token, user, ...) to the page context * [GeoApps] Adding client endpoints * [GeoApps] Missing "post_save" signal * [GeoApps] Finalize GeoApp resources management * Fix "bbox_to_projection" coords order * Fix 'bbox_to_projection' coords order * Fix "bbox_to_projection" coords order (cherry picked from commit 72d6c1e) * Fix "bbox_to_projection" coords order: check GDAL version >= 3.0.4 * Include missing 'mapstore2_adapter.geoapps' app to default INSTALLED_APPS * Include mapstore client branch dependencies into requirements * Revert security commit on branch * Minor review of the current advanced resource workflow implementation GeoNode#6551 * Minor review of the current advanced resource workflow implementation GeoNode#6551 * Fix tests on Travis * Fix tests on Travis * Fix tests on Travis * Fix tests on Travis * Fix tests on Travis (cherry picked from commit c7f651c) # Conflicts: # geonode/layers/tests.py * Fix logical errors on approval workflow * Fix logical errors on approval workflow (cherry picked from commit 7a3d5d0) * Fix tests on Travis * Cleanup "app_embed" template * Advanced workflow: remove change_permissions to the owner if not a manager * Advanced workflow: remove change_permissions to the owner if not a manager (cherry picked from commit 9a1552a) * Fix app_embed template * Advanced workflow: remove change_permissions to the owner if not a manager * Advanced workflow: remove change_permissions to the owner if not a manager * Advanced workflow: remove change_permissions to the owner if not a manager (cherry picked from commit f23096c) * Advanced workflow: remove change_permissions to the owner if not a manager (cherry picked from commit bfe51a7) * Advanced workflow: remove change_permissions to the owner if not a manager * Advanced workflow: remove change_permissions to the owner if not a manager (cherry picked from commit d9ec566) * Advanced workflow: filter actions stream returned to the users accordingly to their perms * Advanced workflow: filter actions stream returned to the users accordingly to their perms (cherry picked from commit 7f51346) * Advanced workflow: filter actions stream returned to the users accordingly to their perms * Add new settings from django-allauth 0.43.0 * Advanced workflow: filter actions stream returned to the users accordingly to their perms (cherry picked from commit e2522fd) * Add new settings from django-allauth 0.43.0 (cherry picked from commit 00f4be1) * Code styling alerts: remove unnecessary pass * Refreshing static libs * Refreshing static libs * Code styling alerts: remove unnecessary pass (cherry picked from commit 0676f6e) * Refreshing static libs (cherry picked from commit f27d0df) * Refreshing static libs (cherry picked from commit 5b166bc) * Advanced Workflow: Make sure the APIs counters are coherent with the visible resources * Advanced Workflow: Make sure the APIs counters are coherent with the visible resources (cherry picked from commit 1855d74) * fix english/italian translations (GeoNode#6563) * fix english/italian translations (GeoNode#6563) * Advanced Workflow: fix "request editing" action when published * Advanced Workflow: fix "request editing" action when published -> send messages to group managers too * Advanced Workflow: fix "request editing" action when published (cherry picked from commit 1041b12) * Advanced Workflow: fix "request editing" action when published -> send messages to group managers too (cherry picked from commit 5c93ef3) * Fix test on travis * fix english/italian translations (GeoNode#6563) * fix english/italian translations (GeoNode#6563) * fix english/italian translations (GeoNode#6563) * Avoid override User settings on "set_attributes_from_geoserver" * - Docs links to 3.x branch * Improve Celery Async Tasks configuration (cherry picked from commit 50e208a) * Improve Celery Async Tasks configuration (cherry picked from commit d5150e8) * Improve Celery Async Tasks configuration (cherry picked from commit 50e208a) * - Replace build.geo-solutions.it with www.dropbox.com (cherry picked from commit 882e3e5) (cherry picked from commit 7b970f8) * [Security] Hardening Advanced Workflow resources visibility (cherry picked from commit 2103f13) (cherry picked from commit 025c82e) * [Hardening] Removing redundant and replacement of instance abstract from GeoServer * Bump drf-yasg from 1.17.1 to 1.20.0 * [Hardening] Fixes: db connection closed and worker hangs with celery 4.2+ celery/celery#4878 * [Hardening] Optimizing celery tasks settings * [Hardening] Optimizing celery tasks settings * - Documents REST v2 APIs * [Fixes GeoNode#6596] Incorrect Legend displayed in the layer detail page (cherry picked from commit 0aa6902) * [Fixes GeoNode#6596] Incorrect Legend displayed in the layer detail page (cherry picked from commit 0aa6902) * - Update travis dist to '20.04 focal' * - Fix geolimits panel translations * - Filter Comments on Recent Activities accordingly to the user's perms * [Hardening] Remove wrong class initializer * [Hardening] LGTM warning fixes * [CI Optimizations] - Continuous integration builders: CircleCI config based on "spcgeonode" docker-compose (cherry picked from commit 7f091a7) * - Enable "memcached" plugins for monitoring * - Extend "documents" to accept and render video, audio and more image formats - Add "attribution" field to ResourceBase model * - Generating documents thumbnails for video and audio mime types (cherry picked from commit d1f4251) (cherry picked from commit 5c89762) * - Merge with master branch * - Generating documents thumbnails for video and audio mime types (cherry picked from commit 197c7ab) * - Fixing doc image thumn generation * - Updating translations * - expose documents 'href' from REST serializer API endpoint * [Hardening] - expose **secured** documents 'href' from REST serializer API endpoint * [Hardening] - generate **secured** thumbnail for uploaded images * - Restore missing list key on GXP_PTYPES enumeration (cherry picked from commit 2352613) * [FIX GeoNode#6626] add tinymce editor to resource text areas (cherry picked from commit 45bb0dc) * [FIX GeoNode#6626] add tinymce editor to resource text areas (cherry picked from commit 45bb0dc) * [Hardening] Correctly manage "_resolve_object" exception as Django error templates (cherry picked from commit 017d885) # Conflicts: # geonode/views.py * - Remove wrong migration * [Hardening] Using "apply_async" instead of "delay" for async signals calls * [Hardening] Avoid exit prematurely from geoserver cascading delete signal * Fix travis tests * [Fixes GeoNode#5779] Data edition permissions set in GeoNode for a layer are not applied on the WFS (cherry picked from commit 9e4e839) * - Cleaning up wrong migrations * [Performance] - Improve Style editing requests callbacks * [Performance] - Transform "geoserver_post_save_layers" to an asynchronous task * [Performance] - Improve Style editing requests callbacks * [Optimization] Improve 'navbar' content reposition script * [Performance] - Transform "geoserver_post_save_layers" to an asynchronous task * [Performance] - Improve Style editing requests callbacks * FIXES[GeoNode#6653] Mail notifications for private datasets are public * - exclude query optimization * [Performance] Dinamically loading the list of users geo-limits (cherry picked from commit c54cc61) (cherry picked from commit 756c1aa) * [Fixes: GeoNode#6640] Style Tag outside of html (GeoNode#6657) * [Minor Layout Issue] - Missing title on "map list" page (cherry picked from commit 971e65f) * added Document Creation Fallback, fixed exclude_user_ids.append() * - Correct "geoapps" notification types * - Fix remaining issues: 1. Layer create does no send "title" before sending notifications - 2. Doc created does not set "permissions" before sending notifications * Typo: _QUEUE_ALL_FLAG * Typo: _QUEUE_ALL_FLAG (cherry picked from commit 8d9118f) * - Fix asynchronous notification engine task * - Fix asynchronous notification engine task (cherry picked from commit 79274eb) * - Do not send notifications if the resource has no title * - Do not send notifications if the resource has no title (cherry picked from commit c3d470e) * - Asynchronous "probe" task for Remote Services * [FIXES GeoNode#6653] Mail notifications for private datasets are public * - Fixes rating notifications * - Fixes rating notifications (cherry picked from commit b814692) * - Fixes "guardian.exceptions.ObjectNotPersisted: Object None needs to be persisted first" exception on "set_workflow_perms" calls * - Fixes "guardian.exceptions.ObjectNotPersisted: Object None needs to be persisted first" exception on "set_workflow_perms" calls (cherry picked from commit fe35d46) * - Fixes "guardian.exceptions.ObjectNotPersisted: Object None needs to be persisted first" exception on "set_workflow_perms" calls * - Fixes "guardian.exceptions.ObjectNotPersisted: Object None needs to be persisted first" exception on "set_workflow_perms" calls (cherry picked from commit dee7de1) * - Fix LGTM issues * - Fix LGTM issues (cherry picked from commit 08644a6) * - Fix LGTM issues * - Fix LGTM issues (cherry picked from commit df112c8) * no notifications for resource owner, except for comments. PEP 8 reformatting * resource owners will get notified on updates of their resources * [Fixes GeoNode#6665] Improve WYSIWYG metadata editor to store formatted and plain texts * - Travis test-cases: "ensure owner won't be notified on upload" * [Hardening] Do not fail in case of datastore with multiple geometries * - Minor refactoring and clean out of the "geoserver_post_save_layers" task body * [Hardening] Make "set_attributes" method more resilient to "Attribute.MultipleObjectsReturned" exception * [Hardening] Make "helpers" methods more resilient to "Layer.MultipleObjectsReturned" and "Layer.DoesNotExist" exceptions * - Minor environmnet params improvements. Exposing DB connection timeouts to .env * - Explicit error codes along with description on Layer Upload form * [Transaltions] - Explicit error codes along with description on Layer Upload form * [Transaltions] - Explicit error codes along with description on Layer Upload form (cherry picked from commit 395089e) # Conflicts: # geonode/static/geonode/js/upload/LayerInfo.js (cherry picked from commit f62b69a) * [Docker] Use local nginx build * Merge branch 'master' of https://github.com/GeoNode/geonode into rest_api_v2_geonode_apps * [Hardening] More resiliet to 'missing thumbnail' on filesystem issues * - GeoApp Test Cases * - Typo * - Update mapstore client and adapter versions * - Set local .sh files exec perms * - Bump pycsw to version 2.6.0 * - Bump pycsw to version 2.6.0 * - Bump pycsw to version 2.6.0 * - Align "setup.cfg" to "requirements.txt" * - Fix travis Co-authored-by: Toni <[email protected]> Co-authored-by: Piotr Dankowski <[email protected]> Co-authored-by: Florian Hoedt <[email protected]>
1 parent 6527b05 commit f554424

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3236
-23
lines changed

geonode/api/api.py

+37-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import json
2222
import time
2323

24+
from django.apps import apps
2425
from django.db.models import Q
2526
from django.conf.urls import url
2627
from django.contrib.auth import get_user_model
@@ -52,6 +53,7 @@
5253
from geonode.base.models import ThesaurusKeywordLabel
5354
from geonode.layers.models import Layer, Style
5455
from geonode.maps.models import Map
56+
from geonode.geoapps.models import GeoApp
5557
from geonode.documents.models import Document
5658
from geonode.groups.models import GroupProfile, GroupCategory
5759
from django.core.serializers.json import DjangoJSONEncoder
@@ -67,7 +69,8 @@
6769
FILTER_TYPES = {
6870
'layer': Layer,
6971
'map': Map,
70-
'document': Document
72+
'document': Document,
73+
'geoapp': GeoApp
7174
}
7275

7376

@@ -90,19 +93,37 @@ def get_resources_counts(self, options):
9093
unpublished_not_visible=settings.RESOURCE_PUBLISHING,
9194
private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES)
9295

96+
subtypes = []
9397
if resources and resources.count() > 0:
9498
if options['title_filter']:
9599
resources = resources.filter(title__icontains=options['title_filter'])
96-
97100
if options['type_filter']:
98101
_type_filter = options['type_filter']
102+
103+
for label, app in apps.app_configs.items():
104+
if hasattr(app, 'type') and app.type == 'GEONODE_APP':
105+
if hasattr(app, 'default_model'):
106+
_model = apps.get_model(label, app.default_model)
107+
if issubclass(_model, _type_filter):
108+
subtypes.append(
109+
resources.filter(
110+
polymorphic_ctype__model=_model.__name__.lower()))
111+
99112
if not isinstance(_type_filter, str):
100113
_type_filter = _type_filter.__name__.lower()
101114
resources = resources.filter(polymorphic_ctype__model=_type_filter)
102115

103-
counts = list(resources.values(options['count_type']).annotate(count=Count(options['count_type'])))
116+
counts = list()
117+
if subtypes:
118+
for subtype in subtypes:
119+
counts.append(
120+
subtype.values(options['count_type']).annotate(count=Count(options['count_type'])).first()
121+
)
122+
else:
123+
counts = list(resources.values(options['count_type']).annotate(count=Count(options['count_type'])))
104124

105-
return dict([(c[options['count_type']], c['count']) for c in counts])
125+
return dict(
126+
[(c[options['count_type']], c['count']) for c in counts if c and c['count'] and options['count_type']])
106127

107128
def to_json(self, data, options=None):
108129
options = options or {}
@@ -869,8 +890,18 @@ def _get_resource_counts(request, resourcebase_filter_kwargs):
869890
'layer',
870891
'document',
871892
'map',
893+
'geoapp',
872894
'all'
873895
]
896+
897+
subtypes = []
898+
for label, app in apps.app_configs.items():
899+
if hasattr(app, 'type') and app.type == 'GEONODE_APP':
900+
if hasattr(app, 'default_model'):
901+
_model = apps.get_model(label, app.default_model)
902+
if issubclass(_model, GeoApp):
903+
types.append(_model.__name__.lower())
904+
subtypes.append(_model.__name__.lower())
874905
counts = {}
875906
for type_ in types:
876907
counts[type_] = {
@@ -881,6 +912,8 @@ def _get_resource_counts(request, resourcebase_filter_kwargs):
881912
}
882913
for record in qs:
883914
resource_type = record['polymorphic_ctype__model']
915+
if resource_type in subtypes:
916+
resource_type = 'geoapp'
884917
is_visible = all((record['is_approved'], record['is_published']))
885918
counts['all']['total'] += record['counts']
886919
counts['all']['visible'] += record['counts'] if is_visible else 0

geonode/api/resourcebase_api.py

+66
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from geonode import get_version, qgis_server, geoserver
5050
from geonode.layers.models import Layer
5151
from geonode.maps.models import Map
52+
from geonode.geoapps.models import GeoApp
5253
from geonode.documents.models import Document
5354
from geonode.base.models import ResourceBase
5455
from geonode.base.models import HierarchicalKeyword
@@ -165,6 +166,8 @@ def build_filters(self, filters=None, ignore_bad_filters=False, **kwargs):
165166
filters=filters, ignore_bad_filters=ignore_bad_filters, **kwargs)
166167
if 'type__in' in filters and filters['type__in'] in FILTER_TYPES.keys():
167168
orm_filters.update({'type': filters.getlist('type__in')})
169+
if 'app_type__in' in filters:
170+
orm_filters.update({'polymorphic_ctype__model': filters['app_type__in'].lower()})
168171
if 'extent' in filters:
169172
orm_filters.update({'extent': filters['extent']})
170173
orm_filters['f_method'] = filters['f_method'] if 'f_method' in filters else 'and'
@@ -1016,6 +1019,69 @@ class Meta(CommonMetaApi):
10161019
GeonodeApiKeyAuthentication())
10171020

10181021

1022+
class GeoAppResource(CommonModelApi):
1023+
1024+
"""GeoApps API"""
1025+
1026+
def format_objects(self, objects):
1027+
"""
1028+
Formats the objects and provides reference to list of layers in GeoApp
1029+
resources.
1030+
1031+
:param objects: GeoApp objects
1032+
"""
1033+
formatted_objects = []
1034+
for obj in objects:
1035+
# convert the object to a dict using the standard values.
1036+
formatted_obj = model_to_dict(obj, fields=self.VALUES)
1037+
username = obj.owner.get_username()
1038+
full_name = (obj.owner.get_full_name() or username)
1039+
formatted_obj['owner__username'] = username
1040+
formatted_obj['owner_name'] = full_name
1041+
if obj.category:
1042+
formatted_obj['category__gn_description'] = obj.category.gn_description
1043+
if obj.group:
1044+
formatted_obj['group'] = obj.group
1045+
try:
1046+
formatted_obj['group_name'] = GroupProfile.objects.get(slug=obj.group.name)
1047+
except GroupProfile.DoesNotExist:
1048+
formatted_obj['group_name'] = obj.group
1049+
1050+
formatted_obj['keywords'] = [k.name for k in obj.keywords.all()] if obj.keywords else []
1051+
formatted_obj['regions'] = [r.name for r in obj.regions.all()] if obj.regions else []
1052+
1053+
if 'site_url' not in formatted_obj or len(formatted_obj['site_url']) == 0:
1054+
formatted_obj['site_url'] = settings.SITEURL
1055+
1056+
# Probe Remote Services
1057+
formatted_obj['store_type'] = 'geoapp'
1058+
formatted_obj['online'] = True
1059+
1060+
# replace thumbnail_url with curated_thumbs
1061+
try:
1062+
if hasattr(obj, 'curatedthumbnail'):
1063+
if hasattr(obj.curatedthumbnail.img_thumbnail, 'url'):
1064+
formatted_obj['thumbnail_url'] = obj.curatedthumbnail.thumbnail_url
1065+
else:
1066+
formatted_obj['thumbnail_url'] = ''
1067+
except Exception as e:
1068+
formatted_obj['thumbnail_url'] = ''
1069+
logger.exception(e)
1070+
1071+
formatted_objects.append(formatted_obj)
1072+
return formatted_objects
1073+
1074+
class Meta(CommonMetaApi):
1075+
paginator_class = CrossSiteXHRPaginator
1076+
filtering = CommonMetaApi.filtering
1077+
filtering.update({'app_type': ALL})
1078+
queryset = GeoApp.objects.distinct().order_by('-date')
1079+
resource_name = 'geoapps'
1080+
authentication = MultiAuthentication(SessionAuthentication(),
1081+
OAuthAuthentication(),
1082+
GeonodeApiKeyAuthentication())
1083+
1084+
10191085
class DocumentResource(CommonModelApi):
10201086

10211087
"""Documents API"""

geonode/api/urls.py

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
api.register(resourcebase_resources.FeaturedResourceBaseResource())
4747
api.register(resourcebase_resources.LayerResource())
4848
api.register(resourcebase_resources.MapResource())
49+
api.register(resourcebase_resources.GeoAppResource())
4950
api.register(resourcebase_resources.ResourceBaseResource())
5051

5152
router = routers.DynamicRouter()

geonode/base/api/serializers.py

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@
4444
logger = logging.getLogger(__name__)
4545

4646

47+
class ResourceBaseTypesSerializer(DynamicEphemeralSerializer):
48+
49+
class Meta:
50+
name = 'resource-type'
51+
52+
resource_types = serializers.ListField()
53+
54+
4755
class PermSpecSerialiazer(DynamicEphemeralSerializer):
4856

4957
class Meta:

geonode/base/api/tests.py

+60-4
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ def test_base_resources(self):
197197
# Pagination
198198
self.assertEqual(len(response.data['resources']), 17)
199199

200-
# Search
200+
def test_search_resources(self):
201+
"""
202+
Ensure we can search across the Resource Base list.
203+
"""
204+
url = reverse('base-resources-list')
201205
# Admin
202206
self.assertTrue(self.client.login(username='admin', password='admin'))
203207

@@ -209,7 +213,11 @@ def test_base_resources(self):
209213
# Pagination
210214
self.assertEqual(len(response.data['resources']), 1)
211215

212-
# Filtering
216+
def test_filter_resources(self):
217+
"""
218+
Ensure we can filter across the Resource Base list.
219+
"""
220+
url = reverse('base-resources-list')
213221
# Admin
214222
self.assertTrue(self.client.login(username='admin', password='admin'))
215223

@@ -297,7 +305,11 @@ def test_base_resources(self):
297305
# Pagination
298306
self.assertEqual(len(response.data['resources']), 12)
299307

300-
# Sorting
308+
def test_sort_resources(self):
309+
"""
310+
Ensure we can sort the Resource Base list.
311+
"""
312+
url = reverse('base-resources-list')
301313
# Admin
302314
self.assertTrue(self.client.login(username='admin', password='admin'))
303315

@@ -330,7 +342,11 @@ def test_base_resources(self):
330342
reversed_resource_titles = sorted(resource_titles.copy())
331343
self.assertNotEqual(resource_titles, reversed_resource_titles)
332344

333-
# Get & Set Permissions
345+
def test_perms_resources(self):
346+
"""
347+
Ensure we can Get & Set Permissions across the Resource Base list.
348+
"""
349+
url = reverse('base-resources-list')
334350
# Admin
335351
self.assertTrue(self.client.login(username='admin', password='admin'))
336352

@@ -371,3 +387,43 @@ def test_base_resources(self):
371387
self.assertEqual(response.status_code, 200)
372388
resource_perm_spec = response.data
373389
self.assertFalse('norman' in resource_perm_spec['users'])
390+
391+
def test_featured_and_published_resources(self):
392+
"""
393+
Ensure we can Get & Set Permissions across the Resource Base list.
394+
"""
395+
url = reverse('base-resources-list')
396+
# Admin
397+
self.assertTrue(self.client.login(username='admin', password='admin'))
398+
399+
resources = ResourceBase.objects.filter(owner__username='bobby')
400+
401+
url = urljoin(f"{reverse('base-resources-list')}/", 'featured/')
402+
response = self.client.get(url, format='json')
403+
self.assertEqual(response.status_code, 200)
404+
self.assertEqual(len(response.data), 5)
405+
self.assertEqual(response.data['total'], 0)
406+
# Pagination
407+
self.assertEqual(len(response.data['resources']), 0)
408+
409+
resources.filter(resource_type='map').update(featured=True)
410+
url = urljoin(f"{reverse('base-resources-list')}/", 'featured/')
411+
response = self.client.get(url, format='json')
412+
self.assertEqual(response.status_code, 200)
413+
self.assertEqual(len(response.data), 5)
414+
self.assertEqual(response.data['total'], 2)
415+
# Pagination
416+
self.assertEqual(len(response.data['resources']), 2)
417+
418+
def test_resource_types(self):
419+
"""
420+
Ensure we can Get & Set Permissions across the Resource Base list.
421+
"""
422+
url = urljoin(f"{reverse('base-resources-list')}/", 'resource_types/')
423+
response = self.client.get(url, format='json')
424+
self.assertEqual(response.status_code, 200)
425+
self.assertTrue('resource_types' in response.data)
426+
self.assertTrue('layer' in response.data['resource_types'])
427+
self.assertTrue('map' in response.data['resource_types'])
428+
self.assertTrue('document' in response.data['resource_types'])
429+
self.assertTrue('service' in response.data['resource_types'])

geonode/base/api/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@
2323

2424
router.register(r'users', views.UserViewSet, 'users')
2525
router.register(r'groups', views.GroupViewSet, 'group-profiles')
26-
router.register(r'base_resources', views.ResourceBaseViewSet, 'base-resources')
26+
router.register(r'resources', views.ResourceBaseViewSet, 'base-resources')
2727

2828
urlpatterns = []

geonode/base/api/views.py

+68-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
#
1919
#########################################################################
20+
from django.apps import apps
2021
from django.conf import settings
2122
from django.contrib.auth import get_user_model
2223

@@ -45,7 +46,8 @@
4546
UserSerializer,
4647
PermSpecSerialiazer,
4748
GroupProfileSerializer,
48-
ResourceBaseSerializer
49+
ResourceBaseSerializer,
50+
ResourceBaseTypesSerializer
4951
)
5052
from .pagination import GeoNodeApiPagination
5153

@@ -145,6 +147,71 @@ class ResourceBaseViewSet(DynamicModelViewSet):
145147
serializer_class = ResourceBaseSerializer
146148
pagination_class = GeoNodeApiPagination
147149

150+
def _filtered(self, request, filter):
151+
paginator = GeoNodeApiPagination()
152+
paginator.page_size = request.GET.get('page_size', 10)
153+
resources = ResourceBase.objects.filter(**filter)
154+
exclude = []
155+
for resource in resources:
156+
if not request.user.is_superuser and \
157+
not request.user.has_perm('view_resourcebase', resource.get_self_resource()):
158+
exclude.append(resource.id)
159+
resources = resources.exclude(id__in=exclude)
160+
result_page = paginator.paginate_queryset(resources, request)
161+
serializer = ResourceBaseSerializer(result_page, embed=True, many=True)
162+
return paginator.get_paginated_response({"resources": serializer.data})
163+
164+
@extend_schema(methods=['get'], responses={200: ResourceBaseSerializer(many=True)},
165+
description="API endpoint allowing to retrieve the approved Resources.")
166+
@action(detail=False, methods=['get'])
167+
def approved(self, request):
168+
return self._filtered(request, {"is_approved": True})
169+
170+
@extend_schema(methods=['get'], responses={200: ResourceBaseSerializer(many=True)},
171+
description="API endpoint allowing to retrieve the published Resources.")
172+
@action(detail=False, methods=['get'])
173+
def published(self, request):
174+
return self._filtered(request, {"is_published": True})
175+
176+
@extend_schema(methods=['get'], responses={200: ResourceBaseSerializer(many=True)},
177+
description="API endpoint allowing to retrieve the featured Resources.")
178+
@action(detail=False, methods=['get'])
179+
def featured(self, request):
180+
return self._filtered(request, {"featured": True})
181+
182+
@extend_schema(methods=['get'], responses={200: ResourceBaseTypesSerializer()},
183+
description="""
184+
Returns the list of available ResourceBase polymorphic_ctypes.
185+
186+
the mapping looks like:
187+
```
188+
{
189+
"resource_types": [
190+
"layer",
191+
"map",
192+
"document",
193+
"service"
194+
]
195+
}
196+
```
197+
""")
198+
@action(detail=False, methods=['get'])
199+
def resource_types(self, request):
200+
resource_types = []
201+
for _model in apps.get_models():
202+
if _model.__name__ == "ResourceBase":
203+
resource_types = [_m.__name__.lower() for _m in _model.__subclasses__()]
204+
if "geoapp" in resource_types:
205+
from geonode.geoapps.models import GeoApp
206+
resource_types.remove("geoapp")
207+
for label, app in apps.app_configs.items():
208+
if hasattr(app, 'type') and app.type == 'GEONODE_APP':
209+
if hasattr(app, 'default_model'):
210+
_model = apps.get_model(label, app.default_model)
211+
if issubclass(_model, GeoApp):
212+
resource_types.append(_model.__name__.lower())
213+
return Response({"resource_types": resource_types})
214+
148215
@extend_schema(methods=['get'], responses={200: PermSpecSerialiazer()},
149216
description="""
150217
Gets an object's the permission levels based on the perm_spec JSON.

0 commit comments

Comments
 (0)