[MERGE] web_*: reorganize files, finish migration and apply guidelines

This commit is contained in:
Thibault Delavallée
2016-08-03 13:43:38 +02:00
77 changed files with 424 additions and 380 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
import openerp.addons.web.http as http
from openerp.addons.web.http import request
import openerp.http as http
from openerp.http import request
class google_calendar_controller(http.Controller):
+1 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import werkzeug
from openerp.addons.web import http
from openerp import http
from openerp.http import request
+1 -1
View File
@@ -6,7 +6,7 @@ from odoo.exceptions import AccessError
from odoo.sql_db import TestCursor
from odoo.tools import config
from odoo.tools.misc import find_in_path
from odoo.addons.web.http import request
from odoo.http import request
from odoo.tools.safe_eval import safe_eval as eval
from odoo.exceptions import UserError
+2 -6
View File
@@ -1,9 +1,5 @@
import sys
# Mock deprecated openerp.addons.web.http module
import openerp.http
sys.modules['openerp.addons.web.http'] = openerp.http
http = openerp.http
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import controllers
import models
+3
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Web',
'category': 'Hidden',
+3
View File
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main, pivot
+65 -65
View File
@@ -1,45 +1,45 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import babel.messages.pofile
import base64
import csv
import datetime
import functools
import glob
import hashlib
import imghdr
import itertools
import jinja2
import json
import logging
import operator
import datetime
import hashlib
import os
import re
import json
import sys
import time
import werkzeug.utils
import werkzeug.wrappers
import zlib
from xml.etree import ElementTree
from cStringIO import StringIO
import babel.messages.pofile
import werkzeug.utils
import werkzeug.wrappers
from openerp.api import Environment
try:
import xlwt
except ImportError:
xlwt = None
import openerp
import openerp.modules.registry
from openerp.modules import get_resource_path
from openerp.tools import topological_sort
from openerp.tools.translate import _
from openerp.tools import ustr
from openerp.tools.misc import str2bool
from openerp import http
from openerp.http import request, serialize_exception as _serialize_exception
from openerp.exceptions import AccessError
import odoo
import odoo.modules.registry
from odoo.api import Environment
from odoo.modules import get_resource_path
from odoo.tools import topological_sort
from odoo.tools.translate import _
from odoo.tools.misc import str2bool
from odoo import http
from odoo.http import request, serialize_exception as _serialize_exception
from odoo.exceptions import AccessError
_logger = logging.getLogger(__name__)
@@ -48,7 +48,7 @@ if hasattr(sys, 'frozen'):
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'views'))
loader = jinja2.FileSystemLoader(path)
else:
loader = jinja2.PackageLoader('openerp.addons.web', "views")
loader = jinja2.PackageLoader('odoo.addons.web', "views")
env = jinja2.Environment(loader=loader, autoescape=True)
env.filters["json"] = json.dumps
@@ -57,7 +57,7 @@ env.filters["json"] = json.dumps
BUNDLE_MAXAGE = 60 * 60 * 24 * 7
#----------------------------------------------------------
# OpenERP Web helpers
# Odoo Web helpers
#----------------------------------------------------------
db_list = http.db_list
@@ -163,16 +163,16 @@ def module_installed(environment):
def module_installed_bypass_session(dbname):
try:
registry = openerp.modules.registry.RegistryManager.get(dbname)
registry = odoo.modules.registry.RegistryManager.get(dbname)
with registry.cursor() as cr:
return module_installed(
environment=Environment(cr, openerp.SUPERUSER_ID, {}))
environment=Environment(cr, odoo.SUPERUSER_ID, {}))
except Exception:
pass
return {}
def module_boot(db=None):
server_wide_modules = openerp.conf.server_wide_modules or ['web']
server_wide_modules = odoo.conf.server_wide_modules or ['web']
serverside = []
dbside = []
for i in server_wide_modules:
@@ -245,7 +245,7 @@ def manifest_list(extension, mods=None, db=None, debug=None):
db: a database name (return all installed modules in that database)
"""
if debug is not None:
_logger.warning("openerp.addons.web.main.manifest_list(): debug parameter is deprecated")
_logger.warning("odoo.addons.web.main.manifest_list(): debug parameter is deprecated")
files = manifest_glob(extension, addons=mods, db=db, include_remotes=True)
return [wp for _fp, wp in files]
@@ -345,7 +345,7 @@ def generate_views(action):
action['views'] = [(view_id, view_modes[0])]
def fix_view_modes(action):
""" For historical reasons, OpenERP has weird dealings in relation to
""" For historical reasons, Odoo has weird dealings in relation to
view_mode and the view_type attribute (on window actions):
* one of the view modes is ``tree``, which stands for both list views
@@ -427,7 +427,7 @@ def binary_content(xmlid=None, model='ir.attachment', id=None, field='datas', un
download=download, mimetype=mimetype, default_mimetype=default_mimetype, env=env)
#----------------------------------------------------------
# OpenERP Web web Controllers
# Odoo Web web Controllers
#----------------------------------------------------------
class Home(http.Controller):
@@ -462,12 +462,12 @@ class Home(http.Controller):
return http.redirect_with_hash(redirect)
if not request.uid:
request.uid = openerp.SUPERUSER_ID
request.uid = odoo.SUPERUSER_ID
values = request.params.copy()
try:
values['databases'] = http.db_list()
except openerp.exceptions.AccessDenied:
except odoo.exceptions.AccessDenied:
values['databases'] = None
if request.httprequest.method == 'POST':
@@ -482,6 +482,7 @@ class Home(http.Controller):
values['error'] = _("Wrong login/password")
return request.render('web.login', values)
class WebClient(http.Controller):
@http.route('/web/webclient/csslist', type='json', auth="none")
@@ -494,21 +495,20 @@ class WebClient(http.Controller):
@http.route('/web/webclient/locale/<string:lang>', type='http', auth="none")
def load_locale(self, lang):
magic_file_finding = [lang.replace("_",'-').lower(), lang.split('_')[0]]
magic_file_finding = [lang.replace("_", '-').lower(), lang.split('_')[0]]
addons_path = http.addons_manifest['web']['addons_path']
#load momentjs locale
momentjs_locale_file = False
# load momentjs locale
momentjs_locale = ""
for code in magic_file_finding:
try:
with open(os.path.join(addons_path, 'web', 'static', 'lib', 'moment', 'locale', code + '.js'), 'r') as f:
momentjs_locale = f.read()
#we found a locale matching so we can exit
# we found a locale matching so we can exit
break
except IOError:
continue
#return the content of the locale
# return the content of the locale
headers = [('Content-Type', 'application/javascript'), ('Cache-Control', 'max-age=%s' % (36000))]
return request.make_response(momentjs_locale, headers)
@@ -551,47 +551,47 @@ class WebClient(http.Controller):
@http.route('/web/webclient/translations', type='json', auth="none")
def translations(self, mods=None, lang=None):
request.disable_db = False
uid = openerp.SUPERUSER_ID
if mods is None:
m = request.registry.get('ir.module.module')
mods = [x['name'] for x in m.search_read(request.cr, uid,
[('state','=','installed')], ['name'])]
mods = [x['name'] for x in request.env['ir.module.module'].sudo().search_read(
[('state', '=', 'installed')], ['name'])]
if lang is None:
lang = request.context["lang"]
res_lang = request.registry.get('res.lang')
ids = res_lang.search(request.cr, uid, [("code", "=", lang)])
langs = request.env['res.lang'].sudo().search([("code", "=", lang)])
lang_params = None
if ids:
lang_params = res_lang.read(request.cr, uid, ids[0],
["name", "direction", "date_format", "time_format", "grouping", "decimal_point", "thousands_sep"])
if langs:
lang_params = langs.read([
"name", "direction", "date_format", "time_format",
"grouping", "decimal_point", "thousands_sep"])[0]
# Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is
# done server-side when the language is loaded, so we only need to load the user's lang.
ir_translation = request.registry.get('ir.translation')
translations_per_module = {}
messages = ir_translation.search_read(request.cr, uid, [('module','in',mods),('lang','=',lang),
('comments','like','openerp-web'),('value','!=',False),
('value','!=','')],
['module','src','value','lang'], order='module')
messages = request.env['ir.translation'].sudo().search_read([
('module', 'in', mods), ('lang', '=', lang),
('comments', 'like', 'openerp-web'), ('value', '!=', False),
('value', '!=', '')],
['module', 'src', 'value', 'lang'], order='module')
for mod, msg_group in itertools.groupby(messages, key=operator.itemgetter('module')):
translations_per_module.setdefault(mod,{'messages':[]})
translations_per_module[mod]['messages'].extend({'id': m['src'],
'string': m['value']} \
for m in msg_group)
translations_per_module.setdefault(mod, {'messages': []})
translations_per_module[mod]['messages'].extend({
'id': m['src'],
'string': m['value']}
for m in msg_group)
return {
'lang_parameters': lang_params,
'modules': translations_per_module,
'multi_lang': len(res_lang.get_installed(request.cr, uid)) > 1,
'multi_lang': len(request.env['res.lang'].sudo().get_installed()) > 1,
}
@http.route('/web/webclient/version_info', type='json', auth="none")
def version_info(self):
return openerp.service.common.exp_version()
return odoo.service.common.exp_version()
@http.route('/web/tests', type='http', auth="none")
def index(self, mod=None, **kwargs):
return request.render('web.qunit_suite')
class Proxy(http.Controller):
@http.route('/web/proxy/post/<path:path>', type='http', auth='user', methods=['GET'])
@@ -613,15 +613,15 @@ class Database(http.Controller):
def _render_template(self, **d):
d.setdefault('manage',True)
d['insecure'] = openerp.tools.config['admin_passwd'] == 'admin'
d['list_db'] = openerp.tools.config['list_db']
d['langs'] = openerp.service.db.exp_list_lang()
d['countries'] = openerp.service.db.exp_list_countries()
d['insecure'] = odoo.tools.config['admin_passwd'] == 'admin'
d['list_db'] = odoo.tools.config['list_db']
d['langs'] = odoo.service.db.exp_list_lang()
d['countries'] = odoo.service.db.exp_list_countries()
# databases list
d['databases'] = []
try:
d['databases'] = http.db_list()
except openerp.exceptions.AccessDenied:
except odoo.exceptions.AccessDenied:
monodb = db_monodb()
if monodb:
d['databases'] = [monodb]
@@ -668,14 +668,14 @@ class Database(http.Controller):
@http.route('/web/database/backup', type='http', auth="none", methods=['POST'], csrf=False)
def backup(self, master_pwd, name, backup_format = 'zip'):
try:
openerp.service.db.check_super(master_pwd)
odoo.service.db.check_super(master_pwd)
ts = datetime.datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S")
filename = "%s_%s.%s" % (name, ts, backup_format)
headers = [
('Content-Type', 'application/octet-stream; charset=binary'),
('Content-Disposition', content_disposition(filename)),
]
dump_stream = openerp.service.db.dump_db(name, None, backup_format)
dump_stream = odoo.service.db.dump_db(name, None, backup_format)
response = werkzeug.wrappers.Response(dump_stream, headers=headers, direct_passthrough=True)
return response
except Exception, e:
@@ -742,7 +742,7 @@ class Session(http.Controller):
@http.route('/web/session/modules', type='json', auth="user")
def modules(self):
# return all installed modules. Web client is smart enough to not load a module twice
return module_installed(environment=request.env(user=openerp.SUPERUSER_ID))
return module_installed(environment=request.env(user=odoo.SUPERUSER_ID))
@http.route('/web/session/save_session_action', type='json', auth="user")
def save_session_action(self, the_action):
@@ -977,7 +977,7 @@ class Binary(http.Controller):
width = 500
if height > 500:
height = 500
content = openerp.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG')
content = odoo.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG')
image_base64 = content and base64.b64decode(content) or self.placeholder()
headers.append(('Content-Length', len(image_base64)))
@@ -1054,14 +1054,14 @@ class Binary(http.Controller):
dbname = db_monodb()
if not uid:
uid = openerp.SUPERUSER_ID
uid = odoo.SUPERUSER_ID
if not dbname:
response = http.send_file(placeholder(imgname + imgext))
else:
try:
# create an empty registry
registry = openerp.modules.registry.Registry(dbname)
registry = odoo.modules.registry.Registry(dbname)
with registry.cursor() as cr:
cr.execute("""SELECT c.logo_web, c.write_date
FROM res_users u
@@ -1155,7 +1155,7 @@ class Export(http.Controller):
fields['.id'] = fields.pop('id', {'string': 'ID'})
fields_sequence = sorted(fields.iteritems(),
key=lambda field: openerp.tools.ustr(field[1].get('string', '')))
key=lambda field: odoo.tools.ustr(field[1].get('string', '')))
records = []
for field_name, field in fields_sequence:
@@ -1275,7 +1275,7 @@ class ExportFormat(object):
raise NotImplementedError()
def from_data(self, fields, rows):
""" Conversion method from OpenERP's export data to whatever the
""" Conversion method from Odoo's export data to whatever the
current export class outputs
:params list fields: a list of fields to export
+17 -14
View File
@@ -1,21 +1,24 @@
from openerp import http
import json
from openerp.http import request, serialize_exception as _serialize_exception
from cStringIO import StringIO
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import deque
import json
from odoo import http
from odoo.http import request
try:
import xlwt
except ImportError:
xlwt = None
class TableExporter(http.Controller):
@http.route('/web/pivot/check_xlwt', type='json', auth='none')
def check_xlwt(self):
return xlwt is not None
@http.route('/web/pivot/export_xls', type='http', auth="user")
def export_xls(self, data, token):
jdata = json.loads(data)
@@ -29,41 +32,41 @@ class TableExporter(http.Controller):
# Step 1: writing headers
headers = jdata['headers']
# x,y: current coordinates
# x,y: current coordinates
# carry: queue containing cell information when a cell has a >= 2 height
# and the drawing code needs to add empty cells below
x, y, carry = 1, 0, deque()
x, y, carry = 1, 0, deque()
for i, header_row in enumerate(headers):
worksheet.write(i,0, '', header_plain)
worksheet.write(i, 0, '', header_plain)
for header in header_row:
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for i in range(nbr_measures):
worksheet.write(y, x+i, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height':cell['height'] - 1})
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + nbr_measures
style = header_plain if 'expanded' in header else header_bold
for i in range(header['width']):
worksheet.write(y, x + i, header['title'] if i == 0 else '', style)
if header['height'] > 1:
carry.append({'x': x, 'height':header['height'] - 1})
x = x + header['width'];
carry.append({'x': x, 'height': header['height'] - 1})
x = x + header['width']
while (carry and carry[0]['x'] == x):
cell = carry.popleft()
for i in range(nbr_measures):
worksheet.write(y, x+i, '', header_plain)
if cell['height'] > 1:
carry.append({'x': x, 'height':cell['height'] - 1})
carry.append({'x': x, 'height': cell['height'] - 1})
x = x + nbr_measures
x, y = 1, y + 1
# Step 2: measure row
if nbr_measures > 1:
worksheet.write(y,0, '', header_plain)
worksheet.write(y, 0, '', header_plain)
for measure in jdata['measure_row']:
style = header_bold if measure['is_bold'] else header_plain
worksheet.write(y, x, measure['measure'], style);
worksheet.write(y, x, measure['measure'], style)
x = x + 1
y = y + 1
+1
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import ir_http
+5 -2
View File
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
import openerp
from openerp import models
from openerp.http import request
from odoo import models
from odoo.http import request
class Http(models.AbstractModel):
+2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import test_js
import test_menu
import test_serving_base
View File
+4
View File
@@ -1,5 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp.tests
class WebSuite(openerp.tests.HttpCase):
def test_01_js(self):
self.phantom_js('/web/tests?mod=web',"","", login='admin')
+2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import unittest
from ..controllers import main
+5 -1
View File
@@ -1,18 +1,22 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import random
import unittest
from openerp.tools import topological_sort
from odoo.tools import topological_sort
def sample(population):
return random.sample(
population,
random.randint(0, min(len(population), 5)))
class TestModulesLoading(unittest.TestCase):
def setUp(self):
self.mods = map(str, range(1000))
def test_topological_sort(self):
random.shuffle(self.mods)
modules = [
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+4 -1
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Web Calendar',
'category': 'Hidden',
@@ -10,7 +13,7 @@ Odoo Web Calendar view.
'version': '2.0',
'depends': ['web'],
'data' : [
'views/web_calendar.xml',
'views/web_calendar_templates.xml',
],
'qweb': [
'static/src/xml/*.xml',
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<template id="assets_backend" name="web_calendar assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/web_calendar/static/src/less/web_calendar.less"/>
@@ -11,5 +10,4 @@
<script type="text/javascript" src="/web_calendar/static/src/js/web_calendar.js"></script>
</xpath>
</template>
</data>
</openerp>
</odoo>
+3
View File
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import controllers
+5 -2
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Odoo Web Diagram',
'category': 'Hidden',
@@ -8,8 +11,8 @@ Openerp Web Diagram view.
""",
'version': '2.0',
'depends': ['web'],
'data' : [
'views/web_diagram.xml',
'data': [
'views/web_diagram_templates.xml',
],
'qweb': [
'static/src/xml/*.xml',
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main
+10 -4
View File
@@ -1,9 +1,15 @@
import openerp
from openerp.tools.safe_eval import safe_eval as eval
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
class DiagramView(openerp.http.Controller):
import odoo
import odoo.http as http
@openerp.http.route('/web_diagram/diagram/get_diagram_info', type='json', auth='user')
from odoo.tools.safe_eval import safe_eval as eval
class DiagramView(http.Controller):
@http.route('/web_diagram/diagram/get_diagram_info', type='json', auth='user')
def get_diagram_info(self, req, id, model, node, connector,
src_node, des_node, label, **kw):
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<odoo>
<data>
<template id="assets_backend" name="web_diagram assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
@@ -10,4 +10,4 @@
</xpath>
</template>
</data>
</openerp>
</odoo>
+2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import controllers
import models
import tests
+3
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Web Editor',
'category': 'Hidden',
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import main
+34 -33
View File
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
from openerp.http import request, STATIC_CACHE
from openerp.addons.web import http
import json
import io
from PIL import Image, ImageFont, ImageDraw
from openerp import tools
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import cStringIO
import werkzeug.wrappers
import time
import io
import json
import logging
import time
import werkzeug.wrappers
from PIL import Image, ImageFont, ImageDraw
from odoo.http import request
from odoo import http, tools
logger = logging.getLogger(__name__)
@@ -25,8 +28,6 @@ class Web_Editor(http.Controller):
#------------------------------------------------------
@http.route('/web_editor/field/html', type='http', auth="user")
def FieldTextHtml(self, model=None, res_id=None, field=None, callback=None, **kwargs):
cr, uid, context = request.cr, request.uid, request.context
kwargs.update(
model=model,
res_id=res_id,
@@ -39,17 +40,16 @@ class Web_Editor(http.Controller):
kwargs[k] = int(kwargs[k])
trans = dict(
lang=kwargs.get('lang', context.get('lang')),
lang=kwargs.get('lang', request.env.context.get('lang')),
translatable=kwargs.get('translatable'),
edit_translations=kwargs.get('edit_translations'),
editable=kwargs.get('enable_editor'))
context.update(trans)
kwargs.update(trans)
record = None
if model and kwargs.get('res_id'):
record = request.registry[model].browse(cr, uid, kwargs.get('res_id'), context)
record = request.env[model].with_context(trans).browse(kwargs.get('res_id'))
kwargs.update(content=record and getattr(record, field) or "")
@@ -141,23 +141,23 @@ class Web_Editor(http.Controller):
# the upload argument doesn't allow us to access the files if more than
# one file is uploaded, as upload references the first file
# therefore we have to recover the files from the request object
Attachments = request.registry['ir.attachment'] # registry for the attachment table
Attachments = request.env['ir.attachment'] # registry for the attachment table
uploads = []
message = None
if not upload: # no image provided, storing the link and the image name
name = url.split("/").pop() # recover filename
attachment_id = Attachments.create(request.cr, request.uid, {
attachment = Attachments.create({
'name': name,
'type': 'url',
'url': url,
'public': True,
'res_model': 'ir.ui.view',
}, request.context)
uploads += Attachments.read(request.cr, request.uid, [attachment_id], ['name', 'mimetype', 'checksum', 'url'], request.context)
})
uploads += attachment.read(['name', 'mimetype', 'checksum', 'url'])
else: # images provided
try:
attachment_ids = []
attachments = request.env['ir.attachment']
for c_file in request.httprequest.files.getlist('upload'):
data = c_file.read()
try:
@@ -172,15 +172,15 @@ class Web_Editor(http.Controller):
except IOError, e:
pass
attachment_id = Attachments.create(request.cr, request.uid, {
attachment = Attachments.create({
'name': c_file.filename,
'datas': data.encode('base64'),
'datas_fname': c_file.filename,
'public': True,
'res_model': 'ir.ui.view',
}, request.context)
attachment_ids.append(attachment_id)
uploads += Attachments.read(request.cr, request.uid, attachment_ids, ['name', 'mimetype', 'checksum', 'url'], request.context)
})
attachments += attachment
uploads += attachments.read(['name', 'mimetype', 'checksum', 'url'])
except Exception, e:
logger.exception("Failed to upload image to attachment")
message = unicode(e)
@@ -199,25 +199,26 @@ class Web_Editor(http.Controller):
Returns a dict mapping attachments which would not be removed (if any)
mapped to the views preventing their removal
"""
cr, uid, context = request.cr, request.uid, request.context
Attachment = request.registry['ir.attachment']
Views = request.registry['ir.ui.view']
Attachment = attachments_to_remove = request.env['ir.attachment']
Views = request.env['ir.ui.view']
attachments_to_remove = []
# views blocking removal of the attachment
removal_blocked_by = {}
for attachment in Attachment.browse(cr, uid, ids, context=context):
for attachment in Attachment.browse(ids):
# in-document URLs are html-escaped, a straight search will not
# find them
url = tools.html_escape(attachment.local_url)
ids = Views.search(cr, uid, ["|", ('arch_db', 'like', '"%s"' % url), ('arch_db', 'like', "'%s'" % url)], context=context)
views = Views.search([
"|",
('arch_db', 'like', '"%s"' % url),
('arch_db', 'like', "'%s'" % url)
])
if ids:
removal_blocked_by[attachment.id] = Views.read(
cr, uid, ids, ['name'], context=context)
if views:
removal_blocked_by[attachment.id] = views.read(['name'])
else:
attachments_to_remove.append(attachment.id)
attachments_to_remove += attachment
if attachments_to_remove:
Attachment.unlink(cr, uid, attachments_to_remove, context=context)
attachments_to_remove.unlink()
return removal_blocked_by
+2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import ir_attachment
import ir_qweb
import ir_ui_view
+10 -28
View File
@@ -1,36 +1,18 @@
# -*- coding: utf-8 -*-
import openerp
from openerp.osv import osv, fields
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import contextlib
from sys import maxint
from openerp.addons.web.http import request
import datetime
import hashlib
import time
import os
from openerp.tools import html_escape as escape, ustr, image_resize_and_sharpen, image_save_for_web
from PIL import Image
import cStringIO
import logging
logger = logging.getLogger(__name__)
from odoo import api, models, fields
class ir_attachment(osv.osv):
class IrAttachment(models.Model):
_inherit = "ir.attachment"
def _local_url_get(self, cr, uid, ids, name, arg, context=None):
result = {}
for attach in self.browse(cr, uid, ids, context=context):
if attach.url:
result[attach.id] = attach.url
else:
result[attach.id] = '/web/image/%s?unique=%s' % (attach.id, attach.checksum)
return result
local_url = fields.Char("Attachment URL", compute='_compute_local_url')
_columns = {
'local_url': fields.function(_local_url_get, string="Attachment URL", type='char'),
}
@api.one
def _compute_local_url(self):
if self.url:
self.local_url = self.url
else:
self.local_url = '/web/image/%s?unique=%s' % (self.id, self.checksum)
+6 -4
View File
@@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
from openerp.osv import orm
from openerp.http import request
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.http import request
class ir_http(orm.AbstractModel):
class IrHttp(models.AbstractModel):
_inherit = 'ir.http'
def _dispatch(self):
@@ -13,4 +15,4 @@ class ir_http(orm.AbstractModel):
request.context['edit_translations'] = True
if request.context.get('lang') != "en_US" and 'translatable' not in request.context:
request.context['translatable'] = True
return super(ir_http, self)._dispatch()
return super(IrHttp, self)._dispatch()
+29 -44
View File
@@ -1,9 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
"""
Web_editor-context rendering needs to add some metadata to rendered and allow to edit fields,
as well as render a few fields differently.
Also, adds methods to convert values back to openerp models.
Also, adds methods to convert values back to Odoo models.
"""
import ast
@@ -22,22 +24,19 @@ import pytz
from dateutil import parser
from lxml import etree, html
from PIL import Image as I
import openerp.modules
import odoo.modules
from odoo import api
import openerp
from openerp.osv import orm, fields
from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import html_escape as escape
from openerp.addons.base.ir import ir_qweb
from odoo import api, models, fields
from odoo.tools import ustr
from odoo.tools import html_escape as escape
from odoo.addons.base.ir import ir_qweb
REMOTE_CONNECTION_TIMEOUT = 2.5
logger = logging.getLogger(__name__)
class QWeb(orm.AbstractModel):
class QWeb(models.AbstractModel):
""" QWeb object for rendering editor stuff
"""
_inherit = 'ir.qweb'
@@ -69,7 +68,7 @@ class QWeb(orm.AbstractModel):
#------------------------------------------------------
class Field(orm.AbstractModel):
class Field(models.AbstractModel):
_name = 'ir.qweb.field'
_inherit = 'ir.qweb.field'
@@ -98,14 +97,14 @@ class Field(orm.AbstractModel):
return self.value_from_string(element.text_content().strip())
class Integer(orm.AbstractModel):
class Integer(models.AbstractModel):
_name = 'ir.qweb.field.integer'
_inherit = 'ir.qweb.field.integer'
value_from_string = int
class Float(orm.AbstractModel):
class Float(models.AbstractModel):
_name = 'ir.qweb.field.float'
_inherit = 'ir.qweb.field.float'
@@ -117,7 +116,7 @@ class Float(orm.AbstractModel):
.replace(lang.decimal_point, '.'))
class ManyToOne(orm.AbstractModel):
class ManyToOne(models.AbstractModel):
_name = 'ir.qweb.field.many2one'
_inherit = 'ir.qweb.field.many2one'
@@ -146,7 +145,7 @@ class ManyToOne(orm.AbstractModel):
return None
class Contact(orm.AbstractModel):
class Contact(models.AbstractModel):
_name = 'ir.qweb.field.contact'
_inherit = 'ir.qweb.field.contact'
@@ -165,18 +164,7 @@ class Contact(orm.AbstractModel):
return node and node.__html__()
def parse_fuzzy(in_format, value):
day_first = in_format.find('%d') < in_format.find('%m')
if '%y' in in_format:
year_first = in_format.find('%y') < in_format.find('%d')
else:
year_first = in_format.find('%Y') < in_format.find('%d')
return parser.parse(value, dayfirst=day_first, yearfirst=year_first)
class Date(orm.AbstractModel):
class Date(models.AbstractModel):
_name = 'ir.qweb.field.date'
_inherit = 'ir.qweb.field.date'
@@ -192,11 +180,10 @@ class Date(orm.AbstractModel):
if not value:
return False
datetime.datetime.strptime(value, DEFAULT_SERVER_DATE_FORMAT)
return value
class DateTime(orm.AbstractModel):
class DateTime(models.AbstractModel):
_name = 'ir.qweb.field.datetime'
_inherit = 'ir.qweb.field.datetime'
@@ -205,13 +192,11 @@ class DateTime(orm.AbstractModel):
attrs = super(DateTime, self).attributes(record, field_name, options, values)
value = record[field_name]
if isinstance(value, basestring):
value = datetime.datetime.strptime(
value, DEFAULT_SERVER_DATETIME_FORMAT)
value = fields.Datetime.from_string(value)
if value:
# convert from UTC (server timezone) to user timezone
value = fields.datetime.context_timestamp(
self._cr, self._uid, timestamp=value, context=self.env.context)
value = value.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
value = fields.Datetime.context_timestamp(self, timestamp=value)
value = fields.Datetime.to_string(value)
attrs['data-oe-original'] = value
return attrs
@@ -240,10 +225,10 @@ class DateTime(orm.AbstractModel):
exc_info=True)
# format back to string
return dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
return fields.Datetime.to_string(dt)
class Text(orm.AbstractModel):
class Text(models.AbstractModel):
_name = 'ir.qweb.field.text'
_inherit = 'ir.qweb.field.text'
@@ -252,7 +237,7 @@ class Text(orm.AbstractModel):
return html_to_text(element)
class Selection(orm.AbstractModel):
class Selection(models.AbstractModel):
_name = 'ir.qweb.field.selection'
_inherit = 'ir.qweb.field.selection'
@@ -270,7 +255,7 @@ class Selection(orm.AbstractModel):
value, selection))
class HTML(orm.AbstractModel):
class HTML(models.AbstractModel):
_name = 'ir.qweb.field.html'
_inherit = 'ir.qweb.field.html'
@@ -284,7 +269,7 @@ class HTML(orm.AbstractModel):
return '\n'.join(content)
class Image(orm.AbstractModel):
class Image(models.AbstractModel):
"""
Widget options:
@@ -361,7 +346,7 @@ class Image(orm.AbstractModel):
if sep and sep != '/':
rest.replace(sep, '/')
path = openerp.modules.get_module_resource(
path = odoo.modules.get_module_resource(
match.group('module'), 'static', *(rest.split('/')))
if not path:
@@ -403,7 +388,7 @@ class Image(orm.AbstractModel):
return out.getvalue().encode('base64')
class Monetary(orm.AbstractModel):
class Monetary(models.AbstractModel):
_name = 'ir.qweb.field.monetary'
_inherit = 'ir.qweb.field.monetary'
@@ -417,7 +402,7 @@ class Monetary(orm.AbstractModel):
.replace(lang.decimal_point, '.'))
class Duration(orm.AbstractModel):
class Duration(models.AbstractModel):
_name = 'ir.qweb.field.duration'
_inherit = 'ir.qweb.field.duration'
@@ -435,14 +420,14 @@ class Duration(orm.AbstractModel):
return float(value)
class RelativeDatetime(orm.AbstractModel):
class RelativeDatetime(models.AbstractModel):
_name = 'ir.qweb.field.relative'
_inherit = 'ir.qweb.field.relative'
# get formatting from ir.qweb.field.relative but edition/save from datetime
class QwebView(orm.AbstractModel):
class QwebView(models.AbstractModel):
_name = 'ir.qweb.field.qweb'
_inherit = 'ir.qweb.field.qweb'
+6 -4
View File
@@ -3,14 +3,16 @@
from lxml import etree
from openerp import models, api
from openerp.tools.translate import encode, xml_translate, html_translate
from odoo import models, api
from odoo.tools.translate import encode, xml_translate, html_translate
def edit_translation_mapping(data):
data = dict(data, model=data['name'].partition(',')[0])
return '<span data-oe-model="%(model)s" data-oe-translation-id="%(id)s" data-oe-translation-state="%(state)s">%(value)s</span>' % data
class ir_translation(models.Model):
class IrTranslation(models.Model):
_inherit = 'ir.translation'
@api.model
@@ -18,7 +20,7 @@ class ir_translation(models.Model):
if self._context.get('edit_translations'):
self.insert_missing(field, records)
return edit_translation_mapping
return super(ir_translation, self)._get_terms_mapping(field, records)
return super(IrTranslation, self)._get_terms_mapping(field, records)
@api.multi
def save_html(self, value):
+30 -42
View File
@@ -1,40 +1,33 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import copy
import openerp
from openerp.exceptions import AccessError
from openerp.osv import osv
from lxml import etree, html
from openerp import api
from odoo.exceptions import AccessError
from odoo import api, models
class view(osv.osv):
class IrUiView(models.Model):
_inherit = 'ir.ui.view'
@api.cr_uid_ids_context
def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None):
if isinstance(id_or_xml_id, list):
id_or_xml_id = id_or_xml_id[0]
if not values:
values = {}
if values.get('editable'):
@api.multi
def render(self, values=None, engine='ir.qweb'):
if values and values.get('editable'):
try:
if not isinstance(id_or_xml_id, (int, long)):
if '.' not in id_or_xml_id:
raise ValueError('Invalid template id: %r' % (id_or_xml_id,))
id_or_xml_id = self.get_view_id(cr, uid, id_or_xml_id, context=context)
self.check_access_rights(cr, uid, 'write')
self.check_access_rule(cr, uid, [id_or_xml_id], 'write', context=context)
self.check_access_rights('write')
self.check_access_rule('write')
except AccessError:
values['editable'] = False
return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context)
return super(IrUiView, self).render(values=values, engine=engine)
#------------------------------------------------------
# Save from html
#------------------------------------------------------
def extract_embedded_fields(self, cr, uid, arch, context=None):
@api.model
def extract_embedded_fields(self, arch):
return arch.xpath('//*[@data-oe-model != "ir.ui.view"]')
@api.model
@@ -62,12 +55,12 @@ class view(osv.osv):
return etree.tostring(
arch_no_whitespace, encoding='unicode', pretty_print=True)
def replace_arch_section(self, cr, uid, view_id, section_xpath, replacement, context=None):
@api.multi
def replace_arch_section(self, section_xpath, replacement):
# the root of the arch section shouldn't actually be replaced as it's
# not really editable itself, only the content truly is editable.
[view] = self.browse(cr, uid, [view_id], context=context)
arch = etree.fromstring(view.arch.encode('utf-8'))
self.ensure_one()
arch = etree.fromstring(self.arch.encode('utf-8'))
# => get the replacement root
if not section_xpath:
root = arch
@@ -84,7 +77,8 @@ class view(osv.osv):
return arch
def to_field_ref(self, cr, uid, el, context=None):
@api.model
def to_field_ref(self, el):
# filter out meta-information inserted in the document
attributes = dict((k, v) for k, v in el.items()
if not k.startswith('data-oe-'))
@@ -94,34 +88,28 @@ class view(osv.osv):
out.tail = el.tail
return out
def save(self, cr, uid, res_id, value, xpath=None, context=None):
@api.multi
def save(self, value, xpath=None):
""" Update a view section. The view section may embed fields to write
:param str model:
:param int res_id:
:param str xpath: valid xpath to the tag to replace
"""
res_id = int(res_id)
arch_section = html.fromstring(
value, parser=html.HTMLParser(encoding='utf-8'))
if xpath is None:
# value is an embedded field on its own, not a view section
self.save_embedded_field(cr, uid, arch_section, context=context)
self.save_embedded_field(arch_section)
return
for el in self.extract_embedded_fields(cr, uid, arch_section, context=context):
self.save_embedded_field(cr, uid, el, context=context)
for el in self.extract_embedded_fields(arch_section):
self.save_embedded_field(el)
# transform embedded field back to t-field
el.getparent().replace(el, self.to_field_ref(cr, uid, el, context=context))
el.getparent().replace(el, self.to_field_ref(el))
arch = self.replace_arch_section(cr, uid, res_id, xpath, arch_section, context=context)
self.write(cr, uid, res_id, {
'arch': self._pretty_arch(arch)
}, context=context)
for view in self:
arch = view.replace_arch_section(xpath, arch_section)
view.write({'arch': view._pretty_arch(arch)})
view = self.browse(cr, openerp.SUPERUSER_ID, res_id, context=context)
if view.model_data_id:
view.model_data_id.write({'noupdate': True})
self.sudo().mapped('model_data_id').write({'noupdate': True})
+29 -31
View File
@@ -1,43 +1,41 @@
# -*- coding: utf-8 -*-
from openerp.osv import orm, fields
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields
class test_converter(orm.Model):
class ConverterTest(models.Model):
_name = 'web_editor.converter.test'
# disable translation export for those brilliant field labels and values
_translate = False
_columns = {
'char': fields.char(),
'integer': fields.integer(),
'float': fields.float(),
'numeric': fields.float(digits=(16, 2)),
'many2one': fields.many2one('web_editor.converter.test.sub'),
'binary': fields.binary(),
'date': fields.date(),
'datetime': fields.datetime(),
'selection': fields.selection([
(1, "réponse A"),
(2, "réponse B"),
(3, "réponse C"),
(4, "réponse <D>"),
]),
'selection_str': fields.selection([
('A', "Qu'il n'est pas arrivé à Toronto"),
('B', "Qu'il était supposé arriver à Toronto"),
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
('D', "La réponse D"),
], string=u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
u"qu'il fait une escale technique à St Claude, on dit:"),
'html': fields.html(),
'text': fields.text(),
}
char = fields.Char()
integer = fields.Integer()
float = fields.Float()
numeric = fields.Float(digits=(16, 2))
many2one = fields.Many2one('web_editor.converter.test.sub')
binary = fields.Binary()
date = fields.Date()
datetime = fields.Datetime()
selection = fields.Selection([
(1, "réponse A"),
(2, "réponse B"),
(3, "réponse C"),
(4, "réponse <D>"),
])
selection_str = fields.Selection([
('A', "Qu'il n'est pas arrivé à Toronto"),
('B', "Qu'il était supposé arriver à Toronto"),
('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
('D', "La réponse D"),
], string=u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
u"qu'il fait une escale technique à St Claude, on dit:")
html = fields.Html()
text = fields.Text()
class test_converter_sub(orm.Model):
class ConverterTestSub(models.Model):
_name = 'web_editor.converter.test.sub'
_columns = {
'name': fields.char(),
}
name = fields.Char()
+2 -2
View File
@@ -110,7 +110,7 @@
<span class="text-muted">— or — </span>
<label for="iamgeurl">Add an image URL</label>
<div class="form-group btn-group">
<input class="form-control url pull-left" id="iamgeurl" name="url" placeholder="http://openerp.com/logo.png" style="width: 320px;" type="text"/>
<input class="form-control url pull-left" id="iamgeurl" name="url" placeholder="https://www.odoo.com/logo.png" style="width: 320px;" type="text"/>
<button class="btn btn-default" type="submit">Add</button>
</div>
</div>
@@ -238,7 +238,7 @@
<label class="control-label" for="link-external">URL or Email Address
</label>
</h4>
<input class="form-control url email-address url-source" id="link-external" placeholder="http://openerp.com" type="text"/>
<input class="form-control url email-address url-source" id="link-external" placeholder="https://www.odoo.com" type="text"/>
</li>
</ul>
<div class="form-horizontal link-style">
+2
View File
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import test_ui
import test_converter
+4 -2
View File
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import textwrap
import unittest
from lxml import etree, html
from lxml.builder import E
from openerp.tests import common
from openerp.addons.web_editor.models.ir_qweb import html_to_text
from odoo.tests import common
from odoo.addons.web_editor.models.ir_qweb import html_to_text
class TestHTMLToText(unittest.TestCase):
+5 -2
View File
@@ -1,7 +1,10 @@
import openerp.tests
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import odoo.tests
class TestUi(openerp.tests.HttpCase):
class TestUi(odoo.tests.HttpCase):
post_install = True
at_install = False
+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<odoo>
<template id="layout" name="Editor layout">&lt;!DOCTYPE html&gt;
<html t-att-lang="lang and lang.replace('_', '-')"
@@ -66,4 +66,4 @@
</t>
</template>
</openerp>
</odoo>
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+4 -1
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Base Kanban',
'category': 'Hidden',
@@ -9,7 +12,7 @@ Odoo Web kanban view.
'version': '2.0',
'depends': ['web'],
'data' : [
'views/web_kanban.xml',
'views/web_kanban_templates.xml',
],
'qweb' : [
'static/src/xml/*.xml',
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<template id="assets_backend" name="web_kanban assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/web_kanban/static/src/less/kanban_dashboard.less"/>
@@ -15,5 +14,4 @@
<script type="text/javascript" src="/web_kanban/static/src/js/compatibility.js"></script>
</xpath>
</template>
</data>
</openerp>
</odoo>
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+4 -1
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Gauge Widget for Kanban',
'category': 'Hidden',
@@ -7,7 +10,7 @@ This widget allows to display gauges using d3 library.
'version': '1.0',
'depends': ['web_kanban'],
'data' : [
'views/web_kanban_gauge.xml',
'views/web_kanban_gauge_templates.xml',
],
'qweb': [
],
@@ -1,10 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<template id="assets_backend" name="web_kanban_gauge assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/web_kanban_gauge/static/src/js/kanban_gauge.js"></script>
</xpath>
</template>
</data>
</openerp>
</odoo>
+2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Planner',
'category': 'Extra Tools',
+2
View File
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import web_planner
+5 -3
View File
@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from urllib import urlencode
from openerp import api, models, fields
from odoo import api, models, fields
class Planner(models.Model):
@@ -11,8 +13,8 @@ class Planner(models.Model):
Each Planner has link to ir.ui.menu record that is a top menu used to display the
planner launcher(progressbar)
Method _prepare_<planner_application>_data(self, cr, uid, context) that
generate the values used to display in specific planner pages
Method _prepare_<planner_application>_data(self) (model method) that
generates the values used to display in specific planner pages
"""
_name = 'web.planner'
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import controllers
import models
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Odoo Settings Dashboard',
'version': '1.0',
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import main
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from openerp import http
from openerp.exceptions import AccessError
from openerp.http import request
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo import fields, http
from odoo.exceptions import AccessError
from odoo.http import request
class WebSettingsDashboard(http.Controller):
@@ -47,7 +48,7 @@ class WebSettingsDashboard(http.Controller):
# See update.py for this computation
limit_date = datetime.now() - timedelta(15)
enterprise_users = request.env['res.users'].search_count([("login_date", ">=", limit_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)), ('share', '=', False)])
enterprise_users = request.env['res.users'].search_count([("login_date", ">=", fields.Datetime.to_string(limit_date)), ('share', '=', False)])
return {
'apps': {
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import res_users
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import models, api
from odoo import models, api
class Users(models.Model):
class ResUsers(models.Model):
_inherit = 'res.users'
@api.model
+3
View File
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import models
+4 -2
View File
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Tours',
'category': 'Hidden',
@@ -10,8 +12,8 @@ Odoo Web tours.
'version': '0.1',
'depends': ['web'],
'data': [
'assets.xml',
'views/tour.xml'
'views/tour_templates.xml',
'views/tour_views.xml'
],
'qweb': [
"static/src/xml/*.xml",
+3
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import ir_http
import ir_ui_menu
import tour
+6 -2
View File
@@ -1,5 +1,9 @@
from openerp import models
from openerp.http import request
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.http import request
class Http(models.AbstractModel):
_inherit = 'ir.http'
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<odoo>
<data>
<template id="assets_common" name="tours assets" inherit_id="web.assets_common">
<xpath expr="." position="inside">
@@ -17,4 +17,4 @@
</xpath>
</template>
</data>
</openerp>
</odoo>
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="edit_tour_list" model="ir.ui.view">
<field name="model">web_tour.tour</field>
<field name="arch" type="xml">
@@ -28,5 +26,4 @@
<field name="search_view_id" ref="edit_tour_search"/>
</record>
<menuitem action="edit_tour_action" id="menu_tour_action" parent="base.next_id_2" sequence="5"/>
</data>
</openerp>
</odoo>
+1 -1
View File
@@ -13,7 +13,7 @@ import werkzeug.wrappers
import openerp
from openerp.addons.web.controllers.main import WebClient, Binary
from openerp.addons.web import http
from openerp import http
from openerp.http import request
from openerp.osv.orm import browse_record
from openerp.addons.website.models.website import slug
+1 -1
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from openerp.addons.web.http import request
from openerp.http import request
from openerp.osv import orm
import ast
+1 -1
View File
@@ -21,7 +21,7 @@ from openerp.tools.translate import _
from openerp.osv import orm, osv, fields
from openerp.tools import html_escape as escape, ustr, image_resize_and_sharpen, image_save_for_web
from openerp.tools.safe_eval import safe_eval
from openerp.addons.web.http import request
from openerp.http import request
from werkzeug.exceptions import NotFound
logger = logging.getLogger(__name__)
+25 -25
View File
@@ -7,8 +7,11 @@ from lxml.html import builder as h
from openerp.tests import common
def attrs(**kwargs):
return dict(('data-oe-%s' % key, str(value)) for key, value in kwargs.iteritems())
class TestViewSaving(common.TransactionCase):
def eq(self, a, b):
self.assertEqual(a.tag, b.tag)
@@ -42,8 +45,7 @@ class TestViewSaving(common.TransactionCase):
})
def test_embedded_extraction(self):
fields = self.registry('ir.ui.view').extract_embedded_fields(
self.cr, self.uid, self.arch, context=None)
fields = self.env['ir.ui.view'].extract_embedded_fields(self.arch)
expect = [
h.SPAN("My Company", attrs(model='res.company', id=1, field='name', type='char')),
@@ -56,7 +58,7 @@ class TestViewSaving(common.TransactionCase):
embedded = h.SPAN("+00 00 000 00 0 000", attrs(
model='res.company', id=1, field='phone', type='char'))
self.registry('ir.ui.view').save_embedded_field(self.cr, self.uid, embedded)
self.env['ir.ui.view'].save_embedded_field(embedded)
company = self.registry('res.company').browse(self.cr, self.uid, 1)
self.assertEqual(company.phone, "+00 00 000 00 0 000")
@@ -66,37 +68,37 @@ class TestViewSaving(common.TransactionCase):
e1 = h.SPAN("My Company", attrs(model='res.company', id=1, field='name'))
e2 = h.SPAN("Leeroy Jenkins", attrs(model='res.company', id=1, field='name'))
View = self.registry('ir.ui.view')
View = self.env['ir.ui.view']
View.save_embedded_field(self.cr, self.uid, e1)
View.save_embedded_field(e1)
# FIXME: more precise exception
with self.assertRaises(Exception):
View.save_embedded_field(self.cr, self.uid, e2)
View.save_embedded_field(e2)
def test_embedded_to_field_ref(self):
View = self.registry('ir.ui.view')
View = self.env['ir.ui.view']
embedded = h.SPAN("My Company", attrs(expression="bob"))
self.eq(
View.to_field_ref(self.cr, self.uid, embedded, context=None),
View.to_field_ref(embedded),
h.SPAN({'t-field': 'bob'})
)
def test_to_field_ref_keep_attributes(self):
View = self.registry('ir.ui.view')
View = self.env['ir.ui.view']
att = attrs(expression="bob", model="res.company", id=1, field="name")
att['id'] = "whop"
att['class'] = "foo bar"
embedded = h.SPAN("My Company", att)
self.eq(View.to_field_ref(self.cr, self.uid, embedded, context=None),
self.eq(View.to_field_ref(embedded),
h.SPAN({'t-field': 'bob', 'class': 'foo bar', 'id': 'whop'}))
def test_replace_arch(self):
replacement = h.P("Wheee")
result = self.registry('ir.ui.view').replace_arch_section(
self.cr, self.uid, self.view_id, None, replacement)
self.cr, self.uid, [self.view_id], None, replacement)
self.eq(result, h.DIV("Wheee"))
@@ -104,7 +106,7 @@ class TestViewSaving(common.TransactionCase):
replacement = h.DIV(h.P("Wheee"))
result = self.registry('ir.ui.view').replace_arch_section(
self.cr, self.uid, self.view_id, None, replacement)
self.cr, self.uid, [self.view_id], None, replacement)
self.eq(result, replacement)
@@ -112,7 +114,7 @@ class TestViewSaving(common.TransactionCase):
replacement = h.H1("I am the greatest title alive!")
result = self.registry('ir.ui.view').replace_arch_section(
self.cr, self.uid, self.view_id, '/div/div[1]/h3',
self.cr, self.uid, [self.view_id], '/div/div[1]/h3',
replacement)
self.eq(result, h.DIV(
@@ -134,12 +136,12 @@ class TestViewSaving(common.TransactionCase):
def test_multiple_xpath_matches(self):
with self.assertRaises(ValueError):
self.registry('ir.ui.view').replace_arch_section(
self.cr, self.uid, self.view_id, '/div/div/h3',
self.cr, self.uid, [self.view_id], '/div/div/h3',
h.H6("Lol nope"))
def test_save(self):
Company = self.registry('res.company')
View = self.registry('ir.ui.view')
View = self.env['ir.ui.view']
replacement = ET.tostring(h.DIV(
h.H3("Column 2"),
@@ -149,14 +151,13 @@ class TestViewSaving(common.TransactionCase):
h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund", type='char'))),
)
), encoding='utf-8')
View.save(self.cr, self.uid, res_id=self.view_id, value=replacement,
xpath='/div/div[2]')
View.browse(self.view_id).save(value=replacement, xpath='/div/div[2]')
company = Company.browse(self.cr, self.uid, 1)
self.assertEqual(company.name, "Acme Corporation")
self.assertEqual(company.phone, "+12 3456789")
self.eq(
ET.fromstring(View.browse(self.cr, self.uid, self.view_id).arch.encode('utf-8')),
ET.fromstring(View.browse(self.view_id).arch.encode('utf-8')),
h.DIV(
h.DIV(
h.H3("Column 1"),
@@ -180,7 +181,7 @@ class TestViewSaving(common.TransactionCase):
'arch':'<t><p><h1>hello world</h1></p></t>',
'type':'qweb'
})
view = self.registry('ir.ui.view').browse(self.cr, self.uid, view_id)
view = self.env['ir.ui.view'].browse(view_id)
# script and style text nodes should not escaped client side
replacement = '<script>1 && "hello & world"</script>'
view.save(replacement, xpath='/t/p/h1')
@@ -212,26 +213,25 @@ class TestViewSaving(common.TransactionCase):
node = html.tostring(h.SPAN(
"Acme Corporation",
attrs(model='res.company', id=company_id, field="name", expression='bob', type='char')))
self.registry('ir.ui.view').save(self.cr, self.uid, res_id=company_id,value=node)
View = self.env['ir.ui.view']
View.browse(company_id).save(value=node)
company = Company.browse(self.cr, self.uid, company_id)
self.assertEqual(company.name, "Acme Corporation")
def test_field_tail(self):
View = self.registry('ir.ui.view')
View = self.env['ir.ui.view']
replacement = ET.tostring(
h.LI(h.SPAN("+12 3456789", attrs(
model='res.company', id=1, type='char',
field='phone', expression="edmund")),
"whop whop"
), encoding="utf-8")
View.save(self.cr, self.uid, res_id = self.view_id, value=replacement,
xpath='/div/div[2]/ul/li[3]')
View.browse(self.view_id).save(value=replacement, xpath='/div/div[2]/ul/li[3]')
self.eq(
ET.fromstring(View.browse(self.cr, self.uid, self.view_id).arch.encode('utf-8')),
ET.fromstring(View.browse(self.view_id).arch.encode('utf-8')),
h.DIV(
h.DIV(
h.H3("Column 1"),
+2 -2
View File
@@ -5,8 +5,8 @@ import json
import werkzeug
from openerp import tools
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http
from openerp.http import request
from openerp.addons.website.models.website import slug, unslug
from odoo.addons.website.controllers.main import QueryURL
from openerp.exceptions import UserError
@@ -7,8 +7,8 @@ import werkzeug
from collections import OrderedDict
from odoo import fields
from odoo.addons.web import http
from odoo.addons.web.http import request
from odoo import http
from odoo.http import request
from odoo.addons.website.models.website import slug, unslug
from odoo.addons.website_partner.controllers.main import WebsitePartnerPage
from odoo.tools.translate import _
+2 -2
View File
@@ -3,10 +3,10 @@
import openerp
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp import http
from openerp.addons.website.models.website import unslug
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.http import request
import werkzeug.urls
+2 -2
View File
@@ -10,9 +10,9 @@ import base64
import openerp
from openerp import tools, _
from openerp.addons.web import http
from openerp import http
from openerp.addons.web.controllers.main import binary_content
from openerp.addons.web.http import request
from openerp.http import request
from openerp.addons.website.models.website import slug
@@ -2,8 +2,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import json
from odoo.addons.web import http
from odoo.addons.web.http import request
from odoo import http
from odoo.http import request
from odoo.tools import html_escape as escape
+1 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import werkzeug
from openerp.addons.web import http
from openerp import http
from openerp.http import request
@@ -5,8 +5,8 @@ import werkzeug.urls
from odoo import fields
from odoo.addons.web import http
from odoo.addons.web.http import request
from odoo import http
from odoo.http import request
from odoo.addons.website.models.website import unslug
from odoo.tools.translate import _
+2 -2
View File
@@ -2,8 +2,8 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http
from openerp.http import request
import werkzeug
import datetime
import time
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp import http
from openerp.http import request
import datetime
from werkzeug.exceptions import NotFound
@@ -3,7 +3,7 @@
import urlparse
from odoo import http
from odoo.addons.web.http import request
from odoo.http import request
from odoo.addons.website_mail.controllers.main import WebsiteMail
+1 -1
View File
@@ -3,7 +3,7 @@ import base64
import logging
import werkzeug
from openerp.addons.web import http
from openerp import http
from openerp.exceptions import AccessError, UserError
from openerp.http import request
from openerp.tools.translate import _
+2 -2
View File
@@ -3,8 +3,8 @@
import json
from odoo import _
from odoo.addons.web import http
from odoo.addons.web.http import request
from odoo import http
from odoo.http import request
class Twitter(http.Controller):
+1 -1
View File
@@ -10,7 +10,7 @@ from collections import OrderedDict
from openerp import api, models, tools
from openerp.tools import safe_eval
from openerp.addons.web.http import request
from openerp.http import request
from odoo.modules.module import get_resource_path
import json
from time import time