diff --git a/geonode/base/models.py b/geonode/base/models.py index 8bd804cadf7..632dc9d5ee5 100644 --- a/geonode/base/models.py +++ b/geonode/base/models.py @@ -40,7 +40,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.staticfiles.templatetags import staticfiles from django.core.files.storage import default_storage as storage - +from django.utils.html import strip_tags from mptt.models import MPTTModel, TreeForeignKey from imagekit.models import ImageSpecField @@ -883,14 +883,19 @@ def __init__(self, *args, **kwargs): super(ResourceBase, self).__init__(*args, **kwargs) def __str__(self): - return "{0}".format(self.title) + return str(self.title) def _remove_html_tags(self, attribute_str): + _attribute_str = attribute_str try: pattern = re.compile('<.*?>') - return re.sub(pattern, '', attribute_str) + _attribute_str = html.unescape( + re.sub(pattern, '', attribute_str).replace('\n', ' ').replace('\r', '').strip()) except Exception: - return attribute_str + if attribute_str: + _attribute_str = html.unescape( + attribute_str.replace('\n', ' ').replace('\r', '').strip()) + return strip_tags(_attribute_str) @property def raw_abstract(self): diff --git a/geonode/base/tests.py b/geonode/base/tests.py index 143382383f0..8c7a8e713d5 100644 --- a/geonode/base/tests.py +++ b/geonode/base/tests.py @@ -19,7 +19,11 @@ ######################################################################### import os -from unittest.mock import patch +from unittest.mock import patch, Mock +from urllib.parse import urlparse + +import requests +from django.core.exceptions import ObjectDoesNotExist from guardian.shortcuts import assign_perm, get_perms from imagekit.cachefiles.backends import Simple @@ -32,23 +36,41 @@ from geonode.maps.models import Map from geonode.services.models import Service from geonode.tests.base import GeoNodeBaseTestSupport +from geonode.base import thumb_utils from geonode.base.models import ( - ResourceBase, MenuPlaceholder, Menu, MenuItem, Configuration, TopicCategory + ResourceBase, + MenuPlaceholder, + Menu, + MenuItem, + Configuration, + TopicCategory, + Thesaurus, + ThesaurusKeyword ) +from django.conf import settings from django.template import Template, Context from django.contrib.auth import get_user_model +from django.core.files.storage import default_storage as storage from django.test import Client, TestCase, override_settings, SimpleTestCase from django.shortcuts import reverse from geonode.base.middleware import ReadOnlyMiddleware, MaintenanceMiddleware from geonode.base.models import CuratedThumbnail -from geonode.base.templatetags.base_tags import get_visibile_resources +from geonode.base.templatetags.base_tags import get_visibile_resources, facets +from geonode.base.templatetags.thesaurus import ( + get_name_translation, get_unique_thesaurus_set, + get_thesaurus_title, + get_thesaurus_date, +) +from geonode.base.templatetags.user_messages import show_notification from geonode import geoserver from geonode.decorators import on_ogc_backend from django.core.files import File from django.core.management import call_command from django.core.management.base import CommandError +from geonode.base.forms import ThesaurusAvailableForm + test_image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0)) @@ -59,13 +81,63 @@ def setUp(self): super(ThumbnailTests, self).setUp() self.rb = ResourceBase.objects.create() + def tearDown(self): + super().tearDown() + def test_initial_behavior(self): + """ + Tests that an empty resource has a missing image as default thumbnail. + """ self.assertFalse(self.rb.has_thumbnail()) missing = self.rb.get_thumbnail_url() self.assertTrue('missing_thumb' in os.path.splitext(missing)[0]) + def test_empty_image(self): + """ + Tests that an empty image does not change the current resource thumbnail. + """ + current = self.rb.get_thumbnail_url() + self.rb.save_thumbnail('test-thumb', None) + self.assertEqual(current, urlparse(self.rb.get_thumbnail_url()).path) + + @patch('PIL.Image.open', return_value=test_image) + def test_monochromatic_image(self, image): + """ + Tests that an monochromatic image does not change the current resource thumbnail. + """ + filename = 'test-thumb' + + current = self.rb.get_thumbnail_url() + self.rb.save_thumbnail(filename, image) + self.assertEqual(current, urlparse(self.rb.get_thumbnail_url()).path) + + # cleanup: remove saved thumbnail + thumb_utils.remove_thumbs(filename) + self.assertFalse(thumb_utils.thumb_exists(filename)) + + @patch('PIL.Image.open', return_value=test_image) + def test_thumb_utils_methods(self, image): + """ + Bunch of tests on thumb_utils helpers. + """ + filename = 'test-thumb' + upload_path = thumb_utils.thumb_path(filename) + self.assertEqual(upload_path, os.path.join(settings.THUMBNAIL_LOCATION, filename)) + thumb_utils.remove_thumbs(filename) + self.assertFalse(thumb_utils.thumb_exists(filename)) + f = BytesIO(test_image.tobytes()) + f.name = filename + storage.save(upload_path, File(f)) + self.assertTrue(thumb_utils.thumb_exists(filename)) + self.assertEqual(thumb_utils.thumb_size(upload_path), 10000) + + # cleanup: remove saved thumbnail + thumb_utils.remove_thumbs(filename) + self.assertFalse(thumb_utils.thumb_exists(filename)) + class TestThumbnailUrl(GeoNodeBaseTestSupport): + def setUp(self): super(TestThumbnailUrl, self).setUp() rb = ResourceBase.objects.create() @@ -194,52 +266,38 @@ def test_get_menu_placeholder_0(self): self.assertIn( self.menu_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_0_0.title - ) + f'Expected "{self.menu_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_0_0.title - ) + f'Expected "{self.menu_item_0_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_0_1.title - ) + f'Expected "{self.menu_item_0_0_1.title}" string in the rendered template' ) # second menu self.assertIn( self.menu_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_0_1.title - ) + f'Expected "{self.menu_0_1.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_0.title - ) + f'Expected "{self.menu_item_0_1_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_1.title - ) + f'Expected "{self.menu_item_0_1_1.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_2.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_2.title - ) + f'Expected "{self.menu_item_0_1_2.title}" string in the rendered template' ) # menu_placeholder_1 # first menu @@ -247,23 +305,17 @@ def test_get_menu_placeholder_0(self): self.assertNotIn( self.menu_1_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_1_0.title - ) + f'No "{self.menu_1_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_1_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_1_0_0.title - ) + f'No "{self.menu_item_1_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_1_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_1_0_1.title - ) + f'No "{self.menu_item_1_0_1.title}" string expected in the rendered template' ) def test_get_menu_placeholder_1(self): @@ -276,52 +328,38 @@ def test_get_menu_placeholder_1(self): self.assertNotIn( self.menu_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_0_0.title - ) + f'No "{self.menu_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_0_0.title - ) + f'No "{self.menu_item_0_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_0_1.title - ) + f'No "{self.menu_item_0_0_1.title}" string expected in the rendered template' ) # second menu self.assertNotIn( self.menu_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_0_1.title - ) + f'No "{self.menu_0_1.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_0.title - ) + f'No "{self.menu_item_0_1_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_1.title - ) + f'No "{self.menu_item_0_1_1.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_2.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_2.title - ) + f'No "{self.menu_item_0_1_2.title}" string expected in the rendered template' ) # menu_placeholder_1 # first menu @@ -329,23 +367,17 @@ def test_get_menu_placeholder_1(self): self.assertIn( self.menu_1_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_1_0.title - ) + f'Expected "{self.menu_1_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_1_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_1_0_0.title - ) + f'Expected "{self.menu_item_1_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_1_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_1_0_1.title - ) + f'Expected "{self.menu_item_1_0_1.title}" string in the rendered template' ) def test_render_nav_menu_placeholder_0(self): @@ -358,52 +390,38 @@ def test_render_nav_menu_placeholder_0(self): self.assertIn( self.menu_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_0_0.title - ) + f'Expected "{self.menu_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_0_0.title - ) + f'Expected "{self.menu_item_0_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_0_1.title - ) + f'Expected "{self.menu_item_0_0_1.title}" string in the rendered template' ) # second menu self.assertIn( self.menu_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_0_1.title - ) + f'Expected "{self.menu_0_1.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_0.title - ) + f'Expected "{self.menu_item_0_1_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_1.title - ) + f'Expected "{self.menu_item_0_1_1.title}" string in the rendered template' ) self.assertIn( self.menu_item_0_1_2.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_0_1_2.title - ) + f'Expected "{self.menu_item_0_1_2.title}" string in the rendered template' ) # menu_placeholder_1 # first menu @@ -411,23 +429,17 @@ def test_render_nav_menu_placeholder_0(self): self.assertNotIn( self.menu_1_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_1_0.title - ) + f'No "{self.menu_1_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_1_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_1_0_0.title - ) + f'No "{self.menu_item_1_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_1_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_1_0_1.title - ) + f'No "{self.menu_item_1_0_1.title}" string expected in the rendered template' ) def test_render_nav_menu_placeholder_1(self): @@ -440,52 +452,38 @@ def test_render_nav_menu_placeholder_1(self): self.assertNotIn( self.menu_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_0_0.title - ) + f'No "{self.menu_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_0_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_0_0.title - ) + f'No "{self.menu_item_0_0_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_0_1.title - ) + f'No "{self.menu_item_0_0_1.title}" string expected in the rendered template' ) # second menu self.assertNotIn( self.menu_0_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_0_1.title - ) + f'No "{self.menu_0_1.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_0.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_0.title - ) + f'No "{self.menu_item_0_1_0.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_1.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_1.title - ) + f'No "{self.menu_item_0_1_1.title}" string expected in the rendered template' ) self.assertNotIn( self.menu_item_0_1_2.title, rendered, - 'No "{}" string expected in the rendered template'.format( - self.menu_item_0_1_2.title - ) + f'No "{self.menu_item_0_1_2.title}" string expected in the rendered template' ) # menu_placeholder_1 # first menu @@ -493,31 +491,22 @@ def test_render_nav_menu_placeholder_1(self): self.assertIn( self.menu_1_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_1_0.title - ) + f'Expected "{self.menu_1_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_1_0_0.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_1_0_0.title - ) + f'Expected "{self.menu_item_1_0_0.title}" string in the rendered template' ) self.assertIn( self.menu_item_1_0_1.title, rendered, - 'Expected "{}" string in the rendered template'.format( - self.menu_item_1_0_1.title - ) + f'Expected "{self.menu_item_1_0_1.title}" string in the rendered template' ) class DeleteResourcesCommandTests(GeoNodeBaseTestSupport): - def setUp(self): - super().setUp() - def test_delete_resources_no_arguments(self): args = [] kwargs = {} @@ -805,6 +794,13 @@ def test_category_data_shown_for_with_resourcebase_permissions(self): categories = get_visibile_resources(self.user) self.assertEqual(categories['iso_formats'].count(), 1) + def test_visible_notifications(self): + """ + Test that a standard user won't be able to show ADMINS_ONLY_NOTICE_TYPES + """ + self.assertFalse(show_notification('monitoring_alert', self.user)) + self.assertTrue(show_notification('request_download_resourcebase', self.user)) + class TestHtmlTagRemoval(SimpleTestCase): @@ -823,9 +819,148 @@ def test_simple_tags_in_attribute(self): def test_complex_tags_in_attribute(self): tagged_value = """

