summaryrefslogtreecommitdiff
path: root/addons/pad/models/pad.py
blob: 03db10759ac7987b77cb4c1325366fd38c571e73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import logging
import random
import re
import string

import requests

from odoo import api, models, _
from odoo.exceptions import UserError

from ..py_etherpad import EtherpadLiteClient

_logger = logging.getLogger(__name__)


class PadCommon(models.AbstractModel):
    _name = 'pad.common'
    _description = 'Pad Common'

    def _valid_field_parameter(self, field, name):
        return name == 'pad_content_field' or super()._valid_field_parameter(field, name)

    @api.model
    def pad_is_configured(self):
        return bool(self.env.company.pad_server)

    @api.model
    def pad_generate_url(self):
        company = self.env.company.sudo()

        pad = {
            "server": company.pad_server,
            "key": company.pad_key,
        }

        # make sure pad server in the form of http://hostname
        if not pad["server"]:
            return pad
        if not pad["server"].startswith('http'):
            pad["server"] = 'http://' + pad["server"]
        pad["server"] = pad["server"].rstrip('/')
        # generate a salt
        s = string.ascii_uppercase + string.digits
        salt = ''.join([s[random.SystemRandom().randint(0, len(s) - 1)] for i in range(10)])
        # path
        # etherpad hardcodes pad id length limit to 50
        path = '-%s-%s' % (self._name, salt)
        path = '%s%s' % (self.env.cr.dbname.replace('_', '-')[0:50 - len(path)], path)
        # contruct the url
        url = '%s/p/%s' % (pad["server"], path)

        # if create with content
        if self.env.context.get('field_name') and self.env.context.get('model'):
            myPad = EtherpadLiteClient(pad["key"], pad["server"] + '/api')
            try:
                myPad.createPad(path)
            except IOError:
                raise UserError(_("Pad creation failed, either there is a problem with your pad server URL or with your connection."))

            # get attr on the field model
            model = self.env[self.env.context["model"]]
            field = model._fields[self.env.context['field_name']]
            real_field = field.pad_content_field

            res_id = self.env.context.get("object_id")
            record = model.browse(res_id)
            # get content of the real field
            real_field_value = record[real_field] or self.env.context.get('record', {}).get(real_field, '')
            if real_field_value:
                myPad.setHtmlFallbackText(path, real_field_value)

        return {
            "server": pad["server"],
            "path": path,
            "url": url,
        }

    @api.model
    def pad_get_content(self, url):
        company = self.env.company.sudo()
        myPad = EtherpadLiteClient(company.pad_key, (company.pad_server or '') + '/api')
        content = ''
        if url:
            split_url = url.split('/p/')
            path = len(split_url) == 2 and split_url[1]
            try:
                content = myPad.getHtml(path).get('html', '')
            except IOError:
                _logger.warning('Http Error: the credentials might be absent for url: "%s". Falling back.' % url)
                try:
                    r = requests.get('%s/export/html' % url)
                    r.raise_for_status()
                except Exception:
                    _logger.warning("No pad found with url '%s'.", url)
                else:
                    mo = re.search('<body>(.*)</body>', r.content.decode(), re.DOTALL)
                    if mo:
                        content = mo.group(1)

        return content

    # TODO
    # reverse engineer protocol to be setHtml without using the api key

    def write(self, vals):
        self._set_field_to_pad(vals)
        self._set_pad_to_field(vals)
        return super(PadCommon, self).write(vals)

    @api.model
    def create(self, vals):
        # Case of a regular creation: we receive the pad url, so we need to update the
        # corresponding field
        self._set_pad_to_field(vals)
        pad = super(PadCommon, self).create(vals)

        # Case of a programmatical creation (e.g. copy): we receive the field content, so we need
        # to create the corresponding pad
        if self.env.context.get('pad_no_create', False):
            return pad
        for k, field in self._fields.items():
            if hasattr(field, 'pad_content_field') and k not in vals:
                ctx = {
                    'model': self._name,
                    'field_name': k,
                    'object_id': pad.id,
                }
                pad_info = self.with_context(**ctx).pad_generate_url()
                pad[k] = pad_info.get('url')
        return pad

    def _set_field_to_pad(self, vals):
        # Update the pad if the `pad_content_field` is modified
        for k, field in self._fields.items():
            if hasattr(field, 'pad_content_field') and vals.get(field.pad_content_field) and self[k]:
                company = self.env.user.sudo().company_id
                myPad = EtherpadLiteClient(company.pad_key, (company.pad_server or '') + '/api')
                path = self[k].split('/p/')[1]
                myPad.setHtmlFallbackText(path, vals[field.pad_content_field])

    def _set_pad_to_field(self, vals):
        # Update the `pad_content_field` if the pad is modified
        for k, v in list(vals.items()):
            field = self._fields.get(k)
            if hasattr(field, 'pad_content_field'):
                vals[field.pad_content_field] = self.pad_get_content(v)