Skip to content

Commit

Permalink
feat: set canonical header for file
Browse files Browse the repository at this point in the history
  • Loading branch information
mamico committed Oct 28, 2024
1 parent 436edad commit d72e456
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
13 changes: 11 additions & 2 deletions plone/namedfile/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ def handle_request_range(self, file):
except ValueError:
return default

def get_canonical(self, file):
filename = getattr(file, "filename", None)
if filename is None:
return f"{self.context.absolute_url()}/@@download/{self.fieldname}"
else:
return f"{self.context.absolute_url()}/@@download/{self.fieldname}/filename"

def set_headers(self, file):
# With filename None, set_headers will not add the download headers.
if not self.filename:
Expand All @@ -135,7 +142,8 @@ def set_headers(self, file):
self.filename = self.fieldname
if self.filename is None:
self.filename = "file.ext"
set_headers(file, self.request.response, filename=self.filename)
canonical = self.get_canonical(file)
set_headers(file, self.request.response, filename=self.filename, canonical=canonical)

def _getFile(self):
if not self.fieldname:
Expand Down Expand Up @@ -185,4 +193,5 @@ def set_headers(self, file):
if mimetype not in self.allowed_inline_mimetypes:
# Let the Download view handle this.
return super().set_headers(file)
set_headers(file, self.request.response)
canonical = self.get_canonical(file)
set_headers(file, self.request.response, canonical=canonical)
15 changes: 15 additions & 0 deletions plone/namedfile/tests/test_display_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ def get_disposition_header(browser):
return browser.headers.get(name, None)


def get_canonical_header(browser):
name = "Link"
for header, value in browser.headers.items():
if header == name or header == name.lower():
if 'rel="canonical"' in map(str.strip, value.split(";")):
return value
return None


def custom_available_sizes():
# Define available image scales.
return {"custom": (10, 10)}
Expand Down Expand Up @@ -96,12 +105,16 @@ def assert_download_works(self, base_url):
self.assertIsNotNone(header)
self.assertIn("attachment", header)
self.assertIn("filename", header)
header = get_canonical_header(browser)
self.assertTrue(header.startswith(f"<{base_url}/@@download/{self.field_name}>"))

def assert_display_inline_works(self, base_url):
# Test that displaying this file inline works.
browser = self.get_anon_browser()
browser.open(base_url + f"/@@display-file/{self.field_name}")
self.assertIsNone(get_disposition_header(browser))
header = get_canonical_header(browser)
self.assertTrue(header.startswith(f"<{base_url}/@@download/{self.field_name}>"))

def assert_display_inline_is_download(self, base_url):
# Test that displaying this file inline turns into a download.
Expand All @@ -111,6 +124,8 @@ def assert_display_inline_is_download(self, base_url):
self.assertIsNotNone(header)
self.assertIn("attachment", header)
self.assertIn("filename", header)
header = get_canonical_header(browser)
self.assertTrue(header.startswith(f"<{base_url}/@@download/{self.field_name}>"))

def assert_scale_view_works(self, base_url):
# Test that accessing a scale view shows the image inline.
Expand Down
4 changes: 3 additions & 1 deletion plone/namedfile/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def get_contenttype(file=None, filename=None, default="application/octet-stream"
return default


def set_headers(file, response, filename=None):
def set_headers(file, response, filename=None, canonical=None):
"""Set response headers for the given file. If filename is given, set
the Content-Disposition to attachment.
"""
Expand All @@ -149,6 +149,8 @@ def set_headers(file, response, filename=None):
"Content-Disposition", f"attachment; filename*=UTF-8''{filename}"
)

if canonical is not None:
response.setHeader("Link", f'<{canonical}>; rel="canonical"')

def stream_data(file, start=0, end=None):
"""Return the given file as a stream if possible."""
Expand Down

0 comments on commit d72e456

Please sign in to comment.