-

Something in container
""" - attribute_target_value = """This is not a templated text - Something in container""" +
Something in ícontainer

£682m

""" + attribute_target_value = """This is not a templated text Something in ícontainer £682m""" + r = ResourceBase() + filtered_value = r._remove_html_tags(tagged_value) + self.assertEqual(filtered_value, attribute_target_value) + + def test_converted_html_in_tags_with_char_references(self): + tagged_value = """

<p>Abstract value & some text</p>

""" + attribute_target_value = """Abstract value & some text""" + r = ResourceBase() + filtered_value = r._remove_html_tags(tagged_value) + self.assertEqual(filtered_value, attribute_target_value) + + def test_converted_html_in_tags_with_with_multiple_tags(self): + tagged_value = """

Abstract value & some text

""" + attribute_target_value = """Abstract value & some text""" r = ResourceBase() filtered_value = r._remove_html_tags(tagged_value) self.assertEqual(filtered_value, attribute_target_value) + + +class TestTagThesaurus(TestCase): + # loading test thesausurs + fixtures = [ + "test_thesaurus.json" + ] + + def setUp(self): + self.sut = Thesaurus( + identifier="foo_name", + title="GEMET - INSPIRE themes, version 1.0", + date="2018-05-23T10:25:56", + description="GEMET - INSPIRE themes, version 1.0", + slug="", + about="http://inspire.ec.europa.eu/theme", + ) + self.tkeywords = ThesaurusKeyword.objects.all() + + def test_get_unique_thesaurus_list(self): + tid = self.__get_last_thesaurus().id + actual = get_unique_thesaurus_set(self.tkeywords) + self.assertSetEqual({tid}, actual) + + def test_get_thesaurus_title(self): + tid = self.__get_last_thesaurus().id + actual = get_thesaurus_title(tid) + self.assertEqual(self.sut.title, actual) + + def test_get_thesaurus_date(self): + tid = self.__get_last_thesaurus().id + actual = get_thesaurus_date(tid) + self.assertEqual(self.sut.date, actual) + + def test_get_name_translation_raise_exception_if_identifier_does_not_exists(self): + with self.assertRaises(ObjectDoesNotExist): + get_name_translation('foo_indentifier') + + @patch('geonode.base.templatetags.thesaurus.get_language') + def test_get_name_translation_return_thesauro_title_if_label_for_selected_language_does_not_exists(self, lang): + lang.return_value = 'ke' + actual = get_name_translation('inspire-theme') + expected = "GEMET - INSPIRE themes, version 1.0" + self.assertEqual(expected, actual) + + @patch('geonode.base.templatetags.thesaurus.get_language') + def test_get_name_translation_return_label_title_if_label_for_selected_language_exists(self, lang): + lang.return_value = 'it' + actual = get_name_translation('inspire-theme') + expected = "Tema GEMET - INSPIRE, versione 1.0" + self.assertEqual(expected, actual) + + @staticmethod + def __get_last_thesaurus(): + return Thesaurus.objects.all().order_by("-id")[0] + + +@override_settings(THESAURUS_DEFAULT_LANG="en") +class TestThesaurusAvailableForm(TestCase): + # loading test thesausurs + fixtures = [ + "test_thesaurus.json" + ] + + def setUp(self): + self.sut = ThesaurusAvailableForm + + def test_form_is_invalid_if_required_fields_are_missing(self): + actual = self.sut(data={}) + self.assertFalse(actual.is_valid()) + + def test_form_is_invalid_if_fileds_send_unexpected_values(self): + actual = self.sut(data={"1": [1, 2]}) + self.assertFalse(actual.is_valid()) + + def test_form_is_valid_if_fileds_send_expected_values(self): + actual = self.sut(data={"1": 1}) + self.assertTrue(actual.is_valid()) + + +class TestFacets(TestCase): + def setUp(self): + self.user = get_user_model().objects.create(username='test', email='test@test.com') + Layer.objects.create( + owner=self.user, title='test_boxes', abstract='nothing', storeType='dataStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_1', abstract='contains boxes', storeType='dataStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_2', purpose='contains boxes', storeType='dataStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_3', storeType='dataStore', is_approved=True + ) + + Layer.objects.create( + owner=self.user, title='test_boxes', abstract='nothing', storeType='coverageStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_1', abstract='contains boxes', storeType='coverageStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_2', purpose='contains boxes', storeType='coverageStore', is_approved=True + ) + Layer.objects.create( + owner=self.user, title='test_boxes', storeType='coverageStore', is_approved=True + ) + + self.request_mock = Mock(spec=requests.Request, GET=Mock()) + + def test_facets_filter_layers_returns_correctly(self): + self.request_mock.GET.get.side_effect = lambda key, self: { + 'title__icontains': 'boxes', + 'abstract__icontains': 'boxes', + 'purpose__icontains': 'boxes', + 'date__gte': None, + 'date__range': None, + 'date__lte': None, + 'extent': None + }.get(key) + self.request_mock.GET.getlist.return_value = None + self.request_mock.user = self.user + results = facets({'request': self.request_mock}) + self.assertEqual(results['vector'], 3) + self.assertEqual(results['raster'], 4) diff --git a/geonode/catalogue/templates/geonode_metadata_full.html b/geonode/catalogue/templates/geonode_metadata_full.html index 989a6cfc9b9..bf2e545af9a 100644 --- a/geonode/catalogue/templates/geonode_metadata_full.html +++ b/geonode/catalogue/templates/geonode_metadata_full.html @@ -59,7 +59,7 @@

{{ resource.title }}

{{resource.date}}, {% trans resource.date_type|title %}
{% trans "Abstract" %}
-
{{resource.abstract}}
+
{{resource.raw_abstract}}
{% trans "Edition" %}
{% if resource.edition %} @@ -79,7 +79,7 @@

{{ resource.title }}

{% trans "Purpose" %}
{% if resource.purpose %} -
{{resource.purpose}}
+
{{resource.raw_purpose}}
{% else %}
--
{% endif %} @@ -93,7 +93,7 @@

{{ resource.title }}

{% trans "Restrictions" %}
{{resource.restriction_code_type}}
-
{{resource.constraints_other}}
+
{{resource.raw_constraints_other}}
{% trans "License" %}
{{resource.license}}
@@ -117,11 +117,11 @@

{{ resource.title }}

{% trans "Supplemental Information" %}
-
{{resource.supplemental_information}}
+
{{resource.raw_supplemental_information}}
{% trans "Data Quality" %}
{% if resource.data_quality_statement %} -
{{resource.data_quality_statement}}
+
{{resource.raw_data_quality_statement}}
{% else %}
--
{% endif %}