# Copyright 2024 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError class DmsFieldTemplate(models.Model): _name = "dms.field.template" _inherit = "dms.field.mixin" _description = "Dms Field Template" name = fields.Char(required=True) company_id = fields.Many2one( comodel_name="res.company", string="Company", store=True, index=True, ) storage_id = fields.Many2one( comodel_name="dms.storage", domain=[("save_type", "!=", "attachment")], string="Storage", ) parent_directory_id = fields.Many2one( comodel_name="dms.directory", domain="[('storage_id', '=', storage_id)]", string="Parent directory", ) model_id = fields.Many2one( comodel_name="ir.model", string="Model", domain=[("transient", "=", False), ("model", "!=", "dms.field.template")], index=True, ) model = fields.Char( compute="_compute_model", compute_sudo=True, store=True, string="Model name" ) group_ids = fields.Many2many( comodel_name="dms.access.group", string="Groups", ) user_field_id = fields.Many2one( comodel_name="ir.model.fields", domain="[('model_id', '=', model_id),('relation', '=', 'res.users')]", string="User field", ) directory_format_name = fields.Char( string="Directory format name", default="{{object.display_name}}", help="""You can set expressions to be used for the directory name, e.g.: {{object.name}}""", ) @api.depends("model_id") def _compute_model(self): for item in self: item.model = item.model_id.model def _get_template_from_model(self, model): return self.search([("model", "=", model)], limit=1) @api.model_create_multi def create(self, vals_list): """Create dms directory automatically in the creation in install mode.""" result = super().create(vals_list) if self.env.context.get("install_mode"): for item in result: item_ctx = item.with_context(res_model=item._name, res_id=item.id) item_ctx.create_dms_directory() return result @api.model def create_dms_directory(self): """According to the model, create the directory linked to that record and the subdirectories.""" res_model = self.env.context.get("res_model") res_id = self.env.context.get("res_id") record = self.env[res_model].browse(res_id) directory_model = self.env["dms.directory"].sudo() if res_model == "dms.field.template": return directory_model.create( { "storage_id": record.storage_id.id, "res_id": record.id, "res_model": record._name, "is_root_directory": True, "name": record.display_name, "group_ids": record.group_ids.ids, } ) template = self._get_template_from_model(res_model).sudo() if not template: raise ValidationError(_("There is no template linked to this model")) total_directories = directory_model.search_count( [ ("parent_id", "=", self.parent_directory_id.id), ("res_model", "=", res_model), ("res_id", "=", res_id), ] ) if total_directories > 0: raise ValidationError(_("There is already a linked directory created.")) # Create root directory + files dms_directory_ids = template.dms_directory_ids new_directory = directory_model.create( template._prepare_directory_vals(dms_directory_ids, record) ) self._copy_files_from_directory(dms_directory_ids, new_directory) # Create child directories self._create_child_directories(new_directory, dms_directory_ids) return new_directory def _copy_files_from_directory(self, directory, new_directory): for file in directory.file_ids: file.copy({"directory_id": new_directory.id}) def _prepare_autogenerated_group(self, record): group_name = _("Autogenerated group from %(model)s (%(name)s) #%(id)s") % { "model": record._description, "name": record.display_name, "id": record.id, } vals = { "name": group_name, # We need to set all the permissions so that the user can manage their # documents (directories and files) "perm_create": True, "perm_write": True, "perm_unlink": True, "dms_field_ref": "%s,%s" % (record._name, record.id), "explicit_user_ids": [(5, 0)], } # Apply sudo() because the user may not have permissions to access # ir.model.fields. user_field = self.sudo().user_field_id if user_field: user = record[user_field.name] if user: vals["explicit_user_ids"] += [(4, user.id)] return vals def _get_autogenerated_group(self, record): """Get the existing auto-generated group or create a new one. The permissions of the auto-generated group should be changed to make sure you have the correct data. """ group_model = self.env["dms.access.group"] group_ref = group_model._get_item_from_dms_field_ref(record) if group_ref: group_ref.write(self._prepare_autogenerated_group(record)) return group_ref # Create the autogenerated group linked to the record return group_model.create(self._prepare_autogenerated_group(record)) def _create_child_directories(self, parent, directory): # Create child directories (all leves) + files directory_model = self.env["dms.directory"].sudo() for child_directory in directory.child_directory_ids: child = directory_model.create( { "name": child_directory.name, "is_root_directory": False, "parent_id": parent.id, } ) self._copy_files_from_directory(child_directory, child) self._create_child_directories(child, child_directory) def _prepare_directory_vals(self, directory, record): # Groups of the new directory will be those of the template + auto-generate groups = directory.group_ids groups += self._get_autogenerated_group(record) directory_name = self.env["mail.render.mixin"]._render_template( self.directory_format_name, record._name, record.ids, engine="inline_template", )[record.id] vals = { "storage_id": directory.storage_id.id, "res_id": record.id, "res_model": record._name, "name": directory_name, "group_ids": [(4, group.id) for group in groups], } if not self.parent_directory_id: vals.update({"is_root_directory": True}) else: vals.update( {"parent_id": self.parent_directory_id.id, "inherit_group_ids": False} ) return vals @api.constrains("model_id") def _check_model_id(self): for template in self: if self.env["dms.field.template"].search( [("model_id", "=", template.model_id.id), ("id", "!=", template.id)] ): raise UserError( _("There is already a template created for this model.") )