Skip to content
Snippets Groups Projects
redner_template.py 8.96 KiB
Newer Older
##############################################################################
#
#    Redner Odoo module
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
#    Copyright © 2016 XCG Consulting <https://xcg-consulting.fr>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

oury.balde's avatar
oury.balde committed
import logging

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

from ..redner import Redner
oury.balde's avatar
oury.balde committed

logger = logging.getLogger(__name__)

Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
_redner = None

LANGUAGE_MJML_MUSTACHE = "text/mjml|mustache"
DEFAULT_LANGUAGE = LANGUAGE_MJML_MUSTACHE

oury.balde's avatar
oury.balde committed

class RednerTemplate(models.Model):
    _name = "redner.template"
oury.balde's avatar
oury.balde committed

    name = fields.Char(
        string="Name",
        required=True,
        help="This is a name of template mjml redner",
    )

    body = fields.Text(
        string="Template remote Id",
        translate=True,
        help="Code for the mjml redner template must be added here",
    )

    slug = fields.Char(string="Slug")

    active = fields.Boolean(
        string="Active",
        default=True,
        help=(
            "If unchecked, it will allow you to hide the "
            "template without removing it."
        ),
oury.balde's avatar
oury.balde committed
    )

    is_mjml = fields.Boolean(
        string="Is MJML",
        default=True,
        help="set to false if your template doesn't contain MJML",
    )

    detected_keywords = fields.Text(
        string="Variables", readonly=True, compute="_compute_keywords"
oury.balde's avatar
oury.balde committed
    )

    language = fields.Selection(
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
        string="Language",
        selection=[
            ("text/html|mustache", "HTML + mustache"),
            (LANGUAGE_MJML_MUSTACHE, "MJML + mustache"),
            (
                "application/vnd.oasis.opendocument.text|od+mustache",
                "OpenDocument + mustache",
            ),
        ],
oury.balde's avatar
oury.balde committed
        required=True,
        help="templating language",
    )

    redner_id = fields.Char(string="Redner ID", readonly=True)

    locale_id = fields.Many2one(
        comodel_name="res.lang",
        string="Locale",
        help="Optional translation language (ISO code).",
    )

    template_data = fields.Binary("Libreoffice Template")

oury.balde's avatar
oury.balde committed
    @property
    def redner(self):
        """Try to avoid Redner instance to be over created"""
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
        global _redner
        if _redner is None:
