Skip to content

Commit

Permalink
Merged master into release
Browse files Browse the repository at this point in the history
  • Loading branch information
suricactus committed Nov 14, 2023
2 parents b8a1c5e + de7b43e commit 69dad79
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 42 deletions.
75 changes: 50 additions & 25 deletions docker-app/qfieldcloud/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,20 @@ class ModelAdminEstimateCountMixin:
class QFieldCloudModelAdmin( # type: ignore
ModelAdminNoPkOrderChangeListMixin, ModelAdminEstimateCountMixin, admin.ModelAdmin
):
pass
def has_delete_permission(self, request, obj=None):
"""Reimplementing this Django Admin method to allow deleting related objects in django admin from another ModelAdmin.
If one tries to delete a Project with already ran Jobs, they cannot, because the Job ModelAdmin has def has_delete_permission: return True .
"""
if hasattr(self, "has_direct_delete_permission"):
perm = f"admin:{self.model._meta.app_label}_{self.model._meta.model_name}"
if request.resolver_match.view_name.startswith(perm):
if callable(self.has_direct_delete_permission):
return self.has_direct_delete_permission(request, obj)
else:
return self.has_direct_delete_permission

return super().has_delete_permission(request, obj)


def admin_urlname_by_obj(value, arg):
Expand Down Expand Up @@ -312,13 +325,11 @@ def format_pre_json(value):
class GeodbInline(admin.TabularInline):
model = Geodb
extra = 0
has_direct_delete_permission = False

def has_add_permission(self, request, obj):
return False

def has_delete_permission(self, request, obj):
return False

def has_change_permission(self, request, obj):
return False

Expand All @@ -332,7 +343,7 @@ def has_add_permission(self, request, obj):
return True
return obj.type in (User.Type.PERSON, User.Type.ORGANIZATION)

def has_delete_permission(self, request, obj):
def has_direct_delete_permission(self, request, obj):
if obj is None:
return True
return obj.type in (User.Type.PERSON, User.Type.ORGANIZATION)
Expand All @@ -352,7 +363,7 @@ def has_add_permission(self, request, obj):
return True
return obj.type in (User.Type.PERSON, User.Type.ORGANIZATION)

def has_delete_permission(self, request, obj):
def has_direct_delete_permission(self, request, obj):
if obj is None:
return True
return obj.type in (User.Type.PERSON, User.Type.ORGANIZATION)
Expand All @@ -366,30 +377,26 @@ def has_change_permission(self, request, obj):
class UserAccountInline(admin.StackedInline):
model = UserAccount
extra = 1
has_direct_delete_permission = False

def has_add_permission(self, request, obj):
return obj is None

def has_delete_permission(self, request, obj):
return False


class ProjectInline(admin.TabularInline):
model = Project
extra = 0

fields = ("owned_project", "is_public", "overwrite_conflicts")
readonly_fields = ("owned_project",)
has_direct_delete_permission = False

def owned_project(self, obj):
return model_admin_url(obj, obj.name)

def has_add_permission(self, request, obj):
return False

def has_delete_permission(self, request, obj):
return False

def has_change_permission(self, request, obj):
return False

Expand All @@ -403,7 +410,7 @@ def has_add_permission(self, request, obj):
return True
return obj.type == User.Type.PERSON

def has_delete_permission(self, request, obj):
def has_direct_delete_permission(self, request, obj):
if obj is None:
return True
return obj.type == User.Type.PERSON
Expand Down Expand Up @@ -598,6 +605,7 @@ class ProjectAdmin(QFieldCloudModelAdmin):
"status_code",
"project_filename",
"file_storage_bytes",
"storage_keep_versions",
"created_at",
"updated_at",
"data_last_updated_at",
Expand Down Expand Up @@ -626,6 +634,15 @@ class ProjectAdmin(QFieldCloudModelAdmin):

ordering = ("-updated_at",)

def get_form(self, *args, **kwargs):
help_texts = {
"file_storage_bytes": _(
"Use this value to limit the maximum number of file versions. When empty current plan's default will be used. Usually availlable to Premium users only."
)
}
kwargs.update({"help_texts": help_texts})
return super().get_form(*args, **kwargs)

def get_search_results(self, request, queryset, search_term):
filters = search_parser(
request,
Expand Down Expand Up @@ -683,13 +700,11 @@ class DeltaInline(admin.TabularInline):
# TODO find a way to use dynamic fields
# "feedback__pre",
)
has_direct_delete_permission = False

def has_add_permission(self, request, obj):
return False

def has_delete_permission(self, request, obj):
return False

# def feedback__pre(self, instance):
# return format_pre_json(instance.feedback)

Expand Down Expand Up @@ -760,6 +775,7 @@ class JobAdmin(QFieldCloudModelAdmin):
"output__pre",
"feedback__pre",
)
has_direct_delete_permission = False

def get_queryset(self, request):
return super().get_queryset(request).defer("output", "feedback")
Expand Down Expand Up @@ -826,9 +842,6 @@ def has_add_permission(self, request, obj=None):
def has_change_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
return False

def output__pre(self, instance):
return format_pre(instance.output)

Expand Down Expand Up @@ -870,6 +883,7 @@ class ApplyJobDeltaInline(admin.TabularInline):
"status",
"output__pre",
)
has_direct_delete_permission = False

