(backend) expose url_preview on item object

We want to distinguish the url and the url_preview. The url_preview
property will be provided only if the mimetype is listed in the
ITEM_PREVIEWABLE_MIME_TYPES settings. Also an other route is added to
nginx forcing the content-disposition with the value attachment for
download url.
This commit is contained in:
Manuel Raynaud
2025-09-27 09:47:24 +02:00
parent 886651810b
commit 47e97a5a39
14 changed files with 261 additions and 4 deletions
+2 -1
View File
@@ -10,7 +10,8 @@ and this project adheres to
### Added
- ✨(back) create wopi applcation #2
- ✨(backend) create wopi applcation #2
- ✨(backend) expose url_preview on item object
### Changed
@@ -17,6 +17,25 @@ server {
proxy_set_header X-Amz-Date $authDate;
proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
# Get resource from Minio
proxy_pass http://minio:9000/drive-media-storage/;
proxy_set_header Host minio:9000;
add_header Content-Disposition "attachment";
}
# Proxy auth for media
location /media/preview/ {
# Auth request configuration
auth_request /media-auth;
auth_request_set $authHeader $upstream_http_authorization;
auth_request_set $authDate $upstream_http_x_amz_date;
auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
# Pass specific headers from the auth response
proxy_set_header Authorization $authHeader;
proxy_set_header X-Amz-Date $authDate;
proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
# Get resource from Minio
proxy_pass http://minio:9000/drive-media-storage/;
proxy_set_header Host minio:9000;
+16
View File
@@ -132,6 +132,7 @@ class ListItemSerializer(serializers.ModelSerializer):
nb_accesses = serializers.IntegerField(read_only=True)
user_roles = serializers.SerializerMethodField()
url = serializers.SerializerMethodField()
url_preview = serializers.SerializerMethodField()
creator = UserLiteSerializer(read_only=True)
hard_delete_at = serializers.SerializerMethodField(read_only=True)
is_wopi_supported = serializers.SerializerMethodField()
@@ -157,6 +158,7 @@ class ListItemSerializer(serializers.ModelSerializer):
"type",
"upload_state",
"url",
"url_preview",
"filename",
"mimetype",
"main_workspace",
@@ -184,6 +186,7 @@ class ListItemSerializer(serializers.ModelSerializer):
"type",
"upload_state",
"url",
"url_preview",
"mimetype",
"main_workspace",
"size",
@@ -228,6 +231,17 @@ class ListItemSerializer(serializers.ModelSerializer):
return f"{settings.MEDIA_BASE_URL}{settings.MEDIA_URL}{quote(item.file_key)}"
def get_url_preview(self, item):
"""Return the URL of the item."""
if (
item.type != models.ItemTypeChoices.FILE
or item.upload_state == models.ItemUploadStateChoices.PENDING
or item.filename is None
or not utils.is_previewable_item(item)
):
return None
return f"{settings.MEDIA_BASE_URL}{settings.MEDIA_URL_PREVIEW}{quote(item.file_key)}"
def get_hard_delete_at(self, item):
"""Return the hard delete date of the item."""
if item.deleted_at is None:
@@ -279,6 +293,7 @@ class ItemSerializer(ListItemSerializer):
"type",
"upload_state",
"url",
"url_preview",
"filename",
"mimetype",
"main_workspace",
@@ -304,6 +319,7 @@ class ItemSerializer(ListItemSerializer):
"type",
"upload_state",
"url",
"url_preview",
"mimetype",
"main_workspace",
"size",
+16
View File
@@ -133,6 +133,22 @@ def generate_upload_policy(item):
return policy
def is_previewable_item(item):
"""
Check if a mime type is previewable.
"""
if item.mimetype is None:
return False
for allowed in settings.ITEM_PREVIEWABLE_MIME_TYPES:
if allowed.endswith("/"):
if item.mimetype.startswith(allowed):
return True
elif item.mimetype == allowed:
return True
return False
def get_item_file_head_object(item):
"""
Get the head object of an item file.
+5 -1
View File
@@ -49,7 +49,7 @@ UUID_REGEX = (
)
FILE_EXT_REGEX = '[^.\\/:*?&"<>|\r\n]+'
MEDIA_STORAGE_URL_PATTERN = re.compile(
f"{settings.MEDIA_URL:s}"
f"{settings.MEDIA_URL:s}(?P<preview>preview/)?"
f"(?P<key>{ITEM_FOLDER:s}/(?P<pk>{UUID_REGEX:s})/.*{FILE_EXT_REGEX:s})$"
)
@@ -1183,6 +1183,10 @@ class ItemViewSet(
logger.debug("Item '%s' is not ready", item.id)
raise drf.exceptions.PermissionDenied()
if url_params.get("preview") and not utils.is_previewable_item(item):
logger.debug("Item '%s' is not previewable", item.id)
raise drf.exceptions.PermissionDenied()
# Generate S3 authorization headers using the extracted URL parameters
request = utils.generate_s3_authorization_headers(f"{url_params.get('key'):s}")
@@ -57,6 +57,7 @@ def test_api_items_children_list_anonymous_public_standalone():
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -91,6 +92,7 @@ def test_api_items_children_list_anonymous_public_standalone():
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -129,6 +131,7 @@ def test_api_items_children_list_anonymous_public_parent():
child2.upload_state = models.ItemUploadStateChoices.READY
child2.filename = "logo.png"
child2.mimetype = "image/png"
child2.save()
response = APIClient().get(f"/api/v1.0/items/{item.id!s}/children/")
@@ -162,6 +165,7 @@ def test_api_items_children_list_anonymous_public_parent():
"type": models.ItemTypeChoices.FILE,
"upload_state": models.ItemUploadStateChoices.PENDING,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -194,7 +198,8 @@ def test_api_items_children_list_anonymous_public_parent():
"type": models.ItemTypeChoices.FILE,
"upload_state": models.ItemUploadStateChoices.READY,
"url": f"http://localhost:8083/media/item/{child2.id!s}/logo.png",
"mimetype": None,
"url_preview": f"http://localhost:8083/media/preview/item/{child2.id!s}/logo.png",
"mimetype": "image/png",
"main_workspace": False,
"filename": child2.filename,
"size": None,
@@ -280,6 +285,7 @@ def test_api_items_children_list_authenticated_unrelated_public_or_authenticated
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -314,6 +320,7 @@ def test_api_items_children_list_authenticated_unrelated_public_or_authenticated
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -385,6 +392,7 @@ def test_api_items_children_list_authenticated_public_or_authenticated_parent(
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -419,6 +427,7 @@ def test_api_items_children_list_authenticated_public_or_authenticated_parent(
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -515,6 +524,7 @@ def test_api_items_children_list_authenticated_related_direct():
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -549,6 +559,7 @@ def test_api_items_children_list_authenticated_related_direct():
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -621,6 +632,7 @@ def test_api_items_children_list_authenticated_related_parent():
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -655,6 +667,7 @@ def test_api_items_children_list_authenticated_related_parent():
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -790,6 +803,7 @@ def test_api_items_children_list_authenticated_related_team_members(
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -824,6 +838,7 @@ def test_api_items_children_list_authenticated_related_team_members(
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -892,6 +907,7 @@ def test_api_items_children_list_filter_type():
if child1.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child1.filename,
@@ -939,6 +955,7 @@ def test_api_items_children_list_filter_type():
if child2.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": child2.filename,
@@ -86,6 +86,7 @@ def test_api_item_favorite_list_authenticated_with_favorite():
"updated_at": item.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": item.upload_state,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": ["reader"],
"main_workspace": False,
@@ -67,6 +67,7 @@ def test_api_items_list_format():
item2.upload_state = models.ItemUploadStateChoices.READY
item2.filename = "logo.png"
item2.mimetype = "image/png"
item2.save()
item3 = user.get_main_workspace()
@@ -106,7 +107,8 @@ def test_api_items_list_format():
"type": models.ItemTypeChoices.FILE,
"upload_state": models.ItemUploadStateChoices.READY,
"url": f"http://localhost:8083/media/item/{item2.id!s}/logo.png",
"mimetype": None,
"url_preview": f"http://localhost:8083/media/preview/item/{item2.id!s}/logo.png",
"mimetype": "image/png",
"main_workspace": False,
"filename": item2.filename,
"size": None,
@@ -138,6 +140,7 @@ def test_api_items_list_format():
"type": models.ItemTypeChoices.FOLDER,
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -170,6 +173,7 @@ def test_api_items_list_format():
"type": models.ItemTypeChoices.FOLDER,
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": True,
"filename": item3.filename,
@@ -51,6 +51,7 @@ def test_api_items_retrieve_anonymous_public_standalone():
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -104,6 +105,7 @@ def test_api_items_retrieve_anonymous_public_parent():
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -202,6 +204,7 @@ def test_api_items_retrieve_authenticated_unrelated_public_or_authenticated(reac
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -261,6 +264,7 @@ def test_api_items_retrieve_authenticated_public_or_authenticated_parent(reach):
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -398,6 +402,7 @@ def test_api_items_retrieve_authenticated_related_direct():
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -459,6 +464,7 @@ def test_api_items_retrieve_authenticated_related_parent():
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -638,6 +644,7 @@ def test_api_items_retrieve_authenticated_related_team_members(
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -711,6 +718,7 @@ def test_api_items_retrieve_authenticated_related_team_administrators(
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -784,6 +792,7 @@ def test_api_items_retrieve_authenticated_related_team_owners(
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -1149,6 +1158,7 @@ def test_api_items_retrieve_file_with_url_property(upload_state):
"type": models.ItemTypeChoices.FILE,
"upload_state": upload_state,
"url": f"http://localhost:8083/media/item/{item.id!s}/logo.png",
"url_preview": f"http://localhost:8083/media/preview/item/{item.id!s}/logo.png",
"mimetype": "image/png",
"main_workspace": False,
"filename": item.filename,
@@ -1160,6 +1170,74 @@ def test_api_items_retrieve_file_with_url_property(upload_state):
}
@pytest.mark.parametrize(
"upload_state",
[
models.ItemUploadStateChoices.READY,
models.ItemUploadStateChoices.ANALYZING,
models.ItemUploadStateChoices.FILE_TOO_LARGE_TO_ANALYZE,
models.ItemUploadStateChoices.SUSPICIOUS,
],
)
def test_api_items_retrieve_file_with_url_property_non_previewable(upload_state):
"""
The `url` property should not be none if the item is not pending but the
url preview should.
"""
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
item = factories.ItemFactory(
creator=user,
type=models.ItemTypeChoices.FILE,
link_reach="public",
update_upload_state=upload_state,
filename="document.odt",
mimetype="application/vnd.oasis.opendocument.text",
size=8,
users=[(user, models.RoleChoices.OWNER)],
)
response = client.get(f"/api/v1.0/items/{item.id!s}/")
assert response.status_code == 200
assert response.json() == {
"id": str(item.id),
"abilities": item.get_abilities(user),
"created_at": item.created_at.isoformat().replace("+00:00", "Z"),
"creator": {
"id": str(item.creator.id),
"full_name": item.creator.full_name,
"short_name": item.creator.short_name,
},
"depth": 1,
"is_favorite": False,
"link_reach": "public",
"link_role": item.link_role,
"nb_accesses": 1,
"numchild": 0,
"numchild_folder": 0,
"path": str(item.path),
"title": item.title,
"updated_at": item.updated_at.isoformat().replace("+00:00", "Z"),
"user_roles": [models.RoleChoices.OWNER],
"type": models.ItemTypeChoices.FILE,
"upload_state": upload_state,
"url": f"http://localhost:8083/media/item/{item.id!s}/document.odt",
"url_preview": None,
"mimetype": "application/vnd.oasis.opendocument.text",
"main_workspace": False,
"filename": item.filename,
"size": 8,
"description": None,
"deleted_at": None,
"hard_delete_at": None,
"is_wopi_supported": False,
}
def test_api_items_retrieve_file_with_url_property_with_spaces():
"""
The `url` property should have white spaces encoded.
@@ -1206,6 +1284,10 @@ def test_api_items_retrieve_file_with_url_property_with_spaces():
"type": models.ItemTypeChoices.FILE,
"upload_state": models.ItemUploadStateChoices.READY,
"url": f"http://localhost:8083/media/item/{item.id!s}/logo%20with%20spaces.png",
"url_preview": (
f"http://localhost:8083/media/preview/item/{item.id!s}/"
"logo%20with%20spaces.png"
),
"mimetype": "image/png",
"main_workspace": False,
"filename": item.filename,
@@ -1344,6 +1426,7 @@ def test_api_items_retrieve_file_analysing_not_creator():
"type": models.ItemTypeChoices.FILE,
"upload_state": models.ItemUploadStateChoices.ANALYZING,
"url": f"http://localhost:8083/media/item/{item.id!s}/logo.png",
"url_preview": f"http://localhost:8083/media/preview/item/{item.id!s}/logo.png",
"mimetype": "image/png",
"main_workspace": False,
"filename": item.filename,
@@ -82,6 +82,7 @@ def test_api_items_search_authenticated_without_filters():
.replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": ["owner"],
},
{
@@ -115,6 +116,7 @@ def test_api_items_search_authenticated_without_filters():
"updated_at": top_parent.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
},
{
@@ -175,6 +177,7 @@ def test_api_items_search_authenticated_without_filters():
),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
}
],
@@ -185,6 +188,7 @@ def test_api_items_search_authenticated_without_filters():
"updated_at": parent.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
},
{
@@ -245,6 +249,7 @@ def test_api_items_search_authenticated_without_filters():
),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
},
{
@@ -277,6 +282,7 @@ def test_api_items_search_authenticated_without_filters():
"updated_at": parent.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
},
],
@@ -287,6 +293,7 @@ def test_api_items_search_authenticated_without_filters():
"updated_at": children.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": "pending",
"url": None,
"url_preview": None,
"user_roles": [top_parent_access.role],
},
]
@@ -86,6 +86,7 @@ def test_api_items_trashbin_format(settings):
if item.type == models.ItemTypeChoices.FILE
else None,
"url": None,
"url_preview": None,
"mimetype": None,
"main_workspace": False,
"filename": item.filename,
@@ -161,6 +161,7 @@ def test_items_api_anonymous_to_a_public_tree_structure():
"updated_at": level2_1.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -194,6 +195,7 @@ def test_items_api_anonymous_to_a_public_tree_structure():
"updated_at": level2_2.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -225,6 +227,7 @@ def test_items_api_anonymous_to_a_public_tree_structure():
"updated_at": level1_2.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -374,6 +377,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level2_1.item.get_roles(user)),
"main_workspace": False,
@@ -417,6 +421,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level3_1.item.get_roles(user)),
"main_workspace": False,
@@ -452,6 +457,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level2_2.item.get_roles(user)),
"main_workspace": False,
@@ -487,6 +493,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level1_1.item.get_roles(user)),
"main_workspace": False,
@@ -524,6 +531,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level1_2.item.get_roles(user)),
"main_workspace": False,
@@ -561,6 +569,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(level1_3.item.get_roles(user)),
"main_workspace": False,
@@ -592,6 +601,7 @@ def test_items_api_tree_authenticated_direct_access(django_assert_num_queries):
"updated_at": root.item.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": list(root.item.get_roles(user)),
"main_workspace": True,
@@ -706,6 +716,7 @@ def test_api_items_tree_authenticated_with_access_authenticated():
"updated_at": level1_1.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -740,6 +751,7 @@ def test_api_items_tree_authenticated_with_access_authenticated():
"updated_at": level2_1.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -773,6 +785,7 @@ def test_api_items_tree_authenticated_with_access_authenticated():
"updated_at": level2_2.updated_at.isoformat().replace("+00:00", "Z"),
"upload_state": None,
"url": None,
"url_preview": None,
"mimetype": None,
"user_roles": [],
"main_workspace": False,
@@ -0,0 +1,63 @@
"""Test utils.is_previewable_item"""
import pytest
from core import factories, models
from core.api import utils
pytestmark = pytest.mark.django_db
def test_is_previewable_item_returns_false_when_mimetype_is_none():
"""Test is_previewable_item returns False when mimetype is None"""
item = factories.ItemFactory(
type=models.ItemTypeChoices.FILE,
filename="file.bin",
mimetype=None,
)
assert utils.is_previewable_item(item) is False
def test_is_previewable_item_returns_true_for_prefix_allowed_type_image_png():
"""Test is_previewable_item returns True for prefix allowed type image/png"""
item = factories.ItemFactory(
type=models.ItemTypeChoices.FILE,
filename="image.png",
mimetype="image/png",
)
assert utils.is_previewable_item(item) is True
def test_is_previewable_item_returns_true_for_exact_allowed_type_pdf():
"""Test is_previewable_item returns True for exact allowed type application/pdf"""
item = factories.ItemFactory(
type=models.ItemTypeChoices.FILE,
filename="document.pdf",
mimetype="application/pdf",
)
assert utils.is_previewable_item(item) is True
def test_is_previewable_item_returns_false_for_unallowed_type_json():
"""Test is_previewable_item returns False for unallowed type application/json"""
item = factories.ItemFactory(
type=models.ItemTypeChoices.FILE,
filename="data.json",
mimetype="application/json",
)
assert utils.is_previewable_item(item) is False
def test_is_previewable_item_returns_false_for_exact_mismatch_pdfx():
"""Test is_previewable_item returns False for exact mismatch application/pdfx"""
item = factories.ItemFactory(
type=models.ItemTypeChoices.FILE,
filename="document.pdfx",
mimetype="application/pdfx",
)
assert utils.is_previewable_item(item) is False
+12
View File
@@ -102,6 +102,7 @@ class Base(Configuration):
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(DATA_DIR, "static")
MEDIA_URL = "/media/"
MEDIA_URL_PREVIEW = "/media/preview/"
MEDIA_ROOT = os.path.join(DATA_DIR, "media")
MEDIA_BASE_URL = values.Value(
None, environ_name="MEDIA_BASE_URL", environ_prefix=None
@@ -173,6 +174,17 @@ class Base(Configuration):
environ_prefix=None,
)
ITEM_PREVIEWABLE_MIME_TYPES = values.ListValue(
[
"image/",
"video/",
"audio/",
"application/pdf",
],
environ_name="ITEM_PREVIEWABLE_MIME_TYPES",
environ_prefix=None,
)
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/