[IMP] web: Clean the export file names

The purpose of this task is to clean up and provide clear export
file names.

Currently, When exporting data:
 - From a pivot view, the file name is 'table'
 - From a list view, the file name is technical name of model

so in this commit, Change the export file name as below:

 - For list view quick export and standard record export,
the filename will be 'model_description(model_technical_name)'

 - For pivot view quick export,
the file name will be 'Pivot(string set on view)(model_technical_name)'
and if string is not set then 'PivotUntitled(model_technical_name)'

In all the cases space and forward slash will be removed.

closes odoo/odoo#64123

Taskid: 2237840
Related: odoo/enterprise#15579
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
This commit is contained in:
Nidhi Patel
2021-02-03 07:14:16 +00:00
committed by Xavier Morel
parent 6595e770f0
commit 892e3d7d25
5 changed files with 73 additions and 17 deletions
+22 -10
View File
@@ -34,7 +34,7 @@ import odoo
import odoo.modules.registry
from odoo.api import call_kw, Environment
from odoo.modules import get_module_path, get_resource_path
from odoo.tools import image_process, topological_sort, html_escape, pycompat, ustr, apply_inheritance_specs, lazy_property, float_repr
from odoo.tools import image_process, topological_sort, html_escape, pycompat, ustr, apply_inheritance_specs, lazy_property, float_repr, osutil
from odoo.tools.mimetypes import guess_mimetype
from odoo.tools.translate import _
from odoo.tools.misc import str2bool, xlsxwriter, file_open
@@ -1884,12 +1884,20 @@ class ExportFormat(object):
""" Provides the format's content type """
raise NotImplementedError()
def filename(self, base):
""" Creates a valid filename for the format (with extension) from the
provided base name (exension-less)
"""
@property
def extension(self):
raise NotImplementedError()
def filename(self, base):
""" Creates a filename *without extension* for the item / format of
model ``base``.
"""
if base not in request.env:
return base
model_description = request.env['ir.model']._get(base).name
return f"{model_description} ({base})"
def from_data(self, fields, rows):
""" Conversion method from Odoo's export data to whatever the
current export class outputs
@@ -1939,9 +1947,11 @@ class ExportFormat(object):
export_data = records.export_data(field_names).get('datas',[])
response_data = self.from_data(columns_headers, export_data)
# TODO: call `clean_filename` directly in `content_disposition`?
return request.make_response(response_data,
headers=[('Content-Disposition',
content_disposition(self.filename(model))),
content_disposition(
osutil.clean_filename(self.filename(model) + self.extension))),
('Content-Type', self.content_type)],
cookies={'fileToken': token})
@@ -1956,8 +1966,9 @@ class CSVExport(ExportFormat, http.Controller):
def content_type(self):
return 'text/csv;charset=utf8'
def filename(self, base):
return base + '.csv'
@property
def extension(self):
return '.csv'
def from_group_data(self, fields, groups):
raise UserError(_("Exporting grouped data to csv is not supported."))
@@ -1991,8 +2002,9 @@ class ExcelExport(ExportFormat, http.Controller):
def content_type(self):
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
def filename(self, base):
return base + '.xlsx'
@property
def extension(self):
return '.xlsx'
def from_group_data(self, fields, groups):
with GroupExportXlsxWriter(fields, groups.count) as xlsx_writer:
+5 -4
View File
@@ -5,9 +5,9 @@ from collections import deque
import io
import json
from odoo import http
from odoo.http import request
from odoo.tools import ustr
from odoo import http, _
from odoo.http import content_disposition, request
from odoo.tools import ustr, osutil
from odoo.tools.misc import xlsxwriter
@@ -102,9 +102,10 @@ class TableExporter(http.Controller):
workbook.close()
xlsx_data = output.getvalue()
filename = osutil.clean_filename(_("Pivot %(title)s (%(model_name)s)", title=jdata['title'], model_name=jdata['model']))
response = request.make_response(xlsx_data,
headers=[('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
('Content-Disposition', 'attachment; filename=table.xlsx')],
('Content-Disposition', content_disposition(filename + '.xlsx'))],
cookies={'fileToken': token})
return response
@@ -140,6 +140,7 @@ odoo.define('web.PivotController', function (require) {
}
const table = this.model.exportData();
table.title = this.title;
table.model = this.modelName;
session.get_file({
url: '/web/pivot/export_xlsx',
data: { data: JSON.stringify(table) },
+1 -1
View File
@@ -5,7 +5,6 @@ from . import _monkeypatches
from . import pycompat
from . import win32
from . import appdirs
from . import osutil
from . import pdf
from . import cloc
from .config import config
@@ -21,3 +20,4 @@ from .xml_utils import *
from .date_utils import *
from .convert import *
from .template_inheritance import *
from . import osutil
+44 -2
View File
@@ -4,15 +4,57 @@
"""
Some functions related to the os and os.path module
"""
from contextlib import contextmanager
import logging
import os
from os.path import join as opj
import re
import tempfile
import zipfile
from contextlib import contextmanager
from os.path import join as opj
from .translate import _
_logger = logging.getLogger(__name__)
WINDOWS_RESERVED = re.compile(r'''
^
# forbidden stems: reserved keywords
(:?CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])
# even with an extension this is recommended against
(:?\..*)?
$
''', flags=re.IGNORECASE | re.VERBOSE)
def clean_filename(name, replacement=''):
""" Strips or replaces possibly problematic or annoying characters our of
the input string, in order to make it a valid filename in most operating
systems (including dropping reserved Windows filenames).
If this results in an empty string, results in "Untitled" (localized).
Allows:
* any alphanumeric character (unicode)
* underscore (_) as that's innocuous
* dot (.) except in leading position to avoid creating dotfiles
* dash (-) except in leading position to avoid annoyance / confusion with
command options
* brackets ([ and ]), while they correspond to shell *character class*
they're a common way to mark / tag files especially on windows
* parenthesis ("(" and ")"), a more natural though less common version of
the former
* space (" ")
:param str name: file name to clean up
:param str replacement:
replacement string to use for sequences of problematic input, by default
an empty string to remove them entirely, each contiguous sequence of
problems is replaced by a single replacement
:rtype: str
"""
if WINDOWS_RESERVED.match(name):
return _("Untitled")
return re.sub(r'[^\w_.()\[\] -]+', replacement, name).lstrip('.-') or _("Untitled")
def listdir(dir, recursive=False):
"""Allow to recursively get the file listing following symlinks, returns