mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-25 07:27:39 -05:00
[FIX] upstream patch for base
This commit is contained in:
1 parent
b3b3296895
commit
ace588a952
6 files changed
+62
-8
No files matched your search
@@ -219,6 +219,18 @@ class AssetsBundle(object):
|
||||
**self._get_asset_url_values(id=id, unique=unique, extra=extra, name=name, sep=sep, type=type)
|
||||
)
|
||||
|
||||
def _unlink_attachments(self, attachments):
|
||||
""" Unlinks attachments without actually calling unlink, so that the ORM cache is not cleared.
|
||||
|
||||
Specifically, if an attachment is generated while a view is rendered, clearing the ORM cache
|
||||
could unload fields loaded with a sudo(), and expected to be readable by the view.
|
||||
Such a view would be website.layout when main_object is an ir.ui.view.
|
||||
"""
|
||||
to_delete = set(attach.store_fname for attach in attachments if attach.store_fname)
|
||||
self.env.cr.execute(f"DELETE FROM {attachments._table} WHERE id IN %s", [tuple(attachments.ids)])
|
||||
for file_path in to_delete:
|
||||
attachments._file_delete(file_path)
|
||||
|
||||
def clean_attachments(self, type):
|
||||
""" Takes care of deleting any outdated ir.attachment records associated to a bundle before
|
||||
saving a fresh one.
|
||||
@@ -244,7 +256,7 @@ class AssetsBundle(object):
|
||||
# avoid to invalidate cache if it's already empty (mainly useful for test)
|
||||
|
||||
if attachments:
|
||||
attachments.unlink()
|
||||
self._unlink_attachments(attachments)
|
||||
# force bundle invalidation on other workers
|
||||
self.env['ir.qweb'].clear_caches()
|
||||
|
||||
@@ -478,7 +490,7 @@ class AssetsBundle(object):
|
||||
compiled = self.run_rtlcss(compiled)
|
||||
|
||||
if not self.css_errors and old_attachments:
|
||||
old_attachments.unlink()
|
||||
self._unlink_attachments(old_attachments)
|
||||
old_attachments = None
|
||||
|
||||
fragments = self.rx_css_split.split(compiled)
|
||||
|
||||
@@ -128,6 +128,7 @@ class IrAttachment(models.Model):
|
||||
|
||||
def _mark_for_gc(self, fname):
|
||||
""" Add ``fname`` in a checklist for the filestore garbage collection. """
|
||||
fname = re.sub('[.]', '', fname).strip('/\\')
|
||||
# we use a spooldir: add an empty file in the subdirectory 'checklist'
|
||||
full_path = os.path.join(self._full_path('checklist'), fname)
|
||||
if not os.path.exists(full_path):
|
||||
@@ -564,7 +565,9 @@ class IrAttachment(models.Model):
|
||||
|
||||
def copy(self, default=None):
|
||||
self.check('write')
|
||||
default = dict(default or {}, datas=self.datas)
|
||||
if not (default or {}).keys() & {'datas', 'db_datas', 'raw'}:
|
||||
# ensure the content is kept and recomputes checksum/store_fname
|
||||
default = dict(default or {}, raw=self.raw)
|
||||
return super(IrAttachment, self).copy(default)
|
||||
|
||||
def unlink(self):
|
||||
|
||||
@@ -195,6 +195,9 @@ class IrMailServer(models.Model):
|
||||
elif not host:
|
||||
mail_server = self.sudo().search([], order='sequence', limit=1)
|
||||
|
||||
if not mail_server:
|
||||
mail_server = self.env['ir.mail_server']
|
||||
|
||||
if mail_server:
|
||||
smtp_server = mail_server.smtp_host
|
||||
smtp_port = mail_server.smtp_port
|
||||
@@ -243,7 +246,7 @@ class IrMailServer(models.Model):
|
||||
local, at, domain = smtp_user.rpartition('@')
|
||||
if at:
|
||||
smtp_user = local + at + idna.encode(domain).decode('ascii')
|
||||
connection.login(smtp_user, smtp_password or '')
|
||||
mail_server._smtp_login(connection, smtp_user, smtp_password or '')
|
||||
|
||||
# Some methods of SMTP don't check whether EHLO/HELO was sent.
|
||||
# Anyway, as it may have been sent by login(), all subsequent usages should consider this command as sent.
|
||||
@@ -251,6 +254,18 @@ class IrMailServer(models.Model):
|
||||
|
||||
return connection
|
||||
|
||||
def _smtp_login(self, connection, smtp_user, smtp_password):
|
||||
"""Authenticate the SMTP connection.
|
||||
|
||||
Can be overridden in other module for different authentication methods.Can be
|
||||
called on the model itself or on a singleton.
|
||||
|
||||
:param connection: The SMTP connection to authenticate
|
||||
:param smtp_user: The user to used for the authentication
|
||||
:param smtp_password: The password to used for the authentication
|
||||
"""
|
||||
connection.login(smtp_user, smtp_password)
|
||||
|
||||
def build_email(self, email_from, email_to, subject, body, email_cc=None, email_bcc=None, reply_to=False,
|
||||
attachments=None, message_id=None, references=None, object_id=False, subtype='plain', headers=None,
|
||||
body_alternative=None, subtype_alternative='plain'):
|
||||
|
||||
@@ -963,7 +963,6 @@ class Module(models.Model):
|
||||
[('id', 'not in', excluded_category_ids)],
|
||||
])
|
||||
|
||||
Module = self.env['ir.module.module']
|
||||
records = self.env['ir.module.category'].search_read(domain, ['display_name'], order="sequence")
|
||||
|
||||
values_range = OrderedDict()
|
||||
@@ -976,7 +975,7 @@ class Module(models.Model):
|
||||
kwargs.get('filter_domain', []),
|
||||
[('category_id', 'child_of', record_id), ('category_id', 'not in', excluded_category_ids)]
|
||||
])
|
||||
record['__count'] = Module.search_count(model_domain)
|
||||
record['__count'] = self.env['ir.module.module'].search_count(model_domain)
|
||||
values_range[record_id] = record
|
||||
|
||||
return {
|
||||
|
||||
@@ -999,6 +999,10 @@ class Users(models.Model):
|
||||
return 'base/static/img/user-slash.png'
|
||||
return super()._get_placeholder_filename(field=field)
|
||||
|
||||
def _mfa_type(self):
|
||||
""" If an MFA method is enabled, returns its type as a string. """
|
||||
return
|
||||
|
||||
def _mfa_url(self):
|
||||
""" If an MFA method is enabled, returns the URL for its second step. """
|
||||
return
|
||||
|
||||
@@ -24,11 +24,12 @@ class TestIrAttachment(TransactionCase):
|
||||
# Blob1
|
||||
self.blob1 = b'blob1'
|
||||
self.blob1_b64 = base64.b64encode(self.blob1)
|
||||
blob1_hash = hashlib.sha1(self.blob1).hexdigest()
|
||||
self.blob1_fname = blob1_hash[:HASH_SPLIT] + '/' + blob1_hash
|
||||
self.blob1_hash = hashlib.sha1(self.blob1).hexdigest()
|
||||
self.blob1_fname = self.blob1_hash[:HASH_SPLIT] + '/' + self.blob1_hash
|
||||
|
||||
# Blob2
|
||||
self.blob2 = b'blob2'
|
||||
self.blob2_b64 = base64.b64encode(self.blob2)
|
||||
|
||||
def assertApproximately(self, value, expectedSize, delta=1):
|
||||
# we don't used bin_size in context, because on write, the cached value is the data and not
|
||||
@@ -239,3 +240,23 @@ class TestIrAttachment(TransactionCase):
|
||||
self.env['ir.config_parameter'].set_param('base.image_autoresize_max_px', '0')
|
||||
attach.raw = img_bin
|
||||
self.assertApproximately(attach.raw, fullsize)
|
||||
|
||||
def test_11_copy(self):
|
||||
"""
|
||||
Copying an attachment preserves the data
|
||||
"""
|
||||
document = self.Attachment.create({'name': 'document', 'datas': self.blob2_b64})
|
||||
document2 = document.copy({'name': "document (copy)"})
|
||||
self.assertEqual(document2.name, "document (copy)")
|
||||
self.assertEqual(document2.datas, document.datas)
|
||||
self.assertEqual(document2.db_datas, document.db_datas)
|
||||
self.assertEqual(document2.store_fname, document.store_fname)
|
||||
self.assertEqual(document2.checksum, document.checksum)
|
||||
|
||||
document3 = document.copy({'datas': self.blob1_b64})
|
||||
self.assertEqual(document3.datas, self.blob1_b64)
|
||||
self.assertEqual(document3.raw, self.blob1)
|
||||
self.assertTrue(self.filestore) # no data in db but has a store_fname
|
||||
self.assertEqual(document3.db_datas, False)
|
||||
self.assertEqual(document3.store_fname, self.blob1_fname)
|
||||
self.assertEqual(document3.checksum, self.blob1_hash)
|
||||
Reference in new issue
Block a user