Files
odoo_source/addons/pad/models/pad.py
T
Xavier BOL (xbo) d4d09d0fe1 [FIX] pad: copy the pad_content_field content in pad when genrate pad url
Before this commit, in Project App, when the "Collaborative Pads" is
enabled in the Settings of this app, if the user create a task without
project or in a project that has not the pad enabled and he writes a
description for his new task and select (another) project in which the
pads is enabled then the description field in the form view changed to
have the collaborative pad and the problem is the description is not
copied in the pad and seems erase/delete for the user.

This commit checks if the pad_content_field is not empty after
generating the pad url for the new task/record, if it is the case then
we copy the content in the pad and the user can continue his edition
before saving the task/record.

Step to reproduce:
-----------------

1. Go to Settings of the Project App and enable the Collaborative Pads
2. Go to the Project App, in the Projects dashboard (kanban view of projects)
3. Create two Projects (one called "Project A" and the other called "Project B")
4. Edit the Project B to activate the pad, that is, check the Use
collaborative pads checkbox.
5. Go to the view list of tasks in the "Project A"
6. Click on "Create" button to create a task in the task form view.
7. Write a description for this task.
8. Change the project of this task by the Project B. With this change,
the collaborative pad is actived for this task since the Project B has
the pad, and then the description in the pad is empty rather than have
the content that we have just written.

task-2515150

closes #70401

X-original-commit: d2b55c775d705128fa65c859c5f47bdaf4b705fe
2021-05-17 18:32:53 +00:00

155 lines
5.9 KiB
Python

# -*- 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 markupsafe import Markup
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['ir.config_parameter'].sudo().get_param('pad.pad_server'))
@api.model
def pad_generate_url(self):
pad = {
"server": self.env['ir.config_parameter'].sudo().get_param('pad.pad_server'),
"key": self.env['ir.config_parameter'].sudo().get_param('pad.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):
pad = {
"server": self.env['ir.config_parameter'].sudo().get_param('pad.pad_server'),
"key": self.env['ir.config_parameter'].sudo().get_param('pad.pad_key'),
}
myPad = EtherpadLiteClient(pad['key'], (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 Markup(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]:
pad = {
"server": self.env['ir.config_parameter'].sudo().get_param('pad.pad_server'),
"key": self.env['ir.config_parameter'].sudo().get_param('pad.pad_key'),
}
myPad = EtherpadLiteClient(pad['key'], (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)