From 4d8f43aab2acd43852b71ec1a96d03fb6eb6ce64 Mon Sep 17 00:00:00 2001 From: Parthiv Patel Date: Thu, 25 Nov 2021 09:36:15 +0000 Subject: [PATCH] [PATCH] Upstream patch - 25112021 --- addons/hr_fleet/models/employee.py | 3 +- addons/link_tracker/models/link_tracker.py | 2 +- .../static/src/components/message/message.js | 32 +++++++- .../form_renderer/form_renderer_tests.js | 75 +++++++++++++++++++ addons/mass_mailing/models/mailing.py | 2 +- addons/mrp/models/mrp_production.py | 4 +- addons/project/models/project.py | 2 +- .../models/purchase.py | 3 +- addons/purchase_stock/models/stock.py | 2 +- addons/sale_coupon/models/coupon_program.py | 2 +- .../sale_project/views/sale_order_views.xml | 2 +- addons/sale_stock/models/sale_order.py | 2 +- addons/sale_stock/models/stock.py | 2 +- addons/stock/models/stock_picking.py | 2 +- .../stock_account/models/stock_inventory.py | 2 +- addons/website/models/website_page.py | 2 + 16 files changed, 121 insertions(+), 18 deletions(-) diff --git a/addons/hr_fleet/models/employee.py b/addons/hr_fleet/models/employee.py index 774eefb2d9..2bc3fbfa4d 100644 --- a/addons/hr_fleet/models/employee.py +++ b/addons/hr_fleet/models/employee.py @@ -14,12 +14,13 @@ class Employee(models.Model): self.ensure_one() cars = self.env['fleet.vehicle.assignation.log'].search([ ('driver_id', 'in', (self.user_id.partner_id | self.sudo().address_home_id).ids)]).mapped('vehicle_id') + return { "type": "ir.actions.act_window", "res_model": "fleet.vehicle", "views": [[False, "kanban"], [False, "form"], [False, "tree"]], "domain": [("id", "in", cars.ids)], - "context": dict(create=False), + "context": dict(self._context, create=False), "name": "History Employee Cars", } diff --git a/addons/link_tracker/models/link_tracker.py b/addons/link_tracker/models/link_tracker.py index f0a4d584c4..e899624fdb 100644 --- a/addons/link_tracker/models/link_tracker.py +++ b/addons/link_tracker/models/link_tracker.py @@ -173,7 +173,7 @@ class LinkTracker(models.Model): def action_view_statistics(self): action = self.env['ir.actions.act_window']._for_xml_id('link_tracker.link_tracker_click_action_statistics') action['domain'] = [('link_id', '=', self.id)] - action['context'] = dict(action.get('context', {}), create=False) + action['context'] = dict(self._context, create=False) return action def action_visit_page(self): diff --git a/addons/mail/static/src/components/message/message.js b/addons/mail/static/src/components/message/message.js index b1d9443c8a..59c942d721 100644 --- a/addons/mail/static/src/components/message/message.js +++ b/addons/mail/static/src/components/message/message.js @@ -112,6 +112,21 @@ class Message extends Component { * regular time. */ this._intervalId = undefined; + /** + * States the index of the last "read more" that was inserted. + * Useful to remember the state for each "read more" even if their DOM + * is re-rendered. + */ + this._lastReadMoreIndex = 0; + /** + * Determines whether each "read more" is opened or closed. The keys are + * index, which is determined by their order of appearance in the DOM. + * If body changes so that "read more" count is different, their default + * value will be "wrong" at the next render but this is an acceptable + * limitation. It's more important to save the state correctly in a + * typical non-changing situation. + */ + this._isReadMoreByIndex = new Map(); this._constructor(); } @@ -404,6 +419,7 @@ class Message extends Component { } for (const group of groups) { + const index = this._lastReadMoreIndex++; // Insert link just before the first node const $readMoreLess = $('', { class: 'o_Message_readMoreLess', @@ -412,16 +428,23 @@ class Message extends Component { }).insertBefore(group[0]); // Toggle All next nodes - let isReadMore = true; - $readMoreLess.click(e => { - e.preventDefault(); - isReadMore = !isReadMore; + if (!this._isReadMoreByIndex.has(index)) { + this._isReadMoreByIndex.set(index, true); + } + const updateFromState = () => { + const isReadMore = this._isReadMoreByIndex.get(index); for (const $child of group) { $child.hide(); $child.toggle(!isReadMore); } $readMoreLess.text(isReadMore ? READ_MORE : READ_LESS); + }; + $readMoreLess.click(e => { + e.preventDefault(); + this._isReadMoreByIndex.set(index, !this._isReadMoreByIndex.get(index)); + updateFromState(); }); + updateFromState(); } } @@ -443,6 +466,7 @@ class Message extends Component { for (const el of [...this._contentRef.el.querySelectorAll(':scope .o_Message_readMoreLess')]) { el.remove(); } + this._lastReadMoreIndex = 0; this._insertReadMoreLess($(this._contentRef.el)); this.env.messagingBus.trigger('o-component-message-read-more-less-inserted', { message: this.message, diff --git a/addons/mail/static/src/widgets/form_renderer/form_renderer_tests.js b/addons/mail/static/src/widgets/form_renderer/form_renderer_tests.js index 9cf1781f74..0022cef443 100644 --- a/addons/mail/static/src/widgets/form_renderer/form_renderer_tests.js +++ b/addons/mail/static/src/widgets/form_renderer/form_renderer_tests.js @@ -975,6 +975,81 @@ QUnit.test('chatter just contains "creating a new record" message during the cre ); }); +QUnit.test('[TECHNICAL] unfolded read more/less links should not fold on message click besides those button links', async function (assert) { + // message click triggers a re-render. Before writing of this test, the + // insertion of read more/less links were done during render. This meant + // any re-render would re-insert the read more/less links. If some button + // links were unfolded, any re-render would fold them again. + // + // This previous behavior is undesirable, and results to bothersome UX + // such as inability to copy/paste unfolded message content due to click + // from text selection automatically folding all read more/less links. + assert.expect(3); + + this.data['mail.message'].records.push({ + author_id: 100, + // "data-o-mail-quote" added by server is intended to be compacted in read more/less blocks + body: ` +
+ Dear Joel Willis,
+ Thank you for your enquiry.
+ If you have any questions, please let us know. +

+ Thank you,
+ --
+ System +
+
+ `, + id: 1000, + model: 'res.partner', + res_id: 2, + }); + this.data['res.partner'].records.push({ + display_name: "Someone", + id: 100, + }); + await this.createView({ + data: this.data, + hasView: true, + // View params + View: FormView, + model: 'res.partner', + res_id: 2, + arch: ` +
+ + + +
+ +
+
+ `, + }); + assert.strictEqual( + document.querySelector('.o_Message_readMoreLess').textContent, + "read more", + "read more/less link on message should be folded initially (read more)" + ); + + document.querySelector('.o_Message_readMoreLess').click(), + assert.strictEqual( + document.querySelector('.o_Message_readMoreLess').textContent, + "read less", + "read more/less link on message should be unfolded after a click from initial rendering (read less)" + ); + + await afterNextRender( + () => document.querySelector('.o_Message').click(), + ); + assert.strictEqual( + document.querySelector('.o_Message_readMoreLess').textContent, + "read less", + "read more/less link on message should still be unfolded after a click on message aside of this button click (read less)" + ); +}); + }); }); }); diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index e86a98fca4..8899a83e6e 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -393,7 +393,7 @@ class MassMailing(models.Model): 'view_mode': 'tree', 'res_model': self.mailing_model_real, 'domain': [('id', 'in', res_ids)], - 'context': dict(create=False) + 'context': dict(self._context, create=False) } def update_opt_out(self, email, list_ids, value): diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 4109a44ef7..e0983c5e12 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -407,7 +407,7 @@ class MrpProduction(models.Model): else: action['views'] = form_view action['res_id'] = pickings.id - action['context'] = dict(action.get('context', {}), create=False, default_origin=self.name) + action['context'] = dict(self._context, default_origin=self.name, create=False) return action @api.depends('product_uom_id', 'product_qty', 'product_id.uom_id') @@ -1623,7 +1623,7 @@ class MrpProduction(models.Model): self.ensure_one() action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap") action['domain'] = [('production_id', '=', self.id)] - action['context'] = dict(action.get('context', {}), create=False) + action['context'] = dict(self._context, default_origin=self.name) return action @api.model diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 8dd6e4d520..5b95c2ead6 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -1364,7 +1364,7 @@ class Task(models.Model): 'res_model': 'project.task', 'res_id': self.parent_id.id, 'type': 'ir.actions.act_window', - 'context': dict(create=False) + 'context': dict(self._context, create=False) } def action_subtask(self): diff --git a/addons/purchase_product_matrix/models/purchase.py b/addons/purchase_product_matrix/models/purchase.py index 38dfb3313a..954c5b744f 100644 --- a/addons/purchase_product_matrix/models/purchase.py +++ b/addons/purchase_product_matrix/models/purchase.py @@ -115,8 +115,9 @@ class PurchaseOrder(models.Model): # Recompute prices for new/modified lines: for line in self.order_line.filtered(lambda line: line.product_id.id in product_ids): - res = line._product_id_change() or res + line._product_id_change() line._onchange_quantity() + res = line.onchange_product_id_warning() or res return res def _get_matrix(self, product_template): diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py index 6357d169b9..96f3a4b075 100644 --- a/addons/purchase_stock/models/stock.py +++ b/addons/purchase_stock/models/stock.py @@ -283,5 +283,5 @@ class ProductionLot(models.Model): self.ensure_one() action = self.env["ir.actions.actions"]._for_xml_id("purchase.purchase_form_action") action['domain'] = [('id', 'in', self.mapped('purchase_order_ids.id'))] - action['context'] = dict(action.get('context', {}), create=False) + action['context'] = dict(self._context, create=False) return action diff --git a/addons/sale_coupon/models/coupon_program.py b/addons/sale_coupon/models/coupon_program.py index 95999278ef..16d2d8af37 100644 --- a/addons/sale_coupon/models/coupon_program.py +++ b/addons/sale_coupon/models/coupon_program.py @@ -26,7 +26,7 @@ class CouponProgram(models.Model): 'search_view_id': [self.env.ref('sale.sale_order_view_search_inherit_quotation').id], 'type': 'ir.actions.act_window', 'domain': [('id', 'in', orders.ids)], - 'context': dict(create=False), + 'context': dict(self._context, create=False), } def _check_promo_code(self, order, coupon_code): diff --git a/addons/sale_project/views/sale_order_views.xml b/addons/sale_project/views/sale_order_views.xml index 2b13f3540b..10401adf03 100644 --- a/addons/sale_project/views/sale_order_views.xml +++ b/addons/sale_project/views/sale_order_views.xml @@ -10,7 +10,7 @@ - diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 4d034b32c0..f80efb5037 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -204,7 +204,7 @@ class SaleOrder(models.Model): picking_id = picking_id[0] else: picking_id = pickings[0] - action['context'] = dict(action.get('context', {}), default_partner_id=self.partner_id.id, default_picking_type_id=picking_id.picking_type_id.id, default_origin=self.name, default_group_id=picking_id.group_id.id, group_by=[]) + action['context'] = dict(self._context, default_partner_id=self.partner_id.id, default_picking_type_id=picking_id.picking_type_id.id, default_origin=self.name, default_group_id=picking_id.group_id.id) return action def action_cancel(self): diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py index c5e70aea78..94cd2209fc 100644 --- a/addons/sale_stock/models/stock.py +++ b/addons/sale_stock/models/stock.py @@ -183,5 +183,5 @@ class ProductionLot(models.Model): self.ensure_one() action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action['domain'] = [('id', 'in', self.mapped('sale_order_ids.id'))] - action['context'] = dict(action.get('context', {}), create=False) + action['context'] = dict(self._context, create=False) return action diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 5cf84a5ef3..2558d17b25 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -1375,7 +1375,7 @@ class Picking(models.Model): action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap") scraps = self.env['stock.scrap'].search([('picking_id', '=', self.id)]) action['domain'] = [('id', 'in', scraps.ids)] - action['context'] = dict(action.get('context', {}), create=False) + action['context'] = dict(self._context, create=False) return action def action_see_packages(self): diff --git a/addons/stock_account/models/stock_inventory.py b/addons/stock_account/models/stock_inventory.py index fb5ce1a06e..3c335b2df9 100644 --- a/addons/stock_account/models/stock_inventory.py +++ b/addons/stock_account/models/stock_inventory.py @@ -28,7 +28,7 @@ class StockInventory(models.Model): self.ensure_one() action_data = self.env['ir.actions.act_window']._for_xml_id('account.action_move_journal_line') action_data['domain'] = [('stock_move_id.id', 'in', self.move_ids.ids)] - action_data['context'] = dict(action_data.get('context', {}), create=False) + action_data['context'] = dict(self._context, create=False) return action_data def post_inventory(self): diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py index fa9cba029f..382ab2c24b 100644 --- a/addons/website/models/website_page.py +++ b/addons/website/models/website_page.py @@ -196,6 +196,8 @@ class Page(models.Model): if not pages_linked_to_iruiview and not page.view_id.inherit_children_ids: # If there is no other pages linked to that ir_ui_view, we can delete the ir_ui_view page.view_id.unlink() + # Make sure website._get_menu_ids() will be recomputed + self.clear_caches() return super(Page, self).unlink() def write(self, vals):