from odoo import _, fields, models from odoo.exceptions import UserError class DocumentsSentinel: """Sentinel class to prevent context spoofing via RPC.""" pass class IrActionsServer(models.Model): _inherit = 'ir.actions.server' # The documents actions add a check that the current document is in a folder # on which the current action is pinned (to avoid executing server action # means to be used in a different folder). usage = fields.Selection( selection_add=[('documents_embedded', 'Documents')], ondelete={'documents_embedded': 'set ir_actions_server'}, ) def action_open_documents_server_action_view(self): self.check_access('read') form_view = self.env.ref('documents.ir_actions_server_view_form_documents', raise_if_not_found=False) search_view = self.env.ref('documents.ir_actions_server_action_search_documents', raise_if_not_found=False) return { 'context': { 'default_model_id': self.env['ir.model']._get_id('documents.document'), 'default_update_path': 'tag_ids', 'default_usage': 'documents_embedded', }, 'display_name': _('Server Actions'), 'domain': [ ('model_name', '=', 'documents.document'), ('parent_id', '=', False), ], 'help': """

%s

""" % _('No server actions found for Documents!'), 'res_model': 'ir.actions.server', 'target': 'current', 'type': 'ir.actions.act_window', 'view_mode': 'list,form', 'views': [(False, 'list'), (form_view.id if form_view else False, 'form')], 'search_view_id': [search_view.id if search_view else False, 'search'], } def _run(self, records, eval_context): """Override to implement 'Entry Point' security for Documents actions.""" if isinstance(self.env.context.get('__documents_embedded_checked'), DocumentsSentinel): return super()._run(records, eval_context) descendants = self while descendants.child_ids - descendants: descendants |= descendants.child_ids if 'documents_embedded' in descendants.mapped('usage'): self._check_is_document_action_embedded(records) return super(IrActionsServer, self.with_context( __documents_embedded_checked=DocumentsSentinel() ))._run(records, eval_context) return super()._run(records, eval_context) def _check_is_document_action_embedded(self, records): """Verify that the action is explicitly embedded (pinned) on the current folder of the given records.""" for record in records: record_actions = record.available_embedded_actions_ids.action_id record_server_actions_sudo = record_actions.sudo().filtered(lambda a: a.type == 'ir.actions.server') available_server_actions_sudo = self.env['ir.actions.server'].browse(record_server_actions_sudo.ids).sudo() # Add the child actions actions = available_server_actions_sudo while actions.child_ids: next_actions = actions.child_ids - available_server_actions_sudo available_server_actions_sudo |= actions.child_ids actions = next_actions if self not in available_server_actions_sudo: raise UserError(_('This action was not made available on the containing folder.'))