[FIX] website_slides: fix resource download file extension
To reproduce the problem: - ensures python-magic is not installed. It is used to guess the mime type of a file. The code uses an alternate solution if not present and this is that alternate version that's unable to guess the mime type of an xlsx file. - in website_slides, open a course - open a content - go to the "Additional Resources" tab - add an xlsx file - remove the extension of the file in the name - open the public website and open that content - click on the link of the file you just have added - instead of a xlsx, you get a zip file This fix solves the problem by using the extension of the file uploaded if it is valid. Technical note: prior version 16, the mime type stored in the attachment was used to determine the extension of the file when downloading it. From version 16, if the extension is missing in the file name, the system tries to guess it using python-magic if installed and a custom implementation if not: _odoo_guess_mimetype. The custom implementation doesn't support the detection of xlsx files (and other formats) while python-magic supports it. That's why the problem described above only appends while python-magic is not installed. To solve the problem we have added in the download URL the file name as the the name of the resource with the extension of the file name uploaded (extension not added if the name had already the same extension as the file name). Task-3200998 closes odoo/odoo#115273 X-original-commit: 3a7388a69991d968c349133be4ac52745bbac65b Signed-off-by: Stéphane Debauche (std) <std@odoo.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from werkzeug.urls import url_encode
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.mimetypes import get_extension
|
||||
|
||||
|
||||
class SlideResource(models.Model):
|
||||
@@ -48,3 +51,12 @@ class SlideResource(models.Model):
|
||||
for record in self:
|
||||
if record.resource_type != 'file' and record.data:
|
||||
raise ValidationError(_("Resource %(resource_name)s is a link and should not contain a data file", resource_name=record.name))
|
||||
|
||||
def _get_download_url(self):
|
||||
self.ensure_one()
|
||||
extension_file_name = get_extension(self.file_name) if self.file_name else ''
|
||||
file_name = self.name if self.name.endswith(extension_file_name) else self.name + extension_file_name
|
||||
return f'/web/content/slide.slide.resource/{self.id}/data?' + url_encode({
|
||||
'download': 'true',
|
||||
'filename': file_name
|
||||
})
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from psycopg2 import IntegrityError
|
||||
from werkzeug.urls import url_quote
|
||||
|
||||
from odoo.addons.website_slides.tests import common
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tests import HttpCase
|
||||
from odoo.tests.common import users
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
class TestResources(common.SlidesCase):
|
||||
class TestResources(common.SlidesCase, HttpCase):
|
||||
|
||||
@users('user_officer')
|
||||
@mute_logger('odoo')
|
||||
@@ -53,3 +55,34 @@ class TestResources(common.SlidesCase):
|
||||
'resource_type': 'url',
|
||||
'slide_id': self.slide.id,
|
||||
})
|
||||
|
||||
@users('user_officer')
|
||||
def test_download_file_name_extension(self):
|
||||
""" Test the resource download file name extension. """
|
||||
resource_name = 'Test Resource with special character éè!?&"'
|
||||
resource = self.env["slide.slide.resource"].create({
|
||||
'name': resource_name,
|
||||
'file_name': 'test.png',
|
||||
'resource_type': 'file',
|
||||
# A file for which _odoo_guess_mimetype and python_magic can detect the mime type: a png file
|
||||
'data': 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAACXBIWXMAAC4jAA'
|
||||
'AuIwF4pT92AAAAD0lEQVQIHQEEAPv/AIdaewLIAV0IjhGPAAAAAElFTkSuQmCC',
|
||||
'slide_id': self.slide.id,
|
||||
})
|
||||
self.authenticate(self.env.user.login, self.env.user.login)
|
||||
|
||||
for name, file_name, expected_download_name in (
|
||||
# The extension is determined from the file name extension
|
||||
(resource_name, 'test.xlsx', f'{resource_name}.xlsx'),
|
||||
(f'{resource_name}.xlsx', 'test.xlsx', f'{resource_name}.xlsx'),
|
||||
(f'{resource_name}.txt', 'test.xlsx', f'{resource_name}.txt.xlsx'),
|
||||
# .unknown_long_ext is considered as invalid so no extension is appended to the name
|
||||
(f'{resource_name}.txt', 'test.unknown_long_ext', f'{resource_name}.txt'),
|
||||
# No valid extension, the extension is detected at download time from the file content
|
||||
(resource_name, 'test.unknown_long_ext', f'{resource_name}.png'),
|
||||
(resource_name, 'test', f'{resource_name}.png'),
|
||||
):
|
||||
resource.write({'name': name, 'file_name': file_name})
|
||||
with self.subTest(name=name, file_name=file_name, expected_download_name=expected_download_name):
|
||||
self.assertIn(f"filename*=UTF-8''{url_quote(expected_download_name)}",
|
||||
self.url_open(resource._get_download_url()).headers['Content-Disposition'])
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
</t>
|
||||
<div class="o_wslides_js_course_join ps-4" t-if="aside_slide._has_additional_resources()">
|
||||
<li t-foreach="aside_slide.slide_resource_ids" t-as="resource" t-if="resource.resource_type == 'file'">
|
||||
<a t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true" class="text-decoration-none small">
|
||||
<a t-att-href="resource._get_download_url()" class="text-decoration-none small">
|
||||
<i class="fa fa-download me-1"/><span t-field="resource.name"/>
|
||||
</a>
|
||||
</li>
|
||||
@@ -439,7 +439,7 @@
|
||||
</span>
|
||||
<div class="text-muted me-auto border-start ps-3">
|
||||
<t t-foreach="slide.slide_resource_ids" t-as="resource" t-if="resource.resource_type == 'file' and resource.data">
|
||||
<a t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true" t-esc="resource.name"/><br />
|
||||
<a t-att-href="resource._get_download_url()" t-esc="resource.name"/><br />
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
<div class="o_wslides_js_course_join ps-0" t-if="slide._has_additional_resources('file')">
|
||||
<t t-set="resources" t-value="slide.slide_resource_ids.filtered(lambda res: res.resource_type == 'file')"/>
|
||||
<li t-foreach="resources" t-as="resource" class="mb-1">
|
||||
<a class="o_wslides_fs_slide_link" t-attf-href="/web/content/slide.slide.resource/#{resource.id}/data?download=true">
|
||||
<a class="o_wslides_fs_slide_link" t-att-href="resource._get_download_url()">
|
||||
<i class="fa fa-download me-2"/><span t-esc="resource.name"/>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user