diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py index ac8172a2a0d..ebd25b8517e 100644 --- a/geonode/base/api/serializers.py +++ b/geonode/base/api/serializers.py @@ -574,7 +574,6 @@ def __init__(self, *args, **kwargs): 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) diff --git a/geonode/base/migrations/0086_linkedresource.py b/geonode/base/migrations/0086_linkedresource.py index cc5929e788d..e5b5f42d435 100644 --- a/geonode/base/migrations/0086_linkedresource.py +++ b/geonode/base/migrations/0086_linkedresource.py @@ -5,19 +5,31 @@ class Migration(migrations.Migration): - dependencies = [ - ('base', '0085_alter_resourcebase_uuid'), + ("base", "0085_alter_resourcebase_uuid"), ] operations = [ migrations.CreateModel( - name='LinkedResource', + name="LinkedResource", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('source', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='linked_to', to='base.resourcebase')), - ('target', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='linked_by', to='base.resourcebase')), - ('internal', models.BooleanField(default=False)), + ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ( + "source", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name="linked_to", to="base.resourcebase" + ), + ), + ( + "target", + models.ForeignKey( + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="linked_by", + to="base.resourcebase", + ), + ), + ("internal", models.BooleanField(default=False)), ], ), ] diff --git a/geonode/documents/migrations/0037_delete_documentresourcelink.py b/geonode/documents/migrations/0037_delete_documentresourcelink.py index a03389fd6b6..72e49c51ae4 100644 --- a/geonode/documents/migrations/0037_delete_documentresourcelink.py +++ b/geonode/documents/migrations/0037_delete_documentresourcelink.py @@ -4,17 +4,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('documents', '0036_clean_document_thumbnails'), - ('base', '0086_linkedresource'), + ("documents", "0036_clean_document_thumbnails"), + ("base", "0086_linkedresource"), ] operations = [ - migrations.RunSQL("INSERT INTO base_linkedresource(source_id, target_id, internal)" - "SELECT document_id, object_id, false as internal " - "FROM documents_documentresourcelink;"), + migrations.RunSQL( + "INSERT INTO base_linkedresource(source_id, target_id, internal)" + "SELECT document_id, object_id, false as internal " + "FROM documents_documentresourcelink;" + ), migrations.DeleteModel( - name='DocumentResourceLink', + name="DocumentResourceLink", ), ] diff --git a/geonode/facets/tests.py b/geonode/facets/tests.py index 3245fe72d9a..8b4812a110f 100644 --- a/geonode/facets/tests.py +++ b/geonode/facets/tests.py @@ -42,8 +42,8 @@ from geonode.facets.providers.category import CategoryFacetProvider 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 -import geonode.facets.views as views logger = logging.getLogger(__name__) @@ -85,7 +85,7 @@ def _create_thesauri(cls): for tn in range(2): t = Thesaurus.objects.create(identifier=f"t_{tn}", title=f"Thesaurus {tn}", order=100 + tn * 10) cls.thesauri[tn] = t - for tl in ( + for tl in ( # fmt: skip "en", "it", ): @@ -94,7 +94,7 @@ def _create_thesauri(cls): for tkn in range(10): tk = ThesaurusKeyword.objects.create(thesaurus=t, alt_label=f"T{tn}_K{tkn}_ALT") cls.thesauri_k[f"{tn}_{tkn}"] = tk - for tkl in ( + for tkl in ( # fmt: skip "en", "it", ): @@ -104,7 +104,7 @@ def _create_thesauri(cls): def _create_regions(cls): cls.regions = {} - for code, name in ( + for code, name in ( # fmt: skip ("R0", "Region0"), ("R1", "Region1"), ("R2", "Region2"), @@ -115,7 +115,7 @@ def _create_regions(cls): def _create_categories(cls): cls.cats = {} - for code, name in ( + for code, name in ( # fmt: skip ("C0", "Cat0"), ("C1", "Cat1"), ("C2", "Cat2"), @@ -126,7 +126,7 @@ def _create_categories(cls): def _create_keywords(cls): cls.kw = {} - for code, name in ( + for code, name in ( # fmt: skip ("K0", "Keyword0"), ("K1", "Keyword1"), ("K2", "Keyword2"), @@ -186,14 +186,14 @@ def _create_resources(self): if (x % 6) in (0, 1, 2): d.featured = True - for reg, idx in ( + for reg, idx in ( # fmt: skip ("R0", (0, 1)), ("R1", (0, 2, 8, 13)), ): if x in idx: d.regions.add(self.regions[reg]) - for kw, idx in ( + for kw, idx in ( # fmt: skip ("K0", (0, 3, 4, 5)), ("K1", [1, 4]), ("K2", [2, 5]), @@ -201,7 +201,7 @@ def _create_resources(self): if x in idx: d.keywords.add(self.kw[kw]) - for cat, idx in ( + for cat, idx in ( # fmt: skip ("C0", [0, 2, 4]), ("C1", [5, 15, 16]), ("C2", [18, 19]), @@ -218,7 +218,7 @@ def _facets_to_map(facets): def test_facets_base(self): req = self.rf.get(reverse("list_facets"), data={"lang": "en"}) - res: JsonResponse = views.list_facets(req) + res: JsonResponse = ListFacetsView.as_view()(req) obj = json.loads(res.content) self.assertIn("facets", obj) facets_list = obj["facets"] @@ -238,13 +238,13 @@ def test_facets_rich(self): # run the request req = self.rf.get(reverse("list_facets"), data={"include_topics": 1, "lang": "en"}) - res: JsonResponse = views.list_facets(req) + res: JsonResponse = ListFacetsView.as_view()(req) obj = json.loads(res.content) facets_list = obj["facets"] self.assertEqual(8, len(facets_list)) fmap = self._facets_to_map(facets_list) - for expected in ( + for expected in ( # fmt: skip { "name": "category", "topics": { @@ -356,7 +356,7 @@ def test_bad_lang(self): # run the request with a valid language req = self.rf.get(reverse("get_facet", args=["t_0"]), data={"lang": "en"}) - res: JsonResponse = views.get_facet(req, "t_0") + res: JsonResponse = GetFacetView.as_view()(req, "t_0") obj = json.loads(res.content) self.assertEqual(2, obj["topics"]["total"]) @@ -366,7 +366,7 @@ def test_bad_lang(self): # run the request with an INVALID language req = self.rf.get(reverse("get_facet", args=["t_0"]), data={"lang": "ZZ"}) - res: JsonResponse = views.get_facet(req, "t_0") + res: JsonResponse = GetFacetView.as_view()(req, "t_0") obj = json.loads(res.content) self.assertEqual(2, obj["topics"]["total"]) @@ -374,18 +374,6 @@ def test_bad_lang(self): self.assertEqual("T0_K0_ALT", obj["topics"]["items"][0]["label"]) # check for the alternate label self.assertFalse(obj["topics"]["items"][0]["is_localized"]) # check for the localization flag - def test_topics(self): - for facet, keys, exp in ( - ("t_0", [self.thesauri_k["0_0"].id, self.thesauri_k["0_1"].id, -999], 2), - ("category", ["C1", "C2", "nomatch"], 2), - ("owner", [self.user.id, -100], 1), - ("region", ["R0", "R1", "nomatch"], 2), - ): - req = self.rf.get(reverse("get_facet_topics", args=[facet]), data={"lang": "en", "key": keys}) - res: JsonResponse = views.get_facet_topics(req, facet) - obj = json.loads(res.content) - self.assertEqual(exp, len(obj["topics"]["items"]), f"Unexpected topic count {exp} for facet {facet}") - def test_prefiltering(self): reginfo = RegionFacetProvider().get_info() regfilter = reginfo["filter"] @@ -403,7 +391,7 @@ def test_prefiltering(self): (reginfo["name"], {t1filter: self.thesauri_k["1_0"].id}, 2, 3), ): req = self.rf.get(reverse("get_facet", args=[facet]), data=filters) - res: JsonResponse = views.get_facet(req, facet) + res: JsonResponse = GetFacetView.as_view()(req, facet) obj = json.loads(res.content) self.assertEqual(totals, obj["topics"]["total"], f"Bad totals for facet '{facet} and filter {filters}") self.assertEqual(count0, obj["topics"]["items"][0]["count"], f"Bad count0 for facet '{facet}") @@ -423,7 +411,7 @@ def test_prefiltering_tkeywords(self): (featname, {t1filter: tkey_1_1}, expected_feat), ): req = self.rf.get(reverse("get_facet", args=[facet]), data=params) - res: JsonResponse = views.get_facet(req, facet) + res: JsonResponse = GetFacetView.as_view()(req, facet) obj = json.loads(res.content) self.assertEqual( @@ -439,13 +427,13 @@ def test_prefiltering_tkeywords(self): # Run the single request req = self.rf.get(reverse("list_facets"), data={"include_topics": 1, t1filter: tkey_1_1}) - res: JsonResponse = views.list_facets(req) + res: JsonResponse = ListFacetsView.as_view()(req) obj = json.loads(res.content) facets_list = obj["facets"] fmap = self._facets_to_map(facets_list) - for name, items in ( + for name, items in ( # fmt: skip (regname, expected_region), (featname, expected_feat), ): @@ -466,7 +454,7 @@ def test_config(self): ("owner", "select", 8), ): req = self.rf.get(reverse("get_facet", args=[facet]), data={"include_config": True}) - res: JsonResponse = views.get_facet(req, facet) + res: JsonResponse = GetFacetView.as_view()(req, facet) obj = json.loads(res.content) self.assertIn("config", obj, "Config info not found in payload") conf = obj["config"] @@ -525,7 +513,7 @@ def t(tk): (kwname, {t0filter: t("0_0"), regfilter: "R0", "key": ["K0"]}, {"K0": None}), ): req = self.rf.get(reverse("get_facet", args=[facet]), data=params) - res: JsonResponse = views.get_facet(req, facet) + res: JsonResponse = GetFacetView.as_view()(req, facet) obj = json.loads(res.content) # self.assertEqual(totals, obj["topics"]["total"], f"Bad totals for facet '{facet} and params {params}") diff --git a/geonode/facets/urls.py b/geonode/facets/urls.py index 42bf08de85b..8d96976e53c 100644 --- a/geonode/facets/urls.py +++ b/geonode/facets/urls.py @@ -18,10 +18,9 @@ ######################################################################### from django.urls import path -from . import views +from .views import ListFacetsView, GetFacetView urlpatterns = [ - path("facets", views.list_facets, name="list_facets"), - path("facets/", views.get_facet, name="get_facet"), - path("facets//topics", views.get_facet_topics, name="get_facet_topics"), + path("facets", ListFacetsView.as_view(), name="list_facets"), + path("facets/", GetFacetView.as_view(), name="get_facet"), ] diff --git a/geonode/facets/views.py b/geonode/facets/views.py index a02c8aa961f..5bd186b7045 100644 --- a/geonode/facets/views.py +++ b/geonode/facets/views.py @@ -21,11 +21,10 @@ from urllib.parse import urlencode from rest_framework.authentication import SessionAuthentication, BasicAuthentication -from rest_framework.decorators import api_view, authentication_classes +from rest_framework.views import APIView -from django.http import HttpResponseNotFound, JsonResponse, HttpResponseBadRequest +from django.http import HttpResponseNotFound, JsonResponse from django.urls import reverse - from django.conf import settings from geonode.base.api.views import ResourceBaseViewSet @@ -44,188 +43,172 @@ logger = logging.getLogger(__name__) -@api_view(["GET"]) -@authentication_classes([SessionAuthentication, BasicAuthentication]) -def list_facets(request, **kwargs): - lang, lang_requested = _resolve_language(request) - add_links = _resolve_boolean(request, PARAM_ADD_LINKS, False) - include_topics = _resolve_boolean(request, PARAM_INCLUDE_TOPICS, False) - include_config = _resolve_boolean(request, PARAM_INCLUDE_CONFIG, False) - - facets = [] - prefiltered = None - - for provider in facet_registry.get_providers(): - logger.debug("Fetching data from provider %r", provider) - info = provider.get_info(lang=lang) +class BaseFacetingView(APIView): + authentication_classes = [SessionAuthentication, BasicAuthentication] + + @classmethod + def _get_topics( + cls, + provider, + queryset, + page: int = 0, + page_size: int = DEFAULT_FACET_PAGE_SIZE, + lang: str = "en", + topic_contains: str = None, + keys: set = {}, + **kwargs, + ): + start = page * page_size + end = start + page_size + + cnt, items = provider.get_facet_items( + queryset, start=start, end=end, lang=lang, topic_contains=topic_contains, keys=keys + ) + if keys: + keys.difference_update({str(t["key"]) for t in items}) + if keys: + ext = provider.get_topics(keys, lang) + items.extend(ext) + cnt += len(ext) + logger.debug("Extending facets to %d for %s", cnt, provider.name) + + return {"page": page, "page_size": page_size, "start": start, "total": cnt, "items": items} + + @classmethod + def _prefilter_topics(cls, request): + """ + Perform some prefiltering on resources, such as + - auth visibility + - filtering by other facets already applied + :param request: + :return: a QuerySet on ResourceBase + """ + 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}") + + if filters: + viewset = ResourceBaseViewSet(request=request, format_kwarg={}, kwargs=filters) + viewset.initial(request) + return get_visible_resources(queryset=viewset.filter_queryset(viewset.get_queryset()), user=request.user) + else: + # return ResourceBase.objects + return get_visible_resources(ResourceBase.objects, request.user) + + @classmethod + def _resolve_language(cls, request) -> (str, bool): + """ + :return: the resolved language, a boolean telling if the language was requested + """ + # first try with an explicit request using params + if lang := request.GET.get(PARAM_LANG, None): + return lang, True + # 2nd try: use LANGUAGE_CODE + try: + return request.LANGUAGE_CODE.split("-")[0], False + except AttributeError: + return settings.LANGUAGE_CODE, False + + @classmethod + def _resolve_boolean(cls, request, name, fallback=None): + """ + Parse boolean query params + """ + val = request.GET.get(name, None) + if val is None: + return fallback + + val = val.lower() + if val.startswith("t") or val.startswith("y") or val == "1": + return True + elif val.startswith("f") or val.startswith("n") or val == "0": + return False + else: + return fallback + + +class ListFacetsView(BaseFacetingView): + def get(self, request, *args, **kwargs): + lang, lang_requested = self._resolve_language(request) + add_links = self._resolve_boolean(request, PARAM_ADD_LINKS, False) + include_topics = self._resolve_boolean(request, PARAM_INCLUDE_TOPICS, False) + include_config = self._resolve_boolean(request, PARAM_INCLUDE_CONFIG, False) + + facets = [] + prefiltered = None + + for provider in facet_registry.get_providers(): + logger.debug("Fetching data from provider %r", provider) + info = provider.get_info(lang=lang) + + if include_config: + info["config"] = provider.config + + if add_links: + link_args = {PARAM_ADD_LINKS: True} + if lang_requested: # only add lang param if specified in current call + link_args[PARAM_LANG] = lang + info["link"] = f"{reverse('get_facet', args=[info['name']])}?{urlencode(link_args)}" + + if include_topics: + prefiltered = prefiltered or self._prefilter_topics(request) + info["topics"] = self._get_topics(provider, queryset=prefiltered, lang=lang) + + facets.append(info) + + logger.debug("Returning facets %r", facets) + return JsonResponse({"facets": facets}) + + +class GetFacetView(BaseFacetingView): + def get(self, request, facet): + logger.debug("get_facet -> %r for user '%r'", facet, request.user.username) + + # retrieve provider for the requested facet + provider: FacetProvider = facet_registry.get_provider(facet) + if not provider: + return HttpResponseNotFound() + + # parse some query params + lang, lang_requested = self._resolve_language(request) + add_link = self._resolve_boolean(request, PARAM_ADD_LINKS, False) + include_config = self._resolve_boolean(request, PARAM_INCLUDE_CONFIG, False) + + topic_contains = request.GET.get(PARAM_TOPIC_CONTAINS, None) + keys = set(request.query_params.getlist("key")) + + page = int(request.GET.get(PARAM_PAGE, 0)) + page_size = int(request.GET.get(PARAM_PAGE_SIZE, DEFAULT_FACET_PAGE_SIZE)) + + info = provider.get_info(lang) if include_config: info["config"] = provider.config - if add_links: - link_args = {PARAM_ADD_LINKS: True} - if lang_requested: # only add lang param if specified in current call - link_args[PARAM_LANG] = lang - info["link"] = f"{reverse('get_facet', args=[info['name']])}?{urlencode(link_args)}" - - if include_topics: - prefiltered = prefiltered or _prefilter_topics(request) - info["topics"] = _get_topics(provider, queryset=prefiltered, lang=lang) - - facets.append(info) - - logger.debug("Returning facets %r", facets) - return JsonResponse({"facets": facets}) - - -@api_view(["GET"]) -@authentication_classes([SessionAuthentication, BasicAuthentication]) -def get_facet(request, facet): - logger.debug("get_facet -> %r for user '%r'", facet, request.user.username) - - # retrieve provider for the requested facet - provider: FacetProvider = facet_registry.get_provider(facet) - if not provider: - return HttpResponseNotFound() - - # parse some query params - lang, lang_requested = _resolve_language(request) - add_link = _resolve_boolean(request, PARAM_ADD_LINKS, False) - include_config = _resolve_boolean(request, PARAM_INCLUDE_CONFIG, False) - - topic_contains = request.GET.get(PARAM_TOPIC_CONTAINS, None) - keys = set(request.query_params.getlist("key")) - - page = int(request.GET.get(PARAM_PAGE, 0)) - page_size = int(request.GET.get(PARAM_PAGE_SIZE, DEFAULT_FACET_PAGE_SIZE)) - - info = provider.get_info(lang) - if include_config: - info["config"] = provider.config - - qs = _prefilter_topics(request) - topics = _get_topics( - provider, queryset=qs, page=page, page_size=page_size, lang=lang, topic_contains=topic_contains, keys=keys - ) - - if add_link: - exist_prev = page > 0 - exist_next = topics["total"] > (page + 1) * page_size - link = reverse("get_facet", args=[info["name"]]) - for exist, link_name, p in ( - (exist_prev, "prev", page - 1), - (exist_next, "next", page + 1), - ): - link_param = {PARAM_PAGE: p, PARAM_PAGE_SIZE: page_size, PARAM_LANG: lang, PARAM_ADD_LINKS: True} - if lang_requested: # only add lang param if specified in current call - link_param[PARAM_LANG] = lang - if topic_contains: - link_param[PARAM_TOPIC_CONTAINS] = topic_contains - info[link_name] = f"{link}?{urlencode(link_param)}" if exist else None - - if topic_contains: - # in the payload let's rmb this is a filtered output - info["topic_contains"] = topic_contains - - info["topics"] = topics - - return JsonResponse(info) - - -@api_view(["GET"]) -def get_facet_topics(request, facet): - logger.debug("get_facet_topics -> %r", facet) - - # retrieve provider for the requested facet - provider: FacetProvider = facet_registry.get_provider(facet) - if not provider: - return HttpResponseNotFound("Facet not found") - - # parse some query params - lang, lang_requested = _resolve_language(request) - keys = request.query_params.getlist("key") - if not keys: - return HttpResponseBadRequest("Missing key parameter") - - ret = {"topics": {"items": provider.get_topics(keys, lang=lang)}} - return JsonResponse(ret) - - -def _get_topics( - provider, - queryset, - page: int = 0, - page_size: int = DEFAULT_FACET_PAGE_SIZE, - lang: str = "en", - topic_contains: str = None, - keys: set = {}, - **kwargs, -): - start = page * page_size - end = start + page_size - - cnt, items = provider.get_facet_items( - queryset, start=start, end=end, lang=lang, topic_contains=topic_contains, keys=keys - ) - - if keys: - keys.difference_update({str(t["key"]) for t in items}) - if keys: - ext = provider.get_topics(keys, lang) - items.extend(ext) - cnt += len(ext) - logger.debug("Extending facets to %d for %s", cnt, provider.name) - - return {"page": page, "page_size": page_size, "start": start, "total": cnt, "items": items} - - -def _prefilter_topics(request): - """ - Perform some prefiltering on resources, such as - - auth visibility - - filtering by other facets already applied - :param request: - :return: a QuerySet on ResourceBase - """ - 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}") - - if filters: - viewset = ResourceBaseViewSet(request=request, format_kwarg={}, kwargs=filters) - viewset.initial(request) - return get_visible_resources(queryset=viewset.filter_queryset(viewset.get_queryset()), user=request.user) - else: - # return ResourceBase.objects - return get_visible_resources(ResourceBase.objects, request.user) - - -def _resolve_language(request) -> (str, bool): - """ - :return: the resolved language, a boolean telling if the language was requested - """ - # first try with an explicit request using params - if lang := request.GET.get(PARAM_LANG, None): - return lang, True - # 2nd try: use LANGUAGE_CODE - try: - return request.LANGUAGE_CODE.split("-")[0], False - except AttributeError: - return settings.LANGUAGE_CODE, False - - -def _resolve_boolean(request, name, fallback=None): - """ - Parse boolean query params - """ - val = request.GET.get(name, None) - if val is None: - return fallback - - val = val.lower() - if val.startswith("t") or val.startswith("y") or val == "1": - return True - elif val.startswith("f") or val.startswith("n") or val == "0": - return False - else: - return fallback + 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 + ) + + if add_link: + exist_prev = page > 0 + exist_next = topics["total"] > (page + 1) * page_size + link = reverse("get_facet", args=[info["name"]]) + for exist, link_name, p in ( + (exist_prev, "prev", page - 1), + (exist_next, "next", page + 1), + ): + link_param = {PARAM_PAGE: p, PARAM_PAGE_SIZE: page_size, PARAM_LANG: lang, PARAM_ADD_LINKS: True} + if lang_requested: # only add lang param if specified in current call + link_param[PARAM_LANG] = lang + if topic_contains: + link_param[PARAM_TOPIC_CONTAINS] = topic_contains + info[link_name] = f"{link}?{urlencode(link_param)}" if exist else None + + if topic_contains: + # in the payload let's rmb this is a filtered output + info["topic_contains"] = topic_contains + + info["topics"] = topics + + return JsonResponse(info) diff --git a/geonode/layers/templates/layouts/panels.html b/geonode/layers/templates/layouts/panels.html index e3fcb70739e..40a9c3e4321 100644 --- a/geonode/layers/templates/layouts/panels.html +++ b/geonode/layers/templates/layouts/panels.html @@ -261,7 +261,7 @@ {% trans "Optional" %} {% endif %} - + {% endblock edit_progress %} {% block breadcrumbs %} @@ -762,7 +762,7 @@ {{timeseries_form.presentation}} -
+

{{timeseries_form.precision_value}}
@@ -823,4 +823,4 @@

{% trans "Selecting an Attribute" %}

- \ No newline at end of file + diff --git a/geonode/maps/templates/layouts/map_panels.html b/geonode/maps/templates/layouts/map_panels.html index aa0f24bd27e..b6cbea5ceec 100644 --- a/geonode/maps/templates/layouts/map_panels.html +++ b/geonode/maps/templates/layouts/map_panels.html @@ -462,7 +462,7 @@ {{ map_form.constraints_other }} - + {% endblock map_constraints_other %} diff --git a/geonode/themes/migrations/0015_alter_geonodethemecustomization_is_enabled.py b/geonode/themes/migrations/0015_alter_geonodethemecustomization_is_enabled.py new file mode 100644 index 00000000000..fcd18f8ee3c --- /dev/null +++ b/geonode/themes/migrations/0015_alter_geonodethemecustomization_is_enabled.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.21 on 2023-10-23 10:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('geonode_themes', '0014_auto_20220214_0910'), + ] + + operations = [ + migrations.AlterField( + model_name='geonodethemecustomization', + name='is_enabled', + field=models.BooleanField(default=False, help_text='Set this theme as the current global theme for GeoNode. This will disable the current theme (if any)'), + ), + ] diff --git a/geonode/themes/models.py b/geonode/themes/models.py index 40f49478a1f..b94f9c6dd3c 100644 --- a/geonode/themes/models.py +++ b/geonode/themes/models.py @@ -54,7 +54,8 @@ class GeoNodeThemeCustomization(models.Model): name = models.CharField(max_length=100, help_text="This will not appear anywhere.") description = models.TextField(null=True, blank=True, help_text="This will not appear anywhere.") is_enabled = models.BooleanField( - default=False, help_text="Enabling this theme will disable the current enabled theme (if any)" + default=False, + help_text="Set this theme as the current global theme for GeoNode. This will disable the current theme (if any)", ) logo = models.ImageField(upload_to="img/%Y/%m", null=True, blank=True) extra_css = models.TextField(