diff --git a/.circleci/config.yml b/.circleci/config.yml
index a5524f5ab34..2945c6ce190 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -128,7 +128,7 @@ workflows:
codecov_name: api
load_docker_cache: false
save_docker_cache: false
- test_suite: ./test.sh geonode.base.api.tests geonode.layers.api.tests geonode.maps.api.tests geonode.documents.api.tests geonode.geoapps.api.tests geonode.upload.api.tests
+ test_suite: ./test.sh geonode.api.tests geonode.base.api.tests geonode.layers.api.tests geonode.maps.api.tests geonode.documents.api.tests geonode.geoapps.api.tests geonode.upload.api.tests
- build:
name: geonode_test_csw
codecov_name: csw
diff --git a/.clabot b/.clabot
index 6a4e514540c..73b12456cf3 100644
--- a/.clabot
+++ b/.clabot
@@ -75,6 +75,7 @@
"EHJ-52n",
"MartinPontius",
"ahmdthr",
- "fvicent"
+ "fvicent",
+ "RegisSinjari"
]
}
diff --git a/.devcontainer/.env b/.devcontainer/.env
index 2713c3db517..98923e8ed11 100644
--- a/.devcontainer/.env
+++ b/.devcontainer/.env
@@ -166,7 +166,7 @@ SECRET_KEY='myv-y4#7j-d*p-__@j#*3z@!y24fz8%^z2v6atuy4bo9vqr1_a'
CACHE_BUSTING_STATIC_ENABLED=False
MEMCACHED_ENABLED=False
-MEMCACHED_BACKEND=django.core.cache.backends.memcached.MemcachedCache
+MEMCACHED_BACKEND=django.core.cache.backends.memcached.PyLibMCCache
MEMCACHED_LOCATION=127.0.0.1:11211
MEMCACHED_LOCK_EXPIRE=3600
MEMCACHED_LOCK_TIMEOUT=10
diff --git a/.env.sample b/.env.sample
index 6e5dd10da9d..2ec4a7b8c9a 100644
--- a/.env.sample
+++ b/.env.sample
@@ -49,10 +49,6 @@ ALLOWED_HOSTS="['django', '{hostname}']"
DEFAULT_BACKEND_UPLOADER=geonode.importer
TIME_ENABLED=True
MOSAIC_ENABLED=False
-HAYSTACK_SEARCH=False
-HAYSTACK_ENGINE_URL=http://elasticsearch:9200/
-HAYSTACK_ENGINE_INDEX_NAME=haystack
-HAYSTACK_SEARCH_RESULTS_PER_PAGE=200
# #################
# nginx
@@ -177,7 +173,7 @@ GEOIP_PATH=/mnt/volumes/statics/geoip.db
CACHE_BUSTING_STATIC_ENABLED=False
MEMCACHED_ENABLED=False
-MEMCACHED_BACKEND=django.core.cache.backends.memcached.MemcachedCache
+MEMCACHED_BACKEND=django.core.cache.backends.memcached.PyLibMCCache
MEMCACHED_LOCATION=memcached:11211
MEMCACHED_LOCK_EXPIRE=3600
MEMCACHED_LOCK_TIMEOUT=10
diff --git a/.env_dev b/.env_dev
index 029bd5c9de7..34f85b41da6 100644
--- a/.env_dev
+++ b/.env_dev
@@ -49,10 +49,6 @@ ALLOWED_HOSTS="['django', '*']"
DEFAULT_BACKEND_UPLOADER=geonode.importer
TIME_ENABLED=True
MOSAIC_ENABLED=False
-HAYSTACK_SEARCH=False
-HAYSTACK_ENGINE_URL=http://elasticsearch:9200/
-HAYSTACK_ENGINE_INDEX_NAME=haystack
-HAYSTACK_SEARCH_RESULTS_PER_PAGE=200
# #################
# nginx
@@ -177,7 +173,7 @@ SECRET_KEY='myv-y4#7j-d*p-__@j#*3z@!y24fz8%^z2v6atuy4bo9vqr1_a'
CACHE_BUSTING_STATIC_ENABLED=False
MEMCACHED_ENABLED=False
-MEMCACHED_BACKEND=django.core.cache.backends.memcached.MemcachedCache
+MEMCACHED_BACKEND=django.core.cache.backends.memcached.PyLibMCCache
MEMCACHED_LOCATION=127.0.0.1:11211
MEMCACHED_LOCK_EXPIRE=3600
MEMCACHED_LOCK_TIMEOUT=10
diff --git a/.env_local b/.env_local
index 7fe89de6cae..3045a3eca17 100644
--- a/.env_local
+++ b/.env_local
@@ -49,10 +49,6 @@ ALLOWED_HOSTS="['django', '*']"
DEFAULT_BACKEND_UPLOADER=geonode.importer
TIME_ENABLED=True
MOSAIC_ENABLED=False
-HAYSTACK_SEARCH=False
-HAYSTACK_ENGINE_URL=http://elasticsearch:9200/
-HAYSTACK_ENGINE_INDEX_NAME=haystack
-HAYSTACK_SEARCH_RESULTS_PER_PAGE=200
# #################
# nginx
@@ -177,7 +173,7 @@ SECRET_KEY='myv-y4#7j-d*p-__@j#*3z@!y24fz8%^z2v6atuy4bo9vqr1_a'
CACHE_BUSTING_STATIC_ENABLED=False
MEMCACHED_ENABLED=False
-MEMCACHED_BACKEND=django.core.cache.backends.memcached.MemcachedCache
+MEMCACHED_BACKEND=django.core.cache.backends.memcached.PyLibMCCache
MEMCACHED_LOCATION=127.0.0.1:11211
MEMCACHED_LOCK_EXPIRE=3600
MEMCACHED_LOCK_TIMEOUT=10
diff --git a/.env_test b/.env_test
index b2fd6ebb6a0..8026c6c0002 100644
--- a/.env_test
+++ b/.env_test
@@ -49,10 +49,6 @@ ALLOWED_HOSTS="['django', 'localhost', '127.0.0.1']"
DEFAULT_BACKEND_UPLOADER=geonode.importer
TIME_ENABLED=True
MOSAIC_ENABLED=False
-HAYSTACK_SEARCH=False
-HAYSTACK_ENGINE_URL=http://elasticsearch:9200/
-HAYSTACK_ENGINE_INDEX_NAME=haystack
-HAYSTACK_SEARCH_RESULTS_PER_PAGE=200
# #################
# nginx
@@ -186,7 +182,7 @@ GEOIP_PATH=/mnt/volumes/statics/geoip.db
CACHE_BUSTING_STATIC_ENABLED=False
MEMCACHED_ENABLED=False
-MEMCACHED_BACKEND=django.core.cache.backends.memcached.MemcachedCache
+MEMCACHED_BACKEND=django.core.cache.backends.memcached.PyLibMCCache
MEMCACHED_LOCATION=memcached:11211
MEMCACHED_LOCK_EXPIRE=3600
MEMCACHED_LOCK_TIMEOUT=10
diff --git a/.github/ISSUE_TEMPLATE/gnip.md b/.github/ISSUE_TEMPLATE/gnip.md
index 2e2aac39552..a8f7ec8c1ba 100644
--- a/.github/ISSUE_TEMPLATE/gnip.md
+++ b/.github/ISSUE_TEMPLATE/gnip.md
@@ -56,7 +56,6 @@ Project Steering Committee:
* Alessio Fabiani:
* Francesco Bartoli:
* Giovanni Allegri:
-* Simone Dalmasso:
* Toni Schoenbuchner:
* Florian Hoedt:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2ac7e72bd1..0144ba5c7d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,97 @@
-# Change Log
+# Changelogs
+
+## [4.3.0](https://github.com/GeoNode/geonode/releases/tag/4.3.0) (2024-05-30)
+### New features and improvements
+
+- **3D and MapSore catalog plugins available for maps**: This release includes a major upgrade of the MapStore framework upon which the GeoNode client is built. One of the key benefits brought by the alignment to the latest MapStore versions, is the integration of new plugins powered by MapStore. Two of these plugins have been enabled by default for maps: **3D view**, powered by Cesium and the [**Catalog tool**](https://docs.mapstore.geosolutionsgroup.com/en/latest/user-guide/catalog/) which gives users the option to include external layers (from WMS, TMS, CSW, etc. services) directly into a map. The 3D view is the first step toward the publishing of 3D content with GeoNode. Meanwhile external 3D Tiles sources can already be viewed inside a GeoNode map thanks to the [Catalog service for 3D Tiles](https://docs.mapstore.geosolutionsgroup.com/en/v2023.02.01/user-guide/catalog/#3d-tiles-catalog).
+- **Map Viewers**: Thanks again to the work done to extend the integration with MapStore, its concept of Application Contexts have been adapted and integrated in GeoNode under the the name "Map Viewers". With a Map Viewer the list of tools and plugins available in a map can be configured. Map Viewers can be created from a map (the feature is available under the Edit menu) and they can be used by multiple maps. With Map Viewers all the plugins offered by MapStore can be used inside GeoNode maps.
+- **Hiding resources from catalog listing**: There are cases where a resource is created only to be used within other resources. Images uploaded to be included in a geostory, datasets only meant to be viewed inside maps, are examples. Sometimes we don't want these resoruces to be visibile inside the catalog list, or inside search results. For these cases a new "Advertized" attribute is available under the Settings tab of the Metadata editor. When this flag is turned off the resource will only be listed to its owner. This doesn't affect the permissions on the resource. Users with view permissions can still visualize the resource, but it won't be listed in the catalog to them.
+- **Extended Users API**: The User API has been extended to support the full management of GeoNode accounts. This API makes GeoNode compliant with the Data policies that request to give users the option to delete their account and their data from apps. The new APIs also support the transfer of resource ownership to another user and the ability to step down from the Group Manager role for a user.
+- **Groups facet**: A new facet (filter) has been added to the filters panel for filtering resources by Group.
+
+### Other minor improvements and fixes
+
+ - **Backup/Restore**: Several improvements that make the B/R service more robust and performant. Code customizations (layouts, etc.) are excluded now from projects backups.
+ - **Owner was update when geoapp were saved**: This fixes a problem with geoapps (geostories and dashboards) where the current user was assigned as owner when the resource was saved.
+ - **Fixed textara for rich HTML metadata fields**: It wasn't editable due to a regression introduced in previous versions.
+ - **Metadata and SLD file uploads**: Fixed a problem with the legacy code that prevented the upload of XML and SLD metadata files for an existing resource.
+
+ ### Software upgrades:
+ - Django 4.2.9
+ - Geoserver 2.24.3
+ - MapStore 2024.01.00
+
+The full list of changes [here](https://github.com/GeoNode/geonode/milestone/43?closed=1)
+
+## [4.2.0](https://github.com/GeoNode/geonode/releases/tag/4.2.0) (2024-01-10)
+
+### New features and improvements
+
+- **Enhanced faceted filtering**: Filtering of resources has undergone a deep refactoring with the goal of improving its performance and implement proper (cascaded) faceted filtering. The resoruces, and their counting, are calculated from the intersection of the topics selected between different filters (AND logic) and the union of the optics selected from the same filter (OR logic). An improvement is planned for a future version to let users select additional topics from the same filter that happen to be hidden because not cointained in the current results.
+- **Location view and management**: A new "Location" tab is available inside the information side panel, where the bounding box of the resoruce and its centroid are displayed. The location for spatial resources (datasets and maps) are automatically retrieved by the data itself. For non-spatial resources the same panel gives editors the tools to set the bounding bos and the position of the resource.
+- **Linked resources**: The options to link resources has been extended to any type of resource. It's possible to related any resource with any other resource (one or many), and the relationship is disaplyed inside the new "Lined resources" tab inside the information side panel. The direction of the relationship is also visible, since linked resources are disaplyed under two distinct groups "Linked from" and "Lined to".
+
+### Other minor improvements
+ - Implemented a generic and pluggable OIDC SocialAccount Provider, which extends and improves the one provided by the `allauth` module already available in GeoNode
+ - Documents from remote URLs can be created from the client
+ - Regions are not assigned to new resources automatically anymore. A pluggable and configurable option lets administrators implement specific logic for the automatic assignmenet if required
+ - Option to configure a WMTS service to generate thumbnail backgrounds
+ - The rendering of thumbnails for the maps now take into account ordering and opacity
+ - Implemented the option to do not register new users as contributors automatically (default behaviour)
+
+### Software upgrades:
+ - Django 3.2.23
+ - Geoserver 2.23.3
+ - PostgreSQL 15.3 / PostGIS 3.3
+ - Nginx 1.25.3
+
+The full list of changes [here](https://github.com/GeoNode/geonode/milestone/37?closed=1)
+
+## [4.1.0](https://github.com/GeoNode/geonode/releases/tag/4.1.0) (2023-06-05)
+
+### New upload engine
+GeoNode integrates a brand new importer module based on [GDAL/OGR](https://gdal.org/), which offers increased robustness and reliability to the upload UI and API services. GeoPackage (vector), GeoJSON, KML/KMZ formats and a new CSV handler have been implemented.
+
+### Thesaurus faceting and date filtering
+If thesaurus and thesaurus keywords are configured and assigned to resources, they will be available inside the filters panel, along with the number of associated resources.
+Date filtering (from/top) has also been added.
+
+### Time series configurable after the upload
+The configuration of (potential) time series at upload time was confusing for users, and not very robust.
+With the new importer, the optional configuration of vector time series can be done afterward, through the Settings tab inside the Metadata editing page
+Only vector fomats that provide date(time) fields natively are supported. Conversion from string fields is not implemented.
+
+### Linked resources
+This restore a functionality available in previous versions of GeoNode.
+A tab inside the info panel has been added where relationships between datasets, maps and documents are reported.
+
+### Vector dataset attributes
+A tab inside the info panel has been added showing the attributes of vector datasets
+
+### Remote documents
+The API has been extended to permit the creation of document resources referencing remote URLs
+
+### ISO-19115 XML upload via API
+The API now supports the upload of a metadata XML file along with the resource data
+
+### Software upgrades
+
+ - [Geoserver 2.23.0](https://geoserver.org/announcements/2023/04/05/geoserver-2-23-0-released.html) is now the reference version. This version includes Geofence WPS rules which are employed by GeoNode to strengthen the security of the OGC/WPS processes.
+- [MapStore 2022.02.xx](https://github.com/geosolutions-it/MapStore2/tree/2022.02.xx)
+- [Django 3.2.19](https://docs.djangoproject.com/en/4.2/releases/3.2.19/)
+- PostgreSQL 13 and PostGIS 3.3.3
+
+
+### Security and Bug Fixes
+- [CVE-2023-26043](https://github.com/GeoNode/geonode/security/advisories/GHSA-mcmc-c59m-pqq8)
+Fixed a vulnerability to XML External Entity (XXE) injection
+- [CVE-2023-28442](https://github.com/GeoNode/geonode/security/advisories/GHSA-87mh-vw7c-5v6w)
+Fixed information leak
+
+You can see the **full list of closed issues [here](https://github.com/GeoNode/geonode/compare/4.1.0...4.0.3)**.
+
+## System requirements
+Python >3.9 is required to run GeoNode 4.1.0, since many of its dependencies have dropped support for older versions.
## [4.1.2](https://github.com/GeoNode/geonode/tree/4.1.2) (2023-08-11)
## Bug Fixes
@@ -28,7 +121,7 @@ Only vector fomats that provide date(time) fields natively are supported. Conver
### Related resources
This restore a functionality available in previous versions of GeoNode.
-A tab inside the info panel has been added where relationships between datasets, maps and documents are reported.
+A tab inside the info panel has been added where relationships between datasets, maps and documents are reported.
### Vector dataset attributes
A tab inside the info panel has been added showing the attributes of vector datasets
@@ -732,344 +825,344 @@ Python >3.9 is required to run GeoNode 4.1.0, since many of its dependencies hav
## [Full Changelog](https://github.com/GeoNode/geonode/compare/2.10...2.10.1)
-
2019-11-12: dependabot-preview[bot] Bump urllib3 from 1.25.6 to 1.25.7
- 2019-11-12: dependabot-preview[bot] Bump deprecated from 1.2.6 to 1.2.7
- 2019-11-12: dependabot-preview[bot] Bump sqlalchemy from 1.3.10 to 1.3.11
- 2019-11-11: dependabot-preview[bot] Bump pytest-django from 3.6.0 to 3.7.0
- 2019-11-11: afabiani [Fixes #5223] Layer details broken if no store identified
- 2019-11-11: dependabot-preview[bot] Bump tqdm from 4.37.0 to 4.38.0
- 2019-11-11: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
- 2019-11-11: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.5 to 1.4.6
- 2019-11-11: dependabot-preview[bot] Bump django-mapstore-adapter from 1.0.11 to 1.0.12
- 2019-11-11: dependabot-preview[bot] Bump twisted from 19.7.0 to 19.10.0
- 2019-11-11: dependabot-preview[bot] Bump kombu from 4.6.5 to 4.6.6
- 2019-11-09: Florian Hoedt added unicode tests for Menu, MenuItem and MenuPlaceholder
- 2019-11-08: Alessio Fabiani Revert "Bump amqp from 2.5.1 to 2.5.2"
- 2019-11-08: gioscarda [Fixes #5214] Fix regression on monitoring countries endpoint
- 2019-11-08: afabiani [Fixes #5209] MapStore client critical slow-down with latest Chrome updates
- 2019-11-08: afabiani [Hardening] layer_detail typo
- 2019-11-08: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
- 2019-11-08: gioscarda [Fixes #5203] avoid count label in countries, add missing flags
- 2019-11-06: dependabot-preview[bot] Update six requirement from <1.11.0 to <1.14.0
- 2019-11-06: dependabot-preview[bot] Bump psutil from 5.6.4 to 5.6.5
- 2019-11-05: Florian Hoedt fixed pep8 errors
- 2019-11-05: Florian Hoedt fixes #5197
- 2019-11-04: dependabot-preview[bot] Bump psutil from 5.6.3 to 5.6.4
- 2019-11-04: dependabot-preview[bot] Bump django from 1.11.25 to 1.11.26
- 2019-11-04: dependabot-preview[bot] Bump python-dateutil from 2.8.0 to 2.8.1
- 2019-11-04: afabiani - _resourcebase_info_panel is showing the tkeywords only if THESAURUS is enabled and adding commas between words
- 2019-11-04: afabiani [Fixes #5181] Wrong words in French translation
- 2019-10-31: Fanevanjanahary Changed wrong words in French version
- 2019-11-04: afabiani - Fixes "field" rendering error when Thesauri have been enabled
- 2019-11-01: dependabot-preview[bot] Bump tqdm from 4.36.1 to 4.37.0
- 2019-11-01: Toni Schönbuchner [Fixes #5172] Add fix for import thesaurus
- 2019-11-01: Toni Schönbuchner [Fixes #5172] Add docs for thesaurus
- 2019-10-31: afabiani [Fixes #5158] Update thesaurus management command and fix autocomplete
- 2019-10-30: afabiani [Fixes #4661] Replace Layer functionality incomplete/broken
- 2019-09-05: capooti Mitigates possibilities for error #2932
- 2019-10-30: dependabot-preview[bot] Bump setuptools from 41.5.1 to 41.6.0
- 2019-10-30: Toni Schönbuchner [Fixes #5105] Error when using THESAURI
- 2019-10-29: afabiani [Docs] Cleaning up misleading instructions
- 2019-10-29: afabiani [Fixes #5136] Assignment of users to member groups should be done at activation time
- 2019-10-29: capooti [Implements #5149] Add documentation for migrating GeoNode from 2.4 to 2.10.1
- 2019-10-29: Alessio Fabiani [Fixes #5117] Publishing layers of two remote services with same (#5135)
- 2019-10-29: dependabot-preview[bot] Bump setuptools from 41.5.0 to 41.5.1 (#5139)
- 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix assertation error
- 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix blank space
- 2019-10-29: dependabot-preview[bot] Bump flake8 from 3.7.8 to 3.7.9
- 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix Flake8
- 2019-10-29: Toni Schönbuchner [Fixes #5138] Escape Hierarchical-tags
- 2019-10-29: Toni Schönbuchner [Fixes #5137] Striptags for service resources
- 2019-10-28: dependabot-preview[bot] Bump setuptools from 41.4.0 to 41.5.0
- 2019-10-28: dependabot-preview[bot] Bump decorator from 4.4.0 to 4.4.1
- 2019-10-28: dependabot-preview[bot] Update django-modeltranslation requirement
- 2019-10-25: capooti [Fixes #5121] ResourceBase API returns an error if a curated thumbnail img file is not existing anymore
- 2019-10-25: Alessio Fabiani [Fixes #4827] Line layer uploaded to GeoNode is rendered as point layer (#5116)
- 2019-10-24: Alessio Fabiani [Fixes #4827] Line layer uploaded to GeoNode is rendered as point layer (#5092)
- 2019-10-24: dependabot-preview[bot] Update django-floppyforms requirement from <=1.7.0 to <1.9.0
- 2019-10-23: capooti Enable thesauri api load only if a thesauri is defined
- 2019-10-23: afabiani [Documentation] Fixed .env docker description for geonode-project
- 2019-10-23: afabiani [Documentation] Fixed typo goenode to geonode
- 2019-10-22: Alessio Fabiani Update settings.py
- 2019-10-22: afabiani [Fixes #5096] Disabling geonode.monitoring causes model exceptions on views
- 2019-10-22: dependabot-preview[bot] Bump pillow from 6.2.0 to 6.2.1
- 2019-10-22: Florian Hoedt Update index.html
- 2019-10-21: afabiani [Testing] Code Coverage / CHANGELOG updates
- 2019-10-21: dependabot-preview[bot] Bump python-slugify from 3.0.6 to 4.0.0
- 2019-10-21: dependabot-preview[bot] Bump psycopg2 from 2.8.3 to 2.8.4
- 2019-10-21: dependabot-preview[bot] Bump django-celery-beat from 1.1.1 to 1.5.0
- 2019-10-21: dependabot-preview[bot] Bump django-leaflet from 0.24.0 to 0.25.0
- 2019-10-21: afabiani [Testing] Code Coverage
- 2019-10-21: afabiani [Testing] Code Coverage
- 2019-10-19: Toni [Fixes #5073] Added blank line
- 2019-10-19: Toni [Fixes #5073] Update install docs
- 2019-10-18: afabiani [Fixes #5070] Add Bing background layer config to MapStore2 adapter if apiKey is provided
- 2019-10-18: afabiani [Hardening] coverage run integration tests too
- 2019-10-18: Toni Schönbuchner [Fixes: #4659] Double message on Submit invite users
- 2019-10-18: afabiani [Hardening] coverage run integration tests too
- 2019-10-18: afabiani [Hardening] coverage run integration tests too
- 2019-10-18: afabiani [Hardening] coverage run integration tests too
- 2019-10-18: gioscarda [Fixes #5051] arrangement for geonode_logstash contrib app
- 2019-10-17: afabiani [Hardening] A bit more robust logical check
- 2019-10-17: afabiani [Hardening] Original Dataset link: skip checks for external links
- 2019-10-17: afabiani [Hardening] A bit more robust logical check
- 2019-10-17: afabiani [Fixes #4942] Download layer filter not sending CQL filter anymore
- 2019-10-17: afabiani [Fixes #4956] "Original Dataset" Link behavior improvements
- 2019-10-17: gioscarda [Fixes #5031] Disable monitoring by default
- 2019-10-17: dependabot-preview[bot] Bump pytest-django from 3.5.1 to 3.6.0
- 2019-10-16: afabiani [Documentation] Add "OAUTH2_API_KEY" setting to the settings list
- 2019-10-15: afabiani - Hide "private" groups to "non-members"
- 2019-10-16: Alessio Fabiani Update gnip.md
- 2019-10-16: Toni Schönbuchner fix flake8 issue with _
- 2019-10-16: Toni Schönbuchner #5054 | 5053 Update Translations
- 2019-10-15: afabiani [Hardening] Fix backup/restore GeoServer rest endpoints
- 2019-10-15: dependabot-preview[bot] Bump pytest from 4.6.5 to 4.6.6
- 2019-10-13: Lilian Guimarães Remove plusone (Closes #4929)
- 2019-10-11: dependabot-preview[bot] Bump python-slugify from 3.0.4 to 3.0.6
- 2019-10-11: dependabot-preview[bot] Bump sqlalchemy from 1.3.9 to 1.3.10 (#5032)
- 2019-10-11: Toni #4930 Remove Edit Data button for RASTER layer (#5035)
- 2019-10-10: Paolo Corti Fixes #5005 (#5027)
- 2019-10-09: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.3 to 1.4.4 (#5029)
- 2019-10-08: afabiani [Documentation] Loading (and Processing) external data / importlayers and updatelayers Management Commands
- 2019-10-08: afabiani [Documentation] Backporting GeoNode/GeoServer AA Sections - update GeoFence db config
- 2019-10-08: afabiani [Documentation] Backporting GeoNode/GeoServer AA Sections
- 2019-10-08: afabiani [Documentation] LDAP Contrib Module Documentation
- 2019-10-08: afabiani [Hardening] Cleanup unneeded deps
- 2019-10-08: Alessio Fabiani Adding LiliGuimaraes to CLA
- 2019-10-07: dependabot-preview[bot] Bump django-mapstore-adapter from 1.0.8 to 1.0.9
- 2019-10-07: dependabot-preview[bot] Bump sqlalchemy from 1.3.8 to 1.3.9
- 2019-10-07: dependabot-preview[bot] Bump beautifulsoup4 from 4.8.0 to 4.8.1
- 2019-10-07: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.2 to 1.4.3
- 2019-10-07: dependabot-preview[bot] Bump pytz from 2019.2 to 2019.3
- 2019-10-07: dependabot-preview[bot] Bump setuptools from 41.2.0 to 41.4.0
- 2019-10-07: afabiani [Revert][Hardening] http_client caching requests
- 2019-10-07: afabiani [Hardening] http_client caching requests
- 2019-10-07: afabiani [Hardening] - Get rid of redoundant/unuseful methods
- 2019-10-07: afabiani - Add 'spatialreference.org' to default settings
- 2019-10-07: Toni [Fixes: #5000] Update Frontend section
- 2019-10-06: Steffen Berger [Fixes #4954] only needed attributes added to cookie, when adding layer to cart (#4968)
- 2019-10-06: Toni [Fixes: 4988] Rework static build process (#4989)
- 2019-10-02: Alessio Fabiani [Hardening] Make master more resilient to errors
- 2019-10-04: dependabot-preview[bot] Bump docker from 4.0.2 to 4.1.0
- 2019-10-04: gioscarda [Fixes #4990] fix set_layers_permissions managament command
- 2019-10-04: Alessio Fabiani Adding "sjohn-atenekom" to the authorized list of users
- 2019-10-02: afabiani [Docs] Updating installation instructions for GeoNode Project
- 2019-10-01: dependabot-preview[bot] Bump pillow from 6.1.0 to 6.2.0
- 2019-10-01: dependabot-preview[bot] Bump django from 1.11.24 to 1.11.25
- 2019-09-30: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
- 2019-09-30: dependabot-preview[bot] Bump kombu from 4.6.4 to 4.6.5
- 2019-09-30: afabiani [Docs] update geonode-project setup instructions
- 2019-09-30: Alessio Fabiani [Fixes #4959] By using UPLOADER=geonode.rest we are facing a timing issues on signals (#4960)
- 2019-09-30: Alessio Fabiani [Fixes #4964] Date widget has gone (#4969)
- 2019-09-27: Alessio Fabiani [Fixes #4949] UnicodeEncodeError in upload.models.update_from_session (#4958)
- 2019-09-27: zoran995 Use Tomcat 8.5.46
- 2019-09-27: dependabot-preview[bot] Update httplib2 requirement from <0.13.2 to <0.14.1
- 2019-09-27: afabiani [Fixes #4739] Layer post_save deletes links to external thumbnails / data
- 2019-09-26: afabiani [Hardening] Missing link name on "set_all_layers_metadata" mgmt command
- 2019-09-26: afabiani [Hardening] Sanity checks on links number
- 2019-09-26: afabiani [Hardening] Correct bbox checks on geoext JS
- 2019-09-25: dependabot-preview[bot] Bump urllib3 from 1.25.5 to 1.25.6
- 2019-09-23: root [Hardening] Make gslurp resilient to errors in case 'ignore_errors' has been specified
- 2019-09-23: dependabot-preview[bot] Bump python-slugify from 3.0.3 to 3.0.4
- 2019-09-23: Alessio Fabiani [Hardening] Favourite model _unicode resilient to None
- 2019-09-23: Toni [Fixes: #4963] Fix wrong template include for all_auth
- 2019-09-23: afabiani [Hardening] Add metadata links names to 'set_all_layers_metadata' mgmt cmd
- 2019-09-23: afabiani [Hardening] Add metadata links names to 'set_all_layers_metadata' mgmt cmd
- 2019-09-22: Toni Schönbuchner #4933 forgot password in modal
- 2019-09-20: dependabot-preview[bot] Bump urllib3 from 1.25.3 to 1.25.5
- 2019-09-20: afabiani [Fixes #4907] Add the possibility of enabling "captcha" on GeoNode signup form
- 2019-09-19: afabiani [Hardening] Exposing to env AVATAR_GRAVATAR_SSL option
- 2019-09-19: afabiani [Hardening] Exposing to env AVATAR_GRAVATAR_SSL option
- 2019-09-19: afabiani Bump django-mapstore-adapter to 1.0.8
- 2019-09-19: afabiani [Hardening] Removing redundant float conversion of coordinates
- 2019-09-19: dependabot-preview[bot] Bump tqdm from 4.36.0 to 4.36.1
- 2019-09-19: afabiani [Fixes #4924] Add member to Group is broken
- 2019-09-19: Toni [Fixes: #4920] Footer Display Code on Detail Layer pages
- 2019-09-18: Toni #fixes 4916 – show featured items
- 2019-09-18: GeoSolutions [Hardening] Minor refactoring: check in a more pythonic way
- 2019-09-18: dependabot-preview[bot] Bump tqdm from 4.35.0 to 4.36.0
- 2019-09-18: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.3 to 1.0.4
- 2019-09-18: Alessio Fabiani [Fixes #4911] On Monitoring plugin the uptime is not correctly computed (#4912)
- 2019-09-18: afabiani - updating Italian translations
- 2019-09-17: GeoSolutions [Hardening] Remove code warnings
- 2019-09-17: GeoSolutions [Hardening] Remove code warnings
- 2019-09-17: gioscarda [Fixes #4908] change monitoring label
- 2019-09-17: afabiani [Hardening] Make 'set_all_layers_metadata' management command more robust
- 2019-09-17: gioscarda [Fixes #4903] fixing hits count
- 2019-09-14: afabiani [Hardening] Get rid of harmful logging
- 2019-09-13: afabiani [Fixes #4790] Issue adding remote service with non-ascii characters in the service name - 'ascii' codec can't encode character u'\xe9' in position 8: ordinal not in range(128)
- 2019-09-13: gioscarda [Fixes #4896] fixing data in response
- 2019-09-13: afabiani [Hardening] Get rid of crazy cycle over the GeoServer catalog
- 2019-09-12: afabiani [Fixes #4884] Attached SLD works only with ZIP files when "geonode.importer" method is enabled
- 2019-09-13: afabiani [Hardening] Upgrade angular from 1.5.0 to 1.6.0
- 2019-09-12: root [Hardeninig] Remove unuseful computation
- 2019-09-12: afabiani [Hardening] Refresh js deps
- 2019-09-12: afabiani [Fixes #4881] Improve initial generate thumbnail quality
- 2019-09-12: afabiani [Fixes #4886] Permissions "Select2" widget not correctly forwarding values to the form
- 2019-09-12: gioscarda [Fixes #4883] monitoring frontened refactoring
- 2019-09-12: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.0 to 1.4.1
- 2019-09-11: root [Hardening] Check for duplicates links on 'set_all_layers_metadata' management command
- 2019-09-11: dependabot-preview[bot] Bump django-storages from 1.7.1 to 1.7.2
- 2019-09-11: afabiani [Hardening] Javascript deps
- 2019-09-11: gioscarda fixing monitoring tests
- 2019-09-11: afabiani [Hardening] Javascript deps
- 2019-09-10: capooti Fixes #4827
- 2019-09-10: capooti Fixes #4846 and #4866
- 2019-09-10: afabiani [Fixes #4863] Trying to assign permission to more than one users, fails
- 2019-09-10: afabiani - Fixing integration test-cases
- 2019-09-10: afabiani - Fixing integration test-cases
- 2019-09-10: gioscarda [Fixes #4861] improved monitoring api filtering
- 2019-09-10: afabiani - Fixing integration test-cases
- 2019-09-10: afabiani - Fixing integration test-cases
- 2019-09-10: afabiani [Fixes #4855] Download links always return whole world bbox
- 2019-09-09: afabiani Bump gn-gsimporter from 1.0.10 to 1.0.12
- 2019-09-09: afabiani Bump django-mapstore-adapter from 1.0.6 to 1.0.7
- 2019-09-09: gioscarda [Fixes #4849] retrieve cpu and mem usage metric data
- 2019-09-08: afabiani Fixes #4844
- 2019-09-06: capooti Fixes #4844
- 2019-09-06: gioscarda Added docs related new monitoring requests
- 2019-09-06: root [Hardening] 'set_resource_default_links' avoid hitting GeoServer catalog if it is not needed
- 2019-09-06: root [Hardening] HTTP connector resilient to SSL invalid certs
- 2019-09-06: afabiani [JS] Fix GeoExplorer layer bbox parsing
- 2019-09-06: gioscarda [Fixes #4842] Monitoring APIs improvements
- 2019-09-06: afabiani [Hardening] More readable name for celery files results store / serialized JS async calls on LayerInfo upload prototype
- 2019-09-06: afabiani Bump django-mapstore-adapter from 1.0.5 to 1.0.6
- 2019-09-06: afabiani [Fixes #4831] GeoNode Importer mode not really asynchronous
- 2019-09-05: dependabot-preview[bot] Bump geonode-oauth-toolkit from 1.1.4.5 to 1.1.4.6
- 2019-09-05: root [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
- 2019-09-05: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.2 to 1.0.3
- 2019-09-05: afabiani [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
- 2019-09-05: afabiani [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
- 2019-09-04: Alessio Fabiani [Fixes #4813] Removing a layer is broken - Celery error (#4817)
- 2019-09-04: afabiani [Fixes #4772] Get rid of deprecated "Publish local map layers as WMS layer group"
- 2019-09-04: afabiani [Hardening] allow monitoring and serviceprocessors to perform internal requests against non verified SSL urls
- 2019-09-04: afabiani [Minor] fix tooltip blinking on home page after bootstrap CSS updates
- 2019-08-30: Alessio Fabiani Update .clabot
- 2019-09-02: afabiani [Fixes #4809] Static assets still using Select2 3.5 instead of Select2 4
- 2019-09-02: dependabot-preview[bot] Bump django from 1.11.23 to 1.11.24
- 2019-09-02: Toni Schönbuchner updated angular, select2, bootstrap
- 2019-09-01: afabiani - Using YARN instead of BOWER (deprecated) and update dependencies
- 2019-08-30: Toni Add missing ngCookies when DEBUG_STATIC true
- 2019-08-30: Alessio Fabiani Update .clabot
- 2019-08-30: Alessio Fabiani Update .clabot
- 2019-08-30: dependabot-preview[bot] Bump django-allauth from 0.39.1 to 0.40.0
- 2019-08-29: afabiani OSGeo Project logo
- 2019-08-29: afabiani Updating "CONTRIBUTING" policies ad instructions
- 2019-08-29: Alessio Fabiani Update .clabot
- 2019-08-29: Alessio Fabiani Update .clabot
- 2019-08-29: Alessio Fabiani Update .clabot
- 2019-08-29: Alessio Fabiani Create .clabot
- 2019-08-29: Alessio Fabiani Delete signatures
- 2019-08-29: Alessio Fabiani Create signatures
- 2019-08-29: Alessio Fabiani Create CODE_OF_CONDUCT.md
- 2019-08-28: dependabot-preview[bot] Bump sqlalchemy from 1.3.7 to 1.3.8
- 2019-08-28: Toni Schönbuchner improved upload error handling
- 2019-08-28: Toni Add sentence regarding minimal requirements
- 2019-08-27: Toni Schönbuchner Removed autocomplete from password fields
- 2019-08-26: dependabot-preview[bot] Bump tqdm from 4.34.0 to 4.35.0
- 2019-08-21: gioscarda [Fixes #4759] Merging of the user analytics PR and resolving conflicts
- 2019-08-22: afabiani Update issue templates
- 2019-08-22: Alessio Fabiani Update issue templates
- 2019-08-22: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.1 to 1.0.2
- 2019-08-22: dependabot-preview[bot] Update idna requirement from <2.7,>=2.5 to >=2.5,<2.9
- 2019-08-22: afabiani Bump django-mapstore-adapter to 1.0.5
- 2019-08-22: afabiani Removing Bower which is deprecated: installing Yarn instead
- 2019-08-22: afabiani [Docs] Updating GeoNode release tag
- 2019-08-22: dependabot-preview[bot] Bump setuptools from 41.1.0 to 41.2.0
- 2019-08-21: afabiani Update django-tastypie requirement from <=0.14.0 to <0.15.0
- 2019-08-21: dependabot-preview[bot] Bump pytest-bdd from 3.2.0 to 3.2.1
- 2019-08-20: Alessio Fabiani [Fixes #4713] Attributes are not added in layers_attributes table when a layer is uploaded (#4751)
- 2019-08-20: dependabot-preview[bot] Bump pytest-bdd from 3.1.1 to 3.2.0
- 2019-08-20: dependabot-preview[bot] Bump tqdm from 4.33.0 to 4.34.0
- 2019-08-19: dependabot-preview[bot] Update pyproj requirement from <2.2.2.0,>=1.9.5 to >=1.9.5,<2.2.3.0
- 2019-08-19: dependabot-preview[bot] Update django-jsonfield requirement from <1.2.1 to <1.3.2
- 2019-08-19: Alessio Fabiani Bump geonode-oauth-toolkit from 1.3.1 to 1.1.4.1rc0 (#4745)
- 2019-08-16: dependabot-preview[bot] Bump kombu from 4.6.3 to 4.6.4
- 2019-08-15: dependabot-preview[bot] Bump amqp from 2.5.0 to 2.5.1
- 2019-08-15: dependabot-preview[bot] Bump sqlalchemy from 1.3.6 to 1.3.7
- 2019-08-14: dependabot-preview[bot] Bump lxml from 4.4.0 to 4.4.1
- 2019-08-14: dependabot-preview[bot] Bump setuptools from 41.0.1 to 41.1.0
- 2019-08-14: afabiani - Fix typo
- 2019-08-14: capooti Restore base/tests.py
- 2019-08-14: capooti Implement GNIP curated-thumbs
- 2019-08-14: afabiani Bump django-geonode-mapstore-client from 1.3.1 to 1.4.0
- 2019-08-13: afabiani - Update spcgeonode template docker images
- 2019-08-13: afabiani Bump gn-gsimporter from 1.0.9 to 1.0.10
- 2019-08-13: afabiani - Update static assets
- 2019-08-13: afabiani - Updated Italian translations
- 2019-08-13: afabiani - Updated French translations
- 2019-08-13: Toni Schönbuchner added missing imports
- 2019-08-09: dependabot-preview[bot] Bump tqdm from 4.32.2 to 4.33.0
- 2019-08-08: Toni Schönbuchner added defusedxml requirements.txt
- 2019-08-08: Toni Schönbuchner used defusedxml for parse and fromstring
- 2019-08-07: dependabot-preview[bot] Bump mercantile from 1.1.1 to 1.1.2
- 2019-08-07: dependabot-preview[bot] Bump pytest from 4.6.4 to 4.6.5
- 2019-08-07: dependabot-preview[bot] Bump invoke from 1.2.0 to 1.3.0
- 2019-08-06: dependabot-preview[bot] Bump twisted from 19.2.1 to 19.7.0
- 2019-08-01: dependabot-preview[bot] Bump pytz from 2019.1 to 2019.2
- 2019-08-01: dependabot-preview[bot] Bump django from 1.11.22 to 1.11.23
- 2019-07-31: Joseph Stachelek typo fix
- 2019-07-30: dependabot-preview[bot] Bump coverage from 4.5.3 to 4.5.4
- 2019-07-29: dependabot-preview[bot] Bump python-slugify from 3.0.2 to 3.0.3
- 2019-07-29: dependabot-preview[bot] Bump lxml from 4.3.4 to 4.4.0
- 2019-07-29: dependabot-preview[bot] Update httplib2 requirement from <0.13.1 to <0.13.2
- 2019-07-26: Alessio Fabiani Update stale.yml
- 2019-07-26: Alessio Fabiani Create stale.yml
- 2019-07-25: afabiani - Update django-geonode-mapstore-client
- 2019-07-24: Francesco Pennica updated some strings in the italian translation
- 2019-07-24: dependabot-preview[bot] Bump beautifulsoup4 from 4.7.1 to 4.8.0
- 2019-07-23: dependabot-preview[bot] Update django-modeltranslation requirement
- 2019-07-23: Sylvain POULAIN Update django.po
- 2019-07-23: dependabot-preview[bot] Bump sqlalchemy from 1.3.5 to 1.3.6
- 2019-07-22: dependabot-preview[bot] Bump django-polymorphic from 2.0.3 to 2.1.2
- 2019-07-22: dependabot-preview[bot] Bump pytest-bdd from 3.1.0 to 3.1.1
- 2019-07-22: dependabot-preview[bot] Bump docker from 3.7.0 to 4.0.2
- 2019-07-22: dependabot-preview[bot] Bump parse-type from 0.4.1 to 0.5.2
- 2019-07-22: dependabot-preview[bot] Bump amqp from 2.4.2 to 2.5.0
- 2019-07-22: dependabot-preview[bot] Update httplib2 requirement from <=0.10.3 to <0.13.1
- 2019-07-22: afabiani - docker-compose uses image: geonode/geonode:latest
- 2019-07-20: Vanessa Bremerich typo in map_panels.html, broken link
- 2019-07-19: Tobias Fixes #4649 Internal proxy handling of relative URL segments breaks MS2
- 2019-07-19: Francesco Bartoli Fix geonode docker image version
- 2019-07-19: Athanasios Fotis Add some missing translation for Greek locale
- 2019-07-18: dependabot-preview[bot] Update pyproj requirement from <2.2.1.0,>=1.9.5 to >=1.9.5,<2.2.2.0
- 2019-07-18: dependabot-preview[bot] Bump decorator from 4.3.2 to 4.4.0
- 2019-07-18: dependabot-preview[bot] Update django-modeltranslation requirement
- 2019-07-18: dependabot-preview[bot] Bump mercantile from 1.0.4 to 1.1.1
- 2019-07-18: dependabot-preview[bot] Bump sqlalchemy from 1.3.3 to 1.3.5
- 2019-07-18: dependabot-preview[bot] Update elasticsearch requirement from <3.0.0,>=2.0.0 to >=2.0.0,<8.0.0
- 2019-07-18: dependabot-preview[bot] Bump readthedocs-sphinx-ext from 0.6.0 to 1.0.0
- 2019-07-18: dependabot-preview[bot] Bump jdcal from 1.4 to 1.4.1
- 2019-06-07: srto Make templates use static url set by Django
- 2019-07-18: afabiani Bump owslib from 0.17.1 to 0.18.0
- 2019-07-18: Florian Hoedt added gannebamm to authors
- 2019-07-18: afabiani [Fixes #4656] When DEBUG=False configured remote services should be PROXY ALLOWED
- 2019-07-17: afabiani [Fixes #4653] Use GeoServer 2.15.2
- 2019-07-17: dependabot-preview[bot] Bump pytest from 4.6.3 to 4.6.4
- 2019-07-17: dependabot-preview[bot] Bump kombu from 4.4.0 to 4.6.3
- 2019-07-17: dependabot-preview[bot] Bump django-cors-headers from 2.5.2 to 3.0.2
- 2019-07-17: dependabot-preview[bot] Update xmltodict requirement from <=0.10.2 to <0.12.1
- 2019-07-16: dependabot-preview[bot] Bump flake8 from 3.7.7 to 3.7.8
- 2019-07-16: dependabot-preview[bot] Update mock requirement from <=2.0.0 to <4.0.0
- 2019-07-15: afabiani - Bump to version 2.10.1 'maintenance' 0
- 2019-07-15: afabiani [Fixes #4639] Switch library "gsconfig" to "geoserver-restoconfig"
- 2019-07-12: Alessio Fabiani Add PULL_REQUEST_TEMPLATE.md
- 2019-07-12: dependabot-preview[bot] Bump oauthlib from 3.0.1 to 3.0.2
- 2019-07-12: dependabot-preview[bot] Bump lxml from 4.3.3 to 4.3.4
- 2019-07-11: Sylvain POULAIN Geoserver doesn't restart with https
- 2019-07-10: prbordon spanish translation update
- 2019-07-10: afabiani [Social Account Settings] LinkedIn profile fields updated
- 2019-07-10: afabiani - Updating README.md links to the doc
- 2019-07-10: Francesco Bartoli Align nginx tag value driven by .env variable (#4624)
- 2019-07-10: Francesco Bartoli Align nginx tag value driven by .env variable (#4623)
- 2019-07-10: afabiani - 2.10 stable release
- 2019-07-08: afabiani - Make sure the DB is available
- 2019-07-08: afabiani - Make sure we have updated migrations
- 2019-07-08: afabiani - Make sure the DB is available
- 2019-07-08: afabiani - Make sure the DB is available
- 2019-06-25: Alessio Fabiani Try spcgeonode with mapstore2 and postgis
- 2019-06-10: afabiani [SPCgeonode] GeoServer 2.14.3
- 2019-06-10: afabiani - Fix travis
+ 2019-11-12: dependabot-preview[bot] Bump urllib3 from 1.25.6 to 1.25.7
+ 2019-11-12: dependabot-preview[bot] Bump deprecated from 1.2.6 to 1.2.7
+ 2019-11-12: dependabot-preview[bot] Bump sqlalchemy from 1.3.10 to 1.3.11
+ 2019-11-11: dependabot-preview[bot] Bump pytest-django from 3.6.0 to 3.7.0
+ 2019-11-11: afabiani [Fixes #5223] Layer details broken if no store identified
+ 2019-11-11: dependabot-preview[bot] Bump tqdm from 4.37.0 to 4.38.0
+ 2019-11-11: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
+ 2019-11-11: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.5 to 1.4.6
+ 2019-11-11: dependabot-preview[bot] Bump django-mapstore-adapter from 1.0.11 to 1.0.12
+ 2019-11-11: dependabot-preview[bot] Bump twisted from 19.7.0 to 19.10.0
+ 2019-11-11: dependabot-preview[bot] Bump kombu from 4.6.5 to 4.6.6
+ 2019-11-09: Florian Hoedt added unicode tests for Menu, MenuItem and MenuPlaceholder
+ 2019-11-08: Alessio Fabiani Revert "Bump amqp from 2.5.1 to 2.5.2"
+ 2019-11-08: gioscarda [Fixes #5214] Fix regression on monitoring countries endpoint
+ 2019-11-08: afabiani [Fixes #5209] MapStore client critical slow-down with latest Chrome updates
+ 2019-11-08: afabiani [Hardening] layer_detail typo
+ 2019-11-08: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
+ 2019-11-08: gioscarda [Fixes #5203] avoid count label in countries, add missing flags
+ 2019-11-06: dependabot-preview[bot] Update six requirement from <1.11.0 to <1.14.0
+ 2019-11-06: dependabot-preview[bot] Bump psutil from 5.6.4 to 5.6.5
+ 2019-11-05: Florian Hoedt fixed pep8 errors
+ 2019-11-05: Florian Hoedt fixes #5197
+ 2019-11-04: dependabot-preview[bot] Bump psutil from 5.6.3 to 5.6.4
+ 2019-11-04: dependabot-preview[bot] Bump django from 1.11.25 to 1.11.26
+ 2019-11-04: dependabot-preview[bot] Bump python-dateutil from 2.8.0 to 2.8.1
+ 2019-11-04: afabiani - _resourcebase_info_panel is showing the tkeywords only if THESAURUS is enabled and adding commas between words
+ 2019-11-04: afabiani [Fixes #5181] Wrong words in French translation
+ 2019-10-31: Fanevanjanahary Changed wrong words in French version
+ 2019-11-04: afabiani - Fixes "field" rendering error when Thesauri have been enabled
+ 2019-11-01: dependabot-preview[bot] Bump tqdm from 4.36.1 to 4.37.0
+ 2019-11-01: Toni Schönbuchner [Fixes #5172] Add fix for import thesaurus
+ 2019-11-01: Toni Schönbuchner [Fixes #5172] Add docs for thesaurus
+ 2019-10-31: afabiani [Fixes #5158] Update thesaurus management command and fix autocomplete
+ 2019-10-30: afabiani [Fixes #4661] Replace Layer functionality incomplete/broken
+ 2019-09-05: capooti Mitigates possibilities for error #2932
+ 2019-10-30: dependabot-preview[bot] Bump setuptools from 41.5.1 to 41.6.0
+ 2019-10-30: Toni Schönbuchner [Fixes #5105] Error when using THESAURI
+ 2019-10-29: afabiani [Docs] Cleaning up misleading instructions
+ 2019-10-29: afabiani [Fixes #5136] Assignment of users to member groups should be done at activation time
+ 2019-10-29: capooti [Implements #5149] Add documentation for migrating GeoNode from 2.4 to 2.10.1
+ 2019-10-29: Alessio Fabiani [Fixes #5117] Publishing layers of two remote services with same (#5135)
+ 2019-10-29: dependabot-preview[bot] Bump setuptools from 41.5.0 to 41.5.1 (#5139)
+ 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix assertation error
+ 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix blank space
+ 2019-10-29: dependabot-preview[bot] Bump flake8 from 3.7.8 to 3.7.9
+ 2019-10-29: Toni Schönbuchner [Fixes #5138] Fix Flake8
+ 2019-10-29: Toni Schönbuchner [Fixes #5138] Escape Hierarchical-tags
+ 2019-10-29: Toni Schönbuchner [Fixes #5137] Striptags for service resources
+ 2019-10-28: dependabot-preview[bot] Bump setuptools from 41.4.0 to 41.5.0
+ 2019-10-28: dependabot-preview[bot] Bump decorator from 4.4.0 to 4.4.1
+ 2019-10-28: dependabot-preview[bot] Update django-modeltranslation requirement
+ 2019-10-25: capooti [Fixes #5121] ResourceBase API returns an error if a curated thumbnail img file is not existing anymore
+ 2019-10-25: Alessio Fabiani [Fixes #4827] Line layer uploaded to GeoNode is rendered as point layer (#5116)
+ 2019-10-24: Alessio Fabiani [Fixes #4827] Line layer uploaded to GeoNode is rendered as point layer (#5092)
+ 2019-10-24: dependabot-preview[bot] Update django-floppyforms requirement from <=1.7.0 to <1.9.0
+ 2019-10-23: capooti Enable thesauri api load only if a thesauri is defined
+ 2019-10-23: afabiani [Documentation] Fixed .env docker description for geonode-project
+ 2019-10-23: afabiani [Documentation] Fixed typo goenode to geonode
+ 2019-10-22: Alessio Fabiani Update settings.py
+ 2019-10-22: afabiani [Fixes #5096] Disabling geonode.monitoring causes model exceptions on views
+ 2019-10-22: dependabot-preview[bot] Bump pillow from 6.2.0 to 6.2.1
+ 2019-10-22: Florian Hoedt Update index.html
+ 2019-10-21: afabiani [Testing] Code Coverage / CHANGELOG updates
+ 2019-10-21: dependabot-preview[bot] Bump python-slugify from 3.0.6 to 4.0.0
+ 2019-10-21: dependabot-preview[bot] Bump psycopg2 from 2.8.3 to 2.8.4
+ 2019-10-21: dependabot-preview[bot] Bump django-celery-beat from 1.1.1 to 1.5.0
+ 2019-10-21: dependabot-preview[bot] Bump django-leaflet from 0.24.0 to 0.25.0
+ 2019-10-21: afabiani [Testing] Code Coverage
+ 2019-10-21: afabiani [Testing] Code Coverage
+ 2019-10-19: Toni [Fixes #5073] Added blank line
+ 2019-10-19: Toni [Fixes #5073] Update install docs
+ 2019-10-18: afabiani [Fixes #5070] Add Bing background layer config to MapStore2 adapter if apiKey is provided
+ 2019-10-18: afabiani [Hardening] coverage run integration tests too
+ 2019-10-18: Toni Schönbuchner [Fixes: #4659] Double message on Submit invite users
+ 2019-10-18: afabiani [Hardening] coverage run integration tests too
+ 2019-10-18: afabiani [Hardening] coverage run integration tests too
+ 2019-10-18: afabiani [Hardening] coverage run integration tests too
+ 2019-10-18: gioscarda [Fixes #5051] arrangement for geonode_logstash contrib app
+ 2019-10-17: afabiani [Hardening] A bit more robust logical check
+ 2019-10-17: afabiani [Hardening] Original Dataset link: skip checks for external links
+ 2019-10-17: afabiani [Hardening] A bit more robust logical check
+ 2019-10-17: afabiani [Fixes #4942] Download layer filter not sending CQL filter anymore
+ 2019-10-17: afabiani [Fixes #4956] "Original Dataset" Link behavior improvements
+ 2019-10-17: gioscarda [Fixes #5031] Disable monitoring by default
+ 2019-10-17: dependabot-preview[bot] Bump pytest-django from 3.5.1 to 3.6.0
+ 2019-10-16: afabiani [Documentation] Add "OAUTH2_API_KEY" setting to the settings list
+ 2019-10-15: afabiani - Hide "private" groups to "non-members"
+ 2019-10-16: Alessio Fabiani Update gnip.md
+ 2019-10-16: Toni Schönbuchner fix flake8 issue with _
+ 2019-10-16: Toni Schönbuchner #5054 | 5053 Update Translations
+ 2019-10-15: afabiani [Hardening] Fix backup/restore GeoServer rest endpoints
+ 2019-10-15: dependabot-preview[bot] Bump pytest from 4.6.5 to 4.6.6
+ 2019-10-13: Lilian Guimarães Remove plusone (Closes #4929)
+ 2019-10-11: dependabot-preview[bot] Bump python-slugify from 3.0.4 to 3.0.6
+ 2019-10-11: dependabot-preview[bot] Bump sqlalchemy from 1.3.9 to 1.3.10 (#5032)
+ 2019-10-11: Toni #4930 Remove Edit Data button for RASTER layer (#5035)
+ 2019-10-10: Paolo Corti Fixes #5005 (#5027)
+ 2019-10-09: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.3 to 1.4.4 (#5029)
+ 2019-10-08: afabiani [Documentation] Loading (and Processing) external data / importlayers and updatelayers Management Commands
+ 2019-10-08: afabiani [Documentation] Backporting GeoNode/GeoServer AA Sections - update GeoFence db config
+ 2019-10-08: afabiani [Documentation] Backporting GeoNode/GeoServer AA Sections
+ 2019-10-08: afabiani [Documentation] LDAP Contrib Module Documentation
+ 2019-10-08: afabiani [Hardening] Cleanup unneeded deps
+ 2019-10-08: Alessio Fabiani Adding LiliGuimaraes to CLA
+ 2019-10-07: dependabot-preview[bot] Bump django-mapstore-adapter from 1.0.8 to 1.0.9
+ 2019-10-07: dependabot-preview[bot] Bump sqlalchemy from 1.3.8 to 1.3.9
+ 2019-10-07: dependabot-preview[bot] Bump beautifulsoup4 from 4.8.0 to 4.8.1
+ 2019-10-07: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.2 to 1.4.3
+ 2019-10-07: dependabot-preview[bot] Bump pytz from 2019.2 to 2019.3
+ 2019-10-07: dependabot-preview[bot] Bump setuptools from 41.2.0 to 41.4.0
+ 2019-10-07: afabiani [Revert][Hardening] http_client caching requests
+ 2019-10-07: afabiani [Hardening] http_client caching requests
+ 2019-10-07: afabiani [Hardening] - Get rid of redoundant/unuseful methods
+ 2019-10-07: afabiani - Add 'spatialreference.org' to default settings
+ 2019-10-07: Toni [Fixes: #5000] Update Frontend section
+ 2019-10-06: Steffen Berger [Fixes #4954] only needed attributes added to cookie, when adding layer to cart (#4968)
+ 2019-10-06: Toni [Fixes: 4988] Rework static build process (#4989)
+ 2019-10-02: Alessio Fabiani [Hardening] Make master more resilient to errors
+ 2019-10-04: dependabot-preview[bot] Bump docker from 4.0.2 to 4.1.0
+ 2019-10-04: gioscarda [Fixes #4990] fix set_layers_permissions managament command
+ 2019-10-04: Alessio Fabiani Adding "sjohn-atenekom" to the authorized list of users
+ 2019-10-02: afabiani [Docs] Updating installation instructions for GeoNode Project
+ 2019-10-01: dependabot-preview[bot] Bump pillow from 6.1.0 to 6.2.0
+ 2019-10-01: dependabot-preview[bot] Bump django from 1.11.24 to 1.11.25
+ 2019-09-30: dependabot-preview[bot] Bump amqp from 2.5.1 to 2.5.2
+ 2019-09-30: dependabot-preview[bot] Bump kombu from 4.6.4 to 4.6.5
+ 2019-09-30: afabiani [Docs] update geonode-project setup instructions
+ 2019-09-30: Alessio Fabiani [Fixes #4959] By using UPLOADER=geonode.rest we are facing a timing issues on signals (#4960)
+ 2019-09-30: Alessio Fabiani [Fixes #4964] Date widget has gone (#4969)
+ 2019-09-27: Alessio Fabiani [Fixes #4949] UnicodeEncodeError in upload.models.update_from_session (#4958)
+ 2019-09-27: zoran995 Use Tomcat 8.5.46
+ 2019-09-27: dependabot-preview[bot] Update httplib2 requirement from <0.13.2 to <0.14.1
+ 2019-09-27: afabiani [Fixes #4739] Layer post_save deletes links to external thumbnails / data
+ 2019-09-26: afabiani [Hardening] Missing link name on "set_all_layers_metadata" mgmt command
+ 2019-09-26: afabiani [Hardening] Sanity checks on links number
+ 2019-09-26: afabiani [Hardening] Correct bbox checks on geoext JS
+ 2019-09-25: dependabot-preview[bot] Bump urllib3 from 1.25.5 to 1.25.6
+ 2019-09-23: root [Hardening] Make gslurp resilient to errors in case 'ignore_errors' has been specified
+ 2019-09-23: dependabot-preview[bot] Bump python-slugify from 3.0.3 to 3.0.4
+ 2019-09-23: Alessio Fabiani [Hardening] Favourite model _unicode resilient to None
+ 2019-09-23: Toni [Fixes: #4963] Fix wrong template include for all_auth
+ 2019-09-23: afabiani [Hardening] Add metadata links names to 'set_all_layers_metadata' mgmt cmd
+ 2019-09-23: afabiani [Hardening] Add metadata links names to 'set_all_layers_metadata' mgmt cmd
+ 2019-09-22: Toni Schönbuchner #4933 forgot password in modal
+ 2019-09-20: dependabot-preview[bot] Bump urllib3 from 1.25.3 to 1.25.5
+ 2019-09-20: afabiani [Fixes #4907] Add the possibility of enabling "captcha" on GeoNode signup form
+ 2019-09-19: afabiani [Hardening] Exposing to env AVATAR_GRAVATAR_SSL option
+ 2019-09-19: afabiani [Hardening] Exposing to env AVATAR_GRAVATAR_SSL option
+ 2019-09-19: afabiani Bump django-mapstore-adapter to 1.0.8
+ 2019-09-19: afabiani [Hardening] Removing redundant float conversion of coordinates
+ 2019-09-19: dependabot-preview[bot] Bump tqdm from 4.36.0 to 4.36.1
+ 2019-09-19: afabiani [Fixes #4924] Add member to Group is broken
+ 2019-09-19: Toni [Fixes: #4920] Footer Display Code on Detail Layer pages
+ 2019-09-18: Toni #fixes 4916 – show featured items
+ 2019-09-18: GeoSolutions [Hardening] Minor refactoring: check in a more pythonic way
+ 2019-09-18: dependabot-preview[bot] Bump tqdm from 4.35.0 to 4.36.0
+ 2019-09-18: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.3 to 1.0.4
+ 2019-09-18: Alessio Fabiani [Fixes #4911] On Monitoring plugin the uptime is not correctly computed (#4912)
+ 2019-09-18: afabiani - updating Italian translations
+ 2019-09-17: GeoSolutions [Hardening] Remove code warnings
+ 2019-09-17: GeoSolutions [Hardening] Remove code warnings
+ 2019-09-17: gioscarda [Fixes #4908] change monitoring label
+ 2019-09-17: afabiani [Hardening] Make 'set_all_layers_metadata' management command more robust
+ 2019-09-17: gioscarda [Fixes #4903] fixing hits count
+ 2019-09-14: afabiani [Hardening] Get rid of harmful logging
+ 2019-09-13: afabiani [Fixes #4790] Issue adding remote service with non-ascii characters in the service name - 'ascii' codec can't encode character u'\xe9' in position 8: ordinal not in range(128)
+ 2019-09-13: gioscarda [Fixes #4896] fixing data in response
+ 2019-09-13: afabiani [Hardening] Get rid of crazy cycle over the GeoServer catalog
+ 2019-09-12: afabiani [Fixes #4884] Attached SLD works only with ZIP files when "geonode.importer" method is enabled
+ 2019-09-13: afabiani [Hardening] Upgrade angular from 1.5.0 to 1.6.0
+ 2019-09-12: root [Hardeninig] Remove unuseful computation
+ 2019-09-12: afabiani [Hardening] Refresh js deps
+ 2019-09-12: afabiani [Fixes #4881] Improve initial generate thumbnail quality
+ 2019-09-12: afabiani [Fixes #4886] Permissions "Select2" widget not correctly forwarding values to the form
+ 2019-09-12: gioscarda [Fixes #4883] monitoring frontened refactoring
+ 2019-09-12: dependabot-preview[bot] Bump django-geonode-mapstore-client from 1.4.0 to 1.4.1
+ 2019-09-11: root [Hardening] Check for duplicates links on 'set_all_layers_metadata' management command
+ 2019-09-11: dependabot-preview[bot] Bump django-storages from 1.7.1 to 1.7.2
+ 2019-09-11: afabiani [Hardening] Javascript deps
+ 2019-09-11: gioscarda fixing monitoring tests
+ 2019-09-11: afabiani [Hardening] Javascript deps
+ 2019-09-10: capooti Fixes #4827
+ 2019-09-10: capooti Fixes #4846 and #4866
+ 2019-09-10: afabiani [Fixes #4863] Trying to assign permission to more than one users, fails
+ 2019-09-10: afabiani - Fixing integration test-cases
+ 2019-09-10: afabiani - Fixing integration test-cases
+ 2019-09-10: gioscarda [Fixes #4861] improved monitoring api filtering
+ 2019-09-10: afabiani - Fixing integration test-cases
+ 2019-09-10: afabiani - Fixing integration test-cases
+ 2019-09-10: afabiani [Fixes #4855] Download links always return whole world bbox
+ 2019-09-09: afabiani Bump gn-gsimporter from 1.0.10 to 1.0.12
+ 2019-09-09: afabiani Bump django-mapstore-adapter from 1.0.6 to 1.0.7
+ 2019-09-09: gioscarda [Fixes #4849] retrieve cpu and mem usage metric data
+ 2019-09-08: afabiani Fixes #4844
+ 2019-09-06: capooti Fixes #4844
+ 2019-09-06: gioscarda Added docs related new monitoring requests
+ 2019-09-06: root [Hardening] 'set_resource_default_links' avoid hitting GeoServer catalog if it is not needed
+ 2019-09-06: root [Hardening] HTTP connector resilient to SSL invalid certs
+ 2019-09-06: afabiani [JS] Fix GeoExplorer layer bbox parsing
+ 2019-09-06: gioscarda [Fixes #4842] Monitoring APIs improvements
+ 2019-09-06: afabiani [Hardening] More readable name for celery files results store / serialized JS async calls on LayerInfo upload prototype
+ 2019-09-06: afabiani Bump django-mapstore-adapter from 1.0.5 to 1.0.6
+ 2019-09-06: afabiani [Fixes #4831] GeoNode Importer mode not really asynchronous
+ 2019-09-05: dependabot-preview[bot] Bump geonode-oauth-toolkit from 1.1.4.5 to 1.1.4.6
+ 2019-09-05: root [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
+ 2019-09-05: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.2 to 1.0.3
+ 2019-09-05: afabiani [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
+ 2019-09-05: afabiani [Hardening] Reduce db connection idle timeout / Assert the GeoServer resource actually exists and throw an exception accordingly
+ 2019-09-04: Alessio Fabiani [Fixes #4813] Removing a layer is broken - Celery error (#4817)
+ 2019-09-04: afabiani [Fixes #4772] Get rid of deprecated "Publish local map layers as WMS layer group"
+ 2019-09-04: afabiani [Hardening] allow monitoring and serviceprocessors to perform internal requests against non verified SSL urls
+ 2019-09-04: afabiani [Minor] fix tooltip blinking on home page after bootstrap CSS updates
+ 2019-08-30: Alessio Fabiani Update .clabot
+ 2019-09-02: afabiani [Fixes #4809] Static assets still using Select2 3.5 instead of Select2 4
+ 2019-09-02: dependabot-preview[bot] Bump django from 1.11.23 to 1.11.24
+ 2019-09-02: Toni Schönbuchner updated angular, select2, bootstrap
+ 2019-09-01: afabiani - Using YARN instead of BOWER (deprecated) and update dependencies
+ 2019-08-30: Toni Add missing ngCookies when DEBUG_STATIC true
+ 2019-08-30: Alessio Fabiani Update .clabot
+ 2019-08-30: Alessio Fabiani Update .clabot
+ 2019-08-30: dependabot-preview[bot] Bump django-allauth from 0.39.1 to 0.40.0
+ 2019-08-29: afabiani OSGeo Project logo
+ 2019-08-29: afabiani Updating "CONTRIBUTING" policies ad instructions
+ 2019-08-29: Alessio Fabiani Update .clabot
+ 2019-08-29: Alessio Fabiani Update .clabot
+ 2019-08-29: Alessio Fabiani Update .clabot
+ 2019-08-29: Alessio Fabiani Create .clabot
+ 2019-08-29: Alessio Fabiani Delete signatures
+ 2019-08-29: Alessio Fabiani Create signatures
+ 2019-08-29: Alessio Fabiani Create CODE_OF_CONDUCT.md
+ 2019-08-28: dependabot-preview[bot] Bump sqlalchemy from 1.3.7 to 1.3.8
+ 2019-08-28: Toni Schönbuchner improved upload error handling
+ 2019-08-28: Toni Add sentence regarding minimal requirements
+ 2019-08-27: Toni Schönbuchner Removed autocomplete from password fields
+ 2019-08-26: dependabot-preview[bot] Bump tqdm from 4.34.0 to 4.35.0
+ 2019-08-21: gioscarda [Fixes #4759] Merging of the user analytics PR and resolving conflicts
+ 2019-08-22: afabiani Update issue templates
+ 2019-08-22: Alessio Fabiani Update issue templates
+ 2019-08-22: dependabot-preview[bot] Bump geoserver-restconfig from 1.0.1 to 1.0.2
+ 2019-08-22: dependabot-preview[bot] Update idna requirement from <2.7,>=2.5 to >=2.5,<2.9
+ 2019-08-22: afabiani Bump django-mapstore-adapter to 1.0.5
+ 2019-08-22: afabiani Removing Bower which is deprecated: installing Yarn instead
+ 2019-08-22: afabiani [Docs] Updating GeoNode release tag
+ 2019-08-22: dependabot-preview[bot] Bump setuptools from 41.1.0 to 41.2.0
+ 2019-08-21: afabiani Update django-tastypie requirement from <=0.14.0 to <0.15.0
+ 2019-08-21: dependabot-preview[bot] Bump pytest-bdd from 3.2.0 to 3.2.1
+ 2019-08-20: Alessio Fabiani [Fixes #4713] Attributes are not added in layers_attributes table when a layer is uploaded (#4751)
+ 2019-08-20: dependabot-preview[bot] Bump pytest-bdd from 3.1.1 to 3.2.0
+ 2019-08-20: dependabot-preview[bot] Bump tqdm from 4.33.0 to 4.34.0
+ 2019-08-19: dependabot-preview[bot] Update pyproj requirement from <2.2.2.0,>=1.9.5 to >=1.9.5,<2.2.3.0
+ 2019-08-19: dependabot-preview[bot] Update django-jsonfield requirement from <1.2.1 to <1.3.2
+ 2019-08-19: Alessio Fabiani Bump geonode-oauth-toolkit from 1.3.1 to 1.1.4.1rc0 (#4745)
+ 2019-08-16: dependabot-preview[bot] Bump kombu from 4.6.3 to 4.6.4
+ 2019-08-15: dependabot-preview[bot] Bump amqp from 2.5.0 to 2.5.1
+ 2019-08-15: dependabot-preview[bot] Bump sqlalchemy from 1.3.6 to 1.3.7
+ 2019-08-14: dependabot-preview[bot] Bump lxml from 4.4.0 to 4.4.1
+ 2019-08-14: dependabot-preview[bot] Bump setuptools from 41.0.1 to 41.1.0
+ 2019-08-14: afabiani - Fix typo
+ 2019-08-14: capooti Restore base/tests.py
+ 2019-08-14: capooti Implement GNIP curated-thumbs
+ 2019-08-14: afabiani Bump django-geonode-mapstore-client from 1.3.1 to 1.4.0
+ 2019-08-13: afabiani - Update spcgeonode template docker images
+ 2019-08-13: afabiani Bump gn-gsimporter from 1.0.9 to 1.0.10
+ 2019-08-13: afabiani - Update static assets
+ 2019-08-13: afabiani - Updated Italian translations
+ 2019-08-13: afabiani - Updated French translations
+ 2019-08-13: Toni Schönbuchner added missing imports
+ 2019-08-09: dependabot-preview[bot] Bump tqdm from 4.32.2 to 4.33.0
+ 2019-08-08: Toni Schönbuchner added defusedxml requirements.txt
+ 2019-08-08: Toni Schönbuchner used defusedxml for parse and fromstring
+ 2019-08-07: dependabot-preview[bot] Bump mercantile from 1.1.1 to 1.1.2
+ 2019-08-07: dependabot-preview[bot] Bump pytest from 4.6.4 to 4.6.5
+ 2019-08-07: dependabot-preview[bot] Bump invoke from 1.2.0 to 1.3.0
+ 2019-08-06: dependabot-preview[bot] Bump twisted from 19.2.1 to 19.7.0
+ 2019-08-01: dependabot-preview[bot] Bump pytz from 2019.1 to 2019.2
+ 2019-08-01: dependabot-preview[bot] Bump django from 1.11.22 to 1.11.23
+ 2019-07-31: Joseph Stachelek typo fix
+ 2019-07-30: dependabot-preview[bot] Bump coverage from 4.5.3 to 4.5.4
+ 2019-07-29: dependabot-preview[bot] Bump python-slugify from 3.0.2 to 3.0.3
+ 2019-07-29: dependabot-preview[bot] Bump lxml from 4.3.4 to 4.4.0
+ 2019-07-29: dependabot-preview[bot] Update httplib2 requirement from <0.13.1 to <0.13.2
+ 2019-07-26: Alessio Fabiani Update stale.yml
+ 2019-07-26: Alessio Fabiani Create stale.yml
+ 2019-07-25: afabiani - Update django-geonode-mapstore-client
+ 2019-07-24: Francesco Pennica updated some strings in the italian translation
+ 2019-07-24: dependabot-preview[bot] Bump beautifulsoup4 from 4.7.1 to 4.8.0
+ 2019-07-23: dependabot-preview[bot] Update django-modeltranslation requirement
+ 2019-07-23: Sylvain POULAIN Update django.po
+ 2019-07-23: dependabot-preview[bot] Bump sqlalchemy from 1.3.5 to 1.3.6
+ 2019-07-22: dependabot-preview[bot] Bump django-polymorphic from 2.0.3 to 2.1.2
+ 2019-07-22: dependabot-preview[bot] Bump pytest-bdd from 3.1.0 to 3.1.1
+ 2019-07-22: dependabot-preview[bot] Bump docker from 3.7.0 to 4.0.2
+ 2019-07-22: dependabot-preview[bot] Bump parse-type from 0.4.1 to 0.5.2
+ 2019-07-22: dependabot-preview[bot] Bump amqp from 2.4.2 to 2.5.0
+ 2019-07-22: dependabot-preview[bot] Update httplib2 requirement from <=0.10.3 to <0.13.1
+ 2019-07-22: afabiani - docker-compose uses image: geonode/geonode:latest
+ 2019-07-20: Vanessa Bremerich typo in map_panels.html, broken link
+ 2019-07-19: Tobias Fixes #4649 Internal proxy handling of relative URL segments breaks MS2
+ 2019-07-19: Francesco Bartoli Fix geonode docker image version
+ 2019-07-19: Athanasios Fotis Add some missing translation for Greek locale
+ 2019-07-18: dependabot-preview[bot] Update pyproj requirement from <2.2.1.0,>=1.9.5 to >=1.9.5,<2.2.2.0
+ 2019-07-18: dependabot-preview[bot] Bump decorator from 4.3.2 to 4.4.0
+ 2019-07-18: dependabot-preview[bot] Update django-modeltranslation requirement
+ 2019-07-18: dependabot-preview[bot] Bump mercantile from 1.0.4 to 1.1.1
+ 2019-07-18: dependabot-preview[bot] Bump sqlalchemy from 1.3.3 to 1.3.5
+ 2019-07-18: dependabot-preview[bot] Update elasticsearch requirement from <3.0.0,>=2.0.0 to >=2.0.0,<8.0.0
+ 2019-07-18: dependabot-preview[bot] Bump readthedocs-sphinx-ext from 0.6.0 to 1.0.0
+ 2019-07-18: dependabot-preview[bot] Bump jdcal from 1.4 to 1.4.1
+ 2019-06-07: srto Make templates use static url set by Django
+ 2019-07-18: afabiani Bump owslib from 0.17.1 to 0.18.0
+ 2019-07-18: Florian Hoedt added gannebamm to authors
+ 2019-07-18: afabiani [Fixes #4656] When DEBUG=False configured remote services should be PROXY ALLOWED
+ 2019-07-17: afabiani [Fixes #4653] Use GeoServer 2.15.2
+ 2019-07-17: dependabot-preview[bot] Bump pytest from 4.6.3 to 4.6.4
+ 2019-07-17: dependabot-preview[bot] Bump kombu from 4.4.0 to 4.6.3
+ 2019-07-17: dependabot-preview[bot] Bump django-cors-headers from 2.5.2 to 3.0.2
+ 2019-07-17: dependabot-preview[bot] Update xmltodict requirement from <=0.10.2 to <0.12.1
+ 2019-07-16: dependabot-preview[bot] Bump flake8 from 3.7.7 to 3.7.8
+ 2019-07-16: dependabot-preview[bot] Update mock requirement from <=2.0.0 to <4.0.0
+ 2019-07-15: afabiani - Bump to version 2.10.1 'maintenance' 0
+ 2019-07-15: afabiani [Fixes #4639] Switch library "gsconfig" to "geoserver-restoconfig"
+ 2019-07-12: Alessio Fabiani Add PULL_REQUEST_TEMPLATE.md
+ 2019-07-12: dependabot-preview[bot] Bump oauthlib from 3.0.1 to 3.0.2
+ 2019-07-12: dependabot-preview[bot] Bump lxml from 4.3.3 to 4.3.4
+ 2019-07-11: Sylvain POULAIN Geoserver doesn't restart with https
+ 2019-07-10: prbordon spanish translation update
+ 2019-07-10: afabiani [Social Account Settings] LinkedIn profile fields updated
+ 2019-07-10: afabiani - Updating README.md links to the doc
+ 2019-07-10: Francesco Bartoli Align nginx tag value driven by .env variable (#4624)
+ 2019-07-10: Francesco Bartoli Align nginx tag value driven by .env variable (#4623)
+ 2019-07-10: afabiani - 2.10 stable release
+ 2019-07-08: afabiani - Make sure the DB is available
+ 2019-07-08: afabiani - Make sure we have updated migrations
+ 2019-07-08: afabiani - Make sure the DB is available
+ 2019-07-08: afabiani - Make sure the DB is available
+ 2019-06-25: Alessio Fabiani Try spcgeonode with mapstore2 and postgis
+ 2019-06-10: afabiani [SPCgeonode] GeoServer 2.14.3
+ 2019-06-10: afabiani - Fix travis
2019-03-08: Francesco Frassinelli Test standard docker setup with geonode-selenium
## [2.10](https://github.com/GeoNode/geonode/tree/2.10.0-release) (2019-07-10)
@@ -1092,1205 +1185,1205 @@ Python >3.9 is required to run GeoNode 4.1.0, since many of its dependencies hav
## [Full Changelog](https://github.com/GeoNode/geonode/compare/2.8.1...2.10.0-release)
- 2019-07-10: afabiani - 2.10 stable release
- 2019-07-09: dependabot-preview[bot] Bump psutil from 5.6.1 to 5.6.3
- 2019-07-09: afabiani - GeoServer Render Thumbnails using the correct auth headers
- 2019-07-09: dependabot-preview[bot] Bump pillow from 6.0.0 to 6.1.0
- 2019-07-09: dependabot-preview[bot] Bump tqdm from 4.31.1 to 4.32.2
- 2019-07-09: afabiani - Improve Monitoring Request Filter Logging
- 2019-07-09: afabiani - use correct resolved object when saving metadata
- 2019-07-09: afabiani - use correct resolved object when saving metadata
- 2019-07-08: dependabot-preview[bot] Bump pytz from 2018.9 to 2019.1
- 2019-07-08: dependabot-preview[bot] Bump splinter from 0.10.0 to 0.11.0
- 2019-07-08: Florian Hoedt switched to latest tags for docker images to
- 2019-07-05: Sylvain POULAIN Update Dockerfile
- 2019-07-04: dependabot-preview[bot] [Security] Bump django from 1.11.21 to 1.11.22
- 2019-07-02: dependabot-preview[bot] Bump pytest-django from 3.4.8 to 3.5.1
- 2019-07-02: dependabot-preview[bot] Bump python-slugify from 3.0.1 to 3.0.2
- 2019-07-02: dependabot-preview[bot] Bump readthedocs-sphinx-ext from 0.5.17 to 0.6.0
- 2019-07-02: Sylvain POULAIN Solve letsencrypt ssl error
- 2019-07-01: Toni Added numpy for SPC
- 2019-07-01: Toni Added numpy to requirements.txt
- 2019-07-01: Toni Simplify pygdal installation
- 2019-06-30: Francesco Bartoli Add dev mode with regular Docker (#4514)
- 2019-06-30: Toni Schönbuchner fixed typos on start
- 2019-06-29: Toni Schönbuchner added ansible to docs
- 2019-06-28: Lorenzo Pini Add vim text editor, fix typos
- 2019-06-28: gioscarda [docs] fixing typos in basic index
- 2019-06-28: gioscarda [docs] GeoNode Project Themes
- 2019-06-28: afabiani [Docs] Getting Started part 4
- 2019-06-28: afabiani [Docs] Getting Started part 3
- 2019-06-28: afabiani [Docs] Getting Started part 2
- 2019-06-28: afabiani [Docs] Getting Started part 1
- 2019-06-28: afabiani [Docs] Removing misleading contents
- 2019-06-28: afabiani [Docs] Removing misleading contents
- 2019-06-28: afabiani [Docs] Administering GeoNode: OAuth2 Access Tokens management
- 2019-06-28: afabiani [Docs] finalize geonode-project debug setup and startup section
- 2019-06-27: afabiani [Docs] Monitoring
- 2019-06-27: capooti Implement #4429
- 2019-06-27: afabiani [Docs] Manage the base metadata choices using the admin panel
- 2019-06-27: afabiani [Docs] Announcemnts and Dismissals
- 2019-06-27: afabiani - Add AnnouncementPermissionsBackend to default AUTHENTICATION_BACKENDS
- 2019-06-27: afabiani [Closes #4584] Allow admins to decide if the Topic Category should be mandatory or not
- 2019-06-27: afabiani [Closes #4584] Allow admins to decide if the Topic Category should be mandatory or not
- 2019-06-27: gioscarda [docs] added groups management sections
- 2019-06-27: afabiani [Static assets] Refresh/update static assets
- 2019-06-27: afabiani [Dependencies] Updating monitoring static assets in order to remove vulnerabilities
- 2019-06-26: Lorenzo Pini Update index.rst
- 2019-06-26: gioscarda [docs] group categories and group profiles
- 2019-06-26: afabiani [Fixes #4579] Set Thumbnail tool broken (GeoExplorer only)
- 2019-06-26: afabiani - Allow members_only announcements also
- 2019-06-26: afabiani [Docs] Manage Profiles, Layers, Maps and Documents from the Admin panel
- 2019-06-26: afabiani - Show Contact Roles again on the Profile Admin panel
- 2019-06-26: afabiani [Docs] Group based advanced data workflow
- 2019-06-26: afabiani [Fixes #4575] Group Category summary page does not show associated group details
- 2019-06-26: gioscarda [docs] manage users from admin
- 2019-06-25: geosolutions [Optimization] optimize thumb generation algo
- 2019-06-25: gioscarda [Docs] fixing typos
- 2019-06-25: gioscarda accessing admin panel, change admin password
- 2019-06-25: afabiani [Docs] Administering > Simple Theming
- 2019-06-25: afabiani [Docs] Administering > Simple Theming (work in progress)
- 2019-06-25: afabiani [Docs] Administering > Simple Theming (work in progress)
- 2019-06-25: gioscarda fixing typos
- 2019-06-25: gioscarda remote service types in user guide
- 2019-06-25: Lorenzo Pini Typos in installation document
- 2019-06-25: Toni Fixed SESSION_EXPIRED_CONTROL_ENABLED to True
- 2019-06-25: gioscarda linking docs with resources explanation in user guide
- 2019-06-25: afabiani [Docs] Administering index structure / Change the default langs
- 2019-06-25: gioscarda fixing typos
- 2019-06-25: gioscarda exif images docs
- 2019-06-25: afabiani [Docs] About GeoNode / SPC GeoNode
- 2019-06-25: gioscarda publishing data usage docs
- 2019-06-24: Angelos Tzotsos Reclassifying requirements based on Ubuntu available packages
- 2019-06-24: afabiani - fix travis
- 2019-06-24: dependabot-preview[bot] Update django-jsonfield requirement from <=1.0.1 to <1.2.1
- 2019-06-24: dependabot-preview[bot] Bump owslib from 0.16.0 to 0.17.1
- 2019-06-24: afabiani [hardening] make sure we do not fall into dangeruos huge loops
- 2019-06-24: dependabot-preview[bot] Bump django-activity-stream from 0.7.0 to 0.8.0
- 2019-06-24: afabiani Bump Django MapStore Adapter to version 1.0.3
- 2019-06-21: Toni Fixed Typos
- 2019-06-21: afabiani - GeoServer Proxy should allow PUBLIC_LOCATION to fetch security headers too
- 2019-06-21: gioscarda managing_maps
- 2019-06-21: afabiani - Fix travis tests
- 2019-06-21: afabiani - Fix GeoServer version to 2.14.3
- 2019-06-21: afabiani - Travis-selenium tentative fix
- 2019-06-21: afabiani [Fixes #4552] Improve/fix thumbnail generation for Layers and Maps at creation time
- 2019-06-21: afabiani [Fixes #4553] Exception thrown when trying to upload features with non UTF-8 encoding
- 2019-06-20: afabiani - Travis-selenium tentative fix
- 2019-06-20: afabiani - Travis-selenium tentative fix
- 2019-06-19: capooti Fixes #4534
- 2019-06-19: afabiani - spcgeonode settings tune up
- 2019-06-19: afabiani [Fixes #4547] "set_all_layers_metadata" deletes Thumbnail metadata links
- 2019-06-19: dependabot-preview[bot] Bump pytest from 4.3.1 to 4.6.3
- 2019-06-18: afabiani [Fixes #4545] MapStore2 print button broken with 1.1 client release
- 2019-06-18: afabiani [Fixes #4543] Enforce GeoNode REST service API security
- 2019-06-18: Alessio Fabiani [Closes #4532] Add search filters for groups and group categories (#4533)
- 2019-06-18: dependabot-preview[bot] [Security] Bump twisted from 18.9.0 to 19.2.1
- 2019-06-18: dependabot-preview[bot] Update pyproj requirement from <=1.9.5.1,>=1.9.5 to >=1.9.5,<2.2.1.0
- 2019-06-18: dependabot-preview[bot] [Security] Bump urllib3 from 1.24.1 to 1.24.2
- 2019-06-18: afabiani [Fixes #4541] Wrong permissions inconsistency message when changing layer permisions
- 2019-06-18: Francesco Frassinelli Use python_requires
- 2019-06-18: afabiani [Fixes #4538] Guardian returns the whole list of permissions associated with a resource.
- 2019-06-18: dependabot-preview[bot] Bump pillow from 5.4.1 to 6.0.0
- 2019-06-17: dependabot-preview[bot] Bump setuptools from 40.8.0 to 41.0.1
- 2019-06-17: afabiani - updating requirements: django_geoexplorer-4.0.43
- 2019-06-17: afabiani [Closes #4519] Option to regenerate resource links at migration or not
- 2019-06-15: afabiani - updating requirements
- 2019-06-15: afabiani - Fix get started link on index page
- 2019-06-15: Toni Schönbuchner removed opensource heart
- 2019-06-15: Toni Schönbuchner added image again
- 2019-06-15: Toni Added logo, correct 404 to new docs.
- 2019-06-14: afabiani Fix build
- 2019-06-14: afabiani - Minor Adjustments to README and settings
- 2019-06-14: Toni Added t-book to authors.
- 2019-06-13: Francesco Frassinelli Add new geotiff for Travis CI (#4507)
- 2019-06-13: Toni Push Travis again.
- 2019-06-13: Florian Hoedt changed basemap for thumbnails to wikimedia, fixes #4459
- 2019-06-13: Toni Schönbuchner Fixes: #4478
- 2019-06-13: Florian Hoedt defuzzyied the translations
- 2019-06-13: Florian Hoedt Update docker-compose.yml
- 2019-06-13: Florian Hoedt fixes #4354
- 2019-06-13: Florian Hoedt added postgis 10.x, importer, pgdumper for geo/nogeo dbs
- 2019-06-13: Florian Hoedt merge with global master (#11)
- 2019-06-12: afabiani [Fixes #4487] Issue adding remote services - FAILED - could not convert string to float: EPSG:4326
- 2019-06-13: Toni Updated arabic translation
- 2019-06-13: Toni Fixed OpenSource Image
- 2019-06-12: florian fixes geoserver auth button #4335
- 2019-06-12: florian fixes geoserver auth button #4335
- 2019-06-12: Toni Fixed Readme Header
- 2019-06-12: ppasq Make the menu item Invite users available for authenticated users only
- 2019-06-12: Francesco Bartoli Close #4472
- 2019-06-12: Francesco Frassinelli Add ISSUE_TEMPLATE.md
- 2019-06-12: ppasq Make the user message disappear after 5 secs
- 2019-06-12: ppasq Fix less file not aligned to corresponding css
- 2019-06-09: afabiani - Docker optimizations
- 2019-06-07: afabiani - Fix travis
- 2019-06-06: afabiani [Links] Fix Metadata Catalogue Records Update
- 2019-06-06: afabiani - Fix travis
- 2019-06-06: afabiani [Links] fix download links bbox generation
- 2019-06-06: afabiani - Travis fix
- 2019-06-06: afabiani - Travis fix
- 2019-06-06: afabiani - Travis fix
- 2019-06-06: afabiani [Links] Remove old instances breaking the command execution
- 2019-06-06: afabiani [Proxy Hotfix] self.username overrides user param
- 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
- 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
- 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
- 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
- 2019-06-06: Alessio Fabiani Update requirements.txt
- 2019-06-06: srto Store filename returned by storage API
- 2019-06-05: Tobi Use Django file storage API to construct media URL
- 2019-06-05: afabiani [Settings] Removing refuses
- 2019-06-05: afabiani [Monitoring] relax host name constraint
- 2019-06-03: afabiani - Travis fixes
- 2019-06-03: afabiani - Improve docker build
- 2019-06-03: afabiani [Fixes #4450] Thumbnails on layers not showing
- 2019-06-03: afabiani [Settings] Restoring /gs to /geoserver by default
- 2019-05-30: afabiani - MapStore2 Client as default
- 2019-05-30: afabiani - MapStore2 Client as default
- 2019-05-29: afabiani - MapStore2 Client as default
- 2019-05-30: afabiani [Fixes #4447] Database locked when using sqlite/spatialite databases
- 2019-05-30: Toni Schönbuchner fixes #4440 correct float on people profile small screens
- 2019-05-30: Toni Schönbuchner Fixes: #4443 issues translations on index
- 2019-05-29: afabiani - Removing contrib apps deps
- 2019-05-29: afabiani [Monitoring] Decimal digits on model
- 2019-05-28: afabiani [Fixes #4437] [Monitoring] collect_metrics often timeouts
- 2019-05-28: afabiani [Fixes #4352] TemplateSyntaxError at trans in backups/confirm_cancel.html
- 2019-05-28: afabiani - Fix travis
- 2019-05-28: Alessio Fabiani [Fixes #4428] refactor linkedin field extraction (#4435)
- 2019-05-28: afabiani - simplify the PR changes and get rid of UPDATE_THUMBS_ON_STYLE_CHANGE additional setting
- 2019-05-28: afabiani [Minor] Hardening GeoNode checks and settings
- 2019-05-28: afabiani [Minor] Hardening GeoNode checks and settings
- 2019-05-27: afabiani - Fix Tests
- 2019-05-27: afabiani [Minor] Hardening GeoNode checks
- 2019-05-27: afabiani - Fix Tests
- 2019-05-27: afabiani GeoNode Cleanup and Test Coverage
- 2019-05-27: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / database_shards,worldmap,geotiffio,geosites extracted
- 2019-05-27: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / favorite,exif,monitoring promoted / slack,nlp,mp removed
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
- 2019-05-23: capooti Add UPDATE_THUMBS_ON_STYLE_CHANGE setting - refs #4092
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: Jonathan Doig Fix TemplateSyntaxError at trans
- 2019-05-23: Jonathan Doig Fix Geonode issue 4352
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
- 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
- 2019-05-22: afabiani [Minor Improvements] Few more checks with the email backend
- 2019-05-22: afabiani [Fixes #4424] Reverting back to GeoServer 2.14.2
- 2019-05-21: afabiani - Convert pinax templates bodies to HTML
- 2019-05-21: geosolutions - Custom PINAX Email Backend: allows html notifications
- 2019-05-20: afabiani [Fixes #4417] 'ows_api' for GetCapabilities, should provide public urls anyway
- 2019-05-20: afabiani [Fixes #4418] General SLD management misbehavior
- 2019-05-19: Alessio Fabiani Update requirements.txt
- 2019-05-19: afabiani [Hardenining] Avoid hard failure if GeoServer resource is not reachable
- 2019-05-19: afabiani [Closes #4415] Upgrade PyCSW version to 2.4.0
- 2019-05-17: afabiani [Fixes #4410] Avoid re-writing thumbnail every time a Layer is saved
- 2019-05-17: Francesco Bartoli Revert "Allow for https as public protocol"
- 2019-05-17: afabiani [Closes #4408] Expose 'settings' variable to env
- 2019-05-17: afabiani [Monitoring] timeout decorator to collect_metrics management command; avoids stucking on db connections
- 2019-05-17: Jonathan Doig Updates as requested for Geonode PR 4403
- 2019-05-17: Jonathan Doig Infer protocol from port if defined
- 2019-05-17: afabiani [Monitoring] timeout decorator to collect_metrics management command; avoids stucking on db connections
- 2019-05-16: afabiani [Close #4405] [GeoNode 2.10] Upgrade GeoServer version to 2.15.1
- 2019-05-16: afabiani [Fixes #4404] Performance issue with resource base apis
- 2019-05-16: afabiani [Maps Model] Removing CharField 200 length limitation
- 2019-05-16: Jonathan Doig Add default GEONODE_LB_PROTOCOL to docker-compose files
- 2019-05-16: Jonathan Doig Allow for https as public protocol
- 2019-05-15: afabiani [Minor fix] Do not break the map if cannot read the syles from GeoServer
- 2019-05-15: afabiani [Minor updates] Aligning with 2.8.2
- 2019-05-15: afabiani [Release] GeoNode 2.8.1
- 2019-05-15: afabiani [Release] Preparing 2.8rc1
- 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
- 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
- 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
- 2019-05-13: Hayden Elza Spelling
- 2019-05-13: afabiani - Removing geonode.contrig.sites app by default
- 2019-05-13: afabiani [Minor fix] do not overwrite thumbnail if it already exists
- 2019-05-10: geosolutions [Template] minor improvements
- 2019-05-10: afabiani - More Theme options
- 2019-05-10: afabiani [Themes] Add more nav customization options
- 2019-05-10: geosolutions [Template] typo
- 2019-05-10: afabiani [Themes] Add more nav customization options
- 2019-05-10: afabiani [Minor fixes] settings typos and fixoauth mgt commands
- 2019-05-10: afabiani [Minor fixes] settings typos and fixoauth mgt commands
- 2019-05-10: afabiani [Minor improvements] Allow requests get to use timeouts / expose Monitoring variables
- 2019-05-09: Alessio Fabiani Update local_settings.py.geoserver.sample
- 2019-05-09: Alessio Fabiani revert wrong setting
- 2019-05-09: afabiani [Minor optimizations] settings X_FRAME_OPTIONS allow localhost / add geoserver local services paths to geoserver proxy
- 2019-05-08: afabiani - [Minor fix] Proxy will attach access_token only if remote netloc matches the local one
- 2019-05-06: Angelos Tzotsos Update Greek translations
- 2019-05-04: Angelos Tzotsos Regenerate Greek translations
- 2019-05-04: Alessio Fabiani Remove refuses from index.html
- 2019-05-03: afabiani [Closes #4384] Add a Cookies & Privacy policy customization section to GeoNode Themes
- 2019-05-02: afabiani [Minor Refactoring] Avoid code duplication: centralize 'create_geoserver_db_featurestore' on geoserver helpers classes only
- 2019-05-02: Toni Schönbuchner Rebuild static files, missing in #4380
- 2019-04-30: afabiani [Minor fix] Trying to parse geoserver error messages in a cleanest way
- 2019-04-30: Toni Delete dsgvo.html
- 2019-04-30: Florian Hoedt fixes profile text overflow UI glitch #4376
- 2019-04-29: afabiani [Minor] Improves Proxy response error status message
- 2019-04-29: afabiani [Minor fix] allow sld POST request since the user might want to create a new style
- 2019-04-29: afabiani [Closes #4377] Introduce a 'style-check' endpoint for style edit permission checks
- 2019-04-29: Ahmed Nour Eldeen update geosites contrib app to be work with geonode 2.10 (#4287)
- 2019-04-28: Toni Schönbuchner Fixes: #4374, added missing quote to trans
- 2019-04-28: Toni Update German translation
- 2019-04-26: afabiani [Closes #4369] List of strings without trans tags and not listed in .po file
- 2019-04-25: Toni Schönbuchner Fix #4368 Announcement text layout overlaps on index.html
- 2019-04-24: afabiani - fix test case
- 2019-04-23: capooti Bump django-geoexplorer-worldmap 4.0.67
- 2019-04-23: afabiani - django-geoexplorer 4.0.42: Fixing GeoServerStyleWriter plugin
- 2019-04-23: afabiani [Fixes #4357] tags do not support unicode text
- 2019-04-23: afabiani - Fix adv settings docs titeling
- 2019-04-23: afabiani - minor integrity checks
- 2019-04-23: afabiani - Fixes/improx as per reviews
- 2019-04-23: afabiani - pin urllib3 version conflicting with geonode-oauth-toolkit requirements
- 2019-04-18: afabiani - Bump version to 2.10rc5
- 2019-04-15: Angelos Tzotsos Settings fix for geosites, patch from debian package
- 2019-04-15: Angelos Tzotsos Fixed missing debian changelog records
- 2019-04-12: Toni Schönbuchner #4338 fix thumbnail creation with different bbox
- 2019-04-10: afabiani - Geonode logo as favicon
- 2019-04-10: afabiani - Text fixes
- 2019-04-10: afabiani [Fixes #4321] GeoNode is not able to show errors properly on upload / [Closes #4241] Upload failure: saved_layer is None
- 2019-04-09: afabiani - Tentative fix tests
- 2019-04-09: afabiani [Issue #4325] List of documentation improvements
- 2019-04-09: afabiani - Docs: updates to advanced settgins and docs version
- 2019-04-09: afabiani - Add 'Syncronize button immediately' to Layers List too
- 2019-04-08: hisham waleed karam add outh test
- 2019-04-09: afabiani - Added docs for DELAYED SECURITY settings also
- 2019-04-09: afabiani - SESSION_EXPIRED var to False by Default
- 2019-04-08: afabiani - Restore session expire messages, but using cookie storeage instead of session
- 2019-04-06: Toni Schönbuchner #4348 fix failing read_the_docs
- 2019-04-05: Francesco Frassinelli 8 KB buffer for uwsgi
- 2019-04-04: afabiani - Fix test cases
- 2019-04-04: Francesco Frassinelli 8 KB buffer for spc uwsgi
- 2019-04-04: Francesco Frassinelli Set SESSION_EXPIRED_CONTROL_ENABLED for SPC
- 2019-04-04: afabiani [Fixes #4322] SESSION_EXPIRED_CONTROL_ENABLE=True breaks GeoNode
- 2019-04-04: Francesco Frassinelli Polish tests
- 2019-04-04: Francesco Frassinelli Remove failing test
- 2019-04-04: afabiani [Fixes #4322] SESSION_EXPIRED_CONTROL_ENABLE=True breaks GeoNode
- 2019-04-04: Francesco Frassinelli GeoServer web UI url default fixed
- 2019-04-03: afabiani GNIP: Improvements to GeoNode Layers download links - original dataset option in a migration
- 2019-04-03: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
- 2019-04-03: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
- 2019-04-03: afabiani [Fixes #4333] Upload from GUI doesn't handle projection properly
- 2019-04-03: Francesco Bartoli Change conversion to 5-digits round
- 2019-04-03: Francesco Bartoli Convert for integer comparison
- 2019-04-03: afabiani [Fixes #4333] Upload from GUI doesn't handle projection properly
- 2019-04-01: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
- 2019-03-29: afabiani - Show Original Data Set links also
- 2019-03-29: afabiani [Closes #4323] Layers Download Links need some care
- 2019-03-28: Jeremiah Cooper Remove awesome_slugify and all references to it, replace with python-slugify which is a newer and still active replacement
- 2019-03-28: Francesco Frassinelli Revert "add async_thumbnail to improve upload time"
- 2019-03-26: Jeremiah Cooper Allow Travis CI to run on the 2.20.x branch
- 2019-03-26: Jeremiah Cooper Remove unused dependency coreschema
- 2019-03-26: Jeremiah Cooper Upgrade django-polymorphic from version 1.3.1 to 2.0.3
- 2019-03-26: Jeremiah Cooper Upgrade Pillow from version 3.3.2 to version 5.4.1
- 2019-03-26: Francesco Frassinelli Fix GeoServer wait for Postgres
- 2019-03-26: Jeremiah Cooper Update pinax-notifications from 4.1.0 to 5.0.3.
- 2019-03-26: afabiani [Closes #4312] Remove GeoGig and api_basemaps contrib apps integrated stuff
- 2019-03-22: afabiani - [Closes #4308] Allow users to refresh layer attributes and statistics through both GeoNode UI and Management Commands
- 2019-03-21: Jeremiah Cooper Upgrade invoke from 0.22.1 to 1.2.0
- 2019-03-21: Jeremiah Cooper Upgrade oauthlib from version 2.1.0 to version 3.0.1
- 2019-03-21: Jeremiah Cooper Add 2.20 to the list of branches that Travis CI runs tests against.
- 2019-03-21: Jeremiah Cooper Remove nose as we're not using it for any of the tests anymore.
- 2019-03-21: Jeremiah Cooper Update TEST_RUN_INTEGRATION_BDD to install geckodriver and run firefox as headless.
- 2019-03-20: Jeremiah Cooper Flake8 fixes.
- 2019-03-20: Jeremiah Cooper Ensure selenium with the firefox geckodriver web driver is available for BDD tests.
- 2019-03-20: Jeremiah Cooper Resolve failing BDD tests.
- 2019-03-20: Jeremiah Cooper The latest version of splinter no longer supports phantomjs, use the default firefox driver instead.
- 2019-03-20: Jeremiah Cooper Don't install specific versions of the testing dependencies for BDD tests, use what we already have in requirements.txt
- 2019-03-20: Jeremiah Cooper Resolve remaining flake8 issues.
- 2019-03-20: Jeremiah Cooper Resolve all W605 invalid escape sequence flake8 errors. https://lintlyci.github.io/Flake8Rules/rules/W605.html
- 2019-03-20: Jeremiah Cooper Newer versions of flake8 are stricter and have more findings. Use autopep8 to resolve some of these.
- 2019-03-20: Jeremiah Cooper Modify flake8 settings to extend the default ignore list, instead of completely overwriting it.
- 2019-03-19: Francesco Bartoli Align all settings with GEOSERVER_WEB_UI_LOCATION default value
- 2019-03-19: Francesco Bartoli Fix #4298
- 2019-03-19: Jeremiah Cooper Clean up unused testing dependencies and upgrade current testing dependencies
- 2019-03-19: Jeremiah Cooper Remove unused jenkins files and pycodestyle dependency.
- 2019-03-19: afabiani - GEOSERVER_WEB_UI_LOCATION default value should point to localhost 8080
- 2019-03-19: afabiani - Typo on request headers key retrieval
- 2019-03-19: afabiani - Introducing Proxy Max Retries Option
- 2019-03-19: afabiani - Fix Travis
- 2019-03-18: Jeremiah Cooper Update requirements_docs.txt to reflect the latest dependency changes.
- 2019-03-18: Jeremiah Cooper Remove unnecessary Ubuntu package comments.
- 2019-03-18: afabiani - Restore codecov status check
- 2019-03-18: afabiani [Fixes #4293] CSW download link: metadata are empty
- 2019-03-18: Jeremiah Cooper Upgrade various python dependencies to their latest compatible versions.
- 2019-03-18: afabiani - 'base.auth' must skip None or Anonymous users
- 2019-03-18: afabiani - Make sure we use "basic.auth" whenever we check for 'access_token'
- 2019-03-17: afabiani - Fix Travis tests
- 2019-03-17: afabiani - Trevis tests fix
- 2019-03-17: afabiani - Trevis tests fix
- 2019-03-16: afabiani - Revert commit breaking tests
- 2019-03-16: afabiani - Fix test cases
- 2019-03-15: afabiani - Hardening GeoNode and cleanup: http_client uses Bearer Auth whenever it's possible
- 2019-03-15: afabiani - Fix smoke test cases
- 2019-03-15: afabiani - Send warning message whenever the session has expired
- 2019-03-15: afabiani - Hardening GeoNode and cleanup: http_client uses Bearer Auth whenever it's possible
- 2019-03-14: afabiani [Closes #4106] [Remote Services] Add the possibility of filtering the list of resources
- 2019-03-14: afabiani [Closes #4251] Introduce a SessionExpiredMiddleware in order to checkfor access_token validity
- 2019-03-14: Ahmed Nour Eldeen add color picker to theme admin
- 2019-03-14: Ahmed Nour Eldeen fix geoserver public location
- 2019-03-13: afabiani - Proxy: use requests instead of old fashion httplib
- 2019-03-13: Ahmed Nour Eldeen fix geoserver default public location
- 2019-03-12: Jeremiah Cooper Use pycodestyle<2.4.0 until we upgrade the other related dependencies.
- 2019-03-08: Jeremiah Cooper pep8 dependency is deprecated, remove and replace it with pycodestyle
- 2019-03-12: afabiani - Minor: double checks action data format str vs. json
- 2019-03-11: Jeremiah Cooper Remove unnecessary dependencies in debian control file to mirror the removals in requirements.txt.
- 2019-03-08: Jeremiah Cooper Update requirements_docs.txt to reflect changes in requirements.txt.
- 2019-03-08: Jeremiah Cooper Remove unused Pinax, WeasyPrint and debug dependencies
- 2019-03-12: Ahmed Nour Eldeen fix csw tests
- 2019-03-12: Ahmed Nour Eldeen remove redundant settings
- 2019-03-12: Ahmed Nour Eldeen Update settings.py
- 2019-03-11: Ahmed Nour Eldeen remove unused and misleading settings
- 2019-03-07: capooti Bump django-geoexplorer-worldmap 4.0.64
- 2019-03-07: Francesco Frassinelli Add frafra to AUTHORS
- 2019-03-07: Francesco Frassinelli Run dos2unix on .env files
- 2019-03-05: afabiani - Use access_token when parsing layer attributes
- 2019-03-05: Toni Schönbuchner Updated german translation
- 2019-03-05: Francesco Frassinelli Fix Postgres wait on SPC GeoServer
- 2019-02-28: afabiani [minor] removing redoundant middleware class from settings
- 2019-02-27: afabiani [Fixes #4254] Pavement YAMLLoadWarning
- 2019-02-26: Jeremiah Cooper Adds back pyopenssl to requirements.txt.
- 2019-02-26: Jeremiah Cooper Removes unnecessary dependencies from the requirements.txt and settings.py files.
- 2019-02-22: Francesco Frassinelli Disable system_site_packages for geonode-selenium
- 2019-02-22: capooti Bump django-geoexplorer-worldmap==4.0.63
- 2019-02-22: afabiani - Fix smoke test cases
- 2019-02-22: afabiani - Fix smoke test cases
- 2019-02-22: afabiani [Closes #4249] Contribute back upstream menu management from IGAD
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani Fixes Travis geonode-selenium build
- 2019-02-21: afabiani [Fixes #4247] 404 error on CSV upload
- 2019-02-20: Francesco Frassinelli Import SSL Certificate for GeoServer
- 2019-02-15: Francesco Frassinelli Test with geonode-selenium
- 2019-02-19: Denis Rykov Typo fix
- 2019-02-19: Francesco Frassinelli Add support for plugins
- 2019-02-19: Francesco Frassinelli Update GeoServer to 2.14.2
- 2019-02-18: Francesco Frassinelli Improve comment about POSTGRES_PASSWORD
- 2019-02-18: Francesco Frassinelli Use DATABASE_URL in pgdumper
- 2019-02-18: Francesco Frassinelli Use custom Postgres password
- 2019-02-18: Francesco Frassinelli Add ON_ERROR_STOP=1
- 2019-02-18: Francesco Frassinelli Remove extra Postgres wait
- 2019-02-17: Hisham waleed karam use 'update' queue instead if using the default
- 2019-02-14: capooti Fixed a broken link in WorldMap documentation
- 2019-02-14: Francesco Frassinelli Add suffix geoserver to BASEURL
- 2019-02-14: afabiani - add missing header to Python file
- 2019-02-14: hisham waleed karam fix Object of type Map is not JSON serializable
- 2019-02-14: Francesco Frassinelli Use geoserver instead of gs (partial revert)
- 2019-02-14: hisham waleed karam tastypie OAuth Backend
- 2019-02-13: Francesco Frassinelli Wait for GeoServer and PostgreSQL instead of failing
- 2019-02-14: Francesco Frassinelli Add rabbitmq volume to main docker-compose.yml
- 2019-02-13: hisham waleed karam use regex to extract schema
- 2019-02-13: afabiani [minor] There was a wrong 'expiring' check on purging old tokens
- 2019-02-13: Francesco Frassinelli Add rabbitmq volume
- 2019-02-13: Ahmed Nour Eldeen for haystack also get 'vector_time' layers when 'vector' type is selected as 'vector' is super type of 'vector_time'(same behavior of rest api)
- 2019-02-13: Ahmed Nour Eldeen filter by vector time layers is not working when haystack is enabled. as for vector_time layer, 'vectorTimeSeries' is indexed instead of 'vector_time'
- 2019-02-12: Alessio Fabiani Update requirements_docs.txt
- 2019-02-12: Alessio Fabiani Update requirements.txt
- 2019-02-12: Alessio Fabiani Update requirements_docs.txt
- 2019-02-12: Alessio Fabiani Update requirements.txt
- 2019-02-12: afabiani [Related to #4219] - mitigates the issue: Delayed Security Sync Task seems causing issues with sqlite queries
- 2019-02-11: Francesco Bartoli Remove not used settings sample
- 2019-02-11: Francesco Bartoli Revert " Includes additional layer fields in the search index" (#4217)
- 2019-02-11: Francesco Bartoli Fix missing replacement of geoserver public location (#4215)
- 2018-09-12: hisham waleed karam Make sure geogig can be created in a schema other than public
- 2019-02-07: hisham waleed karam remove unused import
- 2019-02-08: giohappy moved login/logout callbacks to profile module; renamed oauth utils to auth
- 2019-02-07: capooti Remove unwanted line of code in worldmap client
- 2019-02-07: hisham waleed karam use celery task to generate thumbnails
- 2019-02-07: hisham waleed karam Fix handling of missing layers.
- 2019-02-07: hisham waleed karam disable threading in test
- 2019-02-06: hisham waleed karam use celery first if enabled
- 2019-02-06: hisham waleed karam add async_thumbnail to improve upload time
- 2018-09-12: Ahmed Nour Eldeen Includes additional fields in the search index
- 2019-02-06: Francesco Frassinelli Use gs instead of geoserver for URLs in Docker
- 2019-02-05: afabiani [Security Cleanup] - Remove unuseful and potentially blocking calls from signals and login/out calls
- 2019-02-05: Alessio Fabiani Cleaning up session if no valid access_token key has been found
- 2019-02-05: Alessio Fabiani Update oauth.py
- 2019-02-05: afabiani - Backporting GEOSERVER_WEB_UI_LOCATION to sample local settings
- 2019-02-04: giohappy fixed exception retrieving token object from empty session token
- 2019-02-04: Alessio Fabiani Adding all Oauth2 endpoints to lockdown exempt uris
- 2019-02-04: giohappy fixed another pep8 issue
- 2019-02-04: giohappy fixed pep8 issues
- 2019-02-04: giohappy fixed mixing of token object and string in login/logout
- 2019-02-01: hisham waleed karam fix flake8
- 2019-02-01: hisham waleed karam fix flake8
- 2019-01-31: afabiani - fix pavemenet cmdopts options
- 2019-01-31: afabiani - Travis Fix & Optimize
- 2019-01-30: capooti Related to fix for #4178
- 2019-01-30: hisham waleed karam fix force list status code
- 2019-01-30: afabiani - Split Travis integration tests into 3 different tasks
- 2019-01-30: hisham waleed karam fix import
- 2019-01-30: hisham waleed karam use requests retry to improve geonode/geoserver connection
- 2019-01-30: giohappy proxy auth refactoring with extended checks
- 2019-01-30: hisham waleed karam fix layer_thumbnail returned None instead.
- 2019-01-30: hisham waleed karam fix map_thumbnail returned None instead.
- 2019-01-30: hisham waleed karam improve map_thumbnail view
- 2019-01-30: hisham waleed karam improve layer_thumbnail view
- 2019-01-29: capooti Added chinese to ALL_LANGUAGES enumeration
- 2019-01-29: giohappy introduce new GEOSERVER_WEB_UI_LOCATION options
- 2019-01-29: Florian Hoedt update to PostgreSQL 10
- 2019-01-28: afabiani - Added also '/api/layers' as per giohappy review
- 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
- 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
- 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
- 2019-01-25: Alessio Fabiani Make flake8 happy
- 2019-01-25: afabiani [Fixes #4181] WMS links are not created for Remote services
- 2019-01-25: giohappy Geoserver menu link shouldn't be proxied
- 2019-01-25: afabiani [Fixes #4178] - Unable to create maps with access_token no longer string
- 2019-01-24: giohappy removed test on links in layers list output
- 2019-01-24: giohappy make flake happy
- 2019-01-24: giohappy show OGC links inside layers list from the API
- 2019-01-24: afabiani - Fix pep8 issues
- 2019-01-24: afabiani [Fixes #4174] - Proxy should pass Bearer authentication to Geoserver transparently
- 2019-01-24: afabiani [Fixes #4174] - Proxy should pass Bearer authentication to Geoserver transparently
- 2019-01-24: afabiani [Fixes #304] Proxy should pass Bearer authentication to Geoserver transparently
- 2019-01-24: afabiani - Removing duplicate code
- 2019-01-23: afabiani [Fixes #4170] Clean management of OAuth2 Access Tokens
- 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
- 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
- 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
- 2019-01-22: gannebamm missed to revert the celery.env to default
- 2019-01-22: admin added ALLOWED_DOCUMENT_TYPES and MAX_DOCUMENT_SIZE as environmental setting to django.env and celery.env. Modified settings.py to read these (doc size was allready implemented)
- 2019-01-22: gannebamm changed some environmental vars back to default
- 2019-01-22: admin added ALLOWED_DOCUMENT_TYPES and MAX_DOCUMENT_SIZE as environmental setting to django.env and celery.env. Modified settings.py to read these (doc size was allready implemented)
- 2019-01-21: afabiani - Making OAUTH2 Access Token expiration seconds configurable by settings/env
- 2019-01-17: capooti Bump django-geoexplorer-worldmap 4.0.62
- 2019-01-17: Francesco Frassinelli Replace exit with a valid command (#4158)
- 2019-01-16: capooti urlsuffix can be numeric. Fixes #4159
- 2019-01-16: Francesco Frassinelli Typos and extra spaces removed
- 2019-01-15: Alessio Fabiani Tentative fix readthedocs build
- 2019-01-11: afabiani [Fixes #4153] GeoLite Legacy db discontinued so paver setup fails in Geonode 2.8 dev install
- 2019-01-08: Alessio Fabiani [Fixes #4150] - Security vulnerabilities on deps (#4151)
- 2019-01-02: Toni Schönbuchner removed windows binary from docs
- 2019-01-02: Toni Schönbuchner removed windows and apt from readme
- 2018-12-21: Francesco Frassinelli Fix geonode.binary arguments escaping
- 2018-12-21: Francesco Frassinelli Fix geonode.binary arguments escaping
- 2018-12-20: Toni Schönbuchner Restored older GeoServer Version #4120
- 2018-12-19: afabiani - default OGC timeout to 60 secs
- 2018-12-19: Paolo Corti Fixes #4132 worldmap client print template (#4133)
- 2018-12-19: Paolo Corti Fixes #4134 handles multiple groups for a new map in worldmap client (#4135)
- 2018-12-19: Paolo Corti Improvements to autocomplete. Fixes #4136 (#4137)
- 2018-10-31: Alessio Fabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters (#4026)
- 2018-12-17: afabiani - vulnerability issues: urllib3 bump to version 1.24.1
- 2018-12-14: afabiani - proj deps
- 2018-12-14: afabiani - re-enable django_filters not included into INSTALLED_APPS
- 2018-12-14: afabiani - Sentinel2 Backgroung Title refs
- 2018-12-12: capooti Add the sync_geonode_layers command. Fixes #4115
- 2018-12-12: capooti Fixes #4117
- 2018-12-12: afabiani - 'Get Started' inherits the Jumbotron paragraph style
- 2018-12-10: afabiani - Allow GeoServer Proxy to recognize prefix on non-root paths also
- 2018-12-07: afabiani - Fix small autoescape issue on detail templates
- 2018-12-06: Francesco Frassinelli Fix codecov link
- 2018-12-05: capooti [worldmap-client] Makes Google StreetView optional. Bump django-geoexplorer-worldmap 4.0.61. Remove some unused code
- 2018-12-05: afabiani - Silencing deprecated warnings
- 2018-12-05: afabiani - Silencing deprecated warnings
- 2018-12-05: afabiani - local_settings.py.geoserver update ms2 backgrounds / catalog takes siteurl / allowed hosts checking for env vars
- 2018-12-05: afabiani [Fixes #4105] [Remote Services] harvesting resources pagination does not keep memory of the selection if changing page
- 2018-12-04: capooti When using worldmap client we need geoserver layers to use gxp_gnsource vs gxp_wmscsource
- 2018-12-03: afabiani - Revert wrong setting
- 2018-12-03: afabiani - E-mail Tasks Bindings
- 2018-12-03: afabiani - Remote Services Async Tasks Binding
- 2018-11-30: afabiani - Layed Detail: legend url and title accoringly to the current style selected
- 2018-11-29: capooti Port the worldmap map notes application. Fixes #4101
- 2018-11-29: afabiani - Remove wrong sample layers from local_settings geoserver sample
- 2018-11-29: afabiani - View Layer should not behave as Edit Layer
- 2018-11-29: afabiani - Finalize: Update docker compose files and refresh of JS assets
- 2018-11-29: afabiani - Removing unused crontab dependency
- 2018-11-28: capooti Bump django-geoexplorer-worldmap 4.0.60
- 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
- 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
- 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
- 2018-11-28: afabiani - pep8 issues
- 2018-11-28: afabiani - Refreshing static assets
- 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
- 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
- 2018-11-28: afabiani - Aligning and improving the docker-compose settings
- 2018-11-28: afabiani - Refreshing static assets
- 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-28: afabiani - Update docker compose files
- 2018-11-26: afabiani - align Layer Detail page style with the Map Detail one
- 2018-11-26: afabiani - minor updates to local_settings.py.geoserver.sample
- 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
- 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
- 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
- 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
- 2018-11-16: capooti Fixes #4089: identify does not work on layers added from the search interface
- 2018-11-15: capooti Fixes #4086: worldmap gazetteer fails to be updated when field names are not ascii
- 2018-11-15: capooti Detect if client is mobile and ask to use wm mobile client
- 2018-11-15: afabiani - Proxy: include "DELETE" operation also when checking for Auth headers
- 2018-11-15: afabiani - Proxy: include "DELETE" operation also when checking for Auth headers
- 2018-11-14: capooti [worldmap-client] Fixes #4082: remove unneeded tools from map details page
- 2018-11-14: afabiani - Display only default style legend at startup
- 2018-11-14: afabiani - Display only default style legend at startup
- 2018-11-14: Alessio Fabiani - geoserver proxy style check fix
- 2018-11-14: afabiani - minor log formatting
- 2018-11-14: Alessio Fabiani - geoserver proxy style check fix
- 2018-11-14: afabiani - minor log formatting
- 2018-11-14: afabiani - minor log formatting
- 2018-11-13: capooti Fixes #4080 (worldmap client "layers list" failing with new maps)
- 2018-11-13: afabiani - Fix GeoServer Signals
- 2018-11-13: afabiani - Fix GeoServer Signals
- 2018-11-13: afabiani [Closes #4077] Update Celery and Kombu libraries
- 2018-11-13: afabiani [Closes #4077] Update Celery and Kombu libraries
- 2018-11-13: afabiani - Allow multiple Style Legends
- 2018-11-13: afabiani - Allow multiple Style Legends
- 2018-11-13: afabiani [Fixes #4075] GeoServer REST Proxy cannot handle SLD DELETE and POST if no SLD body has been provided
- 2018-11-13: afabiani [Fixes #4075] GeoServer REST Proxy cannot handle SLD DELETE and POST if no SLD body has been provided
- 2018-11-13: Hisham waleed karam fix ident_json view
- 2018-11-12: capooti [worldmap-client] Improvements and fixes on the Gazetteer. Fixes #4072.
- 2018-11-09: capooti Localize some strings in the worldmap composer
- 2018-11-09: capooti Fixes #4068: remove duplicate keywords field in adanced metadata editing
- 2018-11-09: Alessio Fabiani - Allow pluggable GIS client to hook their custom Style Edit Page (#4067)
- 2018-11-09: afabiani - Allow pluggable GIS client to hook their custom Style Edit Page
- 2018-11-09: capooti Fixes #4064: resources sort broken on Firefox
- 2018-11-09: afabiani - Filter Remote Services Links to the REST MAP
- 2018-11-09: afabiani - Filter Remote Services Links to the REST MAP
- 2018-11-09: afabiani - fix localsettings sample
- 2018-11-08: capooti Disable numeric fields from feature search. Fixes #4062
- 2018-11-08: afabiani - Fix tests
- 2018-11-08: afabiani - Improve Thumbnail method so that it can include a background also [Refers #3982]
- 2018-11-08: afabiani - set to completed step to check if no _ALLOW_TIME_STEP is false [Refers #3800]
- 2018-11-08: afabiani - Cleanup local_settings.py.geoserver.sample
- 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
- 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
- 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
- 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
- 2018-11-08: afabiani - Thumbnail Generation call at "finished" messages only
- 2018-11-07: capooti [worldmap-client] Now attributes order is honored when identifying features. Fixes #4060
- 2018-11-07: Paolo Corti [Worldmap client] Uses now WFS for GetFeatureInfo only if user has download permissions. Otherwise uses WMS. Fixes #4058 (#4059)
- 2018-11-07: afabiani - Settgins Optimizations
- 2018-11-07: afabiani - Fix resource api exception
- 2018-11-07: afabiani - Fix resource api exception
- 2018-11-06: Alessio Fabiani Fixes some broken links as per #4047 (#4055)
- 2018-11-06: afabiani - JS siteUrl trailing slash if not present
- 2018-11-06: afabiani - JS siteUrl trailing slash if not present
- 2018-11-06: afabiani - Make sure only layers have been added to the cart session
- 2018-11-06: afabiani - Make sure only layers have been added to the cart session
- 2018-11-06: afabiani - Update static libs
- 2018-11-06: afabiani - Update static libs
- 2018-11-06: afabiani - Fix security test cases
- 2018-11-05: capooti Fixes #4056 (uncorrectly set WPS in sync_geofence_with_guardian)
- 2018-11-05: afabiani Fixes some broken links as per #4047
- 2018-11-05: afabiani - minor update to local_settings geoserver sample
- 2018-11-05: afabiani [Fixes #4053] save map broken on 2.8 branch?
- 2018-11-01: capooti Avoid duplication of layers when using link (snapshots) in worldmap client. Refs #4046
- 2018-10-31: capooti Fixes layers duplication introduced with a snapshot. Refs #4046
- 2018-10-31: Alessio Fabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters (#4026)
- 2018-10-31: afabiani - Fix test cases
- 2018-10-31: afabiani - Rename columns of non-UTF-8 shapefiles attributes before ingesting
- 2018-10-31: afabiani - Rename columns of non-UTF-8 shapefiles attributes before ingesting
- 2018-10-30: Paolo Corti Fixes #4044 (broken comments when using worldmap client) (#4045)
- 2018-10-30: Alessio Fabiani [Fixes #4040] GeoServer SLD POST may fail if style does not already exist locally. (#4041)
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
- 2018-10-30: afabiani [Fixes #4039] GeoNode 'proxy' removes original url netloc from the underlying request
- 2018-10-30: afabiani [Fixes #4038] Remove Django deps known vulnerabilities #4038
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: afabiani - Proxy Improvements
- 2018-10-30: afabiani - Python vulnerabilities
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: capooti Fixes #4036
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: Paolo Corti Fixes #4031 (#4032)
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: afabiani - Fix test cases
- 2018-10-30: afabiani - fix test cases
- 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files - rebuild js boundles
- 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files - rebuild js boundles
- 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files
- 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
- 2018-10-29: capooti Fixes #4033
- 2018-10-30: Alessio Fabiani [Closes #4029] Metadata Download Link for RemoteServices should report the Service Type instead of a generic 'http-download' (#4030)
- 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
- 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
- 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
- 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
- 2018-10-29: afabiani - Push RemoteService Type on DownloadLinks Metadata
- 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
- 2018-10-26: Paolo Corti Manage worldmap client using client hooksets standard approach. Refs #4019 (#4023)
- 2018-10-26: afabiani - fix test cases
- 2018-10-26: afabiani - fix test cases
- 2018-10-26: afabiani - general security and encoding updates
- 2018-10-26: afabiani [Regression] View only perm does not works on GeoServer as espected because guardian always inserts a * * rule on GeoFence
- 2018-10-26: afabiani [Regression] View only perm does not works on GeoServer as espected because guardian always inserts a * * rule on GeoFence
- 2018-10-26: olivierdalang (readme) More details for migration steps
- 2018-10-26: olivierdalang fix migrations instructions for older docker
- 2018-10-24: olivierdalang [spcgeonode] several modifications
- 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
- 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
- 2018-10-24: afabiani [Fixes #4016] New thumbnail is not well centered
- 2018-10-23: afabiani [Fixes #4016] New thumbnail is not well centered
- 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
- 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
- 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
- 2018-10-24: afabiani - minor changes
- 2018-10-24: afabiani [Fixes #4016] New thumbnail is not well centered
- 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
- 2018-10-24: Alessio Fabiani Update helpers.py
- 2018-10-24: kappu Thumb creation last fixes
- 2018-10-23: afabiani [Fixes #4016] New thumbnail is not well centered
- 2018-10-23: afabiani - Make sure mercanitle does not hit Y -90/90
- 2018-10-23: Alessio Fabiani Update utils.py
- 2018-10-23: olivierdalang [spcgeonode] improve ci
- 2018-10-23: olivierdalang [spcgeonode] CI : use docker executor again and run tests
- 2018-10-22: olivierdalang [spcgeonode] fix entrypoint perms for django (when mounted)
- 2018-10-23: olivierdalang [spcgenonode] remove secrets
- 2018-10-22: kappu Fixing thumb generation
- 2018-10-19: olivierdalang [spcgeonode] integrate in main geonode repo
- 2018-10-18: afabiani - Fix Upload Session encoding issues
- 2018-10-18: afabiani - Fix Upload Session encoding issues
- 2018-10-18: afabiani - Fix Upload Session encoding issues
- 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
- 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
- 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
- 2018-10-18: afabiani - thumb improvs
- 2018-10-18: afabiani - thumb improvs
- 2018-10-18: afabiani - thumb improvs
- 2018-10-18: Alessio Fabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or "upload-sessions" (#4012)
- 2018-10-18: Alessio Fabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or "upload-sessions" (#4012)
- 2018-10-18: afabiani - fix travis
- 2018-10-18: Paolo Corti PR with Worldmap changes running on master (2.10.x) (#3998)
- 2018-10-17: afabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or 'upload-sessions'
- 2018-10-17: afabiani - fix travix build
- 2018-10-17: Alessio Fabiani Revert wrong commit
- 2018-10-17: afabiani - Mitigate encoding issues for non-UTF-8 Resource names
- 2018-10-17: afabiani - fix travix build
- 2018-10-17: afabiani [Backport to 2.8.x] Fix for #3872 - Theme refactoring
- 2018-10-17: afabiani [Backport to 2.8.x][Closes #4004] Allow to send json body request to {layers,maps}//thumbnail to regenerate the thumbnail
- 2018-10-17: olivierdalang Fix for #3872 - simplified implementation for homepage customization
- 2018-10-17: Alessio Fabiani [Cumulative patch] - Backport minor fixes from master (#4006)
- 2018-10-17: Francesco Bartoli Fix #3999 (#4000)
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani [Cumulative patch] - Backport minor fixes from master
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani - fix travis build
- 2018-10-16: afabiani [Closes #4004] Allow to send json body request to {layers,maps}//thumbnail to regenerate the thumbnail
- 2018-10-16: afabiani [Closes #4001] Typo on sync_with_guardian
- 2018-10-16: afabiani [Closes #4001] Typo on sync_with_guardian
- 2018-10-16: afabiani - fix thumbs and gf rules
- 2018-10-16: Alessio Fabiani [Fixes #3993] Adding group access makes layer public (#3995)
- 2018-10-16: Alessio Fabiani [Closes #3982] Improve Thumbnail method so that it can include a background also (#3997)
- 2018-10-16: afabiani [Backport to 2.8.x][Fixes #3993] Adding group access makes layer public
- 2018-10-16: afabiani - Themes customization
- 2018-10-16: olivierdalang fix travis (to be squashed...)
- 2018-10-16: olivierdalang fix... (to be squashed)
- 2018-10-16: olivierdalang fix tests
- 2018-10-16: olivierdalang tests
- 2018-10-16: olivierdalang remove unused identifier
- 2018-10-16: olivierdalang formatting
- 2018-10-15: afabiani - Fix security test cases
- 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
- 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
- 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
- 2018-10-15: afabiani [Backport to 2.8.x][Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
- 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
- 2018-10-15: afabiani - Test cases
- 2018-10-15: afabiani [Fixes #3984] Recurring User 'None' Error Geofence/Geoserver
- 2018-10-15: afabiani [Fixes #3984] Recurring User 'None' Error Geofence/Geoserver
- 2018-10-15: olivierdalang [themes] several improvements
- 2018-08-31: olivierdalang simpler implementation for homepage customization
- 2018-10-14: Alessio Fabiani Update signals.py
- 2018-10-14: Alessio Fabiani Update signals.py
- 2018-10-12: afabiani - fix naming
- 2018-10-12: afabiani [Closes #3982] Improve Thumbnail method so that it can include a background also
- 2018-10-12: afabiani - improve render thumbnail procedure
- 2018-10-12: afabiani - fix testscases
- 2018-10-11: afabiani - improve render thumbnail procedure
- 2018-10-11: Ernest CHIARELLO Update setup_docker_compose.txt
- 2018-10-10: Alessio Fabiani pep8 issues
- 2018-10-09: mikefedak Deny access to layers/documents
- 2018-09-25: Erwin Sterrenburg set to completed step to check if no _ALLOW_TIME_STEP is false
- 2018-10-03: Alessio Fabiani [Fixes #3947] GeoWebCache Tiled Layer Cache explicit invalidation (#3949)
- 2018-10-03: afabiani [Fixes #3953] "bbox_to_projection" flips coords and does not honor the geonode convention [x0, x1, y0, y1]
- 2018-10-02: Alessio Fabiani Update requirements.txt
- 2018-10-01: afabiani [Fixes #3951] [Cross-site scripting test - Security related - Issue] Improvements to Tastypie paginator
- 2018-10-01: afabiani [Closes #3948] WPS Endpoint should be exposed to non-protected proxy
- 2018-10-09: afabiani - geofence rest endpoint 2.13.x compliant
- 2018-10-01: Alessio Fabiani [Fixes #3945] GeoFence rules priority computation should be more accurate (#3946)
- 2018-09-27: afabiani [Closes #3942] Make 'Edit data' link aware of the storeType
- 2018-09-25: afabiani [Fixes #3919] fix updatelayers command
- 2018-09-25: afabiani [Closes #3934] Missing OWS endpoints on GeoNode proxy
- 2018-10-08: afabiani - replace build server url
- 2018-09-25: afabiani [Closes #3935] celery.log file based handler causing issues if not correctly configured
- 2018-10-09: afabiani - Improve test coverage
- 2018-10-08: Alessio Fabiani [Fixes #3962] Remove Django vulnerabilties (#3963)
- 2018-10-08: afabiani - replace build server url
- 2018-10-04: afabiani - disable codecov report on geosolutions branch
- 2018-10-04: afabiani [Closes #3957] Increase "geoserver" tests coverage
- 2018-10-04: afabiani [Closes #3957] Increase "geoserver" tests coverage
- 2018-10-03: afabiani - codecov reports
- 2018-10-03: afabiani - update .travis.yml
- 2018-10-03: afabiani - update .travis.yml
- 2018-10-03: afabiani - update .travis.yml
- 2018-10-03: Alessio Fabiani Update README.rst
- 2018-10-03: afabiani - codecov reports
- 2018-10-03: afabiani - codecov reports
- 2018-10-03: afabiani - fix test cases
- 2018-10-03: Alessio Fabiani [Fixes #3947] GeoWebCache Tiled Layer Cache explicit invalidation (#3949)
- 2018-10-03: Alessio Fabiani Fix pep8 issues
- 2018-10-03: afabiani [Fixes #3954] [GeoServer 2.14.x Upgrade] Layer Capabilities 1.3.0
- 2018-10-03: afabiani - Fix integration test_capabilities test cases
- 2018-10-03: afabiani [Fixes #3953] "bbox_to_projection" flips coords and does not honor the geonode convention [x0, x1, y0, y1]
- 2018-10-03: afabiani - Fix bbox projection
- 2018-10-03: afabiani - Fix bbox projection
- 2018-10-03: afabiani - Fix integration test_capabilities test cases
- 2018-10-02: afabiani - Fix integration test_capabilities test cases
- 2018-10-02: afabiani - Fix layer capabilities; WMS 1.3.0
- 2018-10-02: afabiani - remove restricted dirty_state condition from catalog
- 2018-10-02: afabiani - Delayed GeoFence Security Rules Invalidation
- 2018-10-02: Alessio Fabiani Update requirements.txt
- 2018-10-01: afabiani [Fixes #3951] [Cross-site scripting test - Security related - Issue] Improvements to Tastypie paginator
- 2018-10-01: afabiani [Closes #3948] WPS Endpoint should be exposed to non-protected proxy
- 2018-10-01: Alessio Fabiani [Fixes #3945] GeoFence rules priority computation should be more accurate (#3946)
- 2018-09-27: afabiani [Closes #3942] Make 'Edit data' link aware of the storeType
- 2018-09-26: afabiani - GeoFence rules checks on test cases
- 2018-09-26: afabiani - GeoFence rules checks on test cases
- 2018-09-25: Erwin Sterrenburg set to completed step to check if no _ALLOW_TIME_STEP is false
- 2018-09-25: afabiani [Fixes #3919] fix updatelayers command
- 2018-09-25: afabiani [Closes #3934] Missing OWS endpoints on GeoNode proxy
- 2018-09-25: afabiani [Closes #3935] celery.log file based handler causing issues if not correctly configured
- 2018-09-24: afabiani - Improving groups activity tests
- 2018-09-24: afabiani - Fixing groups test cases
- 2018-09-24: afabiani [Issue #3927] Switching to 2.8.x branch: enabling 2.8.x branch on .travis.yml
- 2018-09-24: afabiani [Fixes #3929] Show documents also on Groups Activity Tabs
- 2018-09-24: afabiani - Fix migrations
- 2018-09-24: afabiani - Switching to 2.8.x branch
- 2018-09-19: Hisham waleed karam return layer styles in map json as a list
- 2018-09-19: Hisham waleed karam fix for AttributeError("'str' object has no attribute 'username'",))
- 2018-09-19: gannebamm fix missing '-get' in apt-get (#3918)
- 2018-09-13: afabiani [Fixes #3914] [Remote Services] Mitigate name clashes for service names
- 2018-09-13: afabiani [Fixes #3914] [Remote Services] Mitigate name clashes for service names
- 2018-09-07: afabiani - fix integration test cases - GeoFence apis cleanup
- 2018-09-07: afabiani Fix test cases and geofence model
- 2018-09-07: amefad Remove old configuration for GeoServer security from docs (#3908)
- 2018-09-06: Francesco Bartoli Revert and then apply Fix #3893 again (#3903)
- 2018-09-06: Francesco Bartoli Fix #3893
- 2018-09-05: Alessio Fabiani [Closes #3896] Add a sample docker-compose.override.localhost.yml to run docker-compose on localhost (#3897)
- 2018-09-05: Alessio Fabiani [Fixes #3890] wsgi.py should either check for local_settings (apt) and settings (docker)
- 2018-09-07: amefad Remove old configuration for GeoServer security from docs (#3908)
- 2018-09-06: Francesco Bartoli [2.7.x] Backport issue #3893 (#3904)
- 2018-09-06: Francesco Bartoli Revert and then apply Fix #3893 again (#3903)
- 2018-09-06: afabiani [Fixes #3900] [2.7.x] Fix py.test selenium tests
- 2018-09-06: Francesco Bartoli Fix #3893
- 2018-09-05: Alessio Fabiani [Closes #3896] Add a sample docker-compose.override.localhost.yml to run docker-compose on localhost (#3897)
- 2018-09-05: afabiani [Fixes #3894] [2.7.x] docker-compose.yml should point to the 2.7.x image and not the latest one
- 2018-09-05: Alessio Fabiani [Fixes #3890] wsgi.py should either check for local_settings (apt) and settings (docker)
- 2018-09-05: afabiani - fix integration test cases
- 2018-09-04: afabiani [Fixes #3886] Remove javascript packages vulnerabilities
- 2018-09-04: afabiani [Fixes #3884] Update GeoServer to 2.14.x on master
- 2018-08-31: Alessio Fabiani Removing "requirements.txt" known vulnerabilities
- 2018-08-31: Alessio Fabiani Py.Test BDD - Fix splinter and selenium versions
- 2018-08-28: Simone Dalmasso update geonode-updateip to use the new flags
- 2018-08-28: Alessio Fabiani [Fixes #3874] sudo geonode-updateip fails on fresh Ubuntu 16.04 install
- 2018-08-28: Simone Dalmasso add docs for the new flags of updateip
- 2018-08-28: Alessio Fabiani 2.10rc4
- 2018-08-28: Alessio Fabiani Updated changelog for version 2.10rc4
- 2018-08-23: geo 2.10.0 rc3
- 2018-08-23: geo Updated changelog for version 2.10rc2
- 2018-08-23: geo 2.10.0 rc2
- 2018-08-23: geo 2.10.0 rc2
- 2018-08-23: geo Updated changelog for version 2.10rc1
- 2018-08-23: geo 2.10.0 rc1
- 2018-08-20: Alessio Fabiani 2.10.0 rc0
- 2018-08-20: Alessio Fabiani 2.10.0 rc0
- 2018-08-20: Alessio Fabiani 2.8.1 release
- 2018-08-20: Alessio Fabiani Updated changelog for version 2.8.1rc0
- 2018-08-20: Alessio Fabiani 2.8.1 release
- 2018-08-03: Alessio Fabiani Backporting PRs fixes from master: https://github.com/GeoNode/geonode/pull/3866 https://github.com/GeoNode/geonode/pull/3865 https://github.com/GeoNode/geonode/pull/3864
- 2018-07-31: capooti Now there is not a local_settings.py file, so we need to set DATABASES when using worldmap application
- 2018-07-31: capooti Includes commands in Makefile to create and remove databases needed when using the worldmap contrib application
- 2018-07-31: afabiani - #3180 restoring angular 1.4.0
- 2018-07-31: Toni Schönbuchner allow long item titles to break without whitespace
- 2018-07-31: afabiani - #3180 restoring angular 1.4.0
- 2018-07-28: Toni Schönbuchner prevent change of height on cart item hover
- 2018-07-27: capooti Ported the mobile client as per #404
- 2018-07-27: afabiani - Fix typo
- 2018-07-27: afabiani - Fix typo
- 2018-07-27: afabiani Backport fixes from master
- 2018-07-27: afabiani - Minor hardening on Map configuration stuff
- 2018-07-27: afabiani - Fix integration tests
- 2018-07-26: afabiani Backport fixes from master
- 2018-07-26: afabiani - Fix QGis integration tests
- 2018-07-26: afabiani - Fix QGis integration tests
- 2018-07-25: afabiani - Fix QGis Server Integration Tests
- 2018-07-25: Toni Schönbuchner changed readme to rst with extension and changed setup.py
- 2018-07-25: Toni Schönbuchner submission of new readme for github
- 2018-07-25: afabiani Backport fixes from master
- 2018-07-25: afabiani - Fix QGis Server Integration Tests
- 2018-07-25: afabiani - Updating ElasticSearch dependencies
- 2018-07-25: Boney Bun fix a srid bug when uploading vector layers
- 2018-07-24: afabiani - Updating ElasticSearch dependencies
- 2018-07-24: afabiani - Remove circular local_settings import
- 2018-07-24: Toni Schönbuchner centered magnifier in big search and aligned metadata checkbox
- 2018-07-24: afabiani - Tentative fix geoserver docker compose
- 2018-07-24: afabiani - Tentative fix geoserver docker compose
- 2018-07-24: Alessio Fabiani - Backporting fixes from master
- 2018-07-23: Hisham waleed karam Update settings.py
- 2018-07-23: afabiani - Backporting master branch fixes
- 2018-07-23: afabiani - Add storeType to Layers Capabilities response
- 2018-07-22: Francesco Bartoli Backport #3856
- 2018-07-22: Francesco Bartoli Add variable to set geoserver JAVA_OPTS
- 2018-07-21: Francesco Bartoli Backport fix #3853
- 2018-07-21: Francesco Bartoli Fix #3853
- 2018-07-21: Alessio Fabiani - MapLoom GIS client hooksets (#3851)
- 2018-07-21: Toni Schönbuchner Added explanations regarding pygdal install #3784
- 2018-07-20: afabiani - MapLoom GIS client hooksets
- 2018-07-20: afabiani - MapLoom GIS client hooksets
- 2018-07-20: afabiani - Fix JS vulnerability
- 2018-07-20: afabiani - MapLoom GIS client hooksets
- 2018-07-20: afabiani Backport fixes from master branch
- 2018-07-20: afabiani - Fixing test-cases
- 2018-07-19: afabiani Update gsconfig and gsimporter versions
- 2018-07-19: afabiani - Remove ws prefixed URL from links in order to publish a full DescribeLayer on Links
- 2018-07-19: afabiani - Update gnimporter version
- 2018-07-19: afabiani - Update gnimporter version
- 2018-07-16: giohappy fix GeoNode DB name in docker env
- 2018-07-15: giohappy note on docker-compose up for Windows users (see #3709)
- 2018-07-14: Alessio Fabiani - Backport fixes and PRs from master (#3846)
- 2018-07-13: afabiani - Minor Layout improvements
- 2018-07-13: afabiani - Minor Layout improvements
- 2018-07-13: afabiani - Minor Layout improvements
- 2018-07-13: afabiani - Backport fixes and PRs from master
- 2018-07-13: afabiani - Exclude public-invite groups from metadata choices
- 2018-07-13: afabiani [Fixes #3834] STATIC_URL vs static template tag
- 2018-07-12: afabiani - Fixes issue #3843 - Fix vulnerability with Pillow dependency
- 2018-07-11: afabiani - backport from master
- 2018-07-11: Toni Schönbuchner Restrict use of Edit Document Button
- 2018-07-10: Toni Schönbuchner corrected Ubuntu 14.04 to 16.04 in documentation
- 2018-07-10: Toni Schönbuchner added search input for styles to manage page
- 2018-07-07: afabiani - Fix max zoom issue
- 2018-07-07: afabiani - Fix max zoom issue
- 2018-07-05: geo - Packagind scripts updates
- 2018-07-05: geo - Packagin scripts updates
- 2018-07-05: geo - Packagin scripts updates
- 2018-07-05: afabiani [backport 2.7.x] Minor improvements: allow registered users to invite others / improve French translation
- 2018-07-05: afabiani - Improving French translations
- 2018-07-04: afabiani - Allow portal contributors to invite users
- 2018-07-03: Glenn Vorhes add missing ast import
- 2018-07-03: Glenn Vorhes add missing ast import
- 2018-07-03: afabiani - Fixes layer replace
- 2018-07-03: Alessio Fabiani Update helpers.py
- 2018-07-03: Alessio Fabiani Update helpers.py
- 2018-07-03: afabiani - Fixes layer replace
- 2018-07-02: afabiani - Fixes layer replace
- 2018-06-28: afabiani - Fix kombu/messaging initialization
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani pep8 fixes
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani pep8 fixes
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-27: afabiani pep8 issues
- 2018-06-27: afabiani pep8 issues
- 2018-06-27: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-27: afabiani - Fix celery initialization when using GeoNode ad a depenency
- 2018-06-27: Alessio Fabiani Externalize OGC TIMEOUT setting as ENV var
- 2018-06-27: Alessio Fabiani Externalize OGC TIMEOUT setting as ENV var
- 2018-06-26: Alessio Fabiani Update Dockerfile
- 2018-06-25: afabiani - Backporting Docker Improvs and Fixes from master branch
- 2018-06-25: afabiani - Docker make use of GeoServer Importer Uploader
- 2018-06-25: afabiani - minor improvements geoserver helper
- 2018-06-25: afabiani - Fix localhost docker compose var
- 2018-06-25: afabiani - Tentative fix doscker-compose vars
- 2018-06-21: Alessio Fabiani - Backport stable fixes from master branch
- 2018-06-21: afabiani - Improve Map Embed Template and allow it passing through client hooksets
- 2018-06-21: Alessio Fabiani - Backport stable fixes from master branch
- 2018-06-21: afabiani - Improve Map Embed Template and allow it passing through client hooksets
- 2018-06-21: afabiani - Updating the oauth2 toolkit dep version
- 2018-06-19: afabiani - GeoNode Client Hooksets: allow client configuration tweaking from pluggable client library
- 2018-06-17: Francesco Bartoli Don't raise an exception if variable is missing
- 2018-06-14: afabiani - Update requirements: adding openssl deps
- 2018-06-14: afabiani - Update requirements: adding openssl deps
- 2018-06-13: Alessio Fabiani [Fixes #3800] Uploading shapefiles without a datefield and time-enabled is False in importer settings fails in 2.7.x
- 2018-06-13: afabiani [Fixes #3800] Uploading shapefiles without a datefield and time-enabled is False in importer settings fails in 2.7.x
- 2018-06-12: Alessio Fabiani - SITEURL rstrip (/) consistently
- 2018-06-12: afabiani - SITEURL rstrip (/) consistently
- 2018-06-12: Alessio Fabiani Backport from master branch
- 2018-06-11: afabiani Improvements to PyCSW Constraints and Local Mappings
- 2018-06-11: afabiani Improvements to PyCSW Constraints and Local Mappings
- 2018-06-07: giohappy Set default datastore from env for OGC server settings
- 2018-06-07: afabiani Improvements to PyCSW Constraints and Local Mappings
- 2018-06-07: giohappy value should be datastores key name, not value
- 2018-06-07: giohappy Set default datastore from env for OGC server settings
- 2018-06-06: afabiani [Backport fixes from master]
- 2018-06-06: afabiani [Fw-port #3817] Implements GNIP #3718 (Worldmap contrib application)
- 2018-06-06: afabiani [Backport fixes from master]
- 2018-06-06: afabiani [Fixes #3824] Manage style page show style name instead of title
- 2018-06-06: afabiani - OIDC 1.0 compliancy / notifications fixes
- 2018-06-04: afabiani - OIDC 1.0 compliancy preparation: add api > UserInfo method
- 2018-06-02: Tim Sutton Fix issues where docker client may be incompatible with docker server API by forcing to APV version 1.24
- 2018-05-31: capooti By default we dont use hypermap
- 2018-05-31: capooti Fixes a problem with createlayer app
- 2018-05-30: capooti Fixes PEP8 violations and a syntax error
- 2018-05-29: capooti By default USE_WORLDMAP is False
- 2018-05-29: capooti Sync with latest GeoNode 2.7.x
- 2018-05-29: capooti Fixes pep8 violations
- 2018-05-29: afabiani - Add iso8961 time rules (yyyy/yyyy-mm/yyyy-mm-dd) on Templates also
- 2018-05-28: Alessio Fabiani - Backport from master
- 2018-05-28: Giovanni Allegri DATETIME_INPUT_FORMATS switched to list since Django 1.9
- 2018-05-28: afabiani [geoext client] - Zoom To Data and not to nearest Scale
- 2018-05-24: capooti Reset a couple of files
- 2018-05-24: capooti Removed the worldmap.queue application for now
- 2018-05-24: capooti Updated worldmap installation documentation
- 2018-05-24: Alessio Fabiani - Backport fixes from master branch
- 2018-05-24: afabiani - pep8 issues
- 2018-05-24: afabiani - Update pip install instructions on docs and README
- 2018-05-24: afabiani - Include django-celery-mon dep on requirements.txt
- 2018-05-24: afabiani - ASYNC MODE uses ASYNC CELERY TASKS
- 2018-05-23: afabiani - ImageMosaics refactoring: first step - support ZIP archives with granules and .properties files
- 2018-05-23: afabiani - Update dependencies versions
- 2018-05-23: afabiani - Fix Map Detail page structure issue and errors with GetCapabilities
- 2018-05-22: capooti Now using django-geoexplorer-worldmap from the pypi package
- 2018-05-21: capooti Including a local_settings sample file for worldmap
- 2018-05-21: capooti Updated requirements for WorldMap
- 2018-05-21: capooti Fixing a number of things before sending PR to GeoNode 2.7.x
- 2018-05-21: capooti Removing all static files, which should be added by pip install worldmap-geoexplorer
- 2018-05-21: capooti Removing worldmap account, which will be part of the cga geonode project
- 2018-05-21: capooti Removing from git the compiled geoexplorer worldmap client
- 2018-05-21: Alessio Fabiani - Backport commit 6c0e8ca from master
- 2018-05-21: afabiani - Restored the possibility of sending multiple uploads
- 2018-05-21: capooti A couple of fixes and removing geoexplorer source code
- 2018-05-21: Alessio Fabiani - Backport commit 15123a5 from master
- 2018-05-21: afabiani - Translations and minor refactoring of upload validator
- 2018-05-18: capooti Removed stale worldmap documentation page
- 2018-05-18: capooti Move the worldmap documentation to the correct place
- 2018-05-18: capooti Synced with GeoNode 2.7.x
- 2018-05-16: capooti Updating the client
- 2018-05-16: Alessio Fabiani - Allow registered users to edit Remote Services
- 2018-05-16: afabiani - Reduced size of layer-upload tooltips square
- 2018-05-16: afabiani - Allow registered users to manage Remote Services
- 2018-05-16: Alessio Fabiani - Backport fixes from master
- 2018-05-16: afabiani - Correct management of SLDs / Add GWC filterParameter to SLDs
- 2018-05-16: Alessio Fabiani - Backport fixes from master
- 2018-05-15: afabiani - Correct management of SLDs / Add GWC filterParameter to SLDs
- 2018-05-15: afabiani - Increase Test Coverage
- 2018-05-15: afabiani - Allow unapproved layers to be published on maps
- 2018-05-15: afabiani - Fix for issue Map Composer Menu not show complete #3804
- 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
- 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
- 2018-05-14: afabiani - Fix ResponseNotReady issue
- 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
- 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
- 2018-05-14: Alessio Fabiani Update integration.py
- 2018-05-14: afabiani Backporting Master PRs
- 2018-05-14: afabiani - Fix celery tasks hanging forever
- 2018-05-14: afabiani - Minor improvements
- 2018-05-11: capooti Fixes #375
- 2018-05-11: capooti Fixes #3801
- 2018-05-11: erwin Generalize logo-urls in profile-detail template.
- 2018-05-11: afabiani Backporting Master PRs
- 2018-05-11: afabiani - Docker Compose improvs
- 2018-05-10: capooti Now thumbanils are not generate from layers which are created. Fixes #358
- 2018-05-10: capooti Fixes part of #358 (the layer extent)
- 2018-05-10: capooti Some improvement to the createlayer application
- 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/37
- 2018-05-10: camp-zju Modify the file for translation
- 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/36
- 2018-05-10: camp-zju Add Chinese translation file
- 2018-05-10: camp-zju Add Chinese translation file
- 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/34
- 2018-05-10: afabiani - minor tweak on settings for Docker
- 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/33
- 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/32
- 2018-05-10: camp-zju Fixes 31
- 2018-05-09: afabiani - Dockerfile: update pip install
- 2018-05-09: capooti Fixes #367
- 2018-05-09: afabiani - Travis pip cache
- 2018-05-09: afabiani - Test coverage
- 2018-05-09: afabiani - Integration test coverage
- 2018-05-09: afabiani - Integration test coverage
- 2018-05-09: afabiani - pep8 issues
- 2018-05-09: afabiani - Minor fixes to backup & restore commands
- 2018-05-08: Cezary Statkiewicz test-specific requirements: twisted
- 2018-05-08: afabiani - Legend links for remote services
- 2018-05-08: afabiani - Legend links for remote services
- 2018-05-08: Cezary Statkiewicz monitoring: resolve 2-letter codes to 3-letter codes
- 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
- 2018-05-08: afabiani - Fix updatelayers mgmt command
- 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
- 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
- 2018-05-08: afabiani - Monitoring GeoIP error management
- 2018-05-08: Cezary Statkiewicz Monitoring geoip2 (#286)
- 2018-05-08: afabiani - Disabling synchronous remote services probe from model
- 2018-05-08: Cezary Statkiewicz handle new geoip format properly
- 2018-05-08: Cezary Statkiewicz use maxmind v2 db format if needed
- 2018-05-07: afabiani - Improve Test Coverage
- 2018-05-07: afabiani Backporting Master PRs
- 2018-05-07: afabiani Backporting Master PRs
- 2018-05-07: Alessio Fabiani Update helpers.py
- 2018-05-07: afabiani - Test coverage improvements
- 2018-05-07: Alessio Fabiani Update helpers.py
- 2018-05-04: giohappy included default settings for social providers
- 2018-05-03: capooti Fixes the updatelayers command
- 2018-05-02: capooti renaming celery to celery_app
- 2018-05-02: capooti Fixing a couple of things broke when merging
- 2018-05-02: capooti Fixes a couple of things which were broken by merge with 2.8.0
- 2018-05-01: capooti Synced with GeoNode 2.8.0
- 2018-04-30: capooti Updating geoexplorer to last version
- 2018-04-30: capooti Sync with GeoNode 2.8 part 1/2
- 2018-04-28: Alessio Fabiani Update helpers.py
- 2018-04-27: capooti Now categories order is respected for existing maps. Refs #341
- 2018-04-26: capooti Re-enable thumbnails for layers. Fixes #351
- 2018-04-26: capooti Fixes #350
- 2018-04-26: Alessio Fabiani Backporting Master PRs
- 2018-04-26: afabiani - fixes and improvements to Layer replase functionalities
- 2018-04-26: Alessio Fabiani Forward port 2.8.0 changelogs
- 2018-04-26: Ahmed Nour Eldeen check geometry type
- 2018-04-26: afabiani - Fix layer replase
- 2018-04-26: afabiani - Fix remote services layout
- 2018-04-26: Cezary Statkiewicz catch geoserver error in messaging, to avoid looped delivery
- 2018-04-26: Alessio Fabiani Update utils.py
- 2018-04-26: afabiani - Fix test cases
- 2018-04-26: afabiani - Fix test cases
- 2018-04-26: Alessio Fabiani - Fix test cases
- 2018-04-26: afabiani - Fix test cases
- 2018-04-26: Alessio Fabiani Updated changelog for version 2.8
- 2018-04-26: Alessio Fabiani Constrain pip to 9.0.3
- 2018-04-26: Alessio Fabiani Updated changelog for version 2.8
- 2018-04-26: Alessio Fabiani Constrain pip to 9.0.3
- 2018-04-26: afabiani - Restore production/docker requirements
- 2018-04-26: afabiani - DB consistency checks
- 2018-04-26: Alessio Fabiani Update utils.py
- 2018-04-26: Alessio Fabiani Update utils.py
- 2018-04-26: afabiani - Update avatar version
- 2018-04-26: Alessio Fabiani Update utils.py
- 2018-04-26: Alessio Fabiani Update utils.py
- 2018-04-25: capooti Use HTML widget in GXP for any field starting with "descriptio". Refs #348
- 2018-04-24: afabiani Just fix requirements versions for GeoNode modules in order to avoid compatibility issues
- 2018-04-24: afabiani Minor improvement to custom_theme_html template
- 2018-04-24: afabiani [Closes #3662] GNIP: Improvements to GeoNode Layers download links
- 2018-04-24: afabiani Just fix requirements versions for GeoNode modules in order to avoid compatibility issues
- 2018-04-24: afabiani Minor improvement to custom_theme_html template
- 2018-04-24: afabiani [Closes #3662] GNIP: Improvements to GeoNode Layers download links
- 2018-04-24: Cezary Statkiewicz use geoip2 for monitoring
- 2018-04-24: Cezary Statkiewicz use geoip2 for monitoring
- 2018-04-24: olivierdalang fix slow login/logout on certain circumstances
- 2018-04-24: olivierdalang fix slow login/logout on certain circumstances
- 2018-04-23: afabiani - Fix reproj issue on bbox_to_projection
- 2018-04-23: afabiani - Fix reproj issue on bbox_to_projection
- 2018-04-20: Alessio Fabiani Backport master fixes
- 2018-04-20: Alessio Fabiani - Fix issue with layer upload
- 2018-04-20: Alessio Fabiani - Fix issue with layer upload
- 2018-04-20: afabiani - Restoring Live Server port settings on integration tests in order to avoid address conflicts
- 2018-04-19: afabiani - Split test cases on travis
- 2018-04-19: afabiani - Split test cases on travis
- 2018-04-19: afabiani - Align with master
- 2018-04-18: afabiani - cleanup
- 2018-04-18: afabiani - align with master branch
- 2018-04-17: afabiani - Fix integration test cases
- 2018-04-16: afabiani [Closes #3661] django 1.11 LTS support on master
- 2018-04-17: Alessio Fabiani Better alignment of jumbotron image
- 2018-04-16: afabiani [Closes #3661] django 1.11 LTS support on master
- 2018-04-14: afabiani Prepare 2.8.1
- 2018-04-13: capooti Fixes #343
- 2018-04-13: Alessio Fabiani Align to master branch
- 2018-04-12: capooti Fixes #340
- 2018-04-12: capooti Fixes #336
- 2018-04-12: capooti Fixes #327
- 2018-04-11: capooti Fixes #330
- 2018-04-11: Ahmed Nour Eldeen get geometry type for layers
- 2018-04-09: capooti Fixes #300
- 2018-04-09: capooti Fixes #334
- 2018-04-05: capooti Adds a status message when updating the gazetteer fields for a layer
- 2018-04-05: capooti Removed a stale file
- 2018-04-04: capooti Make layer configuration in json map more robust
- 2018-04-04: capooti Improve map thumbnails
- 2018-04-03: capooti Forgot file in previous commit
- 2018-04-03: capooti Add the feature search functionality
- 2018-04-03: Alessio Fabiani Update README
- 2018-04-03: Alessio Fabiani Update README
- 2018-04-03: Alessio Fabiani Updated changelog for version 2.8
- 2018-04-03: Alessio Fabiani Release 2.8.0
- 2018-03-30: capooti Fixes #301
- 2018-03-29: capooti Fixes the regression casuing all the extent issues
- 2018-03-28: capooti Fixes zoom to extent tool
- 2018-03-28: capooti Fixes #322
- 2018-03-26: capooti Correctly identify a local layer when using GeoNodeQueryTool
- 2018-03-26: capooti Point to the correct warper site in the rectify images dialog
- 2018-03-26: capooti Fixes #298
- 2018-03-26: capooti Fixes #314
- 2018-03-21: capooti Fixes #283
- 2018-03-20: capooti Fixes permission issues with editing and use the correct source for wm layers
- 2018-03-16: capooti Fixes #272
- 2018-03-16: capooti Forgot one requirement in previous commit
- 2018-03-16: capooti Fixes #260 (gazetteer)
- 2018-03-08: capooti Fixes #267
- 2018-03-06: capooti Restore map print tool
- 2018-03-06: capooti Migrate worldmap map revisions. Fixes #266
- 2018-03-05: capooti Adding a missing import which caused an error when looking at map page as anonymous user
- 2018-03-05: capooti Removed google earth tool. Fixed the gxp_mapshare tool. Fixing google map api key read
- 2018-03-05: capooti Trying to increase accesstoken expiration to see if this affects #283
- 2018-03-02: capooti Update instructions adding the configuration of update_last_wm_geonode_layers
- 2018-03-02: capooti Fixes #280
- 2018-03-02: capooti Fixes #264
- 2018-02-27: capooti Updating instructions for geonode-worldmap
- 2018-02-26: capooti Ported the action model and its api, which is needed by hypermap
- 2018-02-26: capooti Fixed javascript build.xml and a few things in GeoExplorer.js
- 2018-02-23: capooti Added worldmap geoexplorer client source code. Fixes #265
- 2018-02-21: capooti WorldMap api version is now 2.8
- 2018-02-21: capooti Add url dispatcher for worldmap api
- 2018-02-21: capooti Fixes a problem with layers upload
- 2018-02-16: capooti Synced with geonode master
- 2018-02-15: capooti Fixes #273
- 2018-02-15: capooti Fixes #263
- 2018-02-15: capooti Now it is possible to edit and style also from local dev
- 2018-02-14: capooti Enable DB_DATASTORE when using WorldMap
- 2018-02-14: capooti Remove an ipdb line
- 2018-02-12: capooti Align to GeoNode master. Fixes #276
- 2018-02-08: capooti Updated installation instructions
- 2018-02-06: Ben Lewis Update README.md
- 2018-02-06: capooti Hard fixing wms endpoint for now for having style edit working
- 2018-02-01: capooti Now layer is correctly displayed in map
- 2018-01-31: capooti Added instructions for geonode/worldmap contrib app configuration
- 2018-01-31: capooti Removing worldmap settings from general settings file
- 2018-01-31: capooti Fixes a problem with upload - not sure if this is a general GeoNode problem, will need to look into it
- 2018-01-31: capooti Redefining some urls when using worldmap contrib application
- 2018-01-31: capooti Added context processors worldmap variables
- 2018-01-30: capooti Fixes migration for apps still using South. Move the content_map field to the appropriate model
- 2018-01-30: capooti Fixing a migration
- 2018-01-30: capooti Fixes a syntax error
- 2018-01-18: capooti Basic version of WorldMap GeoNode unforked version with working maps
- 2018-01-11: capooti Start removing forked code in GeoNode/WorldMap
- 2018-01-10: Ubuntu Fixed conflicts with master
- 2017-11-21: capooti Sync with geonode master
- 2017-11-21: Way Barrios Fixing settings import, we should use django.conf in stead of geonode
- 2017-11-14: Way Barrios removing pdb
- 2017-11-14: Way Barrios removing pdb dependency
- 2017-11-07: Way Barrios Adding missing migrations
- 2017-11-03: capooti Sync with GeoNode master
- 2017-11-03: capooti Sync pavement with the one from master
- 2017-10-31: capooti Added missing migration
- 2017-10-27: capooti Updating config to download GeoServer 2.12
- 2017-10-24: Way Barrios removing is_certifier from people profile
- 2017-10-19: Way Barrios Dataverse and Datatables migration
- 2017-10-17: capooti Enable layer-brose again
- 2017-10-13: Lenninlasd Add missing translations
- 2017-10-10: Lenninlasd Add extra metadata attributes
- 2017-10-04: capooti erge branch 'master' into wm-develop
- 2017-09-26: Way Barrios removing certification app from GeoNode
- 2017-09-20: capooti Added a missing migrations for map
- 2017-09-19: capooti Adding migrations for the gazetteer fields
- 2017-09-19: capooti Fixes some migrations problem and make sure that "paver sync" correctly build worldmap database
+ 2019-07-10: afabiani - 2.10 stable release
+ 2019-07-09: dependabot-preview[bot] Bump psutil from 5.6.1 to 5.6.3
+ 2019-07-09: afabiani - GeoServer Render Thumbnails using the correct auth headers
+ 2019-07-09: dependabot-preview[bot] Bump pillow from 6.0.0 to 6.1.0
+ 2019-07-09: dependabot-preview[bot] Bump tqdm from 4.31.1 to 4.32.2
+ 2019-07-09: afabiani - Improve Monitoring Request Filter Logging
+ 2019-07-09: afabiani - use correct resolved object when saving metadata
+ 2019-07-09: afabiani - use correct resolved object when saving metadata
+ 2019-07-08: dependabot-preview[bot] Bump pytz from 2018.9 to 2019.1
+ 2019-07-08: dependabot-preview[bot] Bump splinter from 0.10.0 to 0.11.0
+ 2019-07-08: Florian Hoedt switched to latest tags for docker images to
+ 2019-07-05: Sylvain POULAIN Update Dockerfile
+ 2019-07-04: dependabot-preview[bot] [Security] Bump django from 1.11.21 to 1.11.22
+ 2019-07-02: dependabot-preview[bot] Bump pytest-django from 3.4.8 to 3.5.1
+ 2019-07-02: dependabot-preview[bot] Bump python-slugify from 3.0.1 to 3.0.2
+ 2019-07-02: dependabot-preview[bot] Bump readthedocs-sphinx-ext from 0.5.17 to 0.6.0
+ 2019-07-02: Sylvain POULAIN Solve letsencrypt ssl error
+ 2019-07-01: Toni Added numpy for SPC
+ 2019-07-01: Toni Added numpy to requirements.txt
+ 2019-07-01: Toni Simplify pygdal installation
+ 2019-06-30: Francesco Bartoli Add dev mode with regular Docker (#4514)
+ 2019-06-30: Toni Schönbuchner fixed typos on start
+ 2019-06-29: Toni Schönbuchner added ansible to docs
+ 2019-06-28: Lorenzo Pini Add vim text editor, fix typos
+ 2019-06-28: gioscarda [docs] fixing typos in basic index
+ 2019-06-28: gioscarda [docs] GeoNode Project Themes
+ 2019-06-28: afabiani [Docs] Getting Started part 4
+ 2019-06-28: afabiani [Docs] Getting Started part 3
+ 2019-06-28: afabiani [Docs] Getting Started part 2
+ 2019-06-28: afabiani [Docs] Getting Started part 1
+ 2019-06-28: afabiani [Docs] Removing misleading contents
+ 2019-06-28: afabiani [Docs] Removing misleading contents
+ 2019-06-28: afabiani [Docs] Administering GeoNode: OAuth2 Access Tokens management
+ 2019-06-28: afabiani [Docs] finalize geonode-project debug setup and startup section
+ 2019-06-27: afabiani [Docs] Monitoring
+ 2019-06-27: capooti Implement #4429
+ 2019-06-27: afabiani [Docs] Manage the base metadata choices using the admin panel
+ 2019-06-27: afabiani [Docs] Announcemnts and Dismissals
+ 2019-06-27: afabiani - Add AnnouncementPermissionsBackend to default AUTHENTICATION_BACKENDS
+ 2019-06-27: afabiani [Closes #4584] Allow admins to decide if the Topic Category should be mandatory or not
+ 2019-06-27: afabiani [Closes #4584] Allow admins to decide if the Topic Category should be mandatory or not
+ 2019-06-27: gioscarda [docs] added groups management sections
+ 2019-06-27: afabiani [Static assets] Refresh/update static assets
+ 2019-06-27: afabiani [Dependencies] Updating monitoring static assets in order to remove vulnerabilities
+ 2019-06-26: Lorenzo Pini Update index.rst
+ 2019-06-26: gioscarda [docs] group categories and group profiles
+ 2019-06-26: afabiani [Fixes #4579] Set Thumbnail tool broken (GeoExplorer only)
+ 2019-06-26: afabiani - Allow members_only announcements also
+ 2019-06-26: afabiani [Docs] Manage Profiles, Layers, Maps and Documents from the Admin panel
+ 2019-06-26: afabiani - Show Contact Roles again on the Profile Admin panel
+ 2019-06-26: afabiani [Docs] Group based advanced data workflow
+ 2019-06-26: afabiani [Fixes #4575] Group Category summary page does not show associated group details
+ 2019-06-26: gioscarda [docs] manage users from admin
+ 2019-06-25: geosolutions [Optimization] optimize thumb generation algo
+ 2019-06-25: gioscarda [Docs] fixing typos
+ 2019-06-25: gioscarda accessing admin panel, change admin password
+ 2019-06-25: afabiani [Docs] Administering > Simple Theming
+ 2019-06-25: afabiani [Docs] Administering > Simple Theming (work in progress)
+ 2019-06-25: afabiani [Docs] Administering > Simple Theming (work in progress)
+ 2019-06-25: gioscarda fixing typos
+ 2019-06-25: gioscarda remote service types in user guide
+ 2019-06-25: Lorenzo Pini Typos in installation document
+ 2019-06-25: Toni Fixed SESSION_EXPIRED_CONTROL_ENABLED to True
+ 2019-06-25: gioscarda linking docs with resources explanation in user guide
+ 2019-06-25: afabiani [Docs] Administering index structure / Change the default langs
+ 2019-06-25: gioscarda fixing typos
+ 2019-06-25: gioscarda exif images docs
+ 2019-06-25: afabiani [Docs] About GeoNode / SPC GeoNode
+ 2019-06-25: gioscarda publishing data usage docs
+ 2019-06-24: Angelos Tzotsos Reclassifying requirements based on Ubuntu available packages
+ 2019-06-24: afabiani - fix travis
+ 2019-06-24: dependabot-preview[bot] Update django-jsonfield requirement from <=1.0.1 to <1.2.1
+ 2019-06-24: dependabot-preview[bot] Bump owslib from 0.16.0 to 0.17.1
+ 2019-06-24: afabiani [hardening] make sure we do not fall into dangeruos huge loops
+ 2019-06-24: dependabot-preview[bot] Bump django-activity-stream from 0.7.0 to 0.8.0
+ 2019-06-24: afabiani Bump Django MapStore Adapter to version 1.0.3
+ 2019-06-21: Toni Fixed Typos
+ 2019-06-21: afabiani - GeoServer Proxy should allow PUBLIC_LOCATION to fetch security headers too
+ 2019-06-21: gioscarda managing_maps
+ 2019-06-21: afabiani - Fix travis tests
+ 2019-06-21: afabiani - Fix GeoServer version to 2.14.3
+ 2019-06-21: afabiani - Travis-selenium tentative fix
+ 2019-06-21: afabiani [Fixes #4552] Improve/fix thumbnail generation for Layers and Maps at creation time
+ 2019-06-21: afabiani [Fixes #4553] Exception thrown when trying to upload features with non UTF-8 encoding
+ 2019-06-20: afabiani - Travis-selenium tentative fix
+ 2019-06-20: afabiani - Travis-selenium tentative fix
+ 2019-06-19: capooti Fixes #4534
+ 2019-06-19: afabiani - spcgeonode settings tune up
+ 2019-06-19: afabiani [Fixes #4547] "set_all_layers_metadata" deletes Thumbnail metadata links
+ 2019-06-19: dependabot-preview[bot] Bump pytest from 4.3.1 to 4.6.3
+ 2019-06-18: afabiani [Fixes #4545] MapStore2 print button broken with 1.1 client release
+ 2019-06-18: afabiani [Fixes #4543] Enforce GeoNode REST service API security
+ 2019-06-18: Alessio Fabiani [Closes #4532] Add search filters for groups and group categories (#4533)
+ 2019-06-18: dependabot-preview[bot] [Security] Bump twisted from 18.9.0 to 19.2.1
+ 2019-06-18: dependabot-preview[bot] Update pyproj requirement from <=1.9.5.1,>=1.9.5 to >=1.9.5,<2.2.1.0
+ 2019-06-18: dependabot-preview[bot] [Security] Bump urllib3 from 1.24.1 to 1.24.2
+ 2019-06-18: afabiani [Fixes #4541] Wrong permissions inconsistency message when changing layer permisions
+ 2019-06-18: Francesco Frassinelli Use python_requires
+ 2019-06-18: afabiani [Fixes #4538] Guardian returns the whole list of permissions associated with a resource.
+ 2019-06-18: dependabot-preview[bot] Bump pillow from 5.4.1 to 6.0.0
+ 2019-06-17: dependabot-preview[bot] Bump setuptools from 40.8.0 to 41.0.1
+ 2019-06-17: afabiani - updating requirements: django_geoexplorer-4.0.43
+ 2019-06-17: afabiani [Closes #4519] Option to regenerate resource links at migration or not
+ 2019-06-15: afabiani - updating requirements
+ 2019-06-15: afabiani - Fix get started link on index page
+ 2019-06-15: Toni Schönbuchner removed opensource heart
+ 2019-06-15: Toni Schönbuchner added image again
+ 2019-06-15: Toni Added logo, correct 404 to new docs.
+ 2019-06-14: afabiani Fix build
+ 2019-06-14: afabiani - Minor Adjustments to README and settings
+ 2019-06-14: Toni Added t-book to authors.
+ 2019-06-13: Francesco Frassinelli Add new geotiff for Travis CI (#4507)
+ 2019-06-13: Toni Push Travis again.
+ 2019-06-13: Florian Hoedt changed basemap for thumbnails to wikimedia, fixes #4459
+ 2019-06-13: Toni Schönbuchner Fixes: #4478
+ 2019-06-13: Florian Hoedt defuzzyied the translations
+ 2019-06-13: Florian Hoedt Update docker-compose.yml
+ 2019-06-13: Florian Hoedt fixes #4354
+ 2019-06-13: Florian Hoedt added postgis 10.x, importer, pgdumper for geo/nogeo dbs
+ 2019-06-13: Florian Hoedt merge with global master (#11)
+ 2019-06-12: afabiani [Fixes #4487] Issue adding remote services - FAILED - could not convert string to float: EPSG:4326
+ 2019-06-13: Toni Updated arabic translation
+ 2019-06-13: Toni Fixed OpenSource Image
+ 2019-06-12: florian fixes geoserver auth button #4335
+ 2019-06-12: florian fixes geoserver auth button #4335
+ 2019-06-12: Toni Fixed Readme Header
+ 2019-06-12: ppasq Make the menu item Invite users available for authenticated users only
+ 2019-06-12: Francesco Bartoli Close #4472
+ 2019-06-12: Francesco Frassinelli Add ISSUE_TEMPLATE.md
+ 2019-06-12: ppasq Make the user message disappear after 5 secs
+ 2019-06-12: ppasq Fix less file not aligned to corresponding css
+ 2019-06-09: afabiani - Docker optimizations
+ 2019-06-07: afabiani - Fix travis
+ 2019-06-06: afabiani [Links] Fix Metadata Catalogue Records Update
+ 2019-06-06: afabiani - Fix travis
+ 2019-06-06: afabiani [Links] fix download links bbox generation
+ 2019-06-06: afabiani - Travis fix
+ 2019-06-06: afabiani - Travis fix
+ 2019-06-06: afabiani - Travis fix
+ 2019-06-06: afabiani [Links] Remove old instances breaking the command execution
+ 2019-06-06: afabiani [Proxy Hotfix] self.username overrides user param
+ 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
+ 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
+ 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
+ 2019-06-06: afabiani [Settings] Make client hooksets auto-configurable via env GEONODE_CLIENT_LAYER_PREVIEW_LIBRARY
+ 2019-06-06: Alessio Fabiani Update requirements.txt
+ 2019-06-06: srto Store filename returned by storage API
+ 2019-06-05: Tobi Use Django file storage API to construct media URL
+ 2019-06-05: afabiani [Settings] Removing refuses
+ 2019-06-05: afabiani [Monitoring] relax host name constraint
+ 2019-06-03: afabiani - Travis fixes
+ 2019-06-03: afabiani - Improve docker build
+ 2019-06-03: afabiani [Fixes #4450] Thumbnails on layers not showing
+ 2019-06-03: afabiani [Settings] Restoring /gs to /geoserver by default
+ 2019-05-30: afabiani - MapStore2 Client as default
+ 2019-05-30: afabiani - MapStore2 Client as default
+ 2019-05-29: afabiani - MapStore2 Client as default
+ 2019-05-30: afabiani [Fixes #4447] Database locked when using sqlite/spatialite databases
+ 2019-05-30: Toni Schönbuchner fixes #4440 correct float on people profile small screens
+ 2019-05-30: Toni Schönbuchner Fixes: #4443 issues translations on index
+ 2019-05-29: afabiani - Removing contrib apps deps
+ 2019-05-29: afabiani [Monitoring] Decimal digits on model
+ 2019-05-28: afabiani [Fixes #4437] [Monitoring] collect_metrics often timeouts
+ 2019-05-28: afabiani [Fixes #4352] TemplateSyntaxError at trans in backups/confirm_cancel.html
+ 2019-05-28: afabiani - Fix travis
+ 2019-05-28: Alessio Fabiani [Fixes #4428] refactor linkedin field extraction (#4435)
+ 2019-05-28: afabiani - simplify the PR changes and get rid of UPDATE_THUMBS_ON_STYLE_CHANGE additional setting
+ 2019-05-28: afabiani [Minor] Hardening GeoNode checks and settings
+ 2019-05-28: afabiani [Minor] Hardening GeoNode checks and settings
+ 2019-05-27: afabiani - Fix Tests
+ 2019-05-27: afabiani [Minor] Hardening GeoNode checks
+ 2019-05-27: afabiani - Fix Tests
+ 2019-05-27: afabiani GeoNode Cleanup and Test Coverage
+ 2019-05-27: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / database_shards,worldmap,geotiffio,geosites extracted
+ 2019-05-27: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / favorite,exif,monitoring promoted / slack,nlp,mp removed
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
+ 2019-05-23: capooti Add UPDATE_THUMBS_ON_STYLE_CHANGE setting - refs #4092
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: Jonathan Doig Fix TemplateSyntaxError at trans
+ 2019-05-23: Jonathan Doig Fix Geonode issue 4352
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / createlayer promoted
+ 2019-05-23: afabiani [Ref #4311] GNIP: Contrib apps cleanup on GeoNode / metadataxsl promoted
+ 2019-05-22: afabiani [Minor Improvements] Few more checks with the email backend
+ 2019-05-22: afabiani [Fixes #4424] Reverting back to GeoServer 2.14.2
+ 2019-05-21: afabiani - Convert pinax templates bodies to HTML
+ 2019-05-21: geosolutions - Custom PINAX Email Backend: allows html notifications
+ 2019-05-20: afabiani [Fixes #4417] 'ows_api' for GetCapabilities, should provide public urls anyway
+ 2019-05-20: afabiani [Fixes #4418] General SLD management misbehavior
+ 2019-05-19: Alessio Fabiani Update requirements.txt
+ 2019-05-19: afabiani [Hardenining] Avoid hard failure if GeoServer resource is not reachable
+ 2019-05-19: afabiani [Closes #4415] Upgrade PyCSW version to 2.4.0
+ 2019-05-17: afabiani [Fixes #4410] Avoid re-writing thumbnail every time a Layer is saved
+ 2019-05-17: Francesco Bartoli Revert "Allow for https as public protocol"
+ 2019-05-17: afabiani [Closes #4408] Expose 'settings' variable to env
+ 2019-05-17: afabiani [Monitoring] timeout decorator to collect_metrics management command; avoids stucking on db connections
+ 2019-05-17: Jonathan Doig Updates as requested for Geonode PR 4403
+ 2019-05-17: Jonathan Doig Infer protocol from port if defined
+ 2019-05-17: afabiani [Monitoring] timeout decorator to collect_metrics management command; avoids stucking on db connections
+ 2019-05-16: afabiani [Close #4405] [GeoNode 2.10] Upgrade GeoServer version to 2.15.1
+ 2019-05-16: afabiani [Fixes #4404] Performance issue with resource base apis
+ 2019-05-16: afabiani [Maps Model] Removing CharField 200 length limitation
+ 2019-05-16: Jonathan Doig Add default GEONODE_LB_PROTOCOL to docker-compose files
+ 2019-05-16: Jonathan Doig Allow for https as public protocol
+ 2019-05-15: afabiani [Minor fix] Do not break the map if cannot read the syles from GeoServer
+ 2019-05-15: afabiani [Minor updates] Aligning with 2.8.2
+ 2019-05-15: afabiani [Release] GeoNode 2.8.1
+ 2019-05-15: afabiani [Release] Preparing 2.8rc1
+ 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
+ 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
+ 2019-05-14: geosolutions [Cleanup] cleanup settings and exposing some of them as env variables
+ 2019-05-13: Hayden Elza Spelling
+ 2019-05-13: afabiani - Removing geonode.contrig.sites app by default
+ 2019-05-13: afabiani [Minor fix] do not overwrite thumbnail if it already exists
+ 2019-05-10: geosolutions [Template] minor improvements
+ 2019-05-10: afabiani - More Theme options
+ 2019-05-10: afabiani [Themes] Add more nav customization options
+ 2019-05-10: geosolutions [Template] typo
+ 2019-05-10: afabiani [Themes] Add more nav customization options
+ 2019-05-10: afabiani [Minor fixes] settings typos and fixoauth mgt commands
+ 2019-05-10: afabiani [Minor fixes] settings typos and fixoauth mgt commands
+ 2019-05-10: afabiani [Minor improvements] Allow requests get to use timeouts / expose Monitoring variables
+ 2019-05-09: Alessio Fabiani Update local_settings.py.geoserver.sample
+ 2019-05-09: Alessio Fabiani revert wrong setting
+ 2019-05-09: afabiani [Minor optimizations] settings X_FRAME_OPTIONS allow localhost / add geoserver local services paths to geoserver proxy
+ 2019-05-08: afabiani - [Minor fix] Proxy will attach access_token only if remote netloc matches the local one
+ 2019-05-06: Angelos Tzotsos Update Greek translations
+ 2019-05-04: Angelos Tzotsos Regenerate Greek translations
+ 2019-05-04: Alessio Fabiani Remove refuses from index.html
+ 2019-05-03: afabiani [Closes #4384] Add a Cookies & Privacy policy customization section to GeoNode Themes
+ 2019-05-02: afabiani [Minor Refactoring] Avoid code duplication: centralize 'create_geoserver_db_featurestore' on geoserver helpers classes only
+ 2019-05-02: Toni Schönbuchner Rebuild static files, missing in #4380
+ 2019-04-30: afabiani [Minor fix] Trying to parse geoserver error messages in a cleanest way
+ 2019-04-30: Toni Delete dsgvo.html
+ 2019-04-30: Florian Hoedt fixes profile text overflow UI glitch #4376
+ 2019-04-29: afabiani [Minor] Improves Proxy response error status message
+ 2019-04-29: afabiani [Minor fix] allow sld POST request since the user might want to create a new style
+ 2019-04-29: afabiani [Closes #4377] Introduce a 'style-check' endpoint for style edit permission checks
+ 2019-04-29: Ahmed Nour Eldeen update geosites contrib app to be work with geonode 2.10 (#4287)
+ 2019-04-28: Toni Schönbuchner Fixes: #4374, added missing quote to trans
+ 2019-04-28: Toni Update German translation
+ 2019-04-26: afabiani [Closes #4369] List of strings without trans tags and not listed in .po file
+ 2019-04-25: Toni Schönbuchner Fix #4368 Announcement text layout overlaps on index.html
+ 2019-04-24: afabiani - fix test case
+ 2019-04-23: capooti Bump django-geoexplorer-worldmap 4.0.67
+ 2019-04-23: afabiani - django-geoexplorer 4.0.42: Fixing GeoServerStyleWriter plugin
+ 2019-04-23: afabiani [Fixes #4357] tags do not support unicode text
+ 2019-04-23: afabiani - Fix adv settings docs titeling
+ 2019-04-23: afabiani - minor integrity checks
+ 2019-04-23: afabiani - Fixes/improx as per reviews
+ 2019-04-23: afabiani - pin urllib3 version conflicting with geonode-oauth-toolkit requirements
+ 2019-04-18: afabiani - Bump version to 2.10rc5
+ 2019-04-15: Angelos Tzotsos Settings fix for geosites, patch from debian package
+ 2019-04-15: Angelos Tzotsos Fixed missing debian changelog records
+ 2019-04-12: Toni Schönbuchner #4338 fix thumbnail creation with different bbox
+ 2019-04-10: afabiani - Geonode logo as favicon
+ 2019-04-10: afabiani - Text fixes
+ 2019-04-10: afabiani [Fixes #4321] GeoNode is not able to show errors properly on upload / [Closes #4241] Upload failure: saved_layer is None
+ 2019-04-09: afabiani - Tentative fix tests
+ 2019-04-09: afabiani [Issue #4325] List of documentation improvements
+ 2019-04-09: afabiani - Docs: updates to advanced settgins and docs version
+ 2019-04-09: afabiani - Add 'Syncronize button immediately' to Layers List too
+ 2019-04-08: hisham waleed karam add outh test
+ 2019-04-09: afabiani - Added docs for DELAYED SECURITY settings also
+ 2019-04-09: afabiani - SESSION_EXPIRED var to False by Default
+ 2019-04-08: afabiani - Restore session expire messages, but using cookie storeage instead of session
+ 2019-04-06: Toni Schönbuchner #4348 fix failing read_the_docs
+ 2019-04-05: Francesco Frassinelli 8 KB buffer for uwsgi
+ 2019-04-04: afabiani - Fix test cases
+ 2019-04-04: Francesco Frassinelli 8 KB buffer for spc uwsgi
+ 2019-04-04: Francesco Frassinelli Set SESSION_EXPIRED_CONTROL_ENABLED for SPC
+ 2019-04-04: afabiani [Fixes #4322] SESSION_EXPIRED_CONTROL_ENABLE=True breaks GeoNode
+ 2019-04-04: Francesco Frassinelli Polish tests
+ 2019-04-04: Francesco Frassinelli Remove failing test
+ 2019-04-04: afabiani [Fixes #4322] SESSION_EXPIRED_CONTROL_ENABLE=True breaks GeoNode
+ 2019-04-04: Francesco Frassinelli GeoServer web UI url default fixed
+ 2019-04-03: afabiani GNIP: Improvements to GeoNode Layers download links - original dataset option in a migration
+ 2019-04-03: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
+ 2019-04-03: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
+ 2019-04-03: afabiani [Fixes #4333] Upload from GUI doesn't handle projection properly
+ 2019-04-03: Francesco Bartoli Change conversion to 5-digits round
+ 2019-04-03: Francesco Bartoli Convert for integer comparison
+ 2019-04-03: afabiani [Fixes #4333] Upload from GUI doesn't handle projection properly
+ 2019-04-01: afabiani [GNIP #3944] Make GeoFence efficient for GeoNode instances with a large number of layers
+ 2019-03-29: afabiani - Show Original Data Set links also
+ 2019-03-29: afabiani [Closes #4323] Layers Download Links need some care
+ 2019-03-28: Jeremiah Cooper Remove awesome_slugify and all references to it, replace with python-slugify which is a newer and still active replacement
+ 2019-03-28: Francesco Frassinelli Revert "add async_thumbnail to improve upload time"
+ 2019-03-26: Jeremiah Cooper Allow Travis CI to run on the 2.20.x branch
+ 2019-03-26: Jeremiah Cooper Remove unused dependency coreschema
+ 2019-03-26: Jeremiah Cooper Upgrade django-polymorphic from version 1.3.1 to 2.0.3
+ 2019-03-26: Jeremiah Cooper Upgrade Pillow from version 3.3.2 to version 5.4.1
+ 2019-03-26: Francesco Frassinelli Fix GeoServer wait for Postgres
+ 2019-03-26: Jeremiah Cooper Update pinax-notifications from 4.1.0 to 5.0.3.
+ 2019-03-26: afabiani [Closes #4312] Remove GeoGig and api_basemaps contrib apps integrated stuff
+ 2019-03-22: afabiani - [Closes #4308] Allow users to refresh layer attributes and statistics through both GeoNode UI and Management Commands
+ 2019-03-21: Jeremiah Cooper Upgrade invoke from 0.22.1 to 1.2.0
+ 2019-03-21: Jeremiah Cooper Upgrade oauthlib from version 2.1.0 to version 3.0.1
+ 2019-03-21: Jeremiah Cooper Add 2.20 to the list of branches that Travis CI runs tests against.
+ 2019-03-21: Jeremiah Cooper Remove nose as we're not using it for any of the tests anymore.
+ 2019-03-21: Jeremiah Cooper Update TEST_RUN_INTEGRATION_BDD to install geckodriver and run firefox as headless.
+ 2019-03-20: Jeremiah Cooper Flake8 fixes.
+ 2019-03-20: Jeremiah Cooper Ensure selenium with the firefox geckodriver web driver is available for BDD tests.
+ 2019-03-20: Jeremiah Cooper Resolve failing BDD tests.
+ 2019-03-20: Jeremiah Cooper The latest version of splinter no longer supports phantomjs, use the default firefox driver instead.
+ 2019-03-20: Jeremiah Cooper Don't install specific versions of the testing dependencies for BDD tests, use what we already have in requirements.txt
+ 2019-03-20: Jeremiah Cooper Resolve remaining flake8 issues.
+ 2019-03-20: Jeremiah Cooper Resolve all W605 invalid escape sequence flake8 errors. https://lintlyci.github.io/Flake8Rules/rules/W605.html
+ 2019-03-20: Jeremiah Cooper Newer versions of flake8 are stricter and have more findings. Use autopep8 to resolve some of these.
+ 2019-03-20: Jeremiah Cooper Modify flake8 settings to extend the default ignore list, instead of completely overwriting it.
+ 2019-03-19: Francesco Bartoli Align all settings with GEOSERVER_WEB_UI_LOCATION default value
+ 2019-03-19: Francesco Bartoli Fix #4298
+ 2019-03-19: Jeremiah Cooper Clean up unused testing dependencies and upgrade current testing dependencies
+ 2019-03-19: Jeremiah Cooper Remove unused jenkins files and pycodestyle dependency.
+ 2019-03-19: afabiani - GEOSERVER_WEB_UI_LOCATION default value should point to localhost 8080
+ 2019-03-19: afabiani - Typo on request headers key retrieval
+ 2019-03-19: afabiani - Introducing Proxy Max Retries Option
+ 2019-03-19: afabiani - Fix Travis
+ 2019-03-18: Jeremiah Cooper Update requirements_docs.txt to reflect the latest dependency changes.
+ 2019-03-18: Jeremiah Cooper Remove unnecessary Ubuntu package comments.
+ 2019-03-18: afabiani - Restore codecov status check
+ 2019-03-18: afabiani [Fixes #4293] CSW download link: metadata are empty
+ 2019-03-18: Jeremiah Cooper Upgrade various python dependencies to their latest compatible versions.
+ 2019-03-18: afabiani - 'base.auth' must skip None or Anonymous users
+ 2019-03-18: afabiani - Make sure we use "basic.auth" whenever we check for 'access_token'
+ 2019-03-17: afabiani - Fix Travis tests
+ 2019-03-17: afabiani - Trevis tests fix
+ 2019-03-17: afabiani - Trevis tests fix
+ 2019-03-16: afabiani - Revert commit breaking tests
+ 2019-03-16: afabiani - Fix test cases
+ 2019-03-15: afabiani - Hardening GeoNode and cleanup: http_client uses Bearer Auth whenever it's possible
+ 2019-03-15: afabiani - Fix smoke test cases
+ 2019-03-15: afabiani - Send warning message whenever the session has expired
+ 2019-03-15: afabiani - Hardening GeoNode and cleanup: http_client uses Bearer Auth whenever it's possible
+ 2019-03-14: afabiani [Closes #4106] [Remote Services] Add the possibility of filtering the list of resources
+ 2019-03-14: afabiani [Closes #4251] Introduce a SessionExpiredMiddleware in order to checkfor access_token validity
+ 2019-03-14: Ahmed Nour Eldeen add color picker to theme admin
+ 2019-03-14: Ahmed Nour Eldeen fix geoserver public location
+ 2019-03-13: afabiani - Proxy: use requests instead of old fashion httplib
+ 2019-03-13: Ahmed Nour Eldeen fix geoserver default public location
+ 2019-03-12: Jeremiah Cooper Use pycodestyle<2.4.0 until we upgrade the other related dependencies.
+ 2019-03-08: Jeremiah Cooper pep8 dependency is deprecated, remove and replace it with pycodestyle
+ 2019-03-12: afabiani - Minor: double checks action data format str vs. json
+ 2019-03-11: Jeremiah Cooper Remove unnecessary dependencies in debian control file to mirror the removals in requirements.txt.
+ 2019-03-08: Jeremiah Cooper Update requirements_docs.txt to reflect changes in requirements.txt.
+ 2019-03-08: Jeremiah Cooper Remove unused Pinax, WeasyPrint and debug dependencies
+ 2019-03-12: Ahmed Nour Eldeen fix csw tests
+ 2019-03-12: Ahmed Nour Eldeen remove redundant settings
+ 2019-03-12: Ahmed Nour Eldeen Update settings.py
+ 2019-03-11: Ahmed Nour Eldeen remove unused and misleading settings
+ 2019-03-07: capooti Bump django-geoexplorer-worldmap 4.0.64
+ 2019-03-07: Francesco Frassinelli Add frafra to AUTHORS
+ 2019-03-07: Francesco Frassinelli Run dos2unix on .env files
+ 2019-03-05: afabiani - Use access_token when parsing layer attributes
+ 2019-03-05: Toni Schönbuchner Updated german translation
+ 2019-03-05: Francesco Frassinelli Fix Postgres wait on SPC GeoServer
+ 2019-02-28: afabiani [minor] removing redoundant middleware class from settings
+ 2019-02-27: afabiani [Fixes #4254] Pavement YAMLLoadWarning
+ 2019-02-26: Jeremiah Cooper Adds back pyopenssl to requirements.txt.
+ 2019-02-26: Jeremiah Cooper Removes unnecessary dependencies from the requirements.txt and settings.py files.
+ 2019-02-22: Francesco Frassinelli Disable system_site_packages for geonode-selenium
+ 2019-02-22: capooti Bump django-geoexplorer-worldmap==4.0.63
+ 2019-02-22: afabiani - Fix smoke test cases
+ 2019-02-22: afabiani - Fix smoke test cases
+ 2019-02-22: afabiani [Closes #4249] Contribute back upstream menu management from IGAD
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani Fixes Travis geonode-selenium build
+ 2019-02-21: afabiani [Fixes #4247] 404 error on CSV upload
+ 2019-02-20: Francesco Frassinelli Import SSL Certificate for GeoServer
+ 2019-02-15: Francesco Frassinelli Test with geonode-selenium
+ 2019-02-19: Denis Rykov Typo fix
+ 2019-02-19: Francesco Frassinelli Add support for plugins
+ 2019-02-19: Francesco Frassinelli Update GeoServer to 2.14.2
+ 2019-02-18: Francesco Frassinelli Improve comment about POSTGRES_PASSWORD
+ 2019-02-18: Francesco Frassinelli Use DATABASE_URL in pgdumper
+ 2019-02-18: Francesco Frassinelli Use custom Postgres password
+ 2019-02-18: Francesco Frassinelli Add ON_ERROR_STOP=1
+ 2019-02-18: Francesco Frassinelli Remove extra Postgres wait
+ 2019-02-17: Hisham waleed karam use 'update' queue instead if using the default
+ 2019-02-14: capooti Fixed a broken link in WorldMap documentation
+ 2019-02-14: Francesco Frassinelli Add suffix geoserver to BASEURL
+ 2019-02-14: afabiani - add missing header to Python file
+ 2019-02-14: hisham waleed karam fix Object of type Map is not JSON serializable
+ 2019-02-14: Francesco Frassinelli Use geoserver instead of gs (partial revert)
+ 2019-02-14: hisham waleed karam tastypie OAuth Backend
+ 2019-02-13: Francesco Frassinelli Wait for GeoServer and PostgreSQL instead of failing
+ 2019-02-14: Francesco Frassinelli Add rabbitmq volume to main docker-compose.yml
+ 2019-02-13: hisham waleed karam use regex to extract schema
+ 2019-02-13: afabiani [minor] There was a wrong 'expiring' check on purging old tokens
+ 2019-02-13: Francesco Frassinelli Add rabbitmq volume
+ 2019-02-13: Ahmed Nour Eldeen for haystack also get 'vector_time' layers when 'vector' type is selected as 'vector' is super type of 'vector_time'(same behavior of rest api)
+ 2019-02-13: Ahmed Nour Eldeen filter by vector time layers is not working when haystack is enabled. as for vector_time layer, 'vectorTimeSeries' is indexed instead of 'vector_time'
+ 2019-02-12: Alessio Fabiani Update requirements_docs.txt
+ 2019-02-12: Alessio Fabiani Update requirements.txt
+ 2019-02-12: Alessio Fabiani Update requirements_docs.txt
+ 2019-02-12: Alessio Fabiani Update requirements.txt
+ 2019-02-12: afabiani [Related to #4219] - mitigates the issue: Delayed Security Sync Task seems causing issues with sqlite queries
+ 2019-02-11: Francesco Bartoli Remove not used settings sample
+ 2019-02-11: Francesco Bartoli Revert " Includes additional layer fields in the search index" (#4217)
+ 2019-02-11: Francesco Bartoli Fix missing replacement of geoserver public location (#4215)
+ 2018-09-12: hisham waleed karam Make sure geogig can be created in a schema other than public
+ 2019-02-07: hisham waleed karam remove unused import
+ 2019-02-08: giohappy moved login/logout callbacks to profile module; renamed oauth utils to auth
+ 2019-02-07: capooti Remove unwanted line of code in worldmap client
+ 2019-02-07: hisham waleed karam use celery task to generate thumbnails
+ 2019-02-07: hisham waleed karam Fix handling of missing layers.
+ 2019-02-07: hisham waleed karam disable threading in test
+ 2019-02-06: hisham waleed karam use celery first if enabled
+ 2019-02-06: hisham waleed karam add async_thumbnail to improve upload time
+ 2018-09-12: Ahmed Nour Eldeen Includes additional fields in the search index
+ 2019-02-06: Francesco Frassinelli Use gs instead of geoserver for URLs in Docker
+ 2019-02-05: afabiani [Security Cleanup] - Remove unuseful and potentially blocking calls from signals and login/out calls
+ 2019-02-05: Alessio Fabiani Cleaning up session if no valid access_token key has been found
+ 2019-02-05: Alessio Fabiani Update oauth.py
+ 2019-02-05: afabiani - Backporting GEOSERVER_WEB_UI_LOCATION to sample local settings
+ 2019-02-04: giohappy fixed exception retrieving token object from empty session token
+ 2019-02-04: Alessio Fabiani Adding all Oauth2 endpoints to lockdown exempt uris
+ 2019-02-04: giohappy fixed another pep8 issue
+ 2019-02-04: giohappy fixed pep8 issues
+ 2019-02-04: giohappy fixed mixing of token object and string in login/logout
+ 2019-02-01: hisham waleed karam fix flake8
+ 2019-02-01: hisham waleed karam fix flake8
+ 2019-01-31: afabiani - fix pavemenet cmdopts options
+ 2019-01-31: afabiani - Travis Fix & Optimize
+ 2019-01-30: capooti Related to fix for #4178
+ 2019-01-30: hisham waleed karam fix force list status code
+ 2019-01-30: afabiani - Split Travis integration tests into 3 different tasks
+ 2019-01-30: hisham waleed karam fix import
+ 2019-01-30: hisham waleed karam use requests retry to improve geonode/geoserver connection
+ 2019-01-30: giohappy proxy auth refactoring with extended checks
+ 2019-01-30: hisham waleed karam fix layer_thumbnail returned None instead.
+ 2019-01-30: hisham waleed karam fix map_thumbnail returned None instead.
+ 2019-01-30: hisham waleed karam improve map_thumbnail view
+ 2019-01-30: hisham waleed karam improve layer_thumbnail view
+ 2019-01-29: capooti Added chinese to ALL_LANGUAGES enumeration
+ 2019-01-29: giohappy introduce new GEOSERVER_WEB_UI_LOCATION options
+ 2019-01-29: Florian Hoedt update to PostgreSQL 10
+ 2019-01-28: afabiani - Added also '/api/layers' as per giohappy review
+ 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
+ 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
+ 2019-01-25: afabiani [Closes #4183] Fix and improve LOCKDOWN_GEONODE mechanism
+ 2019-01-25: Alessio Fabiani Make flake8 happy
+ 2019-01-25: afabiani [Fixes #4181] WMS links are not created for Remote services
+ 2019-01-25: giohappy Geoserver menu link shouldn't be proxied
+ 2019-01-25: afabiani [Fixes #4178] - Unable to create maps with access_token no longer string
+ 2019-01-24: giohappy removed test on links in layers list output
+ 2019-01-24: giohappy make flake happy
+ 2019-01-24: giohappy show OGC links inside layers list from the API
+ 2019-01-24: afabiani - Fix pep8 issues
+ 2019-01-24: afabiani [Fixes #4174] - Proxy should pass Bearer authentication to Geoserver transparently
+ 2019-01-24: afabiani [Fixes #4174] - Proxy should pass Bearer authentication to Geoserver transparently
+ 2019-01-24: afabiani [Fixes #304] Proxy should pass Bearer authentication to Geoserver transparently
+ 2019-01-24: afabiani - Removing duplicate code
+ 2019-01-23: afabiani [Fixes #4170] Clean management of OAuth2 Access Tokens
+ 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
+ 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
+ 2019-01-23: afabiani [Fixes #4168] GeoNode Proxy should be using OAUTH2 Bearer Header now
+ 2019-01-22: gannebamm missed to revert the celery.env to default
+ 2019-01-22: admin added ALLOWED_DOCUMENT_TYPES and MAX_DOCUMENT_SIZE as environmental setting to django.env and celery.env. Modified settings.py to read these (doc size was allready implemented)
+ 2019-01-22: gannebamm changed some environmental vars back to default
+ 2019-01-22: admin added ALLOWED_DOCUMENT_TYPES and MAX_DOCUMENT_SIZE as environmental setting to django.env and celery.env. Modified settings.py to read these (doc size was allready implemented)
+ 2019-01-21: afabiani - Making OAUTH2 Access Token expiration seconds configurable by settings/env
+ 2019-01-17: capooti Bump django-geoexplorer-worldmap 4.0.62
+ 2019-01-17: Francesco Frassinelli Replace exit with a valid command (#4158)
+ 2019-01-16: capooti urlsuffix can be numeric. Fixes #4159
+ 2019-01-16: Francesco Frassinelli Typos and extra spaces removed
+ 2019-01-15: Alessio Fabiani Tentative fix readthedocs build
+ 2019-01-11: afabiani [Fixes #4153] GeoLite Legacy db discontinued so paver setup fails in Geonode 2.8 dev install
+ 2019-01-08: Alessio Fabiani [Fixes #4150] - Security vulnerabilities on deps (#4151)
+ 2019-01-02: Toni Schönbuchner removed windows binary from docs
+ 2019-01-02: Toni Schönbuchner removed windows and apt from readme
+ 2018-12-21: Francesco Frassinelli Fix geonode.binary arguments escaping
+ 2018-12-21: Francesco Frassinelli Fix geonode.binary arguments escaping
+ 2018-12-20: Toni Schönbuchner Restored older GeoServer Version #4120
+ 2018-12-19: afabiani - default OGC timeout to 60 secs
+ 2018-12-19: Paolo Corti Fixes #4132 worldmap client print template (#4133)
+ 2018-12-19: Paolo Corti Fixes #4134 handles multiple groups for a new map in worldmap client (#4135)
+ 2018-12-19: Paolo Corti Improvements to autocomplete. Fixes #4136 (#4137)
+ 2018-10-31: Alessio Fabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters (#4026)
+ 2018-12-17: afabiani - vulnerability issues: urllib3 bump to version 1.24.1
+ 2018-12-14: afabiani - proj deps
+ 2018-12-14: afabiani - re-enable django_filters not included into INSTALLED_APPS
+ 2018-12-14: afabiani - Sentinel2 Backgroung Title refs
+ 2018-12-12: capooti Add the sync_geonode_layers command. Fixes #4115
+ 2018-12-12: capooti Fixes #4117
+ 2018-12-12: afabiani - 'Get Started' inherits the Jumbotron paragraph style
+ 2018-12-10: afabiani - Allow GeoServer Proxy to recognize prefix on non-root paths also
+ 2018-12-07: afabiani - Fix small autoescape issue on detail templates
+ 2018-12-06: Francesco Frassinelli Fix codecov link
+ 2018-12-05: capooti [worldmap-client] Makes Google StreetView optional. Bump django-geoexplorer-worldmap 4.0.61. Remove some unused code
+ 2018-12-05: afabiani - Silencing deprecated warnings
+ 2018-12-05: afabiani - Silencing deprecated warnings
+ 2018-12-05: afabiani - local_settings.py.geoserver update ms2 backgrounds / catalog takes siteurl / allowed hosts checking for env vars
+ 2018-12-05: afabiani [Fixes #4105] [Remote Services] harvesting resources pagination does not keep memory of the selection if changing page
+ 2018-12-04: capooti When using worldmap client we need geoserver layers to use gxp_gnsource vs gxp_wmscsource
+ 2018-12-03: afabiani - Revert wrong setting
+ 2018-12-03: afabiani - E-mail Tasks Bindings
+ 2018-12-03: afabiani - Remote Services Async Tasks Binding
+ 2018-11-30: afabiani - Layed Detail: legend url and title accoringly to the current style selected
+ 2018-11-29: capooti Port the worldmap map notes application. Fixes #4101
+ 2018-11-29: afabiani - Remove wrong sample layers from local_settings geoserver sample
+ 2018-11-29: afabiani - View Layer should not behave as Edit Layer
+ 2018-11-29: afabiani - Finalize: Update docker compose files and refresh of JS assets
+ 2018-11-29: afabiani - Removing unused crontab dependency
+ 2018-11-28: capooti Bump django-geoexplorer-worldmap 4.0.60
+ 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
+ 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
+ 2018-11-28: afabiani - Finalize: Update docker compose files and refresh of JS assets
+ 2018-11-28: afabiani - pep8 issues
+ 2018-11-28: afabiani - Refreshing static assets
+ 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
+ 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
+ 2018-11-28: afabiani - Aligning and improving the docker-compose settings
+ 2018-11-28: afabiani - Refreshing static assets
+ 2018-11-28: afabiani - Fix the way how to Style Manage page fetches the styles: read them from GeoNode and align with the GeoServer instance
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-28: afabiani - Update docker compose files
+ 2018-11-26: afabiani - align Layer Detail page style with the Map Detail one
+ 2018-11-26: afabiani - minor updates to local_settings.py.geoserver.sample
+ 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
+ 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
+ 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
+ 2018-11-22: afabiani - Minor Refactoring: Edit Map buttons names
+ 2018-11-16: capooti Fixes #4089: identify does not work on layers added from the search interface
+ 2018-11-15: capooti Fixes #4086: worldmap gazetteer fails to be updated when field names are not ascii
+ 2018-11-15: capooti Detect if client is mobile and ask to use wm mobile client
+ 2018-11-15: afabiani - Proxy: include "DELETE" operation also when checking for Auth headers
+ 2018-11-15: afabiani - Proxy: include "DELETE" operation also when checking for Auth headers
+ 2018-11-14: capooti [worldmap-client] Fixes #4082: remove unneeded tools from map details page
+ 2018-11-14: afabiani - Display only default style legend at startup
+ 2018-11-14: afabiani - Display only default style legend at startup
+ 2018-11-14: Alessio Fabiani - geoserver proxy style check fix
+ 2018-11-14: afabiani - minor log formatting
+ 2018-11-14: Alessio Fabiani - geoserver proxy style check fix
+ 2018-11-14: afabiani - minor log formatting
+ 2018-11-14: afabiani - minor log formatting
+ 2018-11-13: capooti Fixes #4080 (worldmap client "layers list" failing with new maps)
+ 2018-11-13: afabiani - Fix GeoServer Signals
+ 2018-11-13: afabiani - Fix GeoServer Signals
+ 2018-11-13: afabiani [Closes #4077] Update Celery and Kombu libraries
+ 2018-11-13: afabiani [Closes #4077] Update Celery and Kombu libraries
+ 2018-11-13: afabiani - Allow multiple Style Legends
+ 2018-11-13: afabiani - Allow multiple Style Legends
+ 2018-11-13: afabiani [Fixes #4075] GeoServer REST Proxy cannot handle SLD DELETE and POST if no SLD body has been provided
+ 2018-11-13: afabiani [Fixes #4075] GeoServer REST Proxy cannot handle SLD DELETE and POST if no SLD body has been provided
+ 2018-11-13: Hisham waleed karam fix ident_json view
+ 2018-11-12: capooti [worldmap-client] Improvements and fixes on the Gazetteer. Fixes #4072.
+ 2018-11-09: capooti Localize some strings in the worldmap composer
+ 2018-11-09: capooti Fixes #4068: remove duplicate keywords field in adanced metadata editing
+ 2018-11-09: Alessio Fabiani - Allow pluggable GIS client to hook their custom Style Edit Page (#4067)
+ 2018-11-09: afabiani - Allow pluggable GIS client to hook their custom Style Edit Page
+ 2018-11-09: capooti Fixes #4064: resources sort broken on Firefox
+ 2018-11-09: afabiani - Filter Remote Services Links to the REST MAP
+ 2018-11-09: afabiani - Filter Remote Services Links to the REST MAP
+ 2018-11-09: afabiani - fix localsettings sample
+ 2018-11-08: capooti Disable numeric fields from feature search. Fixes #4062
+ 2018-11-08: afabiani - Fix tests
+ 2018-11-08: afabiani - Improve Thumbnail method so that it can include a background also [Refers #3982]
+ 2018-11-08: afabiani - set to completed step to check if no _ALLOW_TIME_STEP is false [Refers #3800]
+ 2018-11-08: afabiani - Cleanup local_settings.py.geoserver.sample
+ 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
+ 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
+ 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
+ 2018-11-08: afabiani - upload_session.completed_step = 'time' if _ALLOW_TIME_STEP else 'check'
+ 2018-11-08: afabiani - Thumbnail Generation call at "finished" messages only
+ 2018-11-07: capooti [worldmap-client] Now attributes order is honored when identifying features. Fixes #4060
+ 2018-11-07: Paolo Corti [Worldmap client] Uses now WFS for GetFeatureInfo only if user has download permissions. Otherwise uses WMS. Fixes #4058 (#4059)
+ 2018-11-07: afabiani - Settgins Optimizations
+ 2018-11-07: afabiani - Fix resource api exception
+ 2018-11-07: afabiani - Fix resource api exception
+ 2018-11-06: Alessio Fabiani Fixes some broken links as per #4047 (#4055)
+ 2018-11-06: afabiani - JS siteUrl trailing slash if not present
+ 2018-11-06: afabiani - JS siteUrl trailing slash if not present
+ 2018-11-06: afabiani - Make sure only layers have been added to the cart session
+ 2018-11-06: afabiani - Make sure only layers have been added to the cart session
+ 2018-11-06: afabiani - Update static libs
+ 2018-11-06: afabiani - Update static libs
+ 2018-11-06: afabiani - Fix security test cases
+ 2018-11-05: capooti Fixes #4056 (uncorrectly set WPS in sync_geofence_with_guardian)
+ 2018-11-05: afabiani Fixes some broken links as per #4047
+ 2018-11-05: afabiani - minor update to local_settings geoserver sample
+ 2018-11-05: afabiani [Fixes #4053] save map broken on 2.8 branch?
+ 2018-11-01: capooti Avoid duplication of layers when using link (snapshots) in worldmap client. Refs #4046
+ 2018-10-31: capooti Fixes layers duplication introduced with a snapshot. Refs #4046
+ 2018-10-31: Alessio Fabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters (#4026)
+ 2018-10-31: afabiani - Fix test cases
+ 2018-10-31: afabiani - Rename columns of non-UTF-8 shapefiles attributes before ingesting
+ 2018-10-31: afabiani - Rename columns of non-UTF-8 shapefiles attributes before ingesting
+ 2018-10-30: Paolo Corti Fixes #4044 (broken comments when using worldmap client) (#4045)
+ 2018-10-30: Alessio Fabiani [Fixes #4040] GeoServer SLD POST may fail if style does not already exist locally. (#4041)
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
+ 2018-10-30: afabiani [Fixes #4039] GeoNode 'proxy' removes original url netloc from the underlying request
+ 2018-10-30: afabiani [Fixes #4038] Remove Django deps known vulnerabilities #4038
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: afabiani - Proxy Improvements
+ 2018-10-30: afabiani - Python vulnerabilities
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: capooti Fixes #4036
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: Paolo Corti Fixes #4031 (#4032)
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: afabiani - Fix test cases
+ 2018-10-30: afabiani - fix test cases
+ 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files - rebuild js boundles
+ 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files - rebuild js boundles
+ 2018-10-30: afabiani [Fixes #3775] Hard-coded urls to static-files and media-files
+ 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
+ 2018-10-29: capooti Fixes #4033
+ 2018-10-30: Alessio Fabiani [Closes #4029] Metadata Download Link for RemoteServices should report the Service Type instead of a generic 'http-download' (#4030)
+ 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
+ 2018-10-29: afabiani [Refers #3775] Hard-coded urls to static-files and media-files
+ 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
+ 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
+ 2018-10-29: afabiani - Push RemoteService Type on DownloadLinks Metadata
+ 2018-10-29: afabiani [Fixes #4025] Regression with uploading a shapefile with no ascii characters
+ 2018-10-26: Paolo Corti Manage worldmap client using client hooksets standard approach. Refs #4019 (#4023)
+ 2018-10-26: afabiani - fix test cases
+ 2018-10-26: afabiani - fix test cases
+ 2018-10-26: afabiani - general security and encoding updates
+ 2018-10-26: afabiani [Regression] View only perm does not works on GeoServer as espected because guardian always inserts a * * rule on GeoFence
+ 2018-10-26: afabiani [Regression] View only perm does not works on GeoServer as espected because guardian always inserts a * * rule on GeoFence
+ 2018-10-26: olivierdalang (readme) More details for migration steps
+ 2018-10-26: olivierdalang fix migrations instructions for older docker
+ 2018-10-24: olivierdalang [spcgeonode] several modifications
+ 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
+ 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
+ 2018-10-24: afabiani [Fixes #4016] New thumbnail is not well centered
+ 2018-10-23: afabiani [Fixes #4016] New thumbnail is not well centered
+ 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
+ 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
+ 2018-10-24: afabiani - regression: document upload fails since it is wrongly trying to purge GeoFence rules
+ 2018-10-24: afabiani - minor changes
+ 2018-10-24: afabiani [Fixes #4016] New thumbnail is not well centered
+ 2018-10-24: afabiani [Fixes #4017] Layer/Map Details Page does not parse the time dimension from the new 1.3.0 GetCapabilities
+ 2018-10-24: Alessio Fabiani Update helpers.py
+ 2018-10-24: kappu Thumb creation last fixes
+ 2018-10-23: afabiani [Fixes #4016] New thumbnail is not well centered
+ 2018-10-23: afabiani - Make sure mercanitle does not hit Y -90/90
+ 2018-10-23: Alessio Fabiani Update utils.py
+ 2018-10-23: olivierdalang [spcgeonode] improve ci
+ 2018-10-23: olivierdalang [spcgeonode] CI : use docker executor again and run tests
+ 2018-10-22: olivierdalang [spcgeonode] fix entrypoint perms for django (when mounted)
+ 2018-10-23: olivierdalang [spcgenonode] remove secrets
+ 2018-10-22: kappu Fixing thumb generation
+ 2018-10-19: olivierdalang [spcgeonode] integrate in main geonode repo
+ 2018-10-18: afabiani - Fix Upload Session encoding issues
+ 2018-10-18: afabiani - Fix Upload Session encoding issues
+ 2018-10-18: afabiani - Fix Upload Session encoding issues
+ 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
+ 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
+ 2018-10-18: afabiani [Contrib] [Minor] - Update monitoring contrib app / remove vulnerabilities
+ 2018-10-18: afabiani - thumb improvs
+ 2018-10-18: afabiani - thumb improvs
+ 2018-10-18: afabiani - thumb improvs
+ 2018-10-18: Alessio Fabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or "upload-sessions" (#4012)
+ 2018-10-18: Alessio Fabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or "upload-sessions" (#4012)
+ 2018-10-18: afabiani - fix travis
+ 2018-10-18: Paolo Corti PR with Worldmap changes running on master (2.10.x) (#3998)
+ 2018-10-17: afabiani [Fixes #4011] Encoding Issues with Resource having non-UTF8 characters on title and/or 'upload-sessions'
+ 2018-10-17: afabiani - fix travix build
+ 2018-10-17: Alessio Fabiani Revert wrong commit
+ 2018-10-17: afabiani - Mitigate encoding issues for non-UTF-8 Resource names
+ 2018-10-17: afabiani - fix travix build
+ 2018-10-17: afabiani [Backport to 2.8.x] Fix for #3872 - Theme refactoring
+ 2018-10-17: afabiani [Backport to 2.8.x][Closes #4004] Allow to send json body request to {layers,maps}//thumbnail to regenerate the thumbnail
+ 2018-10-17: olivierdalang Fix for #3872 - simplified implementation for homepage customization
+ 2018-10-17: Alessio Fabiani [Cumulative patch] - Backport minor fixes from master (#4006)
+ 2018-10-17: Francesco Bartoli Fix #3999 (#4000)
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani [Cumulative patch] - Backport minor fixes from master
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani - fix travis build
+ 2018-10-16: afabiani [Closes #4004] Allow to send json body request to {layers,maps}//thumbnail to regenerate the thumbnail
+ 2018-10-16: afabiani [Closes #4001] Typo on sync_with_guardian
+ 2018-10-16: afabiani [Closes #4001] Typo on sync_with_guardian
+ 2018-10-16: afabiani - fix thumbs and gf rules
+ 2018-10-16: Alessio Fabiani [Fixes #3993] Adding group access makes layer public (#3995)
+ 2018-10-16: Alessio Fabiani [Closes #3982] Improve Thumbnail method so that it can include a background also (#3997)
+ 2018-10-16: afabiani [Backport to 2.8.x][Fixes #3993] Adding group access makes layer public
+ 2018-10-16: afabiani - Themes customization
+ 2018-10-16: olivierdalang fix travis (to be squashed...)
+ 2018-10-16: olivierdalang fix... (to be squashed)
+ 2018-10-16: olivierdalang fix tests
+ 2018-10-16: olivierdalang tests
+ 2018-10-16: olivierdalang remove unused identifier
+ 2018-10-16: olivierdalang formatting
+ 2018-10-15: afabiani - Fix security test cases
+ 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
+ 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
+ 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
+ 2018-10-15: afabiani [Backport to 2.8.x][Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
+ 2018-10-15: afabiani [Fixes #3987] GeoFence Management commands are not resilient to duplicated rules
+ 2018-10-15: afabiani - Test cases
+ 2018-10-15: afabiani [Fixes #3984] Recurring User 'None' Error Geofence/Geoserver
+ 2018-10-15: afabiani [Fixes #3984] Recurring User 'None' Error Geofence/Geoserver
+ 2018-10-15: olivierdalang [themes] several improvements
+ 2018-08-31: olivierdalang simpler implementation for homepage customization
+ 2018-10-14: Alessio Fabiani Update signals.py
+ 2018-10-14: Alessio Fabiani Update signals.py
+ 2018-10-12: afabiani - fix naming
+ 2018-10-12: afabiani [Closes #3982] Improve Thumbnail method so that it can include a background also
+ 2018-10-12: afabiani - improve render thumbnail procedure
+ 2018-10-12: afabiani - fix testscases
+ 2018-10-11: afabiani - improve render thumbnail procedure
+ 2018-10-11: Ernest CHIARELLO Update setup_docker_compose.txt
+ 2018-10-10: Alessio Fabiani pep8 issues
+ 2018-10-09: mikefedak Deny access to layers/documents
+ 2018-09-25: Erwin Sterrenburg set to completed step to check if no _ALLOW_TIME_STEP is false
+ 2018-10-03: Alessio Fabiani [Fixes #3947] GeoWebCache Tiled Layer Cache explicit invalidation (#3949)
+ 2018-10-03: afabiani [Fixes #3953] "bbox_to_projection" flips coords and does not honor the geonode convention [x0, x1, y0, y1]
+ 2018-10-02: Alessio Fabiani Update requirements.txt
+ 2018-10-01: afabiani [Fixes #3951] [Cross-site scripting test - Security related - Issue] Improvements to Tastypie paginator
+ 2018-10-01: afabiani [Closes #3948] WPS Endpoint should be exposed to non-protected proxy
+ 2018-10-09: afabiani - geofence rest endpoint 2.13.x compliant
+ 2018-10-01: Alessio Fabiani [Fixes #3945] GeoFence rules priority computation should be more accurate (#3946)
+ 2018-09-27: afabiani [Closes #3942] Make 'Edit data' link aware of the storeType
+ 2018-09-25: afabiani [Fixes #3919] fix updatelayers command
+ 2018-09-25: afabiani [Closes #3934] Missing OWS endpoints on GeoNode proxy
+ 2018-10-08: afabiani - replace build server url
+ 2018-09-25: afabiani [Closes #3935] celery.log file based handler causing issues if not correctly configured
+ 2018-10-09: afabiani - Improve test coverage
+ 2018-10-08: Alessio Fabiani [Fixes #3962] Remove Django vulnerabilties (#3963)
+ 2018-10-08: afabiani - replace build server url
+ 2018-10-04: afabiani - disable codecov report on geosolutions branch
+ 2018-10-04: afabiani [Closes #3957] Increase "geoserver" tests coverage
+ 2018-10-04: afabiani [Closes #3957] Increase "geoserver" tests coverage
+ 2018-10-03: afabiani - codecov reports
+ 2018-10-03: afabiani - update .travis.yml
+ 2018-10-03: afabiani - update .travis.yml
+ 2018-10-03: afabiani - update .travis.yml
+ 2018-10-03: Alessio Fabiani Update README.rst
+ 2018-10-03: afabiani - codecov reports
+ 2018-10-03: afabiani - codecov reports
+ 2018-10-03: afabiani - fix test cases
+ 2018-10-03: Alessio Fabiani [Fixes #3947] GeoWebCache Tiled Layer Cache explicit invalidation (#3949)
+ 2018-10-03: Alessio Fabiani Fix pep8 issues
+ 2018-10-03: afabiani [Fixes #3954] [GeoServer 2.14.x Upgrade] Layer Capabilities 1.3.0
+ 2018-10-03: afabiani - Fix integration test_capabilities test cases
+ 2018-10-03: afabiani [Fixes #3953] "bbox_to_projection" flips coords and does not honor the geonode convention [x0, x1, y0, y1]
+ 2018-10-03: afabiani - Fix bbox projection
+ 2018-10-03: afabiani - Fix bbox projection
+ 2018-10-03: afabiani - Fix integration test_capabilities test cases
+ 2018-10-02: afabiani - Fix integration test_capabilities test cases
+ 2018-10-02: afabiani - Fix layer capabilities; WMS 1.3.0
+ 2018-10-02: afabiani - remove restricted dirty_state condition from catalog
+ 2018-10-02: afabiani - Delayed GeoFence Security Rules Invalidation
+ 2018-10-02: Alessio Fabiani Update requirements.txt
+ 2018-10-01: afabiani [Fixes #3951] [Cross-site scripting test - Security related - Issue] Improvements to Tastypie paginator
+ 2018-10-01: afabiani [Closes #3948] WPS Endpoint should be exposed to non-protected proxy
+ 2018-10-01: Alessio Fabiani [Fixes #3945] GeoFence rules priority computation should be more accurate (#3946)
+ 2018-09-27: afabiani [Closes #3942] Make 'Edit data' link aware of the storeType
+ 2018-09-26: afabiani - GeoFence rules checks on test cases
+ 2018-09-26: afabiani - GeoFence rules checks on test cases
+ 2018-09-25: Erwin Sterrenburg set to completed step to check if no _ALLOW_TIME_STEP is false
+ 2018-09-25: afabiani [Fixes #3919] fix updatelayers command
+ 2018-09-25: afabiani [Closes #3934] Missing OWS endpoints on GeoNode proxy
+ 2018-09-25: afabiani [Closes #3935] celery.log file based handler causing issues if not correctly configured
+ 2018-09-24: afabiani - Improving groups activity tests
+ 2018-09-24: afabiani - Fixing groups test cases
+ 2018-09-24: afabiani [Issue #3927] Switching to 2.8.x branch: enabling 2.8.x branch on .travis.yml
+ 2018-09-24: afabiani [Fixes #3929] Show documents also on Groups Activity Tabs
+ 2018-09-24: afabiani - Fix migrations
+ 2018-09-24: afabiani - Switching to 2.8.x branch
+ 2018-09-19: Hisham waleed karam return layer styles in map json as a list
+ 2018-09-19: Hisham waleed karam fix for AttributeError("'str' object has no attribute 'username'",))
+ 2018-09-19: gannebamm fix missing '-get' in apt-get (#3918)
+ 2018-09-13: afabiani [Fixes #3914] [Remote Services] Mitigate name clashes for service names
+ 2018-09-13: afabiani [Fixes #3914] [Remote Services] Mitigate name clashes for service names
+ 2018-09-07: afabiani - fix integration test cases - GeoFence apis cleanup
+ 2018-09-07: afabiani Fix test cases and geofence model
+ 2018-09-07: amefad Remove old configuration for GeoServer security from docs (#3908)
+ 2018-09-06: Francesco Bartoli Revert and then apply Fix #3893 again (#3903)
+ 2018-09-06: Francesco Bartoli Fix #3893
+ 2018-09-05: Alessio Fabiani [Closes #3896] Add a sample docker-compose.override.localhost.yml to run docker-compose on localhost (#3897)
+ 2018-09-05: Alessio Fabiani [Fixes #3890] wsgi.py should either check for local_settings (apt) and settings (docker)
+ 2018-09-07: amefad Remove old configuration for GeoServer security from docs (#3908)
+ 2018-09-06: Francesco Bartoli [2.7.x] Backport issue #3893 (#3904)
+ 2018-09-06: Francesco Bartoli Revert and then apply Fix #3893 again (#3903)
+ 2018-09-06: afabiani [Fixes #3900] [2.7.x] Fix py.test selenium tests
+ 2018-09-06: Francesco Bartoli Fix #3893
+ 2018-09-05: Alessio Fabiani [Closes #3896] Add a sample docker-compose.override.localhost.yml to run docker-compose on localhost (#3897)
+ 2018-09-05: afabiani [Fixes #3894] [2.7.x] docker-compose.yml should point to the 2.7.x image and not the latest one
+ 2018-09-05: Alessio Fabiani [Fixes #3890] wsgi.py should either check for local_settings (apt) and settings (docker)
+ 2018-09-05: afabiani - fix integration test cases
+ 2018-09-04: afabiani [Fixes #3886] Remove javascript packages vulnerabilities
+ 2018-09-04: afabiani [Fixes #3884] Update GeoServer to 2.14.x on master
+ 2018-08-31: Alessio Fabiani Removing "requirements.txt" known vulnerabilities
+ 2018-08-31: Alessio Fabiani Py.Test BDD - Fix splinter and selenium versions
+ 2018-08-28: Simone Dalmasso update geonode-updateip to use the new flags
+ 2018-08-28: Alessio Fabiani [Fixes #3874] sudo geonode-updateip fails on fresh Ubuntu 16.04 install
+ 2018-08-28: Simone Dalmasso add docs for the new flags of updateip
+ 2018-08-28: Alessio Fabiani 2.10rc4
+ 2018-08-28: Alessio Fabiani Updated changelog for version 2.10rc4
+ 2018-08-23: geo 2.10.0 rc3
+ 2018-08-23: geo Updated changelog for version 2.10rc2
+ 2018-08-23: geo 2.10.0 rc2
+ 2018-08-23: geo 2.10.0 rc2
+ 2018-08-23: geo Updated changelog for version 2.10rc1
+ 2018-08-23: geo 2.10.0 rc1
+ 2018-08-20: Alessio Fabiani 2.10.0 rc0
+ 2018-08-20: Alessio Fabiani 2.10.0 rc0
+ 2018-08-20: Alessio Fabiani 2.8.1 release
+ 2018-08-20: Alessio Fabiani Updated changelog for version 2.8.1rc0
+ 2018-08-20: Alessio Fabiani 2.8.1 release
+ 2018-08-03: Alessio Fabiani Backporting PRs fixes from master: https://github.com/GeoNode/geonode/pull/3866 https://github.com/GeoNode/geonode/pull/3865 https://github.com/GeoNode/geonode/pull/3864
+ 2018-07-31: capooti Now there is not a local_settings.py file, so we need to set DATABASES when using worldmap application
+ 2018-07-31: capooti Includes commands in Makefile to create and remove databases needed when using the worldmap contrib application
+ 2018-07-31: afabiani - #3180 restoring angular 1.4.0
+ 2018-07-31: Toni Schönbuchner allow long item titles to break without whitespace
+ 2018-07-31: afabiani - #3180 restoring angular 1.4.0
+ 2018-07-28: Toni Schönbuchner prevent change of height on cart item hover
+ 2018-07-27: capooti Ported the mobile client as per #404
+ 2018-07-27: afabiani - Fix typo
+ 2018-07-27: afabiani - Fix typo
+ 2018-07-27: afabiani Backport fixes from master
+ 2018-07-27: afabiani - Minor hardening on Map configuration stuff
+ 2018-07-27: afabiani - Fix integration tests
+ 2018-07-26: afabiani Backport fixes from master
+ 2018-07-26: afabiani - Fix QGis integration tests
+ 2018-07-26: afabiani - Fix QGis integration tests
+ 2018-07-25: afabiani - Fix QGis Server Integration Tests
+ 2018-07-25: Toni Schönbuchner changed readme to rst with extension and changed setup.py
+ 2018-07-25: Toni Schönbuchner submission of new readme for github
+ 2018-07-25: afabiani Backport fixes from master
+ 2018-07-25: afabiani - Fix QGis Server Integration Tests
+ 2018-07-25: afabiani - Updating ElasticSearch dependencies
+ 2018-07-25: Boney Bun fix a srid bug when uploading vector layers
+ 2018-07-24: afabiani - Updating ElasticSearch dependencies
+ 2018-07-24: afabiani - Remove circular local_settings import
+ 2018-07-24: Toni Schönbuchner centered magnifier in big search and aligned metadata checkbox
+ 2018-07-24: afabiani - Tentative fix geoserver docker compose
+ 2018-07-24: afabiani - Tentative fix geoserver docker compose
+ 2018-07-24: Alessio Fabiani - Backporting fixes from master
+ 2018-07-23: Hisham waleed karam Update settings.py
+ 2018-07-23: afabiani - Backporting master branch fixes
+ 2018-07-23: afabiani - Add storeType to Layers Capabilities response
+ 2018-07-22: Francesco Bartoli Backport #3856
+ 2018-07-22: Francesco Bartoli Add variable to set geoserver JAVA_OPTS
+ 2018-07-21: Francesco Bartoli Backport fix #3853
+ 2018-07-21: Francesco Bartoli Fix #3853
+ 2018-07-21: Alessio Fabiani - MapLoom GIS client hooksets (#3851)
+ 2018-07-21: Toni Schönbuchner Added explanations regarding pygdal install #3784
+ 2018-07-20: afabiani - MapLoom GIS client hooksets
+ 2018-07-20: afabiani - MapLoom GIS client hooksets
+ 2018-07-20: afabiani - Fix JS vulnerability
+ 2018-07-20: afabiani - MapLoom GIS client hooksets
+ 2018-07-20: afabiani Backport fixes from master branch
+ 2018-07-20: afabiani - Fixing test-cases
+ 2018-07-19: afabiani Update gsconfig and gsimporter versions
+ 2018-07-19: afabiani - Remove ws prefixed URL from links in order to publish a full DescribeLayer on Links
+ 2018-07-19: afabiani - Update gnimporter version
+ 2018-07-19: afabiani - Update gnimporter version
+ 2018-07-16: giohappy fix GeoNode DB name in docker env
+ 2018-07-15: giohappy note on docker-compose up for Windows users (see #3709)
+ 2018-07-14: Alessio Fabiani - Backport fixes and PRs from master (#3846)
+ 2018-07-13: afabiani - Minor Layout improvements
+ 2018-07-13: afabiani - Minor Layout improvements
+ 2018-07-13: afabiani - Minor Layout improvements
+ 2018-07-13: afabiani - Backport fixes and PRs from master
+ 2018-07-13: afabiani - Exclude public-invite groups from metadata choices
+ 2018-07-13: afabiani [Fixes #3834] STATIC_URL vs static template tag
+ 2018-07-12: afabiani - Fixes issue #3843 - Fix vulnerability with Pillow dependency
+ 2018-07-11: afabiani - backport from master
+ 2018-07-11: Toni Schönbuchner Restrict use of Edit Document Button
+ 2018-07-10: Toni Schönbuchner corrected Ubuntu 14.04 to 16.04 in documentation
+ 2018-07-10: Toni Schönbuchner added search input for styles to manage page
+ 2018-07-07: afabiani - Fix max zoom issue
+ 2018-07-07: afabiani - Fix max zoom issue
+ 2018-07-05: geo - Packagind scripts updates
+ 2018-07-05: geo - Packagin scripts updates
+ 2018-07-05: geo - Packagin scripts updates
+ 2018-07-05: afabiani [backport 2.7.x] Minor improvements: allow registered users to invite others / improve French translation
+ 2018-07-05: afabiani - Improving French translations
+ 2018-07-04: afabiani - Allow portal contributors to invite users
+ 2018-07-03: Glenn Vorhes add missing ast import
+ 2018-07-03: Glenn Vorhes add missing ast import
+ 2018-07-03: afabiani - Fixes layer replace
+ 2018-07-03: Alessio Fabiani Update helpers.py
+ 2018-07-03: Alessio Fabiani Update helpers.py
+ 2018-07-03: afabiani - Fixes layer replace
+ 2018-07-02: afabiani - Fixes layer replace
+ 2018-06-28: afabiani - Fix kombu/messaging initialization
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani pep8 fixes
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani pep8 fixes
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-28: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-27: afabiani pep8 issues
+ 2018-06-27: afabiani pep8 issues
+ 2018-06-27: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-27: afabiani - Fix celery initialization when using GeoNode ad a depenency
+ 2018-06-27: Alessio Fabiani Externalize OGC TIMEOUT setting as ENV var
+ 2018-06-27: Alessio Fabiani Externalize OGC TIMEOUT setting as ENV var
+ 2018-06-26: Alessio Fabiani Update Dockerfile
+ 2018-06-25: afabiani - Backporting Docker Improvs and Fixes from master branch
+ 2018-06-25: afabiani - Docker make use of GeoServer Importer Uploader
+ 2018-06-25: afabiani - minor improvements geoserver helper
+ 2018-06-25: afabiani - Fix localhost docker compose var
+ 2018-06-25: afabiani - Tentative fix doscker-compose vars
+ 2018-06-21: Alessio Fabiani - Backport stable fixes from master branch
+ 2018-06-21: afabiani - Improve Map Embed Template and allow it passing through client hooksets
+ 2018-06-21: Alessio Fabiani - Backport stable fixes from master branch
+ 2018-06-21: afabiani - Improve Map Embed Template and allow it passing through client hooksets
+ 2018-06-21: afabiani - Updating the oauth2 toolkit dep version
+ 2018-06-19: afabiani - GeoNode Client Hooksets: allow client configuration tweaking from pluggable client library
+ 2018-06-17: Francesco Bartoli Don't raise an exception if variable is missing
+ 2018-06-14: afabiani - Update requirements: adding openssl deps
+ 2018-06-14: afabiani - Update requirements: adding openssl deps
+ 2018-06-13: Alessio Fabiani [Fixes #3800] Uploading shapefiles without a datefield and time-enabled is False in importer settings fails in 2.7.x
+ 2018-06-13: afabiani [Fixes #3800] Uploading shapefiles without a datefield and time-enabled is False in importer settings fails in 2.7.x
+ 2018-06-12: Alessio Fabiani - SITEURL rstrip (/) consistently
+ 2018-06-12: afabiani - SITEURL rstrip (/) consistently
+ 2018-06-12: Alessio Fabiani Backport from master branch
+ 2018-06-11: afabiani Improvements to PyCSW Constraints and Local Mappings
+ 2018-06-11: afabiani Improvements to PyCSW Constraints and Local Mappings
+ 2018-06-07: giohappy Set default datastore from env for OGC server settings
+ 2018-06-07: afabiani Improvements to PyCSW Constraints and Local Mappings
+ 2018-06-07: giohappy value should be datastores key name, not value
+ 2018-06-07: giohappy Set default datastore from env for OGC server settings
+ 2018-06-06: afabiani [Backport fixes from master]
+ 2018-06-06: afabiani [Fw-port #3817] Implements GNIP #3718 (Worldmap contrib application)
+ 2018-06-06: afabiani [Backport fixes from master]
+ 2018-06-06: afabiani [Fixes #3824] Manage style page show style name instead of title
+ 2018-06-06: afabiani - OIDC 1.0 compliancy / notifications fixes
+ 2018-06-04: afabiani - OIDC 1.0 compliancy preparation: add api > UserInfo method
+ 2018-06-02: Tim Sutton Fix issues where docker client may be incompatible with docker server API by forcing to APV version 1.24
+ 2018-05-31: capooti By default we dont use hypermap
+ 2018-05-31: capooti Fixes a problem with createlayer app
+ 2018-05-30: capooti Fixes PEP8 violations and a syntax error
+ 2018-05-29: capooti By default USE_WORLDMAP is False
+ 2018-05-29: capooti Sync with latest GeoNode 2.7.x
+ 2018-05-29: capooti Fixes pep8 violations
+ 2018-05-29: afabiani - Add iso8961 time rules (yyyy/yyyy-mm/yyyy-mm-dd) on Templates also
+ 2018-05-28: Alessio Fabiani - Backport from master
+ 2018-05-28: Giovanni Allegri DATETIME_INPUT_FORMATS switched to list since Django 1.9
+ 2018-05-28: afabiani [geoext client] - Zoom To Data and not to nearest Scale
+ 2018-05-24: capooti Reset a couple of files
+ 2018-05-24: capooti Removed the worldmap.queue application for now
+ 2018-05-24: capooti Updated worldmap installation documentation
+ 2018-05-24: Alessio Fabiani - Backport fixes from master branch
+ 2018-05-24: afabiani - pep8 issues
+ 2018-05-24: afabiani - Update pip install instructions on docs and README
+ 2018-05-24: afabiani - Include django-celery-mon dep on requirements.txt
+ 2018-05-24: afabiani - ASYNC MODE uses ASYNC CELERY TASKS
+ 2018-05-23: afabiani - ImageMosaics refactoring: first step - support ZIP archives with granules and .properties files
+ 2018-05-23: afabiani - Update dependencies versions
+ 2018-05-23: afabiani - Fix Map Detail page structure issue and errors with GetCapabilities
+ 2018-05-22: capooti Now using django-geoexplorer-worldmap from the pypi package
+ 2018-05-21: capooti Including a local_settings sample file for worldmap
+ 2018-05-21: capooti Updated requirements for WorldMap
+ 2018-05-21: capooti Fixing a number of things before sending PR to GeoNode 2.7.x
+ 2018-05-21: capooti Removing all static files, which should be added by pip install worldmap-geoexplorer
+ 2018-05-21: capooti Removing worldmap account, which will be part of the cga geonode project
+ 2018-05-21: capooti Removing from git the compiled geoexplorer worldmap client
+ 2018-05-21: Alessio Fabiani - Backport commit 6c0e8ca from master
+ 2018-05-21: afabiani - Restored the possibility of sending multiple uploads
+ 2018-05-21: capooti A couple of fixes and removing geoexplorer source code
+ 2018-05-21: Alessio Fabiani - Backport commit 15123a5 from master
+ 2018-05-21: afabiani - Translations and minor refactoring of upload validator
+ 2018-05-18: capooti Removed stale worldmap documentation page
+ 2018-05-18: capooti Move the worldmap documentation to the correct place
+ 2018-05-18: capooti Synced with GeoNode 2.7.x
+ 2018-05-16: capooti Updating the client
+ 2018-05-16: Alessio Fabiani - Allow registered users to edit Remote Services
+ 2018-05-16: afabiani - Reduced size of layer-upload tooltips square
+ 2018-05-16: afabiani - Allow registered users to manage Remote Services
+ 2018-05-16: Alessio Fabiani - Backport fixes from master
+ 2018-05-16: afabiani - Correct management of SLDs / Add GWC filterParameter to SLDs
+ 2018-05-16: Alessio Fabiani - Backport fixes from master
+ 2018-05-15: afabiani - Correct management of SLDs / Add GWC filterParameter to SLDs
+ 2018-05-15: afabiani - Increase Test Coverage
+ 2018-05-15: afabiani - Allow unapproved layers to be published on maps
+ 2018-05-15: afabiani - Fix for issue Map Composer Menu not show complete #3804
+ 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
+ 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
+ 2018-05-14: afabiani - Fix ResponseNotReady issue
+ 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
+ 2018-05-14: afabiani - Test GeoServer Integration Tests running with Docker Compose
+ 2018-05-14: Alessio Fabiani Update integration.py
+ 2018-05-14: afabiani Backporting Master PRs
+ 2018-05-14: afabiani - Fix celery tasks hanging forever
+ 2018-05-14: afabiani - Minor improvements
+ 2018-05-11: capooti Fixes #375
+ 2018-05-11: capooti Fixes #3801
+ 2018-05-11: erwin Generalize logo-urls in profile-detail template.
+ 2018-05-11: afabiani Backporting Master PRs
+ 2018-05-11: afabiani - Docker Compose improvs
+ 2018-05-10: capooti Now thumbanils are not generate from layers which are created. Fixes #358
+ 2018-05-10: capooti Fixes part of #358 (the layer extent)
+ 2018-05-10: capooti Some improvement to the createlayer application
+ 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/37
+ 2018-05-10: camp-zju Modify the file for translation
+ 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/36
+ 2018-05-10: camp-zju Add Chinese translation file
+ 2018-05-10: camp-zju Add Chinese translation file
+ 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/34
+ 2018-05-10: afabiani - minor tweak on settings for Docker
+ 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/33
+ 2018-05-10: camp-zju Fixes https://github.com/camp-zju/geonode/issues/32
+ 2018-05-10: camp-zju Fixes 31
+ 2018-05-09: afabiani - Dockerfile: update pip install
+ 2018-05-09: capooti Fixes #367
+ 2018-05-09: afabiani - Travis pip cache
+ 2018-05-09: afabiani - Test coverage
+ 2018-05-09: afabiani - Integration test coverage
+ 2018-05-09: afabiani - Integration test coverage
+ 2018-05-09: afabiani - pep8 issues
+ 2018-05-09: afabiani - Minor fixes to backup & restore commands
+ 2018-05-08: Cezary Statkiewicz test-specific requirements: twisted
+ 2018-05-08: afabiani - Legend links for remote services
+ 2018-05-08: afabiani - Legend links for remote services
+ 2018-05-08: Cezary Statkiewicz monitoring: resolve 2-letter codes to 3-letter codes
+ 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
+ 2018-05-08: afabiani - Fix updatelayers mgmt command
+ 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
+ 2018-05-08: Cezary Statkiewicz monitoring support for geoip2
+ 2018-05-08: afabiani - Monitoring GeoIP error management
+ 2018-05-08: Cezary Statkiewicz Monitoring geoip2 (#286)
+ 2018-05-08: afabiani - Disabling synchronous remote services probe from model
+ 2018-05-08: Cezary Statkiewicz handle new geoip format properly
+ 2018-05-08: Cezary Statkiewicz use maxmind v2 db format if needed
+ 2018-05-07: afabiani - Improve Test Coverage
+ 2018-05-07: afabiani Backporting Master PRs
+ 2018-05-07: afabiani Backporting Master PRs
+ 2018-05-07: Alessio Fabiani Update helpers.py
+ 2018-05-07: afabiani - Test coverage improvements
+ 2018-05-07: Alessio Fabiani Update helpers.py
+ 2018-05-04: giohappy included default settings for social providers
+ 2018-05-03: capooti Fixes the updatelayers command
+ 2018-05-02: capooti renaming celery to celery_app
+ 2018-05-02: capooti Fixing a couple of things broke when merging
+ 2018-05-02: capooti Fixes a couple of things which were broken by merge with 2.8.0
+ 2018-05-01: capooti Synced with GeoNode 2.8.0
+ 2018-04-30: capooti Updating geoexplorer to last version
+ 2018-04-30: capooti Sync with GeoNode 2.8 part 1/2
+ 2018-04-28: Alessio Fabiani Update helpers.py
+ 2018-04-27: capooti Now categories order is respected for existing maps. Refs #341
+ 2018-04-26: capooti Re-enable thumbnails for layers. Fixes #351
+ 2018-04-26: capooti Fixes #350
+ 2018-04-26: Alessio Fabiani Backporting Master PRs
+ 2018-04-26: afabiani - fixes and improvements to Layer replase functionalities
+ 2018-04-26: Alessio Fabiani Forward port 2.8.0 changelogs
+ 2018-04-26: Ahmed Nour Eldeen check geometry type
+ 2018-04-26: afabiani - Fix layer replase
+ 2018-04-26: afabiani - Fix remote services layout
+ 2018-04-26: Cezary Statkiewicz catch geoserver error in messaging, to avoid looped delivery
+ 2018-04-26: Alessio Fabiani Update utils.py
+ 2018-04-26: afabiani - Fix test cases
+ 2018-04-26: afabiani - Fix test cases
+ 2018-04-26: Alessio Fabiani - Fix test cases
+ 2018-04-26: afabiani - Fix test cases
+ 2018-04-26: Alessio Fabiani Updated changelog for version 2.8
+ 2018-04-26: Alessio Fabiani Constrain pip to 9.0.3
+ 2018-04-26: Alessio Fabiani Updated changelog for version 2.8
+ 2018-04-26: Alessio Fabiani Constrain pip to 9.0.3
+ 2018-04-26: afabiani - Restore production/docker requirements
+ 2018-04-26: afabiani - DB consistency checks
+ 2018-04-26: Alessio Fabiani Update utils.py
+ 2018-04-26: Alessio Fabiani Update utils.py
+ 2018-04-26: afabiani - Update avatar version
+ 2018-04-26: Alessio Fabiani Update utils.py
+ 2018-04-26: Alessio Fabiani Update utils.py
+ 2018-04-25: capooti Use HTML widget in GXP for any field starting with "descriptio". Refs #348
+ 2018-04-24: afabiani Just fix requirements versions for GeoNode modules in order to avoid compatibility issues
+ 2018-04-24: afabiani Minor improvement to custom_theme_html template
+ 2018-04-24: afabiani [Closes #3662] GNIP: Improvements to GeoNode Layers download links
+ 2018-04-24: afabiani Just fix requirements versions for GeoNode modules in order to avoid compatibility issues
+ 2018-04-24: afabiani Minor improvement to custom_theme_html template
+ 2018-04-24: afabiani [Closes #3662] GNIP: Improvements to GeoNode Layers download links
+ 2018-04-24: Cezary Statkiewicz use geoip2 for monitoring
+ 2018-04-24: Cezary Statkiewicz use geoip2 for monitoring
+ 2018-04-24: olivierdalang fix slow login/logout on certain circumstances
+ 2018-04-24: olivierdalang fix slow login/logout on certain circumstances
+ 2018-04-23: afabiani - Fix reproj issue on bbox_to_projection
+ 2018-04-23: afabiani - Fix reproj issue on bbox_to_projection
+ 2018-04-20: Alessio Fabiani Backport master fixes
+ 2018-04-20: Alessio Fabiani - Fix issue with layer upload
+ 2018-04-20: Alessio Fabiani - Fix issue with layer upload
+ 2018-04-20: afabiani - Restoring Live Server port settings on integration tests in order to avoid address conflicts
+ 2018-04-19: afabiani - Split test cases on travis
+ 2018-04-19: afabiani - Split test cases on travis
+ 2018-04-19: afabiani - Align with master
+ 2018-04-18: afabiani - cleanup
+ 2018-04-18: afabiani - align with master branch
+ 2018-04-17: afabiani - Fix integration test cases
+ 2018-04-16: afabiani [Closes #3661] django 1.11 LTS support on master
+ 2018-04-17: Alessio Fabiani Better alignment of jumbotron image
+ 2018-04-16: afabiani [Closes #3661] django 1.11 LTS support on master
+ 2018-04-14: afabiani Prepare 2.8.1
+ 2018-04-13: capooti Fixes #343
+ 2018-04-13: Alessio Fabiani Align to master branch
+ 2018-04-12: capooti Fixes #340
+ 2018-04-12: capooti Fixes #336
+ 2018-04-12: capooti Fixes #327
+ 2018-04-11: capooti Fixes #330
+ 2018-04-11: Ahmed Nour Eldeen get geometry type for layers
+ 2018-04-09: capooti Fixes #300
+ 2018-04-09: capooti Fixes #334
+ 2018-04-05: capooti Adds a status message when updating the gazetteer fields for a layer
+ 2018-04-05: capooti Removed a stale file
+ 2018-04-04: capooti Make layer configuration in json map more robust
+ 2018-04-04: capooti Improve map thumbnails
+ 2018-04-03: capooti Forgot file in previous commit
+ 2018-04-03: capooti Add the feature search functionality
+ 2018-04-03: Alessio Fabiani Update README
+ 2018-04-03: Alessio Fabiani Update README
+ 2018-04-03: Alessio Fabiani Updated changelog for version 2.8
+ 2018-04-03: Alessio Fabiani Release 2.8.0
+ 2018-03-30: capooti Fixes #301
+ 2018-03-29: capooti Fixes the regression casuing all the extent issues
+ 2018-03-28: capooti Fixes zoom to extent tool
+ 2018-03-28: capooti Fixes #322
+ 2018-03-26: capooti Correctly identify a local layer when using GeoNodeQueryTool
+ 2018-03-26: capooti Point to the correct warper site in the rectify images dialog
+ 2018-03-26: capooti Fixes #298
+ 2018-03-26: capooti Fixes #314
+ 2018-03-21: capooti Fixes #283
+ 2018-03-20: capooti Fixes permission issues with editing and use the correct source for wm layers
+ 2018-03-16: capooti Fixes #272
+ 2018-03-16: capooti Forgot one requirement in previous commit
+ 2018-03-16: capooti Fixes #260 (gazetteer)
+ 2018-03-08: capooti Fixes #267
+ 2018-03-06: capooti Restore map print tool
+ 2018-03-06: capooti Migrate worldmap map revisions. Fixes #266
+ 2018-03-05: capooti Adding a missing import which caused an error when looking at map page as anonymous user
+ 2018-03-05: capooti Removed google earth tool. Fixed the gxp_mapshare tool. Fixing google map api key read
+ 2018-03-05: capooti Trying to increase accesstoken expiration to see if this affects #283
+ 2018-03-02: capooti Update instructions adding the configuration of update_last_wm_geonode_layers
+ 2018-03-02: capooti Fixes #280
+ 2018-03-02: capooti Fixes #264
+ 2018-02-27: capooti Updating instructions for geonode-worldmap
+ 2018-02-26: capooti Ported the action model and its api, which is needed by hypermap
+ 2018-02-26: capooti Fixed javascript build.xml and a few things in GeoExplorer.js
+ 2018-02-23: capooti Added worldmap geoexplorer client source code. Fixes #265
+ 2018-02-21: capooti WorldMap api version is now 2.8
+ 2018-02-21: capooti Add url dispatcher for worldmap api
+ 2018-02-21: capooti Fixes a problem with layers upload
+ 2018-02-16: capooti Synced with geonode master
+ 2018-02-15: capooti Fixes #273
+ 2018-02-15: capooti Fixes #263
+ 2018-02-15: capooti Now it is possible to edit and style also from local dev
+ 2018-02-14: capooti Enable DB_DATASTORE when using WorldMap
+ 2018-02-14: capooti Remove an ipdb line
+ 2018-02-12: capooti Align to GeoNode master. Fixes #276
+ 2018-02-08: capooti Updated installation instructions
+ 2018-02-06: Ben Lewis Update README.md
+ 2018-02-06: capooti Hard fixing wms endpoint for now for having style edit working
+ 2018-02-01: capooti Now layer is correctly displayed in map
+ 2018-01-31: capooti Added instructions for geonode/worldmap contrib app configuration
+ 2018-01-31: capooti Removing worldmap settings from general settings file
+ 2018-01-31: capooti Fixes a problem with upload - not sure if this is a general GeoNode problem, will need to look into it
+ 2018-01-31: capooti Redefining some urls when using worldmap contrib application
+ 2018-01-31: capooti Added context processors worldmap variables
+ 2018-01-30: capooti Fixes migration for apps still using South. Move the content_map field to the appropriate model
+ 2018-01-30: capooti Fixing a migration
+ 2018-01-30: capooti Fixes a syntax error
+ 2018-01-18: capooti Basic version of WorldMap GeoNode unforked version with working maps
+ 2018-01-11: capooti Start removing forked code in GeoNode/WorldMap
+ 2018-01-10: Ubuntu Fixed conflicts with master
+ 2017-11-21: capooti Sync with geonode master
+ 2017-11-21: Way Barrios Fixing settings import, we should use django.conf in stead of geonode
+ 2017-11-14: Way Barrios removing pdb
+ 2017-11-14: Way Barrios removing pdb dependency
+ 2017-11-07: Way Barrios Adding missing migrations
+ 2017-11-03: capooti Sync with GeoNode master
+ 2017-11-03: capooti Sync pavement with the one from master
+ 2017-10-31: capooti Added missing migration
+ 2017-10-27: capooti Updating config to download GeoServer 2.12
+ 2017-10-24: Way Barrios removing is_certifier from people profile
+ 2017-10-19: Way Barrios Dataverse and Datatables migration
+ 2017-10-17: capooti Enable layer-brose again
+ 2017-10-13: Lenninlasd Add missing translations
+ 2017-10-10: Lenninlasd Add extra metadata attributes
+ 2017-10-04: capooti erge branch 'master' into wm-develop
+ 2017-09-26: Way Barrios removing certification app from GeoNode
+ 2017-09-20: capooti Added a missing migrations for map
+ 2017-09-19: capooti Adding migrations for the gazetteer fields
+ 2017-09-19: capooti Fixes some migrations problem and make sure that "paver sync" correctly build worldmap database
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
index 1b408d1c6f0..65c1c9b2b36 100644
--- a/docker-compose-dev.yml
+++ b/docker-compose-dev.yml
@@ -52,7 +52,7 @@ services:
# Nginx is serving django static and media files and proxies to django and geonode
geonode:
- image: geonode/nginx:1.25.3-v1
+ image: geonode/nginx:1.25.3-latest
container_name: nginx4${COMPOSE_PROJECT_NAME}
env_file:
- .env
@@ -69,7 +69,7 @@ services:
# Gets and installs letsencrypt certificates
letsencrypt:
- image: geonode/letsencrypt:2.6.0-v1
+ image: geonode/letsencrypt:2.6.0-latest
container_name: letsencrypt4${COMPOSE_PROJECT_NAME}
env_file:
- .env
@@ -79,7 +79,7 @@ services:
# Geoserver backend
geoserver:
- image: geonode/geoserver:2.24.2-v1
+ image: geonode/geoserver:2.24.4-v1
container_name: geoserver4${COMPOSE_PROJECT_NAME}
healthcheck:
test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://geoserver:8080/geoserver/ows"
@@ -105,7 +105,7 @@ services:
condition: service_healthy
data-dir-conf:
- image: geonode/geoserver_data:2.24.2-v1
+ image: geonode/geoserver_data:2.24.4-v1
container_name: gsconf4${COMPOSE_PROJECT_NAME}
entrypoint: sleep infinity
volumes:
@@ -117,7 +117,7 @@ services:
# PostGIS database.
db:
# use geonode official postgis 15 image
- image: geonode/postgis:15.3-v1
+ image: geonode/postgis:15.3-latest
command: postgres -c "max_connections=${POSTGRESQL_MAX_CONNECTIONS}"
container_name: db4${COMPOSE_PROJECT_NAME}
env_file:
diff --git a/docker-compose-test.yml b/docker-compose-test.yml
index 28738fa28b6..0194bcb559a 100644
--- a/docker-compose-test.yml
+++ b/docker-compose-test.yml
@@ -3,7 +3,7 @@ version: '3.9'
# Common Django template for GeoNode and Celery services below
x-common-django:
&default-common-django
- image: geonode/geonode:4.2.5
+ image: geonode/geonode:4.3.0
build:
context: ./
dockerfile: Dockerfile
@@ -32,7 +32,7 @@ services:
start_period: 60s
interval: 60s
timeout: 10s
- retries: 2
+ retries: 5
environment:
- IS_CELERY=False
entrypoint: ["/usr/src/geonode/entrypoint.sh"]
@@ -52,7 +52,7 @@ services:
# Nginx is serving django static and media files and proxies to django and geonode
geonode:
- image: geonode/nginx:1.25.3-v1
+ image: geonode/nginx:1.25.3-latest
container_name: nginx4${COMPOSE_PROJECT_NAME}
env_file:
- .env_test
@@ -82,7 +82,7 @@ services:
# Gets and installs letsencrypt certificates
letsencrypt:
- image: geonode/letsencrypt:2.6.0-v1
+ image: geonode/letsencrypt:2.6.0-latest
container_name: letsencrypt4${COMPOSE_PROJECT_NAME}
env_file:
- .env_test
@@ -92,7 +92,7 @@ services:
# Geoserver backend
geoserver:
- image: geonode/geoserver:2.24.2-v1
+ image: geonode/geoserver:2.24.4-v1
container_name: geoserver4${COMPOSE_PROJECT_NAME}
healthcheck:
test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://geoserver:8080/geoserver/ows"
@@ -118,7 +118,7 @@ services:
condition: service_healthy
data-dir-conf:
- image: geonode/geoserver_data:2.24.2-v1
+ image: geonode/geoserver_data:2.24.4-v1
container_name: gsconf4${COMPOSE_PROJECT_NAME}
entrypoint: sleep infinity
volumes:
@@ -130,7 +130,7 @@ services:
# PostGIS database.
db:
# use geonode official postgis 15 image
- image: geonode/postgis:15.3-v1
+ image: geonode/postgis:15.3-latest
command: postgres -c "max_connections=${POSTGRESQL_MAX_CONNECTIONS}"
container_name: db4${COMPOSE_PROJECT_NAME}
env_file:
diff --git a/docker-compose.yml b/docker-compose.yml
index 65386e71e96..354010dcff6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,7 +3,7 @@ version: '3.9'
# Common Django template for GeoNode and Celery services below
x-common-django:
&default-common-django
- image: geonode/geonode:4.2.5
+ image: geonode/geonode:4.3.1
#build:
# context: ./
# dockerfile: Dockerfile
@@ -64,7 +64,7 @@ services:
# Nginx is serving django static and media files and proxies to django and geonode
geonode:
- image: geonode/nginx:1.25.3-v1
+ image: geonode/nginx:1.25.3-latest
container_name: nginx4${COMPOSE_PROJECT_NAME}
env_file:
- .env
@@ -81,7 +81,7 @@ services:
# Gets and installs letsencrypt certificates
letsencrypt:
- image: geonode/letsencrypt:2.6.0-v1
+ image: geonode/letsencrypt:2.6.0-latest
container_name: letsencrypt4${COMPOSE_PROJECT_NAME}
env_file:
- .env
@@ -91,7 +91,7 @@ services:
# Geoserver backend
geoserver:
- image: geonode/geoserver:2.24.2-v1
+ image: geonode/geoserver:2.24.4-v1
container_name: geoserver4${COMPOSE_PROJECT_NAME}
healthcheck:
test: "curl -m 10 --fail --silent --write-out 'HTTP CODE : %{http_code}\n' --output /dev/null http://geoserver:8080/geoserver/ows"
@@ -117,7 +117,7 @@ services:
condition: service_healthy
data-dir-conf:
- image: geonode/geoserver_data:2.24.2-v2
+ image: geonode/geoserver_data:2.24.4-v1
container_name: gsconf4${COMPOSE_PROJECT_NAME}
entrypoint: sleep infinity
volumes:
@@ -129,7 +129,7 @@ services:
# PostGIS database.
db:
# use geonode official postgis 15 image
- image: geonode/postgis:15.3-v1
+ image: geonode/postgis:15.3-latest
command: postgres -c "max_connections=${POSTGRESQL_MAX_CONNECTIONS}"
container_name: db4${COMPOSE_PROJECT_NAME}
env_file:
diff --git a/geonode/__init__.py b/geonode/__init__.py
index 13a7dc840e7..f05d886880f 100644
--- a/geonode/__init__.py
+++ b/geonode/__init__.py
@@ -19,10 +19,7 @@
import os
-__version__ = (4, 2, 5, "final", 0)
-
-
-default_app_config = "geonode.apps.AppConfig"
+__version__ = (4, 3, 1, "final", 0)
def get_version():
diff --git a/geonode/api/__init__.py b/geonode/api/__init__.py
index 55b0b0cb9fd..79177e00bdd 100644
--- a/geonode/api/__init__.py
+++ b/geonode/api/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.api.apps.GeoNodeApiAppConfig"
diff --git a/geonode/api/api.py b/geonode/api/api.py
index c80c699ed48..316510bb5f0 100644
--- a/geonode/api/api.py
+++ b/geonode/api/api.py
@@ -22,7 +22,6 @@
from django.apps import apps
from django.db.models import Q
-from django.conf.urls import url
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.urls import reverse
@@ -61,7 +60,6 @@
from tastypie import fields
from tastypie.resources import ModelResource
from tastypie.constants import ALL, ALL_WITH_RELATIONS
-from tastypie.utils import trailing_slash
from geonode.utils import check_ogc_backend
from geonode.security.utils import get_visible_resources
@@ -548,16 +546,7 @@ def dehydrate(self, bundle):
return bundle
def prepend_urls(self):
- if settings.HAYSTACK_SEARCH:
- return [
- url(
- r"^(?P{})/search{}$".format(self._meta.resource_name, trailing_slash()),
- self.wrap_view("get_search"),
- name="api_get_search",
- ),
- ]
- else:
- return []
+ return []
def serialize(self, request, data, format, options=None):
if options is None:
diff --git a/geonode/api/resourcebase_api.py b/geonode/api/resourcebase_api.py
index 794a29c884e..cf98190ac8e 100644
--- a/geonode/api/resourcebase_api.py
+++ b/geonode/api/resourcebase_api.py
@@ -17,7 +17,6 @@
#
#########################################################################
from geonode.base.enumerations import LAYER_TYPES
-import re
import logging
from django.db.models import Q
@@ -29,13 +28,7 @@
from tastypie.constants import ALL, ALL_WITH_RELATIONS
from tastypie.resources import ModelResource
from tastypie import fields
-from tastypie.utils import trailing_slash
-from guardian.shortcuts import get_objects_for_user
-
-from django.conf.urls import url
-from django.core.paginator import Paginator, InvalidPage
-from django.http import Http404
from django.core.exceptions import ObjectDoesNotExist
from django.forms.models import model_to_dict
@@ -67,9 +60,6 @@
from .paginator import CrossSiteXHRPaginator
from django.utils.translation import gettext as _
-if settings.HAYSTACK_SEARCH:
- from haystack.query import SearchQuerySet # noqa
-
logger = logging.getLogger(__name__)
@@ -246,242 +236,6 @@ def filter_h_keywords(self, queryset, keywords):
filtered = queryset
return filtered
- def build_haystack_filters(self, parameters):
- from haystack.inputs import Raw
- from haystack.query import SearchQuerySet, SQ # noqa
-
- sqs = None
-
- # Retrieve Query Params
-
- # Text search
- query = parameters.get("q", None)
-
- # Types and subtypes to filter (map, layer, vector, etc)
- type_facets = parameters.getlist("type__in", [])
-
- # If coming from explore page, add type filter from resource_name
- resource_filter = self._meta.resource_name.rstrip("s")
- if resource_filter != "base" and resource_filter not in type_facets:
- type_facets.append(resource_filter)
-
- # Publication date range (start,end)
- date_end = parameters.get("date__lte", None)
- date_start = parameters.get("date__gte", None)
-
- # Topic category filter
- category = parameters.getlist("category__identifier__in")
-
- # Keyword filter
- keywords = parameters.getlist("keywords__slug__in")
-
- # Region filter
- regions = parameters.getlist("regions__name__in")
-
- # Owner filters
- owner = parameters.getlist("owner__username__in")
-
- # Sort order
- sort = parameters.get("order_by", "relevance")
-
- # Geospatial Elements
- bbox = parameters.get("extent", None)
-
- # Filter by Type and subtype
- if type_facets is not None:
- types = []
- subtypes = []
-
- for type in type_facets:
- if type in {"map", "layer", "document", "user"}:
- # Type is one of our Major Types (not a sub type)
- types.append(type)
- elif type in LAYER_TYPES:
- subtypes.append(type)
-
- if "vector" in subtypes and "vector_time" not in subtypes:
- subtypes.append("vector_time")
-
- if len(subtypes) > 0:
- types.append("layer")
- sqs = SearchQuerySet().narrow(f"subtype:{','.join(map(str, subtypes))}")
-
- if len(types) > 0:
- sqs = (SearchQuerySet() if sqs is None else sqs).narrow(f"type:{','.join(map(str, types))}")
-
- # Filter by Query Params
- # haystack bug? if boosted fields aren't included in the
- # query, then the score won't be affected by the boost
- if query:
- if query.startswith('"') or query.startswith("'"):
- # Match exact phrase
- phrase = query.replace('"', "")
- sqs = (SearchQuerySet() if sqs is None else sqs).filter(
- SQ(title__exact=phrase) | SQ(description__exact=phrase) | SQ(content__exact=phrase)
- )
- else:
- words = [w for w in re.split(r"\W", query, flags=re.UNICODE) if w]
- for i, search_word in enumerate(words):
- if i == 0:
- sqs = (SearchQuerySet() if sqs is None else sqs).filter(
- SQ(title=Raw(search_word)) | SQ(description=Raw(search_word)) | SQ(content=Raw(search_word))
- )
- elif search_word in {"AND", "OR"}:
- pass
- elif words[i - 1] == "OR": # previous word OR this word
- sqs = sqs.filter_or(
- SQ(title=Raw(search_word)) | SQ(description=Raw(search_word)) | SQ(content=Raw(search_word))
- )
- else: # previous word AND this word
- sqs = sqs.filter(
- SQ(title=Raw(search_word)) | SQ(description=Raw(search_word)) | SQ(content=Raw(search_word))
- )
-
- # filter by category
- if category:
- sqs = (SearchQuerySet() if sqs is None else sqs).narrow(f"category:{','.join(map(str, category))}")
-
- # filter by keyword: use filter_or with keywords_exact
- # not using exact leads to fuzzy matching and too many results
- # using narrow with exact leads to zero results if multiple keywords
- # selected
- if keywords:
- for keyword in keywords:
- sqs = (SearchQuerySet() if sqs is None else sqs).filter_or(keywords_exact=keyword)
-
- # filter by regions: use filter_or with regions_exact
- # not using exact leads to fuzzy matching and too many results
- # using narrow with exact leads to zero results if multiple keywords
- # selected
- if regions:
- for region in regions:
- sqs = (SearchQuerySet() if sqs is None else sqs).filter_or(regions_exact__exact=region)
-
- # filter by owner
- if owner:
- sqs = (SearchQuerySet() if sqs is None else sqs).narrow(f"owner__username:{','.join(map(str, owner))}")
-
- # filter by date
- if date_start:
- sqs = (SearchQuerySet() if sqs is None else sqs).filter(SQ(date__gte=date_start))
-
- if date_end:
- sqs = (SearchQuerySet() if sqs is None else sqs).filter(SQ(date__lte=date_end))
-
- # Filter by geographic bounding box
- if bbox:
- left, bottom, right, top = bbox.split(",")
- sqs = (SearchQuerySet() if sqs is None else sqs).exclude(
- SQ(bbox_top__lte=bottom)
- | SQ(bbox_bottom__gte=top)
- | SQ(bbox_left__gte=right)
- | SQ(bbox_right__lte=left)
- )
-
- # Apply sort
- if sort.lower() == "-date":
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("-date")
- elif sort.lower() == "date":
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("date")
- elif sort.lower() == "title":
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("title_sortable")
- elif sort.lower() == "-title":
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("-title_sortable")
- elif sort.lower() == "-popular_count":
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("-popular_count")
- else:
- sqs = (SearchQuerySet() if sqs is None else sqs).order_by("-date")
-
- return sqs
-
- def get_search(self, request, **kwargs):
- self.method_check(request, allowed=["get"])
- self.is_authenticated(request)
- self.throttle_check(request)
-
- # Get the list of objects that matches the filter
- sqs = self.build_haystack_filters(request.GET)
-
- if not settings.SKIP_PERMS_FILTER:
- filter_set = get_objects_for_user(request.user, "base.view_resourcebase")
-
- filter_set = get_visible_resources(
- filter_set,
- request.user if request else None,
- admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
- unpublished_not_visible=settings.RESOURCE_PUBLISHING,
- private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
- )
-
- filter_set_ids = filter_set.values_list("id")
- # Do the query using the filterset and the query term. Facet the
- # results
- if len(filter_set) > 0:
- sqs = (
- sqs.filter(id__in=filter_set_ids)
- .facet("type")
- .facet("subtype")
- .facet("owner")
- .facet("keywords")
- .facet("regions")
- .facet("category")
- )
- else:
- sqs = None
- else:
- sqs = sqs.facet("type").facet("subtype").facet("owner").facet("keywords").facet("regions").facet("category")
-
- if sqs:
- # Build the Facet dict
- facets = {}
- for facet in sqs.facet_counts()["fields"]:
- facets[facet] = {}
- for item in sqs.facet_counts()["fields"][facet]:
- facets[facet][item[0]] = item[1]
-
- # Paginate the results
- paginator = Paginator(sqs, request.GET.get("limit"))
-
- try:
- page = paginator.page(int(request.GET.get("offset") or 0) / int(request.GET.get("limit") or 0 + 1))
- except InvalidPage:
- raise Http404("Sorry, no results on that page.")
-
- if page.has_previous():
- previous_page = page.previous_page_number()
- else:
- previous_page = 1
- if page.has_next():
- next_page = page.next_page_number()
- else:
- next_page = 1
- total_count = sqs.count()
- objects = page.object_list
- else:
- next_page = 0
- previous_page = 0
- total_count = 0
- facets = {}
- objects = []
-
- object_list = {
- "meta": {
- "limit": settings.CLIENT_RESULTS_LIMIT,
- "next": next_page,
- "offset": int(getattr(request.GET, "offset", 0)),
- "previous": previous_page,
- "total_count": total_count,
- "facets": facets,
- },
- "objects": [self.get_haystack_api_fields(x) for x in objects],
- }
-
- self.log_throttled_access(request)
- return self.create_response(request, object_list)
-
- def get_haystack_api_fields(self, haystack_object):
- return {k: v for k, v in haystack_object.get_stored_fields().items() if not re.search("_exact$|_sortable$", k)}
-
def get_list(self, request, **kwargs):
"""
Returns a serialized list of resources.
@@ -533,6 +287,8 @@ def format_objects(self, objects):
if formatted_obj.get("metadata", None):
formatted_obj["metadata"] = [model_to_dict(_m) for _m in formatted_obj["metadata"]]
+ formatted_obj["detail_url"] = obj.detail_url
+
formatted_objects.append(formatted_obj)
return formatted_objects
@@ -574,16 +330,7 @@ def create_response(self, request, data, response_class=HttpResponse, response_o
return response_class(content=serialized, content_type=build_content_type(desired_format), **response_kwargs)
def prepend_urls(self):
- if settings.HAYSTACK_SEARCH:
- return [
- url(
- r"^(?P{})/search{}$".format(self._meta.resource_name, trailing_slash()),
- self.wrap_view("get_search"),
- name="api_get_search",
- ),
- ]
- else:
- return []
+ return []
def hydrate_title(self, bundle):
title = bundle.data.get("title", None)
diff --git a/geonode/api/tests.py b/geonode/api/tests.py
index 61c9214a2c5..95692f2056f 100644
--- a/geonode/api/tests.py
+++ b/geonode/api/tests.py
@@ -55,6 +55,40 @@
logger = logging.getLogger(__name__)
+class UserAndTokenInfoApiTests(GeoNodeBaseTestSupport):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ create_models(type=cls.get_type, integration=cls.get_integration)
+ all_public()
+
+ @classmethod
+ def tearDownClass(cls):
+ super().tearDownClass()
+ remove_models(cls.get_obj_ids, type=cls.get_type, integration=cls.get_integration)
+
+ def test_userinfo_response(self):
+ userinfo_url = reverse("userinfo")
+ _user = get_user_model().objects.get(username="bobby")
+ self.client.login(username="bobby", password="bob")
+ response = self.client.get(userinfo_url)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.json()["sub"], str(_user.pk))
+ self.client.logout()
+ response = self.client.get(userinfo_url)
+ self.assertEqual(response.status_code, 401)
+
+ def test_tokeninfo_response(self):
+ tokeninfo_url = reverse("tokeninfo")
+ _user = get_user_model().objects.get(username="bobby")
+ token = get_or_create_token(_user)
+ response = self.client.post(tokeninfo_url, data={"token": token})
+ self.assertEqual(response.status_code, 200)
+ response_json = response.json()
+ self.assertEqual(response_json["access_token"], token.token)
+ self.assertEqual(response_json["user_id"], _user.pk)
+
+
class PermissionsApiTests(ResourceTestCaseMixin, GeoNodeBaseTestSupport):
@classmethod
def setUpClass(cls):
diff --git a/geonode/api/views.py b/geonode/api/views.py
index da79e1a17d6..b43eb9c13e3 100644
--- a/geonode/api/views.py
+++ b/geonode/api/views.py
@@ -32,7 +32,7 @@
from ..utils import json_response
from ..decorators import superuser_or_apiauth
-from ..base.auth import get_token_object_from_session, extract_headers, get_auth_token
+from ..base.auth import get_token_object_from_session, get_auth_token
def verify_access_token(request, key):
@@ -55,21 +55,13 @@ def verify_access_token(request, key):
@csrf_exempt
def user_info(request):
- headers = extract_headers(request)
user = request.user
- if not user:
+ if not user or user.is_anonymous:
out = {"success": False, "status": "error", "errors": {"user": ["User is not authenticated"]}}
return json_response(out, status=401)
- access_token = None
- if "Authorization" not in headers or "Bearer" not in headers["Authorization"]:
- access_token = get_auth_token(user)
- if not access_token:
- out = {"success": False, "status": "error", "errors": {"auth": ["No token provided."]}}
- return json_response(out, status=403)
- else:
- access_token = headers["Authorization"].replace("Bearer ", "")
+ access_token = get_auth_token(user)
groups = [group.name for group in user.groups.all()]
if user.is_superuser:
diff --git a/geonode/base/__init__.py b/geonode/base/__init__.py
index 48ab2e6d723..dba87a5d75e 100644
--- a/geonode/base/__init__.py
+++ b/geonode/base/__init__.py
@@ -19,25 +19,6 @@
from functools import wraps
from django.conf import settings
-from django.utils.translation import ugettext_noop as _
-
-from geonode.notifications_helper import NotificationsAppConfigBase
-
-
-class BaseAppConfig(NotificationsAppConfigBase):
- name = "geonode.base"
- NOTIFICATIONS = (
- (
- "request_download_resourcebase",
- _("Request to download a resource"),
- _("A request for downloading a resource was sent"),
- ),
- (
- "request_resource_edit",
- _("Request resource change"),
- _("Owner has requested permissions to modify a resource"),
- ),
- )
def register_url_event(event_type=None):
@@ -97,6 +78,3 @@ def register_proxy_event(request):
"""
Process request to geoserver proxy. Extract layer and ows type
"""
-
-
-default_app_config = "geonode.base.BaseAppConfig"
diff --git a/geonode/base/api/mixins.py b/geonode/base/api/mixins.py
new file mode 100644
index 00000000000..9a641c4f8f7
--- /dev/null
+++ b/geonode/base/api/mixins.py
@@ -0,0 +1,55 @@
+#########################################################################
+#
+# Copyright (C) 2024 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+from distutils.util import strtobool
+from rest_framework.mixins import ListModelMixin
+from rest_framework.response import Response
+
+
+class AdvertisedListMixin(ListModelMixin):
+ def list(self, request, *args, **kwargs):
+ queryset = self.filter_queryset(self.get_queryset())
+
+ # advertised
+ # if superuser, all resources will be visible, otherwise only the advertised one and
+ # the resource which the user is owner will be returned
+ user = request.user
+ try:
+ _filter = request.query_params.get("advertised", "None")
+ advertised = strtobool(_filter) if _filter.lower() != "all" else "all"
+ except Exception:
+ advertised = None
+
+ if advertised == "all":
+ pass
+ elif advertised is not None:
+ queryset = queryset.filter(advertised=advertised)
+ else:
+ is_admin = user.is_superuser if user and user.is_authenticated else False
+ if not is_admin and user and not user.is_anonymous:
+ queryset = (queryset.filter(advertised=True) | queryset.filter(owner=user)).distinct()
+ elif not user or user.is_anonymous:
+ queryset = queryset.filter(advertised=True)
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.get_serializer(queryset, many=True)
+ return Response(serializer.data)
diff --git a/geonode/base/api/permissions.py b/geonode/base/api/permissions.py
index 75d19ea36d3..8dc3cffdcfa 100644
--- a/geonode/base/api/permissions.py
+++ b/geonode/base/api/permissions.py
@@ -23,6 +23,7 @@
from rest_framework import permissions
from rest_framework.filters import BaseFilterBackend
+from geonode.people.utils import get_available_users
from geonode.security.permissions import (
BASIC_MANAGE_PERMISSIONS,
DOWNLOAD_PERMISSIONS,
@@ -139,6 +140,11 @@ def has_object_permission(self, request, view, obj):
elif hasattr(obj, "user"):
_request_matches = obj.user == request.user
+ if isinstance(obj, get_user_model()) and not request.user.is_anonymous:
+ if request.method in permissions.SAFE_METHODS and obj in get_available_users(request.user):
+ return True
+ return _request_matches
+
if not _request_matches:
_request_matches = request.user in get_users_with_perms(obj)
return _request_matches
diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py
index eba681a69a1..b91a18c5318 100644
--- a/geonode/base/api/serializers.py
+++ b/geonode/base/api/serializers.py
@@ -17,10 +17,10 @@
#
#########################################################################
import logging
+
from slugify import slugify
from urllib.parse import urljoin
import json
-import warnings
from django.db.models import Q
from django.conf import settings
@@ -30,7 +30,6 @@
from django.db.models.query import QuerySet
from geonode.people import Roles
from django.http import QueryDict
-
from deprecated import deprecated
from rest_framework import serializers
from rest_framework_gis import fields
@@ -72,6 +71,12 @@
logger = logging.getLogger(__name__)
+def user_serializer():
+ import geonode.people.api.serializers as ser
+
+ return ser.UserSerializer
+
+
class BaseDynamicModelSerializer(DynamicModelSerializer):
def to_representation(self, instance):
data = super().to_representation(instance)
@@ -82,38 +87,19 @@ def to_representation(self, instance):
path = f"{path}/"
url = urljoin(path, str(instance.pk))
data["link"] = build_absolute_uri(url)
- except (TypeError, NoReverseMatch) as e:
- logger.exception(e)
- return data
+ parents = []
+ parent = self.parent
+ while parent:
+ parents.append(type(parent).__name__)
+ parent = parent.parent
-class ResourceBaseToRepresentationSerializerMixin(DynamicModelSerializer):
- def to_representation(self, instance):
- request = self.context.get("request")
- data = super(ResourceBaseToRepresentationSerializerMixin, self).to_representation(instance)
- if request:
- data["perms"] = (
- instance.get_user_perms(request.user)
- .union(instance.get_self_resource().get_user_perms(request.user))
- .union(instance.get_real_instance().get_user_perms(request.user))
- )
- if not request.user.is_anonymous and getattr(settings, "FAVORITE_ENABLED", False):
- favorite = Favorite.objects.filter(user=request.user, object_id=instance.pk).count()
- data["favorite"] = favorite > 0
- # Adding links to resource_base api
- obj_id = data.get("pk", None)
- if obj_id:
- dehydrated = []
- link_fields = ["extension", "link_type", "name", "mime", "url"]
-
- links = Link.objects.filter(
- resource_id=int(obj_id), link_type__in=["OGC:WMS", "OGC:WFS", "OGC:WCS", "image", "metadata"]
- )
- for lnk in links:
- formatted_link = model_to_dict(lnk, fields=link_fields)
- dehydrated.append(formatted_link)
- if len(dehydrated) > 0:
- data["links"] = dehydrated
+ logger.warning(
+ f"Deprecated: BaseDynamicModelSerializer should be replaced with proper Field"
+ f" - Parents: {parents} Root: {type(self).__name__}"
+ )
+ except (TypeError, NoReverseMatch) as e:
+ logger.exception(e)
return data
@@ -293,7 +279,7 @@ def __init__(self, **kwargs):
def get_attribute(self, instance):
try:
logger.info(
- "This field is deprecated, and will be removed in the future GeoNode version. Please refer to download_urls"
+ f"Field {self.field_name} is deprecated and will be removed in the future GeoNode version. Please refer to download_urls"
)
_instance = instance.get_real_instance()
return _instance.download_url if hasattr(_instance, "download_url") else None
@@ -350,34 +336,32 @@ def get_attribute(self, instance):
return False
-class UserSerializer(BaseDynamicModelSerializer):
- class Meta:
- ref_name = "UserProfile"
- model = get_user_model()
- name = "user"
- view_name = "users-list"
- fields = ("pk", "username", "first_name", "last_name", "avatar", "perms", "is_superuser", "is_staff", "email")
-
- @classmethod
- def setup_eager_loading(cls, queryset):
- """Perform necessary eager loading of data."""
- queryset = queryset.prefetch_related()
- return queryset
+class AutoLinkField(DynamicComputedField):
+ def get_attribute(self, instance):
+ parents = []
+ parent = self.parent
+ while parent:
+ parents.append(type(parent).__name__)
+ parent = parent.parent
+
+ logger.debug(
+ f"AutoLinkField reading Meta from first parent - Parents: {parents} root: {type(self.root).__name__}"
+ )
- def to_representation(self, instance):
- # Dehydrate users private fields
- request = self.context.get("request")
- data = super().to_representation(instance)
- if not request or not request.user or not request.user.is_authenticated:
- if "perms" in data:
- del data["perms"]
- elif not request.user.is_superuser and not request.user.is_staff:
- if data["username"] != request.user.username:
- if "perms" in data:
- del data["perms"]
- return data
+ try:
+ path = reverse(self.parent.Meta.view_name)
+ if not path.endswith("/"):
+ path = f"{path}/"
+ url = urljoin(path, str(instance.pk))
+ return build_absolute_uri(url)
+
+ except AttributeError as e:
+ logger.exception(f"Parents: {parents} root: {type(self.root).__name__}", exc_info=e)
+ return None
- avatar = AvatarUrlField(240, read_only=True)
+ except Exception as e:
+ logger.exception(e)
+ return None
class ContactRoleField(DynamicComputedField):
@@ -393,7 +377,7 @@ def get_attribute(self, instance):
return getattr(instance, self.contact_type)
def to_representation(self, value):
- return [UserSerializer(embed=True, many=False).to_representation(v) for v in value]
+ return [user_serializer()(embed=True, many=False).to_representation(v) for v in value]
def get_pks_of_users_to_set(self, value):
pks_of_users_to_set = []
@@ -495,93 +479,141 @@ def to_representation(self, instance):
return data
+class LinkedResourceEmbeddedSerializer(DynamicModelSerializer):
+ class Meta:
+ model = ResourceBase
+ fields = ("pk",)
+
+ def to_representation(self, instance):
+ from geonode.base.api.views import base_linked_resources_payload
+
+ request = self.context.get("request", None)
+ _resource = ResourceBase.objects.get(pk=instance)
+
+ return base_linked_resources_payload(_resource, request.user) if request and request.user and _resource else {}
+
+
api_bbox_settable_resource_models = [Document, GeoApp]
-class ResourceBaseSerializer(
- ResourceBaseToRepresentationSerializerMixin,
- BaseDynamicModelSerializer,
-):
- def __init__(self, *args, **kwargs):
- # Instantiate the superclass normally
- super().__init__(*args, **kwargs)
-
- self.fields["pk"] = serializers.CharField(read_only=True)
- self.fields["uuid"] = serializers.CharField(read_only=True)
- self.fields["resource_type"] = serializers.CharField(required=False)
- self.fields["polymorphic_ctype_id"] = serializers.CharField(read_only=True)
- self.fields["owner"] = DynamicRelationField(UserSerializer, embed=True, many=False, read_only=True)
- self.fields["metadata_author"] = ContactRoleField(Roles.METADATA_AUTHOR.name, required=False)
- self.fields["processor"] = ContactRoleField(Roles.PROCESSOR.name, required=False)
- self.fields["publisher"] = ContactRoleField(Roles.PUBLISHER.name, required=False)
- self.fields["custodian"] = ContactRoleField(Roles.CUSTODIAN.name, required=False)
- self.fields["poc"] = ContactRoleField(Roles.POC.name, required=False)
- self.fields["distributor"] = ContactRoleField(Roles.DISTRIBUTOR.name, required=False)
- self.fields["resource_user"] = ContactRoleField(Roles.RESOURCE_USER.name, required=False)
- self.fields["resource_provider"] = ContactRoleField(Roles.RESOURCE_PROVIDER.name, required=False)
- self.fields["originator"] = ContactRoleField(Roles.ORIGINATOR.name, required=False)
- self.fields["principal_investigator"] = ContactRoleField(Roles.PRINCIPAL_INVESTIGATOR.name, required=False)
- self.fields["title"] = serializers.CharField(required=False)
- self.fields["abstract"] = serializers.CharField(required=False)
- self.fields["attribution"] = serializers.CharField(required=False)
- self.fields["doi"] = serializers.CharField(required=False)
- self.fields["alternate"] = serializers.CharField(read_only=True, required=False)
- self.fields["date"] = serializers.DateTimeField(required=False)
- self.fields["date_type"] = serializers.CharField(required=False)
- self.fields["temporal_extent_start"] = serializers.DateTimeField(required=False)
- self.fields["temporal_extent_end"] = serializers.DateTimeField(required=False)
- self.fields["edition"] = serializers.CharField(required=False)
- self.fields["purpose"] = serializers.CharField(required=False)
- self.fields["maintenance_frequency"] = serializers.CharField(required=False)
- self.fields["constraints_other"] = serializers.CharField(required=False)
- self.fields["language"] = serializers.CharField(required=False)
- self.fields["supplemental_information"] = serializers.CharField(required=False)
- self.fields["data_quality_statement"] = serializers.CharField(required=False)
- self.fields["bbox_polygon"] = fields.GeometryField(read_only=True, required=False)
- self.fields["ll_bbox_polygon"] = fields.GeometryField(read_only=True, required=False)
- self.fields["extent"] = ExtentBboxField(required=False)
- self.fields["srid"] = serializers.CharField(required=False)
- self.fields["group"] = ComplexDynamicRelationField(GroupSerializer, embed=True, many=False)
- self.fields["popular_count"] = serializers.CharField(required=False)
- self.fields["share_count"] = serializers.CharField(required=False)
- self.fields["rating"] = serializers.CharField(required=False)
- self.fields["featured"] = serializers.BooleanField(required=False)
- self.fields["is_published"] = serializers.BooleanField(required=False, read_only=True)
- self.fields["is_approved"] = serializers.BooleanField(required=False, read_only=True)
- self.fields["detail_url"] = DetailUrlField(read_only=True)
- self.fields["created"] = serializers.DateTimeField(read_only=True)
- self.fields["last_updated"] = serializers.DateTimeField(read_only=True)
- self.fields["raw_abstract"] = serializers.CharField(read_only=True)
- self.fields["raw_purpose"] = serializers.CharField(read_only=True)
- self.fields["raw_constraints_other"] = serializers.CharField(read_only=True)
- self.fields["raw_supplemental_information"] = serializers.CharField(read_only=True)
- self.fields["raw_data_quality_statement"] = serializers.CharField(read_only=True)
- self.fields["metadata_only"] = serializers.BooleanField(required=False)
- self.fields["processed"] = serializers.BooleanField(read_only=True)
- self.fields["state"] = serializers.CharField(read_only=True)
- self.fields["sourcetype"] = serializers.CharField(read_only=True)
- self.fields["embed_url"] = EmbedUrlField(required=False)
- self.fields["thumbnail_url"] = ThumbnailUrlField(read_only=True)
- self.fields["keywords"] = ComplexDynamicRelationField(
- SimpleHierarchicalKeywordSerializer, embed=False, many=True
- )
- self.fields["tkeywords"] = ComplexDynamicRelationField(SimpleThesaurusKeywordSerializer, embed=False, many=True)
- self.fields["regions"] = DynamicRelationField(SimpleRegionSerializer, embed=True, many=True, read_only=True)
- self.fields["category"] = ComplexDynamicRelationField(SimpleTopicCategorySerializer, embed=True, many=False)
- self.fields["restriction_code_type"] = ComplexDynamicRelationField(
- RestrictionCodeTypeSerializer, embed=True, many=False
- )
- self.fields["license"] = ComplexDynamicRelationField(LicenseSerializer, embed=True, many=False)
- self.fields["spatial_representation_type"] = ComplexDynamicRelationField(
- SpatialRepresentationTypeSerializer, embed=True, many=False
+class PermsSerializer(DynamicModelSerializer):
+ class Meta:
+ model = ResourceBase
+ fields = ("pk",)
+
+ def to_representation(self, instance):
+ request = self.context.get("request", None)
+ resource = ResourceBase.objects.get(pk=instance)
+ return (
+ (
+ resource.get_user_perms(request.user)
+ .union(resource.get_self_resource().get_user_perms(request.user))
+ .union(resource.get_real_instance().get_user_perms(request.user))
+ )
+ if request and request.user and resource
+ else []
)
- self.fields["blob"] = serializers.JSONField(required=False, write_only=True)
- self.fields["is_copyable"] = serializers.BooleanField(read_only=True)
- self.fields["download_url"] = DownloadLinkField(read_only=True)
- self.fields["favorite"] = FavoriteField(read_only=True)
- self.fields["download_urls"] = DownloadArrayLinkField(read_only=True)
- metadata = ComplexDynamicRelationField(ExtraMetadataSerializer, embed=False, many=True, deferred=True)
+
+class LinksSerializer(DynamicModelSerializer):
+ class Meta:
+ model = ResourceBase
+
+ def to_representation(self, instance):
+ ret = []
+ link_fields = ["extension", "link_type", "name", "mime", "url"]
+ links = Link.objects.filter(
+ resource_id=instance, link_type__in=["OGC:WMS", "OGC:WFS", "OGC:WCS", "image", "metadata"]
+ )
+ for lnk in links:
+ formatted_link = model_to_dict(lnk, fields=link_fields)
+ ret.append(formatted_link)
+ return ret
+
+
+class ResourceBaseSerializer(DynamicModelSerializer):
+ pk = serializers.CharField(read_only=True)
+ uuid = serializers.CharField(read_only=True)
+ resource_type = serializers.CharField(required=False)
+ polymorphic_ctype_id = serializers.CharField(read_only=True)
+ owner = DynamicRelationField(user_serializer(), embed=True, read_only=True)
+ metadata_author = ContactRoleField(Roles.METADATA_AUTHOR.name, required=False)
+ processor = ContactRoleField(Roles.PROCESSOR.name, required=False)
+ publisher = ContactRoleField(Roles.PUBLISHER.name, required=False)
+ custodian = ContactRoleField(Roles.CUSTODIAN.name, required=False)
+ poc = ContactRoleField(Roles.POC.name, required=False)
+ distributor = ContactRoleField(Roles.DISTRIBUTOR.name, required=False)
+ resource_user = ContactRoleField(Roles.RESOURCE_USER.name, required=False)
+ resource_provider = ContactRoleField(Roles.RESOURCE_PROVIDER.name, required=False)
+ originator = ContactRoleField(Roles.ORIGINATOR.name, required=False)
+ principal_investigator = ContactRoleField(Roles.PRINCIPAL_INVESTIGATOR.name, required=False)
+ title = serializers.CharField(required=False)
+ abstract = serializers.CharField(required=False)
+ attribution = serializers.CharField(required=False)
+ doi = serializers.CharField(required=False)
+ alternate = serializers.CharField(read_only=True, required=False)
+ date = serializers.DateTimeField(required=False)
+ date_type = serializers.CharField(required=False)
+ temporal_extent_start = serializers.DateTimeField(required=False)
+ temporal_extent_end = serializers.DateTimeField(required=False)
+ edition = serializers.CharField(required=False)
+ purpose = serializers.CharField(required=False)
+ maintenance_frequency = serializers.CharField(required=False)
+ constraints_other = serializers.CharField(required=False)
+ language = serializers.CharField(required=False)
+ supplemental_information = serializers.CharField(required=False)
+ data_quality_statement = serializers.CharField(required=False)
+ bbox_polygon = fields.GeometryField(read_only=True, required=False)
+ ll_bbox_polygon = fields.GeometryField(read_only=True, required=False)
+ extent = ExtentBboxField(required=False)
+ srid = serializers.CharField(required=False)
+ group = ComplexDynamicRelationField(GroupSerializer, embed=True)
+ popular_count = serializers.CharField(required=False)
+ share_count = serializers.CharField(required=False)
+ rating = serializers.CharField(required=False)
+ featured = serializers.BooleanField(required=False)
+ advertised = serializers.BooleanField(required=False)
+ is_published = serializers.BooleanField(required=False, read_only=True)
+ is_approved = serializers.BooleanField(required=False, read_only=True)
+ detail_url = DetailUrlField(read_only=True)
+ created = serializers.DateTimeField(read_only=True)
+ last_updated = serializers.DateTimeField(read_only=True)
+ raw_abstract = serializers.CharField(read_only=True)
+ raw_purpose = serializers.CharField(read_only=True)
+ raw_constraints_other = serializers.CharField(read_only=True)
+ raw_supplemental_information = serializers.CharField(read_only=True)
+ raw_data_quality_statement = serializers.CharField(read_only=True)
+ metadata_only = serializers.BooleanField(required=False)
+ processed = serializers.BooleanField(read_only=True)
+ state = serializers.CharField(read_only=True)
+ sourcetype = serializers.CharField(read_only=True)
+ embed_url = EmbedUrlField(required=False)
+ thumbnail_url = ThumbnailUrlField(read_only=True)
+ keywords = ComplexDynamicRelationField(SimpleHierarchicalKeywordSerializer, many=True)
+ tkeywords = ComplexDynamicRelationField(SimpleThesaurusKeywordSerializer, many=True)
+ regions = DynamicRelationField(SimpleRegionSerializer, embed=True, many=True, read_only=True)
+ category = ComplexDynamicRelationField(SimpleTopicCategorySerializer, embed=True)
+ restriction_code_type = ComplexDynamicRelationField(RestrictionCodeTypeSerializer, embed=True)
+ license = ComplexDynamicRelationField(LicenseSerializer, embed=True)
+ spatial_representation_type = ComplexDynamicRelationField(SpatialRepresentationTypeSerializer, embed=True)
+ blob = serializers.JSONField(required=False, write_only=True)
+ is_copyable = serializers.BooleanField(read_only=True)
+ download_url = DownloadLinkField(read_only=True)
+ favorite = FavoriteField(read_only=True)
+ download_urls = DownloadArrayLinkField(read_only=True)
+ perms = DynamicRelationField(PermsSerializer, source="id", read_only=True)
+ links = DynamicRelationField(LinksSerializer, source="id", read_only=True)
+
+ # Deferred fields
+ metadata = ComplexDynamicRelationField(ExtraMetadataSerializer, many=True, deferred=True)
+ data = DataBlobField(DataBlobSerializer, source="id", deferred=True, required=False)
+ executions = DynamicRelationField(
+ ResourceExecutionRequestSerializer, source="id", deferred=True, required=False, read_only=True
+ )
+ linked_resources = DynamicRelationField(
+ LinkedResourceEmbeddedSerializer, source="id", deferred=True, required=False, read_only=True
+ )
+ link = AutoLinkField(read_only=True)
class Meta:
model = ResourceBase
@@ -635,6 +667,7 @@ class Meta:
"share_count",
"rating",
"featured",
+ "advertised",
"is_published",
"is_approved",
"detail_url",
@@ -656,46 +689,19 @@ class Meta:
"blob",
"metadata",
"executions",
+ "linked_resources",
+ "download_url",
+ "download_urls",
+ "extent",
+ "favorite",
+ "thumbnail_url",
+ "links",
+ "link",
# TODO
# csw_typename, csw_schema, csw_mdsource, csw_insert_date, csw_type, csw_anytext, csw_wkt_geometry,
# metadata_uploaded, metadata_uploaded_preserve, metadata_xml,
# users_geolimits, groups_geolimits
)
- extra_kwargs = {
- "abstract": {"required": False},
- "attribution": {"required": False},
- "doi": {"required": False},
- "date": {"required": False},
- "date_type": {"required": False},
- "temporal_extent_start": {"required": False},
- "temporal_extent_end": {"required": False},
- "edition": {"required": False},
- "purpose": {"required": False},
- "maintenance_frequency": {"required": False},
- "constraints_other": {"required": False},
- "language": {"required": False},
- "supplemental_information": {"required": False},
- "data_quality_statement": {"required": False},
- "bbox_polygon": {"required": False},
- "ll_bbox_polygon": {"required": False},
- "extent": {"required": False},
- "srid": {"required": False},
- "popular_count": {"required": False},
- "share_count": {"required": False},
- "rating": {"required": False},
- "featured": {"required": False},
- "is_published": {"required": False},
- "is_approved": {"required": False},
- "metadata_only": {"required": False},
- "embed_url": {"required": False},
- "thumbnail_url": {"required": False},
- "blob": {"required": False, "write_only": True},
- "executions": {"required": False, "embed": False, "deferred": True, "read_only": True},
- "owner": {"required": False},
- "resource_type": {"required": False},
- "download_url": {"required": False},
- "is_copyable": {"required": False},
- }
def to_internal_value(self, data):
if isinstance(data, str):
@@ -725,30 +731,6 @@ def save(self, **kwargs):
instance.set_bbox_polygon(coords, srid)
return instance
- """
- - Deferred / not Embedded --> ?include[]=data
- """
- data = DataBlobField(
- DataBlobSerializer,
- source="id",
- many=False,
- embed=False,
- deferred=True,
- required=False,
- )
-
- """
- - Deferred / not Embedded --> ?include[]=executions
- """
- executions = DynamicRelationField(
- ResourceExecutionRequestSerializer,
- source="id",
- embed=False,
- deferred=True,
- required=False,
- read_only=True,
- )
-
class FavoriteSerializer(DynamicModelSerializer):
resource = serializers.SerializerMethodField()
@@ -833,24 +815,6 @@ class Meta:
avatar = AvatarUrlField(240, read_only=True)
-class SimpleResourceSerializer(DynamicModelSerializer):
- warnings.warn("SimpleResourceSerializer is deprecated", DeprecationWarning, stacklevel=2)
-
- class Meta:
- name = "linked_resources"
- model = ResourceBase
- fields = ("pk", "title", "resource_type", "detail_url", "thumbnail_url")
-
- def to_representation(self, instance: LinkedResource):
- return {
- "pk": instance.pk,
- "title": f"{'>>>' if instance.is_target else '<<<'} {instance.title}",
- "resource_type": instance.resource_type,
- "detail_url": instance.detail_url,
- "thumbnail_url": instance.thumbnail_url,
- }
-
-
class LinkedResourceSerializer(DynamicModelSerializer):
def __init__(self, *kargs, serialize_source: bool = False, **kwargs):
super().__init__(*kargs, **kwargs)
diff --git a/geonode/base/api/tests.py b/geonode/base/api/tests.py
index 94dae413cd5..7a70b2f2c7b 100644
--- a/geonode/base/api/tests.py
+++ b/geonode/base/api/tests.py
@@ -388,9 +388,7 @@ def test_register_users(self):
Ensure users are created with default groups.
"""
url = reverse("users-list")
- user_data = {
- "username": "new_user",
- }
+ user_data = {"username": "new_user", "password": "@!2XJSL_S&V^0nt", "email": "user@exampl2e.com"}
self.assertTrue(self.client.login(username="admin", password="admin"))
response = self.client.post(url, data=user_data, format="json")
self.assertEqual(response.status_code, 201)
@@ -401,6 +399,15 @@ def test_register_users(self):
response = self.client.post(url, data={"username": "new_user_1"}, format="json")
self.assertEqual(response.status_code, 403)
+ def test_acess_profile_edit(self):
+ # Registered member
+ self.assertTrue(self.client.login(username="bobby", password="bob"))
+ user = get_user_model().objects.get(username="bobby")
+
+ url = f'{reverse("profile_edit")}{user.username}'
+ response = self.client.get(url, format="json")
+ self.assertEqual(response.status_code, 200)
+
def test_update_user_profile(self):
"""
Ensure users cannot update others.
@@ -410,7 +417,7 @@ def test_update_user_profile(self):
username="user_test_delete", email="user_test_delete@geonode.org", password="user"
)
url = reverse("users-detail", kwargs={"pk": user.pk})
- data = {"first_name": "user"}
+ data = {"first_name": "user", "password": "@!2XJSL_S&V^0nt", "email": "user@exampl2e.com"}
# Anonymous
response = self.client.patch(url, data=data, format="json")
self.assertEqual(response.status_code, 403)
@@ -421,14 +428,15 @@ def test_update_user_profile(self):
# User self profile
self.assertTrue(self.client.login(username="user_test_delete", password="user"))
response = self.client.patch(url, data=data, format="json")
- self.assertEqual(response.status_code, 403)
+ self.assertEqual(response.status_code, 200)
# Group manager
group = GroupProfile.objects.create(slug="test_group_manager", title="test_group_manager")
group.join(user)
group.join(get_user_model().objects.get(username="norman"), role="manager")
self.assertTrue(self.client.login(username="norman", password="norman"))
response = self.client.post(url, data=data, format="json")
- self.assertEqual(response.status_code, 403)
+ # malformed url on post
+ self.assertEqual(response.status_code, 405)
# Admin can edit user
self.assertTrue(self.client.login(username="admin", password="admin"))
response = self.client.patch(url, data={"first_name": "user_admin"}, format="json")
@@ -457,14 +465,20 @@ def test_delete_user_profile(self):
self.assertTrue(self.client.login(username="bobby", password="bob"))
response = self.client.delete(url, format="json")
self.assertEqual(response.status_code, 403)
- # User can not delete self profile
+ # User can delete self profile
self.assertTrue(self.client.login(username="user_test_delete", password="user"))
response = self.client.delete(url, format="json")
- self.assertEqual(response.status_code, 403)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(get_user_model().objects.filter(username="user_test_delete").first(), None)
+ # recreate user that was deleted
+ user = get_user_model().objects.create_user(
+ username="user_test_delete", email="user_test_delete@geonode.org", password="user"
+ )
+ url = reverse("users-detail", kwargs={"pk": user.pk})
# Admin can delete user
self.assertTrue(self.client.login(username="admin", password="admin"))
response = self.client.delete(url, format="json")
- self.assertEqual(response.status_code, 204)
+ self.assertEqual(response.status_code, 200)
finally:
user.delete()
@@ -1877,47 +1891,6 @@ def test_tkeywords_list(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data["total"], ThesaurusKeyword.objects.count())
- def test_rating_resource(self):
- resource = Dataset.objects.first()
- url = reverse("base-resources-ratings", args=[resource.pk])
- resource.set_permissions(
- {"users": {get_anonymous_user().username: ["base.view_resourcebase"], "bobby": ["base.add_resourcebase"]}}
- )
- data = {"rating": 3}
- # Anonymous user
- response = self.client.get(url)
- self.assertEqual(response.json()["rating"], 0)
- self.assertEqual(response.json()["overall_rating"], 0)
- self.assertEqual(response.status_code, 200)
-
- response = self.client.post(url, data=data)
- self.assertEqual(response.status_code, 403)
-
- # Authenticated user
- self.assertTrue(self.client.login(username="admin", password="admin"))
- response = self.client.get(url)
- self.assertEqual(response.json()["rating"], 0)
- self.assertEqual(response.json()["overall_rating"], 0)
- self.assertEqual(response.status_code, 200)
-
- response = self.client.post(url, data=data)
- self.assertEqual(response.json()["rating"], 3)
- self.assertEqual(response.json()["overall_rating"], 3.0)
- self.assertEqual(response.status_code, 200)
-
- # Authenticated user2
- self.assertTrue(self.client.login(username="bobby", password="bob"))
- response = self.client.get(url)
- self.assertEqual(response.json()["rating"], 0)
- self.assertEqual(response.json()["overall_rating"], 3.0)
- self.assertEqual(response.status_code, 200)
-
- data["rating"] = 1
- response = self.client.post(url, data=data)
- self.assertEqual(response.json()["rating"], 1)
- self.assertEqual(response.json()["overall_rating"], 2.0)
- self.assertEqual(response.status_code, 200)
-
def test_set_resource_thumbnail(self):
re_uuid = "[0-F]{8}-([0-F]{4}-){3}[0-F]{12}"
resource = Dataset.objects.first()
@@ -2408,6 +2381,14 @@ def _assertCloningWithPerms(self, resource):
self.assertEqual(response.status_code, 200)
resource.delete()
+ def _get_for_object(self, o, viewname):
+ url = reverse(viewname, args=[o.id])
+ response = self.client.get(url, format="json")
+ return response.json()
+
+ def _get_for_map(self, viewname):
+ return self._get_for_object(Map.objects.first(), viewname)
+
def test_base_resources_return_download_link_if_document(self):
"""
Ensure we can access the Resource Base list.
@@ -2415,14 +2396,13 @@ def test_base_resources_return_download_link_if_document(self):
doc = Document.objects.first()
# From resource base API
- url = reverse("base-resources-detail", args=[doc.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_url")
+ json = self._get_for_object(doc, "base-resources-detail")
+ download_url = json.get("resource").get("download_url")
self.assertEqual(build_absolute_uri(doc.download_url), download_url)
# from documents api
- url = reverse("documents-detail", args=[doc.id])
- download_url = response.json().get("resource").get("download_url")
+ json = self._get_for_object(doc, "documents-detail")
+ download_url = json.get("document").get("download_url")
self.assertEqual(build_absolute_uri(doc.download_url), download_url)
def test_base_resources_return_download_link_if_dataset(self):
@@ -2432,46 +2412,41 @@ def test_base_resources_return_download_link_if_dataset(self):
_dataset = Dataset.objects.first()
# From resource base API
- url = reverse("base-resources-detail", args=[_dataset.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_url")
+ json = self._get_for_object(_dataset, "base-resources-detail")
+ download_url = json.get("resource").get("download_url")
self.assertEqual(_dataset.download_url, download_url)
# from dataset api
- url = reverse("datasets-detail", args=[_dataset.id])
- download_url = response.json().get("resource").get("download_url")
+ json = self._get_for_object(_dataset, "datasets-detail")
+ download_url = json.get("dataset").get("download_url")
self.assertEqual(_dataset.download_url, download_url)
def test_base_resources_dont_return_download_link_if_map(self):
"""
Ensure we can access the Resource Base list.
"""
- _map = Map.objects.first()
# From resource base API
- url = reverse("base-resources-detail", args=[_map.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_url", None)
+ json = self._get_for_map("base-resources-detail")
+ download_url = json.get("resource").get("download_url", None)
self.assertIsNone(download_url)
# from maps api
- url = reverse("maps-detail", args=[_map.id])
- download_url = response.json().get("resource").get("download_url")
+ json = self._get_for_map("maps-detail")
+ download_url = json.get("map").get("download_url")
self.assertIsNone(download_url)
def test_base_resources_return_not_download_links_for_maps(self):
"""
Ensure we can access the Resource Base list.
"""
- _map = Map.objects.first()
# From resource base API
- url = reverse("base-resources-detail", args=[_map.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_urls", None)
+ json = self._get_for_map("base-resources-detail")
+ download_url = json.get("resource").get("download_urls", None)
self.assertListEqual([], download_url)
# from maps api
- url = reverse("maps-detail", args=[_map.id])
- download_url = response.json().get("resource").get("download_urls")
+ json = self._get_for_map("maps-detail")
+ download_url = json.get("map").get("download_urls")
self.assertListEqual([], download_url)
def test_base_resources_return_download_links_for_documents(self):
@@ -2481,14 +2456,13 @@ def test_base_resources_return_download_links_for_documents(self):
doc = Document.objects.first()
expected_payload = [{"url": build_absolute_uri(doc.download_url), "ajax_safe": doc.download_is_ajax_safe}]
# From resource base API
- url = reverse("base-resources-detail", args=[doc.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_urls")
+ json = self._get_for_object(doc, "base-resources-detail")
+ download_url = json.get("resource").get("download_urls")
self.assertListEqual(expected_payload, download_url)
# from documents api
- url = reverse("documents-detail", args=[doc.id])
- download_url = response.json().get("resource").get("download_urls")
+ json = self._get_for_object(doc, "documents-detail")
+ download_url = json.get("document").get("download_urls")
self.assertListEqual(expected_payload, download_url)
def test_base_resources_return_download_links_for_datasets(self):
@@ -2501,16 +2475,175 @@ def test_base_resources_return_download_links_for_datasets(self):
]
# From resource base API
- url = reverse("base-resources-detail", args=[_dataset.id])
- response = self.client.get(url, format="json")
- download_url = response.json().get("resource").get("download_urls")
+ json = self._get_for_object(_dataset, "base-resources-detail")
+ download_url = json.get("resource").get("download_urls")
self.assertEqual(expected_payload, download_url)
# from dataset api
- url = reverse("datasets-detail", args=[_dataset.id])
- download_url = response.json().get("resource").get("download_urls")
+ json = self._get_for_object(_dataset, "datasets-detail")
+ download_url = json.get("dataset").get("download_urls")
self.assertEqual(expected_payload, download_url)
+ def test_include_linked_resources(self):
+ dataset = Dataset.objects.first()
+ doc = Document.objects.first()
+ map = Map.objects.first()
+
+ for resource, typed_viewname in (
+ (dataset, "datasets-detail"),
+ (doc, "documents-detail"),
+ (map, "maps-detail"),
+ ):
+ for viewname in (typed_viewname, "base-resources-detail"):
+ for include in (True, False):
+ url = reverse(viewname, args=[resource.id])
+ url = f"{url}{'?include[]=linked_resources' if include else ''}"
+ response = self.client.get(url, format="json").json()
+ json = next(iter(response.values()))
+ if include:
+ self.assertIn("linked_resources", json, "Missing content")
+ else:
+ self.assertNotIn("linked_resources", json, "Unexpected content")
+
+ def test_exclude_all_but_one(self):
+ dataset = Dataset.objects.first()
+ doc = Document.objects.first()
+ map = Map.objects.first()
+
+ for resource, typed_viewname in (
+ (dataset, "datasets-detail"),
+ (doc, "documents-detail"),
+ (map, "maps-detail"),
+ ):
+ for viewname in (typed_viewname, "base-resources-detail"):
+ for field in (
+ "pk",
+ "title",
+ "perms",
+ "links",
+ "linked_resources",
+ "data",
+ "link",
+ ): # test some random fields
+ url = reverse(viewname, args=[resource.id])
+ url = f"{url}?exclude[]=*&include[]={field}"
+ response = self.client.get(url, format="json").json()
+ json = next(iter(response.values()))
+
+ self.assertIn(field, json, "Missing content")
+ self.assertEqual(1, len(json), f"Only expected content was '{field}', found: {json}")
+
+ def test_presets_base(self):
+ dataset = Dataset.objects.first()
+ doc = Document.objects.first()
+ map = Map.objects.first()
+
+ for resource, typed_viewname in (
+ (dataset, "datasets-detail"),
+ (doc, "documents-detail"),
+ (map, "maps-detail"),
+ ):
+ for viewname in (typed_viewname, "base-resources-detail"):
+ url = reverse(viewname, args=[resource.id])
+ url = f"{url}?api_preset=bare"
+ response = self.client.get(url, format="json").json()
+ json = next(iter(response.values()))
+ self.assertSetEqual(
+ {"pk", "title"},
+ set(json.keys()),
+ f"Bad json content for object {type(resource)} JSON:{json}",
+ )
+
+ def test_api_should_return_all_resources_for_admin(self):
+ """
+ Api whould return all resources even if advertised=False.
+ """
+ url = reverse("base-resources-list")
+ self.client.login(username="admin", password="admin")
+ payload = self.client.get(url)
+ prev_count = payload.json().get("total")
+ # update all the resource to advertised=False
+ Dataset.objects.update(advertised=False)
+ url = reverse("base-resources-list")
+ payload = self.client.get(url)
+ new_count = payload.json().get("total")
+ self.assertEqual(new_count, prev_count)
+
+ Dataset.objects.update(advertised=True)
+
+ def test_api_should_return_advertised_resource_if_anonymous(self):
+ """
+ If anonymous user, only the advertised resoruces whould be returned by the API.
+ """
+ url = reverse("base-resources-list")
+ payload = self.client.get(url)
+ prev_count = payload.json().get("total")
+ # update all the resource to advertised=False
+ Dataset.objects.update(advertised=False)
+ url = reverse("base-resources-list")
+ payload = self.client.get(url)
+ new_count = payload.json().get("total")
+ self.assertNotEqual(new_count, prev_count)
+
+ Dataset.objects.update(advertised=True)
+
+ def test_api_should_return_only_the_advertised_false_where_user_is_owner(self):
+ """
+ Api Should return all the resource with advertised=True
+ And the resource with advertised=False if is owner of it
+ """
+ # defining a new user
+ test_user_for_api = get_user_model().objects.create(username="test_user_for_api", password="password")
+ # creating a new resource for the user with advertised=False
+ dataset = create_single_dataset(name="test_resource_for_api", owner=test_user_for_api, advertised=False)
+ url = reverse("base-resources-list")
+ self.client.force_login(test_user_for_api)
+ payload = self.client.get(f"{url}?limit=1000")
+ # the uuid of the dataset is in the returned payload
+ self.assertTrue(dataset.uuid in [k["uuid"] for k in payload.json()["resources"]])
+ # bobby is not able to see the dataset belonging to the previous user
+ self.client.login(username="bobby", password="bob")
+ payload = self.client.get(url)
+ self.assertFalse(dataset.uuid in [k["uuid"] for k in payload.json()["resources"]])
+
+ # cleanup
+ dataset.delete()
+ test_user_for_api.delete()
+
+ def test_api_should_filter_by_advertised_param(self):
+ """
+ If anonymous user, only the advertised resoruces whould be returned by the API.
+ """
+ dts = create_single_dataset("advertised_false")
+ dts.advertised = False
+ dts.save()
+ # should show the result based on the logic
+ url = reverse("base-resources-list")
+ payload = self.client.get(url)
+ prev_count = payload.json().get("total")
+ # the user can see only the advertised resources
+ self.assertEqual(ResourceBase.objects.filter(advertised=True).count(), prev_count)
+
+ payload = self.client.get(f"{url}?advertised=True")
+ # so if advertised is True, we dont see the advertised=False resource
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count)
+
+ payload = self.client.get(f"{url}?advertised=False")
+ # so if advertised is False, we see only the resource with advertised==False
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, 1)
+
+ # if all is requested, we will see all the resources
+ payload = self.client.get(f"{url}?advertised=all")
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count + 1)
+
+ Dataset.objects.update(advertised=True)
+
class TestExtraMetadataBaseApi(GeoNodeBaseTestSupport):
def setUp(self):
@@ -2609,6 +2742,171 @@ def test_only_get_method_is_available(self):
response = self.client.put(url)
self.assertEqual(response.status_code, 403)
+ def test_insert_one_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ response = self.client.post(url, data={"target": [self.map.id]}, content_type="application/json")
+
+ link_connected = LinkedResource.objects.get(source_id=self.doc.id)
+
+ self.assertEqual(response.status_code, 200)
+
+ response_json = response.json()
+
+ self.assertTrue((self.map.id in response_json["success"]))
+
+ self.assertEqual(self.doc.id, link_connected.source_id)
+
+ self.assertEqual(self.map.id, link_connected.target_id)
+
+ def test_insert_linked_resource_invalid_type(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ response = self.client.post(url, data={"target": self.map.id}, content_type="application/json")
+ self.assertEqual(response.status_code, 400)
+
+ def test_insert_self_as_linked_resource(self):
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ # linked resource cannot be linked to itself
+ response = self.client.post(url, data={"target": [self.doc.id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((self.doc.id in response_json["error"]))
+
+ def test_insert_bad_payload_linked_resource(self):
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ # linked resource cannot have an invalid payload
+ response = self.client.post(url, data={"target_XXX": [self.doc.id]})
+
+ self.assertEqual(response.status_code, 400)
+
+ def test_insert_existing_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+ LinkedResource.objects.create(source_id=self.doc.id, target_id=self.doc.id)
+
+ # linked resource cannot be duplicated
+ response = self.client.post(url, data={"target": [self.doc.id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((self.doc.id in response_json["error"]))
+
+ def test_insert_multiple_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ response = self.client.post(
+ url, data={"target": [self.map.id, self.dataset.id]}, content_type="application/json"
+ )
+
+ list_connected = LinkedResource.objects.filter(source_id=self.doc.id).all()
+
+ list_connected_targets = [linked.target_id for linked in list_connected]
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(2, len(list_connected))
+ response_json = response.json()
+ self.assertTrue((self.map.id in response_json["success"]))
+ self.assertTrue((self.dataset.id in response_json["success"]))
+ self.assertEqual(2, len(response_json["success"]))
+
+ self.assertTrue((self.map.id in list_connected_targets))
+ self.assertTrue((self.dataset.id in list_connected_targets))
+
+ def test_insert_invalid_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ # generate an invalid id
+ invalid_id = ResourceBase.objects.last().id + 1
+
+ # make sure id does not exist
+ invalid_resource = ResourceBase.objects.filter(id=invalid_id).first()
+ self.assertEqual(None, invalid_resource)
+
+ response = self.client.post(url, data={"target": [invalid_id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((invalid_id in response_json["error"]))
+
+ def test_insert_valid_and_invalid_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ # generate an invalid id
+ invalid_id = ResourceBase.objects.last().id + 1
+
+ # make sure id does not exist
+ invalid_resource = ResourceBase.objects.filter(id=invalid_id).first()
+ self.assertEqual(None, invalid_resource)
+
+ response = self.client.post(url, data={"target": [invalid_id, self.map.id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((invalid_id in response_json["error"]))
+ self.assertTrue((self.map.id in response_json["success"]))
+
+ def test_delete_invalid_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ # generate an invalid id
+ invalid_id = ResourceBase.objects.last().id + 1
+
+ # make sure id does not exist
+ invalid_resource = ResourceBase.objects.filter(id=invalid_id).first()
+ self.assertEqual(None, invalid_resource)
+
+ response = self.client.delete(url, data={"target": [invalid_id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((invalid_id in response_json["error"]))
+
+ def test_delete_linked_resource(self):
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+
+ LinkedResource.objects.create(source_id=self.doc.id, target_id=self.map.id)
+
+ list_connected = LinkedResource.objects.filter(source_id=self.doc.id).all()
+ # check count after insertion
+ self.assertEqual(1, len(list_connected))
+
+ response = self.client.delete(url, data={"target": [self.map.id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 200)
+ # check count after deletion
+ self.assertEqual(0, len(LinkedResource.objects.filter(source_id=self.doc.id).all()))
+ self.assertTrue((self.map.id in response_json["success"]))
+
+ def test_delete_not_found_from_linked_resource(self):
+ self.client.force_login(get_user_model().objects.get(username="admin"))
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+ # Make sure there are no linked resource
+ linked_res = LinkedResource.objects.filter(source_id=self.doc.id).all()
+ for link in linked_res:
+ link.delete()
+
+ # try deleting a valid resource but not found in linked res
+
+ response = self.client.delete(url, data={"target": [self.map.id]}, content_type="application/json")
+ response_json = response.json()
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue((self.map.id in response_json["error"]))
+
def test_linked_resource_for_document(self):
_d = []
try:
@@ -2794,13 +3092,14 @@ def test_linked_resource_deprecated_pagination(self):
# call the API w/ pagination
url = reverse("base-resources-linked_resources", args=[self.doc.id])
- response = self.client.get(f"{url}?page_size=1")
+ url = f"{url}?page_size=1"
+ response = self.client.get(url)
# validation
self.assertEqual(response.status_code, 200)
payload = response.json()
- self.assertIn("WARNINGS", payload, "Missing WARNINGS element")
+ self.assertIn("WARNINGS", payload, f"Missing WARNINGS element for URL {url}")
self.assertIn("PAGINATION", payload["WARNINGS"], "Missing PAGINATION element")
# call the API w/o pagination
@@ -2811,8 +3110,7 @@ def test_linked_resource_deprecated_pagination(self):
self.assertEqual(response.status_code, 200)
payload = response.json()
- self.assertIn("WARNINGS", payload, "Missing WARNINGS element")
- self.assertNotIn("PAGINATION", payload["WARNINGS"], "Unexpected PAGINATION element")
+ self.assertNotIn("WARNINGS", payload, "Missing WARNINGS element")
finally:
for d in _d:
@@ -2862,7 +3160,7 @@ def test_linked_resource_filter_multiple_resource_type_linktype(self):
res_types_payload = [res["resource_type"] for res in payload["linked_to"]]
for type in res_types_payload:
self.assertTrue(type in res_types_orig)
- self.assertTrue({"linked_to", "WARNINGS"} == set(payload.keys()))
+ self.assertSetEqual({"linked_to"}, set(payload.keys()))
finally:
for d in _d:
@@ -2887,8 +3185,32 @@ def test_linked_resource_filter_multiple_resource_type_without_linktype(self):
res_types_payload = [res["resource_type"] for res in payload["linked_to"]]
for type in res_types_payload:
self.assertTrue(type in res_types_orig)
- payload_keys = {"linked_by", "linked_to", "WARNINGS"}
- self.assertTrue(payload_keys == set(payload.keys()))
+ payload_keys = {"linked_by", "linked_to"}
+ self.assertSetEqual(payload_keys, set(payload.keys()))
+
+ finally:
+ for d in _d:
+ d.delete()
+
+ def test_linked_resource_filter_one_resource_type(self):
+ _d = []
+ try:
+ # data preparation
+ _d.append(LinkedResource.objects.create(source_id=self.doc.id, target_id=self.dataset.id))
+ _d.append(LinkedResource.objects.create(source_id=self.doc.id, target_id=self.map.id))
+ resource_type_param = "dataset"
+ # call api with single resource_type param
+ url = reverse("base-resources-linked_resources", args=[self.doc.id])
+ response = self.client.get(f"{url}?resource_type={resource_type_param}")
+
+ # validation
+ self.assertEqual(response.status_code, 200)
+ payload = response.json()
+
+ res_types_orig = resource_type_param.split(",")
+ res_types_payload = [res["resource_type"] for res in payload["linked_to"]]
+ for r in res_types_payload:
+ self.assertTrue(r in res_types_orig)
finally:
for d in _d:
diff --git a/geonode/base/api/urls.py b/geonode/base/api/urls.py
index b29044ef6fc..69121b96f6f 100644
--- a/geonode/base/api/urls.py
+++ b/geonode/base/api/urls.py
@@ -20,7 +20,6 @@
from . import views
-router.register(r"users", views.UserViewSet, "users")
router.register(r"groups", views.GroupViewSet, "group-profiles")
router.register(r"resources", views.ResourceBaseViewSet, "base-resources")
router.register(r"owners", views.OwnerViewSet, "owners")
diff --git a/geonode/base/api/views.py b/geonode/base/api/views.py
index 31891c6ad14..8fbbdf71407 100644
--- a/geonode/base/api/views.py
+++ b/geonode/base/api/views.py
@@ -21,16 +21,12 @@
import json
import re
-from decimal import Decimal
from uuid import uuid4
from urllib.parse import urljoin, urlparse
from PIL import Image
from django.apps import apps
-from django.contrib.contenttypes.models import ContentType
from django.core.validators import URLValidator
-from django.db import models
-from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.conf import settings
@@ -44,15 +40,12 @@
from oauth2_provider.contrib.rest_framework import OAuth2Authentication
-from pinax.ratings.categories import category_value
-from pinax.ratings.models import OverallRating, Rating
-from pinax.ratings.views import NUM_OF_RATINGS
-
from rest_framework import status
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.parsers import JSONParser, MultiPartParser
+from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly
@@ -61,7 +54,7 @@
from geonode.maps.models import Map
from geonode.layers.models import Dataset
from geonode.favorite.models import Favorite
-from geonode.base.models import Configuration, ExtraMetadata
+from geonode.base.models import Configuration, ExtraMetadata, LinkedResource
from geonode.thumbs.exceptions import ThumbnailError
from geonode.thumbs.thumbnails import create_thumbnail
from geonode.thumbs.utils import _decode_base64, BASE64_PATTERN
@@ -74,8 +67,7 @@
FavoriteFilter,
TKeywordsFilter,
)
-from geonode.groups.models import GroupProfile, GroupMember
-from geonode.people.utils import get_available_users
+from geonode.groups.models import GroupProfile
from geonode.security.permissions import get_compact_perms_list, PermSpec, PermSpecCompact
from geonode.security.utils import (
get_visible_resources,
@@ -87,18 +79,17 @@
from geonode.resource.api.tasks import resouce_service_dispatcher
from geonode.resource.manager import resource_manager
-from guardian.shortcuts import get_objects_for_user
+from geonode.base.api.mixins import AdvertisedListMixin
from .permissions import (
- IsSelfOrAdminOrReadOnly,
IsOwnerOrAdmin,
IsManagerEditOrAdmin,
ResourceBasePermissionsFilter,
UserHasPerms,
)
+
from .serializers import (
FavoriteSerializer,
- UserSerializer,
PermSpecSerialiazer,
GroupProfileSerializer,
ResourceBaseSerializer,
@@ -111,6 +102,7 @@
ExtraMetadataSerializer,
LinkedResourceSerializer,
)
+from geonode.people.api.serializers import UserSerializer
from .pagination import GeoNodeApiPagination
from geonode.base.utils import validate_extra_metadata
@@ -119,71 +111,6 @@
logger = logging.getLogger(__name__)
-class UserViewSet(DynamicModelViewSet):
- """
- API endpoint that allows users to be viewed or edited.
- """
-
- authentication_classes = [SessionAuthentication, BasicAuthentication, OAuth2Authentication]
- permission_classes = [
- IsAuthenticated,
- IsSelfOrAdminOrReadOnly,
- ]
- filter_backends = [DynamicFilterBackend, DynamicSortingFilter, DynamicSearchFilter]
- serializer_class = UserSerializer
- pagination_class = GeoNodeApiPagination
-
- def get_queryset(self):
- """
- Filters and sorts users.
- """
- if self.request and self.request.user:
- queryset = get_available_users(self.request.user)
- else:
- queryset = get_user_model().objects.all()
-
- # Set up eager loading to avoid N+1 selects
- queryset = self.get_serializer_class().setup_eager_loading(queryset)
- return queryset.order_by("username")
-
- @extend_schema(
- methods=["get"],
- responses={200: ResourceBaseSerializer(many=True)},
- description="API endpoint allowing to retrieve the Resources visible to the user.",
- )
- @action(detail=True, methods=["get"])
- def resources(self, request, pk=None):
- user = self.get_object()
- permitted = get_objects_for_user(user, "base.view_resourcebase")
- qs = ResourceBase.objects.all().filter(id__in=permitted).order_by("title")
-
- resources = get_visible_resources(
- qs,
- user,
- admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
- unpublished_not_visible=settings.RESOURCE_PUBLISHING,
- private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
- )
-
- paginator = GeoNodeApiPagination()
- paginator.page_size = request.GET.get("page_size", 10)
- result_page = paginator.paginate_queryset(resources, request)
- serializer = ResourceBaseSerializer(result_page, embed=True, many=True, context={"request": request})
- return paginator.get_paginated_response({"resources": serializer.data})
-
- @extend_schema(
- methods=["get"],
- responses={200: GroupProfileSerializer(many=True)},
- description="API endpoint allowing to retrieve the Groups the user is member of.",
- )
- @action(detail=True, methods=["get"])
- def groups(self, request, pk=None):
- user = self.get_object()
- qs_ids = GroupMember.objects.filter(user=user).values_list("group", flat=True)
- groups = GroupProfile.objects.filter(id__in=qs_ids)
- return Response(GroupProfileSerializer(embed=True, many=True).to_representation(groups))
-
-
class GroupViewSet(DynamicModelViewSet):
"""
API endpoint that allows gropus to be viewed or edited.
@@ -344,7 +271,33 @@ def get_queryset(self):
return queryset.order_by("username")
-class ResourceBaseViewSet(DynamicModelViewSet):
+class ApiPresetsInitializer(APIView):
+ """
+ Replaces the `api_preset` query params with the configured params
+ """
+
+ def initialize_request(self, request, *args, **kwargs):
+ self.replace_presets(request)
+ return super().initialize_request(request, *args, **kwargs)
+
+ def replace_presets(self, request):
+ # we must make the GET mutable since in the filters, some queryparams are popped
+ request.GET._mutable = True
+ try:
+ for preset_name in request.GET.pop("api_preset", []):
+ presets = settings.REST_API_PRESETS.get(preset_name, None)
+ if not presets:
+ logger.info(f'Preset "{preset_name}" is not defined') # maybe return 404?
+ return
+ for param_name in presets.keys():
+ for param_value in presets.get(param_name):
+ if param_value not in request.GET.get(param_name, []):
+ request.GET.appendlist(param_name, param_value)
+ finally:
+ request.GET._mutable = False
+
+
+class ResourceBaseViewSet(ApiPresetsInitializer, DynamicModelViewSet, AdvertisedListMixin):
"""
API endpoint that allows base resources to be viewed or edited.
"""
@@ -693,7 +646,7 @@ def resource_service_permissions(self, request, pk, *args, **kwargs):
"created": request_params.get("created", False),
},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -854,7 +807,7 @@ def resource_service_ingest(self, request, resource_type: str = None, *args, **k
"defaults": request_params.get("defaults", f'{{"owner":"{request.user.username}"}}'),
},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -954,7 +907,7 @@ def resource_service_create(self, request, resource_type: str = None, *args, **k
"defaults": request_params.get("defaults", f'{{"owner":"{request.user.username}"}}'),
},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -1038,7 +991,7 @@ def resource_service_delete(self, request, pk, *args, **kwargs):
geonode_resource=resource,
input_params={"uuid": resource.uuid},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -1159,7 +1112,7 @@ def resource_service_update(self, request, pk, *args, **kwargs):
"notify": request_params.get("notify", True),
},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -1270,7 +1223,7 @@ def resource_service_copy(self, request, pk, *args, **kwargs):
"defaults": request_params.get("defaults", "{}"),
},
)
- resouce_service_dispatcher.apply_async(args=(_exec_request.exec_id,), expiration=30)
+ resouce_service_dispatcher.apply_async(args=(str(_exec_request.exec_id),), expiration=30)
return Response(
{
"status": _exec_request.status,
@@ -1285,47 +1238,6 @@ def resource_service_copy(self, request, pk, *args, **kwargs):
logger.exception(e)
return Response(status=status.HTTP_400_BAD_REQUEST, exception=e)
- @extend_schema(
- methods=["post", "get"],
- responses={200},
- description="API endpoint allowing to rate and get overall rating of the Resource.",
- )
- @action(
- detail=True,
- url_path="ratings",
- url_name="ratings",
- methods=["post", "get"],
- permission_classes=[
- IsAuthenticatedOrReadOnly,
- UserHasPerms(perms_dict={"default": {"POST": ["base.add_resourcebase"]}}),
- ],
- )
- def ratings(self, request, pk, *args, **kwargs):
- resource = get_object_or_404(ResourceBase, pk=pk)
- resource = resource.get_real_instance()
- ct = ContentType.objects.get_for_model(resource)
- if request.method == "POST":
- rating_input = int(request.data.get("rating"))
- category = resource._meta.object_name.lower()
- # check if category is configured in settings.PINAX_RATINGS_CATEGORY_CHOICES
- cat_choice = category_value(resource, category)
-
- # Check for errors and bail early
- if category and cat_choice is None:
- return HttpResponseForbidden("Invalid category. It must match a preconfigured setting")
- if rating_input not in range(NUM_OF_RATINGS + 1):
- return HttpResponseForbidden(f"Invalid rating. It must be a value between 0 and {NUM_OF_RATINGS}")
- Rating.update(rating_object=resource, user=request.user, category=cat_choice, rating=rating_input)
- user_rating = None
- if request.user.is_authenticated:
- user_rating = Rating.objects.filter(object_id=resource.pk, content_type=ct, user=request.user).first()
- overall_rating = OverallRating.objects.filter(object_id=resource.pk, content_type=ct).aggregate(
- r=models.Avg("rating")
- )["r"]
- overall_rating = Decimal(str(overall_rating or "0"))
-
- return Response({"rating": user_rating.rating if user_rating else 0, "overall_rating": overall_rating})
-
@extend_schema(
methods=["put"], responses={200}, description="API endpoint allowing to set thumbnail of the Resource."
)
@@ -1480,85 +1392,136 @@ def _get_request_params(self, request, encode=False):
logger.debug(e)
return request.data
- @extend_schema(methods=["get"], description="Get Linked Resources")
+ @extend_schema(methods=["get", "post", "delete"], description="Get Linked Resources")
@action(
detail=True,
- methods=["get"],
+ methods=["get", "post", "delete"],
permission_classes=[UserHasPerms(perms_dict={"default": {"GET": ["base.view_resourcebase"]}})],
url_path=r"linked_resources", # noqa
url_name="linked_resources",
)
def linked_resources(self, request, pk, *args, **kwargs):
+ resource = self.get_object()
+ if request.method in ("POST", "DELETE"):
+ success_var = []
+ error_var = []
+ payload = {"success": success_var, "error": error_var, "message": "Resources updated successfully"}
+
+ target_ids = request.data.get("target")
+ if not isinstance(target_ids, list):
+ raise ValidationError("Payload is not valid")
+
+ # remove duplicates and self ref
+ target_ids = set(target_ids)
+ if resource.id in target_ids:
+ error_var.append(resource.id)
+
+ valid_ids = target_ids - {resource.id}
+
+ for t_id in valid_ids:
+ try:
+ target = get_object_or_404(ResourceBase, pk=t_id)
+
+ if request.method == "POST":
+ _, created = LinkedResource.objects.get_or_create(source=resource, target=target)
+ if created:
+ success_var.append(t_id)
+ continue
+ error_var.append(t_id)
+ if request.method == "DELETE":
+ link = LinkedResource.objects.filter(source=resource.id, target=t_id).first()
+ if not link:
+ logger.error(f"Resource selected with id {t_id} does not exist")
+ error_var.append(t_id)
+ continue
+ link.delete()
+ success_var.append(t_id)
+ except Exception:
+ error_var.append(t_id)
+ logger.error(f"Resource with id {t_id} not found")
+
+ if len(error_var):
+ payload["message"] = "Some error has occurred during the saving"
+ return Response(payload, status=400)
+
+ return Response(payload, status=200)
+
return base_linked_resources(self.get_object().get_real_instance(), request.user, request.GET)
def base_linked_resources(instance, user, params):
try:
- resource_type = params.get("resource_type")
- link_type = params.get("link_type")
- type_list = resource_type.split(",") if resource_type else []
+ return Response(base_linked_resources_payload(instance, user, params))
+ except Exception as e:
+ logger.exception(e)
+ return Response(data={"message": e.args[0], "success": False}, status=500, exception=True)
- warnings = {}
- if "page_size" in params or "page" in params:
- warnings["PAGINATION"] = "Pagination is not supported on this call"
+def base_linked_resources_payload(instance, user, params={}):
+ resource_type = params.get("resource_type", None)
+ link_type = params.get("link_type", None)
+ type_list = resource_type.split(",") if resource_type else []
- ret = {"WARNINGS": warnings}
+ warnings = {}
- get_visible_resources_p = functools.partial(
- get_visible_resources,
- user=user,
- admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
- unpublished_not_visible=settings.RESOURCE_PUBLISHING,
- private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
- )
+ if "page_size" in params or "page" in params:
+ warnings["PAGINATION"] = "Pagination is not supported on this call"
- if not link_type or link_type == "linked_to":
- # list of linked resources, probably extended by ResourceBase's child class - may be loopable only once
- linked_to_over = instance.get_linked_resources()
+ ret = {"WARNINGS": warnings}
- # resolve the ids of linked resources - using either e QuerySet (preferred) or a list
- if isinstance(linked_to_over, QuerySet):
- linked_to_over_loopable = linked_to_over
- linked_to_id_values = linked_to_over.values("target_id")
- else:
- linked_to_over_loopable = [lr for lr in linked_to_over]
- linked_to_id_values = [lr.target_id for lr in linked_to_over_loopable]
+ get_visible_resources_p = functools.partial(
+ get_visible_resources,
+ user=user,
+ admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
+ unpublished_not_visible=settings.RESOURCE_PUBLISHING,
+ private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
+ )
- # filter resources by visibility / permissions
- linked_to_visib = get_visible_resources_p(ResourceBase.objects.filter(id__in=linked_to_id_values)).order_by(
- "-pk"
- )
- # optionally filter by resource type
- linked_to_visib = linked_to_visib.filter(resource_type__in=type_list) if type_list else linked_to_visib
- linked_to_visib_ids = linked_to_visib.values_list("id", flat=True)
- linked_to = [lres for lres in linked_to_over_loopable if lres.target.id in linked_to_visib_ids]
-
- ret["linked_to"] = LinkedResourceSerializer(linked_to, embed=True, many=True).data
-
- if not link_type or link_type == "linked_by":
- linked_by_over = instance.get_linked_resources(as_target=True)
- if isinstance(linked_by_over, QuerySet):
- linked_by_over_loopable = linked_by_over
- linked_by_id_values = linked_by_over.values("source_id")
- else:
- linked_by_over_loopable = [lr for lr in linked_by_over]
- linked_by_id_values = [lr.source_id for lr in linked_by_over_loopable]
+ if not link_type or link_type == "linked_to":
+ # list of linked resources, probably extended by ResourceBase's child class - may be loopable only once
+ linked_to_over = instance.get_linked_resources()
- linked_by_visib = get_visible_resources_p(ResourceBase.objects.filter(id__in=linked_by_id_values)).order_by(
- "-pk"
- )
+ # resolve the ids of linked resources - using either e QuerySet (preferred) or a list
+ if isinstance(linked_to_over, QuerySet):
+ linked_to_over_loopable = linked_to_over
+ linked_to_id_values = linked_to_over.values("target_id")
+ else:
+ linked_to_over_loopable = [lr for lr in linked_to_over]
+ linked_to_id_values = [lr.target_id for lr in linked_to_over_loopable]
+
+ # filter resources by visibility / permissions
+ linked_to_visib = get_visible_resources_p(ResourceBase.objects.filter(id__in=linked_to_id_values)).order_by(
+ "-pk"
+ )
+ # optionally filter by resource type
+ linked_to_visib = linked_to_visib.filter(resource_type__in=type_list) if type_list else linked_to_visib
+ linked_to_visib_ids = linked_to_visib.values_list("id", flat=True)
+ linked_to = [lres for lres in linked_to_over_loopable if lres.target.id in linked_to_visib_ids]
+
+ ret["linked_to"] = LinkedResourceSerializer(linked_to, embed=True, many=True).data
+
+ if not link_type or link_type == "linked_by":
+ linked_by_over = instance.get_linked_resources(as_target=True)
+ if isinstance(linked_by_over, QuerySet):
+ linked_by_over_loopable = linked_by_over
+ linked_by_id_values = linked_by_over.values("source_id")
+ else:
+ linked_by_over_loopable = [lr for lr in linked_by_over]
+ linked_by_id_values = [lr.source_id for lr in linked_by_over_loopable]
- linked_by_visib = linked_by_visib.filter(resource_type__in=type_list) if type_list else linked_by_visib
- linked_by_visib_ids = linked_by_visib.values_list("id", flat=True)
- linked_by = [lres for lres in linked_by_over_loopable if lres.source.id in linked_by_visib_ids]
+ linked_by_visib = get_visible_resources_p(ResourceBase.objects.filter(id__in=linked_by_id_values)).order_by(
+ "-pk"
+ )
- ret["linked_by"] = LinkedResourceSerializer(
- instance=linked_by, serialize_source=True, embed=True, many=True
- ).data
+ linked_by_visib = linked_by_visib.filter(resource_type__in=type_list) if type_list else linked_by_visib
+ linked_by_visib_ids = linked_by_visib.values_list("id", flat=True)
+ linked_by = [lres for lres in linked_by_over_loopable if lres.source.id in linked_by_visib_ids]
- return Response(ret)
+ ret["linked_by"] = LinkedResourceSerializer(
+ instance=linked_by, serialize_source=True, embed=True, many=True
+ ).data
- except Exception as e:
- logger.exception(e)
- return Response(data={"message": e.args[0], "success": False}, status=500, exception=True)
+ if not ret["WARNINGS"]:
+ ret.pop("WARNINGS")
+
+ return ret
diff --git a/geonode/base/apps.py b/geonode/base/apps.py
new file mode 100644
index 00000000000..4b24947e52a
--- /dev/null
+++ b/geonode/base/apps.py
@@ -0,0 +1,38 @@
+#########################################################################
+#
+# Copyright (C) 2016 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+from django.apps import AppConfig
+from django.utils.translation import gettext_noop as _
+
+from geonode.notifications_helper import NotificationsAppConfigBase
+
+
+class BaseAppConfig(NotificationsAppConfigBase, AppConfig):
+ name = "geonode.base"
+ NOTIFICATIONS = (
+ (
+ "request_download_resourcebase",
+ _("Request to download a resource"),
+ _("A request for downloading a resource was sent"),
+ ),
+ (
+ "request_resource_edit",
+ _("Request resource change"),
+ _("Owner has requested permissions to modify a resource"),
+ ),
+ )
diff --git a/geonode/base/enumerations.py b/geonode/base/enumerations.py
index 93e05d8b60f..4d956e8331f 100644
--- a/geonode/base/enumerations.py
+++ b/geonode/base/enumerations.py
@@ -17,7 +17,7 @@
#
#########################################################################
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
LINK_TYPES = ["original", "data", "image", "metadata", "html", "OGC:WMS", "OGC:WFS", "OGC:WCS"]
diff --git a/geonode/base/forms.py b/geonode/base/forms.py
index 8152da290e5..14552bed7d5 100644
--- a/geonode/base/forms.py
+++ b/geonode/base/forms.py
@@ -34,10 +34,10 @@
from django.forms import models
from django.forms.fields import ChoiceField, MultipleChoiceField
from django.forms.utils import flatatt
-from django.utils.encoding import force_text
+from django.utils.encoding import force_str
from django.utils.html import format_html
from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from modeltranslation.forms import TranslationModelForm
from taggit.forms import TagField
from tinymce.widgets import TinyMCE
@@ -151,7 +151,7 @@ def value_from_datadict(self, data, files, name):
def render_option_value(self, selected_choices, option_value, option_label, data_section=None):
if option_value is None:
option_value = ""
- option_value = force_text(option_value)
+ option_value = force_str(option_value)
if option_value in selected_choices:
selected_html = mark_safe(" selected")
if not self.allow_multiple_selected:
@@ -160,12 +160,12 @@ def render_option_value(self, selected_choices, option_value, option_label, data
else:
selected_html = ""
- label = force_text(option_label)
+ label = force_str(option_label)
if data_section is None:
data_section = ""
else:
- data_section = force_text(data_section)
+ data_section = force_str(data_section)
if "/" in data_section:
label = format_html("{} [{}]", label, data_section.rsplit("/", 1)[1])
@@ -181,7 +181,7 @@ def _region_id_from_choice(choice):
else:
return choice.id
- selected_choices = {force_text(_region_id_from_choice(v)) for v in selected_choices}
+ selected_choices = {force_str(_region_id_from_choice(v)) for v in selected_choices}
output = []
output.append(format_html('', "Global"))
@@ -192,25 +192,25 @@ def _region_id_from_choice(choice):
for option_value, option_label in self.choices:
if isinstance(option_label, (list, tuple)) and not isinstance(option_label, str):
- output.append(format_html('', force_text(option_value)))
+ output.append(format_html('', force_str(option_value)))
for option in option_label:
if isinstance(option, (list, tuple)) and not isinstance(option, str):
if isinstance(option[1][0], (list, tuple)) and not isinstance(option[1][0], str):
for option_child in option[1][0]:
output.append(
self.render_option_value(
- selected_choices, *option_child, data_section=force_text(option[1][0][0])
+ selected_choices, *option_child, data_section=force_str(option[1][0][0])
)
)
else:
output.append(
self.render_option_value(
- selected_choices, *option[1], data_section=force_text(option[0])
+ selected_choices, *option[1], data_section=force_str(option[0])
)
)
else:
output.append(
- self.render_option_value(selected_choices, *option, data_section=force_text(option_value))
+ self.render_option_value(selected_choices, *option, data_section=force_str(option_value))
)
output.append(" ")
diff --git a/geonode/base/management/command_utils.py b/geonode/base/management/command_utils.py
new file mode 100644
index 00000000000..9d70f22d49e
--- /dev/null
+++ b/geonode/base/management/command_utils.py
@@ -0,0 +1,34 @@
+import logging
+from django.conf import settings
+
+DEFAULT_COMMAND_LOGGER_NAME = "geonode.commands"
+
+
+def setup_logger(logger_name=DEFAULT_COMMAND_LOGGER_NAME, formatter_name="command", handler_name="command"):
+ if logger_name not in settings.LOGGING["loggers"]:
+ format = "%(levelname)-7s %(asctime)s %(message)s"
+
+ settings.LOGGING["formatters"][formatter_name] = {
+ "format": format
+ }
+ settings.LOGGING["handlers"][handler_name] = {
+ "level": "DEBUG",
+ "class": "logging.StreamHandler",
+ "formatter": formatter_name
+ }
+ settings.LOGGING["loggers"][logger_name] = {
+ "handlers": [handler_name],
+ "level": "INFO",
+ "propagate": False
+ }
+
+ handler = logging.StreamHandler()
+ handler.setFormatter(logging.Formatter(fmt=format))
+ handler.setLevel(logging.DEBUG)
+
+ logger = logging.getLogger(logger_name)
+ logger.addHandler(handler)
+ logger.setLevel(logging.INFO)
+ logger.propagate = False
+
+ return logger
diff --git a/geonode/base/migrations/0044_resourcebase_bbox_polygon.py b/geonode/base/migrations/0044_resourcebase_bbox_polygon.py
index 1819805da70..28252a3a893 100644
--- a/geonode/base/migrations/0044_resourcebase_bbox_polygon.py
+++ b/geonode/base/migrations/0044_resourcebase_bbox_polygon.py
@@ -2,6 +2,16 @@
import django.contrib.gis.db.models.fields
from django.db import migrations
+from django.contrib.gis.geos import Polygon
+
+
+def populate_polygon(apps, schema_editor):
+ ResourceBase = apps.get_model('base', 'ResourceBase')
+ for res in ResourceBase.objects.all():
+ bbox = [getattr(res, key, None) for key in ('bbox_x0', 'bbox_y0', 'bbox_x1', 'bbox_y1')]
+ if all(bbox):
+ res.bbox_polygon = Polygon.from_bbox(bbox)
+ res.save()
class Migration(migrations.Migration):
@@ -16,4 +26,5 @@ class Migration(migrations.Migration):
name='bbox_polygon',
field=django.contrib.gis.db.models.fields.PolygonField(null=True, blank=True, srid=4326),
),
+ migrations.RunPython(populate_polygon, migrations.RunPython.noop),
]
diff --git a/geonode/base/migrations/0056_resourcebase_ll_bbox_polygon.py b/geonode/base/migrations/0056_resourcebase_ll_bbox_polygon.py
index 944c7567e28..20e8f044fb7 100644
--- a/geonode/base/migrations/0056_resourcebase_ll_bbox_polygon.py
+++ b/geonode/base/migrations/0056_resourcebase_ll_bbox_polygon.py
@@ -1,8 +1,35 @@
# Generated by Django 2.2.16 on 2021-03-02 11:41
+import logging
import django.contrib.gis.db.models.fields
+from django.contrib.gis.geos import Polygon
+
from django.db import migrations
+logger = logging.getLogger(__name__)
+
+
+def populate_ll_polygon(apps, schema_editor):
+ ResourceBase = apps.get_model('base', 'ResourceBase')
+ for res in ResourceBase.objects.all():
+ try:
+ bbox = res.bbox_polygon
+ srid = None
+ srid = int(res.srid.split(':')[1]) if res.srid else 4326
+ # pre-catch well known data problems
+ if srid == 404000:
+ logger.warning(f"Skipping resource ID:{res.id} '{res.title}' SRID:{res.srid} ({srid})")
+ continue
+
+ if bbox and srid:
+ bbox = bbox.clone()
+ bbox.srid = srid
+ bbox.transform(4326)
+ res.ll_bbox_polygon = bbox
+ res.save()
+ except Exception as e:
+ logger.warning(f"Error while transforming resource ID:{res.id} '{res.title}' SRID:{res.srid} ({srid}): {e}", exc_info=e)
+ raise e
class Migration(migrations.Migration):
@@ -16,4 +43,5 @@ class Migration(migrations.Migration):
name='ll_bbox_polygon',
field=django.contrib.gis.db.models.fields.PolygonField(blank=True, null=True, srid=4326),
),
+ migrations.RunPython(populate_ll_polygon, migrations.RunPython.noop),
]
diff --git a/geonode/base/migrations/0074_drop_curated_thumbs.py b/geonode/base/migrations/0074_drop_curated_thumbs.py
index 478ee010e55..086a31ad2ed 100644
--- a/geonode/base/migrations/0074_drop_curated_thumbs.py
+++ b/geonode/base/migrations/0074_drop_curated_thumbs.py
@@ -1,6 +1,5 @@
from django.db import migrations, connection
-from geonode.base.models import ResourceBase
from geonode.storage.manager import storage_manager
from geonode.thumbs.thumbnails import _generate_thumbnail_name
import logging
@@ -13,6 +12,9 @@ def update_thumbnail_urls_and_delete_curated_thumbs_folder(apps, schema_editor):
INNER JOIN base_curatedthumbnail
ON base_resourcebase.id=base_curatedthumbnail.resource_id;
'''
+ # use historical model
+ ResourceBase = apps.get_model('base', 'ResourceBase')
+
c = connection.cursor()
c.execute(query)
results = c.fetchall()
diff --git a/geonode/base/migrations/0089_resourcebase_advertised.py b/geonode/base/migrations/0089_resourcebase_advertised.py
new file mode 100644
index 00000000000..a15fc7a7d1c
--- /dev/null
+++ b/geonode/base/migrations/0089_resourcebase_advertised.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.23 on 2024-01-16 14:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('base', '0088_auto_20231019_1244'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='resourcebase',
+ name='advertised',
+ field=models.BooleanField(default=True, help_text='If False, will hide the resource from search results and catalog listings', verbose_name='Advertised'),
+ ),
+ ]
diff --git a/geonode/base/migrations/0090_alter_resourcebase_polymorphic_ctype.py b/geonode/base/migrations/0090_alter_resourcebase_polymorphic_ctype.py
new file mode 100644
index 00000000000..7b791214a92
--- /dev/null
+++ b/geonode/base/migrations/0090_alter_resourcebase_polymorphic_ctype.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.9 on 2024-01-10 14:56
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("contenttypes", "0002_remove_content_type_name"),
+ ("base", "0089_resourcebase_advertised"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="resourcebase",
+ name="polymorphic_ctype",
+ field=models.ForeignKey(
+ editable=False,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="polymorphic_%(app_label)s.%(class)s_set+",
+ to="contenttypes.contenttype",
+ ),
+ ),
+ ]
diff --git a/geonode/base/migrations/0091_alter_hierarchicalkeyword_slug.py b/geonode/base/migrations/0091_alter_hierarchicalkeyword_slug.py
new file mode 100644
index 00000000000..e884c56b26a
--- /dev/null
+++ b/geonode/base/migrations/0091_alter_hierarchicalkeyword_slug.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.9 on 2024-05-21 08:23
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("base", "0090_alter_resourcebase_polymorphic_ctype"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="hierarchicalkeyword",
+ name="slug",
+ field=models.SlugField(allow_unicode=True, max_length=100, unique=True, verbose_name="slug"),
+ ),
+ ]
diff --git a/geonode/base/models.py b/geonode/base/models.py
index c75918c755e..0c83f340898 100644
--- a/geonode/base/models.py
+++ b/geonode/base/models.py
@@ -46,7 +46,7 @@
from django.contrib.gis.geos import GEOSGeometry, Polygon, Point
from django.contrib.gis.db.models import PolygonField
from django.core.exceptions import ValidationError
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from django.utils.html import strip_tags
from mptt.models import MPTTModel, TreeForeignKey
@@ -55,7 +55,6 @@
from polymorphic.models import PolymorphicModel
from polymorphic.managers import PolymorphicManager
-from pinax.ratings.models import OverallRating
from taggit.models import TagBase, ItemBase
from taggit.managers import TaggableManager, _TaggableManager
@@ -850,7 +849,11 @@ class ResourceBase(PolymorphicModel, PermissionLevelMixin, ItemBase):
is_approved = models.BooleanField(
_("Approved"), default=True, help_text=_("Is this resource validated from a publisher or editor?")
)
-
+ advertised = models.BooleanField(
+ _("Advertised"),
+ default=True,
+ help_text=_("If False, will hide the resource from search results and catalog listings"),
+ )
# fields necessary for the apis
thumbnail_url = models.TextField(_("Thumbnail url"), null=True, blank=True)
thumbnail_path = models.TextField(_("Thumbnail path"), null=True, blank=True)
@@ -2124,16 +2127,6 @@ class GroupGeoLimit(models.Model):
wkt = models.TextField(db_column="wkt", blank=True)
-def rating_post_save(instance, *args, **kwargs):
- """
- Used to fill the average rating field on OverallRating change.
- """
- ResourceBase.objects.filter(id=instance.object_id).update(rating=instance.rating)
-
-
-signals.post_save.connect(rating_post_save, sender=OverallRating)
-
-
class ExtraMetadata(models.Model):
resource = models.ForeignKey(ResourceBase, null=False, blank=False, on_delete=models.CASCADE)
metadata = JSONField(null=True, default=dict, blank=True)
diff --git a/geonode/base/templates/base/user_and_group_permissions.html b/geonode/base/templates/base/user_and_group_permissions.html
index 216aaf7d0d3..cfe3f0f4053 100644
--- a/geonode/base/templates/base/user_and_group_permissions.html
+++ b/geonode/base/templates/base/user_and_group_permissions.html
@@ -12,13 +12,10 @@
-
+
-
-
-
diff --git a/geonode/base/templatetags/base_tags.py b/geonode/base/templatetags/base_tags.py
index 23960e0eeaa..d9bf63f4c36 100644
--- a/geonode/base/templatetags/base_tags.py
+++ b/geonode/base/templatetags/base_tags.py
@@ -21,12 +21,10 @@
from django.db.models import Q
from django.conf import settings
from django.db.models import Count
-from django.utils.translation import ugettext
+from django.utils.translation import gettext_lazy
from django.contrib.auth import get_user_model
-from django.utils.translation import ugettext_lazy as _
-from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import gettext_lazy as _
-from pinax.ratings.models import Rating
from guardian.shortcuts import get_objects_for_user
from geonode.maps.models import Map
@@ -53,7 +51,7 @@
@register.filter(name="template_trans")
def template_trans(text):
try:
- return ugettext(text)
+ return gettext_lazy(text)
except Exception:
return text
@@ -64,12 +62,6 @@ def get_item(dictionary, key):
return dictionary.get(key)
-@register.simple_tag
-def num_ratings(obj):
- ct = ContentType.objects.get_for_model(obj)
- return len(Rating.objects.filter(object_id=obj.pk, content_type=ct))
-
-
@register.simple_tag(takes_context=True)
def facets(context):
request = context["request"]
diff --git a/geonode/base/templatetags/user_messages.py b/geonode/base/templatetags/user_messages.py
index 92c381ed8c9..421e3a692c4 100644
--- a/geonode/base/templatetags/user_messages.py
+++ b/geonode/base/templatetags/user_messages.py
@@ -21,7 +21,7 @@
from django.conf import settings
from django.db.models import Sum
from django.contrib.auth import get_user_model
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
register = template.Library()
diff --git a/geonode/base/urls.py b/geonode/base/urls.py
index 77e3d226b4e..28e2b37aacf 100644
--- a/geonode/base/urls.py
+++ b/geonode/base/urls.py
@@ -16,7 +16,7 @@
# along with this program. If not, see .
#
#########################################################################
-from django.conf.urls import url, include
+from django.urls import include, re_path
from .views import (
DatasetsAutocomplete,
@@ -32,50 +32,50 @@
urlpatterns = [
- url(
+ re_path(
r"^autocomplete_response/$",
ResourceBaseAutocomplete.as_view(),
name="autocomplete_base",
),
- url(
+ re_path(
r"^autocomplete_linked_resource/$",
LinkedResourcesAutocomplete.as_view(),
name="autocomplete_linked_resource",
),
- url(
+ re_path(
r"^autocomplete_region/$",
RegionAutocomplete.as_view(),
name="autocomplete_region",
),
- url(
+ re_path(
r"^autocomplete_hierachical_keyword/$",
HierarchicalKeywordAutocomplete.as_view(),
name="autocomplete_hierachical_keyword",
),
- url(
+ re_path(
r"^thesaurus_available",
ThesaurusAvailable.as_view(),
name="thesaurus_available",
),
- url(
+ re_path(
r"^thesaurus_autocomplete/$",
ThesaurusKeywordLabelAutocomplete.as_view(),
name="thesaurus_autocomplete",
),
- url(
+ re_path(
r"^datasets_autocomplete/$",
DatasetsAutocomplete.as_view(),
name="datasets_autocomplete",
),
- url(
+ re_path(
r"^resource_rights/(?P\d+)$",
OwnerRightsRequestView.as_view(),
name="owner_rights_request",
),
- url(
+ re_path(
r"^resource_clone/?$",
resource_clone,
name="resource_clone",
),
- url(r"^", include("geonode.base.api.urls")),
+ re_path(r"^", include("geonode.base.api.urls")),
]
diff --git a/geonode/base/views.py b/geonode/base/views.py
index ab7359c8b3b..e51c342fedf 100644
--- a/geonode/base/views.py
+++ b/geonode/base/views.py
@@ -31,7 +31,7 @@
from django.http import HttpResponseRedirect
from django.contrib.auth import get_user_model
from django.contrib import messages
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
diff --git a/geonode/br/__init__.py b/geonode/br/__init__.py
index df3f634b661..79177e00bdd 100644
--- a/geonode/br/__init__.py
+++ b/geonode/br/__init__.py
@@ -16,13 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-
-from django.apps import AppConfig
-
-
-class BackupRestoreAppConfig(AppConfig):
- name = "geonode.br"
- verbose_name = "Backup/Restore"
-
-
-default_app_config = "geonode.br.BackupRestoreAppConfig"
diff --git a/geonode/br/apps.py b/geonode/br/apps.py
new file mode 100644
index 00000000000..55624e0ffb9
--- /dev/null
+++ b/geonode/br/apps.py
@@ -0,0 +1,25 @@
+#########################################################################
+#
+# Copyright (C) 2016 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+from django.apps import AppConfig
+
+
+class BackupRestoreAppConfig(AppConfig):
+ name = "geonode.br"
+ verbose_name = "Backup/Restore"
diff --git a/geonode/br/management/commands/restore.py b/geonode/br/management/commands/restore.py
index 95ee228d955..e8b48ba8287 100755
--- a/geonode/br/management/commands/restore.py
+++ b/geonode/br/management/commands/restore.py
@@ -304,7 +304,6 @@ def execute_restore(self, **options):
self.restore_geoserver_vector_data(config, settings, target_folder, soft_reset)
self.restore_geoserver_externals(config, settings, target_folder)
logger.info("*** Recreate GWC tile layers")
- call_command("create_tile_layers")
except Exception as e:
logger.warning(f"*** GeoServer Restore failed: {e}", exc_info=e)
if recovery_file:
diff --git a/geonode/catalogue/__init__.py b/geonode/catalogue/__init__.py
index d1b9c72dbff..a64ed9c768b 100644
--- a/geonode/catalogue/__init__.py
+++ b/geonode/catalogue/__init__.py
@@ -25,7 +25,7 @@
from django.core.exceptions import ImproperlyConfigured
from importlib import import_module
-default_app_config = "geonode.catalogue.apps.GeoNodeCatalogueAppConfig"
+
DEFAULT_CATALOGUE_ALIAS = "default"
# GeoNode uses this if the CATALOGUE setting is empty (None).
diff --git a/geonode/catalogue/management/__init__.py b/geonode/catalogue/management/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/geonode/catalogue/management/commands/__init__.py b/geonode/catalogue/management/commands/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/geonode/catalogue/management/commands/regenerate_xml.py b/geonode/catalogue/management/commands/regenerate_xml.py
new file mode 100644
index 00000000000..ce89abdaaa7
--- /dev/null
+++ b/geonode/catalogue/management/commands/regenerate_xml.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+#########################################################################
+#
+# Copyright (C) 2023 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+import logging
+
+from django.core.management.base import BaseCommand
+
+from geonode.base.management import command_utils
+from geonode.base.models import ResourceBase
+from geonode.layers.models import Dataset
+
+
+logger = logging.getLogger(__name__)
+
+
+class Command(BaseCommand):
+ help = "Re-create XML metadata documents"
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ '-l',
+ '--layer',
+ dest="layers",
+ action='append',
+ help="Only process specified layers ")
+
+ parser.add_argument(
+ "--skip-logger-setup",
+ action="store_false",
+ dest="setup_logger",
+ help='Skips setup of the "geonode.br" logger, "br" handler and "br" format if not present in settings',
+ )
+ parser.add_argument(
+ '-d',
+ '--dry-run',
+ dest="dry-run",
+ action='store_true',
+ help="Do not actually perform any change")
+
+ def handle(self, **options):
+ requested_layers = options.get('layers')
+ dry_run = options.get('dry-run')
+
+ if options.get("setup_logger"):
+ logger = command_utils.setup_logger()
+
+ logger.info(f"==== Running command {__name__}")
+ logger.info(f"{self.help}")
+ logger.info("")
+
+ logger.debug(f"DRY-RUN is {dry_run}")
+ logger.debug(f"LAYERS is {requested_layers}")
+
+ try:
+
+ layers = Dataset.objects.all()
+ tot = len(layers)
+ logger.info(f"Total layers in GeoNode: {tot}")
+ i = 0
+ cnt_ok = 0
+ cnt_bad = 0
+ cnt_skip = 0
+
+ instance: ResourceBase
+ for instance in layers:
+ i += 1
+ logger.info(f"- {i}/{tot} Processing layer {instance.id} [{instance.typename}] '{instance.title}'")
+
+ if requested_layers and instance.typename not in requested_layers:
+ logger.info(" - Layer filtered out by args")
+ cnt_skip += 1
+ continue
+
+ if instance.metadata_uploaded and instance.metadata_uploaded_preserve:
+ logger.info(" - Layer filtered out since it uses custom XML")
+ cnt_skip += 1
+ continue
+
+ try:
+ good = None
+ if not dry_run:
+ try:
+ try:
+ # the save() method triggers the metadata regeneration
+ instance.save()
+ good = True
+ except Exception as e:
+ logger.error(f"Error saving instance '{instance.title}': {e}")
+ raise e
+
+ except Exception as e:
+ logger.exception(f"Error processing '{instance.title}': {e}", e)
+
+ if dry_run or good:
+ logger.info(f" - Done {instance.name}")
+ cnt_ok += 1
+ else:
+ logger.warning(f"Metadata couldn't be regenerated for instance '{instance.title}' ")
+ cnt_bad += 1
+
+ except Exception as e:
+ raise e
+ except Exception as e:
+ raise e
+
+ logger.info("Work completed" + (" [DRYRUN]" if dry_run else ""))
+ logger.info(f"- Metadata regenerated : {cnt_ok}")
+ logger.info(f"- Metadata in error : {cnt_bad}")
+ logger.info(f"- Resources skipped : {cnt_skip}")
diff --git a/geonode/catalogue/metadataxsl/__init__.py b/geonode/catalogue/metadataxsl/__init__.py
index 0143f08e23a..79177e00bdd 100644
--- a/geonode/catalogue/metadataxsl/__init__.py
+++ b/geonode/catalogue/metadataxsl/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.catalogue.metadataxsl.apps.GeoNodeCatalogueMetadataxslAppConfig"
diff --git a/geonode/catalogue/metadataxsl/urls.py b/geonode/catalogue/metadataxsl/urls.py
index 10467b86ff8..0b5e6f59049 100644
--- a/geonode/catalogue/metadataxsl/urls.py
+++ b/geonode/catalogue/metadataxsl/urls.py
@@ -17,7 +17,7 @@
#
#########################################################################
-from django.conf.urls import url
+from django.urls import re_path
from . import views
-urlpatterns = [url(r"^xsl/(?P\d+)$", views.prefix_xsl_line, name="prefix_xsl_line")]
+urlpatterns = [re_path(r"^xsl/(?P\d+)$", views.prefix_xsl_line, name="prefix_xsl_line")]
diff --git a/geonode/catalogue/metadataxsl/views.py b/geonode/catalogue/metadataxsl/views.py
index e6cb71feb2f..04913c48496 100644
--- a/geonode/catalogue/metadataxsl/views.py
+++ b/geonode/catalogue/metadataxsl/views.py
@@ -26,7 +26,7 @@
from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.views.decorators.clickjacking import xframe_options_exempt
diff --git a/geonode/catalogue/models.py b/geonode/catalogue/models.py
index caebd2fe42e..e575be179e6 100644
--- a/geonode/catalogue/models.py
+++ b/geonode/catalogue/models.py
@@ -80,11 +80,14 @@ def catalogue_post_save(instance, sender, **kwargs):
resource=resources.get(), url=metadata_url, extension="xml", link_type="metadata"
).update(**_d)
- # generate an XML document (GeoNode's default is ISO)
if instance.metadata_uploaded and instance.metadata_uploaded_preserve:
md_doc = etree.tostring(dlxml.fromstring(instance.metadata_xml))
else:
- md_doc = catalogue.catalogue.csw_gen_xml(instance, settings.CATALOG_METADATA_TEMPLATE)
+ # generate an XML document (GeoNode's default is ISO)
+ raw_xml = catalogue.catalogue.csw_gen_xml(instance, settings.CATALOG_METADATA_TEMPLATE)
+ md_obj = dlxml.fromstring(raw_xml, parser=etree.XMLParser(remove_blank_text=True))
+ md_doc = etree.tostring(md_obj, pretty_print=True, encoding="unicode")
+
try:
csw_anytext = catalogue.catalogue.csw_gen_anytext(md_doc)
except Exception as e:
diff --git a/geonode/catalogue/templates/catalogue/full_metadata.xml b/geonode/catalogue/templates/catalogue/full_metadata.xml
index 10e66e24c08..a9ce9b62f4e 100644
--- a/geonode/catalogue/templates/catalogue/full_metadata.xml
+++ b/geonode/catalogue/templates/catalogue/full_metadata.xml
@@ -1,4 +1,5 @@
{% load thesaurus %}
+{% load l10n %}
{{layer.uuid}}
@@ -383,6 +384,7 @@
+ {% localize off %}
{{layer.ll_bbox.0}}
@@ -395,6 +397,7 @@
{{layer.ll_bbox.3}}
+ {% endlocalize %}
diff --git a/geonode/catalogue/urls.py b/geonode/catalogue/urls.py
index fed2c84eed1..29d03e49890 100644
--- a/geonode/catalogue/urls.py
+++ b/geonode/catalogue/urls.py
@@ -17,19 +17,19 @@
#
#########################################################################
-from django.conf.urls import url
+from django.urls import re_path
from django.urls import path
from . import views
urlpatterns = [
- url(r"^csw$", views.csw_global_dispatch, name="csw_global_dispatch"),
- url(r"^opensearch$", views.opensearch_dispatch, name="opensearch_dispatch"),
- url(
+ re_path(r"^csw$", views.csw_global_dispatch, name="csw_global_dispatch"),
+ re_path(r"^opensearch$", views.opensearch_dispatch, name="opensearch_dispatch"),
+ re_path(
r"^csw_to_extra_format/(?P[^/]*)/(?P[^/]*).txt$",
views.csw_render_extra_format_txt,
name="csw_render_extra_format_txt",
),
- url(
+ re_path(
r"^csw_to_extra_format/(?P[^/]*)/(?P[^/]*).html$",
views.csw_render_extra_format_html,
name="csw_render_extra_format_html",
diff --git a/geonode/client/__init__.py b/geonode/client/__init__.py
index 55702cb7715..204aed5da98 100644
--- a/geonode/client/__init__.py
+++ b/geonode/client/__init__.py
@@ -19,5 +19,4 @@
from pkgutil import extend_path
-default_app_config = "geonode.client.apps.AppConfig"
__path__ = extend_path(__path__, __name__) # noqa
diff --git a/geonode/client/apps.py b/geonode/client/apps.py
index 66e8fe5d1f3..55202b4f72f 100644
--- a/geonode/client/apps.py
+++ b/geonode/client/apps.py
@@ -17,7 +17,7 @@
#
#########################################################################
from django.apps import AppConfig as BaseAppConfig
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
class AppConfig(BaseAppConfig):
diff --git a/geonode/context_processors.py b/geonode/context_processors.py
index 1230a20cbf8..8b83ba2e269 100644
--- a/geonode/context_processors.py
+++ b/geonode/context_processors.py
@@ -69,9 +69,7 @@ def resource_urls(request):
GROUP_MANDATORY_RESOURCES=getattr(settings, "GROUP_MANDATORY_RESOURCES", False),
GROUP_PRIVATE_RESOURCES=getattr(settings, "GROUP_PRIVATE_RESOURCES", False),
RESOURCE_PUBLISHING=getattr(settings, "RESOURCE_PUBLISHING", False),
- HAYSTACK_SEARCH=getattr(settings, "HAYSTACK_SEARCH", False),
SKIP_PERMS_FILTER=getattr(settings, "SKIP_PERMS_FILTER", False),
- HAYSTACK_FACET_COUNTS=getattr(settings, "HAYSTACK_FACET_COUNTS", False),
CLIENT_RESULTS_LIMIT=getattr(settings, "CLIENT_RESULTS_LIMIT", 10),
API_LIMIT_PER_PAGE=getattr(settings, "API_LIMIT_PER_PAGE", 20),
SRID_DETAIL=getattr(settings, "SRID", dict()).get("DETAIL", "never"),
diff --git a/geonode/custom_translations.py b/geonode/custom_translations.py
index 28ca9be0d09..f065a8da19f 100644
--- a/geonode/custom_translations.py
+++ b/geonode/custom_translations.py
@@ -20,7 +20,7 @@
# this file will be used to provide custom translation strings from e.g. fixtures
# it will be parsed by python manage.py makemessages and update the .po files accordingly
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
texts = [
_(
diff --git a/geonode/documents/__init__.py b/geonode/documents/__init__.py
index 527f5fb5f7d..e69de29bb2d 100644
--- a/geonode/documents/__init__.py
+++ b/geonode/documents/__init__.py
@@ -1,60 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2016 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-
-from django.utils.translation import ugettext_noop as _
-from geonode.notifications_helper import NotificationsAppConfigBase
-
-
-class DocumentsAppConfig(NotificationsAppConfigBase):
- name = "geonode.documents"
- NOTIFICATIONS = (
- (
- "document_created",
- _("Document Created"),
- _("A Document was created"),
- ),
- (
- "document_updated",
- _("Document Updated"),
- _("A Document was updated"),
- ),
- (
- "document_approved",
- _("Document Approved"),
- _("A Document was approved by a Manager"),
- ),
- (
- "document_published",
- _("Document Published"),
- _("A Document was published"),
- ),
- (
- "document_deleted",
- _("Document Deleted"),
- _("A Document was deleted"),
- ),
- (
- "document_rated",
- _("Rating for Document"),
- _("A rating was given to a document"),
- ),
- )
-
-
-default_app_config = "geonode.documents.DocumentsAppConfig"
diff --git a/geonode/documents/api/serializers.py b/geonode/documents/api/serializers.py
index 534a6c3fafb..6c8412a73b5 100644
--- a/geonode/documents/api/serializers.py
+++ b/geonode/documents/api/serializers.py
@@ -42,31 +42,26 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["title"] = serializers.CharField(required=False)
- file_path = GeonodeFilePathField(required=False)
- doc_file = DocumentFieldField(required=False)
+ file_path = GeonodeFilePathField(required=False, write_only=True)
+ doc_file = DocumentFieldField(required=False, write_only=True)
class Meta:
model = Document
name = "document"
view_name = "documents-list"
- fields = (
- "pk",
- "uuid",
- "name",
- "href",
- "subtype",
- "extension",
- "mime_type",
- "executions",
- "file_path",
- "doc_file",
- "doc_url",
- "metadata",
+ fields = list(
+ set(
+ ResourceBaseSerializer.Meta.fields
+ + (
+ "uuid",
+ "name",
+ "href",
+ "subtype",
+ "extension",
+ "mime_type",
+ "file_path",
+ "doc_file",
+ "doc_url",
+ )
+ )
)
-
- def to_representation(self, obj):
- _doc = super(DocumentSerializer, self).to_representation(obj)
- # better to hide internal server file path
- _doc.pop("file_path")
- _doc.pop("doc_file")
- return _doc
diff --git a/geonode/documents/api/tests.py b/geonode/documents/api/tests.py
index d6a0793828a..4d144748d41 100644
--- a/geonode/documents/api/tests.py
+++ b/geonode/documents/api/tests.py
@@ -130,6 +130,39 @@ def test_creation_should_rase_exec_for_unsupported_files(self):
self.assertEqual(400, actual.status_code)
self.assertDictEqual(expected, actual.json())
+ def test_document_listing_advertised(self):
+ document = Document.objects.first()
+ document.advertised = False
+ document.save()
+
+ url = reverse("documents-list")
+
+ payload = self.client.get(url)
+
+ prev_count = payload.json().get("total")
+ # the user can see only the advertised resources
+ self.assertTrue(Document.objects.count() > prev_count)
+
+ payload = self.client.get(f"{url}?advertised=True")
+ # so if advertised is True, we dont see the advertised=False resource
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count)
+
+ payload = self.client.get(f"{url}?advertised=False")
+ # so if advertised is False, we see only the resource with advertised==False
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, 1)
+
+ # if all is requested, we will see all the resources
+ payload = self.client.get(f"{url}?advertised=all")
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count + 1)
+
+ Document.objects.update(advertised=True)
+
def test_creation_should_create_the_doc(self):
"""
If file_path is not available, should raise error
diff --git a/geonode/documents/api/views.py b/geonode/documents/api/views.py
index 1e00ca5c12a..d8fe1ca2395 100644
--- a/geonode/documents/api/views.py
+++ b/geonode/documents/api/views.py
@@ -29,9 +29,10 @@
from geonode import settings
from geonode.base.api.filters import DynamicSearchFilter, ExtentFilter
+from geonode.base.api.mixins import AdvertisedListMixin
from geonode.base.api.pagination import GeoNodeApiPagination
from geonode.base.api.permissions import UserHasPerms
-from geonode.base.api.views import base_linked_resources
+from geonode.base.api.views import base_linked_resources, ApiPresetsInitializer
from geonode.base import enumerations
from geonode.documents.api.exceptions import DocumentException
from geonode.documents.models import Document
@@ -49,7 +50,7 @@
logger = logging.getLogger(__name__)
-class DocumentViewSet(DynamicModelViewSet):
+class DocumentViewSet(ApiPresetsInitializer, DynamicModelViewSet, AdvertisedListMixin):
"""
API endpoint that allows documents to be viewed or edited.
"""
diff --git a/geonode/documents/apps.py b/geonode/documents/apps.py
new file mode 100644
index 00000000000..72e2ad96375
--- /dev/null
+++ b/geonode/documents/apps.py
@@ -0,0 +1,58 @@
+#########################################################################
+#
+# Copyright (C) 2016 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+from django.utils.translation import gettext_noop as _
+from geonode.notifications_helper import NotificationsAppConfigBase
+from django.apps import AppConfig
+
+
+class DocumentsAppConfig(NotificationsAppConfigBase, AppConfig):
+ name = "geonode.documents"
+ NOTIFICATIONS = (
+ (
+ "document_created",
+ _("Document Created"),
+ _("A Document was created"),
+ ),
+ (
+ "document_updated",
+ _("Document Updated"),
+ _("A Document was updated"),
+ ),
+ (
+ "document_approved",
+ _("Document Approved"),
+ _("A Document was approved by a Manager"),
+ ),
+ (
+ "document_published",
+ _("Document Published"),
+ _("A Document was published"),
+ ),
+ (
+ "document_deleted",
+ _("Document Deleted"),
+ _("A Document was deleted"),
+ ),
+ (
+ "document_rated",
+ _("Rating for Document"),
+ _("A rating was given to a document"),
+ ),
+ )
diff --git a/geonode/documents/exif/__init__.py b/geonode/documents/exif/__init__.py
index bc045338385..79177e00bdd 100644
--- a/geonode/documents/exif/__init__.py
+++ b/geonode/documents/exif/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.documents.exif.apps.GeoNodeDocumentsExifAppConfig"
diff --git a/geonode/documents/forms.py b/geonode/documents/forms.py
index 7568dc6863b..29ab23d8a97 100644
--- a/geonode/documents/forms.py
+++ b/geonode/documents/forms.py
@@ -26,7 +26,7 @@
from django import forms
from django.conf import settings
from django.forms import HiddenInput
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.template.defaultfilters import filesizeformat
from geonode.base.forms import ResourceBaseForm, get_tree_data
diff --git a/geonode/documents/models.py b/geonode/documents/models.py
index e0a26fe25b4..c68ccb9bedc 100644
--- a/geonode/documents/models.py
+++ b/geonode/documents/models.py
@@ -24,7 +24,7 @@
from django.db import models
from django.urls import reverse
from django.utils.functional import classproperty
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from geonode.client.hooks import hookset
from geonode.base.models import ResourceBase
diff --git a/geonode/documents/search_indexes.py b/geonode/documents/search_indexes.py
deleted file mode 100644
index d5460936b88..00000000000
--- a/geonode/documents/search_indexes.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2016 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-
-from pinax.ratings.models import OverallRating
-from django.contrib.contenttypes.models import ContentType
-from django.db.models import Avg
-from haystack import indexes
-from geonode.documents.models import Document
-
-
-class DocumentIndex(indexes.SearchIndex, indexes.Indexable):
- id = indexes.IntegerField(model_attr="id")
- abstract = indexes.CharField(model_attr="abstract", boost=1.5)
- category__gn_description = indexes.CharField(model_attr="category__gn_description", null=True)
- csw_type = indexes.CharField(model_attr="csw_type")
- csw_wkt_geometry = indexes.CharField(model_attr="csw_wkt_geometry")
- detail_url = indexes.CharField(model_attr="get_absolute_url")
- owner__username = indexes.CharField(model_attr="owner", faceted=True, null=True)
- srid = indexes.CharField(model_attr="srid")
- supplemental_information = indexes.CharField(model_attr="supplemental_information", null=True)
- thumbnail_url = indexes.CharField(model_attr="thumbnail_url", null=True)
- uuid = indexes.CharField(model_attr="uuid")
- title = indexes.CharField(model_attr="title", boost=2)
- date = indexes.DateTimeField(model_attr="date")
-
- text = indexes.EdgeNgramField(document=True, use_template=True, stored=False)
- type = indexes.CharField(faceted=True)
- title_sortable = indexes.CharField(indexed=False, stored=False) # Necessary for sorting
- category = indexes.CharField(model_attr="category__identifier", faceted=True, null=True, stored=True)
- bbox_left = indexes.FloatField(model_attr="bbox_x0", null=True, stored=False)
- bbox_right = indexes.FloatField(model_attr="bbox_x1", null=True, stored=False)
- bbox_bottom = indexes.FloatField(model_attr="bbox_y0", null=True, stored=False)
- bbox_top = indexes.FloatField(model_attr="bbox_y1", null=True, stored=False)
- temporal_extent_start = indexes.DateTimeField(model_attr="temporal_extent_start", null=True, stored=False)
- temporal_extent_end = indexes.DateTimeField(model_attr="temporal_extent_end", null=True, stored=False)
- keywords = indexes.MultiValueField(model_attr="keyword_slug_list", null=True, faceted=True, stored=True)
- regions = indexes.MultiValueField(model_attr="region_name_list", null=True, faceted=True, stored=True)
- popular_count = indexes.IntegerField(model_attr="popular_count", default=0, boost=20)
- share_count = indexes.IntegerField(model_attr="share_count", default=0)
- rating = indexes.IntegerField(null=True)
- num_ratings = indexes.IntegerField(stored=False)
-
- def get_model(self):
- return Document
-
- def prepare_type(self, obj):
- return "document"
-
- def prepare_rating(self, obj):
- ct = ContentType.objects.get_for_model(obj)
- try:
- rating = OverallRating.objects.filter(object_id=obj.pk, content_type=ct).aggregate(r=Avg("rating"))["r"]
- return float(str(rating or "0"))
- except OverallRating.DoesNotExist:
- return 0.0
-
- def prepare_num_ratings(self, obj):
- ct = ContentType.objects.get_for_model(obj)
- try:
- return OverallRating.objects.filter(object_id=obj.pk, content_type=ct).all().count()
- except OverallRating.DoesNotExist:
- return 0
-
- def prepare_title_sortable(self, obj):
- return obj.title.lower().lstrip()
diff --git a/geonode/documents/templates/documents/document_metadata_advanced.html b/geonode/documents/templates/documents/document_metadata_advanced.html
index f7f8dc083a9..630b9cfc4e1 100644
--- a/geonode/documents/templates/documents/document_metadata_advanced.html
+++ b/geonode/documents/templates/documents/document_metadata_advanced.html
@@ -18,13 +18,10 @@
-
+
-
-
-
{% endif %}
+
+ {{ document_form.advertised.label }}
+ {{ document_form.advertised }}
+
diff --git a/geonode/documents/tests.py b/geonode/documents/tests.py
index 164abe7d519..e8adaa83c6e 100644
--- a/geonode/documents/tests.py
+++ b/geonode/documents/tests.py
@@ -37,7 +37,6 @@
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model
-from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.template.defaultfilters import filesizeformat
@@ -48,7 +47,7 @@
from geonode.compat import ensure_string
from geonode.base.models import License, Region, LinkedResource
from geonode.base.enumerations import SOURCE_TYPE_REMOTE
-from geonode.documents import DocumentsAppConfig
+from geonode.documents.apps import DocumentsAppConfig
from geonode.resource.manager import resource_manager
from geonode.tests.base import GeoNodeBaseTestSupport
from geonode.tests.utils import NotificationsTestsHelper
@@ -616,15 +615,6 @@ def testDocumentsNotifications(self):
self.assertTrue(self.check_notification_out("document_updated", self.u))
self.clear_notifications_queue()
- lct = ContentType.objects.get_for_model(_d)
-
- if "pinax.ratings" in settings.INSTALLED_APPS:
- self.clear_notifications_queue()
- from pinax.ratings.models import Rating
-
- rating = Rating(user=self.norman, content_type=lct, object_id=_d.id, content_object=_d, rating=5)
- rating.save()
- self.assertTrue(self.check_notification_out("document_rated", self.u))
class DocumentResourceLinkTestCase(GeoNodeBaseTestSupport):
diff --git a/geonode/documents/urls.py b/geonode/documents/urls.py
index be338923d49..e30ce9daf82 100644
--- a/geonode/documents/urls.py
+++ b/geonode/documents/urls.py
@@ -16,7 +16,7 @@
# along with this program. If not, see .
#
#########################################################################
-from django.conf.urls import url, include
+from django.urls import include, re_path
from django.contrib.auth.decorators import login_required
from .views import DocumentUploadView, DocumentUpdateView
@@ -27,14 +27,14 @@
}
urlpatterns = [ # 'geonode.documents.views',
- url(r"^(?P\d+)/download/?$", views.document_download, name="document_download"),
- url(r"^(?P\d+)/link/?$", views.document_link, name="document_link"),
- url(r"^(?P\d+)/replace$", login_required(DocumentUpdateView.as_view()), name="document_replace"),
- url(r"^(?P\d+)/embed/?$", views.document_embed, name="document_embed"),
- url(r"^upload/?$", login_required(DocumentUploadView.as_view()), name="document_upload"),
- url(r"^(?P[^/]*)/metadata_detail$", views.document_metadata_detail, name="document_metadata_detail"),
- url(r"^(?P\d+)/metadata$", views.document_metadata, name="document_metadata"),
- url(r"^metadata/batch/$", views.document_batch_metadata, name="document_batch_metadata"),
- url(r"^(?P\d+)/metadata_advanced$", views.document_metadata_advanced, name="document_metadata_advanced"),
- url(r"^", include("geonode.documents.api.urls")),
+ re_path(r"^(?P\d+)/download/?$", views.document_download, name="document_download"),
+ re_path(r"^(?P\d+)/link/?$", views.document_link, name="document_link"),
+ re_path(r"^(?P\d+)/replace$", login_required(DocumentUpdateView.as_view()), name="document_replace"),
+ re_path(r"^(?P\d+)/embed/?$", views.document_embed, name="document_embed"),
+ re_path(r"^upload/?$", login_required(DocumentUploadView.as_view()), name="document_upload"),
+ re_path(r"^(?P[^/]*)/metadata_detail$", views.document_metadata_detail, name="document_metadata_detail"),
+ re_path(r"^(?P\d+)/metadata$", views.document_metadata, name="document_metadata"),
+ re_path(r"^metadata/batch/$", views.document_batch_metadata, name="document_batch_metadata"),
+ re_path(r"^(?P\d+)/metadata_advanced$", views.document_metadata_advanced, name="document_metadata_advanced"),
+ re_path(r"^", include("geonode.documents.api.urls")),
]
diff --git a/geonode/documents/utils.py b/geonode/documents/utils.py
index 5957d713eee..63facbbaf09 100644
--- a/geonode/documents/utils.py
+++ b/geonode/documents/utils.py
@@ -29,7 +29,7 @@
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import loader
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify
from django_downloadview.response import DownloadResponse
diff --git a/geonode/documents/views.py b/geonode/documents/views.py
index 49a4c605b1b..545fd715647 100644
--- a/geonode/documents/views.py
+++ b/geonode/documents/views.py
@@ -27,7 +27,7 @@
from django.conf import settings
from django.contrib import messages
from django.shortcuts import render, get_object_or_404
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.contrib.auth.decorators import login_required
from django.template import loader
from django.views.generic.edit import CreateView, UpdateView
diff --git a/geonode/facets/__init__.py b/geonode/facets/__init__.py
index 1759e754b7f..da86ef5219a 100644
--- a/geonode/facets/__init__.py
+++ b/geonode/facets/__init__.py
@@ -16,5 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-
-default_app_config = "geonode.facets.apps.GeoNodeFacetsConfig"
diff --git a/geonode/facets/models.py b/geonode/facets/models.py
index c4109d31d9e..a2ae79e98af 100644
--- a/geonode/facets/models.py
+++ b/geonode/facets/models.py
@@ -30,6 +30,7 @@
FACET_TYPE_CATEGORY = "category"
FACET_TYPE_BASE = "base"
FACET_TYPE_KEYWORD = "keyword"
+FACET_TYPE_GROUP = "group"
logger = logging.getLogger(__name__)
diff --git a/geonode/facets/providers/group.py b/geonode/facets/providers/group.py
new file mode 100644
index 00000000000..c8de489b461
--- /dev/null
+++ b/geonode/facets/providers/group.py
@@ -0,0 +1,109 @@
+#########################################################################
+#
+# Copyright (C) 2023 Open Source Geospatial Foundation - all rights reserved
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+
+import logging
+
+from django.db.models import Count
+from geonode.facets.models import FacetProvider, DEFAULT_FACET_PAGE_SIZE, FACET_TYPE_GROUP
+from geonode.groups.models import GroupProfile
+from geonode.security.utils import get_user_visible_groups
+
+logger = logging.getLogger(__name__)
+
+
+class GroupFacetProvider(FacetProvider):
+ """
+ Implements faceting for resource's group
+ """
+
+ @property
+ def name(self) -> str:
+ return "group"
+
+ def get_info(self, lang="en", **kwargs) -> dict:
+ return {
+ "name": self.name,
+ "filter": "filter{group.in}",
+ "label": "Group",
+ "type": FACET_TYPE_GROUP,
+ }
+
+ def get_facet_items(
+ self,
+ queryset,
+ start: int = 0,
+ end: int = DEFAULT_FACET_PAGE_SIZE,
+ lang="en",
+ topic_contains: str = None,
+ keys: set = {},
+ **kwargs,
+ ) -> (int, list):
+ logger.debug("Retrieving facets for %s", self.name)
+
+ filters = dict()
+ if keys:
+ logger.debug("Filtering by keys %r", keys)
+ filters["group__id__in"] = keys
+
+ if topic_contains:
+ filters["group__name__icontains"] = topic_contains
+
+ visible_groups = get_user_visible_groups(user=kwargs["user"])
+
+ q = (
+ queryset.values("group__name", "group__id")
+ .annotate(count=Count("group__id"))
+ .filter(**filters)
+ .filter(group__id__in=[group.group_id for group in visible_groups])
+ .order_by("-count")
+ )
+
+ logger.debug(" PREFILTERED QUERY ---> %s\n\n", queryset.query)
+ logger.debug(" ADDITIONAL FILTERS ---> %s\n\n", filters)
+ logger.debug(" FINAL QUERY ---> %s\n\n", q.query)
+
+ cnt = q.count()
+ logger.debug(f" q.count() {q.count()}")
+ logger.info("Found %d facets for %s", cnt, self.name)
+ logger.debug(" ---> %s\n\n", q.query)
+ logger.debug(" ---> %r\n\n", q.all())
+
+ topics = [
+ {
+ "key": r["group__id"],
+ "label": r["group__name"],
+ "count": r["count"],
+ }
+ for r in q[start:end].all()
+ ]
+
+ return cnt, topics
+
+ def get_topics(self, keys: list, lang="en", **kwargs) -> list:
+ q = GroupProfile.objects.filter(group__id__in=keys)
+
+ logger.debug(" ---> %s\n\n", q.query)
+ logger.debug(" ---> %r\n\n", q.all())
+
+ return [{"key": r.group_id, "label": r.slug, "count": len(list(r.resources()))} for r in q.all()]
+
+ @classmethod
+ def register(cls, registry, **kwargs) -> None:
+ registry.register_facet_provider(GroupFacetProvider(**kwargs))
diff --git a/geonode/facets/tests.py b/geonode/facets/tests.py
index c227b152e35..5cd6cba1b57 100644
--- a/geonode/facets/tests.py
+++ b/geonode/facets/tests.py
@@ -36,15 +36,17 @@
Region,
TopicCategory,
HierarchicalKeyword,
+ GroupProfile,
)
from geonode.facets.models import facet_registry
from geonode.facets.providers.baseinfo import FeaturedFacetProvider
from geonode.facets.providers.category import CategoryFacetProvider
+from geonode.facets.providers.group import GroupFacetProvider
from geonode.facets.providers.keyword import KeywordFacetProvider
from geonode.facets.providers.region import RegionFacetProvider
from geonode.facets.views import ListFacetsView, GetFacetView
from geonode.tests.base import GeoNodeBaseTestSupport
-
+from django.contrib.auth.models import Group
logger = logging.getLogger(__name__)
@@ -61,6 +63,7 @@ def setUpClass(cls):
cls._create_regions()
cls._create_categories()
cls._create_keywords()
+ cls._create_groups()
cls._create_resources()
cls.rf = RequestFactory()
@@ -123,6 +126,17 @@ def _create_categories(cls):
):
cls.cats[code] = TopicCategory.objects.create(identifier=code, description=name, gn_description=name)
+ @classmethod
+ def _create_groups(cls):
+ cls.group_admin = Group.objects.create(name="UserAdmin")
+ cls.group_common = Group.objects.create(name="UserCommon")
+ cls.group_profile_admin = GroupProfile.objects.create(
+ group_id=cls.group_admin, title="UserAdmin", slug="UserAdmin"
+ )
+ cls.group_profile_common = GroupProfile.objects.create(
+ group_id=cls.group_common, title="UserCommon", slug="UserCommon"
+ )
+
@classmethod
def _create_keywords(cls):
cls.kw = {}
@@ -138,7 +152,6 @@ def _create_keywords(cls):
@classmethod
def _create_resources(self):
public_perm_spec = {"users": {"AnonymousUser": ["view_resourcebase"]}, "groups": []}
-
for x in range(20):
d: ResourceBase = ResourceBase.objects.create(
title=f"dataset_{x:02}",
@@ -152,16 +165,16 @@ def _create_resources(self):
# These are the assigned keywords to the Resources
- # RB00 -> T1K0 R0,R1 FEAT K0 C0
- # RB01 -> T0K0 T1K0 R0 FEAT K1
- # RB02 -> T1K0 R1 FEAT K2 C0
- # RB03 -> T0K0 T1K0 K0
- # RB04 -> T1K0 K0K1 C0
- # RB05 -> T0K0 T1K0 K0 K2 C1
- # RB06 -> T1K0 FEAT
- # RB07 -> T0K0 T1K0 R2 FEAT K3 C3
- # RB08 -> T1K0 T1K1 R1,R2 FEAT K3 C3
- # RB09 -> T0K0 T1K0 T1K1 R2 K3 C3
+ # RB00 -> T1K0 R0,R1 FEAT K0 C0 group_admin
+ # RB01 -> T0K0 T1K0 R0 FEAT K1 group_admin
+ # RB02 -> T1K0 R1 FEAT K2 C0 group_admin
+ # RB03 -> T0K0 T1K0 K0 group_admin
+ # RB04 -> T1K0 K0K1 C0 group_admin
+ # RB05 -> T0K0 T1K0 K0 K2 C1 group_admin
+ # RB06 -> T1K0 FEAT group_admin
+ # RB07 -> T0K0 T1K0 R2 FEAT K3 C3 group_common
+ # RB08 -> T1K0 T1K1 R1,R2 FEAT K3 C3 group_common
+ # RB09 -> T0K0 T1K0 T1K1 R2 K3 C3 group_common
# RB10 -> T1K1 R2 K3 C3
# RB11 -> T0K0 T0K1 T1K1
# RB12 -> T1K1 FEAT
@@ -173,6 +186,14 @@ def _create_resources(self):
# RB18 -> FEAT C2
# RB19 -> T0K0 T0K1 FEAT C2
+ if x < 7:
+ logger.debug(f"ASSIGNING GROUP {self.group_admin.name} TO RB {d}")
+ d.group = self.group_admin
+
+ if 7 <= x < 10:
+ logger.debug(f"ASSIGNING GROUP {self.group_common.name} TO RB {d}")
+ d.group = self.group_common
+
if x % 2 == 1:
logger.debug(f"ADDING KEYWORDS {self.thesauri_k['0_0']} to RB {d}")
d.tkeywords.add(self.thesauri_k["0_0"])
@@ -227,9 +248,9 @@ def test_facets_base(self):
obj = json.loads(res.content)
self.assertIn("facets", obj)
facets_list = obj["facets"]
- self.assertEqual(8, len(facets_list))
+ self.assertEqual(9, len(facets_list))
fmap = self._facets_to_map(facets_list)
- for name in ("category", "owner", "t_0", "t_1", "featured", "resourcetype", "keyword"):
+ for name in ("group", "category", "owner", "t_0", "t_1", "featured", "resourcetype", "keyword"):
self.assertIn(name, fmap)
def test_facets_rich(self):
@@ -247,7 +268,7 @@ def test_facets_rich(self):
obj = json.loads(res.content)
facets_list = obj["facets"]
- self.assertEqual(8, len(facets_list))
+ self.assertEqual(9, len(facets_list))
fmap = self._facets_to_map(facets_list)
for expected in ( # fmt: skip
{
@@ -496,6 +517,12 @@ def test_count0(self):
kwflt = kwinfo["filter"]
kwname = kwinfo["name"]
+ groupinfo = GroupFacetProvider().get_info()
+ grflt = groupinfo["filter"]
+ grname = groupinfo["name"]
+ g_admin_id = self.group_admin.id
+ g_comm_id = self.group_common.id
+
t0flt = facet_registry.get_provider("t_0").get_info()["filter"]
t1flt = facet_registry.get_provider("t_1").get_info()["filter"]
@@ -525,6 +552,13 @@ def t(tk):
(regname, {t1flt: [t("1_1"), t("1_0")]}, {"R0": 2, "R1": 3, "R2": 4}),
(regname, {t1flt: t("1_1"), "key": ["R0", "R1"]}, {"R1": 1, "R0": None}),
(regname, {t1flt: t("1_1"), "key": ["R0"]}, {"R0": None}),
+ # groups
+ (grname, {grflt: [g_admin_id, g_comm_id], "key": [g_admin_id, g_comm_id]}, {g_admin_id: 7, g_comm_id: 3}),
+ (grname, {grflt: [g_admin_id], "key": [g_admin_id]}, {g_admin_id: 7}),
+ (grname, {catflt: ["C0"], grflt: [g_comm_id]}, {}),
+ (grname, {catflt: ["C1"], grflt: [g_admin_id]}, {g_admin_id: 1}),
+ (grname, {catflt: ["C0"], grflt: [g_admin_id]}, {g_admin_id: 3}),
+ (grname, {catflt: ["C0", "C1"], grflt: [g_admin_id, g_comm_id]}, {g_admin_id: 4}),
# category
(catname, {t1flt: t("1_0")}, {"C0": 3, "C1": 1, "C3": 3}),
(catname, {t1flt: t("1_0"), "key": ["C0", "C2"]}, {"C0": 3, "C2": None}),
@@ -564,4 +598,99 @@ def test_thesauri_reloading(self):
# Thesauri facets are cached.
# Make sure that when Thesauri or ThesauriLabel change the facets cache is invalidated
# TODO impl+test
+
pass
+
+ def test_group_facet_api_call(self):
+ resource_count_admin = 7
+ resource_count_common = 3
+
+ expected_response_base = {
+ "name": "group",
+ "filter": "filter{group.in}",
+ "label": "Group",
+ "type": "group",
+ "topics": {
+ "page": 0,
+ "page_size": 10,
+ "start": 0,
+ "total": 2,
+ "items": [
+ {
+ "key": self.group_profile_admin.group_id,
+ "label": self.group_profile_admin.slug,
+ "count": resource_count_admin,
+ },
+ {
+ "key": self.group_profile_common.group_id,
+ "label": self.group_profile_common.slug,
+ "count": resource_count_common,
+ },
+ ],
+ },
+ }
+ expected_response_filtered = {
+ "name": "group",
+ "filter": "filter{group.in}",
+ "label": "Group",
+ "type": "group",
+ "topics": {
+ "page": 0,
+ "page_size": 10,
+ "start": 0,
+ "total": 1,
+ "items": [
+ {
+ "key": self.group_profile_admin.group_id,
+ "label": self.group_profile_admin.slug,
+ "count": resource_count_admin,
+ }
+ ],
+ },
+ }
+
+ url_filtered = f"{reverse('get_facet',args=['group'])}?filter{{group.in}}={self.group_admin.id}&include_topics=true&key={self.group_admin.id}"
+ url_base = f"{reverse('get_facet',args=['group'])}"
+
+ response_filtered = self.client.get(url_filtered)
+ response_dict_filtered = response_filtered.json()
+
+ response_base = self.client.get(url_base)
+ response_dict_base = response_base.json()
+
+ self.assertEqual(
+ response_filtered.status_code,
+ 200,
+ "Unexpected status code, got %s expected 200" % (response_filtered.status_code),
+ )
+ self.assertEqual(
+ response_base.status_code, 200, "Unexpected status code, got %s expected 200" % (response_base.status_code)
+ )
+
+ self.assertDictEqual(expected_response_filtered, response_dict_filtered)
+ self.assertDictEqual(expected_response_base, response_dict_base)
+
+ def test_group_facets_are_filtered_by_words(self):
+ # there are some groups and the facets return them
+ url = f"{reverse('get_facet',args=['group'])}"
+
+ response = self.client.get(url)
+ self.assertEqual(200, response.status_code, response.json())
+
+ self.assertTrue(response.json().get("topics", {}).get("total", 0) > 0)
+
+ # topic_contains with real name should return 1
+ url = f"{reverse('get_facet',args=['group'])}?topic_contains=UserAdmin"
+ response = self.client.get(url)
+
+ self.assertEqual(200, response.status_code, response.json())
+
+ self.assertEqual(1, response.json().get("topics", {}).get("total", 0))
+
+ # topic_contains with a random string to be searched for should be 0
+ url = f"{reverse('get_facet',args=['group'])}?topic_contains=abc123scfuqbrwefbasascgiu"
+ response = self.client.get(url)
+
+ self.assertEqual(200, response.status_code, response.json())
+
+ self.assertEqual(0, response.json().get("topics", {}).get("total", 0))
diff --git a/geonode/facets/views.py b/geonode/facets/views.py
index 5bd186b7045..d4cabc696ef 100644
--- a/geonode/facets/views.py
+++ b/geonode/facets/views.py
@@ -62,7 +62,7 @@ def _get_topics(
end = start + page_size
cnt, items = provider.get_facet_items(
- queryset, start=start, end=end, lang=lang, topic_contains=topic_contains, keys=keys
+ queryset, start=start, end=end, lang=lang, topic_contains=topic_contains, keys=keys, **kwargs
)
if keys:
@@ -86,7 +86,7 @@ def _prefilter_topics(cls, request):
"""
logger.debug("Filtering by user '%s'", request.user)
filters = {k: vlist for k, vlist in request.query_params.lists() if k.startswith("filter{")}
- logger.warning(f"FILTERING BY {filters}")
+ logger.warning(f"FILTERING BY {filters}")
if filters:
viewset = ResourceBaseViewSet(request=request, format_kwarg={}, kwargs=filters)
@@ -153,7 +153,7 @@ def get(self, request, *args, **kwargs):
if include_topics:
prefiltered = prefiltered or self._prefilter_topics(request)
- info["topics"] = self._get_topics(provider, queryset=prefiltered, lang=lang)
+ info["topics"] = self._get_topics(provider, queryset=prefiltered, lang=lang, user=request.user)
facets.append(info)
@@ -187,7 +187,14 @@ def get(self, request, facet):
qs = self._prefilter_topics(request)
topics = self._get_topics(
- provider, queryset=qs, page=page, page_size=page_size, lang=lang, topic_contains=topic_contains, keys=keys
+ provider,
+ queryset=qs,
+ page=page,
+ page_size=page_size,
+ lang=lang,
+ topic_contains=topic_contains,
+ keys=keys,
+ user=request.user,
)
if add_link:
diff --git a/geonode/favorite/__init__.py b/geonode/favorite/__init__.py
index 83005e67c7f..79177e00bdd 100644
--- a/geonode/favorite/__init__.py
+++ b/geonode/favorite/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.favorite.apps.GeoNodeFavoriteAppConfig"
diff --git a/geonode/geoapps/__init__.py b/geonode/geoapps/__init__.py
index 35ab8aa62a1..e69de29bb2d 100644
--- a/geonode/geoapps/__init__.py
+++ b/geonode/geoapps/__init__.py
@@ -1,61 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2020 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-from django.utils.translation import ugettext_noop as _
-from geonode.notifications_helper import NotificationsAppConfigBase
-
-
-class GeoNodeAppsConfig(NotificationsAppConfigBase):
- name = "geonode.geoapps"
- type = "GEONODE_APP"
-
- NOTIFICATIONS = (
- (
- "geoapp_created",
- _("App Created"),
- _("A App was created"),
- ),
- (
- "geoapp_updated",
- _("App Updated"),
- _("A App was updated"),
- ),
- (
- "geoapp_approved",
- _("App Approved"),
- _("A App was approved by a Manager"),
- ),
- (
- "geoapp_published",
- _("App Published"),
- _("A App was published"),
- ),
- (
- "geoapp_deleted",
- _("App Deleted"),
- _("A App was deleted"),
- ),
- (
- "geoapp_rated",
- _("Rating for App"),
- _("A rating was given to an App"),
- ),
- )
-
-
-default_app_config = "geonode.geoapps.GeoNodeAppsConfig"
diff --git a/geonode/geoapps/api/serializers.py b/geonode/geoapps/api/serializers.py
index c9fde60158d..c89440ccddc 100644
--- a/geonode/geoapps/api/serializers.py
+++ b/geonode/geoapps/api/serializers.py
@@ -37,7 +37,7 @@ class Meta:
model = GeoApp
name = "geoapp"
view_name = "geoapps-list"
- fields = ("pk", "uuid", "data", "name", "executions", "metadata")
+ fields = list(set(ResourceBaseSerializer.Meta.fields + ("name",)))
def extra_update_checks(self, validated_data):
_user_profiles = {}
diff --git a/geonode/geoapps/api/tests.py b/geonode/geoapps/api/tests.py
index 4d38680e743..fc409095972 100644
--- a/geonode/geoapps/api/tests.py
+++ b/geonode/geoapps/api/tests.py
@@ -76,6 +76,39 @@ def test_geoapps_list(self):
json.loads(response.data["geoapps"][0]["data"]), {"test_data": {"test": ["test_1", "test_2", "test_3"]}}
)
+ def test_geoapp_listing_advertised(self):
+ app = GeoApp.objects.first()
+ app.advertised = False
+ app.save()
+
+ url = reverse("geoapps-list")
+
+ payload = self.client.get(url)
+
+ prev_count = payload.json().get("total")
+ # the user can see only the advertised resources
+ self.assertEqual(GeoApp.objects.filter(advertised=True).count(), prev_count)
+
+ payload = self.client.get(f"{url}?advertised=True")
+ # so if advertised is True, we dont see the advertised=False resource
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count)
+
+ payload = self.client.get(f"{url}?advertised=False")
+ # so if advertised is False, we see only the resource with advertised==False
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, 1)
+
+ # if all is requested, we will see all the resources
+ payload = self.client.get(f"{url}?advertised=all")
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count + 1)
+
+ GeoApp.objects.update(advertised=True)
+
def test_extra_metadata_included_with_param(self):
_app = GeoApp.objects.first()
url = urljoin(f"{reverse('geoapps-list')}/", f"{_app.pk}")
diff --git a/geonode/geoapps/api/views.py b/geonode/geoapps/api/views.py
index 272bbaebdcf..57136b9e771 100644
--- a/geonode/geoapps/api/views.py
+++ b/geonode/geoapps/api/views.py
@@ -24,8 +24,10 @@
from oauth2_provider.contrib.rest_framework import OAuth2Authentication
from geonode.base.api.filters import DynamicSearchFilter, ExtentFilter
+from geonode.base.api.mixins import AdvertisedListMixin
from geonode.base.api.pagination import GeoNodeApiPagination
from geonode.base.api.permissions import UserHasPerms
+from geonode.base.api.views import ApiPresetsInitializer
from geonode.geoapps.models import GeoApp
from .serializers import GeoAppSerializer
@@ -36,7 +38,7 @@
logger = logging.getLogger(__name__)
-class GeoAppViewSet(DynamicModelViewSet):
+class GeoAppViewSet(ApiPresetsInitializer, DynamicModelViewSet, AdvertisedListMixin):
"""
API endpoint that allows geoapps to be viewed or edited.
"""
diff --git a/geonode/geoapps/apps.py b/geonode/geoapps/apps.py
new file mode 100644
index 00000000000..3985e342070
--- /dev/null
+++ b/geonode/geoapps/apps.py
@@ -0,0 +1,59 @@
+#########################################################################
+#
+# Copyright (C) 2020 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+from django.apps import AppConfig
+from django.utils.translation import gettext_noop as _
+from geonode.notifications_helper import NotificationsAppConfigBase
+
+
+class GeoNodeAppsConfig(NotificationsAppConfigBase, AppConfig):
+ name = "geonode.geoapps"
+ type = "GEONODE_APP"
+
+ NOTIFICATIONS = (
+ (
+ "geoapp_created",
+ _("App Created"),
+ _("A App was created"),
+ ),
+ (
+ "geoapp_updated",
+ _("App Updated"),
+ _("A App was updated"),
+ ),
+ (
+ "geoapp_approved",
+ _("App Approved"),
+ _("A App was approved by a Manager"),
+ ),
+ (
+ "geoapp_published",
+ _("App Published"),
+ _("A App was published"),
+ ),
+ (
+ "geoapp_deleted",
+ _("App Deleted"),
+ _("A App was deleted"),
+ ),
+ (
+ "geoapp_rated",
+ _("Rating for App"),
+ _("A rating was given to an App"),
+ ),
+ )
diff --git a/geonode/geoapps/models.py b/geonode/geoapps/models.py
index a3edfff60e1..30e6ef7eb24 100644
--- a/geonode/geoapps/models.py
+++ b/geonode/geoapps/models.py
@@ -20,7 +20,7 @@
from django.db import models
from django.urls import reverse
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from guardian.shortcuts import get_anonymous_user
diff --git a/geonode/geoapps/templates/apps/app_metadata_advanced.html b/geonode/geoapps/templates/apps/app_metadata_advanced.html
index ee0440f4a53..b6c0d7b4abe 100644
--- a/geonode/geoapps/templates/apps/app_metadata_advanced.html
+++ b/geonode/geoapps/templates/apps/app_metadata_advanced.html
@@ -17,13 +17,10 @@
-
+
-
-
-
{% endif %}
+
+ {{ geoapp_form.advertised.label }}
+ {{ geoapp_form.advertised }}
+
diff --git a/geonode/geoapps/urls.py b/geonode/geoapps/urls.py
index 59ac3221c8b..16f4c7eb0e1 100644
--- a/geonode/geoapps/urls.py
+++ b/geonode/geoapps/urls.py
@@ -16,7 +16,7 @@
# along with this program. If not, see .
#
#########################################################################
-from django.conf.urls import url, include
+from django.urls import include, re_path
from . import views
@@ -26,10 +26,12 @@
urlpatterns = [
# 'geonode.geoapps.views',
- url(r"^new$", views.new_geoapp, name="new_geoapp"),
- url(r"^(?P\d+)/metadata$", views.geoapp_metadata, name="geoapp_metadata"),
- url(r"^(?P[^/]*)/metadata_detail$", views.geoapp_metadata_detail, name="geoapp_metadata_detail"),
- url(r"^(?P\d+)/metadata_advanced$", views.geoapp_metadata_advanced, name="geoapp_metadata_advanced"),
- url(r"^(?P[^/]+)/embed$", views.geoapp_edit, {"template": "apps/app_embed.html"}, name="geoapp_embed"),
- url(r"^", include("geonode.geoapps.api.urls")),
+ re_path(r"^new$", views.new_geoapp, name="new_geoapp"),
+ re_path(r"^(?P\d+)/metadata$", views.geoapp_metadata, name="geoapp_metadata"),
+ re_path(r"^(?P[^/]*)/metadata_detail$", views.geoapp_metadata_detail, name="geoapp_metadata_detail"),
+ re_path(r"^(?P\d+)/metadata_advanced$", views.geoapp_metadata_advanced, name="geoapp_metadata_advanced"),
+ re_path(
+ r"^(?P[^/]+)/embed$", views.geoapp_edit, {"template": "apps/app_embed.html"}, name="geoapp_embed"
+ ),
+ re_path(r"^", include("geonode.geoapps.api.urls")),
]
diff --git a/geonode/geoapps/views.py b/geonode/geoapps/views.py
index 6cb6814bb25..32107506c3c 100644
--- a/geonode/geoapps/views.py
+++ b/geonode/geoapps/views.py
@@ -24,7 +24,7 @@
from django.conf import settings
from django.shortcuts import render
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
diff --git a/geonode/geoserver/__init__.py b/geonode/geoserver/__init__.py
index 4cea4f6d605..b900f157023 100644
--- a/geonode/geoserver/__init__.py
+++ b/geonode/geoserver/__init__.py
@@ -17,69 +17,4 @@
#
#########################################################################
-import logging
-from django.conf import settings
-from django.utils.translation import ugettext_noop as _
-from geonode.notifications_helper import NotificationsAppConfigBase
-
-
-logger = logging.getLogger(__name__)
-
-
-def run_setup_hooks(*args, **kwargs):
- from django.db.models import signals
- from geonode.layers.models import Dataset
- from geonode.maps.models import MapLayer
- from geonode.geoserver.signals import geoserver_pre_delete
- from geonode.geoserver.signals import geoserver_pre_save_maplayer
-
- signals.pre_delete.connect(geoserver_pre_delete, sender=Dataset)
- signals.pre_save.connect(geoserver_pre_save_maplayer, sender=MapLayer)
-
-
-def set_resource_links(*args, **kwargs):
- from geonode.utils import set_resource_default_links
- from geonode.catalogue.models import catalogue_post_save
- from geonode.layers.models import Dataset
-
- if settings.UPDATE_RESOURCE_LINKS_AT_MIGRATE:
- _all_datasets = Dataset.objects.all()
- for index, layer in enumerate(_all_datasets, start=1):
- _lyr_name = layer.name
- message = f"[{index} / {len(_all_datasets)}] Updating Dataset [{_lyr_name}] ..."
- logger.debug(message)
- try:
- set_resource_default_links(layer, layer)
- catalogue_post_save(instance=layer, sender=layer.__class__)
- except Exception:
- logger.exception(f"[ERROR] Dataset [{_lyr_name}] couldn't be updated")
-
-
-class GeoserverAppConfig(NotificationsAppConfigBase):
- name = "geonode.geoserver"
- NOTIFICATIONS = (
- (
- "dataset_uploaded",
- _("Dataset Uploaded"),
- _("A layer was uploaded"),
- ),
- (
- "dataset_rated",
- _("Rating for Dataset"),
- _("A rating was given to a layer"),
- ),
- )
-
- def ready(self):
- super().ready()
- run_setup_hooks()
- # Connect the post_migrate signal with the _set_resource_links
- # method to update links for each resource
- from django.db.models import signals
-
- signals.post_migrate.connect(set_resource_links, sender=self)
-
-
-default_app_config = "geonode.geoserver.GeoserverAppConfig"
-
BACKEND_PACKAGE = "geonode.geoserver"
diff --git a/geonode/geoserver/apps.py b/geonode/geoserver/apps.py
new file mode 100644
index 00000000000..9ddf9e7234c
--- /dev/null
+++ b/geonode/geoserver/apps.py
@@ -0,0 +1,81 @@
+#########################################################################
+#
+# Copyright (C) 2016 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+import logging
+from django.conf import settings
+from django.utils.translation import gettext_noop as _
+from geonode.notifications_helper import NotificationsAppConfigBase
+from django.apps import AppConfig
+
+
+logger = logging.getLogger(__name__)
+
+
+def run_setup_hooks(*args, **kwargs):
+ from django.db.models import signals
+ from geonode.layers.models import Dataset
+ from geonode.maps.models import MapLayer
+ from geonode.geoserver.signals import geoserver_pre_delete
+ from geonode.geoserver.signals import geoserver_pre_save_maplayer
+
+ signals.pre_delete.connect(geoserver_pre_delete, sender=Dataset)
+ signals.pre_save.connect(geoserver_pre_save_maplayer, sender=MapLayer)
+
+
+def set_resource_links(*args, **kwargs):
+ from geonode.utils import set_resource_default_links
+ from geonode.catalogue.models import catalogue_post_save
+ from geonode.layers.models import Dataset
+
+ if settings.UPDATE_RESOURCE_LINKS_AT_MIGRATE:
+ _all_datasets = Dataset.objects.all()
+ for index, layer in enumerate(_all_datasets, start=1):
+ _lyr_name = layer.name
+ message = f"[{index} / {len(_all_datasets)}] Updating Dataset [{_lyr_name}] ..."
+ logger.debug(message)
+ try:
+ set_resource_default_links(layer, layer)
+ catalogue_post_save(instance=layer, sender=layer.__class__)
+ except Exception:
+ logger.exception(f"[ERROR] Dataset [{_lyr_name}] couldn't be updated")
+
+
+class GeoserverAppConfig(NotificationsAppConfigBase, AppConfig):
+ name = "geonode.geoserver"
+ NOTIFICATIONS = (
+ (
+ "dataset_uploaded",
+ _("Dataset Uploaded"),
+ _("A layer was uploaded"),
+ ),
+ (
+ "dataset_rated",
+ _("Rating for Dataset"),
+ _("A rating was given to a layer"),
+ ),
+ )
+
+ def ready(self):
+ super().ready()
+ run_setup_hooks()
+ # Connect the post_migrate signal with the _set_resource_links
+ # method to update links for each resource
+ from django.db.models import signals
+
+ signals.post_migrate.connect(set_resource_links, sender=self)
diff --git a/geonode/geoserver/createlayer/__init__.py b/geonode/geoserver/createlayer/__init__.py
index a304ef09a5b..3d54b02dfef 100644
--- a/geonode/geoserver/createlayer/__init__.py
+++ b/geonode/geoserver/createlayer/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.geoserver.createlayer.apps.GeoNodeGeoserverCreatelayerAppConfig"
diff --git a/geonode/geoserver/createlayer/forms.py b/geonode/geoserver/createlayer/forms.py
index 3c25832f1f5..671ab669ab5 100644
--- a/geonode/geoserver/createlayer/forms.py
+++ b/geonode/geoserver/createlayer/forms.py
@@ -18,7 +18,7 @@
#########################################################################
from django import forms
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
GEOMETRY_TYPES = (
("Point", _("Points")),
diff --git a/geonode/geoserver/createlayer/urls.py b/geonode/geoserver/createlayer/urls.py
index 273136dba5a..5521de8d847 100644
--- a/geonode/geoserver/createlayer/urls.py
+++ b/geonode/geoserver/createlayer/urls.py
@@ -17,7 +17,7 @@
#
#########################################################################
-from django.conf.urls import url
+from django.urls import re_path
from . import views
-urlpatterns = [url(r"$", views.dataset_create, name="dataset_create")]
+urlpatterns = [re_path(r"$", views.dataset_create, name="dataset_create")]
diff --git a/geonode/geoserver/geofence.py b/geonode/geoserver/geofence.py
index 9e7717569bd..8198a2fa82a 100644
--- a/geonode/geoserver/geofence.py
+++ b/geonode/geoserver/geofence.py
@@ -441,6 +441,10 @@ def delete_layer_rules(self, workspace_name: str, layer_name: str) -> bool:
"""Delete all Rules related to a specific Layer"""
try:
batch = self.collect_delete_layer_rules(workspace_name, layer_name)
+ if not batch:
+ logger.error(f"Error removing rules for {workspace_name}:{layer_name}")
+ return False
+
logger.debug(f"Going to remove {batch.length()} rules for layer {workspace_name}:{layer_name}")
return self.geofence.run_batch(batch)
diff --git a/geonode/geoserver/helpers.py b/geonode/geoserver/helpers.py
index 8067f272adb..56d5696ae76 100755
--- a/geonode/geoserver/helpers.py
+++ b/geonode/geoserver/helpers.py
@@ -36,7 +36,6 @@
from collections import defaultdict
from os.path import basename, splitext, isfile
from urllib.parse import urlparse, urlencode, urlsplit, urljoin
-from pinax.ratings.models import OverallRating
from bs4 import BeautifulSoup
import xml.etree.ElementTree as ET
@@ -45,10 +44,9 @@
from django.db import transaction
from django.contrib.auth import get_user_model
from django.utils.module_loading import import_string
-from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.template.loader import render_to_string
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from geoserver.catalog import Catalog, FailedRequestError
from geoserver.resource import FeatureType, Coverage
@@ -869,8 +867,6 @@ def gs_slurp(
)
try:
# delete ratings, and taggit tags:
- ct = ContentType.objects.get_for_model(layer)
- OverallRating.objects.filter(content_type=ct, object_id=layer.id).delete()
layer.keywords.clear()
layer.delete()
@@ -1127,10 +1123,12 @@ def get_dataset(layer, gs_catalog: Catalog):
def clean_styles(layer, gs_catalog: Catalog):
try:
# Cleanup Styles without a Workspace
+ style = None
gs_catalog.reset()
gs_dataset = get_dataset(layer, gs_catalog)
- logger.debug(f'clean_styles: Retrieving style "{gs_dataset.default_style.name}" for cleanup')
- style = gs_catalog.get_style(name=gs_dataset.default_style.name, workspace=None, recursive=True)
+ if gs_dataset is not None:
+ logger.debug(f'clean_styles: Retrieving style "{gs_dataset.default_style.name}" for cleanup')
+ style = gs_catalog.get_style(name=gs_dataset.default_style.name, workspace=None, recursive=True)
if style:
gs_catalog.delete(style, purge=True, recurse=False)
logger.debug(f"clean_styles: Style removed: {gs_dataset.default_style.name}")
@@ -2209,17 +2207,6 @@ def get_dataset_storetype(element):
return LAYER_SUBTYPES.get(element, element)
-def write_uploaded_files_to_disk(target_dir, files):
- result = []
- for django_file in files:
- path = os.path.join(target_dir, django_file.name)
- with open(path, "wb") as fh:
- for chunk in django_file.chunks():
- fh.write(chunk)
- result = path
- return result
-
-
def select_relevant_files(allowed_extensions, files):
"""Filter the input files list for relevant files only
diff --git a/geonode/geoserver/ows.py b/geonode/geoserver/ows.py
index cf4735ab259..fc0e3b3db13 100644
--- a/geonode/geoserver/ows.py
+++ b/geonode/geoserver/ows.py
@@ -23,7 +23,7 @@
from django.conf import settings
from django.urls import reverse
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from lxml import etree
from urllib.parse import urlencode, urljoin
diff --git a/geonode/geoserver/processing/__init__.py b/geonode/geoserver/processing/__init__.py
index 333409c58a5..6b4db6084e8 100644
--- a/geonode/geoserver/processing/__init__.py
+++ b/geonode/geoserver/processing/__init__.py
@@ -16,4 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-default_app_config = "geonode.geoserver.processing.apps.GeoNodeGeoserverProcessingAppConfig"
diff --git a/geonode/geoserver/signals.py b/geonode/geoserver/signals.py
index e8b36dcde0f..0f3a84768bc 100644
--- a/geonode/geoserver/signals.py
+++ b/geonode/geoserver/signals.py
@@ -39,9 +39,9 @@
logger = logging.getLogger("geonode.geoserver.signals")
-geoserver_automatic_default_style_set = Signal(providing_args=["instance"])
+geoserver_automatic_default_style_set = Signal()
-geofence_rule_assign = Signal(providing_args=["instance"])
+geofence_rule_assign = Signal()
def geoserver_delete(typename):
diff --git a/geonode/geoserver/tasks.py b/geonode/geoserver/tasks.py
index da508e3872e..6cd6a4a31a6 100644
--- a/geonode/geoserver/tasks.py
+++ b/geonode/geoserver/tasks.py
@@ -20,7 +20,6 @@
import os
from django.conf import settings
-from django.core.management import call_command
from celery import shared_task
from celery.utils.log import get_task_logger
@@ -239,10 +238,6 @@ def geoserver_post_save_datasets(self, instance_id, *args, **kwargs):
log_lock.debug(f"geoserver_post_save_datasets: Acquired lock {lock_id} for {instance_id}")
try:
sync_instance_with_geoserver(instance_id, *args, **kwargs)
-
- # Updating HAYSTACK Indexes if needed
- if settings.HAYSTACK_SEARCH:
- call_command("update_index")
finally:
lock.release()
log_lock.debug(f"geoserver_post_save_datasets: Releasing lock {lock_id} for {instance_id}")
diff --git a/geonode/geoserver/tests/test_helpers.py b/geonode/geoserver/tests/test_helpers.py
index cf841bdf457..2fb1b3c6754 100644
--- a/geonode/geoserver/tests/test_helpers.py
+++ b/geonode/geoserver/tests/test_helpers.py
@@ -17,18 +17,15 @@
#
#########################################################################
import re
-import os
import time
import logging
from urllib.parse import urljoin
-from pathvalidate import ValidationError
from django.conf import settings
from django.urls import reverse
-from geonode import geoserver, GeoNodeException
-from geonode.utils import safe_path_leaf
+from geonode import geoserver
from geonode.decorators import on_ogc_backend
from geonode.base.populate_test_data import all_public, create_models, remove_models
from geonode.layers.populate_datasets_data import create_dataset_data
@@ -83,35 +80,6 @@ def test_extract_name_from_sld(self):
"""
self.assertIsNone(extract_name_from_sld(gs_catalog, content))
- @on_ogc_backend(geoserver.BACKEND_PACKAGE)
- def test_safe_path_leaf(self):
- base_path = settings.MEDIA_ROOT
-
- malformed_paths = ["c:/etc/passwd", "c:\\etc\\passwd", "\0_a*b:ce%f/(g)h+i_0.txt"]
- for _path in malformed_paths:
- with self.assertRaises(ValidationError):
- safe_path_leaf(_path)
-
- unsafe_paths = [
- "/root/",
- "~/.ssh",
- "$HOME/.ssh",
- "/etc/passwd",
- ".../style.sld",
- 'fi:l*e/p"a?t>h|.t
diff --git a/geonode/geoserver/tests/test_server.py b/geonode/geoserver/tests/test_server.py
index 42d7ec06bb9..f43798cafd8 100644
--- a/geonode/geoserver/tests/test_server.py
+++ b/geonode/geoserver/tests/test_server.py
@@ -594,24 +594,6 @@ def setUp(self):
self.DATABASE_DEFAULT_SETTINGS = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": "development.db"}}
- @on_ogc_backend(geoserver.BACKEND_PACKAGE)
- def test_style_manager(self):
- """
- Ensures the dataset_style_manage route returns a 200.
- """
- layer = Dataset.objects.first()
-
- bob = get_user_model().objects.get(username="bobby")
- assign_perm("change_dataset_style", bob, layer)
-
- self.assertTrue(self.client.login(username="bobby", password="bob"))
- response = self.client.get(reverse("dataset_style_manage", args=(layer.alternate,)))
- self.assertEqual(response.status_code, 200)
-
- form_data = {"default_style": "polygon"}
- response = self.client.post(reverse("dataset_style_manage", args=(layer.alternate,)), data=form_data)
- self.assertEqual(response.status_code, 302)
-
@on_ogc_backend(geoserver.BACKEND_PACKAGE)
def test_style_validity_and_name(self):
# Check that including an SLD with a valid shapefile results in the SLD
@@ -905,7 +887,7 @@ def test_resolve_user(self):
"HTTP_AUTHORIZATION": f"basic {base64.b64encode(invalid_uname_pw).decode()}",
}
- response = self.client.get(reverse("dataset_resolve_user"), **valid_auth_headers)
+ response = self.client.get(reverse("dataset_resolve_user_dep"), **valid_auth_headers)
content = response.content
if isinstance(content, bytes):
content = content.decode("UTF-8")
@@ -924,7 +906,7 @@ def test_resolve_user(self):
self.client.login(username="admin", password="admin")
# Basic check that the returned content is at least valid json
- response = self.client.get(reverse("dataset_resolve_user"))
+ response = self.client.get(reverse("dataset_resolve_user_dep"))
content = response.content
if isinstance(content, bytes):
content = content.decode("UTF-8")
diff --git a/geonode/geoserver/urls.py b/geonode/geoserver/urls.py
index 963854b881c..b35f17647aa 100644
--- a/geonode/geoserver/urls.py
+++ b/geonode/geoserver/urls.py
@@ -18,75 +18,71 @@
#########################################################################
from django.conf import settings
-from django.conf.urls import url
+from django.urls import re_path
from . import views
urlpatterns = [ # 'geonode.geoserver.views',
# REST Endpoints
- url(r"^rest/stores/(?P\w+)/$", views.stores, name="gs_stores"),
- url(
+ re_path(r"^rest/stores/(?P\w+)/$", views.stores, name="gs_stores"),
+ re_path(
r"^rest/styles",
views.geoserver_proxy,
dict(proxy_path="/gs/rest/styles", downstream_path="rest/styles"),
name="gs_styles",
),
- url(
+ re_path(
r"^rest/workspaces/(?P\w+)",
views.geoserver_proxy,
dict(proxy_path="/gs/rest/workspaces", downstream_path="rest/workspaces"),
name="gs_workspaces",
),
- url(
+ re_path(
r"^rest/layers",
views.geoserver_proxy,
dict(proxy_path="/gs/rest/layers", downstream_path="rest/layers"),
name="gs_layers",
),
- url(
+ re_path(
r"^rest/imports",
views.geoserver_proxy,
dict(proxy_path="/gs/rest/imports", downstream_path="rest/imports"),
name="gs_imports",
),
- url(
+ re_path(
r"^rest/sldservice",
views.geoserver_proxy,
dict(proxy_path="/gs/rest/sldservice", downstream_path="rest/sldservice"),
name="gs_sldservice",
),
# OWS Endpoints
- url(r"^ows", views.geoserver_proxy, dict(proxy_path="/gs/ows", downstream_path="ows"), name="ows_endpoint"),
- url(r"^gwc", views.geoserver_proxy, dict(proxy_path="/gs/gwc", downstream_path="gwc"), name="gwc_endpoint"),
- url(r"^wms", views.geoserver_proxy, dict(proxy_path="/gs/wms", downstream_path="wms"), name="wms_endpoint"),
- url(r"^wfs", views.geoserver_proxy, dict(proxy_path="/gs/wfs", downstream_path="wfs"), name="wfs_endpoint"),
- url(r"^wcs", views.geoserver_proxy, dict(proxy_path="/gs/wcs", downstream_path="wcs"), name="wcs_endpoint"),
- url(r"^wps", views.geoserver_proxy, dict(proxy_path="/gs/wps", downstream_path="wps"), name="wps_endpoint"),
- url(r"^pdf", views.geoserver_proxy, dict(proxy_path="/gs/pdf", downstream_path="pdf"), name="pdf_endpoint"),
- url(
+ re_path(r"^ows", views.geoserver_proxy, dict(proxy_path="/gs/ows", downstream_path="ows"), name="ows_endpoint"),
+ re_path(r"^gwc", views.geoserver_proxy, dict(proxy_path="/gs/gwc", downstream_path="gwc"), name="gwc_endpoint"),
+ re_path(r"^wms", views.geoserver_proxy, dict(proxy_path="/gs/wms", downstream_path="wms"), name="wms_endpoint"),
+ re_path(r"^wfs", views.geoserver_proxy, dict(proxy_path="/gs/wfs", downstream_path="wfs"), name="wfs_endpoint"),
+ re_path(r"^wcs", views.geoserver_proxy, dict(proxy_path="/gs/wcs", downstream_path="wcs"), name="wcs_endpoint"),
+ re_path(r"^wps", views.geoserver_proxy, dict(proxy_path="/gs/wps", downstream_path="wps"), name="wps_endpoint"),
+ re_path(r"^pdf", views.geoserver_proxy, dict(proxy_path="/gs/pdf", downstream_path="pdf"), name="pdf_endpoint"),
+ re_path(
r"^(?P[^/]*)/(?P[^/]*)/ows",
views.geoserver_proxy,
dict(proxy_path=f"/gs/{settings.DEFAULT_WORKSPACE}", downstream_path="ows"),
),
- url(
+ re_path(
r"^(?P[^/]*)/(?P[^/]*)/wms",
views.geoserver_proxy,
dict(proxy_path=f"/gs/{settings.DEFAULT_WORKSPACE}", downstream_path="wms"),
),
- url(
+ re_path(
r"^(?P[^/]*)/(?P[^/]*)/wfs",
views.geoserver_proxy,
dict(proxy_path=f"/gs/{settings.DEFAULT_WORKSPACE}", downstream_path="wfs"),
),
- url(
+ re_path(
r"^(?P[^/]*)/(?P[^/]*)/wcs",
views.geoserver_proxy,
dict(proxy_path=f"/gs/{settings.DEFAULT_WORKSPACE}", downstream_path="wcs"),
),
- url(r"^updatelayers/$", views.updatelayers, name="updatelayers"),
- url(r"^(?P[^/]*)/style$", views.dataset_style, name="dataset_style"),
- url(r"^(?P[^/]*)/style/upload$", views.dataset_style_upload, name="dataset_style_upload"),
- url(r"^(?P[^/]*)/style/manage$", views.dataset_style_manage, name="dataset_style_manage"),
- url(r"^acls/?$", views.dataset_acls, name="dataset_acls"),
- url(r"^resolve_user/?$", views.resolve_user, name="dataset_resolve_user"),
- url(r"^online/?$", views.server_online, name="server_online"),
+ re_path(r"^updatelayers/$", views.updatelayers, name="updatelayers"),
+ re_path(r"^acls/?$", views.dataset_acls, name="dataset_acls"),
+ re_path(r"^online/?$", views.server_online, name="server_online"),
]
diff --git a/geonode/geoserver/views.py b/geonode/geoserver/views.py
index 0bbdd104eab..0a601a98c35 100644
--- a/geonode/geoserver/views.py
+++ b/geonode/geoserver/views.py
@@ -22,24 +22,20 @@
import json
import logging
import warnings
-import traceback
from lxml import etree
from owslib.etree import etree as dlxml
-from os.path import isfile
from urllib.parse import urlsplit, urljoin, unquote, parse_qsl
from django.contrib.auth import authenticate
from django.http import HttpResponse, HttpResponseRedirect
from django.views.decorators.http import require_POST
-from django.shortcuts import render
from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.template.loader import get_template
-from django.utils.datastructures import MultiValueDictKeyError
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from guardian.shortcuts import get_objects_for_user
@@ -48,29 +44,21 @@
from geonode.compat import ensure_string
from geonode.base.auth import get_auth_user, get_or_create_token
from geonode.decorators import logged_in_or_basicauth
-from geonode.layers.forms import LayerStyleUploadForm
from geonode.layers.models import Dataset, Style
from geonode.layers.views import _resolve_dataset, _PERMISSION_MSG_MODIFY
from geonode.maps.models import Map
from geonode.proxy.views import proxy, fetch_response_headers
from .tasks import geoserver_update_datasets
from geonode.utils import (
- json_response,
_get_basic_auth_info,
http_client,
- safe_path_leaf,
get_headers,
- get_dataset_workspace,
)
-from geoserver.catalog import FailedRequestError
-from geonode.geoserver.signals import gs_catalog, geoserver_post_save_local
+from geonode.geoserver.signals import geoserver_post_save_local
from .helpers import (
get_stores,
ogc_server_settings,
- extract_name_from_sld,
- set_styles,
style_update,
- set_dataset_style,
ows_endpoint_in_path,
temp_style_name_regex,
get_dataset_capabilities_url,
@@ -144,165 +132,6 @@ def dataset_style(request, layername):
return HttpResponse(f"Default style for {layer.name} changed to {style_name}", status=200)
-@login_required
-def dataset_style_upload(request, layername):
- def respond(*args, **kw):
- kw["content_type"] = "text/html"
- return json_response(*args, **kw)
-
- form = LayerStyleUploadForm(request.POST, request.FILES)
- if not form.is_valid():
- return respond(errors="Please provide an SLD file.")
-
- data = form.cleaned_data
- layer = _resolve_dataset(request, layername, "base.change_resourcebase", _PERMISSION_MSG_MODIFY)
-
- sld = request.FILES["sld"].read()
- sld_name = None
- try:
- # Check SLD is valid
- try:
- _allowed_sld_extensions = [".sld", ".xml", ".css", ".txt", ".yml"]
- if sld and os.path.splitext(safe_path_leaf(sld))[1].lower() in _allowed_sld_extensions:
- if isfile(sld):
- with open(sld) as sld_file:
- sld = sld_file.read()
- etree.XML(sld, parser=etree.XMLParser(resolve_entities=False))
- except Exception:
- logger.exception("The uploaded SLD file is not valid XML")
- raise Exception("The uploaded SLD file is not valid XML")
-
- sld_name = extract_name_from_sld(gs_catalog, sld, sld_file=request.FILES["sld"])
- except Exception as e:
- respond(errors=f"The uploaded SLD file is not valid XML: {e}")
-
- name = data.get("name") or sld_name
-
- set_dataset_style(layer, data.get("title") or name, sld)
-
- return respond(body={"success": True, "style": data.get("title") or name, "updated": data["update"]})
-
-
-@login_required
-def dataset_style_manage(request, layername):
- layer = _resolve_dataset(request, layername, "layers.change_dataset_style", _PERMISSION_MSG_MODIFY)
-
- if request.method == "GET":
- try:
- cat = gs_catalog
-
- # First update the layer style info from GS to GeoNode's DB
- try:
- set_styles(layer, cat)
- except AttributeError:
- logger.warn("Unable to set the default style. Ensure Geoserver is running and that this layer exists.")
-
- gs_styles = []
- # Temporary Hack to remove GeoServer temp styles from the list
- Style.objects.filter(name__iregex=r"\w{8}-\w{4}-\w{4}-\w{4}-\w{12}_(ms)_\d{13}").delete()
- for style in Style.objects.values("name", "sld_title"):
- gs_styles.append((style["name"], style["sld_title"]))
- current_dataset_styles = layer.styles.all()
- dataset_styles = []
- for style in current_dataset_styles:
- sld_title = style.name
- try:
- if style.sld_title:
- sld_title = style.sld_title
- except Exception:
- tb = traceback.format_exc()
- logger.debug(tb)
- dataset_styles.append((style.name, sld_title))
-
- # Render the form
- def_sld_name = None # noqa
- def_sld_title = None # noqa
- default_style = None
- if layer.default_style:
- def_sld_name = layer.default_style.name # noqa
- def_sld_title = layer.default_style.name # noqa
- try:
- if layer.default_style.sld_title:
- def_sld_title = layer.default_style.sld_title
- except Exception:
- tb = traceback.format_exc()
- logger.debug(tb)
- default_style = (def_sld_name, def_sld_title)
-
- return render(
- request,
- "datasets/dataset_style_manage.html",
- context={
- "layer": layer,
- "gs_styles": gs_styles,
- "dataset_styles": dataset_styles,
- "dataset_style_names": [s[0] for s in dataset_styles],
- "default_style": default_style,
- },
- )
- except (FailedRequestError, OSError):
- tb = traceback.format_exc()
- logger.debug(tb)
- msg = (
- f'Could not connect to geoserver at "{ogc_server_settings.LOCATION}"'
- f'to manage style information for layer "{layer.name}"'
- )
- logger.debug(msg)
- # If geoserver is not online, return an error
- return render(request, "datasets/dataset_style_manage.html", context={"layer": layer, "error": msg})
- elif request.method in ("POST", "PUT", "DELETE"):
- try:
- workspace = get_dataset_workspace(layer) or settings.DEFAULT_WORKSPACE
- selected_styles = request.POST.getlist("style-select")
- default_style = request.POST["default_style"]
-
- # Save to GeoServer
- cat = gs_catalog
- try:
- gs_dataset = cat.get_layer(layer.name)
- except Exception:
- gs_dataset = None
-
- if not gs_dataset:
- gs_dataset = cat.get_layer(layer.alternate)
-
- if gs_dataset:
- _default_style = cat.get_style(default_style) or cat.get_style(default_style, workspace=workspace)
- if _default_style:
- gs_dataset.default_style = _default_style
- elif cat.get_style(default_style, workspace=settings.DEFAULT_WORKSPACE):
- gs_dataset.default_style = cat.get_style(default_style, workspace=settings.DEFAULT_WORKSPACE)
- styles = []
- for style in selected_styles:
- _gs_sld = cat.get_style(style) or cat.get_style(style, workspace=workspace)
- if _gs_sld:
- styles.append(_gs_sld)
- elif cat.get_style(style, workspace=settings.DEFAULT_WORKSPACE):
- styles.append(cat.get_style(style, workspace=settings.DEFAULT_WORKSPACE))
- else:
- Style.objects.filter(name=style).delete()
- gs_dataset.styles = styles
- cat.save(gs_dataset)
-
- # Save to Django
- set_styles(layer, cat)
-
- # Invalidate GeoWebCache for the updated resource
- try:
- _stylefilterparams_geowebcache_dataset(layer.alternate)
- _invalidate_geowebcache_dataset(layer.alternate)
- except Exception:
- pass
-
- return HttpResponseRedirect(layer.get_absolute_url())
- except (FailedRequestError, OSError, MultiValueDictKeyError):
- tb = traceback.format_exc()
- logger.debug(tb)
- msg = f'Error Saving Styles for Dataset "{layer.name}"'
- logger.warn(msg)
- return render(request, "datasets/dataset_style_manage.html", context={"layer": layer, "error": msg})
-
-
def style_change_check(request, path, style_name=None, access_token=None):
"""
If the layer has not change_dataset_style permission, return a status of
diff --git a/geonode/groups/__init__.py b/geonode/groups/__init__.py
index 725f83fbfbd..f033279f275 100644
--- a/geonode/groups/__init__.py
+++ b/geonode/groups/__init__.py
@@ -19,5 +19,3 @@
import logging
logger = logging.getLogger(__name__)
-
-default_app_config = "geonode.groups.apps.GroupsAppConfig"
diff --git a/geonode/groups/forms.py b/geonode/groups/forms.py
index 42a116435c8..f63281e1249 100644
--- a/geonode/groups/forms.py
+++ b/geonode/groups/forms.py
@@ -19,7 +19,7 @@
from django import forms
from slugify import slugify
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from modeltranslation.forms import TranslationModelForm
from django.contrib.auth import get_user_model
diff --git a/geonode/groups/models.py b/geonode/groups/models.py
index 59ae6f61392..419700f16b0 100644
--- a/geonode/groups/models.py
+++ b/geonode/groups/models.py
@@ -33,7 +33,7 @@
from django.templatetags.static import static
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from geonode.utils import build_absolute_uri
from geonode.thumbs.utils import MISSING_THUMB
diff --git a/geonode/groups/search_indexes.py b/geonode/groups/search_indexes.py
deleted file mode 100644
index a6191f822de..00000000000
--- a/geonode/groups/search_indexes.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2016 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-
-import json
-
-from django.conf import settings
-
-from haystack import indexes
-
-from geonode.groups.models import GroupProfile
-
-
-class GroupIndex(indexes.SearchIndex, indexes.Indexable):
- text = indexes.CharField(document=True, use_template=True)
- title = indexes.CharField(boost=2)
- # https://github.com/toastdriven/django-haystack/issues/569 - Necessary for sorting
- title_sortable = indexes.CharField(indexed=False)
- description = indexes.CharField(model_attr="description", boost=1.5)
- id = indexes.IntegerField(model_attr="id")
- type = indexes.CharField(faceted=True)
- json = indexes.CharField(indexed=False)
-
- def get_model(self):
- return GroupProfile
-
- def prepare_title(self, obj):
- return str(obj)
-
- def prepare_title_sortable(self, obj):
- return str(obj).lower()
-
- def prepare_type(self, obj):
- return "group"
-
- def prepare_json(self, obj):
- data = {
- "_type": self.prepare_type(obj),
- "title": obj.title,
- "description": obj.description,
- "keywords": [keyword.name for keyword in obj.keywords.all()] if obj.keywords else [],
- "thumb": f"{settings.STATIC_URL}static/img/contact.png",
- "detail": None,
- }
-
- return json.dumps(data)
diff --git a/geonode/groups/urls.py b/geonode/groups/urls.py
index 230a05935ab..7f485ad199d 100644
--- a/geonode/groups/urls.py
+++ b/geonode/groups/urls.py
@@ -17,42 +17,44 @@
#
#
-from django.conf.urls import url
+from django.urls import re_path
from django.views.generic import TemplateView
from .views import GroupDetailView, GroupActivityView, SetGroupDatasetPermission
from . import views
urlpatterns = [ # 'geonode.groups.views',
- url(r"^$", TemplateView.as_view(template_name="groups/group_list.html"), name="group_list"),
- url(r"^categories/$", TemplateView.as_view(template_name="groups/category_list.html"), name="group_category_list"),
- url(r"^categories/_create/$", views.group_category_create, name="group_category_create"),
- url(r"^categories/(?P[-\w]+)/$", views.group_category_detail, name="group_category_detail"),
- url(r"^categories/(?P[-\w]+)/update/$", views.group_category_update, name="group_category_update"),
- url(r"^create/$", views.group_create, name="group_create"),
- url(r"^group/(?P[-\w]+)/$", GroupDetailView.as_view(), name="group_detail"),
- url(r"^group/(?P[-\w]+)/update/$", views.group_update, name="group_update"),
- url(r"^group/(?P[-\w]+)/members/$", views.group_members, name="group_members"),
- url(r"^group/(?P[-\w]+)/members_add/$", views.group_members_add, name="group_members_add"),
- url(
+ re_path(r"^$", TemplateView.as_view(template_name="groups/group_list.html"), name="group_list"),
+ re_path(
+ r"^categories/$", TemplateView.as_view(template_name="groups/category_list.html"), name="group_category_list"
+ ),
+ re_path(r"^categories/_create/$", views.group_category_create, name="group_category_create"),
+ re_path(r"^categories/(?P[-\w]+)/$", views.group_category_detail, name="group_category_detail"),
+ re_path(r"^categories/(?P[-\w]+)/update/$", views.group_category_update, name="group_category_update"),
+ re_path(r"^create/$", views.group_create, name="group_create"),
+ re_path(r"^group/(?P[-\w]+)/$", GroupDetailView.as_view(), name="group_detail"),
+ re_path(r"^group/(?P[-\w]+)/update/$", views.group_update, name="group_update"),
+ re_path(r"^group/(?P[-\w]+)/members/$", views.group_members, name="group_members"),
+ re_path(r"^group/(?P[-\w]+)/members_add/$", views.group_members_add, name="group_members_add"),
+ re_path(
r"^group/(?P[-\w]+)/member_remove/(?P.+)$",
views.group_member_remove,
name="group_member_remove",
),
- url(
+ re_path(
r"^group/(?P[-\w]+)/member_promote/(?P.+)$",
views.group_member_promote,
name="group_member_promote",
),
- url(
+ re_path(
r"^group/(?P[-\w]+)/member_demote/(?P.+)$",
views.group_member_demote,
name="group_member_demote",
),
- url(r"^group/(?P[-\w]+)/remove/$", views.group_remove, name="group_remove"),
- url(r"^group/(?P[-\w]+)/join/$", views.group_join, name="group_join"),
- url(r"^group/(?P[-\w]+)/activity/$", GroupActivityView.as_view(), name="group_activity"),
- url(r"^autocomplete/$", views.GroupProfileAutocomplete.as_view(), name="autocomplete_groups"),
- url(r"^autocomplete_category/$", views.GroupCategoryAutocomplete.as_view(), name="autocomplete_category"),
- url(r"^dataset/permission/$", SetGroupDatasetPermission.as_view(), name="set_group_dataset_permissions"),
+ re_path(r"^group/(?P[-\w]+)/remove/$", views.group_remove, name="group_remove"),
+ re_path(r"^group/(?P[-\w]+)/join/$", views.group_join, name="group_join"),
+ re_path(r"^group/(?P[-\w]+)/activity/$", GroupActivityView.as_view(), name="group_activity"),
+ re_path(r"^autocomplete/$", views.GroupProfileAutocomplete.as_view(), name="autocomplete_groups"),
+ re_path(r"^autocomplete_category/$", views.GroupCategoryAutocomplete.as_view(), name="autocomplete_category"),
+ re_path(r"^dataset/permission/$", SetGroupDatasetPermission.as_view(), name="set_group_dataset_permissions"),
]
diff --git a/geonode/harvesting/__init__.py b/geonode/harvesting/__init__.py
index ca2d43f4135..6b4db6084e8 100644
--- a/geonode/harvesting/__init__.py
+++ b/geonode/harvesting/__init__.py
@@ -16,5 +16,3 @@
# along with this program. If not, see .
#
#########################################################################
-
-default_app_config = "geonode.harvesting.apps.HarvestingAppConfig"
diff --git a/geonode/harvesting/apps.py b/geonode/harvesting/apps.py
index 3c02b92f44c..b766993993d 100644
--- a/geonode/harvesting/apps.py
+++ b/geonode/harvesting/apps.py
@@ -19,7 +19,7 @@
from django.apps import AppConfig
from django.conf import settings
-from django.conf.urls import url, include
+from django.urls import include, re_path
from . import config
@@ -30,7 +30,7 @@ class HarvestingAppConfig(AppConfig):
def ready(self):
from geonode.urls import urlpatterns
- urlpatterns += [url(r"^api/v2/", include("geonode.harvesting.api.urls"))]
+ urlpatterns += [re_path(r"^api/v2/", include("geonode.harvesting.api.urls"))]
settings.CELERY_BEAT_SCHEDULE["harvesting-scheduler"] = {
"task": "geonode.harvesting.tasks.harvesting_scheduler",
"schedule": config.get_setting("HARVESTER_SCHEDULER_FREQUENCY_MINUTES") * 60,
diff --git a/geonode/harvesting/harvesters/geonodeharvester.py b/geonode/harvesting/harvesters/geonodeharvester.py
index 0f9de873476..dad1571b920 100644
--- a/geonode/harvesting/harvesters/geonodeharvester.py
+++ b/geonode/harvesting/harvesters/geonodeharvester.py
@@ -433,14 +433,14 @@ def _get_resource_list_params(self, offset: typing.Optional[int] = 0) -> typing.
result["filter{title.icontains}"] = self.resource_title_filter
if self.start_date_filter is not None:
start_date = dateutil.parser.parse(self.start_date_filter)
- result[
- "filter{date.gte}"
- ] = f"{start_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ result["filter{date.gte}"] = (
+ f"{start_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ )
if self.end_date_filter is not None:
end_date = dateutil.parser.parse(self.end_date_filter)
- result[
- "filter{date.lte}"
- ] = f"{end_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ result["filter{date.lte}"] = (
+ f"{end_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ )
if self.keywords_filter is not None:
result["filter{keywords.slug.in}"] = self.keywords_filter
if self.categories_filter is not None:
@@ -738,14 +738,14 @@ def _get_resource_list_params(self, offset: typing.Optional[int] = 0) -> typing.
result["title__icontains"] = self.resource_title_filter
if self.start_date_filter is not None:
start_date = dateutil.parser.parse(self.start_date_filter)
- result[
- "date__gte"
- ] = f"{start_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ result["date__gte"] = (
+ f"{start_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ )
if self.end_date_filter is not None:
end_date = dateutil.parser.parse(self.end_date_filter)
- result[
- "date__lte"
- ] = f"{end_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ result["date__lte"] = (
+ f"{end_date.astimezone(dt.timezone.utc).replace(microsecond=0).isoformat().split('+')[0]}Z"
+ )
if self.keywords_filter is not None:
result["keywords__slug__in"] = ",".join(self.keywords_filter)
if self.categories_filter is not None:
diff --git a/geonode/invitations/forms.py b/geonode/invitations/forms.py
index c39df8fe54b..16f748c894f 100644
--- a/geonode/invitations/forms.py
+++ b/geonode/invitations/forms.py
@@ -18,7 +18,7 @@
#########################################################################
from django import forms
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model
from invitations.adapters import get_invitations_adapter
diff --git a/geonode/invitations/urls.py b/geonode/invitations/urls.py
index 8140b6ef62d..91c4bdd0551 100644
--- a/geonode/invitations/urls.py
+++ b/geonode/invitations/urls.py
@@ -17,14 +17,14 @@
#
#########################################################################
-from django.conf.urls import url
+from django.urls import re_path
import invitations
from . import views
app_name = "geonode.invitations"
urlpatterns = [
- url(r"^geonode-send-invite/$", views.GeoNodeSendInvite.as_view(), name="geonode-send-invite"),
- url(r"^send-json-invite/$", invitations.views.SendJSONInvite.as_view(), name="send-json-invite"),
- url(r"^accept-invite/(?P\w+)/?$", invitations.views.AcceptInvite.as_view(), name="accept-invite"),
+ re_path(r"^geonode-send-invite/$", views.GeoNodeSendInvite.as_view(), name="geonode-send-invite"),
+ re_path(r"^send-json-invite/$", invitations.views.SendJSONInvite.as_view(), name="send-json-invite"),
+ re_path(r"^accept-invite/(?P\w+)/?$", invitations.views.AcceptInvite.as_view(), name="accept-invite"),
]
diff --git a/geonode/invitations/views.py b/geonode/invitations/views.py
index e630007afb1..9fe43af5af4 100644
--- a/geonode/invitations/views.py
+++ b/geonode/invitations/views.py
@@ -25,7 +25,7 @@
except ImportError:
from django.urls import reverse
from django.utils import timezone
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.contrib.auth.decorators import login_required
from .forms import GeoNodeInviteForm
diff --git a/geonode/layers/__init__.py b/geonode/layers/__init__.py
index df083900be1..e69de29bb2d 100644
--- a/geonode/layers/__init__.py
+++ b/geonode/layers/__init__.py
@@ -1,62 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2016 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-
-from django.utils.translation import ugettext_noop as _
-from geonode.notifications_helper import NotificationsAppConfigBase
-
-
-class DatasetAppConfig(NotificationsAppConfigBase):
- name = "geonode.layers"
- verbose_name = "Dataset"
- verbose_name_plural = "Datasets"
- NOTIFICATIONS = (
- (
- "dataset_created",
- _("Dataset Created"),
- _("A Dataset was created"),
- ),
- (
- "dataset_updated",
- _("Dataset Updated"),
- _("A Dataset was updated"),
- ),
- (
- "dataset_approved",
- _("Dataset Approved"),
- _("A Dataset was approved by a Manager"),
- ),
- (
- "dataset_published",
- _("Dataset Published"),
- _("A Dataset was published"),
- ),
- (
- "dataset_deleted",
- _("Dataset Deleted"),
- _("A Dataset was deleted"),
- ),
- (
- "dataset_rated",
- _("Rating for Dataset"),
- _("A rating was given to a layer"),
- ),
- )
-
-
-default_app_config = "geonode.layers.DatasetAppConfig"
diff --git a/geonode/layers/api/permissions.py b/geonode/layers/api/permissions.py
index 9408469c6fe..8ace5857afe 100644
--- a/geonode/layers/api/permissions.py
+++ b/geonode/layers/api/permissions.py
@@ -16,6 +16,7 @@
# along with this program. If not, see .
#
#########################################################################
+from distutils.util import strtobool
from django.conf import settings
from rest_framework.filters import BaseFilterBackend
@@ -47,6 +48,10 @@ def filter_queryset(self, request, queryset, view):
resources = get_objects_for_user(user, "base.view_resourcebase", **self.shortcut_kwargs).filter(
polymorphic_ctype__model="dataset"
)
+ try:
+ include_dirty = strtobool(request.query_params.get("include_dirty", "False"))
+ except Exception:
+ include_dirty = False
obj_with_perms = get_visible_resources(
resources,
@@ -54,6 +59,7 @@ def filter_queryset(self, request, queryset, view):
admin_approval_required=settings.ADMIN_MODERATE_UPLOADS,
unpublished_not_visible=settings.RESOURCE_PUBLISHING,
private_groups_not_visibile=settings.GROUP_PRIVATE_RESOURCES,
+ include_dirty=include_dirty,
)
return queryset.filter(id__in=obj_with_perms.values("id"))
diff --git a/geonode/layers/api/serializers.py b/geonode/layers/api/serializers.py
index af80ff5bd78..f2020f73f65 100644
--- a/geonode/layers/api/serializers.py
+++ b/geonode/layers/api/serializers.py
@@ -150,39 +150,6 @@ def get_attribute(self, instance):
class DatasetSerializer(ResourceBaseSerializer):
- def __init__(self, *args, **kwargs):
- # Instantiate the superclass normally
- super().__init__(*args, **kwargs)
-
- class Meta:
- model = Dataset
- name = "dataset"
- view_name = "datasets-list"
- fields = (
- "pk",
- "uuid",
- "name",
- "metadata",
- "attribute_set",
- "charset",
- "is_mosaic",
- "has_time",
- "has_elevation",
- "time_regex",
- "elevation_regex",
- "featureinfo_custom_template",
- "ows_url",
- "capabilities_url",
- "dataset_ows_url",
- "workspace",
- "default_style",
- "styles",
- "store",
- "subtype",
- "ptype",
- "executions",
- )
-
name = serializers.CharField(read_only=True)
workspace = serializers.CharField(read_only=True)
store = serializers.CharField(read_only=True)
@@ -192,9 +159,40 @@ class Meta:
styles = DynamicRelationField(StyleSerializer, embed=True, many=True, read_only=True)
attribute_set = DynamicRelationField(AttributeSerializer, embed=True, many=True, read_only=True)
-
featureinfo_custom_template = FeatureInfoTemplateField()
+ class Meta:
+ model = Dataset
+ name = "dataset"
+ view_name = "datasets-list"
+ fields = list(
+ set(
+ ResourceBaseSerializer.Meta.fields
+ + (
+ "uuid",
+ "name",
+ "metadata",
+ "attribute_set",
+ "charset",
+ "is_mosaic",
+ "has_time",
+ "has_elevation",
+ "time_regex",
+ "elevation_regex",
+ "featureinfo_custom_template",
+ "ows_url",
+ "capabilities_url",
+ "dataset_ows_url",
+ "workspace",
+ "default_style",
+ "styles",
+ "store",
+ "subtype",
+ "ptype",
+ )
+ )
+ )
+
def update(self, instance, validated_data):
super().update(instance, validated_data)
@@ -226,42 +224,45 @@ def update(self, instance, validated_data):
return instance
+ def update(self, instance, validated_data):
+ super().update(instance, validated_data)
-class DatasetListSerializer(DatasetSerializer):
- class Meta(DatasetSerializer.Meta):
- fields = (
- "pk",
- "uuid",
- "name",
- "workspace",
- "store",
- "subtype",
- "charset",
- "is_mosaic",
- "has_time",
- "has_elevation",
- "time_regex",
- "elevation_regex",
- "featureinfo_custom_template",
- "ptype",
- "default_style",
- "styles",
- )
+ # Handle updates to attribute_set
+ allowed_fields = ["pk", "description", "attribute_label", "visible", "display_order"]
+ if "blob" in validated_data and "attribute_set" in validated_data["blob"]:
+ attributes = validated_data["blob"]["attribute_set"]
- featureinfo_custom_template = FeatureInfoTemplateField()
+ for attribute in attributes:
+ for field, _ in attribute.items():
+ if field not in allowed_fields:
+ raise ValidationError(
+ f"{field} is not one of the fields that could be edited directly. \
+ Only {str(allowed_fields)} are allowed"
+ )
+ for attribute in attributes:
+ try:
+ if "pk" in attribute:
+ attribute_instance = Attribute.objects.get(pk=attribute["pk"], dataset=instance)
+ for field, value in attribute.items():
+ setattr(attribute_instance, field, value)
+ attribute_instance.save()
+ else:
+ raise Exception("Primary key of the attribute to be patched not specified")
+ except Exception as e:
+ logger.error(e)
+ raise ParseError(str(e))
-class DatasetReplaceAppendSerializer(serializers.Serializer):
- class Meta:
- fields = ("base_file", "dbf_file", "shx_file", "prj_file", "xml_file", "sld_file", "store_spatial_files")
-
- base_file = serializers.CharField()
- dbf_file = serializers.CharField(required=False)
- shx_file = serializers.CharField(required=False)
- prj_file = serializers.CharField(required=False)
- xml_file = serializers.CharField(required=False)
- sld_file = serializers.CharField(required=False)
- store_spatial_files = serializers.BooleanField(required=False, default=True)
+ return instance
+
+
+class DatasetListSerializer(DatasetSerializer):
+ class Meta(DatasetSerializer.Meta):
+ fields = [
+ f
+ for f in DatasetSerializer.Meta.fields
+ if f not in ("attribute_set", "capabilities_url", "dataset_ows_url", "ows_url")
+ ]
class MetadataFileField(DynamicComputedField):
diff --git a/geonode/layers/api/tests.py b/geonode/layers/api/tests.py
index ed72c5053a5..fc7bef7d5bb 100644
--- a/geonode/layers/api/tests.py
+++ b/geonode/layers/api/tests.py
@@ -19,16 +19,15 @@
from io import BytesIO
import logging
-from unittest.mock import patch
from django.contrib.auth import get_user_model
from urllib.parse import urljoin
from django.conf import settings
+from django.test import override_settings
from django.urls import reverse
from rest_framework.test import APITestCase
from guardian.shortcuts import assign_perm, get_anonymous_user
-from geonode.geoserver.createlayer.utils import create_dataset
from geonode.base.models import Link
from geonode.base.populate_test_data import create_models, create_single_dataset
@@ -121,6 +120,92 @@ def test_datasets(self):
_dataset.use_featureinfo_custom_template = False
_dataset.save()
+ @override_settings(REST_API_DEFAULT_PAGE_SIZE=100)
+ def test_filter_dirty_state(self):
+ """
+ ensure that a dirty_state dataset wont be returned
+ """
+
+ # ensure that there is atleast one resource with dirty_state
+ dirty_dataset = Dataset.objects.first()
+ dirty_dataset.dirty_state = True
+ dirty_dataset.save()
+
+ url = reverse("datasets-list")
+
+ response = self.client.get(url, format="json")
+ self.assertEqual(response.status_code, 200)
+ dataset_list = response.data["datasets"]
+
+ # ensure that list count is equal to that of clean data
+ # clean resources
+ resource_count_clean = Dataset.objects.filter(dirty_state=False).count()
+ self.assertEqual(len(dataset_list), resource_count_clean)
+ # ensure that the updated dirty dataset is not in the response
+ self.assertFalse(dirty_dataset.pk in [int(dataset["pk"]) for dataset in dataset_list])
+
+ @override_settings(REST_API_DEFAULT_PAGE_SIZE=100)
+ def test_filter_dirty_state_include_dirty(self):
+ """
+ ensure that all resources are returned when dirty_state is true
+ """
+ # ensure that there is atleast one resource with dirty_state
+ dirty_dataset = Dataset.objects.first()
+ dirty_dataset.dirty_state = True
+ dirty_dataset.save()
+
+ # clean resources
+ resource_count_clean = Dataset.objects.filter(dirty_state=False).count()
+ # dirty resources
+ resource_count_dirty = Dataset.objects.filter(dirty_state=True).count()
+
+ resource_count_all = resource_count_clean + resource_count_dirty
+
+ url = f'{reverse("datasets-list")}?include_dirty=true'
+
+ response = self.client.get(url, format="json")
+ self.assertEqual(response.status_code, 200)
+ dataset_list = response.data["datasets"]
+
+ # ensure that list count is equal to that of all data
+ self.assertEqual(len(dataset_list), resource_count_all)
+
+ # ensure that the updated dirty dataset is in the response
+ self.assertTrue(dirty_dataset.pk in [int(dataset["pk"]) for dataset in dataset_list])
+
+ def test_dataset_listing_advertised(self):
+ app = Dataset.objects.first()
+ app.advertised = False
+ app.save()
+
+ url = reverse("datasets-list")
+
+ payload = self.client.get(url)
+
+ prev_count = payload.json().get("total")
+ # the user can see only the advertised resources
+ self.assertEqual(Dataset.objects.filter(advertised=True).count(), prev_count)
+
+ payload = self.client.get(f"{url}?advertised=True")
+ # so if advertised is True, we dont see the advertised=False resource
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count)
+
+ payload = self.client.get(f"{url}?advertised=False")
+ # so if advertised is False, we see only the resource with advertised==False
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, 1)
+
+ # if all is requested, we will see all the resources
+ payload = self.client.get(f"{url}?advertised=all")
+ new_count = payload.json().get("total")
+ # recheck the count
+ self.assertEqual(new_count, prev_count + 1)
+
+ Dataset.objects.update(advertised=True)
+
def test_extra_metadata_included_with_param(self):
_dataset = Dataset.objects.first()
url = urljoin(f"{reverse('datasets-list')}/", f"{_dataset.pk}")
@@ -197,133 +282,6 @@ def test_raw_HTML_stripped_properties(self):
self.assertEqual(response.data["dataset"]["raw_supplemental_information"], "No information provided í £682m")
self.assertEqual(response.data["dataset"]["raw_data_quality_statement"], "OK 1 2 a b")
- def test_layer_replace_anonymous_should_raise_error(self):
- layer = Dataset.objects.first()
- url = reverse("datasets-replace-dataset", args=(layer.id,))
-
- expected = {
- "success": False,
- "errors": ["Authentication credentials were not provided."],
- "code": "not_authenticated",
- }
-
- response = self.client.patch(url)
- self.assertEqual(403, response.status_code)
- self.assertDictEqual(expected, response.json())
-
- def test_layer_replace_should_redirect_for_not_accepted_method(self):
- layer = Dataset.objects.first()
- url = reverse("datasets-replace-dataset", args=(layer.id,))
- self.client.login(username="admin", password="admin")
-
- response = self.client.post(url)
- self.assertEqual(405, response.status_code)
-
- response = self.client.get(url)
- self.assertEqual(405, response.status_code)
-
- def test_layer_replace_should_raise_error_if_layer_does_not_exists(self):
- url = reverse("datasets-replace-dataset", args=(999999999999999,))
-
- expected = {"success": False, "errors": ["Layer with ID 999999999999999 is not available"], "code": "not_found"}
-
- self.client.login(username="admin", password="admin")
-
- response = self.client.patch(url)
- self.assertEqual(404, response.status_code)
- self.assertDictEqual(expected, response.json())
-
- def test_dataset_append_anonymous_should_raise_error(self):
- layer = Dataset.objects.first()
- url = reverse("datasets-append-dataset", args=(layer.id,))
-
- expected = {
- "success": False,
- "errors": ["Authentication credentials were not provided."],
- "code": "not_authenticated",
- }
-
- response = self.client.patch(url)
- self.assertEqual(403, response.status_code)
- self.assertDictEqual(expected, response.json())
-
- def test_dataset_append_should_redirect_for_not_accepted_method(self):
- layer = Dataset.objects.first()
- url = reverse("datasets-append-dataset", args=(layer.id,))
- self.client.login(username="admin", password="admin")
-
- response = self.client.post(url)
- self.assertEqual(405, response.status_code)
-
- response = self.client.get(url)
- self.assertEqual(405, response.status_code)
-
- def test_dataset_append_should_raise_error_if_layer_does_not_exists(self):
- url = reverse("datasets-append-dataset", args=(999999999999999,))
-
- expected = {"success": False, "errors": ["Layer with ID 999999999999999 is not available"], "code": "not_found"}
-
- self.client.login(username="admin", password="admin")
-
- response = self.client.patch(url)
- self.assertEqual(404, response.status_code, response.json())
- self.assertDictEqual(expected, response.json())
-
- @patch("geonode.layers.api.views.validate_input_source")
- def test_layer_replace_should_work(self, _validate_input_source):
- _validate_input_source.return_value = True
-
- admin = get_user_model().objects.get(username="admin")
-
- if Dataset.objects.filter(name="single_point").exists():
- """
- If the dataset already exists in the test env, we dont want that the test fail
- so we rename it and then we will rollback the cnahge
- """
- _dataset = Dataset.objects.get(name="single_point")
- _dataset.name = "single_point_2"
- _dataset.save()
-
- try:
- layer = create_dataset(
- "single_point",
- "single_point",
- admin,
- "Point",
- )
- except Exception as e:
- if "There is already a layer named" not in e.args[0]:
- raise e
- else:
- layer = create_single_dataset("single_point")
-
- cnt = Dataset.objects.count()
-
- layer.refresh_from_db()
- logger.error(layer.alternate)
- # renaming the file in the same way as the lasyer name
- # the filename must be consiste with the layer name
-
- github_path = "https://github.com/GeoNode/gisdata/tree/master/gisdata/data/good/vector/"
- payload = {
- "store_spatial_files": False,
- "base_file": f"{github_path}/single_point.shp",
- "dbf_file": f"{github_path}/single_point.dbf",
- "shx_file": f"{github_path}/single_point.shx",
- "prj_file": f"{github_path}/single_point.prj",
- }
-
- url = reverse("datasets-replace-dataset", args=(layer.id,))
-
- self.client.login(username="admin", password="admin")
-
- response = self.client.patch(url, data=payload)
- self.assertEqual(200, response.status_code, response.json())
-
- layer.refresh_from_db()
- # evaluate that the number of available layer is not changed
- self.assertEqual(Dataset.objects.count(), cnt)
-
def test_patch_point_of_contact(self):
layer = Dataset.objects.first()
url = urljoin(f"{reverse('datasets-list')}/", f"{layer.id}")
diff --git a/geonode/layers/api/views.py b/geonode/layers/api/views.py
index 629367c3440..038cd43e024 100644
--- a/geonode/layers/api/views.py
+++ b/geonode/layers/api/views.py
@@ -16,8 +16,7 @@
# along with this program. If not, see .
#
#########################################################################
-from urllib.request import Request
-from drf_spectacular.utils import extend_schema, OpenApiExample
+from drf_spectacular.utils import extend_schema
from dynamic_rest.viewsets import DynamicModelViewSet
from dynamic_rest.filters import DynamicFilterBackend, DynamicSortingFilter
@@ -30,21 +29,20 @@
from rest_framework.response import Response
from geonode.base.api.filters import DynamicSearchFilter, ExtentFilter
+from geonode.base.api.mixins import AdvertisedListMixin
from geonode.base.api.pagination import GeoNodeApiPagination
from geonode.base.api.permissions import UserHasPerms
+from geonode.base.api.views import ApiPresetsInitializer
from geonode.layers.api.exceptions import GeneralDatasetException, InvalidDatasetException, InvalidMetadataException
from geonode.layers.metadata import parse_metadata
from geonode.layers.models import Dataset
-from geonode.layers.utils import validate_input_source
from geonode.maps.api.serializers import SimpleMapLayerSerializer, SimpleMapSerializer
from geonode.resource.utils import update_resource
from rest_framework.exceptions import NotFound
from geonode.storage.manager import StorageManager
-from geonode.resource.manager import resource_manager
from .serializers import (
- DatasetReplaceAppendSerializer,
DatasetSerializer,
DatasetListSerializer,
DatasetMetadataSerializer,
@@ -56,7 +54,7 @@
logger = logging.getLogger(__name__)
-class DatasetViewSet(DynamicModelViewSet):
+class DatasetViewSet(ApiPresetsInitializer, DynamicModelViewSet, AdvertisedListMixin):
"""
API endpoint that allows layers to be viewed or edited.
"""
@@ -179,137 +177,3 @@ def maps(self, request, pk=None, *args, **kwargs):
dataset = self.get_object()
resources = dataset.maps
return Response(SimpleMapSerializer(many=True).to_representation(resources))
-
- @extend_schema(
- methods=["patch"],
- responses={200},
- description="API endpoint allowing to replace a dataset.",
- examples=[
- OpenApiExample(
- "Example1",
- summary="expected payload",
- description="Example of the input payload",
- value='{\
- "base_file": "/home/mattia/gis_data/single_point/single_point.shp",\
- "dbf_file": "/home/mattia/gis_data/single_point/single_point.dbf",\
- "shx_file": "/home/mattia/gis_data/single_point/single_point.shx",\
- "prj_file": "/home/mattia/gis_data/single_point/single_point.prj",\
- "xml_file": "/home/mattia/gis_data/single_point.xml",\
- "store_spatial_files": False\
- }',
- )
- ],
- )
- @action(
- detail=False,
- url_path="(?P\d+)/replace", # noqa
- url_name="replace-dataset",
- methods=["patch"],
- serializer_class=DatasetReplaceAppendSerializer,
- )
- def replace(self, request, dataset_id=None, *args, **kwargs):
- """
- Edpoint for replace data to an existing layer
- """
- return self._replace_or_append(request, dataset_id, action="replace")
-
- @extend_schema(
- methods=["patch"],
- responses={200, 500},
- description="API endpoint allowing to append data to dataset.",
- examples=[
- OpenApiExample(
- "Example1",
- summary="expected payload",
- description="Example of the input payload",
- value='{\
- "base_file": "/home/mattia/gis_data/single_point/single_point.shp",\
- "dbf_file": "/home/mattia/gis_data/single_point/single_point.dbf",\
- "shx_file": "/home/mattia/gis_data/single_point/single_point.shx",\
- "prj_file": "/home/mattia/gis_data/single_point/single_point.prj",\
- "xml_file": "/home/mattia/gis_data/single_point.xml",\
- "store_spatial_files": False\
- }',
- )
- ],
- )
- @action(
- detail=False,
- url_path="(?P\d+)/append", # noqa
- url_name="append-dataset",
- methods=["patch"],
- serializer_class=DatasetReplaceAppendSerializer,
- )
- def append(self, request, dataset_id=None, *args, **kwargs):
- """
- Edpoint for replace data to an existing layer
- """
- return self._replace_or_append(request, dataset_id, action="append")
-
- def _replace_or_append(self, request: Request, dataset_id: int, action: str) -> Response:
- """
- Raise error if the datasets does not exists
- """
- if not self.queryset.filter(id=dataset_id).exists():
- raise NotFound(detail=f"Layer with ID {dataset_id} is not available")
-
- serializer = self.serializer_class(data=request.data)
- """
- Raise error if the serializer is invalid. Instead of the default exception
- We raise a custom one with the list of the erros from the serializer
- """
- if not serializer.is_valid(raise_exception=False):
- raise InvalidDatasetException(detail=serializer.errors)
-
- data = serializer.data.copy()
-
- dataset = self.queryset.get(id=dataset_id)
-
- """
- This validation will ensure that the new input file are fully compliant
- with the existing dataset. If not, an InvalidDatasetException is raised
- """
- store_spatial_files = data.pop("store_spatial_files")
-
- try:
- storage_manager = StorageManager(remote_files=data)
-
- storage_manager.clone_remote_files()
-
- files = storage_manager.get_retrieved_paths()
-
- validate_input_source(
- layer=dataset,
- filename=data.get("base_file"),
- files={_file.split(".")[1]: _file for _file in files.values()},
- gtype=dataset.gtype,
- action_type=action,
- storage_manager=storage_manager,
- )
-
- xml_file = files.pop("xml_file", None)
- sld_file = files.pop("sld_file", None)
-
- call_kwargs = {
- "instance": dataset,
- "vals": {"files": list(files.values()), "user": request.user},
- "store_spatial_files": store_spatial_files,
- "xml_file": xml_file,
- "metadata_uploaded": True if xml_file is not None else False,
- "sld_file": sld_file,
- "sld_uploaded": True if sld_file is not None else False,
- }
-
- getattr(resource_manager, action)(**call_kwargs)
-
- except Exception as e:
- raise GeneralDatasetException(e)
- finally:
- """
- Always keep the temporary folder under control.
- """
- if not store_spatial_files:
- storage_manager.delete_retrieved_paths()
- # For now, we will return the input dataset
- data = {"alternate": dataset.alternate, "state": "success", "action": action}
- return Response(data)
diff --git a/geonode/layers/apps.py b/geonode/layers/apps.py
new file mode 100644
index 00000000000..f7c503179a0
--- /dev/null
+++ b/geonode/layers/apps.py
@@ -0,0 +1,63 @@
+#########################################################################
+#
+# Copyright (C) 2016 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+
+from django.apps import AppConfig
+from django.utils.translation import gettext_noop as _
+from geonode.notifications_helper import NotificationsAppConfigBase
+
+
+class DatasetAppConfig(NotificationsAppConfigBase, AppConfig):
+ name = "geonode.layers"
+ verbose_name = "Dataset"
+ verbose_name_plural = "Datasets"
+ NOTIFICATIONS = (
+ (
+ "dataset_created",
+ _("Dataset Created"),
+ _("A Dataset was created"),
+ ),
+ (
+ "dataset_updated",
+ _("Dataset Updated"),
+ _("A Dataset was updated"),
+ ),
+ (
+ "dataset_approved",
+ _("Dataset Approved"),
+ _("A Dataset was approved by a Manager"),
+ ),
+ (
+ "dataset_published",
+ _("Dataset Published"),
+ _("A Dataset was published"),
+ ),
+ (
+ "dataset_deleted",
+ _("Dataset Deleted"),
+ _("A Dataset was deleted"),
+ ),
+ (
+ "dataset_rated",
+ _("Rating for Dataset"),
+ _("A rating was given to a layer"),
+ ),
+ )
+
+
+default_app_config = "geonode.layers.DatasetAppConfig"
diff --git a/geonode/layers/download_handler.py b/geonode/layers/download_handler.py
index 09eb032a53a..7f154a6f30d 100644
--- a/geonode/layers/download_handler.py
+++ b/geonode/layers/download_handler.py
@@ -23,7 +23,7 @@
from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
from django.template.loader import get_template
from django.urls import reverse
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext_lazy as _
from django.conf import settings
from geonode.base.auth import get_or_create_token
from geonode.geoserver.helpers import wps_format_is_supported
diff --git a/geonode/layers/forms.py b/geonode/layers/forms.py
index a51b8494551..38db3c7956c 100644
--- a/geonode/layers/forms.py
+++ b/geonode/layers/forms.py
@@ -16,16 +16,10 @@
# along with this program. If not, see .
#
#########################################################################
-import os
-import zipfile
from django import forms
-from geonode import geoserver
-from geonode.utils import check_ogc_backend
-
import json
-from geonode.utils import unzip_file, mkdtemp
from geonode.base.forms import ResourceBaseForm, get_tree_data
from geonode.layers.models import Dataset, Attribute
@@ -86,152 +80,6 @@ def __init__(self, *args, **kwargs):
)
-class LayerUploadForm(forms.Form):
- base_file = forms.FileField()
- dbf_file = forms.FileField(required=False)
- shx_file = forms.FileField(required=False)
- prj_file = forms.FileField(required=False)
- xml_file = forms.FileField(required=False)
- if check_ogc_backend(geoserver.BACKEND_PACKAGE):
- sld_file = forms.FileField(required=False)
-
- charset = forms.CharField(required=False)
- metadata_uploaded_preserve = forms.BooleanField(required=False)
- metadata_upload_form = forms.BooleanField(required=False)
- style_upload_form = forms.BooleanField(required=False)
-
- spatial_files = ["base_file", "dbf_file", "shx_file", "prj_file"]
-
- # Adding style file based on the backend
- if check_ogc_backend(geoserver.BACKEND_PACKAGE):
- spatial_files.append("sld_file")
-
- spatial_files = tuple(spatial_files)
-
- def clean(self):
- cleaned = super().clean()
- dbf_file = shx_file = prj_file = xml_file = sld_file = None
- base_name = base_ext = None
- if zipfile.is_zipfile(cleaned["base_file"]):
- filenames = zipfile.ZipFile(cleaned["base_file"], allowZip64=True).namelist()
- for filename in filenames:
- name, ext = os.path.splitext(filename)
- if ext.lower() == ".shp":
- if base_name is not None:
- raise forms.ValidationError("Only one shapefile per zip is allowed")
- base_name = name
- base_ext = ext
- elif ext.lower() == ".dbf":
- dbf_file = filename
- elif ext.lower() == ".shx":
- shx_file = filename
- elif ext.lower() == ".prj":
- prj_file = filename
- elif ext.lower() == ".xml":
- xml_file = filename
- elif ext.lower() == ".sld":
- sld_file = filename
- if base_name is None:
- raise forms.ValidationError("Zip files can only contain shapefile.")
- else:
- base_name, base_ext = os.path.splitext(cleaned["base_file"].name)
- if cleaned["dbf_file"] is not None:
- dbf_file = cleaned["dbf_file"].name
- if cleaned["shx_file"] is not None:
- shx_file = cleaned["shx_file"].name
- if cleaned["prj_file"] is not None:
- prj_file = cleaned["prj_file"].name
- if cleaned["xml_file"] is not None:
- xml_file = cleaned["xml_file"].name
- # SLD style only available in GeoServer backend
- if check_ogc_backend(geoserver.BACKEND_PACKAGE):
- if cleaned["sld_file"] is not None:
- sld_file = cleaned["sld_file"].name
-
- if (
- not cleaned["metadata_upload_form"]
- and not cleaned["style_upload_form"]
- and base_ext.lower()
- not in (".shp", ".tif", ".tiff", ".geotif", ".geotiff", ".asc", ".sld", ".kml", ".kmz", ".csv")
- ):
- raise forms.ValidationError(
- f"Only Shapefiles, GeoTiffs, and ASCIIs are supported. You uploaded a {base_ext} file"
- )
- elif cleaned["metadata_upload_form"] and base_ext.lower() not in (".xml"):
- raise forms.ValidationError(f"Only XML files are supported. You uploaded a {base_ext} file")
- elif cleaned["style_upload_form"] and base_ext.lower() not in (".sld"):
- raise forms.ValidationError(f"Only SLD files are supported. You uploaded a {base_ext} file")
-
- if base_ext.lower() == ".shp":
- if dbf_file is None or shx_file is None:
- raise forms.ValidationError("When uploading Shapefiles, .shx and .dbf files are also required.")
- dbf_name, __ = os.path.splitext(dbf_file)
- shx_name, __ = os.path.splitext(shx_file)
- if dbf_name != base_name or shx_name != base_name:
- raise forms.ValidationError(
- "It looks like you're uploading "
- "components from different Shapefiles. Please "
- "double-check your file selections."
- )
- if prj_file is not None:
- if os.path.splitext(prj_file)[0] != base_name:
- raise forms.ValidationError(
- "It looks like you're "
- "uploading components from different Shapefiles. "
- "Please double-check your file selections."
- )
- if xml_file is not None:
- if os.path.splitext(xml_file)[0] != base_name:
- if xml_file.find(".shp") != -1:
- # force rename of file so that file.shp.xml doesn't
- # overwrite as file.shp
- if cleaned.get("xml_file"):
- cleaned["xml_file"].name = f"{base_name}.xml"
- if sld_file is not None:
- if os.path.splitext(sld_file)[0] != base_name:
- if sld_file.find(".shp") != -1:
- # force rename of file so that file.shp.xml doesn't
- # overwrite as file.shp
- if cleaned.get("sld_file"):
- cleaned["sld_file"].name = f"{base_name}.sld"
- return cleaned
-
- def write_files(self):
- absolute_base_file = None
- tempdir = mkdtemp()
- if zipfile.is_zipfile(self.cleaned_data["base_file"]):
- absolute_base_file = unzip_file(self.cleaned_data["base_file"], ".shp", tempdir=tempdir)
- else:
- for field in self.spatial_files:
- f = self.cleaned_data[field]
- if f is not None:
- path = os.path.join(tempdir, f.name)
- with open(path, "wb") as writable:
- for c in f.chunks():
- writable.write(c)
- absolute_base_file = os.path.join(tempdir, self.cleaned_data["base_file"].name)
- return tempdir, absolute_base_file
-
-
-class NewLayerUploadForm(LayerUploadForm):
- if check_ogc_backend(geoserver.BACKEND_PACKAGE):
- sld_file = forms.FileField(required=False)
- xml_file = forms.FileField(required=False)
-
- abstract = forms.CharField(required=False)
- dataset_title = forms.CharField(required=False)
- permissions = JSONField()
- charset = forms.CharField(required=False)
- metadata_uploaded_preserve = forms.BooleanField(required=False)
-
- spatial_files = ["base_file", "dbf_file", "shx_file", "prj_file", "xml_file"]
- # Adding style file based on the backend
- if check_ogc_backend(geoserver.BACKEND_PACKAGE):
- spatial_files.append("sld_file")
-
- spatial_files = tuple(spatial_files)
-
-
class LayerDescriptionForm(forms.Form):
title = forms.CharField(max_length=300, required=True)
abstract = forms.CharField(max_length=2000, widget=forms.Textarea, required=False)
@@ -264,13 +112,6 @@ class Meta:
)
-class LayerStyleUploadForm(forms.Form):
- layerid = forms.IntegerField()
- name = forms.CharField(required=False)
- update = forms.BooleanField(required=False)
- sld = forms.FileField()
-
-
class DatasetTimeSerieForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
_choises = [(None, "-----")] + [
diff --git a/geonode/layers/migrations/0028_auto_20200323_1945.py b/geonode/layers/migrations/0028_auto_20200323_1945.py
index f47ceced37f..c15f64c121e 100755
--- a/geonode/layers/migrations/0028_auto_20200323_1945.py
+++ b/geonode/layers/migrations/0028_auto_20200323_1945.py
@@ -1,16 +1,6 @@
# Populating polygons into bbox_polygon field
from django.db import migrations
-from django.contrib.gis.geos import Polygon
-
-
-def populate_polygon(apps, schema_editor):
- Layer = apps.get_model('layers', 'Layer')
- for layer in Layer.objects.all():
- bbox = [getattr(layer, key, None) for key in ('bbox_x0', 'bbox_y0', 'bbox_x1', 'bbox_y1')]
- if all(bbox):
- layer.bbox_polygon = Polygon.from_bbox(bbox)
- layer.save()
class Migration(migrations.Migration):
@@ -20,6 +10,10 @@ class Migration(migrations.Migration):
('layers', '0027_auto_20170801_1228_squashed_0033_auto_20180606_1543'),
]
+ # This migration used to populate bbox_polygon field for all Layers.
+ # Now all resources are updated in base/0044
+ # See #12144
+
operations = [
- migrations.RunPython(populate_polygon, migrations.RunPython.noop),
+ migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop),
]
diff --git a/geonode/layers/models.py b/geonode/layers/models.py
index bed803f7220..016597dcb44 100644
--- a/geonode/layers/models.py
+++ b/geonode/layers/models.py
@@ -25,7 +25,7 @@
from django.urls import reverse
from django.utils.timezone import now
from django.utils.functional import classproperty
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import gettext_lazy as _
from tinymce.models import HTMLField
diff --git a/geonode/layers/search_indexes.py b/geonode/layers/search_indexes.py
deleted file mode 100644
index fa541beb115..00000000000
--- a/geonode/layers/search_indexes.py
+++ /dev/null
@@ -1,96 +0,0 @@
-#########################################################################
-#
-# Copyright (C) 2016 OSGeo
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-#########################################################################
-
-from pinax.ratings.models import OverallRating
-from django.contrib.contenttypes.models import ContentType
-from django.db.models import Avg
-from haystack import indexes
-from geonode.maps.models import Dataset
-
-
-class LayerIndex(indexes.SearchIndex, indexes.Indexable):
- id = indexes.IntegerField(model_attr="resourcebase_ptr_id")
- abstract = indexes.CharField(model_attr="abstract", boost=1.5)
- category__gn_description = indexes.CharField(model_attr="category__gn_description", null=True)
- csw_type = indexes.CharField(model_attr="csw_type")
- csw_wkt_geometry = indexes.CharField(model_attr="csw_wkt_geometry")
- detail_url = indexes.CharField(model_attr="get_absolute_url")
- owner__username = indexes.CharField(model_attr="owner", faceted=True, null=True)
- is_published = indexes.BooleanField(model_attr="is_published")
- featured = indexes.BooleanField(model_attr="featured")
- srid = indexes.CharField(model_attr="srid")
- supplemental_information = indexes.CharField(model_attr="supplemental_information", null=True)
- thumbnail_url = indexes.CharField(model_attr="thumbnail_url", null=True)
- uuid = indexes.CharField(model_attr="uuid")
- title = indexes.CharField(model_attr="title", boost=2)
- date = indexes.DateTimeField(model_attr="date")
-
- text = indexes.EdgeNgramField(document=True, use_template=True, stored=False)
- type = indexes.CharField(faceted=True)
- subtype = indexes.CharField(faceted=True)
- alternate = indexes.CharField(model_attr="alternate")
- title_sortable = indexes.CharField(indexed=False, stored=False) # Necessary for sorting
- category = indexes.CharField(model_attr="category__identifier", faceted=True, null=True, stored=True)
- bbox_left = indexes.FloatField(model_attr="bbox_x0", null=True, stored=False)
- bbox_right = indexes.FloatField(model_attr="bbox_x1", null=True, stored=False)
- bbox_bottom = indexes.FloatField(model_attr="bbox_y0", null=True, stored=False)
- bbox_top = indexes.FloatField(model_attr="bbox_y1", null=True, stored=False)
- temporal_extent_start = indexes.DateTimeField(model_attr="temporal_extent_start", null=True, stored=False)
- temporal_extent_end = indexes.DateTimeField(model_attr="temporal_extent_end", null=True, stored=False)
- keywords = indexes.MultiValueField(model_attr="keyword_slug_list", null=True, faceted=True, stored=True)
- regions = indexes.MultiValueField(model_attr="region_name_list", null=True, faceted=True, stored=True)
- popular_count = indexes.IntegerField(model_attr="popular_count", default=0, boost=20)
- share_count = indexes.IntegerField(model_attr="share_count", default=0)
- rating = indexes.IntegerField(null=True)
- num_ratings = indexes.IntegerField(stored=False)
-
- def get_model(self):
- return Dataset
-
- def prepare_type(self, obj):
- return "layer"
-
- def prepare_subtype(self, obj):
- if obj.subtype == "vector":
- if obj.has_time:
- return "vector_time"
- else:
- return "vector"
- elif obj.subtype == "raster":
- return "raster"
- elif obj.subtype in ["tileStore", "remote"]:
- return "remote"
-
- def prepare_rating(self, obj):
- ct = ContentType.objects.get_for_model(obj)
- try:
- rating = OverallRating.objects.filter(object_id=obj.pk, content_type=ct).aggregate(r=Avg("rating"))["r"]
- return float(str(rating or "0"))
- except OverallRating.DoesNotExist:
- return 0.0
-
- def prepare_num_ratings(self, obj):
- ct = ContentType.objects.get_for_model(obj)
- try:
- return OverallRating.objects.filter(object_id=obj.pk, content_type=ct).all().count()
- except OverallRating.DoesNotExist:
- return 0
-
- def prepare_title_sortable(self, obj):
- return obj.title.lower()
diff --git a/geonode/layers/templates/datasets/dataset_append.html b/geonode/layers/templates/datasets/dataset_append.html
deleted file mode 100644
index 85dbe1c1c54..00000000000
--- a/geonode/layers/templates/datasets/dataset_append.html
+++ /dev/null
@@ -1,106 +0,0 @@
-{% extends "upload/dataset_upload_base.html" %}
-{% load i18n %}
-{% load static %}
-{% load pinax_ratings_tags %}
-{% load bootstrap_tags %}
-{% load pagination_tags %}
-{% load base_tags %}
-{% load guardian_tags %}
-{% load client_lib_tags %}
-
-{% block title %} {% trans "Append to Dataset" %} - {{ block.super }} {% endblock %}
-
-{% block body_class %}datasets append{% endblock %}
-
-
-{% block head %}
-
-{{ block.super }}
-{% endblock %}
-
-{% block body_outer %}
-
-
-
- {% block additional_info %}{% endblock %}
-
- {% if errors %}
-
- {% for error in errors %}
-
{{ error }}
- {% endfor %}
-
- {% endif %}
-
-
-
-
- {% trans "Drop files here" %}
-
-
-
{% trans " or select them one by one:" %}
-
-
-
-
-
-
- {% trans "Select the charset or leave default" %}
-
- {% for charset in charsets %}
- {% if charset.0 == 'UTF-8' %}
- {{ charset.1 }}
- {% else %}
- {{ charset.1 }}
- {% endif %}
- {% endfor %}
-
-
-
-
-
-
- {% if GEOSERVER_BASE_URL %}
- {% get_obj_perms request.user for resource.dataset as "dataset_perms" %}
- {% endif %}
-
-
-{% endblock %}
-
-
-{% block extra_script %}
-{{ block.super }}
-
-
-{% endblock extra_script %}
\ No newline at end of file
diff --git a/geonode/layers/templates/datasets/dataset_metadata_advanced.html b/geonode/layers/templates/datasets/dataset_metadata_advanced.html
index c7169f960cb..22bc8856781 100644
--- a/geonode/layers/templates/datasets/dataset_metadata_advanced.html
+++ b/geonode/layers/templates/datasets/dataset_metadata_advanced.html
@@ -18,13 +18,10 @@
-
+
-
-
-