From de4213b771f641cbc218ebb1f759b09e064100bf Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 22 May 2020 12:09:31 +0000 Subject: [PATCH 1/6] [IMP] base: remove read access on ir.actions.* Custom actions may contain sensitive information, including business logic Task-id: 8203 --- addons/base_automation/security/ir.model.access.csv | 1 - odoo/addons/base/security/ir.model.access.csv | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/addons/base_automation/security/ir.model.access.csv b/addons/base_automation/security/ir.model.access.csv index 6b1d50350d0..77253ff3e96 100644 --- a/addons/base_automation/security/ir.model.access.csv +++ b/addons/base_automation/security/ir.model.access.csv @@ -1,3 +1,2 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_base_automation,base.automation,model_base_automation,,1,0,0,0 access_base_automation_config,base.automation config,model_base_automation,base.group_system,1,1,1,1 diff --git a/odoo/addons/base/security/ir.model.access.csv b/odoo/addons/base/security/ir.model.access.csv index ae568207ce3..220116ea7c0 100644 --- a/odoo/addons/base/security/ir.model.access.csv +++ b/odoo/addons/base/security/ir.model.access.csv @@ -76,23 +76,16 @@ "access_res_users_apikeys_description_employee","API key wizard employees","model_res_users_apikeys_description","group_user",1,0,1,0 "access_res_users_apikeys_description_portal","API key wizard","model_res_users_apikeys_description","group_portal",1,0,1,0 "access_res_users_apikeys_show_employee","API key result employees","model_res_users_apikeys_show","group_user",1,0,1,0 -"access_ir_actions_all","ir_actions_all","model_ir_actions_actions",,1,0,0,0 "access_ir_actions_group_system","ir_actions_group_system","model_ir_actions_actions","group_system",1,1,1,1 -"access_ir_actions_act_window_all","ir_actions_act_window_all","model_ir_actions_act_window",,1,0,0,0 "access_ir_actions_act_window_system","ir_actions_act_window_system","model_ir_actions_act_window","group_system",1,1,1,1 -"access_ir_actions_act_window_close_all","ir_actions_act_window_close_all","model_ir_actions_act_window_close",,1,0,0,0 "access_ir_actions_act_window_close_group_system","ir_actions_act_window_close_group_system","model_ir_actions_act_window_close","group_system",1,1,1,1 -"access_ir_actions_report_all","ir_actions_report","model_ir_actions_report",,1,0,0,0 "access_ir_actions_report_group_system","ir_actions_report_group_system","model_ir_actions_report","group_system",1,1,1,1 "access_ir_actions_todo_group_system","ir_actions_todo group system","model_ir_actions_todo","group_system",1,1,1,1 -"access_ir_actions_act_window_view_all","ir_actions_act_window_view_all","model_ir_actions_act_window_view",,1,0,0,0 "access_ir_actions_act_window_view_group_system","ir_actions_act_window_view_group_system","model_ir_actions_act_window_view","group_system",1,1,1,1 -"access_ir_actions_act_url_all","ir_actions_act_url_all","model_ir_actions_act_url",,1,0,0,0 "access_ir_actions_act_url_group_system","ir_actions_act_url_group_system","model_ir_actions_act_url","group_system",1,1,1,1 -"access_ir_server_object_lines_all","ir_server_object_lines_all","model_ir_server_object_lines",,1,0,0,0 "access_ir_server_object_lines_group_system","ir_server_object_lines_group_system","model_ir_server_object_lines","group_system",1,1,1,1 -"access_ir_actions_server_all","ir_actions_server_all","model_ir_actions_server",,1,0,0,0 "access_ir_actions_server_group_system","ir_actions_server_group_system","model_ir_actions_server","group_system",1,1,1,1 +"access_ir_actions_client","ir_actions_client all","model_ir_actions_client","group_system",1,1,1,1 "access_res_bank_group_system","res_bank_group_system","model_res_bank","group_system",1,1,1,1 "access_res_bank_group_partner_manager","res_bank_group_partner_manager","model_res_bank","group_partner_manager",1,1,1,1 "access_res_bank_user","res_bank user","model_res_bank","group_user",1,0,0,0 @@ -102,7 +95,6 @@ "access_ir_filter_public","ir_filters all","model_ir_filters","group_public",1,1,1,1 "access_ir_config_parameter_system","ir_config_parameter_system","model_ir_config_parameter","group_system",1,1,1,1 "access_ir_mail_server","ir_mail_server","model_ir_mail_server","group_system",1,1,1,1 -"access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 "access_ir_logging","ir_logging admin","model_ir_logging","group_erp_manager",1,1,1,1 "paperformat_access_portal","ir_actions_report_paperformat group_portal","model_report_paperformat",,1,0,0,0 "paperformat_access_employee","ir_actions_report_paperformat group_system","model_report_paperformat",group_system,1,1,1,1 From 2104efa9b4c18c3cac483b1cba92ee77f5709ee0 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 22 May 2020 12:32:45 +0000 Subject: [PATCH 2/6] [REF] base: remove outdated method Introduced at e3ab323e299c3f but now ir.cron inherits from ir.action.server, no longer needed --- odoo/addons/base/models/ir_actions.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index aea5f3f7b0b..aa9e65620b0 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -597,13 +597,6 @@ class IrActionsServer(models.Model): ) return res or False - def _run_actions(self, ids): - """ - Run server actions with given ids. - Allow crons to run specific server actions - """ - return self.browse(ids).run() - class IrServerObjectLines(models.Model): _name = 'ir.server.object.lines' From f0d37c384b299e5f35cddda02d3f56c6ad37a9a7 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 22 May 2020 13:18:11 +0000 Subject: [PATCH 3/6] [IMP] base: enforce groups_id verification Browse in sudo and allow to access and run in sudo if the user belongs to the groups defined. If the user does not belong to the group, the run must be called in sudo or with a priviledged user. If no group is defined, the user can execute the action if he can write on the target model. Safe actions (e.g. crm.action_your_pipeline) need to have the correct group on it. Inspired by the logic of access to ir.ui.view, all records are private by default except if explicitly specify a group value. --- .../tests/test_sms_server_actions.py | 1 + odoo/addons/base/models/ir_actions.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/addons/test_mail_full/tests/test_sms_server_actions.py b/addons/test_mail_full/tests/test_sms_server_actions.py index e44cb6847a4..869a61c48a0 100644 --- a/addons/test_mail_full/tests/test_sms_server_actions.py +++ b/addons/test_mail_full/tests/test_sms_server_actions.py @@ -25,6 +25,7 @@ class TestServerAction(TestMailFullCommon, TestRecipients): 'model_id': cls.env['ir.model']._get('mail.test.sms').id, 'state': 'sms', 'sms_template_id': cls.sms_template.id, + 'groups_id': cls.env.ref('base.group_user'), }) def test_action_sms(self): diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index aa9e65620b0..b7f7aa9d2b5 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -463,7 +463,7 @@ class IrActionsServer(models.Model): return True def _run_action_code_multi(self, eval_context): - safe_eval(self.sudo().code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action' + safe_eval(self.code.strip(), eval_context, mode="exec", nocopy=True) # nocopy allows to return 'action' return eval_context.get('action') def _run_action_multi(self, eval_context=None): @@ -564,10 +564,19 @@ class IrActionsServer(models.Model): return action """ res = False - for action in self: + for action in self.sudo(): action_groups = action.groups_id - if action_groups and not (action_groups & self.env.user.groups_id): - raise AccessError(_("You don't have enough access rights to run this action.")) + if action_groups: + if not (action_groups & self.env.user.groups_id): + raise AccessError(_("You don't have enough access rights to run this action.")) + else: + try: + self.env[action.model_name].check_access_rights("write") + except AccessError: + _logger.warning("Forbidden server action %r executed while the user %s does not have access to %s.", + action.name, self.env.user.login, action.model_name, + ) + raise eval_context = self._get_eval_context(action) From e6e0eafccd483e2b002b45b514181c8ce4238b0d Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 2 Jul 2020 10:39:50 +0000 Subject: [PATCH 4/6] [FIX] *: add groups_id where needed To trigger a run of a server action, one must have a group specified or a write access on the model. CRM: only access lead, crm.team is readable by employees Website: all users can access, action_dashboard_redirect takes care of the group --- addons/crm/views/crm_lead_views.xml | 1 + addons/website/views/website_views.xml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index 82ff08fc5dc..68d24df7cec 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -981,6 +981,7 @@ if record: Crm: My Pipeline code + action = model.action_your_pipeline() diff --git a/addons/website/views/website_views.xml b/addons/website/views/website_views.xml index 970aa309523..95241bdbb30 100644 --- a/addons/website/views/website_views.xml +++ b/addons/website/views/website_views.xml @@ -317,13 +317,15 @@ code action = model.action_dashboard_redirect() + - Website: Dashboard + Website: Analytics code action = model.env.ref('website.backend_dashboard').read()[0] + From d5c16492397047ceb97a0879aa61f068a0eeeca8 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 19 Jun 2020 13:02:34 +0000 Subject: [PATCH 5/6] [IMP] base,web: _for_xml_id as a safe access point /web/action/load is the public controller that Should be used by the webclient to fetch actions _for_xml_id is the default access method on actions that implements fields filtering to avoid leaking server action code or other information not needed by the webclient Implementing whitelist of fields that can be access per model --- addons/event_sale/models/sale_order.py | 2 +- addons/fleet/models/fleet_vehicle.py | 5 +- addons/hr_expense/models/hr_expense.py | 4 +- .../models/fleet_vehicle_assignation_log.py | 2 +- .../hr_recruitment/models/hr_recruitment.py | 2 +- .../im_livechat/models/im_livechat_channel.py | 2 +- addons/link_tracker/models/link_tracker.py | 2 +- addons/project/models/project.py | 2 +- .../sale_timesheet/models/project_overview.py | 2 +- addons/web/controllers/main.py | 25 ++++--- odoo/addons/base/models/ir_actions.py | 68 +++++++++++++++---- odoo/addons/base/models/ir_actions_report.py | 5 ++ odoo/addons/base/models/ir_attachment.py | 2 +- .../base/wizard/base_document_layout.py | 2 +- 14 files changed, 91 insertions(+), 34 deletions(-) diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index a5a7d074c09..7a71302d92a 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -26,7 +26,7 @@ class SaleOrder(models.Model): if any(line.event_id for line in so.order_line): return self.env['ir.actions.act_window'] \ .with_context(default_sale_order_id=so.id) \ - .for_xml_id('event_sale', 'action_sale_order_event_registration') + ._for_xml_id('event_sale.action_sale_order_event_registration') return res def action_view_attendee_list(self): diff --git a/addons/fleet/models/fleet_vehicle.py b/addons/fleet/models/fleet_vehicle.py index e2498f854d7..87519bdafd0 100644 --- a/addons/fleet/models/fleet_vehicle.py +++ b/addons/fleet/models/fleet_vehicle.py @@ -281,7 +281,8 @@ class FleetVehicle(models.Model): self.ensure_one() xml_id = self.env.context.get('xml_id') if xml_id: - res = self.env['ir.actions.act_window'].for_xml_id('fleet', xml_id) + + res = self.env['ir.actions.act_window']._for_xml_id('fleet.%s' % xml_id) res.update( context=dict(self.env.context, default_vehicle_id=self.id, group_by=False), domain=[('vehicle_id', '=', self.id)] @@ -296,7 +297,7 @@ class FleetVehicle(models.Model): self.ensure_one() copy_context = dict(self.env.context) copy_context.pop('group_by', None) - res = self.env['ir.actions.act_window'].for_xml_id('fleet', 'fleet_vehicle_costs_action') + res = self.env['ir.actions.act_window']._for_xml_id('fleet.fleet_vehicle_costs_action') res.update( context=dict(copy_context, default_vehicle_id=self.id, search_default_parent_false=True), domain=[('vehicle_id', '=', self.id)] diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 114085d29a5..2635966599d 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -325,7 +325,7 @@ class HrExpense(models.Model): def action_get_attachment_view(self): self.ensure_one() - res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment') + res = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment') res['domain'] = [('res_model', '=', 'hr.expense'), ('res_id', 'in', self.ids)] res['context'] = {'default_res_model': 'hr.expense', 'default_res_id': self.id} return res @@ -915,7 +915,7 @@ class HrExpenseSheet(models.Model): return res def action_get_attachment_view(self): - res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment') + res = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment') res['domain'] = [('res_model', '=', 'hr.expense'), ('res_id', 'in', self.expense_line_ids.ids)] res['context'] = { 'default_res_model': 'hr.expense.sheet', diff --git a/addons/hr_fleet/models/fleet_vehicle_assignation_log.py b/addons/hr_fleet/models/fleet_vehicle_assignation_log.py index 95e191b8fc8..cd93fcdf97f 100644 --- a/addons/hr_fleet/models/fleet_vehicle_assignation_log.py +++ b/addons/hr_fleet/models/fleet_vehicle_assignation_log.py @@ -19,7 +19,7 @@ class FleetVehicleAssignationLog(models.Model): def action_get_attachment_view(self): self.ensure_one() - res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment') + res = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment') res['domain'] = [('res_model', '=', 'fleet.vehicle.assignation.log'), ('res_id', 'in', self.ids)] res['context'] = {'default_res_model': 'fleet.vehicle.assignation.log', 'default_res_id': self.id} return res diff --git a/addons/hr_recruitment/models/hr_recruitment.py b/addons/hr_recruitment/models/hr_recruitment.py index c5532974b17..321e4340ca6 100644 --- a/addons/hr_recruitment/models/hr_recruitment.py +++ b/addons/hr_recruitment/models/hr_recruitment.py @@ -339,7 +339,7 @@ class Applicant(models.Model): partners = self.partner_id | self.user_id.partner_id | self.department_id.manager_id.user_id.partner_id category = self.env.ref('hr_recruitment.categ_meet_interview') - res = self.env['ir.actions.act_window'].for_xml_id('calendar', 'action_calendar_event') + res = self.env['ir.actions.act_window']._for_xml_id('calendar.action_calendar_event') res['context'] = { 'default_partner_ids': partners.ids, 'default_user_id': self.env.uid, diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index 4cf813eb0e8..ce56ce3cfbe 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -98,7 +98,7 @@ class ImLivechatChannel(models.Model): :returns : the ir.action 'action_view_rating' with the correct domain """ self.ensure_one() - action = self.env['ir.actions.act_window'].for_xml_id('im_livechat', 'rating_rating_action_view_livechat_rating') + action = self.env['ir.actions.act_window']._for_xml_id('im_livechat.rating_rating_action_view_livechat_rating') action['domain'] = [('parent_res_id', '=', self.id), ('parent_res_model', '=', 'im_livechat.channel')] return action diff --git a/addons/link_tracker/models/link_tracker.py b/addons/link_tracker/models/link_tracker.py index 2ee2fad31ab..50b00b9011f 100644 --- a/addons/link_tracker/models/link_tracker.py +++ b/addons/link_tracker/models/link_tracker.py @@ -145,7 +145,7 @@ class LinkTracker(models.Model): raise NotImplementedError('Moved on mail.render.mixin') def action_view_statistics(self): - action = self.env['ir.actions.act_window'].for_xml_id('link_tracker', 'link_tracker_click_action_statistics') + 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(self._context, create=False) return action diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 325c446ae59..374e5dcda48 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -442,7 +442,7 @@ class Project(models.Model): def action_view_all_rating(self): """ return the action to see all the rating of the project and activate default filters""" - action = self.env['ir.actions.act_window'].for_xml_id('project', 'rating_rating_action_view_project_rating') + action = self.env['ir.actions.act_window']._for_xml_id('project.rating_rating_action_view_project_rating') action['name'] = _('Ratings of %s') % (self.name,) action_context = ast.literal_eval(action['context']) if action['context'] else {} action_context.update(self._context) diff --git a/addons/sale_timesheet/models/project_overview.py b/addons/sale_timesheet/models/project_overview.py index e8052912d8c..dcaba19bc5d 100644 --- a/addons/sale_timesheet/models/project_overview.py +++ b/addons/sale_timesheet/models/project_overview.py @@ -542,7 +542,7 @@ def _to_action_data(model=None, *, action=None, views=None, res_id=None, domain= # pass in either action or (model, views) if action: assert model is None and views is None - act = clean_action(action.read()[0]) + act = clean_action(action.read()[0], env=action.env) model = act['res_model'] views = act['views'] # FIXME: search-view-id, possibly help? diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 6e5bd95432c..1907064bd6b 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -324,12 +324,18 @@ def set_cookie_and_redirect(redirect_url): redirect.autocorrect_location_header = False return redirect -def clean_action(action): +def clean_action(action, env): action.setdefault('flags', {}) action_type = action.setdefault('type', 'ir.actions.act_window_close') if action_type == 'ir.actions.act_window': - return fix_view_modes(action) - return action + action = fix_view_modes(action) + + # When returning an action, only few information are really usefull + return { + field: value + for field, value in action.items() + if field in env[action['type']]._get_readable_fields() + } # I think generate_views,fix_view_modes should go into js ActionManager def generate_views(action): @@ -1357,7 +1363,7 @@ class DataSet(http.Controller): def call_button(self, model, method, args, kwargs): action = self._call_kw(model, method, args, kwargs) if isinstance(action, dict) and action.get('type') != '': - return clean_action(action) + return clean_action(action, env=request.env) return False @http.route('/web/dataset/resequence', type='json', auth="user") @@ -1687,7 +1693,7 @@ class Action(http.Controller): except Exception: action_id = 0 # force failed read - base_action = Actions.browse([action_id]).read(['type']) + base_action = Actions.browse([action_id]).sudo().read(['type']) if base_action: ctx = dict(request.context) action_type = base_action[0]['type'] @@ -1696,15 +1702,16 @@ class Action(http.Controller): if additional_context: ctx.update(additional_context) request.context = ctx - action = request.env[action_type].browse([action_id]).read() + action = request.env[action_type].sudo().browse([action_id]).read() if action: - value = clean_action(action[0]) + value = clean_action(action[0], env=request.env) return value @http.route('/web/action/run', type='json', auth="user") def run(self, action_id): - result = request.env['ir.actions.server'].browse([action_id]).run() - return clean_action(result) if result else False + action = request.env['ir.actions.server'].browse([action_id]) + result = action.run() + return clean_action(result, env=action.env) if result else False class Export(http.Controller): diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index b7f7aa9d2b5..8ef27fb588a 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -135,6 +135,38 @@ class IrActions(models.Model): result['action'] = sorted(result['action'], key=lambda vals: vals.get('sequence', 0)) return result + @api.model + def _for_xml_id(self, full_xml_id): + """ Returns the action content for the provided xml_id + + :param module: the module the act_window originates in + :param xml_id: the namespace-less id of the action (the @id + attribute from the XML file) + :return: A read() view of the ir.actions.action safe for web use + """ + record = self.env.ref(full_xml_id) + assert isinstance(self.env[record._name], type(self)) + # TO CHECK MAT: check groups_id/res_model access? + action = record.sudo().read()[0] + return { + field: value + for field, value in action.items() + if field in record._get_readable_fields() + } + + def _get_readable_fields(self): + """ return the list of fields that are safe to read + + Fetched via /web/action/load or _for_xml_id method + Only fields used by the web client should included + Accessing content useful for the server-side must + be done manually with superuser + """ + return { + "binding_model_id", "binding_type", "binding_view_types", + "display_name", "help", "id", "name", "type", "xml_id", + } + class IrActionsActWindow(models.Model): _name = 'ir.actions.act_window' @@ -232,18 +264,6 @@ class IrActionsActWindow(models.Model): values['help'] = self.with_context(**ctx).env[model].get_empty_list_help(values.get('help', '')) return result - @api.model - def for_xml_id(self, module, xml_id): - """ Returns the act_window object created for the provided xml_id - - :param module: the module the act_window originates in - :param xml_id: the namespace-less id of the action (the @id - attribute from the XML file) - :return: A read() view of the ir.actions.act_window - """ - record = self.env.ref("%s.%s" % (module, xml_id)) - return record.read()[0] - @api.model_create_multi def create(self, vals_list): self.clear_caches() @@ -268,6 +288,14 @@ class IrActionsActWindow(models.Model): return set(row[0] for row in self._cr.fetchall()) + def _get_readable_fields(self): + return super()._get_readable_fields() | { + "context", "domain", "filter", "groups_id", "limit", "res_id", + "res_model", "search_view", "search_view_id", "target", "view_id", + "view_mode", "views", + } + + VIEW_TYPES = [ ('tree', 'Tree'), ('form', 'Form'), @@ -321,6 +349,11 @@ class IrActionsActUrl(models.Model): target = fields.Selection([('new', 'New Window'), ('self', 'This Window')], string='Action Target', default='new', required=True) + def _get_readable_fields(self): + return super()._get_readable_fields() | { + "target", "url", + } + class IrActionsServer(models.Model): """ Server actions model. Server action work on a base model and offer various @@ -421,6 +454,11 @@ class IrActionsServer(models.Model): if not self._check_m2m_recursion('child_ids'): raise ValidationError(_('Recursion found in child server actions')) + def _get_readable_fields(self): + return super()._get_readable_fields() | { + "groups_id", "model_name", + } + def _get_runner(self): multi = True t = type(self) @@ -799,3 +837,9 @@ class IrActionsActClient(models.Model): params_store = doc.find(".//field[@name='params_store']") params_store.getparent().remove(params_store) return doc + + + def _get_readable_fields(self): + return super()._get_readable_fields() | { + "context", "params", "res_model", "tag", "target", + } diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py index 0a927ccacc0..fc1eb44c29e 100644 --- a/odoo/addons/base/models/ir_actions_report.py +++ b/odoo/addons/base/models/ir_actions_report.py @@ -143,6 +143,11 @@ class IrActionsReport(models.Model): else: return FALSE_DOMAIN + def _get_readable_fields(self): + return super()._get_readable_fields() | { + "params", "report_name", "report_type", "target", + } + def associated_view(self): """Used in the ir.actions.report form view in order to search naively after the view(s) used in the rendering. diff --git a/odoo/addons/base/models/ir_attachment.py b/odoo/addons/base/models/ir_attachment.py index 0473bc5b7bb..6854a50c8f3 100644 --- a/odoo/addons/base/models/ir_attachment.py +++ b/odoo/addons/base/models/ir_attachment.py @@ -570,7 +570,7 @@ class IrAttachment(models.Model): @api.model def action_get(self): - return self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment') + return self.env['ir.actions.act_window']._for_xml_id('base.action_attachment') @api.model def get_serve_attachment(self, url, extra_domain=None, extra_fields=None, order=None): diff --git a/odoo/addons/base/wizard/base_document_layout.py b/odoo/addons/base/wizard/base_document_layout.py index 5232acb7f06..e93a63e1bfd 100644 --- a/odoo/addons/base/wizard/base_document_layout.py +++ b/odoo/addons/base/wizard/base_document_layout.py @@ -182,7 +182,7 @@ class BaseDocumentLayout(models.TransientModel): def action_open_base_document_layout(self, action_ref=None): if not action_ref: action_ref = 'base.action_base_document_layout_configurator' - return self.env.ref(action_ref).read()[0] + return self.env["ir.actions.actions"]._for_xml_id(action_ref) def document_layout_save(self): # meant to be overridden From 6156f98288b16f6c84d36bacf16d5a66a661abb3 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 25 May 2020 07:04:52 +0000 Subject: [PATCH 6/6] [FIX] *: adapt action content retrieval Use _for_xml_id to replace all the self.env.ref().read()[0] This has the advantage of having a single point of control and to add the fields filtering and model verification. Add sudo for other operations on ir.actions.* --- .../models/account_journal_dashboard.py | 5 ++- addons/account/models/account_move.py | 4 +- .../account/models/account_reconcile_model.py | 2 +- addons/account/models/company.py | 4 +- addons/account/models/partner.py | 2 +- .../auth_signup/models/res_config_settings.py | 2 +- .../base_automation/models/base_automation.py | 4 +- .../base_setup/models/res_config_settings.py | 2 +- addons/calendar/models/calendar_attendee.py | 2 +- addons/calendar/models/mail_activity.py | 2 +- addons/crm/models/crm_lead.py | 2 +- addons/crm/models/crm_team.py | 4 +- addons/crm/models/res_partner.py | 2 +- addons/crm/models/utm.py | 2 +- .../models/crm_iap_lead_mining_request.py | 4 +- .../models/crm_reveal_rule.py | 4 +- addons/delivery/models/stock_picking.py | 2 +- addons/event/models/res_partner.py | 2 +- addons/event_sale/models/event_event.py | 2 +- .../event_sale/models/event_registration.py | 2 +- addons/event_sale/models/sale_order.py | 2 +- addons/hr/models/res_users.py | 2 +- addons/hr_attendance/models/hr_employee.py | 2 +- addons/hr_holidays/models/hr_leave_type.py | 4 +- addons/hr_recruitment/models/hr_job.py | 2 +- addons/mass_mailing/models/mailing.py | 4 +- addons/mass_mailing/models/mailing_list.py | 2 +- .../models/mailing_mailing.py | 2 +- .../models/mailing_mailing.py | 4 +- .../mass_mailing_sms/models/mailing_list.py | 2 +- addons/mass_mailing_sms/models/utm.py | 4 +- addons/mrp/models/mrp_production.py | 8 ++-- addons/mrp/models/mrp_workcenter.py | 2 +- addons/mrp/models/mrp_workorder.py | 4 +- addons/mrp/models/product.py | 8 ++-- addons/mrp_account/models/mrp_production.py | 2 +- addons/payment/models/res_company.py | 2 +- .../payment_acquirer_onboarding_wizard.py | 2 +- addons/portal/models/portal_mixin.py | 2 +- addons/project/models/project.py | 8 ++-- .../project/wizard/project_delete_wizard.py | 2 +- .../wizard/project_task_type_delete.py | 6 +-- addons/purchase/models/product.py | 4 +- addons/purchase/models/purchase.py | 2 +- addons/purchase_stock/models/stock.py | 2 +- addons/sale/models/product_product.py | 2 +- addons/sale/models/product_template.py | 2 +- addons/sale/models/res_company.py | 4 +- addons/sale/models/sale.py | 2 +- addons/sale/models/sales_team.py | 2 +- addons/sale/models/utm.py | 4 +- addons/sale_crm/models/crm_lead.py | 8 ++-- addons/sale_crm/models/crm_team.py | 2 +- addons/sale_project/models/project.py | 2 +- addons/sale_project/models/sale_order.py | 2 +- addons/sale_stock/models/sale_order.py | 2 +- addons/sale_stock/models/stock.py | 2 +- addons/sale_timesheet/models/project.py | 2 +- addons/sale_timesheet/models/sale_order.py | 2 +- .../wizard/project_create_invoice.py | 2 +- .../wizard/project_create_sale_order.py | 2 +- .../wizard/project_task_create_sale_order.py | 2 +- addons/stock/models/product.py | 18 ++++----- addons/stock/models/stock_move.py | 2 +- addons/stock/models/stock_orderpoint.py | 2 +- addons/stock/models/stock_picking.py | 8 ++-- addons/stock/models/stock_quant.py | 4 +- addons/stock_account/models/stock_picking.py | 2 +- .../wizard/stock_quantity_history.py | 2 +- .../stock_landed_costs/models/account_move.py | 4 +- .../models/stock_landed_cost.py | 2 +- addons/survey/models/res_partner.py | 2 +- addons/website/models/res_company.py | 4 +- addons/website/models/res_config_settings.py | 4 +- addons/website/models/website.py | 4 +- addons/website/views/website_views.xml | 2 +- addons/website_crm/models/crm_lead.py | 2 +- .../website_crm_livechat/models/crm_lead.py | 2 +- .../models/event_question.py | 2 +- addons/website_sale/models/res_company.py | 2 +- addons/website_sale/models/website.py | 2 +- .../models/slide_channel.py | 2 +- addons/website_slides/models/res_partner.py | 2 +- addons/website_slides/models/slide_channel.py | 6 +-- .../models/slide_channel.py | 2 +- .../models/survey_survey.py | 2 +- odoo/addons/base/models/ir_actions.py | 2 +- odoo/addons/base/models/ir_actions_report.py | 39 +++++++++++-------- odoo/addons/base/models/ir_ui_menu.py | 2 +- odoo/addons/base/models/res_company.py | 2 +- 90 files changed, 161 insertions(+), 151 deletions(-) diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 63a75bf323e..0bc805110e8 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -459,7 +459,8 @@ class account_journal(models.Model): if '.' not in action_name: action_name = 'account.%s' % action_name - action = self.env.ref(action_name).read()[0] + action = self.env["ir.actions.act_window"]._for_xml_id(action_name) + context = self._context.copy() if 'context' in action and type(action['context']) == str: context.update(ast.literal_eval(action['context'])) @@ -533,7 +534,7 @@ class account_journal(models.Model): def create_bank_statement(self): """return action to create a bank statements. This button should be called only on journals with type =='bank'""" - action = self.env.ref('account.action_bank_statement_tree').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_bank_statement_tree") action.update({ 'views': [[False, 'form']], 'context': "{'default_journal_id': " + str(self.id) + "}", diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index e72f4f9e2c9..5f69692cff1 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2361,7 +2361,7 @@ class AccountMove(models.Model): return self.move_type == 'out_invoice' and not self.payment_reference def action_reverse(self): - action = self.env.ref('account.action_view_account_move_reversal').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_view_account_move_reversal") if self.is_invoice(): action['name'] = _('Credit Note') @@ -2614,7 +2614,7 @@ class AccountMove(models.Model): # offer the possibility to duplicate thanks to a button instead of a hidden menu, which is more visible def action_duplicate(self): self.ensure_one() - action = self.env.ref('account.action_move_journal_line').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_journal_line") action['context'] = dict(self.env.context) action['context']['form_view_initial_mode'] = 'edit' action['context']['view_no_maturity'] = False diff --git a/addons/account/models/account_reconcile_model.py b/addons/account/models/account_reconcile_model.py index 7f62c573ffe..6e75649cefb 100644 --- a/addons/account/models/account_reconcile_model.py +++ b/addons/account/models/account_reconcile_model.py @@ -226,7 +226,7 @@ class AccountReconcileModel(models.Model): def action_reconcile_stat(self): self.ensure_one() - action = self.env.ref('account.action_move_journal_line').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_journal_line") self._cr.execute(''' SELECT ARRAY_AGG(DISTINCT move_id) FROM account_move_line diff --git a/addons/account/models/company.py b/addons/account/models/company.py index 89cb05ab1a6..ce4951f0954 100644 --- a/addons/account/models/company.py +++ b/addons/account/models/company.py @@ -431,7 +431,7 @@ class ResCompany(models.Model): @api.model def action_open_account_onboarding_sale_tax(self): """ Onboarding step for the invoice layout. """ - action = self.env.ref('account.action_open_account_onboarding_sale_tax').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_open_account_onboarding_sale_tax") action['res_id'] = self.env.company.id return action @@ -485,7 +485,7 @@ class ResCompany(models.Model): with the edi_invoice_template message loaded by default. """ sample_invoice = self._get_sample_invoice() template = self.env.ref('account.email_template_edi_invoice', False) - action = self.env.ref('account.action_open_account_onboarding_sample_invoice').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_open_account_onboarding_sample_invoice") action['context'] = { 'default_res_id': sample_invoice.id, 'default_use_template': bool(template), diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index 1914a270cf3..c602a92240c 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -476,7 +476,7 @@ class ResPartner(models.Model): def action_view_partner_invoices(self): self.ensure_one() - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_out_invoice_type") action['domain'] = [ ('move_type', 'in', ('out_invoice', 'out_refund')), ('partner_id', 'child_of', self.id), diff --git a/addons/auth_signup/models/res_config_settings.py b/addons/auth_signup/models/res_config_settings.py index 662152c2959..b4e62cecc68 100644 --- a/addons/auth_signup/models/res_config_settings.py +++ b/addons/auth_signup/models/res_config_settings.py @@ -18,7 +18,7 @@ class ResConfigSettings(models.TransientModel): config_parameter='base.template_portal_user_id') def open_template_user(self): - action = self.env.ref('base.action_res_users').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_res_users") action['res_id'] = literal_eval(self.env['ir.config_parameter'].sudo().get_param('base.template_portal_user_id', 'False')) action['views'] = [[self.env.ref('base.view_users_form').id, 'form']] return action diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 77be0ddc76d..e5ef6cf2f16 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -164,7 +164,7 @@ class BaseAutomation(models.Model): if '__action_done' not in self._context: self = self.with_context(__action_done={}) domain = [('model_name', '=', records._name), ('trigger', 'in', triggers)] - actions = self.with_context(active_test=True).search(domain) + actions = self.with_context(active_test=True).sudo().search(domain) return actions.with_env(self.env) def _get_eval_context(self): @@ -258,7 +258,7 @@ class BaseAutomation(models.Model): 'domain_post': domain_post, } try: - self.action_server_id.with_context(**ctx).run() + self.action_server_id.sudo().with_context(**ctx).run() except Exception as e: self._add_postmortem_action(e) raise e diff --git a/addons/base_setup/models/res_config_settings.py b/addons/base_setup/models/res_config_settings.py index 04b695c141f..acbe5a873c5 100644 --- a/addons/base_setup/models/res_config_settings.py +++ b/addons/base_setup/models/res_config_settings.py @@ -60,7 +60,7 @@ class ResConfigSettings(models.TransientModel): } def open_default_user(self): - action = self.env.ref('base.action_res_users').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_res_users") action['res_id'] = self.env.ref('base.default_user').id action['views'] = [[self.env.ref('base.view_users_form').id, 'form']] return action diff --git a/addons/calendar/models/calendar_attendee.py b/addons/calendar/models/calendar_attendee.py index a7d5f1ccd0d..0aeb5d51a63 100644 --- a/addons/calendar/models/calendar_attendee.py +++ b/addons/calendar/models/calendar_attendee.py @@ -107,7 +107,7 @@ class Attendee(models.Model): rendering_context.update({ 'colors': colors, 'ignore_recurrence': ignore_recurrence, - 'action_id': self.env['ir.actions.act_window'].search([('view_id', '=', calendar_view.id)], limit=1).id, + 'action_id': self.env['ir.actions.act_window'].sudo().search([('view_id', '=', calendar_view.id)], limit=1).id, 'dbname': self._cr.dbname, 'base_url': self.env['ir.config_parameter'].sudo().get_param('web.base.url', default='http://localhost:8069'), }) diff --git a/addons/calendar/models/mail_activity.py b/addons/calendar/models/mail_activity.py index 71d7bf0fadb..58ca5ff75d4 100644 --- a/addons/calendar/models/mail_activity.py +++ b/addons/calendar/models/mail_activity.py @@ -17,7 +17,7 @@ class MailActivity(models.Model): def action_create_calendar_event(self): self.ensure_one() - action = self.env.ref('calendar.action_calendar_event').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("calendar.action_calendar_event") action['context'] = { 'default_activity_type_id': self.activity_type_id.id, 'default_res_id': self.env.context.get('default_res_id'), diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 4827eddd040..88be2bb673e 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -800,7 +800,7 @@ class Lead(models.Model): :return dict: dictionary value for created Meeting view """ self.ensure_one() - action = self.env.ref('calendar.action_calendar_event').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("calendar.action_calendar_event") partner_ids = self.env.user.partner_id.ids if self.partner_id: partner_ids.append(self.partner_id.id) diff --git a/addons/crm/models/crm_team.py b/addons/crm/models/crm_team.py index 850349dc79a..e099233691b 100644 --- a/addons/crm/models/crm_team.py +++ b/addons/crm/models/crm_team.py @@ -126,7 +126,7 @@ class Team(models.Model): #TODO JEM : refactor this stuff with xml action, proper customization, @api.model def action_your_pipeline(self): - action = self.env.ref('crm.crm_lead_action_pipeline').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_action_pipeline") user_team_id = self.env.user.sale_team_id.id if user_team_id: # To ensure that the team is readable in multi company @@ -154,7 +154,7 @@ class Team(models.Model): def action_primary_channel_button(self): if self.use_opportunities: - return self.env.ref('crm.crm_case_form_view_salesteams_opportunity').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("crm.crm_case_form_view_salesteams_opportunity") return super(Team,self).action_primary_channel_button() def _graph_get_model(self): diff --git a/addons/crm/models/res_partner.py b/addons/crm/models/res_partner.py index f214c7d5549..8f429c888ec 100644 --- a/addons/crm/models/res_partner.py +++ b/addons/crm/models/res_partner.py @@ -77,7 +77,7 @@ class Partner(models.Model): def schedule_meeting(self): partner_ids = self.ids partner_ids.append(self.env.user.partner_id.id) - action = self.env.ref('calendar.action_calendar_event').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("calendar.action_calendar_event") action['context'] = { 'default_partner_ids': partner_ids, } diff --git a/addons/crm/models/utm.py b/addons/crm/models/utm.py index 3839952b286..fd093579217 100644 --- a/addons/crm/models/utm.py +++ b/addons/crm/models/utm.py @@ -23,7 +23,7 @@ class UtmCampaign(models.Model): def action_redirect_to_leads_opportunities(self): view = 'crm.crm_lead_all_leads' if self.use_leads else 'crm.crm_lead_opportunities' - action = self.env.ref(view).read()[0] + action = self.env.ref(view).sudo().read()[0] action['view_mode'] = 'tree,kanban,graph,pivot,form,calendar' action['domain'] = [('campaign_id', 'in', self.ids)] action['context'] = {'active_test': False, 'create': False} diff --git a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py index 8cd41ebb3bd..f3bedd5905a 100644 --- a/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py +++ b/addons/crm_iap_lead/models/crm_iap_lead_mining_request.py @@ -243,7 +243,7 @@ class CRMLeadMiningRequest(models.Model): def action_get_lead_action(self): self.ensure_one() - action = self.env.ref('crm.crm_lead_all_leads').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_all_leads") action['domain'] = [('id', 'in', self.lead_ids.ids), ('type', '=', 'lead')] action['help'] = _("""

No leads found @@ -254,7 +254,7 @@ class CRMLeadMiningRequest(models.Model): def action_get_opportunity_action(self): self.ensure_one() - action = self.env.ref('crm.crm_lead_opportunities').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_opportunities") action['domain'] = [('id', 'in', self.lead_ids.ids), ('type', '=', 'opportunity')] action['help'] = _("""

No opportunities found diff --git a/addons/crm_iap_lead_website/models/crm_reveal_rule.py b/addons/crm_iap_lead_website/models/crm_reveal_rule.py index d64565a55f8..58cf4093a9c 100644 --- a/addons/crm_iap_lead_website/models/crm_reveal_rule.py +++ b/addons/crm_iap_lead_website/models/crm_reveal_rule.py @@ -111,13 +111,13 @@ class CRMRevealRule(models.Model): rule.opportunity_count = mapping.get((rule.id, 'opportunity'), 0) def action_get_lead_tree_view(self): - action = self.env.ref('crm.crm_lead_all_leads').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_all_leads") action['domain'] = [('id', 'in', self.lead_ids.ids), ('type', '=', 'lead')] action['context'] = dict(self._context, create=False) return action def action_get_opportunity_tree_view(self): - action = self.env.ref('crm.crm_lead_opportunities').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_opportunities") action['domain'] = [('id', 'in', self.lead_ids.ids), ('type', '=', 'opportunity')] action['context'] = dict(self._context, create=False) return action diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index 20ad15160f2..1d561631ce8 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -219,7 +219,7 @@ class StockPicking(models.Model): for tracker in carrier_trackers: msg += '' + tracker[0] + '
' self.message_post(body=msg) - return self.env.ref('delivery.act_delivery_trackers_url').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("delivery.act_delivery_trackers_url") client_action = { 'type': 'ir.actions.act_url', diff --git a/addons/event/models/res_partner.py b/addons/event/models/res_partner.py index 16e09995969..1311e15d2f6 100644 --- a/addons/event/models/res_partner.py +++ b/addons/event/models/res_partner.py @@ -19,7 +19,7 @@ class ResPartner(models.Model): partner.event_count = self.env['event.event'].search_count([('registration_ids.partner_id', 'child_of', partner.ids)]) def action_event_view(self): - action = self.env.ref('event.action_event_view').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("event.action_event_view") action['context'] = {} action['domain'] = [('registration_ids.partner_id', 'child_of', self.ids)] return action diff --git a/addons/event_sale/models/event_event.py b/addons/event_sale/models/event_event.py index b246a033109..1072cf73a48 100644 --- a/addons/event_sale/models/event_event.py +++ b/addons/event_sale/models/event_event.py @@ -34,7 +34,7 @@ class Event(models.Model): def action_view_linked_orders(self): """ Redirects to the orders linked to the current events """ - sale_order_action = self.env.ref('sale.action_orders').read()[0] + sale_order_action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") sale_order_action.update({ 'domain': [('state', '!=', 'cancel'), ('order_line.event_id', 'in', self.ids)], 'context': {'create': 0}, diff --git a/addons/event_sale/models/event_registration.py b/addons/event_sale/models/event_registration.py index f7b6d79a395..1acb6126adb 100644 --- a/addons/event_sale/models/event_registration.py +++ b/addons/event_sale/models/event_registration.py @@ -58,7 +58,7 @@ class EventRegistration(models.Model): registration.utm_medium_id = False def action_view_sale_order(self): - action = self.env.ref('sale.action_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action['views'] = [(False, 'form')] action['res_id'] = self.sale_order_id.id return action diff --git a/addons/event_sale/models/sale_order.py b/addons/event_sale/models/sale_order.py index 7a71302d92a..9f170b994ce 100644 --- a/addons/event_sale/models/sale_order.py +++ b/addons/event_sale/models/sale_order.py @@ -30,7 +30,7 @@ class SaleOrder(models.Model): return res def action_view_attendee_list(self): - action = self.env.ref('event.event_registration_action_tree').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("event.event_registration_action_tree") action['domain'] = [('sale_order_id', 'in', self.ids)] return action diff --git a/addons/hr/models/res_users.py b/addons/hr/models/res_users.py index e95b7a8c57d..a00e9b1d134 100644 --- a/addons/hr/models/res_users.py +++ b/addons/hr/models/res_users.py @@ -188,7 +188,7 @@ class User(models.Model): @api.model def action_get(self): if self.env.user.employee_id: - return self.sudo().env.ref('hr.res_users_action_my').read()[0] + return self.sudo().env.ref('hr.res_users_action_my').sudo().read()[0] return super(User, self).action_get() @api.depends('employee_ids') diff --git a/addons/hr_attendance/models/hr_employee.py b/addons/hr_attendance/models/hr_employee.py index b1901554e44..bb67506dbb9 100644 --- a/addons/hr_attendance/models/hr_employee.py +++ b/addons/hr_attendance/models/hr_employee.py @@ -124,7 +124,7 @@ class HrEmployeeBase(models.AbstractModel): """ self.ensure_one() employee = self.sudo() - action_message = self.env.ref('hr_attendance.hr_attendance_action_greeting_message').read()[0] + action_message = self.env["ir.actions.actions"]._for_xml_id("hr_attendance.hr_attendance_action_greeting_message") action_message['previous_attendance_change_date'] = employee.last_attendance_id and (employee.last_attendance_id.check_out or employee.last_attendance_id.check_in) or False action_message['employee_name'] = employee.name action_message['barcode'] = employee.barcode diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index 5841c3fab4d..772cde2ee93 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -347,7 +347,7 @@ class HolidaysType(models.Model): def action_see_days_allocated(self): self.ensure_one() - action = self.env.ref('hr_holidays.hr_leave_allocation_action_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("hr_holidays.hr_leave_allocation_action_all") domain = [ ('holiday_status_id', 'in', self.ids), ('holiday_type', '!=', 'employee'), @@ -366,7 +366,7 @@ class HolidaysType(models.Model): def action_see_group_leaves(self): self.ensure_one() - action = self.env.ref('hr_holidays.hr_leave_action_action_approve_department').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("hr_holidays.hr_leave_action_action_approve_department") action['domain'] = [ ('holiday_status_id', '=', self.ids[0]), ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))) diff --git a/addons/hr_recruitment/models/hr_job.py b/addons/hr_recruitment/models/hr_job.py index edf7d025ad5..b01f311750d 100644 --- a/addons/hr_recruitment/models/hr_job.py +++ b/addons/hr_recruitment/models/hr_job.py @@ -129,7 +129,7 @@ class Job(models.Model): return self.env.ref('hr_recruitment.mt_job_new') def action_get_attachment_tree_view(self): - action = self.env.ref('base.action_attachment').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_attachment") action['context'] = { 'default_res_model': self._name, 'default_res_id': self.ids[0] diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 15b38a1d1d2..d329cb2e572 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -300,7 +300,7 @@ class MassMailing(models.Model): def action_schedule(self): self.ensure_one() - action = self.env.ref('mass_mailing.mailing_mailing_schedule_date_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_mailing_schedule_date_action") action['context'] = dict(self.env.context, default_mass_mailing_id=self.id) return action @@ -332,7 +332,7 @@ class MassMailing(models.Model): return self._action_view_traces_filtered('sent') def _action_view_traces_filtered(self, view_filter): - action = self.env.ref('mass_mailing.mailing_trace_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.mailing_trace_action") action['name'] = _('%s Traces') % (self.name) action['context'] = {'search_default_mass_mailing_id': self.id,} filter_key = 'search_default_filter_%s' % (view_filter) diff --git a/addons/mass_mailing/models/mailing_list.py b/addons/mass_mailing/models/mailing_list.py index 57173bdbdfb..f019721894f 100644 --- a/addons/mass_mailing/models/mailing_list.py +++ b/addons/mass_mailing/models/mailing_list.py @@ -60,7 +60,7 @@ class MassMailingList(models.Model): return [(list.id, "%s (%s)" % (list.name, list.contact_nbr)) for list in self] def action_view_contacts(self): - action = self.env.ref('mass_mailing.action_view_mass_mailing_contacts').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.action_view_mass_mailing_contacts") action['domain'] = [('list_ids', 'in', self.ids)] context = dict(self.env.context, search_default_filter_valid_email_recipient=1, default_list_ids=self.ids) action['context'] = context diff --git a/addons/mass_mailing_crm/models/mailing_mailing.py b/addons/mass_mailing_crm/models/mailing_mailing.py index 19af7cff904..941b1b7b076 100644 --- a/addons/mass_mailing_crm/models/mailing_mailing.py +++ b/addons/mass_mailing_crm/models/mailing_mailing.py @@ -28,7 +28,7 @@ class MassMailing(models.Model): def action_redirect_to_leads_and_opportunities(self): view = 'crm.crm_lead_all_leads' if self.use_leads else 'crm.crm_lead_opportunities' - action = self.env.ref(view).read()[0] + action = self.env.ref(view).sudo().read()[0] action['view_mode'] = 'tree,kanban,graph,pivot,form,calendar' action['domain'] = [('source_id', 'in', self.source_id.ids)] action['context'] = {'active_test': False, 'create': False} diff --git a/addons/mass_mailing_sale/models/mailing_mailing.py b/addons/mass_mailing_sale/models/mailing_mailing.py index 290875290bf..f4a5ee71fac 100644 --- a/addons/mass_mailing_sale/models/mailing_mailing.py +++ b/addons/mass_mailing_sale/models/mailing_mailing.py @@ -29,13 +29,13 @@ class MassMailing(models.Model): mass_mailing.sale_invoiced_amount = 0 def action_redirect_to_quotations(self): - action = self.env.ref('sale.action_quotations_with_onboarding').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations_with_onboarding") action['domain'] = self._get_sale_utm_domain() action['context'] = {'create': False} return action def action_redirect_to_invoiced(self): - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_out_invoice_type") moves = self.env['account.move'].search(self._get_sale_utm_domain()) action['context'] = { 'create': False, diff --git a/addons/mass_mailing_sms/models/mailing_list.py b/addons/mass_mailing_sms/models/mailing_list.py index 56b96df552f..86b27e81566 100644 --- a/addons/mass_mailing_sms/models/mailing_list.py +++ b/addons/mass_mailing_sms/models/mailing_list.py @@ -28,7 +28,7 @@ group by list_id''', (tuple(self.ids), )) def action_view_contacts(self): if self.env.context.get('mailing_sms'): - action = self.env.ref('mass_mailing_sms.mailing_contact_action_sms').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing_sms.mailing_contact_action_sms") action['domain'] = [('list_ids', 'in', self.ids)] context = dict(self.env.context, search_default_filter_valid_sms_recipient=1, default_list_ids=self.ids) action['context'] = context diff --git a/addons/mass_mailing_sms/models/utm.py b/addons/mass_mailing_sms/models/utm.py index 2117a21c5f6..67c41648f97 100644 --- a/addons/mass_mailing_sms/models/utm.py +++ b/addons/mass_mailing_sms/models/utm.py @@ -19,7 +19,7 @@ class UtmCampaign(models.Model): campaign.mailing_sms_count = len(campaign.mailing_sms_ids) def action_create_mass_sms(self): - action = self.env.ref('mass_mailing.action_create_mass_mailings_from_campaign').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing.action_create_mass_mailings_from_campaign") action['context'] = { 'default_campaign_id': self.id, 'default_mailing_type': 'sms', @@ -30,7 +30,7 @@ class UtmCampaign(models.Model): return action def action_redirect_to_mailing_sms(self): - action = self.env.ref('mass_mailing_sms.mailing_mailing_action_sms').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mass_mailing_sms.mailing_mailing_action_sms") action['context'] = { 'default_campaign_id': self.id, 'default_mailing_type': 'sms', diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 0b725a46370..da06b8f9db6 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -394,7 +394,7 @@ class MrpProduction(models.Model): view, if there is only one picking to show. """ self.ensure_one() - action = self.env.ref('stock.action_picking_tree_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all") pickings = self.mapped('picking_ids') if len(pickings) > 1: action['domain'] = [('id', 'in', pickings.ids)] @@ -1247,7 +1247,7 @@ class MrpProduction(models.Model): 'product_expected_qty_uom': expected_qty })) ctx.update({'default_mrp_production_ids': self.ids, 'default_mrp_consumption_warning_line_ids': lines}) - action = self.env.ref('mrp.action_mrp_consumption_warning').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.action_mrp_consumption_warning") action['context'] = ctx return action @@ -1269,7 +1269,7 @@ class MrpProduction(models.Model): 'to_backorder': True })) ctx.update({'default_mrp_production_ids': self.ids, 'default_mrp_production_backorder_line_ids': lines}) - action = self.env.ref('mrp.action_mrp_production_backorder').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.action_mrp_production_backorder") action['context'] = ctx return action @@ -1559,7 +1559,7 @@ class MrpProduction(models.Model): def action_see_move_scrap(self): self.ensure_one() - action = self.env.ref('stock.action_stock_scrap').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap") action['domain'] = [('production_id', '=', self.id)] action['context'] = dict(self._context, default_origin=self.name) return action diff --git a/addons/mrp/models/mrp_workcenter.py b/addons/mrp/models/mrp_workcenter.py index 6f377ceedb4..f90d8e96fff 100644 --- a/addons/mrp/models/mrp_workcenter.py +++ b/addons/mrp/models/mrp_workcenter.py @@ -192,7 +192,7 @@ class MrpWorkcenter(models.Model): return super(MrpWorkcenter, self).write(vals) def action_work_order(self): - action = self.env.ref('mrp.action_work_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.action_work_orders") return action def _get_unavailability_intervals(self, start_datetime, end_datetime): diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py index d559f9f8928..30824a7ff0d 100644 --- a/addons/mrp/models/mrp_workorder.py +++ b/addons/mrp/models/mrp_workorder.py @@ -657,13 +657,13 @@ class MrpWorkorder(models.Model): def action_see_move_scrap(self): self.ensure_one() - action = self.env.ref('stock.action_stock_scrap').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_scrap") action['domain'] = [('workorder_id', '=', self.id)] return action def action_open_wizard(self): self.ensure_one() - action = self.env.ref('mrp.mrp_workorder_mrp_production_form').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_workorder_mrp_production_form") action['res_id'] = self.id return action diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index a879e9d866e..b1b2f449ccf 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -39,7 +39,7 @@ class ProductTemplate(models.Model): def action_used_in_bom(self): self.ensure_one() - action = self.env.ref('mrp.mrp_bom_form_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action") action['domain'] = [('bom_line_ids.product_id', 'in', self.product_variant_ids.ids)] return action @@ -48,7 +48,7 @@ class ProductTemplate(models.Model): template.mrp_product_qty = float_round(sum(template.mapped('product_variant_ids').mapped('mrp_product_qty')), precision_rounding=template.uom_id.rounding) def action_view_mos(self): - action = self.env.ref('mrp.mrp_production_report').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_production_report") action['domain'] = [('state', '=', 'done'), ('product_tmpl_id', 'in', self.ids)] action['context'] = { 'graph_measure': 'product_uom_qty', @@ -97,7 +97,7 @@ class ProductProduct(models.Model): def action_used_in_bom(self): self.ensure_one() - action = self.env.ref('mrp.mrp_bom_form_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.mrp_bom_form_action") action['domain'] = [('bom_line_ids.product_id', '=', self.id)] return action @@ -163,7 +163,7 @@ class ProductProduct(models.Model): product.free_qty = min(ratios_free_qty) // 1 def action_view_bom(self): - action = self.env.ref('mrp.product_open_bom').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("mrp.product_open_bom") template_ids = self.mapped('product_tmpl_id').ids # bom specific to this variant or global to template action['context'] = { diff --git a/addons/mrp_account/models/mrp_production.py b/addons/mrp_account/models/mrp_production.py index b53b38d5a95..d193a98eecd 100644 --- a/addons/mrp_account/models/mrp_production.py +++ b/addons/mrp_account/models/mrp_production.py @@ -79,7 +79,7 @@ class MrpProduction(models.Model): def action_view_stock_valuation_layers(self): self.ensure_one() domain = [('id', 'in', (self.move_raw_ids + self.move_finished_ids + self.scrap_ids.move_id).stock_valuation_layer_ids.ids)] - action = self.env.ref('stock_account.stock_valuation_layer_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action") context = literal_eval(action['context']) context.update(self.env.context) context['no_at_date'] = True diff --git a/addons/payment/models/res_company.py b/addons/payment/models/res_company.py index 405fdff0745..b63d66804ae 100644 --- a/addons/payment/models/res_company.py +++ b/addons/payment/models/res_company.py @@ -22,7 +22,7 @@ class ResCompany(models.Model): # Fail if there are no existing accounts self.env.company.get_chart_of_accounts_or_fail() - action = self.env.ref('payment.action_open_payment_onboarding_payment_acquirer_wizard').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("payment.action_open_payment_onboarding_payment_acquirer_wizard") return action def get_account_invoice_onboarding_steps_states_names(self): diff --git a/addons/payment/wizards/payment_acquirer_onboarding_wizard.py b/addons/payment/wizards/payment_acquirer_onboarding_wizard.py index 2a9a90cc52e..24b6696af59 100644 --- a/addons/payment/wizards/payment_acquirer_onboarding_wizard.py +++ b/addons/payment/wizards/payment_acquirer_onboarding_wizard.py @@ -157,5 +157,5 @@ class PaymentWizard(models.TransientModel): def action_onboarding_other_payment_acquirer(self): self._set_payment_acquirer_onboarding_step_done() - action = self.env.ref('payment.action_payment_acquirer').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("payment.action_payment_acquirer") return action diff --git a/addons/portal/models/portal_mixin.py b/addons/portal/models/portal_mixin.py index a552277aac6..f3e86d80ccd 100644 --- a/addons/portal/models/portal_mixin.py +++ b/addons/portal/models/portal_mixin.py @@ -122,7 +122,7 @@ class PortalMixin(models.AbstractModel): @api.model def action_share(self): - action = self.env.ref('portal.portal_share_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("portal.portal_share_action") action['context'] = {'active_id': self.env.context['active_id'], 'active_model': self.env.context['active_model']} return action diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 374e5dcda48..d2b8e97d264 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -429,13 +429,15 @@ class Project(models.Model): favorite_projects.write({'favorite_user_ids': [(3, self.env.uid)]}) def action_view_tasks(self): - action = self.with_context(active_id=self.id, active_ids=self.ids).env.ref('project.act_project_project_2_project_task_all').read()[0] + action = self.with_context(active_id=self.id, active_ids=self.ids) \ + .env.ref('project.act_project_project_2_project_task_all') \ + .sudo().read()[0] action['display_name'] = self.name return action def action_view_account_analytic_line(self): """ return the action to see all the analytic lines of the project's analytic account """ - action = self.env.ref('analytic.account_analytic_line_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("analytic.account_analytic_line_action") action['context'] = {'default_account_id': self.analytic_account_id.id} action['domain'] = [('account_id', '=', self.analytic_account_id.id)] return action @@ -1320,7 +1322,7 @@ class Task(models.Model): } def action_subtask(self): - action = self.env.ref('project.project_task_action_sub_task').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("project.project_task_action_sub_task") # display all subtasks of current task action['domain'] = [('id', 'child_of', self.id), ('id', '!=', self.id)] diff --git a/addons/project/wizard/project_delete_wizard.py b/addons/project/wizard/project_delete_wizard.py index 7289de88687..9dff0454549 100644 --- a/addons/project/wizard/project_delete_wizard.py +++ b/addons/project/wizard/project_delete_wizard.py @@ -25,4 +25,4 @@ class ProjectDelete(models.TransientModel): def confirm_delete(self): self.with_context(active_test=False).project_ids.unlink() - return self.env.ref('project.open_view_project_all_config').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("project.open_view_project_all_config") diff --git a/addons/project/wizard/project_task_type_delete.py b/addons/project/wizard/project_task_type_delete.py index 4e8f8f4e20e..7efbd55df57 100644 --- a/addons/project/wizard/project_task_type_delete.py +++ b/addons/project/wizard/project_task_type_delete.py @@ -55,16 +55,16 @@ class ProjectTaskTypeDelete(models.TransientModel): project_id = self.env.context.get('default_project_id') if project_id: - action = self.env.ref('project.action_view_task').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") action['domain'] = [('project_id', '=', project_id)] action['context'] = str({ 'pivot_row_groupby': ['user_id'], 'default_project_id': project_id, }) elif self.env.context.get('stage_view'): - action = self.env.ref('project.open_task_type_form').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("project.open_task_type_form") else: - action = self.env.ref('project.action_view_all_task').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_all_task") context = dict(ast.literal_eval(action.get('context')), active_test=True) action['context'] = context diff --git a/addons/purchase/models/product.py b/addons/purchase/models/product.py index 9c53328324a..bb5817086c9 100644 --- a/addons/purchase/models/product.py +++ b/addons/purchase/models/product.py @@ -40,7 +40,7 @@ class ProductTemplate(models.Model): return res def action_view_po(self): - action = self.env.ref('purchase.action_purchase_order_report_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("purchase.action_purchase_order_report_all") action['domain'] = ['&', ('state', 'in', ['purchase', 'done']), ('product_tmpl_id', 'in', self.ids)] action['context'] = { 'graph_measure': 'qty_ordered', @@ -71,7 +71,7 @@ class ProductProduct(models.Model): product.purchased_product_qty = float_round(purchased_data.get(product.id, 0), precision_rounding=product.uom_id.rounding) def action_view_po(self): - action = self.env.ref('purchase.action_purchase_order_report_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("purchase.action_purchase_order_report_all") action['domain'] = ['&', ('state', 'in', ['purchase', 'done']), ('product_id', 'in', self.ids)] action['context'] = { 'graph_measure': 'qty_ordered', diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index 1a2f9539cdf..4cf42269166 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -555,7 +555,7 @@ class PurchaseOrder(models.Model): self.sudo()._read(['invoice_ids']) invoices = self.invoice_ids - action = self.env.ref('account.action_move_in_invoice_type') + action = self.env.ref('account.action_move_in_invoice_type').sudo() result = action.read()[0] # choose the view_mode accordingly if len(invoices) > 1: diff --git a/addons/purchase_stock/models/stock.py b/addons/purchase_stock/models/stock.py index 1b70a05954c..0577d5a10f4 100644 --- a/addons/purchase_stock/models/stock.py +++ b/addons/purchase_stock/models/stock.py @@ -272,7 +272,7 @@ class ProductionLot(models.Model): def action_view_po(self): self.ensure_one() - action = self.env.ref('purchase.purchase_form_action').read()[0] + 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(self._context, create=False) return action diff --git a/addons/sale/models/product_product.py b/addons/sale/models/product_product.py index 2808b3034e1..f530fdf89d7 100644 --- a/addons/sale/models/product_product.py +++ b/addons/sale/models/product_product.py @@ -35,7 +35,7 @@ class ProductProduct(models.Model): return r def action_view_sales(self): - action = self.env.ref('sale.report_all_channels_sales_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.report_all_channels_sales_action") action['domain'] = [('product_id', 'in', self.ids)] action['context'] = { 'pivot_measures': ['product_uom_qty'], diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py index 005bf32a071..4f4e31f3515 100644 --- a/addons/sale/models/product_template.py +++ b/addons/sale/models/product_template.py @@ -77,7 +77,7 @@ class ProductTemplate(models.Model): 'shared product.') % (target_company.name, ', '.join(used_products))) def action_view_sales(self): - action = self.env.ref('sale.report_all_channels_sales_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.report_all_channels_sales_action") action['domain'] = [('product_tmpl_id', 'in', self.ids)] action['context'] = { 'pivot_measures': ['product_uom_qty'], diff --git a/addons/sale/models/res_company.py b/addons/sale/models/res_company.py index 406b7dc9982..f75a010cb3d 100644 --- a/addons/sale/models/res_company.py +++ b/addons/sale/models/res_company.py @@ -33,7 +33,7 @@ class ResCompany(models.Model): def action_open_sale_onboarding_payment_acquirer(self): """ Called by onboarding panel above the quotation list.""" self.env.company.get_chart_of_accounts_or_fail() - action = self.env.ref('sale.action_open_sale_onboarding_payment_acquirer_wizard').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_open_sale_onboarding_payment_acquirer_wizard") return action def _get_sample_sales_order(self): @@ -94,7 +94,7 @@ class ResCompany(models.Model): self.action_close_sale_quotation_onboarding() - action = self.env.ref('sale.action_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action.update({ 'views': [[self.env.ref('sale.view_order_form').id, 'form']], 'view_mode': 'form', diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index cb9e5b0fb96..00966eef9a1 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -570,7 +570,7 @@ class SaleOrder(models.Model): def action_view_invoice(self): invoices = self.mapped('invoice_ids') - action = self.env.ref('account.action_move_out_invoice_type').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_out_invoice_type") if len(invoices) > 1: action['domain'] = [('id', 'in', invoices.ids)] elif len(invoices) == 1: diff --git a/addons/sale/models/sales_team.py b/addons/sale/models/sales_team.py index da500582a2b..9e592561f89 100644 --- a/addons/sale/models/sales_team.py +++ b/addons/sale/models/sales_team.py @@ -130,7 +130,7 @@ class CrmTeam(models.Model): def action_primary_channel_button(self): if self._context.get('in_sales_app'): - return self.env.ref('sale.action_order_report_so_salesteam').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("sale.action_order_report_so_salesteam") return super(CrmTeam, self).action_primary_channel_button() def update_invoiced_target(self, value): diff --git a/addons/sale/models/utm.py b/addons/sale/models/utm.py index 7e3ad525df2..1eec837aa90 100644 --- a/addons/sale/models/utm.py +++ b/addons/sale/models/utm.py @@ -46,13 +46,13 @@ class UtmCampaign(models.Model): campaign.invoiced_amount = 0 def action_redirect_to_quotations(self): - action = self.env.ref('sale.action_quotations_with_onboarding').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations_with_onboarding") action['domain'] = [('campaign_id', '=', self.id)] action['context'] = {'default_campaign_id': self.id} return action def action_redirect_to_invoiced(self): - action = self.env.ref('account.action_move_journal_line').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_journal_line") invoices = self.env['account.move'].search([('campaign_id', '=', self.id)]) action['context'] = { 'create': False, diff --git a/addons/sale_crm/models/crm_lead.py b/addons/sale_crm/models/crm_lead.py index 1c44088bece..59439949a01 100644 --- a/addons/sale_crm/models/crm_lead.py +++ b/addons/sale_crm/models/crm_lead.py @@ -34,12 +34,12 @@ class CrmLead(models.Model): def action_sale_quotations_new(self): if not self.partner_id: - return self.env.ref("sale_crm.crm_quotation_partner_action").read()[0] + return self.env["ir.actions.actions"]._for_xml_id("sale_crm.crm_quotation_partner_action") else: return self.action_new_quotation() def action_new_quotation(self): - action = self.env.ref("sale_crm.sale_action_quotations_new").read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale_crm.sale_action_quotations_new") action['context'] = { 'search_default_opportunity_id': self.id, 'default_opportunity_id': self.id, @@ -56,7 +56,7 @@ class CrmLead(models.Model): return action def action_view_sale_quotation(self): - action = self.env.ref('sale.action_quotations_with_onboarding').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations_with_onboarding") action['context'] = { 'search_default_draft': 1, 'search_default_partner_id': self.partner_id.id, @@ -71,7 +71,7 @@ class CrmLead(models.Model): return action def action_view_sale_order(self): - action = self.env.ref('sale.action_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action['context'] = { 'search_default_partner_id': self.partner_id.id, 'default_partner_id': self.partner_id.id, diff --git a/addons/sale_crm/models/crm_team.py b/addons/sale_crm/models/crm_team.py index 8aa03ec32d8..438d9f08575 100644 --- a/addons/sale_crm/models/crm_team.py +++ b/addons/sale_crm/models/crm_team.py @@ -15,7 +15,7 @@ class CrmTeam(models.Model): def action_primary_channel_button(self): if self._context.get('in_sales_app') and self.use_opportunities: - return self.env.ref('sale.action_order_report_so_salesteam').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("sale.action_order_report_so_salesteam") return super(CrmTeam,self).action_primary_channel_button() def _graph_get_model(self): diff --git a/addons/sale_project/models/project.py b/addons/sale_project/models/project.py index d2834b4105a..9cc79509f71 100644 --- a/addons/sale_project/models/project.py +++ b/addons/sale_project/models/project.py @@ -134,7 +134,7 @@ class ProjectTask(models.Model): so_to_confirm.action_confirm() # redirect create invoice wizard (of the Sales Order) - action = self.env.ref('sale.action_view_sale_advance_payment_inv').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_view_sale_advance_payment_inv") context = literal_eval(action.get('context', "{}")) context.update({ 'active_id': self.sale_order_id.id if len(self) == 1 else False, diff --git a/addons/sale_project/models/sale_order.py b/addons/sale_project/models/sale_order.py index 44885e0b71a..b9bb84afa90 100644 --- a/addons/sale_project/models/sale_order.py +++ b/addons/sale_project/models/sale_order.py @@ -72,7 +72,7 @@ class SaleOrder(models.Model): eval_context.update({'active_id': task_projects.id}) action['context'] = safe_eval(action['context'], eval_context) else: - action = self.env.ref('project.action_view_task').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("project.action_view_task") action['context'] = {} # erase default context to avoid default filter if len(self.tasks_ids) > 1: # cross project kanban task action['views'] = [[False, 'kanban'], [list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'calendar'], [False, 'pivot']] diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 03edef1412b..4e8dbee574d 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -180,7 +180,7 @@ class SaleOrder(models.Model): of given sales order ids. It can either be a in a list or in a form view, if there is only one delivery order to show. ''' - action = self.env.ref('stock.action_picking_tree_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all") pickings = self.mapped('picking_ids') if len(pickings) > 1: diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py index e1d36c38f7d..9fd4787db8a 100644 --- a/addons/sale_stock/models/stock.py +++ b/addons/sale_stock/models/stock.py @@ -135,7 +135,7 @@ class ProductionLot(models.Model): def action_view_so(self): self.ensure_one() - action = self.env.ref('sale.action_orders').read()[0] + 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(self._context, create=False) return action diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py index 0354d95276f..5c66df9526f 100644 --- a/addons/sale_timesheet/models/project.py +++ b/addons/sale_timesheet/models/project.py @@ -132,7 +132,7 @@ class Project(models.Model): } def action_view_timesheet_plan(self): - action = self.env.ref('sale_timesheet.project_timesheet_action_client_timesheet_plan').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale_timesheet.project_timesheet_action_client_timesheet_plan") action['params'] = { 'project_ids': self.ids, } diff --git a/addons/sale_timesheet/models/sale_order.py b/addons/sale_timesheet/models/sale_order.py index 365118bdeb0..f4b47ebc552 100644 --- a/addons/sale_timesheet/models/sale_order.py +++ b/addons/sale_timesheet/models/sale_order.py @@ -52,7 +52,7 @@ class SaleOrder(models.Model): def action_view_timesheet(self): self.ensure_one() - action = self.env.ref('sale_timesheet.timesheet_action_from_sales_order').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale_timesheet.timesheet_action_from_sales_order") action['context'] = {} # erase default filters if self.timesheet_count > 0: action['domain'] = [('so_line', 'in', self.order_line.ids)] diff --git a/addons/sale_timesheet/wizard/project_create_invoice.py b/addons/sale_timesheet/wizard/project_create_invoice.py index 54b964a54f0..b1da206802d 100644 --- a/addons/sale_timesheet/wizard/project_create_invoice.py +++ b/addons/sale_timesheet/wizard/project_create_invoice.py @@ -51,7 +51,7 @@ class ProjectCreateInvoice(models.TransientModel): def action_create_invoice(self): if not self.sale_order_id and self.sale_order_id.invoice_status != 'to invoice': raise UserError(_("The selected Sales Order should contain something to invoice.")) - action = self.env.ref('sale.action_view_sale_advance_payment_inv').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_view_sale_advance_payment_inv") action['context'] = { 'active_ids': self.sale_order_id.ids } diff --git a/addons/sale_timesheet/wizard/project_create_sale_order.py b/addons/sale_timesheet/wizard/project_create_sale_order.py index aad2648a0d0..89abafe8335 100644 --- a/addons/sale_timesheet/wizard/project_create_sale_order.py +++ b/addons/sale_timesheet/wizard/project_create_sale_order.py @@ -75,7 +75,7 @@ class ProjectCreateSalesOrder(models.TransientModel): sale_order = self._create_sale_order() view_form_id = self.env.ref('sale.view_order_form').id - action = self.env.ref('sale.action_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action.update({ 'views': [(view_form_id, 'form')], 'view_mode': 'form', diff --git a/addons/sale_timesheet/wizard/project_task_create_sale_order.py b/addons/sale_timesheet/wizard/project_task_create_sale_order.py index ffabb0ac9c7..9558974aae8 100644 --- a/addons/sale_timesheet/wizard/project_task_create_sale_order.py +++ b/addons/sale_timesheet/wizard/project_task_create_sale_order.py @@ -44,7 +44,7 @@ class ProjectTaskCreateSalesOrder(models.TransientModel): sale_order = self._prepare_sale_order() sale_order.action_confirm() view_form_id = self.env.ref('sale.view_order_form').id - action = self.env.ref('sale.action_orders').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_orders") action.update({ 'views': [(view_form_id, 'form')], 'view_mode': 'form', diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py index cf78e64024d..e937f070ac0 100644 --- a/addons/stock/models/product.py +++ b/addons/stock/models/product.py @@ -431,7 +431,7 @@ class Product(models.Model): return res def action_view_orderpoints(self): - action = self.env.ref('stock.action_orderpoint').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_orderpoint") action['context'] = literal_eval(action.get('context')) action['context'].pop('search_default_trigger', False) action['context'].update({ @@ -451,7 +451,7 @@ class Product(models.Model): def action_view_stock_move_lines(self): self.ensure_one() - action = self.env.ref('stock.stock_move_line_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_line_action") action['domain'] = [('product_id', '=', self.id)] return action @@ -466,7 +466,7 @@ class Product(models.Model): def action_open_product_lot(self): self.ensure_one() - action = self.env.ref('stock.action_production_lot_form').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_production_lot_form") action['domain'] = [('product_id', '=', self.id)] action['context'] = { 'default_product_id': self.id, @@ -513,7 +513,7 @@ class Product(models.Model): def action_product_forecast_report(self): self.ensure_one() - action = self.env.ref('stock.stock_replenishment_product_product_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_replenishment_product_product_action") return action @api.model @@ -758,7 +758,7 @@ class ProductTemplate(models.Model): return self.action_open_quants() else: default_product_id = len(self.product_variant_ids) == 1 and self.product_variant_id.id - action = self.env.ref('stock.action_change_product_quantity').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_change_product_quantity") action['context'] = dict( self.env.context, default_product_id=default_product_id, @@ -780,13 +780,13 @@ class ProductTemplate(models.Model): def action_view_stock_move_lines(self): self.ensure_one() - action = self.env.ref('stock.stock_move_line_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_line_action") action['domain'] = [('product_id.product_tmpl_id', 'in', self.ids)] return action def action_open_product_lot(self): self.ensure_one() - action = self.env.ref('stock.action_production_lot_form').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_production_lot_form") action['domain'] = [('product_id.product_tmpl_id', '=', self.id)] action['context'] = { 'default_product_tmpl_id': self.id, @@ -811,13 +811,13 @@ class ProductTemplate(models.Model): 'product_id': products.id, 'warehouse_ids': warehouse.ids, }, config=False) - action = self.env.ref('stock.action_stock_rules_report').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_stock_rules_report") action['context'] = self.env.context return action def action_product_tmpl_forecast_report(self): self.ensure_one() - action = self.env.ref('stock.stock_replenishment_product_product_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id('stock.stock_replenishment_product_product_action') return action class ProductCategory(models.Model): diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 782683e6462..5709abe0f9e 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -673,7 +673,7 @@ class StockMove(models.Model): """ Opens a wizard to assign SN's name on each move lines. """ self.ensure_one() - action = self.env.ref('stock.act_assign_serial_numbers').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.act_assign_serial_numbers") action['context'] = { 'default_product_id': self.product_id.id, 'default_move_id': self.id, diff --git a/addons/stock/models/stock_orderpoint.py b/addons/stock/models/stock_orderpoint.py index 95de53d45db..f6835d863e7 100644 --- a/addons/stock/models/stock_orderpoint.py +++ b/addons/stock/models/stock_orderpoint.py @@ -291,7 +291,7 @@ class StockWarehouseOrderpoint(models.Model): return replenish report ir.actions.act_window """ - action = self.env.ref('stock.action_orderpoint_replenish').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_orderpoint_replenish") action['context'] = self.env.context orderpoints = self.env['stock.warehouse.orderpoint'].search([]) # Remove previous automatically created orderpoint that has been refilled. diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index f4a6a8c6bc2..a9b50f93168 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -200,7 +200,7 @@ class PickingType(models.Model): self.show_reserved = True def _get_action(self, action_xmlid): - action = self.env.ref(action_xmlid).read()[0] + action = self.env["ir.actions.actions"]._for_xml_id(action_xmlid) if self: action['display_name'] = self.display_name @@ -1365,7 +1365,7 @@ class Picking(models.Model): def action_see_move_scrap(self): self.ensure_one() - action = self.env.ref('stock.action_stock_scrap').read()[0] + 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(self._context, create=False) @@ -1373,14 +1373,14 @@ class Picking(models.Model): def action_see_packages(self): self.ensure_one() - action = self.env.ref('stock.action_package_view').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_package_view") packages = self.move_line_ids.mapped('result_package_id') action['domain'] = [('id', 'in', packages.ids)] action['context'] = {'picking_id': self.id} return action def action_picking_move_tree(self): - action = self.env.ref('stock.stock_move_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action") action['views'] = [ (self.env.ref('stock.view_picking_move_tree').id, 'tree'), ] diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 78cb6166f24..b21a2c362fc 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -202,7 +202,7 @@ class StockQuant(models.Model): def action_view_stock_moves(self): self.ensure_one() - action = self.env.ref('stock.stock_move_line_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_line_action") action['domain'] = [ ('product_id', '=', self.product_id.id), '|', @@ -718,7 +718,7 @@ class QuantPackage(models.Model): self.env['stock.quant']._unlink_zero_quants() def action_view_picking(self): - action = self.env.ref('stock.action_picking_tree_all').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all") domain = ['|', ('result_package_id', 'in', self.ids), ('package_id', 'in', self.ids)] pickings = self.env['stock.move.line'].search(domain).mapped('picking_id') action['domain'] = [('id', 'in', pickings.ids)] diff --git a/addons/stock_account/models/stock_picking.py b/addons/stock_account/models/stock_picking.py index 4169320109a..df5a3e07c6a 100644 --- a/addons/stock_account/models/stock_picking.py +++ b/addons/stock_account/models/stock_picking.py @@ -13,7 +13,7 @@ class StockPicking(models.Model): self.ensure_one() scraps = self.env['stock.scrap'].search([('picking_id', '=', self.id)]) domain = [('id', 'in', (self.move_lines + scraps.move_id).stock_valuation_layer_ids.ids)] - action = self.env.ref('stock_account.stock_valuation_layer_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action") context = literal_eval(action['context']) context.update(self.env.context) context['no_at_date'] = True diff --git a/addons/stock_account/wizard/stock_quantity_history.py b/addons/stock_account/wizard/stock_quantity_history.py index b8e275ba9f6..8897a329628 100644 --- a/addons/stock_account/wizard/stock_quantity_history.py +++ b/addons/stock_account/wizard/stock_quantity_history.py @@ -9,7 +9,7 @@ class StockQuantityHistory(models.TransientModel): def open_at_date(self): active_model = self.env.context.get('active_model') if active_model == 'stock.valuation.layer': - action = self.env.ref('stock_account.stock_valuation_layer_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action") action['domain'] = [('create_date', '<=', self.inventory_datetime)] action['display_name'] = str(self.inventory_datetime) return action diff --git a/addons/stock_landed_costs/models/account_move.py b/addons/stock_landed_costs/models/account_move.py index e792c9512ab..72c1b6b05e2 100644 --- a/addons/stock_landed_costs/models/account_move.py +++ b/addons/stock_landed_costs/models/account_move.py @@ -35,12 +35,12 @@ class AccountMove(models.Model): 'split_method': 'equal', }) for l in landed_costs_lines], }) - action = self.env.ref('stock_landed_costs.action_stock_landed_cost').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_landed_costs.action_stock_landed_cost") return dict(action, view_mode='form', res_id=landed_costs.id, views=[(False, 'form')]) def action_view_landed_costs(self): self.ensure_one() - action = self.env.ref('stock_landed_costs.action_stock_landed_cost').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_landed_costs.action_stock_landed_cost") domain = [('id', 'in', self.landed_costs_ids.ids)] context = dict(self.env.context, default_vendor_bill_id=self.id) views = [(self.env.ref('stock_landed_costs.view_stock_landed_cost_tree2').id, 'tree'), (False, 'form'), (False, 'kanban')] diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index 1e975f323cf..a3484f18ea4 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -273,7 +273,7 @@ class StockLandedCost(models.Model): def action_view_stock_valuation_layers(self): self.ensure_one() domain = [('id', 'in', self.stock_valuation_layer_ids.ids)] - action = self.env.ref('stock_account.stock_valuation_layer_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("stock_account.stock_valuation_layer_action") return dict(action, domain=domain) def _get_targeted_move_ids(self): diff --git a/addons/survey/models/res_partner.py b/addons/survey/models/res_partner.py index 93161222c12..1c7a5e1ec6c 100644 --- a/addons/survey/models/res_partner.py +++ b/addons/survey/models/res_partner.py @@ -25,7 +25,7 @@ class ResPartner(models.Model): self.certifications_company_count = sum(child.certifications_count for child in self.child_ids) def action_view_certifications(self): - action = self.env.ref('survey.res_partner_action_certifications').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("survey.res_partner_action_certifications") action['view_mode'] = 'tree' action['domain'] = ['|', ('partner_id', 'in', self.ids), ('partner_id', 'in', self.child_ids.ids)] diff --git a/addons/website/models/res_company.py b/addons/website/models/res_company.py index 7f89720aa91..ae1ff1f5696 100644 --- a/addons/website/models/res_company.py +++ b/addons/website/models/res_company.py @@ -10,7 +10,7 @@ class Company(models.Model): @api.model def action_open_website_theme_selector(self): - action = self.env.ref('website.theme_install_kanban_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website.theme_install_kanban_action") action['target'] = 'new' return action @@ -25,7 +25,7 @@ class Company(models.Model): def _compute_website_theme_onboarding_done(self): """ The step is marked as done if one theme is installed. """ # we need the same domain as the existing action - action = self.env.ref('website.theme_install_kanban_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website.theme_install_kanban_action") domain = literal_eval(action['domain']) domain.append(('state', '=', 'installed')) installed_themes_count = self.env['ir.module.module'].sudo().search_count(domain) diff --git a/addons/website/models/res_config_settings.py b/addons/website/models/res_config_settings.py index a90210817e9..e509d237a66 100644 --- a/addons/website/models/res_config_settings.py +++ b/addons/website/models/res_config_settings.py @@ -144,7 +144,7 @@ class ResConfigSettings(models.TransientModel): super(ResConfigSettings, self).set_values() def open_template_user(self): - action = self.env.ref('base.action_res_users').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_res_users") action['res_id'] = literal_eval(self.env['ir.config_parameter'].sudo().get_param('base.template_portal_user_id', 'False')) action['views'] = [[self.env.ref('base.view_users_form').id, 'form']] return action @@ -189,6 +189,6 @@ class ResConfigSettings(models.TransientModel): def install_theme_on_current_website(self): self.website_id._force() - action = self.env.ref('website.theme_install_kanban_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website.theme_install_kanban_action") action['target'] = 'main' return action diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 3e810dfa729..60605f6a5c4 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -905,8 +905,8 @@ class Website(models.Model): @api.model def action_dashboard_redirect(self): if self.env.user.has_group('base.group_system') or self.env.user.has_group('website.group_website_designer'): - return self.env.ref('website.backend_dashboard').read()[0] - return self.env.ref('website.action_website').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("website.backend_dashboard") + return self.env["ir.actions.actions"]._for_xml_id("website.action_website") def button_go_website(self): self._force() diff --git a/addons/website/views/website_views.xml b/addons/website/views/website_views.xml index 95241bdbb30..e6ad38088ae 100644 --- a/addons/website/views/website_views.xml +++ b/addons/website/views/website_views.xml @@ -324,7 +324,7 @@ Website: Analytics code - action = model.env.ref('website.backend_dashboard').read()[0] + action = model.env.ref('website.backend_dashboard').sudo().read()[0] diff --git a/addons/website_crm/models/crm_lead.py b/addons/website_crm/models/crm_lead.py index 4e62f808870..0ea63e3b4b1 100644 --- a/addons/website_crm/models/crm_lead.py +++ b/addons/website_crm/models/crm_lead.py @@ -28,7 +28,7 @@ class Lead(models.Model): def action_redirect_to_page_views(self): visitors = self.visitor_ids - action = self.env.ref('website.website_visitor_page_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website.website_visitor_page_action") action['domain'] = [('visitor_id', 'in', visitors.ids)] # avoid grouping if only few records if len(visitors.website_track_ids.ids) > 15 and len(visitors.page_ids.ids) > 1: diff --git a/addons/website_crm_livechat/models/crm_lead.py b/addons/website_crm_livechat/models/crm_lead.py index 1b31eaa3a3f..1b2622aa8a8 100644 --- a/addons/website_crm_livechat/models/crm_lead.py +++ b/addons/website_crm_livechat/models/crm_lead.py @@ -16,6 +16,6 @@ class Lead(models.Model): def action_redirect_to_livechat_sessions(self): visitors = self.visitor_ids - action = self.env.ref('website_livechat.website_visitor_livechat_session_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_livechat.website_visitor_livechat_session_action") action['domain'] = [('livechat_visitor_id', 'in', visitors.ids), ('channel_message_ids', '!=', False)] return action diff --git a/addons/website_event_questions/models/event_question.py b/addons/website_event_questions/models/event_question.py index 0eee8eef37b..4d058569d4c 100644 --- a/addons/website_event_questions/models/event_question.py +++ b/addons/website_event_questions/models/event_question.py @@ -46,7 +46,7 @@ class EventQuestion(models.Model): (Along with secondary pivot and tree views) - A tree view showing textual answers values for text_box questions. """ self.ensure_one() - action = self.env.ref('website_event_questions.action_event_registration_report').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_event_questions.action_event_registration_report") action['domain'] = [('question_id', '=', self.id)] if self.question_type == 'simple_choice': action['views'] = [(False, 'graph'), (False, 'pivot'), (False, 'tree')] diff --git a/addons/website_sale/models/res_company.py b/addons/website_sale/models/res_company.py index d0bce9b1121..9326af678a6 100644 --- a/addons/website_sale/models/res_company.py +++ b/addons/website_sale/models/res_company.py @@ -13,5 +13,5 @@ class ResCompany(models.Model): def action_open_website_sale_onboarding_payment_acquirer(self): """ Called by onboarding panel above the quotation list.""" self.env.company.get_chart_of_accounts_or_fail() - action = self.env.ref('website_sale.action_open_website_sale_onboarding_payment_acquirer_wizard').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_sale.action_open_website_sale_onboarding_payment_acquirer_wizard") return action diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index a34f73dfcd8..2f8b4ec6a22 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -364,7 +364,7 @@ class Website(models.Model): @api.model def action_dashboard_redirect(self): if self.env.user.has_group('sales_team.group_sale_salesman'): - return self.env.ref('website.backend_dashboard').read()[0] + return self.env["ir.actions.actions"]._for_xml_id("website.backend_dashboard") return super(Website, self).action_dashboard_redirect() def get_suggested_controllers(self): diff --git a/addons/website_sale_slides/models/slide_channel.py b/addons/website_sale_slides/models/slide_channel.py index ee288fb8b64..fd6ea6d1f37 100644 --- a/addons/website_sale_slides/models/slide_channel.py +++ b/addons/website_sale_slides/models/slide_channel.py @@ -51,7 +51,7 @@ class Channel(models.Model): self.filtered(lambda channel: not channel.is_published and channel.product_id.is_published).sudo().product_id.write({'is_published': False}) def action_view_sales(self): - action = self.env.ref('website_sale_slides.sale_report_action_slides').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_sale_slides.sale_report_action_slides") action['domain'] = [('product_id', 'in', self.product_id.ids)] return action diff --git a/addons/website_slides/models/res_partner.py b/addons/website_slides/models/res_partner.py index fb2aff8dc9e..a36b23227c8 100644 --- a/addons/website_slides/models/res_partner.py +++ b/addons/website_slides/models/res_partner.py @@ -34,7 +34,7 @@ class ResPartner(models.Model): partner.slide_channel_company_count = 0 def action_view_courses(self): - action = self.env.ref('website_slides.slide_channel_action_overview').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_slides.slide_channel_action_overview") action['name'] = _('Followed Courses') action['domain'] = ['|', ('partner_ids', 'in', self.ids), ('partner_ids', 'in', self.child_ids.ids)] return action diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index fc9af7d1812..1479a51722d 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -453,7 +453,7 @@ class Channel(models.Model): # --------------------------------------------------------- def action_redirect_to_members(self, state=None): - action = self.env.ref('website_slides.slide_channel_partner_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_slides.slide_channel_partner_action") action['domain'] = [('channel_id', 'in', self.ids)] if len(self) == 1: action['display_name'] = _('Attendees of %s', self.name) @@ -600,7 +600,7 @@ class Channel(models.Model): self.env['slide.channel.partner'].sudo().search(removed_channel_partner_domain).unlink() def action_view_slides(self): - action = self.env.ref('website_slides.slide_slide_action').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_slides.slide_slide_action") action['context'] = { 'search_default_published': 1, 'default_channel_id': self.id @@ -609,7 +609,7 @@ class Channel(models.Model): return action def action_view_ratings(self): - action = self.env.ref('website_slides.rating_rating_action_slide_channel').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_slides.rating_rating_action_slide_channel") action['name'] = _('Rating of %s') % (self.name) action['domain'] = [('res_id', 'in', self.ids)] return action diff --git a/addons/website_slides_forum/models/slide_channel.py b/addons/website_slides_forum/models/slide_channel.py index fec0d88564c..c45dc14c0d8 100644 --- a/addons/website_slides_forum/models/slide_channel.py +++ b/addons/website_slides_forum/models/slide_channel.py @@ -16,7 +16,7 @@ class Channel(models.Model): def action_redirect_to_forum(self): self.ensure_one() - action = self.env.ref('website_forum.action_forum_post').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_forum.action_forum_post") action['view_mode'] = 'tree' action['context'] = { 'create': False diff --git a/addons/website_slides_survey/models/survey_survey.py b/addons/website_slides_survey/models/survey_survey.py index 98dc3e6a776..a5edc7f40e3 100644 --- a/addons/website_slides_survey/models/survey_survey.py +++ b/addons/website_slides_survey/models/survey_survey.py @@ -27,7 +27,7 @@ class Survey(models.Model): # --------------------------------------------------------- def action_survey_view_slide_channels(self): - action = self.env.ref('website_slides.slide_channel_action_overview').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("website_slides.slide_channel_action_overview") action['display_name'] = _("Courses") if self.slide_channel_count == 1: action.update({'views': [(False, 'form')], diff --git a/odoo/addons/base/models/ir_actions.py b/odoo/addons/base/models/ir_actions.py index 8ef27fb588a..51440593b2f 100644 --- a/odoo/addons/base/models/ir_actions.py +++ b/odoo/addons/base/models/ir_actions.py @@ -117,7 +117,7 @@ class IrActions(models.Model): user_groups = self.env.user.groups_id for action_id, action_model, binding_type in cr.fetchall(): try: - action = self.env[action_model].browse(action_id) + action = self.env[action_model].sudo().browse(action_id) action_groups = getattr(action, 'groups_id', ()) action_model = getattr(action, 'res_model', False) if action_groups and not action_groups & user_groups: diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py index fc1eb44c29e..3c9a636184d 100644 --- a/odoo/addons/base/models/ir_actions_report.py +++ b/odoo/addons/base/models/ir_actions_report.py @@ -494,7 +494,7 @@ class IrActionsReport(models.Model): report_obj = self.env['ir.actions.report'] conditions = [('report_name', '=', report_name)] context = self.env['res.users'].context_get() - return report_obj.with_context(context).search(conditions, limit=1) + return report_obj.with_context(context).sudo().search(conditions, limit=1) @api.model def barcode(self, barcode_type, value, width=600, height=100, humanreadable=0, quiet=1, mask=None): @@ -694,10 +694,13 @@ class IrActionsReport(models.Model): data = {} data.setdefault('report_type', 'pdf') + # access the report details with sudo() but evaluation context as sudo(False) + self_sudo = self.sudo() + # In case of test environment without enough workers to perform calls to wkhtmltopdf, # fallback to render_html. if (tools.config['test_enable'] or tools.config['test_file']) and not self.env.context.get('force_report_rendering'): - return self._render_qweb_html(res_ids, data=data) + return self_sudo._render_qweb_html(res_ids, data=data) # As the assets are generated during the same transaction as the rendering of the # templates calling them, there is a scenario where the assets are unreachable: when @@ -724,21 +727,21 @@ class IrActionsReport(models.Model): # an asset bundle during the execution of test scenarios. In this case, return # the html version. if isinstance(self.env.cr, TestCursor): - return self.with_context(context)._render_qweb_html(res_ids, data=data)[0] + return self_sudo.with_context(context)._render_qweb_html(res_ids, data=data)[0] save_in_attachment = OrderedDict() if res_ids: # Dispatch the records by ones having an attachment and ones requesting a call to # wkhtmltopdf. - Model = self.env[self.model] + Model = self.env[self_sudo.model] record_ids = Model.browse(res_ids) wk_record_ids = Model - if self.attachment: + if self_sudo.attachment: for record_id in record_ids: - attachment = self.retrieve_attachment(record_id) + attachment = self_sudo.retrieve_attachment(record_id) if attachment: - save_in_attachment[record_id.id] = self._retrieve_stream_from_attachment(attachment) - if not self.attachment_use or not attachment: + save_in_attachment[record_id.id] = self_sudo._retrieve_stream_from_attachment(attachment) + if not self_sudo.attachment_use or not attachment: wk_record_ids += record_id else: wk_record_ids = record_ids @@ -749,7 +752,7 @@ class IrActionsReport(models.Model): # - The report is not fully present in attachments. if save_in_attachment and not res_ids: _logger.info('The PDF report has been generated from attachments.') - return self._post_pdf(save_in_attachment), 'pdf' + return self_sudo._post_pdf(save_in_attachment), 'pdf' if self.get_wkhtmltopdf_state() == 'install': # wkhtmltopdf is not installed @@ -758,14 +761,14 @@ class IrActionsReport(models.Model): # bypassed raise UserError(_("Unable to find Wkhtmltopdf on this system. The PDF can not be created.")) - html = self.with_context(context)._render_qweb_html(res_ids, data=data)[0] + html = self_sudo.with_context(context)._render_qweb_html(res_ids, data=data)[0] # Ensure the current document is utf-8 encoded. html = html.decode('utf-8') - bodies, html_ids, header, footer, specific_paperformat_args = self.with_context(context)._prepare_html(html) + bodies, html_ids, header, footer, specific_paperformat_args = self_sudo.with_context(context)._prepare_html(html) - if self.attachment and set(res_ids) != set(html_ids): + if self_sudo.attachment and set(res_ids) != set(html_ids): raise UserError(_("The report's template '%s' is wrong, please contact your administrator. \n\n" "Can not separate file to save as attachment because the report's template does not contains the attributes 'data-oe-model' and 'data-oe-id' on the div with 'article' classname.") % self.name) @@ -778,8 +781,8 @@ class IrActionsReport(models.Model): set_viewport_size=context.get('set_viewport_size'), ) if res_ids: - _logger.info('The PDF report has been generated for model: %s, records %s.' % (self.model, str(res_ids))) - return self._post_pdf(save_in_attachment, pdf_content=pdf_content, res_ids=html_ids), 'pdf' + _logger.info('The PDF report has been generated for model: %s, records %s.' % (self_sudo.model, str(res_ids))) + return self_sudo._post_pdf(save_in_attachment, pdf_content=pdf_content, res_ids=html_ids), 'pdf' return pdf_content, 'pdf' @api.model @@ -813,10 +816,14 @@ class IrActionsReport(models.Model): data = data and dict(data) or {} + if report_model is not None: + # _render_ may be executed in sudo but evaluation context as real user + report_model = report_model.sudo(False) data.update(report_model._get_report_values(docids, data=data)) else: - docs = self.env[self.model].browse(docids) + # _render_ may be executed in sudo but evaluation context as real user + docs = self.env[self.model].sudo(False).browse(docids) data.update({ 'doc_ids': docids, 'doc_model': self.model, @@ -859,7 +866,7 @@ class IrActionsReport(models.Model): discard_logo_check = self.env.context.get('discard_logo_check') if self.env.is_admin() and not self.env.company.external_report_layout_id and config and not discard_logo_check: - action = self.env.ref('base.action_base_document_layout_configurator').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_base_document_layout_configurator") ctx = action.get('context') py_ctx = json.loads(ctx) if ctx else {} report_action['close_on_report_download'] = True diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py index aced50ecb97..8a3790c0de3 100644 --- a/odoo/addons/base/models/ir_ui_menu.py +++ b/odoo/addons/base/models/ir_ui_menu.py @@ -80,7 +80,7 @@ class IrUiMenu(models.Model): """ Return the ids of the menu items visible to the user. """ # retrieve all menus, and determine which ones are visible context = {'ir.ui.menu.full_list': True} - menus = self.with_context(context).search([]) + menus = self.with_context(context).search([]).sudo() groups = self.env.user.groups_id if not debug: diff --git a/odoo/addons/base/models/res_company.py b/odoo/addons/base/models/res_company.py index f05e029475e..3208df545ae 100644 --- a/odoo/addons/base/models/res_company.py +++ b/odoo/addons/base/models/res_company.py @@ -260,7 +260,7 @@ class Company(models.Model): @api.model def action_open_base_onboarding_company(self): """ Onboarding step for company basic information. """ - action = self.env.ref('base.action_open_base_onboarding_company').read()[0] + action = self.env["ir.actions.actions"]._for_xml_id("base.action_open_base_onboarding_company") action['res_id'] = self.env.company.id return action