Skip to content
Snippets Groups Projects
  • oury.balde's avatar
    d2d36e51045a
    Add transactional emails · d2d36e51045a
    oury.balde authored
    Add rednerd template management API to let the user save templates
    for later use with the rendering api:
    
    - POST: /api/v1/template/{account}
    - PUT: /api/v1/template/{account}/{name}
    - DELELTE: /api/v1/template/{account}/{name}
    
    Render a custom template:
    
    - POST: /api/v1/render
    d2d36e51045a
    History
    Add transactional emails
    oury.balde authored
    Add rednerd template management API to let the user save templates
    for later use with the rendering api:
    
    - POST: /api/v1/template/{account}
    - PUT: /api/v1/template/{account}/{name}
    - DELELTE: /api/v1/template/{account}/{name}
    
    Render a custom template:
    
    - POST: /api/v1/render
redner_template.py 5.73 KiB
import logging
import re

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

from .redner import Redner

logger = logging.getLogger(__name__)

PREFIX = "tmpl_"

expression_pattern = re.compile(
    r"""
    (?P<open_bracket>\{\{)    # Match opening brackets {{
    (?P<content>.*)           # Match anything
    (?P<close_bracket>\}\})   # Match closing brackets }}
    """,
    re.VERBOSE,
)

# Match every words which starts with PREFIX
variable_pattern = re.compile(r"(?P<variable>%s[\w]+)" % PREFIX, re.VERBOSE)


def get_redner_tmpl_keys(data) -> list:  # noqa
    """Retrieve every substitution template variables, should be prefixed by
    ``tmpl_``.

    Args:
        data(string): xml body i.e::

            <mjml><mj-body>{{tmpl_replace_me}}</mj-body></mjml>

    Returns:
        list: of keywords
    """

    if not data:
        return []

    return list(
        set(
            [
                v.group("variable")
                for e in expression_pattern.finditer(data)
                for v in variable_pattern.finditer(e.group("content"))
            ]
        )
    )


class RednerTemplate(models.Model):

    _name = "redner.template"

    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,
        required=True,
        help="Code for the mjml redner template must be added here",
    )

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

    publish = fields.Boolean(
        string="Publish",
        default=False,
        help="set to false to add a draft template without publishing",
    )

    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="Keywords", readonly=True, compute="_compute_keywords"
    )

    language = fields.Selection(
        string=_("Language"),
        selection=[("mustache", "mustache"), ("handlebar", "handlebar")],
        default="mustache",
        required=True,
        help="templating language",
    )

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

    produces = fields.Selection(
        selection=[("text/mjml", "MJML"), ("text/html", "HTML")],
        string="Produces",
        default="text/mjml",
    )

    _redner = None

    @property
    def redner(self):
        """Try to avoid Redner instance to be over created"""
        if self._redner is None:

            # 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()

            self._redner = Redner(
                config_model.get_param("redner.api_key"),
                config_model.get_param("redner.server_url"),
                config_model.get_param("redner.account"),
            )

        return self._redner

    @api.model
    def create(self, vals):
        """Overwrite create to create redner template"""

        # 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(
            vals.get("language"),
            vals.get("body"),
            name=vals.get("name"),
            produces=vals.get("produces"),
        )

        return super(RednerTemplate, self).create(vals)

    @api.multi
    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
        try:
            vals["redner_id"] = self.redner.templates.account_template_update(
                self.redner_id,
                vals.get("language", self.language),
                vals.get("body", self.body),
                produces=vals.get("produces"),
            )
        except ValidationError:
            vals["redner_id"] = self.redner.templates.account_template_add(
                vals.get("language", self.language),
                vals.get("body", self.body),
                name=vals.get("name", self.name),
                produces=vals.get("produces"),
            )

        return super(RednerTemplate, self).write(vals)

    @api.multi
    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

        return super(RednerTemplate, self).unlink()

    @api.one
    @api.depends("body")
    def _compute_keywords(self):
        self.detected_keywords = get_redner_tmpl_keys(self.body)

    @api.model
    def get_keywords(self):
        """ Return mjml redner keywords
        """
        return get_redner_tmpl_keys(self.body)

    @api.one
    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).
        """

        # Similar to the "write" method override; not worth factoring out.
        try:
            self.redner.templates.account_template_update(
                self.redner_id, self.language, self.body, self.name
            )
        except ValidationError:
            self.redner_id = self.redner.templates.account_template_add(
                self.language, self.body, self.name
            )