From 8bab33c9482b458e015f9a68682327097a91edab Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Tue, 9 Jul 2019 15:35:52 +0000 Subject: [PATCH 01/51] [FIX] core: avoid writting sources during tests Future runbot improvement may share sources between build, meaning that sources will be readonly to avoid any interaction. _touch() was supposed to handle ro filesystem but f7130556 introduced a new test that was not working in this case. Instead of trying to touch file and pseudomocking the result if the filesystem is readonly, this commit add a real patch on getmtime. cherry pick of b295723c999b94420e7ef2d3bddc040983c54f13 closes odoo/odoo#34822 Signed-off-by: Christophe Monniez (moc) --- .../tests/test_assetsbundle.py | 111 +++++++++--------- 1 file changed, 55 insertions(+), 56 deletions(-) diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py index ec5b6078168..99f5c1c46c7 100644 --- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py +++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py @@ -4,8 +4,9 @@ from collections import Counter import datetime import errno -from os import utime +import os import time +from unittest.mock import patch from odoo import api from odoo.addons.base.models.assetsbundle import AssetsBundle @@ -14,29 +15,27 @@ from odoo.modules.module import get_resource_path from odoo.tests import HttpCase from odoo.tests.common import TransactionCase - -def _touch(filepath, asset, t=None): - try: - utime(filepath, (t, t) if t else None) - except OSError as e: - if e.errno in [errno.EPERM, errno.EACCES, errno.EROFS]: - # Permission denied when touching the asset file, possibly read-only filesystem. - # We alter the asset last modified time to simulate a change in the file - now = datetime.datetime.now() - asset.last_modified = now - for sheet in asset.stylesheets: - sheet.last_modified = now - else: - raise +GETMTINE = os.path.getmtime -class TestJavascriptAssetsBundle(TransactionCase): +class FileTouchable(TransactionCase): + def setUp(self): + super(FileTouchable, self).setUp() + self.touches = {} + + def _touch(self, filepath, touch_time=None): + self.touches[filepath] = touch_time or time.time() + return patch('os.path.getmtime', lambda filename: self.touches.get(filename) or GETMTINE(filename)) + + +class TestJavascriptAssetsBundle(FileTouchable): def setUp(self): super(TestJavascriptAssetsBundle, self).setUp() self.jsbundle_xmlid = 'test_assetsbundle.bundle1' self.cssbundle_xmlid = 'test_assetsbundle.bundle2' self.env['res.lang'].load_lang('ar_SY') + def _get_asset(self, xmlid, env=None): env = (env or self.env) files, remains = env['ir.qweb']._get_asset_content(xmlid, env.context) @@ -103,16 +102,16 @@ class TestJavascriptAssetsBundle(TransactionCase): path = get_resource_path('test_assetsbundle', 'static', 'src', 'js', 'test_jsfile1.js') bundle1 = self._get_asset(self.jsbundle_xmlid) - _touch(path, bundle1) - bundle1.js() - last_modified1 = bundle1.last_modified - version1 = bundle1.version - self.assertNotEquals(last_modified0, last_modified1) - self.assertNotEquals(version0, version1) + with self._touch(path): + bundle1.js() + last_modified1 = bundle1.last_modified + version1 = bundle1.version + self.assertNotEquals(last_modified0, last_modified1) + self.assertNotEquals(version0, version1) - # check if the previous attachment is correctly cleaned - self.assertEquals(len(self._any_ira_for_bundle('js')), 1) + # check if the previous attachment is correctly cleaned + self.assertEquals(len(self._any_ira_for_bundle('js')), 1) def test_04_content_invalidation(self): """ Checks that a bundle is invalidated when its content is modified by adding a file to @@ -356,32 +355,32 @@ class TestJavascriptAssetsBundle(TransactionCase): # Note: No lang specific context given while calling _get_asset so it will load assets for en_US path = get_resource_path('test_assetsbundle', 'static', 'src', 'css', 'test_cssfile1.css') ltr_bundle1 = self._get_asset(self.cssbundle_xmlid) - _touch(path, ltr_bundle1) - ltr_bundle1.css() - ltr_last_modified1 = ltr_bundle1.last_modified - ltr_version1 = ltr_bundle1.version - ltr_ira1 = self._any_ira_for_bundle('css') - self.assertNotEquals(ltr_last_modified0, ltr_last_modified1) - self.assertNotEquals(ltr_version0, ltr_version1) + with self._touch(path): + ltr_bundle1.css() + ltr_last_modified1 = ltr_bundle1.last_modified + ltr_version1 = ltr_bundle1.version + ltr_ira1 = self._any_ira_for_bundle('css') + self.assertNotEquals(ltr_last_modified0, ltr_last_modified1) + self.assertNotEquals(ltr_version0, ltr_version1) - rtl_bundle1 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'})) + rtl_bundle1 = self._get_asset(self.cssbundle_xmlid, env=self.env(context={'lang': 'ar_SY'})) - rtl_bundle1.css() - rtl_last_modified1 = rtl_bundle1.last_modified - rtl_version1 = rtl_bundle1.version - rtl_ira1 = self._any_ira_for_bundle('css', lang='ar_SY') - self.assertNotEquals(rtl_last_modified0, rtl_last_modified1) - self.assertNotEquals(rtl_version0, rtl_version1) + rtl_bundle1.css() + rtl_last_modified1 = rtl_bundle1.last_modified + rtl_version1 = rtl_bundle1.version + rtl_ira1 = self._any_ira_for_bundle('css', lang='ar_SY') + self.assertNotEquals(rtl_last_modified0, rtl_last_modified1) + self.assertNotEquals(rtl_version0, rtl_version1) - # Checks rtl and ltr bundles are different - self.assertNotEquals(ltr_ira1.id, rtl_ira1.id) + # Checks rtl and ltr bundles are different + self.assertNotEquals(ltr_ira1.id, rtl_ira1.id) - # check if the previous attachment is correctly cleaned - css_bundles = self.env['ir.attachment'].search([ - ('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css')) - ]) - self.assertEquals(len(css_bundles), 2) + # check if the previous attachment is correctly cleaned + css_bundles = self.env['ir.attachment'].search([ + ('url', '=like', '/web/content/%-%/{0}%.{1}'.format(self.cssbundle_xmlid, 'css')) + ]) + self.assertEquals(len(css_bundles), 2) def test_18_css_bundle_content_invalidation(self): """ Checks that a bundle is invalidated when its content is modified by adding a file to @@ -538,7 +537,7 @@ class TestAssetsBundleInBrowser(HttpCase): ) -class TestAssetsBundleWithIRAMock(TransactionCase): +class TestAssetsBundleWithIRAMock(FileTouchable): def setUp(self): super(TestAssetsBundleWithIRAMock, self).setUp() self.stylebundle_xmlid = 'test_assetsbundle.bundle3' @@ -585,15 +584,15 @@ class TestAssetsBundleWithIRAMock(TransactionCase): path = get_resource_path('test_assetsbundle', 'static', 'src', 'scss', 'test_file1.scss') t = time.time() + 5 asset = self._get_asset() - _touch(path, asset, t=t) - self._bundle(asset, True, True) + with self._touch(path, t): + self._bundle(asset, True, True) - # Because we are in the same transaction since the beginning of the test, the first asset - # created and the second one have the same write_date, but the file's last modified date - # has really been modified. If we do not update the write_date to a posterior date, we are - # not able to reproduce the case where we compile this bundle again without changing - # anything. - self.cr.execute("update ir_attachment set write_date=clock_timestamp() + interval '10 seconds' where id = (select max(id) from ir_attachment)") + # Because we are in the same transaction since the beginning of the test, the first asset + # created and the second one have the same write_date, but the file's last modified date + # has really been modified. If we do not update the write_date to a posterior date, we are + # not able to reproduce the case where we compile this bundle again without changing + # anything. + self.cr.execute("update ir_attachment set write_date=clock_timestamp() + interval '10 seconds' where id = (select max(id) from ir_attachment)") - # Compile a fourth time, without changes - self._bundle(self._get_asset(), False, False) + # Compile a fourth time, without changes + self._bundle(self._get_asset(), False, False) From b2b639b693dd7215e3ee501ac89c35f3a6b88bb5 Mon Sep 17 00:00:00 2001 From: Julien Castiaux Date: Thu, 11 Jul 2019 12:05:54 +0000 Subject: [PATCH 02/51] [FIX] website_crm_partner_assign: use html5 input date over text Create a lead, assign it to a partner who has portal access. Go to that opportunity on the portal, edit any date to set 3-digit year (0-999) and submit the form. Go to the opportunity using the web app, traceback. Dates are only supported starting to 1900 thus many parsing function assume the year to be a 4-digits year. The html form has been changed to use an input type date with a minimum value instead of a plain text input. This ensure a better support by browser (they use their built-in calendar) and allow basic html5 form validation. opw-2033276 closes odoo/odoo#34796 Signed-off-by: Julien Castiaux --- .../static/src/js/crm_partner_assign.js | 4 +++- .../views/website_crm_partner_assign_templates.xml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index e082fa155fd..2f94c8827ba 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -226,7 +226,9 @@ sAnimations.registry.crmPartnerAssign = sAnimations.Class.extend({ _onEditOppConfirm: function (ev) { ev.preventDefault(); ev.stopPropagation(); - this._buttonExec($(ev.currentTarget), this._editOpportunity); + if ($(".edit_opp_form")[0].checkValidity()) { + this._buttonExec($(ev.currentTarget), this._editOpportunity); + } }, /** * @private diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml index 70ff4d891af..9e060b8d04e 100644 --- a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml +++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml @@ -670,7 +670,7 @@
- +
@@ -695,7 +695,7 @@
- +
From 5b41b5c563c75e18f4cf8d2f7dd66d5e273e4341 Mon Sep 17 00:00:00 2001 From: Gorash Date: Tue, 4 Jun 2019 08:15:48 +0000 Subject: [PATCH 03/51] [FIX] web_editor: escape html tag when copy paste code and text content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes odoo/odoo#33859 Signed-off-by: Jérémy Kersten (jke) --- addons/web_editor/static/src/js/wysiwyg/plugin/text.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/text.js b/addons/web_editor/static/src/js/wysiwyg/plugin/text.js index f2834c531f7..9014b367fee 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/text.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/text.js @@ -780,7 +780,10 @@ var TextPlugin = AbstractPlugin.extend({ clipboardData = e.originalEvent.clipboardData.getData('text/plain'); // get that text as an array of text nodes separated by
where needed var allNewlines = /\n/g; - clipboardData = $('

' + clipboardData.replace(allNewlines, '
') + '

').contents().toArray(); + clipboardData = _.str.trim(clipboardData) + .replace(//g, '>') + .replace(allNewlines, '
'); + clipboardData = $('

' + clipboardData + '

').contents().toArray(); } // Delete selection From e7828c362718b40ed74920a1ce3619766fa84ff2 Mon Sep 17 00:00:00 2001 From: Antoine Guenet Date: Thu, 11 Jul 2019 13:20:28 +0000 Subject: [PATCH 04/51] [FIX] web_editor: properly insert character in empty nested list Fix a bug that can be reproduced like so: - Insert list - Type char - ENTER - TAB - Type char The character gets inserted _after_ the `br` instead of replacing it. closes odoo/odoo#34805 Signed-off-by: Pierre Paridans --- .../static/src/js/wysiwyg/plugin/helper.js | 3 ++ .../static/tests/wysiwyg_keyboard_tests.js | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js b/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js index a025d6a0082..1738e6e7b48 100644 --- a/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js +++ b/addons/web_editor/static/src/js/wysiwyg/plugin/helper.js @@ -1885,6 +1885,9 @@ var HelperPlugin = AbstractPlugin.extend({ // Create empty text node to have a range into the node if (range.sc.tagName && !dom.isVoid(range.sc) && !range.sc.childNodes[range.so]) { $(range.sc).append(invisible); + if (invisible.previousSibling && invisible.previousSibling.tagName === "BR") { + invisible.previousSibling.remove(); + } range = this.context.invoke('editor.setRange', invisible, 0); } diff --git a/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js b/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js index e59f8d14539..861b574f9c6 100644 --- a/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js +++ b/addons/web_editor/static/tests/wysiwyg_keyboard_tests.js @@ -811,6 +811,18 @@ var keyboardTestsChar = [{ start: "b:contents(0)->1", }, }, + { + name: "'a' in empty li", + content: '

', + steps: [{ + start: "p->1", + key: 'a', + }], + test: { + content: '
  • a

', + start: "p:contents(0)->1", + }, + }, ]; QUnit.test('Char', function (assert) { @@ -2239,6 +2251,25 @@ var keyboardTestsComplex = [{ start: "p:eq(1)->0", }, }, + // List indent + { + name: "ENTER -> TAB -> a' at end of li > p (split -> indent -> write)", + content: '
  • a

', + steps: [{ + start: "p:contents()[0]->1", + key: 'ENTER', + }, + { + key: 'TAB', + }, + { + key: 'a', + }], + test: { + content: '
  • a

    • a

', + start: "p:eq(1):contents()[0]->1", + }, + }, ]; QUnit.test('Complex', function (assert) { From 458472485fee6a7186c3f6a4295c17f214d48070 Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Thu, 11 Jul 2019 13:33:32 +0000 Subject: [PATCH 05/51] [FIX] website_slides: statistics recompute when toggle website published As a portal user, complete some steps within a course Completing all of them makes the course completion to 100 % Now, in the backend, unpublish one of these steps Before this commit, the completion was not taking into account that a step did not "exist" anymore i.e. has been unpublished, leading to a completion > 100% After this commit, we search only for steps that are published The completion cannot exceed 100% OPW 2033553 closes odoo/odoo#34800 Signed-off-by: Lucas Perais (lpe) --- addons/website_slides/models/slide_channel.py | 5 +++-- addons/website_slides/tests/test_statistics.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index fcb1eaba419..61e2a11716a 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -22,12 +22,13 @@ class ChannelUsersRelation(models.Model): partner_id = fields.Many2one('res.partner', index=True, required=True) partner_email = fields.Char(related='partner_id.email', readonly=True) - @api.depends('channel_id.slide_partner_ids.partner_id', 'channel_id.slide_partner_ids.completed', 'partner_id') + @api.depends('channel_id.slide_partner_ids.partner_id', 'channel_id.slide_partner_ids.completed', 'partner_id', 'channel_id.slide_partner_ids.slide_id.is_published') def _compute_completion(self): read_group_res = self.env['slide.slide.partner'].sudo().read_group( ['&', '&', ('channel_id', 'in', self.mapped('channel_id').ids), ('partner_id', 'in', self.mapped('partner_id').ids), - ('completed', '=', True)], + ('completed', '=', True), + ('slide_id.is_published', '=', True)], ['channel_id', 'partner_id'], groupby=['channel_id', 'partner_id'], lazy=False) mapped_data = dict() diff --git a/addons/website_slides/tests/test_statistics.py b/addons/website_slides/tests/test_statistics.py index bd067ba8216..be10f03a246 100644 --- a/addons/website_slides/tests/test_statistics.py +++ b/addons/website_slides/tests/test_statistics.py @@ -75,6 +75,15 @@ class TestStatistics(common.SlidesCase): self.assertEqual(channel_emp.completion, 100) self.assertTrue(channel_emp.completed) + self.slide_3.is_published = False + self.assertEqual(channel_emp.completion, 100) + self.assertTrue(channel_emp.completed) + + self.slide_3.is_published = True + self.slide_3.active = False + self.assertEqual(channel_emp.completion, 100) + self.assertTrue(channel_emp.completed) + @mute_logger('odoo.models') def test_channel_user_statistics_complete_check_member(self): (self.slide | self.slide_2).write({'is_preview': True}) From baa0ce6d153b945f6baf4986811ba4c946e26868 Mon Sep 17 00:00:00 2001 From: RomainLibert Date: Fri, 12 Jul 2019 13:58:25 +0000 Subject: [PATCH 06/51] [FIX] l10n_it_edi: use a valid vat number for italy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes odoo/odoo#34857 Signed-off-by: Xavier Dollé (xdo) --- addons/l10n_it_edi/data/account_invoice_demo.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/l10n_it_edi/data/account_invoice_demo.xml b/addons/l10n_it_edi/data/account_invoice_demo.xml index c68a3871df5..81cef201c47 100644 --- a/addons/l10n_it_edi/data/account_invoice_demo.xml +++ b/addons/l10n_it_edi/data/account_invoice_demo.xml @@ -3,7 +3,8 @@ - IT00410123456 + + IT00410128888 0123456789987654 RF01 12345 From b67a3bb403b80e98a2b66dcb8cf570d2b156fb68 Mon Sep 17 00:00:00 2001 From: Priyanka Kakadiya Date: Mon, 24 Jun 2019 13:34:12 +0000 Subject: [PATCH 07/51] [FIX] account_payment: fix payment modal issue purpose of this commit is to show modal with proper height before this commit: currently, the modal structure was not correct, so it is displaying modal-content twice in the modal that's why it shows modal with full height. after this commit: correct modal structure to show it properly, so payment modal in the portal view looks like as expected. task-2025869 closes: https://github.com/odoo/odoo/pull/34334 closes odoo/odoo#34334 Signed-off-by: Damien Bouvy (dbo) --- addons/account_payment/views/account_portal_templates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account_payment/views/account_portal_templates.xml b/addons/account_payment/views/account_portal_templates.xml index bdc9c9da488..997791176a4 100644 --- a/addons/account_payment/views/account_portal_templates.xml +++ b/addons/account_payment/views/account_portal_templates.xml @@ -38,7 +38,7 @@