def job_id(self, instance):
return model_admin_url(instance.apply_job)
Expand All @@ -880,9 +894,6 @@ def output__pre(self, instance):
def has_add_permission(self, request, obj):
return False

def has_delete_permission(self, request, obj):
return False


class IsFinalizedDeltaJobFilter(admin.SimpleListFilter):
title = _("finalized delta job")
Expand Down Expand Up @@ -1121,13 +1132,11 @@ class TeamInline(admin.TabularInline):
extra = 0

fields = ("username",)
has_direct_delete_permission = False

def has_add_permission(self, request, obj):
return False

def has_delete_permission(self, request, obj):
return False

def has_change_permission(self, request, obj):
return False

Expand Down Expand Up @@ -1258,6 +1267,22 @@ def save_model(self, request, obj, form, change):
obj.username = f"@{obj.team_organization.username}/{obj.username}"
obj.save()

def get_form(
self,
request: Any,
obj: Any | None = None,
change: bool | None = None,
**kwargs: Any,
) -> Any:
if obj:
# hide the organization prefix of the team, so the Team admin can be saved without
# the need to manually edit the username
obj.username = obj.username.replace(
f"@{obj.team_organization.username}/", ""
)

return super().get_form(request, obj, bool(change), **kwargs)


class InvitationAdmin(InvitationAdminBase):
list_display = ("email", "inviter", "created", "sent", "accepted")
Expand Down
29 changes: 18 additions & 11 deletions docker-app/qfieldcloud/core/tests/test_qgis_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,21 +207,25 @@ def test_upload_and_list_file(self):
response = self.client.get(f"/api/v1/files/{self.project1.id}/")
self.assertTrue(status.is_success(response.status_code))

json = response.json()
json = sorted(json, key=lambda k: k["name"])
payload = response.json()
payload = sorted(payload, key=lambda k: k["name"])

self.assertEqual(json[0]["name"], "aaa/file.txt")
self.assertEqual(json[0]["size"], 13)
self.assertEqual(json[1]["name"], "file2.txt")
self.assertEqual(json[1]["size"], 13)
self.assertEqual(payload[0]["name"], "aaa/file.txt")
self.assertEqual(payload[0]["size"], 13)
self.assertEqual(payload[1]["name"], "file2.txt")
self.assertEqual(payload[1]["size"], 13)
# check sha256
self.assertEqual(
json[0]["sha256"],
payload[0]["sha256"],
"8663bab6d124806b9727f89bb4ab9db4cbcc3862f6bbf22024dfa7212aa4ab7d",
)
self.assertEqual(
json[1]["sha256"],
payload[1]["sha256"],
"fcc85fb502bd772aa675a0263b5fa665bccd5d8d93349d1dbc9f0f6394dd37b9",
)
# check md5sum
self.assertEqual(payload[0]["md5sum"], "9af2f8218b150c351ad802c6f3d66abe")
self.assertEqual(payload[1]["md5sum"], "3bf4cfdddae3137d565094635a8ebcc9")

def test_upload_and_list_file_checksum(self):
self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token1.key)
Expand Down Expand Up @@ -365,9 +369,8 @@ def test_upload_and_list_file_versions(self):
response = self.client.get(f"/api/v1/files/{self.project1.id}/")
self.assertTrue(status.is_success(response.status_code))

versions = sorted(
response.json()[0]["versions"], key=lambda k: k["last_modified"]
)
payload = response.json()
versions = sorted(payload[0]["versions"], key=lambda k: k["last_modified"])

self.assertEqual(len(versions), 2)
self.assertNotEqual(versions[0]["last_modified"], versions[1]["last_modified"])
Expand All @@ -380,6 +383,10 @@ def test_upload_and_list_file_versions(self):
versions[1]["sha256"],
"fcc85fb502bd772aa675a0263b5fa665bccd5d8d93349d1dbc9f0f6394dd37b9",
)
self.assertEqual(versions[0]["md5sum"], "9af2f8218b150c351ad802c6f3d66abe")
self.assertEqual(versions[1]["md5sum"], "3bf4cfdddae3137d565094635a8ebcc9")
self.assertEqual(payload[0]["sha256"], versions[1]["sha256"])
self.assertEqual(payload[0]["md5sum"], versions[1]["md5sum"])

self.assertEqual(versions[0]["size"], 13)
self.assertEqual(versions[1]["size"], 13)
Expand Down
4 changes: 3 additions & 1 deletion docker-app/qfieldcloud/core/views/files_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ def get(self, request: Request, projectid: str) -> Response:
sha256sum = metadata["sha256sum"]
else:
sha256sum = metadata["Sha256sum"]
files[version.key]["sha256"] = sha256sum

version_data["sha256"] = sha256sum

Expand All @@ -124,6 +123,9 @@ def get(self, request: Request, projectid: str) -> Response:
files[version.key]["last_modified"] = last_modified
files[version.key]["is_attachment"] = is_attachment

if not skip_metadata:
files[version.key]["sha256"] = sha256sum

files[version.key]["versions"].append(version_data)

result_list = [files[key] for key in files]
Expand Down
Loading

0 comments on commit 69dad79

Please sign in to comment.