Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion geonode/assets/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
Asset,
LocalAsset,
)
from rest_framework import serializers
from geonode.assets.utils import is_asset_deletable


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -64,12 +66,16 @@ class AssetSerializer(DynamicModelSerializer):
owner = SimpleUserSerializer(embed=False)
asset_type = ClassTypeField()
subinfo = AssetSubclassField()
deletable = serializers.SerializerMethodField()

class Meta:
model = Asset
name = "asset"
# fields = ("pk", "title", "description", "type", "owner", "created")
fields = ("pk", "title", "description", "type", "owner", "created", "asset_type", "subinfo")
fields = ("pk", "title", "description", "type", "owner", "created", "asset_type", "subinfo", "deletable")

def get_deletable(self, obj):
return is_asset_deletable(obj)


class LocalAssetSerializer(AssetSerializer):
Expand Down
14 changes: 12 additions & 2 deletions geonode/assets/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ def rollback_asset_and_link(asset, link):
logger.error(f"Could not rollback asset[{asset}] and link[{link}]", exc_info=e)


def is_asset_deletable(asset: Asset):
"""
Checks if an asset can be deleted.
currently Assets with titles "Original" or "Files" are protected and cannot be deleted.
Further logics can be added here.
"""
if asset.title in ["Original", "Files"]:
return False
return True


def unlink_asset(resource: ResourceBase, asset: Asset, remove_asset: bool = True):
"""
Unlinks an asset from a resource. By default, the asset is deleted.
Expand All @@ -214,8 +225,7 @@ def unlink_asset(resource: ResourceBase, asset: Asset, remove_asset: bool = True
* If the asset is only linked to the provided resource, the asset itself is deleted,
which in turn triggers the deletion of the associated files.
"""
if asset.title in ["Original", "Files"]:
logger.info(f"Asset '{asset.title}' is protected and will not be unlinked or deleted.")
if not is_asset_deletable(asset):
return False, "Asset is protected and will not be unlinked or deleted."

link = Link.objects.filter(resource=resource, asset=asset).first()
Expand Down
4 changes: 3 additions & 1 deletion geonode/base/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from django.forms.models import model_to_dict
from django.contrib.auth import get_user_model
from django.db.models.query import QuerySet
from geonode.assets.utils import get_default_asset
from geonode.assets.utils import get_default_asset, is_asset_deletable
from geonode.people import Roles
from django.http import QueryDict
from deprecated import deprecated
Expand Down Expand Up @@ -580,8 +580,10 @@ def to_representation(self, instance):
formatted_link = model_to_dict(lnk, fields=link_fields)
ret.append(formatted_link)
if lnk.asset:
deletable = is_asset_deletable(lnk.asset)
extras = {
"type": "asset",
"deletable": deletable,
"content": model_to_dict(lnk.asset, ["title", "description", "type", "created"]),
}
if request and permissions_registry.user_has_perm(
Expand Down
38 changes: 38 additions & 0 deletions geonode/base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@
Thesaurus,
ThesaurusKeyword,
generate_thesaurus_reference,
Link,
)
from geonode.assets.tests import ONE_JSON
from geonode.assets.utils import create_asset
from geonode.base.middleware import ReadOnlyMiddleware, MaintenanceMiddleware
from geonode.base.templatetags.base_tags import get_visibile_resources, facets
from geonode.base.templatetags.thesaurus import (
Expand All @@ -80,6 +83,7 @@
from geonode import geoserver
from geonode.decorators import on_ogc_backend
from geonode.resource.manager import resource_manager
from geonode.base.api.serializers import ResourceBaseSerializer

test_image = Image.new("RGBA", size=(50, 50), color=(155, 0, 0))

Expand Down Expand Up @@ -1324,3 +1328,37 @@ def test_fix_otherrestrictions_codetype(self):
fixed_obj = RestrictionCodeType.objects.get(identifier="otherRestrictions")
self.assertEqual(fixed_obj.description, "limitation not listed")
self.assertEqual(fixed_obj.gn_description, "limitation not listed")


class TestDeletableAssetKey(GeoNodeBaseTestSupport):
def setUp(self):
super().setUp()
self.user = get_user_model().objects.get(username="admin")
self.resource = create_single_dataset("test_resource")
self.asset1 = create_asset(self.user, asset_type="document", title="Test Asset for Deletion", files=[ONE_JSON])
self.asset2 = create_asset(self.user, asset_type="document", title="Original", files=[ONE_JSON])
self.link = Link.objects.create(resource=self.resource, asset=self.asset1, name="test_link")
self.link2 = Link.objects.create(resource=self.resource, asset=self.asset2, name="test_link_2")

def test_deletable_extra_property(self):
serializer = ResourceBaseSerializer(instance=self.resource)
data = serializer.data
links = data.get("links", []) # get value from LinksSerializer

# Create a mapping from asset title to the link's deletable status
deletable_status_by_title = {
link.get("extras", {}).get("content", {}).get("title"): link.get("extras", {}).get("deletable")
for link in links
if link.get("extras", {}).get("content", {}).get("title")
}
# Assertions for specific asset titles
self.assertIn("Test Asset for Deletion", deletable_status_by_title)
self.assertTrue(
deletable_status_by_title["Test Asset for Deletion"],
"Link with title 'Test Asset for Deletion' should have deletable=True",
)
self.assertIn("Original", deletable_status_by_title)
self.assertFalse(
deletable_status_by_title["Original"],
"Link with title 'Original' should have deletable=False",
)
Loading