# Copyright (C) 2004-2010 Tiny SPRL (). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models from odoo.exceptions import ValidationError class DocumentPage(models.Model): """This class is use to manage Document.""" _name = "document.page" _inherit = ["mail.thread", "mail.activity.mixin"] _description = "Document Page" _order = "name" _HTML_WIDGET_DEFAULT_VALUE = "


" name = fields.Char("Title", required=True) type = fields.Selection( [("content", "Content"), ("category", "Category")], help="Page type", default="content", ) active = fields.Boolean(default=True) parent_id = fields.Many2one( "document.page", "Category", domain=[("type", "=", "category")] ) child_ids = fields.One2many("document.page", "parent_id", "Children") content = fields.Html( compute="_compute_content", inverse="_inverse_content", search="_search_content", sanitize=False, ) draft_name = fields.Char( string="Name", help="Name for the changes made", compute="_compute_content", inverse="_inverse_content", ) draft_summary = fields.Char( string="Summary", help="Describe the changes made", compute="_compute_content", inverse="_inverse_content", ) template = fields.Html( help="Template that will be used as a content template " "for all new page of this category.", ) history_head = fields.Many2one( "document.page.history", "HEAD", compute="_compute_history_head", store=True, auto_join=True, ) history_ids = fields.One2many( "document.page.history", "page_id", "History", readonly=True, ) menu_id = fields.Many2one("ir.ui.menu", "Menu", readonly=True) content_date = fields.Datetime( "Last Contribution Date", related="history_head.create_date", store=True, index=True, readonly=True, ) content_uid = fields.Many2one( "res.users", "Last Contributor", related="history_head.create_uid", store=True, index=True, readonly=True, ) company_id = fields.Many2one( "res.company", "Company", help="If set, page is accessible only from this company", index=True, ondelete="cascade", default=lambda self: self.env.company, ) backend_url = fields.Char( string="Backend URL", help="Use it to link resources univocally", compute="_compute_backend_url", ) @api.depends("menu_id", "parent_id.menu_id") def _compute_backend_url(self): tmpl = "/web#id={}&model=document.page&view_type=form" for rec in self: url = tmpl.format(rec.id) # retrieve action action = None parent = rec while not action and parent: action = parent.menu_id.action parent = parent.parent_id if action: url += f"&action={action.id}" rec.backend_url = url @api.constrains("parent_id") def _check_parent_id(self): if not self._check_recursion(): raise ValidationError(_("You cannot create recursive categories.")) def _get_page_index(self, link=True): """Return the index of a document.""" self.ensure_one() index = [ "
  • " + subpage._get_page_index() + "
  • " for subpage in self.child_ids ] r = "" if link: r = f'{self.name}' if index: r += "" return r @api.depends("history_head") def _compute_content(self): for rec in self: if rec.type == "category": rec.content = rec._get_page_index(link=False) rec.draft_name = False rec.draft_summary = False else: if rec.history_head: rec.content = rec.history_head.content rec.draft_name = rec.history_head.name rec.draft_summary = rec.history_head.summary else: # html widget's default, so it doesn't trigger ghost save rec.content = self._HTML_WIDGET_DEFAULT_VALUE rec.draft_name = False rec.draft_summary = False def _inverse_content(self): for rec in self: if rec.type == "content" and rec.content != rec.history_head.content: rec._create_history( { "page_id": rec.id, "name": rec.draft_name, "summary": rec.draft_summary, "content": rec.content, } ) def _create_history(self, vals): self.ensure_one() return self.env["document.page.history"].create(vals) def _search_content(self, operator, value): return [("history_head.content", operator, value)] @api.depends("history_ids") def _compute_history_head(self): for rec in self: if rec.history_ids: rec.history_head = rec.history_ids[0] else: rec.history_head = False @api.onchange("parent_id") def _onchange_parent_id(self): """We Set it the right content to the new parent.""" if ( self.content in (False, self._HTML_WIDGET_DEFAULT_VALUE) and self.parent_id.type == "category" ): self.content = self.parent_id.template def unlink(self): menus = self.mapped("menu_id") res = super().unlink() menus.unlink() return res def copy(self, default=None): default = dict( default or {}, name=_("%s (copy)") % self.name, content=self.content, draft_name="1.0", draft_summary=_("summary"), ) return super().copy(default=default) @api.model_create_multi def create(self, vals_list): """Automatically make the category followers follow the new page""" records = super().create(vals_list) for res in records: if ( res.type == "content" and res.parent_id and res.parent_id.type == "category" ): category = res.parent_id category_followers = category.message_partner_ids.ids if category_followers: res.message_subscribe(partner_ids=category_followers) return records def message_subscribe(self, partner_ids, subtype_ids=None): res = super().message_subscribe(partner_ids, subtype_ids) self._toggle_follow_category_documents() return res def message_unsubscribe(self, partner_ids): res = super().message_unsubscribe(partner_ids) self._toggle_follow_category_documents() return res def _toggle_follow_category_documents(self): """ Follow/unfollow all documents in a category based on the category's follower status. """ for rec in self: if rec.type != "category": continue related_docs = self.env["document.page"].search( [("type", "=", "content"), ("parent_id", "=", rec.id)] ) partner_ids = [self.env.user.partner_id.id] if not rec.message_is_follower: related_docs.message_unsubscribe(partner_ids) else: related_docs.message_subscribe(partner_ids)