oury.balde's avatar
oury.balde committed
            # Bypass security rules when reading these configuration params. By
            # default, only some administrators have access to that model.
            config_model = self.env["ir.config_parameter"].sudo()

Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
            _redner = Redner(
oury.balde's avatar
oury.balde committed
                config_model.get_param("redner.api_key"),
                config_model.get_param("redner.server_url"),
                config_model.get_param("redner.account"),
                int(config_model.get_param("redner.timeout", default="20")),
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
        return _redner
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
    @api.model_create_multi
    def create(self, vals_list):
oury.balde's avatar
oury.balde committed
        """Overwrite create to create redner template"""

Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
        for vals in vals_list:
            # Prepare template params according to the selected language.
            # Use template data field if the selected language is "od";
            # otherwise the body field is used.
            produces, language = vals.get("language", DEFAULT_LANGUAGE).split("|")
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
            body, body_format = (
                (vals.get("template_data", ""), "base64")
                if language == "od+mustache"
                else (vals.get("body"), "text")
            )
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
            # We depend on the API for consistency here
            # So raised error should not result with a created template
            vals["redner_id"] = self.redner.templates.account_template_add(
                language=language,
                body=body,
                name=vals.get("name"),
                produces=produces,
                body_format=body_format,
                version=fields.Datetime.to_string(fields.Datetime.now()),
            )
Houzefa Abbasbhay's avatar
Houzefa Abbasbhay committed
        return super().create(vals_list)
oury.balde's avatar
oury.balde committed

    def write(self, vals):
        """Overwrite write to update redner template"""

        # Similar to the "send_to_rednerd_server" method; not worth factoring
        # out.

        # We depend on the API for consistency here
        # So raised error should not result with an updated template
        if "name" in vals:
            self.ensure_one()

            redner_id = self.redner_id
            vals["redner_id"] = vals["name"]
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
        ret = super().write(vals)
        for record in self:
            try:
                produces, language = record.language.split("|")
                body, body_format = (
                    (record.template_data.decode(), "base64")
                    if language == "od+mustache"
                    else (record.body, "text")
                )

                if "name" not in vals:
                    redner_id = record.redner_id
                record.redner.templates.account_template_update(
                    template_id=redner_id,
                    language=language,
                    body=body,
                    name=vals.get("name", ""),
                    body_format=body_format,
                    version=fields.Datetime.to_string(record.write_date),
                )
            except Exception as e:
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
                logger.error("Failed to update redner template :%s", e)
                raise ValidationError(
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
                    _("Failed to update render template, %s") % e
                ) from e
oury.balde's avatar
oury.balde committed

    def unlink(self):
        """Overwrite unlink to delete redner template"""

        # We do NOT depend on the API for consistency here
        # So raised error should not result block template deletion
        try:
            self.redner.templates.account_template_delete(self.redner_id)
        except Exception:
            pass

Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
        return super().unlink()
    def copy(self, default=None):
        self.ensure_one()
        default = dict(default or {}, name=_("%s (copy)") % self.name)
Vincent Hatakeyama's avatar
Vincent Hatakeyama committed
        return super().copy(default)
oury.balde's avatar
oury.balde committed
    @api.depends("body", "template_data")
oury.balde's avatar
oury.balde committed
    def _compute_keywords(self):
oury.balde's avatar
oury.balde committed
        for record in self:
            record.detected_keywords = "\n".join(record.template_varlist_fetch())
oury.balde's avatar
oury.balde committed

    @api.model
    def get_keywords(self):
oury.balde's avatar
oury.balde committed
        """Return template redner keywords"""

        varlist = self.template_varlist_fetch()
        for name in varlist:
            while "." in name:
                name = name[: name.rfind(".")]
                if name not in varlist:
                    varlist.append(name)

        varlist.sort()

        return varlist
oury.balde's avatar
oury.balde committed
    @api.model
    def template_varlist_fetch(self):
        """Retrieve the list of variables present in the template."""
        try:
            if not self.redner_id:
                return []

            return self.redner.templates.account_template_varlist(self.redner_id)
oury.balde's avatar
oury.balde committed

        except Exception as e:
            logger.warning("Failed to fetch account template varlist: %s", e)
oury.balde's avatar
oury.balde committed
            return []

oury.balde's avatar
oury.balde committed
    def send_to_rednerd_server(self):
        """Send templates to the rednerd server. Useful when you have
        existing templates you want to register onto a new rednerd server (or
        with a new user).
        """
oury.balde's avatar
oury.balde committed
        for record in self:
            # Similar to the "write" method override; not worth factoring out.
            templates = record.redner.templates
            produces, language = record.language.split("|")
            body, body_format = (
                (record.template_data.decode(), "base64")
                if language == "od+mustache"
                else (record.body, "text")
            )

oury.balde's avatar
oury.balde committed
            try:
                templates.account_template_update(
                    template_id=record.redner_id,
                    language=language,
                    body=body,
                    name=record.name,
                    body_format=body_format,
                    version=fields.Datetime.to_string(record.write_date),
oury.balde's avatar
oury.balde committed
                )
            except ValidationError:
                record.redner_id = templates.account_template_add(
                    language=language,
                    body=body,
                    name=record.name,
                    body_format=body_format,
                    version=fields.Datetime.to_string(fields.Datetime.now()),
oury.balde's avatar
oury.balde committed
                )