web_editor/models/html_field_history_mixin.py

141 lines
5.0 KiB
Python
Raw Permalink Normal View History

2024-05-03 12:47:49 +03:00
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from .diff_utils import apply_patch, generate_comparison, generate_patch
class HtmlFieldHistory(models.AbstractModel):
_name = "html.field.history.mixin"
_description = "Field html History"
_html_field_history_size_limit = 300
html_field_history = fields.Json("History data", prefetch=False)
html_field_history_metadata = fields.Json(
"History metadata", compute="_compute_metadata"
)
@api.model
def _get_versioned_fields(self):
"""This method should be overriden
:return: List[string]: A list of name of the fields to be versioned
"""
return []
@api.depends("html_field_history")
def _compute_metadata(self):
for rec in self:
history_metadata = None
if rec.html_field_history:
history_metadata = {}
for field_name in rec.html_field_history:
history_metadata[field_name] = []
for revision in rec.html_field_history[field_name]:
metadata = revision.copy()
metadata.pop("patch")
history_metadata[field_name].append(metadata)
rec.html_field_history_metadata = history_metadata
def write(self, vals):
new_revisions = False
db_contents = None
versioned_fields = self._get_versioned_fields()
vals_contain_versioned_fields = set(vals).intersection(versioned_fields)
if vals_contain_versioned_fields:
self.ensure_one()
db_contents = dict([(f, self[f]) for f in versioned_fields])
fields_data = self.env[self._name]._fields
if any(f in vals and not fields_data[f].sanitize for f in versioned_fields):
raise ValidationError(
"Ensure all versioned fields ( %s ) in model %s are declared as sanitize=True"
% (str(versioned_fields), self._name)
)
# Call super().write before generating the patch to be sure we perform
# the diff on sanitized data
write_result = super().write(vals)
if not vals_contain_versioned_fields:
return write_result
history_revs = self.html_field_history or {}
for field in versioned_fields:
new_content = self[field] or ""
if field not in history_revs:
history_revs[field] = []
old_content = db_contents[field] or ""
if new_content != old_content:
new_revisions = True
patch = generate_patch(new_content, old_content)
revision_id = (
(history_revs[field][0]["revision_id"] + 1)
if history_revs[field]
else 1
)
history_revs[field].insert(
0,
{
"patch": patch,
"revision_id": revision_id,
"create_date": self.env.cr.now().isoformat(),
"create_uid": self.env.uid,
"create_user_name": self.env.user.name,
},
)
limit = self._html_field_history_size_limit
history_revs[field] = history_revs[field][:limit]
# Call super().write again to include the new revision
if new_revisions:
extra_vals = {"html_field_history": history_revs}
write_result = super().write(extra_vals) and write_result
return write_result
def html_field_history_get_content_at_revision(self, field_name, revision_id):
"""Get the requested field content restored at the revision_id.
:param str field_name: the name of the field
:param int revision_id: id of the last revision to restore
:return: string: the restored content
"""
self.ensure_one()
revisions = [
i
for i in self.html_field_history[field_name]
if i["revision_id"] >= revision_id
]
content = self[field_name]
for revision in revisions:
content = apply_patch(content, revision["patch"])
return content
def html_field_history_get_comparison_at_revision(self, field_name, revision_id):
"""For the requested field,
Get a comparison between the current content of the field and the
content restored at the requested revision_id.
:param str field_name: the name of the field
:param int revision_id: id of the last revision to compare
:return: string: the comparison
"""
self.ensure_one()
restored_content = self.html_field_history_get_content_at_revision(
field_name, revision_id
)
return generate_comparison(self[field_name], restored_content)