[MERGE] Merge with lp:openerp-web
bzr revid: hip@tinyerp.com-20121119054316-24eeo5y3011z3z53
This commit is contained in:
commit
786b11f31d
31
.bzrignore
31
.bzrignore
|
@ -1,19 +1,14 @@
|
||||||
.*.swp
|
.*
|
||||||
.bzrignore
|
*.egg-info
|
||||||
.idea
|
*.orig
|
||||||
.project
|
*.vim
|
||||||
.pydevproject
|
|
||||||
.ropeproject
|
|
||||||
.settings
|
|
||||||
.DS_Store
|
|
||||||
openerp/addons/*
|
|
||||||
openerp/filestore*
|
|
||||||
.Python
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
bin/*
|
|
||||||
build/
|
build/
|
||||||
include/
|
RE:^bin/
|
||||||
lib/
|
RE:^dist/
|
||||||
share/
|
RE:^include/
|
||||||
doc/_build/*
|
|
||||||
|
RE:^share/
|
||||||
|
RE:^man/
|
||||||
|
RE:^lib/
|
||||||
|
|
||||||
|
RE:^addons/\w+/doc/_build/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
'name': 'Web',
|
'name': 'Web',
|
||||||
'category': 'Hidden',
|
'category': 'Hidden',
|
||||||
|
'version': '7.0.1.0',
|
||||||
'description':
|
'description':
|
||||||
"""
|
"""
|
||||||
OpenERP Web core module.
|
OpenERP Web core module.
|
||||||
|
@ -40,6 +41,7 @@ This module provides the core of the OpenERP Web Client.
|
||||||
"static/lib/cleditor/jquery.cleditor.js",
|
"static/lib/cleditor/jquery.cleditor.js",
|
||||||
"static/lib/py.js/lib/py.js",
|
"static/lib/py.js/lib/py.js",
|
||||||
"static/src/js/boot.js",
|
"static/src/js/boot.js",
|
||||||
|
"static/src/js/testing.js",
|
||||||
"static/src/js/corelib.js",
|
"static/src/js/corelib.js",
|
||||||
"static/src/js/coresetup.js",
|
"static/src/js/coresetup.js",
|
||||||
"static/src/js/dates.js",
|
"static/src/js/dates.js",
|
||||||
|
@ -67,5 +69,21 @@ This module provides the core of the OpenERP Web Client.
|
||||||
'qweb' : [
|
'qweb' : [
|
||||||
"static/src/xml/*.xml",
|
"static/src/xml/*.xml",
|
||||||
],
|
],
|
||||||
|
'test': [
|
||||||
|
"static/test/testing.js",
|
||||||
|
"static/test/class.js",
|
||||||
|
"static/test/registry.js",
|
||||||
|
"static/test/form.js",
|
||||||
|
"static/test/list-utils.js",
|
||||||
|
"static/test/formats.js",
|
||||||
|
"static/test/rpc.js",
|
||||||
|
"static/test/evals.js",
|
||||||
|
"static/test/search.js",
|
||||||
|
"static/test/Widget.js",
|
||||||
|
"static/test/list.js",
|
||||||
|
"static/test/list-editable.js",
|
||||||
|
"static/test/mutex.js"
|
||||||
|
],
|
||||||
'bootstrap': True,
|
'bootstrap': True,
|
||||||
|
'twitter': False,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from . import main
|
from . import main
|
||||||
|
from . import testing
|
||||||
|
|
|
@ -258,11 +258,21 @@ def concat_files(file_list, reader=None, intersperse=""):
|
||||||
files_concat = intersperse.join(files_content)
|
files_concat = intersperse.join(files_content)
|
||||||
return files_concat, checksum.hexdigest()
|
return files_concat, checksum.hexdigest()
|
||||||
|
|
||||||
|
concat_js_cache = {}
|
||||||
|
|
||||||
def concat_js(file_list):
|
def concat_js(file_list):
|
||||||
content, checksum = concat_files(file_list, intersperse=';')
|
content, checksum = concat_files(file_list, intersperse=';')
|
||||||
|
if checksum in concat_js_cache:
|
||||||
|
content = concat_js_cache[checksum]
|
||||||
|
else:
|
||||||
content = rjsmin(content)
|
content = rjsmin(content)
|
||||||
|
concat_js_cache[checksum] = content
|
||||||
return content, checksum
|
return content, checksum
|
||||||
|
|
||||||
|
def fs2web(path):
|
||||||
|
"""convert FS path into web path"""
|
||||||
|
return '/'.join(path.split(os.path.sep))
|
||||||
|
|
||||||
def manifest_glob(req, addons, key):
|
def manifest_glob(req, addons, key):
|
||||||
if addons is None:
|
if addons is None:
|
||||||
addons = module_boot(req)
|
addons = module_boot(req)
|
||||||
|
@ -278,7 +288,7 @@ def manifest_glob(req, addons, key):
|
||||||
globlist = manifest.get(key, [])
|
globlist = manifest.get(key, [])
|
||||||
for pattern in globlist:
|
for pattern in globlist:
|
||||||
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
|
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
|
||||||
r.append((path, path[len(addons_path):]))
|
r.append((path, fs2web(path[len(addons_path):])))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def manifest_list(req, mods, extension):
|
def manifest_list(req, mods, extension):
|
||||||
|
@ -637,8 +647,7 @@ class WebClient(openerpweb.Controller):
|
||||||
data = fp.read().decode('utf-8')
|
data = fp.read().decode('utf-8')
|
||||||
|
|
||||||
path = file_map[f]
|
path = file_map[f]
|
||||||
# convert FS path into web path
|
web_dir = os.path.dirname(path)
|
||||||
web_dir = '/'.join(os.path.dirname(path).split(os.path.sep))
|
|
||||||
|
|
||||||
data = re.sub(
|
data = re.sub(
|
||||||
rx_import,
|
rx_import,
|
||||||
|
@ -691,12 +700,10 @@ class WebClient(openerpweb.Controller):
|
||||||
until we have established a valid session. This is meant only
|
until we have established a valid session. This is meant only
|
||||||
for translating the login page and db management chrome, using
|
for translating the login page and db management chrome, using
|
||||||
the browser's language. """
|
the browser's language. """
|
||||||
lang = req.httprequest.accept_languages.best or 'en'
|
|
||||||
# For performance reasons we only load a single translation, so for
|
# For performance reasons we only load a single translation, so for
|
||||||
# sub-languages (that should only be partially translated) we load the
|
# sub-languages (that should only be partially translated) we load the
|
||||||
# main language PO instead - that should be enough for the login screen.
|
# main language PO instead - that should be enough for the login screen.
|
||||||
if '-' in lang: # RFC2616 uses '-' separators for sublanguages
|
lang = req.lang.split('_')[0]
|
||||||
lang = lang.split('-')[0]
|
|
||||||
|
|
||||||
translations_per_module = {}
|
translations_per_module = {}
|
||||||
for addon_name in mods:
|
for addon_name in mods:
|
||||||
|
@ -770,15 +777,31 @@ class Database(openerpweb.Controller):
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def create(self, req, fields):
|
def create(self, req, fields):
|
||||||
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
||||||
create_attrs = (
|
return req.session.proxy("db").create_database(
|
||||||
params['super_admin_pwd'],
|
params['super_admin_pwd'],
|
||||||
params['db_name'],
|
params['db_name'],
|
||||||
bool(params.get('demo_data')),
|
bool(params.get('demo_data')),
|
||||||
params['db_lang'],
|
params['db_lang'],
|
||||||
params['create_admin_pwd']
|
params['create_admin_pwd'])
|
||||||
|
|
||||||
|
@openerpweb.jsonrequest
|
||||||
|
def duplicate(self, req, fields):
|
||||||
|
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
||||||
|
return req.session.proxy("db").duplicate_database(
|
||||||
|
params['super_admin_pwd'],
|
||||||
|
params['db_original_name'],
|
||||||
|
params['db_name'])
|
||||||
|
|
||||||
|
@openerpweb.jsonrequest
|
||||||
|
def duplicate(self, req, fields):
|
||||||
|
params = dict(map(operator.itemgetter('name', 'value'), fields))
|
||||||
|
duplicate_attrs = (
|
||||||
|
params['super_admin_pwd'],
|
||||||
|
params['db_original_name'],
|
||||||
|
params['db_name'],
|
||||||
)
|
)
|
||||||
|
|
||||||
return req.session.proxy("db").create_database(*create_attrs)
|
return req.session.proxy("db").duplicate_database(*duplicate_attrs)
|
||||||
|
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def drop(self, req, fields):
|
def drop(self, req, fields):
|
||||||
|
@ -886,10 +909,7 @@ class Session(openerpweb.Controller):
|
||||||
@openerpweb.jsonrequest
|
@openerpweb.jsonrequest
|
||||||
def get_lang_list(self, req):
|
def get_lang_list(self, req):
|
||||||
try:
|
try:
|
||||||
return {
|
return req.session.proxy("db").list_lang() or []
|
||||||
'lang_list': (req.session.proxy("db").list_lang() or []),
|
|
||||||
'error': ""
|
|
||||||
}
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
return {"error": e, "title": "Languages"}
|
return {"error": e, "title": "Languages"}
|
||||||
|
|
||||||
|
@ -1455,11 +1475,24 @@ class Binary(openerpweb.Controller):
|
||||||
try:
|
try:
|
||||||
if not id:
|
if not id:
|
||||||
res = Model.default_get([field], context).get(field)
|
res = Model.default_get([field], context).get(field)
|
||||||
image_data = base64.b64decode(res)
|
image_base64 = res
|
||||||
else:
|
else:
|
||||||
res = Model.read([id], [last_update, field], context)[0]
|
res = Model.read([id], [last_update, field], context)[0]
|
||||||
retag = hashlib.md5(res.get(last_update)).hexdigest()
|
retag = hashlib.md5(res.get(last_update)).hexdigest()
|
||||||
image_data = base64.b64decode(res.get(field))
|
image_base64 = res.get(field)
|
||||||
|
|
||||||
|
if kw.get('resize'):
|
||||||
|
resize = kw.get('resize').split(',');
|
||||||
|
if len(resize) == 2 and int(resize[0]) and int(resize[1]):
|
||||||
|
width = int(resize[0])
|
||||||
|
height = int(resize[1])
|
||||||
|
# resize maximum 500*500
|
||||||
|
if width > 500: width = 500
|
||||||
|
if height > 500: height = 500
|
||||||
|
image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
|
||||||
|
|
||||||
|
image_data = base64.b64decode(image_base64)
|
||||||
|
|
||||||
except (TypeError, xmlrpclib.Fault):
|
except (TypeError, xmlrpclib.Fault):
|
||||||
image_data = self.placeholder(req)
|
image_data = self.placeholder(req)
|
||||||
headers.append(('ETag', retag))
|
headers.append(('ETag', retag))
|
||||||
|
@ -1949,112 +1982,4 @@ class Reports(View):
|
||||||
('Content-Length', len(report))],
|
('Content-Length', len(report))],
|
||||||
cookies={'fileToken': int(token)})
|
cookies={'fileToken': int(token)})
|
||||||
|
|
||||||
class Import(View):
|
|
||||||
_cp_path = "/web/import"
|
|
||||||
|
|
||||||
def fields_get(self, req, model):
|
|
||||||
Model = req.session.model(model)
|
|
||||||
fields = Model.fields_get(False, req.session.eval_context(req.context))
|
|
||||||
return fields
|
|
||||||
|
|
||||||
@openerpweb.httprequest
|
|
||||||
def detect_data(self, req, csvfile, csvsep=',', csvdel='"', csvcode='utf-8', jsonp='callback'):
|
|
||||||
try:
|
|
||||||
data = list(csv.reader(
|
|
||||||
csvfile, quotechar=str(csvdel), delimiter=str(csvsep)))
|
|
||||||
except csv.Error, e:
|
|
||||||
csvfile.seek(0)
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'error': {
|
|
||||||
'message': 'Error parsing CSV file: %s' % e,
|
|
||||||
# decodes each byte to a unicode character, which may or
|
|
||||||
# may not be printable, but decoding will succeed.
|
|
||||||
# Otherwise simplejson will try to decode the `str` using
|
|
||||||
# utf-8, which is very likely to blow up on characters out
|
|
||||||
# of the ascii range (in range [128, 256))
|
|
||||||
'preview': csvfile.read(200).decode('iso-8859-1')}}))
|
|
||||||
|
|
||||||
try:
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps(
|
|
||||||
{'records': data[:10]}, encoding=csvcode))
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({
|
|
||||||
'message': u"Failed to decode CSV file using encoding %s, "
|
|
||||||
u"try switching to a different encoding" % csvcode
|
|
||||||
}))
|
|
||||||
|
|
||||||
@openerpweb.httprequest
|
|
||||||
def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp,
|
|
||||||
meta):
|
|
||||||
modle_obj = req.session.model(model)
|
|
||||||
skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')(
|
|
||||||
simplejson.loads(meta))
|
|
||||||
|
|
||||||
error = None
|
|
||||||
if not (csvdel and len(csvdel) == 1):
|
|
||||||
error = u"The CSV delimiter must be a single character"
|
|
||||||
|
|
||||||
if not indices and fields:
|
|
||||||
error = u"You must select at least one field to import"
|
|
||||||
|
|
||||||
if error:
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'error': {'message': error}}))
|
|
||||||
|
|
||||||
# skip ignored records (@skip parameter)
|
|
||||||
# then skip empty lines (not valid csv)
|
|
||||||
# nb: should these operations be reverted?
|
|
||||||
rows_to_import = itertools.ifilter(
|
|
||||||
None,
|
|
||||||
itertools.islice(
|
|
||||||
csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)),
|
|
||||||
skip, None))
|
|
||||||
|
|
||||||
# if only one index, itemgetter will return an atom rather than a tuple
|
|
||||||
if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
|
|
||||||
else: mapper = operator.itemgetter(*indices)
|
|
||||||
|
|
||||||
data = None
|
|
||||||
error = None
|
|
||||||
try:
|
|
||||||
# decode each data row
|
|
||||||
data = [
|
|
||||||
[record.decode(csvcode) for record in row]
|
|
||||||
for row in itertools.imap(mapper, rows_to_import)
|
|
||||||
# don't insert completely empty rows (can happen due to fields
|
|
||||||
# filtering in case of e.g. o2m content rows)
|
|
||||||
if any(row)
|
|
||||||
]
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
error = u"Failed to decode CSV file using encoding %s" % csvcode
|
|
||||||
except csv.Error, e:
|
|
||||||
error = u"Could not process CSV file: %s" % e
|
|
||||||
|
|
||||||
# If the file contains nothing,
|
|
||||||
if not data:
|
|
||||||
error = u"File to import is empty"
|
|
||||||
if error:
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'error': {'message': error}}))
|
|
||||||
|
|
||||||
try:
|
|
||||||
(code, record, message, _nope) = modle_obj.import_data(
|
|
||||||
fields, data, 'init', '', False,
|
|
||||||
req.session.eval_context(req.context))
|
|
||||||
except xmlrpclib.Fault, e:
|
|
||||||
error = {"message": u"%s, %s" % (e.faultCode, e.faultString)}
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'error':error}))
|
|
||||||
|
|
||||||
if code != -1:
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'success':True}))
|
|
||||||
|
|
||||||
msg = u"Error during import: %s\n\nTrying to import record %r" % (
|
|
||||||
message, record)
|
|
||||||
return '<script>window.top.%s(%s);</script>' % (
|
|
||||||
jsonp, simplejson.dumps({'error': {'message':msg}}))
|
|
||||||
|
|
||||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
# coding=utf-8
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import itertools
|
||||||
|
import json
|
||||||
|
import operator
|
||||||
|
import os
|
||||||
|
|
||||||
|
from mako.template import Template
|
||||||
|
from openerp.modules import module
|
||||||
|
|
||||||
|
from .main import module_topological_sort
|
||||||
|
from ..http import Controller, httprequest
|
||||||
|
|
||||||
|
NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||||
|
<title>OpenERP Testing</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="/web/tests" method="GET">
|
||||||
|
<button name="mod" value="*">Run all tests</button>
|
||||||
|
<ul>
|
||||||
|
% for name, module in modules:
|
||||||
|
<li>${name} <button name="mod" value="${module}">
|
||||||
|
Run Tests</button></li>
|
||||||
|
% endfor
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
NOTFOUND = Template(u"""
|
||||||
|
<p>Unable to find the module [${module}], please check that the module
|
||||||
|
name is correct and the module is on OpenERP's path.</p>
|
||||||
|
<a href="/web/tests"><< Back to tests</a>
|
||||||
|
""")
|
||||||
|
TESTING = Template(u"""<!DOCTYPE html>
|
||||||
|
<html style="height: 100%">
|
||||||
|
<%def name="to_path(module, p)">/${module}/${p}</%def>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||||
|
<title>OpenERP Web Tests</title>
|
||||||
|
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/web/static/lib/qunit/qunit.css">
|
||||||
|
<script src="/web/static/lib/qunit/qunit.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var oe_db_info = ${db_info};
|
||||||
|
// List of modules, each module is preceded by its dependencies
|
||||||
|
var oe_all_dependencies = ${dependencies};
|
||||||
|
QUnit.config.testTimeout = 5 * 60 * 1000;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body id="oe" class="openerp">
|
||||||
|
<div id="qunit"></div>
|
||||||
|
<div id="qunit-fixture"></div>
|
||||||
|
</body>
|
||||||
|
% for module, jss, tests, templates in files:
|
||||||
|
% for js in jss:
|
||||||
|
<script src="${to_path(module, js)}"></script>
|
||||||
|
% endfor
|
||||||
|
% if tests or templates:
|
||||||
|
<script>
|
||||||
|
openerp.testing.current_module = "${module}";
|
||||||
|
% for template in templates:
|
||||||
|
openerp.testing.add_template("${to_path(module, template)}");
|
||||||
|
% endfor
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
% if tests:
|
||||||
|
% for test in tests:
|
||||||
|
<script type="text/javascript" src="${to_path(module, test)}"></script>
|
||||||
|
% endfor
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
|
||||||
|
class TestRunnerController(Controller):
|
||||||
|
_cp_path = '/web/tests'
|
||||||
|
|
||||||
|
@httprequest
|
||||||
|
def index(self, req, mod=None, **kwargs):
|
||||||
|
ms = module.get_modules()
|
||||||
|
manifests = dict(
|
||||||
|
(name, desc)
|
||||||
|
for name, desc in zip(ms, map(self.load_manifest, ms))
|
||||||
|
if desc # remove not-actually-openerp-modules
|
||||||
|
)
|
||||||
|
|
||||||
|
if not mod:
|
||||||
|
return NOMODULE_TEMPLATE.render(modules=(
|
||||||
|
(manifest['name'], name)
|
||||||
|
for name, manifest in manifests.iteritems()
|
||||||
|
if any(testfile.endswith('.js')
|
||||||
|
for testfile in manifest['test'])
|
||||||
|
))
|
||||||
|
sorted_mods = module_topological_sort(dict(
|
||||||
|
(name, manifest.get('depends', []))
|
||||||
|
for name, manifest in manifests.iteritems()
|
||||||
|
))
|
||||||
|
# to_load and to_test should be zippable lists of the same length.
|
||||||
|
# A falsy value in to_test indicate nothing to test at that index (just
|
||||||
|
# load the corresponding part of to_load)
|
||||||
|
to_test = sorted_mods
|
||||||
|
if mod != '*':
|
||||||
|
if mod not in manifests:
|
||||||
|
return req.not_found(NOTFOUND.render(module=mod))
|
||||||
|
idx = sorted_mods.index(mod)
|
||||||
|
to_test = [None] * len(sorted_mods)
|
||||||
|
to_test[idx] = mod
|
||||||
|
|
||||||
|
tests_candicates = [
|
||||||
|
filter(lambda path: path.endswith('.js'),
|
||||||
|
manifests[mod]['test'] if mod else [])
|
||||||
|
for mod in to_test]
|
||||||
|
# remove trailing test-less modules
|
||||||
|
tests = reversed(list(
|
||||||
|
itertools.dropwhile(
|
||||||
|
operator.not_,
|
||||||
|
reversed(tests_candicates))))
|
||||||
|
|
||||||
|
files = [
|
||||||
|
(mod, manifests[mod]['js'], tests, manifests[mod]['qweb'])
|
||||||
|
for mod, tests in itertools.izip(sorted_mods, tests)
|
||||||
|
]
|
||||||
|
|
||||||
|
# if all three db_info parameters are present, send them to the page
|
||||||
|
db_info = dict((k, v) for k, v in kwargs.iteritems()
|
||||||
|
if k in ['source', 'supadmin', 'password'])
|
||||||
|
if len(db_info) != 3:
|
||||||
|
db_info = None
|
||||||
|
|
||||||
|
return TESTING.render(files=files, dependencies=json.dumps(
|
||||||
|
[name for name in sorted_mods
|
||||||
|
if module.get_module_resource(name, 'static')
|
||||||
|
if manifests[name]['js']]), db_info=json.dumps(db_info))
|
||||||
|
|
||||||
|
def load_manifest(self, name):
|
||||||
|
manifest = module.load_information_from_description_file(name)
|
||||||
|
if manifest:
|
||||||
|
path = module.get_module_path(name)
|
||||||
|
manifest['js'] = list(
|
||||||
|
self.expand_patterns(path, manifest.get('js', [])))
|
||||||
|
manifest['test'] = list(
|
||||||
|
self.expand_patterns(path, manifest.get('test', [])))
|
||||||
|
manifest['qweb'] = list(
|
||||||
|
self.expand_patterns(path, manifest.get('qweb', [])))
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
def expand_patterns(self, root, patterns):
|
||||||
|
for pattern in patterns:
|
||||||
|
normalized_pattern = os.path.normpath(os.path.join(root, pattern))
|
||||||
|
for path in glob.glob(normalized_pattern):
|
||||||
|
# replace OS path separators (from join & normpath) by URI ones
|
||||||
|
yield path[len(root):].replace(os.path.sep, '/')
|
|
@ -1,9 +1,6 @@
|
||||||
Asynchronous Operations
|
Asynchronous Operations
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
This documentation is outdated as then() is now pipe. Please never copy api
|
|
||||||
from other libraries just link to it.
|
|
||||||
|
|
||||||
As a language (and runtime), javascript is fundamentally
|
As a language (and runtime), javascript is fundamentally
|
||||||
single-threaded. This means any blocking request or computation will
|
single-threaded. This means any blocking request or computation will
|
||||||
blocks the whole page (and, in older browsers, the software itself
|
blocks the whole page (and, in older browsers, the software itself
|
||||||
|
@ -46,8 +43,8 @@ directly to asynchronous methods is the ability to :ref:`compose them
|
||||||
Using deferreds
|
Using deferreds
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
deferreds have only one method of importance: :js:func:`Deferred.then`. This
|
Deferreds's most important method is :js:func:`Deferred.then`. It is
|
||||||
method is used to attach new callbacks to the deferred object.
|
used to attach new callbacks to the deferred object.
|
||||||
|
|
||||||
* the first parameter attaches a success callback, called when the
|
* the first parameter attaches a success callback, called when the
|
||||||
deferred object is successfully resolved and provided with the
|
deferred object is successfully resolved and provided with the
|
||||||
|
@ -179,9 +176,9 @@ Deferred chaining
|
||||||
|
|
||||||
A second useful composition is starting an asynchronous operation as
|
A second useful composition is starting an asynchronous operation as
|
||||||
the result of an other asynchronous operation, and wanting the result
|
the result of an other asynchronous operation, and wanting the result
|
||||||
of both: :js:func:`Deferred.then` returns the deferred on which it was
|
of both: with the tools described so far, handling e.g. OpenERP's
|
||||||
called, so handle e.g. OpenERP's search/read sequence with this would
|
search/read sequence with this would require something along the lines
|
||||||
require something along the lines of:
|
of:
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
@ -196,21 +193,14 @@ require something along the lines of:
|
||||||
While it doesn't look too bad for trivial code, this quickly gets
|
While it doesn't look too bad for trivial code, this quickly gets
|
||||||
unwieldy.
|
unwieldy.
|
||||||
|
|
||||||
Instead, jQuery provides a tool to handle this kind of chains:
|
But :js:func:`~Deferred.then` also allows handling this kind of
|
||||||
:js:func:`Deferred.pipe`.
|
chains: it returns a new promise object, not the one it was called
|
||||||
|
with, and the return values of the callbacks is actually important to
|
||||||
:js:func:`~Deferred.pipe` has the same signature as
|
it: whichever callback is called,
|
||||||
:js:func:`~Deferred.then` and could be used in the same manner
|
|
||||||
provided its return value was not used.
|
|
||||||
|
|
||||||
It differs from :js:func:`~Deferred.then` in two ways: it returns a
|
|
||||||
new promise object, not the one it was called with, and the return
|
|
||||||
values of the callbacks is actually important to it: whichever
|
|
||||||
callback is called,
|
|
||||||
|
|
||||||
* If the callback is not set (not provided or left to null), the
|
* If the callback is not set (not provided or left to null), the
|
||||||
resolution or rejection value(s) is simply forwarded to
|
resolution or rejection value(s) is simply forwarded to
|
||||||
:js:func:`~Deferred.pipe`'s promise (it's essentially a noop)
|
:js:func:`~Deferred.then`'s promise (it's essentially a noop)
|
||||||
|
|
||||||
* If the callback is set and does not return an observable object (a
|
* If the callback is set and does not return an observable object (a
|
||||||
deferred or a promise), the value it returns (``undefined`` if it
|
deferred or a promise), the value it returns (``undefined`` if it
|
||||||
|
@ -218,7 +208,7 @@ callback is called,
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
promise.pipe(function () {
|
promise.then(function () {
|
||||||
console.log('called');
|
console.log('called');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -235,7 +225,7 @@ callback is called,
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
return Model.search(condition).pipe(function (ids) {
|
return Model.search(condition).then(function (ids) {
|
||||||
return Model.read(ids, fields);
|
return Model.read(ids, fields);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -244,12 +234,109 @@ callback is called,
|
||||||
will be resolved with ``read``'s resolution values if the chain
|
will be resolved with ``read``'s resolution values if the chain
|
||||||
executes correctly.
|
executes correctly.
|
||||||
|
|
||||||
:js:func:`~Deferred.pipe` is also useful to adapt third-party
|
:js:func:`~Deferred.then` is also useful to adapt third-party
|
||||||
promise-based APIs, in order to filter their resolution value counts
|
promise-based APIs, in order to filter their resolution value counts
|
||||||
for instance (to take advantage of :js:func:`when` 's special treatment
|
for instance (to take advantage of :js:func:`when` 's special
|
||||||
of single-value promises).
|
treatment of single-value promises).
|
||||||
|
|
||||||
|
|
||||||
|
jQuery.Deferred API
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. js:function:: when(deferreds…)
|
||||||
|
|
||||||
|
:param deferreds: deferred objects to multiplex
|
||||||
|
:returns: a multiplexed deferred
|
||||||
|
:rtype: :js:class:`Deferred`
|
||||||
|
|
||||||
|
.. js:class:: Deferred
|
||||||
|
|
||||||
|
.. js:function:: Deferred.then(doneCallback[, failCallback])
|
||||||
|
|
||||||
|
Attaches new callbacks to the resolution or rejection of the
|
||||||
|
deferred object. Callbacks are executed in the order they are
|
||||||
|
attached to the deferred.
|
||||||
|
|
||||||
|
To provide only a failure callback, pass ``null`` as the
|
||||||
|
``doneCallback``, to provide only a success callback the
|
||||||
|
second argument can just be ignored (and not passed at all).
|
||||||
|
|
||||||
|
Returns a new deferred which resolves to the result of the
|
||||||
|
corresponding callback, if a callback returns a deferred
|
||||||
|
itself that new deferred will be used as the resolution of the
|
||||||
|
chain.
|
||||||
|
|
||||||
|
:param doneCallback: function called when the deferred is resolved
|
||||||
|
:type doneCallback: Function
|
||||||
|
:param failCallback: function called when the deferred is rejected
|
||||||
|
:type failCallback: Function
|
||||||
|
:returns: the deferred object on which it was called
|
||||||
|
:rtype: :js:class:`Deferred`
|
||||||
|
|
||||||
|
.. js:function:: Deferred.done(doneCallback)
|
||||||
|
|
||||||
|
Attaches a new success callback to the deferred, shortcut for
|
||||||
|
``deferred.then(doneCallback)``.
|
||||||
|
|
||||||
|
This is a jQuery extension to `CommonJS Promises/A`_ providing
|
||||||
|
little value over calling :js:func:`~Deferred.then` directly,
|
||||||
|
it should be avoided.
|
||||||
|
|
||||||
|
:param doneCallback: function called when the deferred is resolved
|
||||||
|
:type doneCallback: Function
|
||||||
|
:returns: the deferred object on which it was called
|
||||||
|
:rtype: :js:class:`Deferred`
|
||||||
|
|
||||||
|
.. js:function:: Deferred.fail(failCallback)
|
||||||
|
|
||||||
|
Attaches a new failure callback to the deferred, shortcut for
|
||||||
|
``deferred.then(null, failCallback)``.
|
||||||
|
|
||||||
|
A second jQuery extension to `Promises/A <CommonJS
|
||||||
|
Promises/A>`_. Although it provides more value than
|
||||||
|
:js:func:`~Deferred.done`, it still is not much and should be
|
||||||
|
avoided as well.
|
||||||
|
|
||||||
|
:param failCallback: function called when the deferred is rejected
|
||||||
|
:type failCallback: Function
|
||||||
|
:returns: the deferred object on which it was called
|
||||||
|
:rtype: :js:class:`Deferred`
|
||||||
|
|
||||||
|
.. js:function:: Deferred.promise()
|
||||||
|
|
||||||
|
Returns a read-only view of the deferred object, with all
|
||||||
|
mutators (resolve and reject) methods removed.
|
||||||
|
|
||||||
|
.. js:function:: Deferred.resolve(value…)
|
||||||
|
|
||||||
|
Called to resolve a deferred, any value provided will be
|
||||||
|
passed onto the success handlers of the deferred object.
|
||||||
|
|
||||||
|
Resolving a deferred which has already been resolved or
|
||||||
|
rejected has no effect.
|
||||||
|
|
||||||
|
.. js:function:: Deferred.reject(value…)
|
||||||
|
|
||||||
|
Called to reject (fail) a deferred, any value provided will be
|
||||||
|
passed onto the failure handler of the deferred object.
|
||||||
|
|
||||||
|
Rejecting a deferred which has already been resolved or
|
||||||
|
rejected has no effect.
|
||||||
|
|
||||||
|
.. [#] or simply calling :js:class:`Deferred` as a function, the
|
||||||
|
result is the same
|
||||||
|
|
||||||
|
.. [#] or not-promises, the `CommonJS Promises/B`_ role of
|
||||||
|
:js:func:`when` is to be able to treat values and promises
|
||||||
|
uniformly: :js:func:`when` will pass promises through directly,
|
||||||
|
but non-promise values and objects will be transformed into a
|
||||||
|
resolved promise (resolving themselves with the value itself).
|
||||||
|
|
||||||
|
jQuery's :js:func:`when` keeps this behavior making deferreds
|
||||||
|
easy to build from "static" values, or allowing defensive code
|
||||||
|
where expected promises are wrapped in :js:func:`when` just in
|
||||||
|
case.
|
||||||
|
|
||||||
.. _promises: http://en.wikipedia.org/wiki/Promise_(programming)
|
.. _promises: http://en.wikipedia.org/wiki/Promise_(programming)
|
||||||
.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/
|
.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/
|
||||||
.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A
|
.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A
|
||||||
|
|
|
@ -15,7 +15,7 @@ DataSet -> Model
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The 6.1 ``DataSet`` API has been deprecated in favor of the smaller
|
The 6.1 ``DataSet`` API has been deprecated in favor of the smaller
|
||||||
and more orthogonal :doc:`Model </dev_rpc>` API, which more closely
|
and more orthogonal :doc:`Model </rpc>` API, which more closely
|
||||||
matches the API in OpenERP Web's Python side and in OpenObject addons
|
matches the API in OpenERP Web's Python side and in OpenObject addons
|
||||||
and removes most stateful behavior of DataSet.
|
and removes most stateful behavior of DataSet.
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ import sys, os
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
|
||||||
sys.path.append(os.path.abspath('_themes'))
|
sys.path.append(os.path.abspath('_themes'))
|
||||||
sys.path.append(os.path.abspath('..'))
|
sys.path.insert(0, os.path.abspath('../addons'))
|
||||||
sys.path.append(os.path.abspath('../openerp'))
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ source_suffix = '.rst'
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'OpenERP Server Developers Documentation'
|
project = u'OpenERP Web Developers Documentation'
|
||||||
copyright = u'2012, OpenERP s.a.'
|
copyright = u'2012, OpenERP s.a.'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
@ -53,7 +52,7 @@ copyright = u'2012, OpenERP s.a.'
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '7.0'
|
version = '7.0'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '7.0b'
|
release = '7.0'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -171,7 +170,7 @@ html_sidebars = {
|
||||||
#html_file_suffix = None
|
#html_file_suffix = None
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'openerp-server-doc'
|
htmlhelp_basename = 'openerp-web-doc'
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
@ -190,7 +189,7 @@ latex_elements = {
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
|
('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation',
|
||||||
u'OpenERP s.a.', 'manual'),
|
u'OpenERP s.a.', 'manual'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -220,7 +219,7 @@ latex_documents = [
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
|
('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation',
|
||||||
[u'OpenERP s.a.'], 1)
|
[u'OpenERP s.a.'], 1)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -234,8 +233,8 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
|
('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation',
|
||||||
u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
|
u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.',
|
||||||
'Miscellaneous'),
|
'Miscellaneous'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -248,10 +247,12 @@ texinfo_documents = [
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
#texinfo_show_urls = 'footnote'
|
#texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
|
todo_include_todos = True
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
'python': ('http://docs.python.org/', None),
|
'python': ('http://docs.python.org/', None),
|
||||||
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
|
'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None),
|
||||||
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
|
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
|
||||||
|
'openerpcommand': ('http://doc.openerp.com/trunk/developers/command', None),
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -14,18 +14,18 @@ Contents:
|
||||||
presentation
|
presentation
|
||||||
async
|
async
|
||||||
|
|
||||||
dev_rpc
|
testing
|
||||||
dev_widget
|
|
||||||
dev_qweb
|
|
||||||
dev_client_action
|
|
||||||
|
|
||||||
internal_form
|
widget
|
||||||
internal_list
|
qweb
|
||||||
internal_search
|
rpc
|
||||||
|
client_action
|
||||||
|
form_view
|
||||||
|
search_view
|
||||||
|
list_view
|
||||||
|
|
||||||
changelog-7.0
|
changelog-7.0
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
|
@ -451,7 +451,7 @@ formview, delegating instead to its
|
||||||
created.
|
created.
|
||||||
|
|
||||||
The result should be a valid form view, see :doc:`Form Notes
|
The result should be a valid form view, see :doc:`Form Notes
|
||||||
<internal_form>` for various peculiarities of the form view
|
<form_view>` for various peculiarities of the form view
|
||||||
format.
|
format.
|
||||||
|
|
||||||
:param editor: editor object asking for the view
|
:param editor: editor object asking for the view
|
|
@ -0,0 +1,25 @@
|
||||||
|
test_empty_find (openerp.addons.web.tests.test_dataset.TestDataSetController) ... ok
|
||||||
|
test_ids_shortcut (openerp.addons.web.tests.test_dataset.TestDataSetController) ... ok
|
||||||
|
test_regular_find (openerp.addons.web.tests.test_dataset.TestDataSetController) ... ok
|
||||||
|
web.testing.stack: direct, value, success ... ok
|
||||||
|
web.testing.stack: direct, deferred, success ... ok
|
||||||
|
web.testing.stack: direct, value, error ... ok
|
||||||
|
web.testing.stack: direct, deferred, failure ... ok
|
||||||
|
web.testing.stack: successful setup ... ok
|
||||||
|
web.testing.stack: successful teardown ... ok
|
||||||
|
web.testing.stack: successful setup and teardown ... ok
|
||||||
|
|
||||||
|
[snip ~150 lines]
|
||||||
|
|
||||||
|
test_convert_complex_context (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
test_convert_complex_domain (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
test_convert_literal_context (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
test_convert_literal_domain (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
test_retrieve_nonliteral_context (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
test_retrieve_nonliteral_domain (openerp.addons.web.tests.test_view.DomainsAndContextsTest) ... ok
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 181 tests in 15.706s
|
||||||
|
|
||||||
|
OK
|
||||||
|
|
|
@ -0,0 +1,693 @@
|
||||||
|
.. highlight:: javascript
|
||||||
|
|
||||||
|
Testing in OpenERP Web
|
||||||
|
======================
|
||||||
|
|
||||||
|
Javascript Unit Testing
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
OpenERP Web 7.0 includes means to unit-test both the core code of
|
||||||
|
OpenERP Web and your own javascript modules. On the javascript side,
|
||||||
|
unit-testing is based on QUnit_ with a number of helpers and
|
||||||
|
extensions for better integration with OpenERP.
|
||||||
|
|
||||||
|
To see what the runner looks like, find (or start) an OpenERP server
|
||||||
|
with the web client enabled, and navigate to ``/web/tests`` e.g. `on
|
||||||
|
OpenERP's CI <http://trunk.runbot.openerp.com/web/tests>`_. This will
|
||||||
|
show the runner selector, which lists all modules with javascript unit
|
||||||
|
tests, and allows starting any of them (or all javascript tests in all
|
||||||
|
modules at once).
|
||||||
|
|
||||||
|
.. image:: ./images/runner.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Clicking any runner button will launch the corresponding tests in the
|
||||||
|
bundled QUnit_ runner:
|
||||||
|
|
||||||
|
.. image:: ./images/tests.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Writing a test case
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
The first step is to list the test file(s). This is done through the
|
||||||
|
``test`` key of the openerp manifest, by adding javascript files to it
|
||||||
|
(next to the usual YAML files, if any):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "Demonstration of web/javascript tests",
|
||||||
|
'category': 'Hidden',
|
||||||
|
'depends': ['web'],
|
||||||
|
'test': ['static/test/demo.js'],
|
||||||
|
}
|
||||||
|
|
||||||
|
and to create the corresponding test file(s)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Test files which do not exist will be ignored, if all test files
|
||||||
|
of a module are ignored (can not be found), the test runner will
|
||||||
|
consider that the module has no javascript tests.
|
||||||
|
|
||||||
|
After that, refreshing the runner selector will display the new module
|
||||||
|
and allow running all of its (0 so far) tests:
|
||||||
|
|
||||||
|
.. image:: ./images/runner2.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
The next step is to create a test case::
|
||||||
|
|
||||||
|
openerp.testing.section('basic section', function (test) {
|
||||||
|
test('my first test', function () {
|
||||||
|
ok(false, "this test has run");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
All testing helpers and structures live in the ``openerp.testing``
|
||||||
|
module. OpenERP tests live in a :js:func:`~openerp.testing.section`,
|
||||||
|
which is itself part of a module. The first argument to a section is
|
||||||
|
the name of the section, the second one is the section body.
|
||||||
|
|
||||||
|
:js:func:`test <openerp.testing.case>`, provided by the
|
||||||
|
:js:func:`~openerp.testing.section` to the callback, is used to
|
||||||
|
register a given test case which will be run whenever the test runner
|
||||||
|
actually does its job. OpenERP Web test case use standard `QUnit
|
||||||
|
assertions`_ within them.
|
||||||
|
|
||||||
|
Launching the test runner at this point will run the test and display
|
||||||
|
the corresponding assertion message, with red colors indicating the
|
||||||
|
test failed:
|
||||||
|
|
||||||
|
.. image:: ./images/tests2.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Fixing the test (by replacing ``false`` to ``true`` in the assertion)
|
||||||
|
will make it pass:
|
||||||
|
|
||||||
|
.. image:: ./images/tests3.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Assertions
|
||||||
|
----------
|
||||||
|
|
||||||
|
As noted above, OpenERP Web's tests use `qunit assertions`_. They are
|
||||||
|
available globally (so they can just be called without references to
|
||||||
|
anything). The following list is available:
|
||||||
|
|
||||||
|
.. js:function:: ok(state[, message])
|
||||||
|
|
||||||
|
checks that ``state`` is truthy (in the javascript sense)
|
||||||
|
|
||||||
|
.. js:function:: strictEqual(actual, expected[, message])
|
||||||
|
|
||||||
|
checks that the actual (produced by a method being tested) and
|
||||||
|
expected values are identical (roughly equivalent to ``ok(actual
|
||||||
|
=== expected, message)``)
|
||||||
|
|
||||||
|
.. js:function:: notStrictEqual(actual, expected[, message])
|
||||||
|
|
||||||
|
checks that the actual and expected values are *not* identical
|
||||||
|
(roughly equivalent to ``ok(actual !== expected, message)``)
|
||||||
|
|
||||||
|
.. js:function:: deepEqual(actual, expected[, message])
|
||||||
|
|
||||||
|
deep comparison between actual and expected: recurse into
|
||||||
|
containers (objects and arrays) to ensure that they have the same
|
||||||
|
keys/number of elements, and the values match.
|
||||||
|
|
||||||
|
.. js:function:: notDeepEqual(actual, expected[, message])
|
||||||
|
|
||||||
|
inverse operation to :js:func:`deepEqual`
|
||||||
|
|
||||||
|
.. js:function:: throws(block[, expected][, message])
|
||||||
|
|
||||||
|
checks that, when called, the ``block`` throws an
|
||||||
|
error. Optionally validates that error against ``expected``.
|
||||||
|
|
||||||
|
:param Function block:
|
||||||
|
:param expected: if a regexp, checks that the thrown error's
|
||||||
|
message matches the regular expression. If an
|
||||||
|
error type, checks that the thrown error is of
|
||||||
|
that type.
|
||||||
|
:type expected: Error | RegExp
|
||||||
|
|
||||||
|
.. js:function:: equal(actual, expected[, message])
|
||||||
|
|
||||||
|
checks that ``actual`` and ``expected`` are loosely equal, using
|
||||||
|
the ``==`` operator and its coercion rules.
|
||||||
|
|
||||||
|
.. js:function:: notEqual(actual, expected[, message])
|
||||||
|
|
||||||
|
inverse operation to :js:func:`equal`
|
||||||
|
|
||||||
|
Getting an OpenERP instance
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
The OpenERP instance is the base through which most OpenERP Web
|
||||||
|
modules behaviors (functions, objects, …) are accessed. As a result,
|
||||||
|
the test framework automatically builds one, and loads the module
|
||||||
|
being tested and all of its dependencies inside it. This new instance
|
||||||
|
is provided as the first positional parameter to your test
|
||||||
|
cases. Let's observe by adding javascript code (not test code) to the
|
||||||
|
test module:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "Demonstration of web/javascript tests",
|
||||||
|
'category': 'Hidden',
|
||||||
|
'depends': ['web'],
|
||||||
|
'js': ['static/src/js/demo.js'],
|
||||||
|
'test': ['static/test/demo.js'],
|
||||||
|
}
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
// src/js/demo.js
|
||||||
|
openerp.web_tests_demo = function (instance) {
|
||||||
|
instance.web_tests_demo = {
|
||||||
|
value_true: true,
|
||||||
|
SomeType: instance.web.Class.extend({
|
||||||
|
init: function (value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
and then adding a new test case, which simply checks that the
|
||||||
|
``instance`` contains all the expected stuff we created in the
|
||||||
|
module::
|
||||||
|
|
||||||
|
// test/demo.js
|
||||||
|
test('module content', function (instance) {
|
||||||
|
ok(instance.web_tests_demo.value_true, "should have a true value");
|
||||||
|
var type_instance = new instance.web_tests_demo.SomeType(42);
|
||||||
|
strictEqual(type_instance.value, 42, "should have provided value");
|
||||||
|
});
|
||||||
|
|
||||||
|
DOM Scratchpad
|
||||||
|
--------------
|
||||||
|
|
||||||
|
As in the wider client, arbitrarily accessing document content is
|
||||||
|
strongly discouraged during tests. But DOM access is still needed to
|
||||||
|
e.g. fully initialize :js:class:`widgets <~openerp.web.Widget>` before
|
||||||
|
testing them.
|
||||||
|
|
||||||
|
Thus, a test case gets a DOM scratchpad as its second positional
|
||||||
|
parameter, in a jQuery instance. That scratchpad is fully cleaned up
|
||||||
|
before each test, and as long as it doesn't do anything outside the
|
||||||
|
scratchpad your code can do whatever it wants::
|
||||||
|
|
||||||
|
// test/demo.js
|
||||||
|
test('DOM content', function (instance, $scratchpad) {
|
||||||
|
$scratchpad.html('<div><span class="foo bar">ok</span></div>');
|
||||||
|
ok($scratchpad.find('span').hasClass('foo'),
|
||||||
|
"should have provided class");
|
||||||
|
});
|
||||||
|
test('clean scratchpad', function (instance, $scratchpad) {
|
||||||
|
ok(!$scratchpad.children().length, "should have no content");
|
||||||
|
ok(!$scratchpad.text(), "should have no text");
|
||||||
|
});
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The top-level element of the scratchpad is not cleaned up, test
|
||||||
|
cases can add text or DOM children but shoud not alter
|
||||||
|
``$scratchpad`` itself.
|
||||||
|
|
||||||
|
Loading templates
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
To avoid the corresponding processing costs, by default templates are
|
||||||
|
not loaded into QWeb. If you need to render e.g. widgets making use of
|
||||||
|
QWeb templates, you can request their loading through the
|
||||||
|
:js:attr:`~TestOptions.templates` option to the :js:func:`test case
|
||||||
|
function <openerp.testing.case>`.
|
||||||
|
|
||||||
|
This will automatically load all relevant templates in the instance's
|
||||||
|
qweb before running the test case:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "Demonstration of web/javascript tests",
|
||||||
|
'category': 'Hidden',
|
||||||
|
'depends': ['web'],
|
||||||
|
'js': ['static/src/js/demo.js'],
|
||||||
|
'test': ['static/test/demo.js'],
|
||||||
|
'qweb': ['static/src/xml/demo.xml'],
|
||||||
|
}
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<!-- src/xml/demo.xml -->
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
<t t-name="DemoTemplate">
|
||||||
|
<t t-foreach="5" t-as="value">
|
||||||
|
<p><t t-esc="value"/></p>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
// test/demo.js
|
||||||
|
test('templates', {templates: true}, function (instance) {
|
||||||
|
var s = instance.web.qweb.render('DemoTemplate');
|
||||||
|
var texts = $(s).find('p').map(function () {
|
||||||
|
return $(this).text();
|
||||||
|
}).get();
|
||||||
|
|
||||||
|
deepEqual(texts, ['0', '1', '2', '3', '4']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Asynchronous cases
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The test case examples so far are all synchronous, they execute from
|
||||||
|
the first to the last line and once the last line has executed the
|
||||||
|
test is done. But the web client is full of :doc:`asynchronous code
|
||||||
|
</async>`, and thus test cases need to be async-aware.
|
||||||
|
|
||||||
|
This is done by returning a :js:class:`deferred <Deferred>` from the
|
||||||
|
case callback::
|
||||||
|
|
||||||
|
// test/demo.js
|
||||||
|
test('asynchronous', {
|
||||||
|
asserts: 1
|
||||||
|
}, function () {
|
||||||
|
var d = $.Deferred();
|
||||||
|
setTimeout(function () {
|
||||||
|
ok(true);
|
||||||
|
d.resolve();
|
||||||
|
}, 100);
|
||||||
|
return d;
|
||||||
|
});
|
||||||
|
|
||||||
|
This example also uses the :js:class:`options parameter <TestOptions>`
|
||||||
|
to specify the number of assertions the case should expect, if less or
|
||||||
|
more assertions are specified the case will count as failed.
|
||||||
|
|
||||||
|
Asynchronous test cases *must* specify the number of assertions they
|
||||||
|
will run. This allows more easily catching situations where e.g. the
|
||||||
|
test architecture was not warned about asynchronous operations.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Asynchronous test cases also have a 2 seconds timeout: if the test
|
||||||
|
does not finish within 2 seconds, it will be considered
|
||||||
|
failed. This pretty much always means the test will not
|
||||||
|
resolve. This timeout *only* applies to the test itself, not to
|
||||||
|
the setup and teardown processes.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If the returned deferred is rejected, the test will be failed
|
||||||
|
unless :js:attr:`~TestOptions.fail_on_rejection` is set to
|
||||||
|
``false``.
|
||||||
|
|
||||||
|
RPC
|
||||||
|
---
|
||||||
|
|
||||||
|
An important subset of asynchronous test cases is test cases which
|
||||||
|
need to perform (and chain, to an extent) RPC calls.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Because they are a subset of asynchronous cases, RPC cases must
|
||||||
|
also provide a valid :js:attr:`assertions count
|
||||||
|
<TestOptions.asserts>`.
|
||||||
|
|
||||||
|
By default, test cases will fail when trying to perform an RPC
|
||||||
|
call. The ability to perform RPC calls must be explicitly requested by
|
||||||
|
a test case (or its containing test suite) through
|
||||||
|
:js:attr:`~TestOptions.rpc`, and can be one of two modes: ``mock`` or
|
||||||
|
``rpc``.
|
||||||
|
|
||||||
|
Mock RPC
|
||||||
|
++++++++
|
||||||
|
|
||||||
|
The preferred (and fastest from a setup and execution time point of
|
||||||
|
view) way to do RPC during tests is to mock the RPC calls: while
|
||||||
|
setting up the test case, provide what the RPC responses "should" be,
|
||||||
|
and only test the code between the "user" (the test itself) and the
|
||||||
|
RPC call, before the call is effectively done.
|
||||||
|
|
||||||
|
To do this, set the :js:attr:`rpc option <TestOptions.rpc>` to
|
||||||
|
``mock``. This will add a third parameter to the test case callback:
|
||||||
|
|
||||||
|
.. js:function:: mock(rpc_spec, handler)
|
||||||
|
|
||||||
|
Can be used in two different ways depending on the shape of the
|
||||||
|
first parameter:
|
||||||
|
|
||||||
|
* If it matches the pattern ``model:method`` (if it contains a
|
||||||
|
colon, essentially) the call will set up the mocking of an RPC
|
||||||
|
call straight to the OpenERP server (through XMLRPC) as
|
||||||
|
performed via e.g. :js:func:`openerp.web.Model.call`.
|
||||||
|
|
||||||
|
In that case, ``handler`` should be a function taking two
|
||||||
|
arguments ``args`` and ``kwargs``, matching the corresponding
|
||||||
|
arguments on the server side and should simply return the value
|
||||||
|
as if it were returned by the Python XMLRPC handler::
|
||||||
|
|
||||||
|
test('XML-RPC', {rpc: 'mock', asserts: 3}, function (instance, $s, mock) {
|
||||||
|
// set up mocking
|
||||||
|
mock('people.famous:name_search', function (args, kwargs) {
|
||||||
|
strictEqual(kwargs.name, 'bob');
|
||||||
|
return [
|
||||||
|
[1, "Microsoft Bob"],
|
||||||
|
[2, "Bob the Builder"],
|
||||||
|
[3, "Silent Bob"]
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
// actual test code
|
||||||
|
return new instance.web.Model('people.famous')
|
||||||
|
.call('name_search', {name: 'bob'}).then(function (result) {
|
||||||
|
strictEqual(result.length, 3, "shoud return 3 people");
|
||||||
|
strictEqual(result[0][1], "Microsoft Bob",
|
||||||
|
"the most famous bob should be Microsoft Bob");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
* Otherwise, if it matches an absolute path (e.g. ``/a/b/c``) it
|
||||||
|
will mock a JSON-RPC call to a web client controller, such as
|
||||||
|
``/web/webclient/translations``. In that case, the handler takes
|
||||||
|
a single ``params`` argument holding all of the parameters
|
||||||
|
provided over JSON-RPC.
|
||||||
|
|
||||||
|
As previously, the handler should simply return the result value
|
||||||
|
as if returned by the original JSON-RPC handler::
|
||||||
|
|
||||||
|
test('JSON-RPC', {rpc: 'mock', asserts: 3, templates: true}, function (instance, $s, mock) {
|
||||||
|
var fetched_dbs = false, fetched_langs = false;
|
||||||
|
mock('/web/database/get_list', function () {
|
||||||
|
fetched_dbs = true;
|
||||||
|
return ['foo', 'bar', 'baz'];
|
||||||
|
});
|
||||||
|
mock('/web/session/get_lang_list', function () {
|
||||||
|
fetched_langs = true;
|
||||||
|
return [['vo_IS', 'Hopelandic / Vonlenska']];
|
||||||
|
});
|
||||||
|
|
||||||
|
// widget needs that or it blows up
|
||||||
|
instance.webclient = {toggle_bars: openerp.testing.noop};
|
||||||
|
var dbm = new instance.web.DatabaseManager({});
|
||||||
|
return dbm.appendTo($s).then(function () {
|
||||||
|
ok(fetched_dbs, "should have fetched databases");
|
||||||
|
ok(fetched_langs, "should have fetched languages");
|
||||||
|
deepEqual(dbm.db_list, ['foo', 'bar', 'baz']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Mock handlers can contain assertions, these assertions should be
|
||||||
|
part of the assertions count (and if multiple calls are made to a
|
||||||
|
handler containing assertions, it multiplies the effective number
|
||||||
|
of assertions).
|
||||||
|
|
||||||
|
.. _testing-rpc-rpc:
|
||||||
|
|
||||||
|
Actual RPC
|
||||||
|
++++++++++
|
||||||
|
|
||||||
|
A more realistic (but significantly slower and more expensive) way to
|
||||||
|
perform RPC calls is to perform actual calls to an actually running
|
||||||
|
OpenERP server. To do this, set the :js:attr:`rpc option
|
||||||
|
<~TestOptions.rpc>` to ``rpc``, it will not provide any new parameter
|
||||||
|
but will enable actual RPC, and the automatic creation and destruction
|
||||||
|
of databases (from a specified source) around tests.
|
||||||
|
|
||||||
|
First, create a basic model we can test stuff with:
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
from openerp.osv import orm, fields
|
||||||
|
|
||||||
|
class TestObject(orm.Model):
|
||||||
|
_name = 'web_tests_demo.model'
|
||||||
|
|
||||||
|
_columns = {
|
||||||
|
'name': fields.char("Name", required=True),
|
||||||
|
'thing': fields.char("Thing"),
|
||||||
|
'other': fields.char("Other", required=True)
|
||||||
|
}
|
||||||
|
_defaults = {
|
||||||
|
'other': "bob"
|
||||||
|
}
|
||||||
|
|
||||||
|
then the actual test::
|
||||||
|
|
||||||
|
test('actual RPC', {rpc: 'rpc', asserts: 4}, function (instance) {
|
||||||
|
var Model = new instance.web.Model('web_tests_demo.model');
|
||||||
|
return Model.call('create', [{name: "Bob"}])
|
||||||
|
.then(function (id) {
|
||||||
|
return Model.call('read', [[id]]);
|
||||||
|
}).then(function (records) {
|
||||||
|
strictEqual(records.length, 1);
|
||||||
|
var record = records[0];
|
||||||
|
strictEqual(record.name, "Bob");
|
||||||
|
strictEqual(record.thing, false);
|
||||||
|
// default value
|
||||||
|
strictEqual(record.other, 'bob');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
This test looks like a "mock" RPC test but for the lack of mock
|
||||||
|
response (and the different ``rpc`` type), however it has further
|
||||||
|
ranging consequences in that it will copy an existing database to a
|
||||||
|
new one, run the test in full on that temporary database and destroy
|
||||||
|
the database, to simulate an isolated and transactional context and
|
||||||
|
avoid affecting other tests. One of the consequences is that it takes
|
||||||
|
a *long* time to run (5~10s, most of that time being spent waiting for
|
||||||
|
a database duplication).
|
||||||
|
|
||||||
|
Furthermore, as the test needs to clone a database, it also has to ask
|
||||||
|
which database to clone, the database/super-admin password and the
|
||||||
|
password of the ``admin`` user (in order to authenticate as said
|
||||||
|
user). As a result, the first time the test runner encounters an
|
||||||
|
``rpc: "rpc"`` test configuration it will produce the following
|
||||||
|
prompt:
|
||||||
|
|
||||||
|
.. image:: ./images/db-query.png
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
and stop the testing process until the necessary information has been
|
||||||
|
provided.
|
||||||
|
|
||||||
|
The prompt will only appear once per test run, all tests will use the
|
||||||
|
same "source" database.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The handling of that information is currently rather brittle and
|
||||||
|
unchecked, incorrect values will likely crash the runner.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The runner does not currently store this information (for any
|
||||||
|
longer than a test run that is), the prompt will have to be filled
|
||||||
|
every time.
|
||||||
|
|
||||||
|
Testing API
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. js:function:: openerp.testing.section(name[, options], body)
|
||||||
|
|
||||||
|
A test section, serves as shared namespace for related tests (for
|
||||||
|
constants or values to only set up once). The ``body`` function
|
||||||
|
should contain the tests themselves.
|
||||||
|
|
||||||
|
Note that the order in which tests are run is essentially
|
||||||
|
undefined, do *not* rely on it.
|
||||||
|
|
||||||
|
:param String name:
|
||||||
|
:param TestOptions options:
|
||||||
|
:param body:
|
||||||
|
:type body: Function<:js:func:`~openerp.testing.case`, void>
|
||||||
|
|
||||||
|
.. js:function:: openerp.testing.case(name[, options], callback)
|
||||||
|
|
||||||
|
Registers a test case callback in the test runner, the callback
|
||||||
|
will only be run once the runner is started (or maybe not at all,
|
||||||
|
if the test is filtered out).
|
||||||
|
|
||||||
|
:param String name:
|
||||||
|
:param TestOptions options:
|
||||||
|
:param callback:
|
||||||
|
:type callback: Function<instance, $, Function<String, Function, void>>
|
||||||
|
|
||||||
|
.. js:class:: TestOptions
|
||||||
|
|
||||||
|
the various options which can be passed to
|
||||||
|
:js:func:`~openerp.testing.section` or
|
||||||
|
:js:func:`~openerp.testing.case`. Except for
|
||||||
|
:js:attr:`~TestOptions.setup` and
|
||||||
|
:js:attr:`~TestOptions.teardown`, an option on
|
||||||
|
:js:func:`~openerp.testing.case` will overwrite the corresponding
|
||||||
|
option on :js:func:`~openerp.testing.section` so
|
||||||
|
e.g. :js:attr:`~TestOptions.rpc` can be set for a
|
||||||
|
:js:func:`~openerp.testing.section` and then differently set for
|
||||||
|
some :js:func:`~openerp.testing.case` of that
|
||||||
|
:js:func:`~openerp.testing.section`
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.asserts
|
||||||
|
|
||||||
|
An integer, the number of assertions which should run during a
|
||||||
|
normal execution of the test. Mandatory for asynchronous tests.
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.setup
|
||||||
|
|
||||||
|
Test case setup, run right before each test case. A section's
|
||||||
|
:js:func:`~TestOptions.setup` is run before the case's own, if
|
||||||
|
both are specified.
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.teardown
|
||||||
|
|
||||||
|
Test case teardown, a case's :js:func:`~TestOptions.teardown`
|
||||||
|
is run before the corresponding section if both are present.
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.fail_on_rejection
|
||||||
|
|
||||||
|
If the test is asynchronous and its resulting promise is
|
||||||
|
rejected, fail the test. Defaults to ``true``, set to
|
||||||
|
``false`` to not fail the test in case of rejection::
|
||||||
|
|
||||||
|
// test/demo.js
|
||||||
|
test('unfail rejection', {
|
||||||
|
asserts: 1,
|
||||||
|
fail_on_rejection: false
|
||||||
|
}, function () {
|
||||||
|
var d = $.Deferred();
|
||||||
|
setTimeout(function () {
|
||||||
|
ok(true);
|
||||||
|
d.reject();
|
||||||
|
}, 100);
|
||||||
|
return d;
|
||||||
|
});
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.rpc
|
||||||
|
|
||||||
|
RPC method to use during tests, one of ``"mock"`` or
|
||||||
|
``"rpc"``. Any other value will disable RPC for the test (if
|
||||||
|
they were enabled by the suite for instance).
|
||||||
|
|
||||||
|
.. js:attribute:: TestOptions.templates
|
||||||
|
|
||||||
|
Whether the current module (and its dependencies)'s templates
|
||||||
|
should be loaded into QWeb before starting the test. A
|
||||||
|
boolean, ``false`` by default.
|
||||||
|
|
||||||
|
The test runner can also use two global configuration values set
|
||||||
|
directly on the ``window`` object:
|
||||||
|
|
||||||
|
* ``oe_all_dependencies`` is an ``Array`` of all modules with a web
|
||||||
|
component, ordered by dependency (for a module ``A`` with
|
||||||
|
dependencies ``A'``, any module of ``A'`` must come before ``A`` in
|
||||||
|
the array)
|
||||||
|
|
||||||
|
* ``oe_db_info`` is an object with 3 keys ``source``, ``supadmin`` and
|
||||||
|
``password``. It is used to pre-configure :ref:`actual RPC
|
||||||
|
<testing-rpc-rpc>` tests, to avoid a prompt being displayed
|
||||||
|
(especially for headless situations).
|
||||||
|
|
||||||
|
Running through Python
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The web client includes the means to run these tests on the
|
||||||
|
command-line (or in a CI system), but while actually running it is
|
||||||
|
pretty simple the setup of the pre-requisite parts has some
|
||||||
|
complexities.
|
||||||
|
|
||||||
|
1. Install unittest2_ and QUnitSuite_ in your Python environment. Both
|
||||||
|
can trivially be installed via `pip <http://pip-installer.org>`_ or
|
||||||
|
`easy_install
|
||||||
|
<http://packages.python.org/distribute/easy_install.html>`_.
|
||||||
|
|
||||||
|
The former is the unit-testing framework used by OpenERP, the
|
||||||
|
latter is an adapter module to run qunit_ test suites and convert
|
||||||
|
their result into something unittest2_ can understand and report.
|
||||||
|
|
||||||
|
2. Install PhantomJS_. It is a headless
|
||||||
|
browser which allows automating running and testing web
|
||||||
|
pages. QUnitSuite_ uses it to actually run the qunit_ test suite.
|
||||||
|
|
||||||
|
The PhantomJS_ website provides pre-built binaries for some
|
||||||
|
platforms, and your OS's package management probably provides it as
|
||||||
|
well.
|
||||||
|
|
||||||
|
If you're building PhantomJS_ from source, I recommend preparing
|
||||||
|
for some knitting time as it's not exactly fast (it needs to
|
||||||
|
compile both `Qt <http://qt-project.org/>`_ and `Webkit
|
||||||
|
<http://www.webkit.org/>`_, both being pretty big projects).
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Because PhantomJS_ is webkit-based, it will not be able to test
|
||||||
|
if Firefox, Opera or Internet Explorer can correctly run the
|
||||||
|
test suite (and it is only an approximation for Safari and
|
||||||
|
Chrome). It is therefore recommended to *also* run the test
|
||||||
|
suites in actual browsers once in a while.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The version of PhantomJS_ this was build through is 1.7,
|
||||||
|
previous versions *should* work but are not actually supported
|
||||||
|
(and tend to just segfault when something goes wrong in
|
||||||
|
PhantomJS_ itself so they're a pain to debug).
|
||||||
|
|
||||||
|
3. Set up :ref:`OpenERP Command <openerpcommand:openerp-command>`,
|
||||||
|
which will be used to actually run the tests: running the qunit_
|
||||||
|
test suite requires a running server, so at this point OpenERP
|
||||||
|
Server isn't able to do it on its own during the building/testing
|
||||||
|
process.
|
||||||
|
|
||||||
|
4. Install a new database with all relevant modules (all modules with
|
||||||
|
a web component at least), then restart the server
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
For some tests, a source database needs to be duplicated. This
|
||||||
|
operation requires that there be no connection to the database
|
||||||
|
being duplicated, but OpenERP doesn't currently break
|
||||||
|
existing/outstanding connections, so restarting the server is
|
||||||
|
the simplest way to ensure everything is in the right state.
|
||||||
|
|
||||||
|
5. Launch ``oe run-tests -d $DATABASE -mweb`` with the correct
|
||||||
|
addons-path specified (and replacing ``$DATABASE`` by the source
|
||||||
|
database you created above)
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you leave out ``-mweb``, the runner will attempt to run all
|
||||||
|
the tests in all the modules, which may or may not work.
|
||||||
|
|
||||||
|
If everything went correctly, you should now see a list of tests with
|
||||||
|
(hopefully) ``ok`` next to their names, closing with a report of the
|
||||||
|
number of tests run and the time it took:
|
||||||
|
|
||||||
|
.. literalinclude:: test-report.txt
|
||||||
|
:language: text
|
||||||
|
|
||||||
|
Congratulation, you have just performed a successful "offline" run of
|
||||||
|
the OpenERP Web test suite.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Note that this runs all the Python tests for the ``web`` module,
|
||||||
|
but all the web tests for all of OpenERP. This can be surprising.
|
||||||
|
|
||||||
|
.. _qunit: http://qunitjs.com/
|
||||||
|
|
||||||
|
.. _qunit assertions: http://api.qunitjs.com/category/assert/
|
||||||
|
|
||||||
|
.. _unittest2: http://pypi.python.org/pypi/unittest2
|
||||||
|
|
||||||
|
.. _QUnitSuite: http://pypi.python.org/pypi/QUnitSuite/
|
||||||
|
|
||||||
|
.. _PhantomJS: http://phantomjs.org/
|
|
@ -11,6 +11,7 @@ import logging
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
|
import random
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
|
@ -21,6 +22,7 @@ import urlparse
|
||||||
import uuid
|
import uuid
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
|
|
||||||
|
import babel.core
|
||||||
import simplejson
|
import simplejson
|
||||||
import werkzeug.contrib.sessions
|
import werkzeug.contrib.sessions
|
||||||
import werkzeug.datastructures
|
import werkzeug.datastructures
|
||||||
|
@ -91,9 +93,25 @@ class WebRequest(object):
|
||||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||||
self.session = self.httpsession.get(self.session_id)
|
self.session = self.httpsession.get(self.session_id)
|
||||||
if not self.session:
|
if not self.session:
|
||||||
self.httpsession[self.session_id] = self.session = session.OpenERPSession()
|
self.session = session.OpenERPSession()
|
||||||
|
self.httpsession[self.session_id] = self.session
|
||||||
self.context = self.params.pop('context', None)
|
self.context = self.params.pop('context', None)
|
||||||
self.debug = self.params.pop('debug', False) != False
|
self.debug = self.params.pop('debug', False) is not False
|
||||||
|
# Determine self.lang
|
||||||
|
lang = self.params.get('lang', None)
|
||||||
|
if lang is None:
|
||||||
|
lang = self.session.eval_context(self.context).get('lang')
|
||||||
|
if lang is None:
|
||||||
|
lang = self.httprequest.cookies.get('lang')
|
||||||
|
if lang is None:
|
||||||
|
lang = self.httprequest.accept_languages.best
|
||||||
|
if lang is None:
|
||||||
|
lang = 'en_US'
|
||||||
|
# tranform 2 letters lang like 'en' into 5 letters like 'en_US'
|
||||||
|
lang = babel.core.LOCALE_ALIASES.get(lang, lang)
|
||||||
|
# we use _ as seprator where RFC2616 uses '-'
|
||||||
|
self.lang = lang.replace('-', '_')
|
||||||
|
|
||||||
|
|
||||||
class JsonRequest(WebRequest):
|
class JsonRequest(WebRequest):
|
||||||
""" JSON-RPC2 over HTTP.
|
""" JSON-RPC2 over HTTP.
|
||||||
|
@ -210,6 +228,10 @@ class JsonRequest(WebRequest):
|
||||||
_logger.debug("<--\n%s", pprint.pformat(response))
|
_logger.debug("<--\n%s", pprint.pformat(response))
|
||||||
|
|
||||||
if jsonp:
|
if jsonp:
|
||||||
|
# If we use jsonp, that's mean we are called from another host
|
||||||
|
# Some browser (IE and Safari) do no allow third party cookies
|
||||||
|
# We need then to manage http sessions manually.
|
||||||
|
response['httpsessionid'] = self.httpsession.sid
|
||||||
mime = 'application/javascript'
|
mime = 'application/javascript'
|
||||||
body = "%s(%s);" % (jsonp, simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder),)
|
body = "%s(%s);" % (jsonp, simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder),)
|
||||||
else:
|
else:
|
||||||
|
@ -336,23 +358,13 @@ class Controller(object):
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
# Session context manager
|
# Session context manager
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
STORES = {}
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def session_context(request, storage_path, session_cookie='httpsessionid'):
|
def session_context(request, session_store, session_lock, sid):
|
||||||
session_store, session_lock = STORES.get(storage_path, (None, None))
|
|
||||||
if not session_store:
|
|
||||||
session_store = werkzeug.contrib.sessions.FilesystemSessionStore( storage_path)
|
|
||||||
session_lock = threading.Lock()
|
|
||||||
STORES[storage_path] = session_store, session_lock
|
|
||||||
|
|
||||||
sid = request.cookies.get(session_cookie)
|
|
||||||
with session_lock:
|
with session_lock:
|
||||||
if sid:
|
if sid:
|
||||||
request.session = session_store.get(sid)
|
request.session = session_store.get(sid)
|
||||||
else:
|
else:
|
||||||
request.session = session_store.new()
|
request.session = session_store.new()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield request.session
|
yield request.session
|
||||||
finally:
|
finally:
|
||||||
|
@ -404,6 +416,18 @@ def session_context(request, storage_path, session_cookie='httpsessionid'):
|
||||||
|
|
||||||
session_store.save(request.session)
|
session_store.save(request.session)
|
||||||
|
|
||||||
|
def session_gc(session_store):
|
||||||
|
if random.random() < 0.001:
|
||||||
|
# we keep session one week
|
||||||
|
last_week = time.time() - 60*60*24*7
|
||||||
|
for fname in os.listdir(session_store.path):
|
||||||
|
path = os.path.join(session_store.path, fname)
|
||||||
|
try:
|
||||||
|
if os.path.getmtime(path) < last_week:
|
||||||
|
os.unlink(path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
# WSGI Application
|
# WSGI Application
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
|
@ -434,26 +458,31 @@ class DisableCacheMiddleware(object):
|
||||||
start_response(status, new_headers)
|
start_response(status, new_headers)
|
||||||
return self.app(environ, start_wrapped)
|
return self.app(environ, start_wrapped)
|
||||||
|
|
||||||
|
def session_path():
|
||||||
|
try:
|
||||||
|
username = getpass.getuser()
|
||||||
|
except Exception:
|
||||||
|
username = "unknown"
|
||||||
|
path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.mkdir(path, 0700)
|
||||||
|
return path
|
||||||
|
|
||||||
class Root(object):
|
class Root(object):
|
||||||
"""Root WSGI application for the OpenERP Web Client.
|
"""Root WSGI application for the OpenERP Web Client.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.httpsession_cookie = 'httpsessionid'
|
|
||||||
self.addons = {}
|
self.addons = {}
|
||||||
|
|
||||||
static_dirs = self._load_addons()
|
static_dirs = self._load_addons()
|
||||||
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
|
app = werkzeug.wsgi.SharedDataMiddleware( self.dispatch, static_dirs)
|
||||||
self.dispatch = DisableCacheMiddleware(app)
|
self.dispatch = DisableCacheMiddleware(app)
|
||||||
|
|
||||||
try:
|
# Setup http sessions
|
||||||
username = getpass.getuser()
|
path = session_path()
|
||||||
except Exception:
|
self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path)
|
||||||
username = "unknown"
|
self.session_lock = threading.Lock()
|
||||||
self.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
_logger.debug('HTTP sessions stored in: %s', path)
|
||||||
|
|
||||||
if not os.path.exists(self.session_storage):
|
|
||||||
os.mkdir(self.session_storage, 0700)
|
|
||||||
_logger.debug('HTTP sessions stored in: %s', self.session_storage)
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
""" Handle a WSGI request
|
""" Handle a WSGI request
|
||||||
|
@ -476,7 +505,13 @@ class Root(object):
|
||||||
if not handler:
|
if not handler:
|
||||||
response = werkzeug.exceptions.NotFound()
|
response = werkzeug.exceptions.NotFound()
|
||||||
else:
|
else:
|
||||||
with session_context(request, self.session_storage, self.httpsession_cookie) as session:
|
sid = request.cookies.get('sid')
|
||||||
|
if not sid:
|
||||||
|
sid = request.args.get('sid')
|
||||||
|
|
||||||
|
session_gc(self.session_store)
|
||||||
|
|
||||||
|
with session_context(request, self.session_store, self.session_lock, sid) as session:
|
||||||
result = handler(request)
|
result = handler(request)
|
||||||
|
|
||||||
if isinstance(result, basestring):
|
if isinstance(result, basestring):
|
||||||
|
@ -486,7 +521,7 @@ class Root(object):
|
||||||
response = result
|
response = result
|
||||||
|
|
||||||
if hasattr(response, 'set_cookie'):
|
if hasattr(response, 'set_cookie'):
|
||||||
response.set_cookie(self.httpsession_cookie, session.sid)
|
response.set_cookie('sid', session.sid)
|
||||||
|
|
||||||
return response(environ, start_response)
|
return response(environ, start_response)
|
||||||
|
|
||||||
|
@ -530,7 +565,7 @@ class Root(object):
|
||||||
:rtype: ``Controller | None``
|
:rtype: ``Controller | None``
|
||||||
"""
|
"""
|
||||||
if l:
|
if l:
|
||||||
ps = '/' + '/'.join(l)
|
ps = '/' + '/'.join(filter(None, l))
|
||||||
method_name = 'index'
|
method_name = 'index'
|
||||||
while ps:
|
while ps:
|
||||||
c = controllers_path.get(ps)
|
c = controllers_path.get(ps)
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
"X-Poedit-Language: Czech\n"
|
"X-Poedit-Language: Czech\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -9,13 +9,13 @@ msgstr ""
|
||||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||||
"PO-Revision-Date: 2012-10-26 12:17+0000\n"
|
"PO-Revision-Date: 2012-10-26 12:17+0000\n"
|
||||||
"Last-Translator: Herczeg Péter <herczegp@gmail.com>\n"
|
"Last-Translator: Herczeg Péter <hp@erp-cloud.hu>\n"
|
||||||
"Language-Team: Hungarian <hu@li.org>\n"
|
"Language-Team: Hungarian <hu@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-27 05:15+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16194)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-23 05:05+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16179)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-22 04:46+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
@ -66,7 +66,7 @@ msgstr ""
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:499
|
#: addons/web/static/src/js/chrome.js:499
|
||||||
msgid "Restored"
|
msgid "Restored"
|
||||||
msgstr ""
|
msgstr "Atkurta"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:499
|
#: addons/web/static/src/js/chrome.js:499
|
||||||
|
@ -253,7 +253,7 @@ msgstr ""
|
||||||
#: addons/web/static/src/js/formats.js:139
|
#: addons/web/static/src/js/formats.js:139
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "(%d records)"
|
msgid "(%d records)"
|
||||||
msgstr ""
|
msgstr "(%d įrašai(-ų))"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/formats.js:325
|
#: addons/web/static/src/js/formats.js:325
|
||||||
|
@ -265,7 +265,7 @@ msgstr "Atsisiųsti"
|
||||||
#: addons/web/static/src/js/formats.js:330
|
#: addons/web/static/src/js/formats.js:330
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Download \"%s\""
|
msgid "Download \"%s\""
|
||||||
msgstr ""
|
msgstr "Parsisiųsti \"%s\""
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/search.js:437
|
#: addons/web/static/src/js/search.js:437
|
||||||
|
@ -527,12 +527,12 @@ msgstr "Forma"
|
||||||
#: addons/web/static/src/xml/base.xml:763
|
#: addons/web/static/src/xml/base.xml:763
|
||||||
#: addons/web/static/src/xml/base.xml:1714
|
#: addons/web/static/src/xml/base.xml:1714
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr "Ištrinti"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:762
|
#: addons/web/static/src/xml/base.xml:762
|
||||||
msgid "Duplicate"
|
msgid "Duplicate"
|
||||||
msgstr ""
|
msgstr "Sukurti kopiją"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/view_form.js:133
|
#: addons/web/static/src/js/view_form.js:133
|
||||||
|
@ -589,12 +589,12 @@ msgstr ""
|
||||||
#: addons/web/static/src/js/view_form.js:2238
|
#: addons/web/static/src/js/view_form.js:2238
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "<em> Create \"<strong>%s</strong>\"</em>"
|
msgid "<em> Create \"<strong>%s</strong>\"</em>"
|
||||||
msgstr ""
|
msgstr "<em> Sukurti \"<strong>%s</strong>\"</em>"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/view_form.js:2244
|
#: addons/web/static/src/js/view_form.js:2244
|
||||||
msgid "<em> Create and Edit...</em>"
|
msgid "<em> Create and Edit...</em>"
|
||||||
msgstr ""
|
msgstr "<em> Sukurti ir redaguoti...</em>"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/view_form.js:2277
|
#: addons/web/static/src/js/view_form.js:2277
|
||||||
|
@ -606,7 +606,7 @@ msgstr "Paieška: "
|
||||||
#: addons/web/static/src/js/view_form.js:2277
|
#: addons/web/static/src/js/view_form.js:2277
|
||||||
#: addons/web/static/src/js/view_form.js:2765
|
#: addons/web/static/src/js/view_form.js:2765
|
||||||
msgid "Create: "
|
msgid "Create: "
|
||||||
msgstr ""
|
msgstr "Sukurti "
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/view_form.js:2062
|
#: addons/web/static/src/js/view_form.js:2062
|
||||||
|
@ -668,7 +668,7 @@ msgstr "Grupė"
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/view_list.js:549
|
#: addons/web/static/src/js/view_list.js:549
|
||||||
msgid "Do you really want to remove these records?"
|
msgid "Do you really want to remove these records?"
|
||||||
msgstr ""
|
msgstr "Ar tikrai norite ištrinti šiuos įrašus?"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/views.js:925
|
#: addons/web/static/src/js/views.js:925
|
||||||
|
@ -813,25 +813,25 @@ msgstr ""
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:297
|
#: addons/web/static/src/xml/base.xml:297
|
||||||
msgid "Invalid username or password"
|
msgid "Invalid username or password"
|
||||||
msgstr ""
|
msgstr "Neteisingas naudotojo vardas arba slaptažodis"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:116
|
#: addons/web/static/src/xml/base.xml:116
|
||||||
#: addons/web/static/src/xml/base.xml:150
|
#: addons/web/static/src/xml/base.xml:150
|
||||||
#: addons/web/static/src/xml/base.xml:301
|
#: addons/web/static/src/xml/base.xml:301
|
||||||
msgid "Database:"
|
msgid "Database:"
|
||||||
msgstr ""
|
msgstr "Duomenų bazė:"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:306
|
#: addons/web/static/src/xml/base.xml:306
|
||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr ""
|
msgstr "Naudotojas"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:308
|
#: addons/web/static/src/xml/base.xml:308
|
||||||
#: addons/web/static/src/xml/base.xml:331
|
#: addons/web/static/src/xml/base.xml:331
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr ""
|
msgstr "Slaptažodis"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:310
|
#: addons/web/static/src/xml/base.xml:310
|
||||||
|
@ -870,17 +870,17 @@ msgstr ""
|
||||||
#: addons/web/static/src/xml/base.xml:195
|
#: addons/web/static/src/xml/base.xml:195
|
||||||
#: addons/web/static/src/xml/base.xml:330
|
#: addons/web/static/src/xml/base.xml:330
|
||||||
msgid "Restore"
|
msgid "Restore"
|
||||||
msgstr ""
|
msgstr "Atkurti"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:332
|
#: addons/web/static/src/xml/base.xml:332
|
||||||
msgid "Back to Login"
|
msgid "Back to Login"
|
||||||
msgstr ""
|
msgstr "Grįžti į prisijungimo langą"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:61
|
#: addons/web/static/src/xml/base.xml:61
|
||||||
msgid "CREATE DATABASE"
|
msgid "CREATE DATABASE"
|
||||||
msgstr ""
|
msgstr "SUKURTI DUOMENŲ BAZĘ"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211
|
#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211
|
||||||
|
@ -922,7 +922,7 @@ msgstr ""
|
||||||
#: addons/web/static/src/xml/base.xml:162
|
#: addons/web/static/src/xml/base.xml:162
|
||||||
#: addons/web/static/src/xml/base.xml:187
|
#: addons/web/static/src/xml/base.xml:187
|
||||||
msgid "Master Password:"
|
msgid "Master Password:"
|
||||||
msgstr ""
|
msgstr "Pagrindinis slaptažodis"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:143
|
#: addons/web/static/src/xml/base.xml:143
|
||||||
|
@ -932,7 +932,7 @@ msgstr ""
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:175
|
#: addons/web/static/src/xml/base.xml:175
|
||||||
msgid "RESTORE DATABASE"
|
msgid "RESTORE DATABASE"
|
||||||
msgstr ""
|
msgstr "ATKURTI DUOMENŲ BAZĘ"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:182
|
#: addons/web/static/src/xml/base.xml:182
|
||||||
|
@ -1012,17 +1012,17 @@ msgstr "OpenERP.com"
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1720
|
#: addons/web/static/src/xml/base.xml:1720
|
||||||
msgid "Old Password:"
|
msgid "Old Password:"
|
||||||
msgstr ""
|
msgstr "Esamas slaptažodis:"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1725
|
#: addons/web/static/src/xml/base.xml:1725
|
||||||
msgid "New Password:"
|
msgid "New Password:"
|
||||||
msgstr ""
|
msgstr "Naujas slaptažodis:"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1730
|
#: addons/web/static/src/xml/base.xml:1730
|
||||||
msgid "Confirm Password:"
|
msgid "Confirm Password:"
|
||||||
msgstr ""
|
msgstr "Patvirtinti slaptažodį:"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:390
|
#: addons/web/static/src/xml/base.xml:390
|
||||||
|
@ -1254,7 +1254,7 @@ msgstr ""
|
||||||
#: addons/web/static/src/xml/base.xml:1222
|
#: addons/web/static/src/xml/base.xml:1222
|
||||||
#: addons/web/static/src/xml/base.xml:1279
|
#: addons/web/static/src/xml/base.xml:1279
|
||||||
msgid "Clear"
|
msgid "Clear"
|
||||||
msgstr ""
|
msgstr "Išvalyti"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1179
|
#: addons/web/static/src/xml/base.xml:1179
|
||||||
|
@ -1408,12 +1408,12 @@ msgstr ""
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1509
|
#: addons/web/static/src/xml/base.xml:1509
|
||||||
msgid "Save & New"
|
msgid "Save & New"
|
||||||
msgstr ""
|
msgstr "Išsaugoti ir sukurti naują"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1510
|
#: addons/web/static/src/xml/base.xml:1510
|
||||||
msgid "Save & Close"
|
msgid "Save & Close"
|
||||||
msgstr ""
|
msgstr "Išsaugoti ir uždaryti"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/xml/base.xml:1617
|
#: addons/web/static/src/xml/base.xml:1617
|
||||||
|
@ -1572,9 +1572,6 @@ msgstr ""
|
||||||
#~ msgid "Advanced Filters"
|
#~ msgid "Advanced Filters"
|
||||||
#~ msgstr "Išplėstiniai filtrai"
|
#~ msgstr "Išplėstiniai filtrai"
|
||||||
|
|
||||||
#~ msgid "Other Options"
|
|
||||||
#~ msgstr "Kiti nustatymai"
|
|
||||||
|
|
||||||
#~ msgid "OK"
|
#~ msgid "OK"
|
||||||
#~ msgstr "Gerai"
|
#~ msgstr "Gerai"
|
||||||
|
|
||||||
|
@ -1602,3 +1599,22 @@ msgstr ""
|
||||||
|
|
||||||
#~ msgid "Reports"
|
#~ msgid "Reports"
|
||||||
#~ msgstr "Ataskaitos"
|
#~ msgstr "Ataskaitos"
|
||||||
|
|
||||||
|
#~ msgid "Other Options"
|
||||||
|
#~ msgstr "Kitos parinktys"
|
||||||
|
|
||||||
|
#~ msgid "LOGOUT"
|
||||||
|
#~ msgstr "ATSIJUNGTI"
|
||||||
|
|
||||||
|
#~ msgid "Create..."
|
||||||
|
#~ msgstr "Sukurti..."
|
||||||
|
|
||||||
|
#~ msgid "Search"
|
||||||
|
#~ msgstr "Ieškoti"
|
||||||
|
|
||||||
|
#~ msgid "Search..."
|
||||||
|
#~ msgstr "Ieškoti..."
|
||||||
|
|
||||||
|
#, python-format
|
||||||
|
#~ msgid "[%(first_record)d to %(last_record)d] of %(records_count)d"
|
||||||
|
#~ msgstr "[%(first_record)d iki %(last_record)d] iš %(records_count)d"
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
@ -243,17 +243,19 @@ msgid ""
|
||||||
"Destination fields should only be selected once, some fields are selected "
|
"Destination fields should only be selected once, some fields are selected "
|
||||||
"more than once:"
|
"more than once:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Pola docelowe muszą być wybierane tylko raz. niektóre inne pola mogą być "
|
||||||
|
"wybierane więcej niż raz:"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/data_import.js:386
|
#: addons/web/static/src/js/data_import.js:386
|
||||||
msgid "*Required Fields are not selected :"
|
msgid "*Required Fields are not selected :"
|
||||||
msgstr ""
|
msgstr "*Wymagane pola nie zostały wybrane :"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/formats.js:139
|
#: addons/web/static/src/js/formats.js:139
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "(%d records)"
|
msgid "(%d records)"
|
||||||
msgstr ""
|
msgstr "(%d rekordów)"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/formats.js:325
|
#: addons/web/static/src/js/formats.js:325
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -15,8 +15,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-11-02 05:20+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16218)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-11-02 05:20+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16218)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:02+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:07+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-11-07 04:55+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16232)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-10-21 05:03+0000\n"
|
"X-Launchpad-Export-Date: 2012-11-15 05:08+0000\n"
|
||||||
"X-Generator: Launchpad (build 16165)\n"
|
"X-Generator: Launchpad (build 16265)\n"
|
||||||
|
|
||||||
#. openerp-web
|
#. openerp-web
|
||||||
#: addons/web/static/src/js/chrome.js:176
|
#: addons/web/static/src/js/chrome.js:176
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
var $tip = this.tip();
|
var $tip = this.tip();
|
||||||
|
|
||||||
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
||||||
$tip[0].className = 'tipsy openerp oe_tooltip '; // reset classname in case of dynamic gravity
|
$tip[0].className = 'tipsy '; // reset classname in case of dynamic gravity
|
||||||
|
$tip.openerpClass('oe_tooltip');
|
||||||
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
||||||
|
|
||||||
var pos = $.extend({}, this.$element.offset(), {
|
var pos = $.extend({}, this.$element.offset(), {
|
||||||
|
|
|
@ -1115,7 +1115,7 @@ body .ui-tooltip { border-width:2px; }
|
||||||
|
|
||||||
|
|
||||||
/*** Input field styling from Bootstrap **/
|
/*** Input field styling from Bootstrap **/
|
||||||
input, textarea {
|
/* input, textarea {
|
||||||
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
-moz-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
-ms-transition: border linear 0.2s, box-shadow linear 0.2s;
|
-ms-transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||||
|
@ -1137,12 +1137,12 @@ input[type=file]:focus, input[type=checkbox]:focus, select:focus {
|
||||||
-moz-box-shadow: none;
|
-moz-box-shadow: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
outline: 1px dotted #666;
|
outline: 1px dotted #666;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
input[type="text"],
|
/*input[type="text"],
|
||||||
input[type="password"],
|
input[type="password"],*/
|
||||||
.ui-autocomplete-input,
|
.ui-autocomplete-input,
|
||||||
textarea,
|
/*textarea,*/
|
||||||
.uneditable-input {
|
.uneditable-input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
|
@ -20,20 +20,6 @@
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
|
||||||
.oe_topbar, .oe_leftbar, .oe_loading {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.openerp.openerp_webclient_container {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-tag .text-button {
|
|
||||||
height: auto !important;
|
|
||||||
min-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.openerp {
|
.openerp {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -46,6 +32,9 @@
|
||||||
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
.openerp.openerp_webclient_container {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.openerp :-moz-placeholder {
|
.openerp :-moz-placeholder {
|
||||||
color: #afafb6 !important;
|
color: #afafb6 !important;
|
||||||
font-style: italic !important;
|
font-style: italic !important;
|
||||||
|
@ -197,6 +186,10 @@
|
||||||
.openerp .oe_bounce_container {
|
.openerp .oe_bounce_container {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
.openerp .text-tag .text-button {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 16px;
|
||||||
|
}
|
||||||
.openerp .ui-tabs {
|
.openerp .ui-tabs {
|
||||||
position: static;
|
position: static;
|
||||||
}
|
}
|
||||||
|
@ -1136,6 +1129,7 @@
|
||||||
height: 40px;
|
height: 40px;
|
||||||
width: 157px;
|
width: 157px;
|
||||||
margin: 14px 0;
|
margin: 14px 0;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
.openerp .oe_footer {
|
.openerp .oe_footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -2306,16 +2300,16 @@
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
.openerp .oe_form_editable .oe_form .oe_form_field_integer input {
|
.openerp .oe_form_editable .oe_form .oe_form_field_integer input {
|
||||||
width: 6em !important;
|
width: 6em;
|
||||||
}
|
}
|
||||||
.openerp .oe_form_editable .oe_form .oe_form_field_float input {
|
.openerp .oe_form_editable .oe_form .oe_form_field_float input {
|
||||||
width: 7em !important;
|
width: 7em;
|
||||||
}
|
}
|
||||||
.openerp .oe_form_editable .oe_form .oe_form_field_date input {
|
.openerp .oe_form_editable .oe_form .oe_form_field_date input {
|
||||||
width: 7.5em !important;
|
width: 7.5em;
|
||||||
}
|
}
|
||||||
.openerp .oe_form_editable .oe_form .oe_form_field_datetime input {
|
.openerp .oe_form_editable .oe_form .oe_form_field_datetime input {
|
||||||
width: 11.5em !important;
|
width: 11.5em;
|
||||||
}
|
}
|
||||||
.openerp .oe_hidden_input_file {
|
.openerp .oe_hidden_input_file {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -2361,6 +2355,77 @@
|
||||||
.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
|
.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_fileupload {
|
||||||
|
display: inline-block;
|
||||||
|
clear: both;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add {
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
left: 2px;
|
||||||
|
top: 7px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add button {
|
||||||
|
display: inline;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 12px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add button.oe_attach {
|
||||||
|
width: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 24px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
color: #7c7bad;
|
||||||
|
box-shadow: none;
|
||||||
|
border: none;
|
||||||
|
text-shadow: none;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
left: -9px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -5px;
|
||||||
|
height: 28px;
|
||||||
|
width: 52px;
|
||||||
|
margin-top: -26px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_add .oe_attach_label {
|
||||||
|
color: #7c7bad;
|
||||||
|
margin-left: -3px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
margin-right: 0px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: solid 1px rgba(124, 123, 173, 0.14);
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments .oe_attachment {
|
||||||
|
padding: 2px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
|
||||||
|
font-size: 23px;
|
||||||
|
margin-top: -5px;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
|
||||||
|
background: #f4f5fa;
|
||||||
|
}
|
||||||
.openerp .oe_form_field_many2one td:first-child {
|
.openerp .oe_form_field_many2one td:first-child {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -2374,6 +2439,9 @@
|
||||||
float: right;
|
float: right;
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_form_field_many2one input {
|
||||||
|
padding-right: 13px;
|
||||||
|
}
|
||||||
.openerp.ui-autocomplete li.oe_m2o_dropdown_option a {
|
.openerp.ui-autocomplete li.oe_m2o_dropdown_option a {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
|
@ -2578,32 +2646,33 @@
|
||||||
top: 5px;
|
top: 5px;
|
||||||
}
|
}
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_cm_button {
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_cm_button {
|
||||||
display: none;
|
line-height: 19px;
|
||||||
|
}
|
||||||
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_input_icon {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: none;
|
||||||
}
|
}
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
|
||||||
height: 27px;
|
height: 27px;
|
||||||
}
|
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
|
|
||||||
-moz-border-radius: 0;
|
-moz-border-radius: 0;
|
||||||
-webkit-border-radius: 0;
|
-webkit-border-radius: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 1px solid #aaaaff;
|
border: 1px solid #aaaaff;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field select {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime > span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date > span {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
|
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_field_float, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_view_integer, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date {
|
|
||||||
min-width: 0 !important;
|
|
||||||
max-width: none !important;
|
|
||||||
}
|
|
||||||
.openerp .oe_list_group_name {
|
.openerp .oe_list_group_name {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -2668,6 +2737,9 @@
|
||||||
text-align: right !important;
|
text-align: right !important;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_list_content td.oe_list_field_date, .openerp .oe_list_content th.oe_list_header_date {
|
||||||
|
min-width: 6em;
|
||||||
|
}
|
||||||
.openerp .oe_list_content > thead {
|
.openerp .oe_list_content > thead {
|
||||||
border-bottom: 2px solid #cacaca;
|
border-bottom: 2px solid #cacaca;
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
|
@ -2735,6 +2807,10 @@
|
||||||
.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child:after, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child:after {
|
.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child:after, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child:after {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
.openerp .oe_list_content > tbody > tr > td.oe_list_field_boolean input {
|
||||||
|
filter: alpha(opacity=50);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
.openerp .oe_list_content > tbody > tr:nth-child(odd) {
|
.openerp .oe_list_content > tbody > tr:nth-child(odd) {
|
||||||
background-color: #f0f0fa;
|
background-color: #f0f0fa;
|
||||||
background-color: #f0f0fa;
|
background-color: #f0f0fa;
|
||||||
|
@ -2882,78 +2958,6 @@
|
||||||
color: #333333;
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.openerp .oe_fileupload {
|
|
||||||
display: inline-block;
|
|
||||||
clear: both;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add {
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
left: 2px;
|
|
||||||
top: 7px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add button {
|
|
||||||
display: inline;
|
|
||||||
height: 24px;
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 12px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add button.oe_attach {
|
|
||||||
width: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 24px;
|
|
||||||
overflow: hidden;
|
|
||||||
background: transparent;
|
|
||||||
color: #7c7bad;
|
|
||||||
box-shadow: none;
|
|
||||||
border: none;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
|
|
||||||
position: relative;
|
|
||||||
top: -1px;
|
|
||||||
left: -9px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: -5px;
|
|
||||||
height: 28px;
|
|
||||||
width: 52px;
|
|
||||||
margin-top: -26px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_add .oe_attach_label {
|
|
||||||
color: #7c7bad;
|
|
||||||
margin-left: -3px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
margin-right: 0px;
|
|
||||||
font-size: 12px;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: solid 1px rgba(124, 123, 173, 0.14);
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments .oe_attachment {
|
|
||||||
padding: 2px;
|
|
||||||
padding-left: 4px;
|
|
||||||
padding-right: 4px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
|
|
||||||
font-size: 23px;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
|
|
||||||
background: #f4f5fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kitten-mode-activated {
|
.kitten-mode-activated {
|
||||||
background-image: url(http://placekitten.com/g/1365/769);
|
background-image: url(http://placekitten.com/g/1365/769);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
@ -3012,8 +3016,8 @@ div.ui-widget-overlay {
|
||||||
.openerp {
|
.openerp {
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
.openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default {
|
.openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default, .openerp .oe_topbar, .openerp .oe_leftbar, .openerp .oe_loading {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
.openerp .oe_list_content button, .openerp .oe_list_content input[type=checkbox] {
|
.openerp .oe_list_content button, .openerp .oe_list_content input[type=checkbox] {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|
|
@ -140,18 +140,6 @@ $sheet-padding: 16px
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
@media print
|
|
||||||
.oe_topbar, .oe_leftbar, .oe_loading
|
|
||||||
display: none !important
|
|
||||||
|
|
||||||
.openerp.openerp_webclient_container
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
// jQueryUI css bug fixing
|
|
||||||
.text-tag .text-button
|
|
||||||
height: auto !important
|
|
||||||
min-height: 16px
|
|
||||||
|
|
||||||
.openerp
|
.openerp
|
||||||
// Global style {{{
|
// Global style {{{
|
||||||
padding: 0
|
padding: 0
|
||||||
|
@ -161,6 +149,8 @@ $sheet-padding: 16px
|
||||||
font-size: 13px
|
font-size: 13px
|
||||||
background: white
|
background: white
|
||||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
|
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
|
||||||
|
&.openerp_webclient_container
|
||||||
|
height: 100%
|
||||||
// }}}
|
// }}}
|
||||||
//Placeholder style{{{
|
//Placeholder style{{{
|
||||||
\:-moz-placeholder
|
\:-moz-placeholder
|
||||||
|
@ -254,6 +244,11 @@ $sheet-padding: 16px
|
||||||
.oe_bounce_container
|
.oe_bounce_container
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
||||||
|
// Bug lp:1051746
|
||||||
|
.text-tag .text-button
|
||||||
|
height: auto !important
|
||||||
|
min-height: 16px
|
||||||
|
|
||||||
// bug noted in jquery ui CSS doesn't seem to occur in IE9,
|
// bug noted in jquery ui CSS doesn't seem to occur in IE9,
|
||||||
// so remove position:relative
|
// so remove position:relative
|
||||||
.ui-tabs
|
.ui-tabs
|
||||||
|
@ -928,6 +923,7 @@ $sheet-padding: 16px
|
||||||
height: 40px
|
height: 40px
|
||||||
width: 157px
|
width: 157px
|
||||||
margin: 14px 0
|
margin: 14px 0
|
||||||
|
border: 0
|
||||||
.oe_footer
|
.oe_footer
|
||||||
position: fixed
|
position: fixed
|
||||||
bottom: 0
|
bottom: 0
|
||||||
|
@ -1837,13 +1833,13 @@ $sheet-padding: 16px
|
||||||
.oe_form_editable
|
.oe_form_editable
|
||||||
.oe_form
|
.oe_form
|
||||||
.oe_form_field_integer input
|
.oe_form_field_integer input
|
||||||
width: 6em !important
|
width: 6em
|
||||||
.oe_form_field_float input
|
.oe_form_field_float input
|
||||||
width: 7em !important
|
width: 7em
|
||||||
.oe_form_field_date input
|
.oe_form_field_date input
|
||||||
width: 7.5em !important
|
width: 7.5em
|
||||||
.oe_form_field_datetime input
|
.oe_form_field_datetime input
|
||||||
width: 11.5em !important
|
width: 11.5em
|
||||||
// }}}
|
// }}}
|
||||||
// FormView.fields_binary {{{
|
// FormView.fields_binary {{{
|
||||||
/* http://www.quirksmode.org/dom/inputfile.html
|
/* http://www.quirksmode.org/dom/inputfile.html
|
||||||
|
@ -1886,6 +1882,64 @@ $sheet-padding: 16px
|
||||||
@include box-sizing(border)
|
@include box-sizing(border)
|
||||||
&:hover .oe_form_field_image_controls
|
&:hover .oe_form_field_image_controls
|
||||||
display: block
|
display: block
|
||||||
|
.oe_fileupload
|
||||||
|
display: inline-block
|
||||||
|
clear: both
|
||||||
|
width: 100%
|
||||||
|
.oe_add
|
||||||
|
float: left
|
||||||
|
position: relative
|
||||||
|
width: 100%
|
||||||
|
left: +2px
|
||||||
|
top: +7px
|
||||||
|
button
|
||||||
|
display: inline
|
||||||
|
height: 24px
|
||||||
|
font-size: 12px
|
||||||
|
line-height: 12px
|
||||||
|
vertical-align: middle
|
||||||
|
button.oe_attach
|
||||||
|
width: 24px
|
||||||
|
overflow: hidden
|
||||||
|
width: 24px
|
||||||
|
overflow: hidden
|
||||||
|
background: transparent
|
||||||
|
color: #7C7BAD
|
||||||
|
box-shadow: none
|
||||||
|
border: none
|
||||||
|
text-shadow: none
|
||||||
|
.oe_e
|
||||||
|
position: relative
|
||||||
|
top: -1px
|
||||||
|
left: -9px
|
||||||
|
input.oe_form_binary_file
|
||||||
|
display: inline-block
|
||||||
|
margin-left: -5px
|
||||||
|
height: 28px
|
||||||
|
width: 52px
|
||||||
|
margin-top: -26px
|
||||||
|
.oe_attach_label
|
||||||
|
color: #7C7BAD
|
||||||
|
margin-left: -3px
|
||||||
|
.oe_attachments
|
||||||
|
margin-bottom: 4px
|
||||||
|
margin-right: 0px
|
||||||
|
font-size: 12px
|
||||||
|
border-radius: 2px
|
||||||
|
border: solid 1px rgba(124,123,173,0.14)
|
||||||
|
.oe_attachment
|
||||||
|
padding: 2px
|
||||||
|
padding-left: 4px
|
||||||
|
padding-right: 4px
|
||||||
|
.oe_e
|
||||||
|
font-size: 23px
|
||||||
|
margin-top: -5px
|
||||||
|
.oe_e:hover
|
||||||
|
text-decoration: none
|
||||||
|
.oe_attachment:nth-child(odd)
|
||||||
|
background: white
|
||||||
|
.oe_attachment:nth-child(even)
|
||||||
|
background: #F4F5FA
|
||||||
// }}}
|
// }}}
|
||||||
// FormView.many2one {{{
|
// FormView.many2one {{{
|
||||||
.oe_form_field_many2one
|
.oe_form_field_many2one
|
||||||
|
@ -1899,6 +1953,8 @@ $sheet-padding: 16px
|
||||||
line-height: 14px
|
line-height: 14px
|
||||||
float: right
|
float: right
|
||||||
padding-left: 2px
|
padding-left: 2px
|
||||||
|
input
|
||||||
|
padding-right: 13px
|
||||||
&.ui-autocomplete
|
&.ui-autocomplete
|
||||||
li.oe_m2o_dropdown_option a
|
li.oe_m2o_dropdown_option a
|
||||||
font-style: italic
|
font-style: italic
|
||||||
|
@ -2041,26 +2097,26 @@ $sheet-padding: 16px
|
||||||
.oe_m2o_drop_down_button
|
.oe_m2o_drop_down_button
|
||||||
top: 5px
|
top: 5px
|
||||||
.oe_m2o_cm_button
|
.oe_m2o_cm_button
|
||||||
display: none
|
line-height: 19px
|
||||||
|
.oe_input_icon
|
||||||
|
margin-top: 5px
|
||||||
.oe_form_field
|
.oe_form_field
|
||||||
|
min-width: 0
|
||||||
|
max-width: none
|
||||||
input, textarea
|
input, textarea
|
||||||
height: $row-height
|
height: $row-height
|
||||||
input, textarea
|
|
||||||
@include radius(0)
|
@include radius(0)
|
||||||
border: 1px solid #aaf
|
border: 1px solid #aaf
|
||||||
margin: 0
|
margin: 0
|
||||||
|
input, textarea, select
|
||||||
|
min-width: 0
|
||||||
&.oe_form_field_float,&.oe_form_view_integer
|
&.oe_form_field_float,&.oe_form_view_integer
|
||||||
input
|
input
|
||||||
text-align: right
|
text-align: right
|
||||||
width: 100% !important
|
width: 100% !important
|
||||||
&.oe_form_field_datetime,&.oe_form_field_date
|
&.oe_form_field_datetime,&.oe_form_field_date
|
||||||
> span
|
|
||||||
width: 100% !important
|
|
||||||
input.oe_datepicker_master
|
input.oe_datepicker_master
|
||||||
width: 100% !important
|
width: 100% !important
|
||||||
.oe_form_field_float,.oe_form_view_integer,&.oe_form_field_datetime,&.oe_form_field_date
|
|
||||||
min-width: 0 !important
|
|
||||||
max-width: none !important
|
|
||||||
.oe_list_group_name
|
.oe_list_group_name
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -2119,6 +2175,8 @@ $sheet-padding: 16px
|
||||||
td.oe_number
|
td.oe_number
|
||||||
text-align: right !important
|
text-align: right !important
|
||||||
max-width: 100px
|
max-width: 100px
|
||||||
|
td.oe_list_field_date, th.oe_list_header_date
|
||||||
|
min-width: 6em
|
||||||
> thead
|
> thead
|
||||||
border-bottom: 2px solid #cacaca
|
border-bottom: 2px solid #cacaca
|
||||||
background: #eee
|
background: #eee
|
||||||
|
@ -2168,6 +2226,8 @@ $sheet-padding: 16px
|
||||||
width: 17px
|
width: 17px
|
||||||
&:after
|
&:after
|
||||||
border-width: 0
|
border-width: 0
|
||||||
|
> td.oe_list_field_boolean input
|
||||||
|
@include opacity()
|
||||||
> tr:nth-child(odd)
|
> tr:nth-child(odd)
|
||||||
background-color: #f0f0fa
|
background-color: #f0f0fa
|
||||||
@include vertical-gradient(#f0f0fa, #eeeef6)
|
@include vertical-gradient(#f0f0fa, #eeeef6)
|
||||||
|
@ -2282,67 +2342,6 @@ $sheet-padding: 16px
|
||||||
float: right
|
float: right
|
||||||
color: #333
|
color: #333
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
.openerp
|
|
||||||
.oe_fileupload
|
|
||||||
display: inline-block
|
|
||||||
clear: both
|
|
||||||
width: 100%
|
|
||||||
.oe_add
|
|
||||||
float: left
|
|
||||||
position: relative
|
|
||||||
width: 100%
|
|
||||||
left: +2px
|
|
||||||
top: +7px
|
|
||||||
button
|
|
||||||
display: inline
|
|
||||||
height: 24px
|
|
||||||
font-size: 12px
|
|
||||||
line-height: 12px
|
|
||||||
vertical-align: middle
|
|
||||||
button.oe_attach
|
|
||||||
width: 24px
|
|
||||||
overflow: hidden
|
|
||||||
width: 24px
|
|
||||||
overflow: hidden
|
|
||||||
background: transparent
|
|
||||||
color: #7C7BAD
|
|
||||||
box-shadow: none
|
|
||||||
border: none
|
|
||||||
text-shadow: none
|
|
||||||
.oe_e
|
|
||||||
position: relative
|
|
||||||
top: -1px
|
|
||||||
left: -9px
|
|
||||||
input.oe_form_binary_file
|
|
||||||
display: inline-block
|
|
||||||
margin-left: -5px
|
|
||||||
height: 28px
|
|
||||||
width: 52px
|
|
||||||
margin-top: -26px
|
|
||||||
.oe_attach_label
|
|
||||||
color: #7C7BAD
|
|
||||||
margin-left: -3px
|
|
||||||
.oe_attachments
|
|
||||||
margin-bottom: 4px
|
|
||||||
margin-right: 0px
|
|
||||||
font-size: 12px
|
|
||||||
border-radius: 2px
|
|
||||||
border: solid 1px rgba(124,123,173,0.14)
|
|
||||||
.oe_attachment
|
|
||||||
padding: 2px
|
|
||||||
padding-left: 4px
|
|
||||||
padding-right: 4px
|
|
||||||
.oe_e
|
|
||||||
font-size: 23px
|
|
||||||
margin-top: -5px
|
|
||||||
.oe_e:hover
|
|
||||||
text-decoration: none
|
|
||||||
.oe_attachment:nth-child(odd)
|
|
||||||
background: white
|
|
||||||
.oe_attachment:nth-child(even)
|
|
||||||
background: #F4F5FA
|
|
||||||
|
|
||||||
// Kitten Mode {{{
|
// Kitten Mode {{{
|
||||||
.kitten-mode-activated
|
.kitten-mode-activated
|
||||||
background-image: url(http://placekitten.com/g/1365/769)
|
background-image: url(http://placekitten.com/g/1365/769)
|
||||||
|
@ -2352,11 +2351,13 @@ $sheet-padding: 16px
|
||||||
opacity: 0.70
|
opacity: 0.70
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
// jQueryUI top level {{{
|
||||||
// The jQuery-ui overlay and Autocomplete are outside the .openerp div, please don't add indentation !!!
|
// The jQuery-ui overlay and Autocomplete are outside the .openerp div, please don't add indentation !!!
|
||||||
div.ui-widget-overlay
|
div.ui-widget-overlay
|
||||||
background: black
|
background: black
|
||||||
@include opacity(0.3)
|
@include opacity(0.3)
|
||||||
|
// TODO: I think only the overlay is problematic, the other top level widgets should use $.fn.openerpClass()
|
||||||
|
// eg: $el.autocomplete().openerpClass();
|
||||||
.ui-widget
|
.ui-widget
|
||||||
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
|
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
|
||||||
color: #4c4c4c
|
color: #4c4c4c
|
||||||
|
@ -2383,11 +2384,14 @@ div.ui-widget-overlay
|
||||||
|
|
||||||
.ui-corner-all
|
.ui-corner-all
|
||||||
@include radius(3px)
|
@include radius(3px)
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// @media print {{{
|
||||||
@media print
|
@media print
|
||||||
.openerp
|
.openerp
|
||||||
.oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default
|
.oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default, .oe_topbar, .oe_leftbar, .oe_loading
|
||||||
display: none
|
// We use !important here because jQuery adds @style = display: block on elements when using $.fn.show()
|
||||||
|
display: none !important
|
||||||
.oe_list_content
|
.oe_list_content
|
||||||
button, input[type=checkbox]
|
button, input[type=checkbox]
|
||||||
visibility: hidden
|
visibility: hidden
|
||||||
|
@ -2413,5 +2417,7 @@ div.ui-widget-overlay
|
||||||
background: none
|
background: none
|
||||||
.openerp div.oe_mail_wall
|
.openerp div.oe_mail_wall
|
||||||
overflow: hidden !important
|
overflow: hidden !important
|
||||||
|
// }}}
|
||||||
|
|
||||||
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
|
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
|
||||||
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
|
||||||
|
|
|
@ -19,12 +19,14 @@
|
||||||
/**
|
/**
|
||||||
* OpenERP instance constructor
|
* OpenERP instance constructor
|
||||||
*
|
*
|
||||||
* @param {Array} modules list of modules to initialize
|
* @param {Array|String} modules list of modules to initialize
|
||||||
*/
|
*/
|
||||||
init: function(modules) {
|
init: function(modules) {
|
||||||
// By default only web will be loaded, the rest will be by loaded
|
if (modules === "fuck your shit, don't load anything you cunt") {
|
||||||
// by openerp.web.Session on the first session_authenticate
|
modules = [];
|
||||||
|
} else {
|
||||||
modules = _.union(['web'], modules || []);
|
modules = _.union(['web'], modules || []);
|
||||||
|
}
|
||||||
var new_instance = {
|
var new_instance = {
|
||||||
// links to the global openerp
|
// links to the global openerp
|
||||||
_openerp: openerp,
|
_openerp: openerp,
|
||||||
|
|
|
@ -48,7 +48,7 @@ instance.web.Notification = instance.web.Widget.extend({
|
||||||
*/
|
*/
|
||||||
instance.web.dialog = function(element) {
|
instance.web.dialog = function(element) {
|
||||||
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
|
var result = element.dialog.apply(element, _.rest(_.toArray(arguments)));
|
||||||
result.dialog("widget").addClass("openerp");
|
result.dialog("widget").openerpClass();
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -190,7 +190,14 @@ instance.web.Dialog = instance.web.Widget.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
instance.web.CrashManager = instance.web.Class.extend({
|
instance.web.CrashManager = instance.web.Class.extend({
|
||||||
|
init: function() {
|
||||||
|
this.active = true;
|
||||||
|
},
|
||||||
|
|
||||||
rpc_error: function(error) {
|
rpc_error: function(error) {
|
||||||
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (error.data.fault_code) {
|
if (error.data.fault_code) {
|
||||||
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
|
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
|
||||||
if (split.length > 1) {
|
if (split.length > 1) {
|
||||||
|
@ -205,6 +212,9 @@ instance.web.CrashManager = instance.web.Class.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
show_warning: function(error) {
|
show_warning: function(error) {
|
||||||
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
|
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
|
||||||
title: "OpenERP " + _.str.capitalize(error.type),
|
title: "OpenERP " + _.str.capitalize(error.type),
|
||||||
buttons: [
|
buttons: [
|
||||||
|
@ -213,7 +223,9 @@ instance.web.CrashManager = instance.web.Class.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
show_error: function(error) {
|
show_error: function(error) {
|
||||||
var self = this;
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
var buttons = {};
|
var buttons = {};
|
||||||
buttons[_t("Ok")] = function() {
|
buttons[_t("Ok")] = function() {
|
||||||
$(this).dialog("close");
|
$(this).dialog("close");
|
||||||
|
@ -310,7 +322,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
||||||
self.db_list = null;
|
self.db_list = null;
|
||||||
});
|
});
|
||||||
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).done(function(result) {
|
var fetch_langs = this.rpc("/web/session/get_lang_list", {}).done(function(result) {
|
||||||
self.lang_list = result.lang_list;
|
self.lang_list = result;
|
||||||
});
|
});
|
||||||
return $.when(fetch_db, fetch_langs).done(self.do_render);
|
return $.when(fetch_db, fetch_langs).done(self.do_render);
|
||||||
},
|
},
|
||||||
|
@ -333,6 +345,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
||||||
self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label");
|
self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label");
|
||||||
self.$el.find("label").addClass("oe_form_label");
|
self.$el.find("label").addClass("oe_form_label");
|
||||||
self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
|
self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
|
||||||
|
self.$el.find("form[name=duplicate_db_form]").validate({ submitHandler: self.do_duplicate });
|
||||||
self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
|
self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
|
||||||
self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
|
self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
|
||||||
self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
|
self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
|
||||||
|
@ -418,6 +431,18 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
||||||
self.do_action(client_action);
|
self.do_action(client_action);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
do_duplicate: function(form) {
|
||||||
|
var self = this;
|
||||||
|
var fields = $(form).serializeArray();
|
||||||
|
self.rpc("/web/database/duplicate", {'fields': fields}).then(function(result) {
|
||||||
|
if (result.error) {
|
||||||
|
self.display_error(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.do_notify("Duplicating database", "The database has been duplicated.");
|
||||||
|
self.start();
|
||||||
|
});
|
||||||
|
},
|
||||||
do_drop: function(form) {
|
do_drop: function(form) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $form = $(form),
|
var $form = $(form),
|
||||||
|
@ -518,6 +543,9 @@ instance.web.Login = instance.web.Widget.extend({
|
||||||
this.selected_db = null;
|
this.selected_db = null;
|
||||||
this.selected_login = null;
|
this.selected_login = null;
|
||||||
this.params = action.params || {};
|
this.params = action.params || {};
|
||||||
|
if (_.isEmpty(this.params)) {
|
||||||
|
this.params = $.bbq.getState(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.params.login_successful) {
|
if (this.params.login_successful) {
|
||||||
this.on('login_successful', this, this.params.login_successful);
|
this.on('login_successful', this, this.params.login_successful);
|
||||||
|
@ -619,9 +647,35 @@ instance.web.Login = instance.web.Widget.extend({
|
||||||
});
|
});
|
||||||
instance.web.client_actions.add("login", "instance.web.Login");
|
instance.web.client_actions.add("login", "instance.web.Login");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to url by replacing window.location
|
||||||
|
* If wait is true, sleep 1s and wait for the server i.e. after a restart.
|
||||||
|
*/
|
||||||
|
instance.web.redirect = function(url, wait) {
|
||||||
|
// Dont display a dialog if some xmlhttprequest are in progress
|
||||||
|
if (instance.client && instance.client.crashmanager) {
|
||||||
|
instance.client.crashmanager.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wait_server = function() {
|
||||||
|
instance.session.rpc("/web/webclient/version_info", {}).done(function() {
|
||||||
|
window.location = url;
|
||||||
|
}).fail(function() {
|
||||||
|
setTimeout(wait_server, 250);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
setTimeout(wait_server, 1000);
|
||||||
|
} else {
|
||||||
|
window.location = url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client action to reload the whole interface.
|
* Client action to reload the whole interface.
|
||||||
* If params has an entry 'menu_id', it opens the given menu entry.
|
* If params.menu_id, it opens the given menu entry.
|
||||||
|
* If params.wait, reload will wait the openerp server to be reachable before reloading
|
||||||
*/
|
*/
|
||||||
instance.web.Reload = function(parent, action) {
|
instance.web.Reload = function(parent, action) {
|
||||||
var params = action.params || {};
|
var params = action.params || {};
|
||||||
|
@ -637,8 +691,8 @@ instance.web.Reload = function(parent, action) {
|
||||||
hash = "#menu_id=" + menu_id;
|
hash = "#menu_id=" + menu_id;
|
||||||
}
|
}
|
||||||
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
|
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
|
||||||
window.onerror = function() {};
|
|
||||||
window.location = url;
|
instance.web.redirect(url, params.wait);
|
||||||
};
|
};
|
||||||
instance.web.client_actions.add("reload", "instance.web.Reload");
|
instance.web.client_actions.add("reload", "instance.web.Reload");
|
||||||
|
|
||||||
|
@ -648,7 +702,7 @@ instance.web.client_actions.add("reload", "instance.web.Reload");
|
||||||
*/
|
*/
|
||||||
instance.web.HistoryBack = function(parent) {
|
instance.web.HistoryBack = function(parent) {
|
||||||
if (!parent.history_back()) {
|
if (!parent.history_back()) {
|
||||||
window.location = '/' + (window.location.search || '');
|
instance.web.Home(parent);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
|
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
|
||||||
|
@ -656,11 +710,10 @@ instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
|
||||||
/**
|
/**
|
||||||
* Client action to go back home.
|
* Client action to go back home.
|
||||||
*/
|
*/
|
||||||
instance.web.Home = instance.web.Widget.extend({
|
instance.web.Home = function(parent, action) {
|
||||||
init: function(parent) {
|
var url = '/' + (window.location.search || '');
|
||||||
window.location = '/' + (window.location.search || '');
|
instance.web.redirect(url, action.params && action.params.wait);
|
||||||
}
|
};
|
||||||
});
|
|
||||||
instance.web.client_actions.add("home", "instance.web.Home");
|
instance.web.client_actions.add("home", "instance.web.Home");
|
||||||
|
|
||||||
instance.web.ChangePassword = instance.web.Widget.extend({
|
instance.web.ChangePassword = instance.web.Widget.extend({
|
||||||
|
@ -887,7 +940,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
||||||
if(res.company_id[0] > 1)
|
if(res.company_id[0] > 1)
|
||||||
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
|
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
|
||||||
self.$el.find('.oe_topbar_name').text(topbar_name);
|
self.$el.find('.oe_topbar_name').text(topbar_name);
|
||||||
var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=image_small&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
|
var avatar_src = self.session.url('/web/binary/image', {model:'res.users', field: 'image_small', id: self.session.uid});
|
||||||
$avatar.attr('src', avatar_src);
|
$avatar.attr('src', avatar_src);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -899,7 +952,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
||||||
on_menu_settings: function() {
|
on_menu_settings: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!this.getParent().has_uncommitted_changes()) {
|
if (!this.getParent().has_uncommitted_changes()) {
|
||||||
self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) {
|
self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }).done(function(result) {
|
||||||
result.res_id = instance.session.uid;
|
result.res_id = instance.session.uid;
|
||||||
self.getParent().action_manager.do_action(result);
|
self.getParent().action_manager.do_action(result);
|
||||||
});
|
});
|
||||||
|
@ -930,6 +983,7 @@ instance.web.Client = instance.web.Widget.extend({
|
||||||
return instance.session.session_bind(this.origin).then(function() {
|
return instance.session.session_bind(this.origin).then(function() {
|
||||||
var $e = $(QWeb.render(self._template, {}));
|
var $e = $(QWeb.render(self._template, {}));
|
||||||
self.replaceElement($e);
|
self.replaceElement($e);
|
||||||
|
$e.openerpClass();
|
||||||
self.bind_events();
|
self.bind_events();
|
||||||
return self.show_common();
|
return self.show_common();
|
||||||
});
|
});
|
||||||
|
@ -1106,10 +1160,10 @@ instance.web.WebClient = instance.web.Client.extend({
|
||||||
var self = this;
|
var self = this;
|
||||||
var state = event.getState(true);
|
var state = event.getState(true);
|
||||||
if (!_.isEqual(this._current_state, state)) {
|
if (!_.isEqual(this._current_state, state)) {
|
||||||
if(state.action_id === undefined && state.menu_id) {
|
if(!state.action && state.menu_id) {
|
||||||
self.menu.has_been_loaded.done(function() {
|
self.menu.has_been_loaded.done(function() {
|
||||||
self.menu.do_reload().done(function() {
|
self.menu.do_reload().done(function() {
|
||||||
self.menu.menu_click(state.menu_id)
|
self.menu.menu_click(state.menu_id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -774,10 +774,10 @@ instance.web.Widget = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
rpc: function(url, data, success, error) {
|
rpc: function(url, data, options) {
|
||||||
var def = $.Deferred().done(success).fail(error);
|
var def = $.Deferred();
|
||||||
var self = this;
|
var self = this;
|
||||||
instance.session.rpc(url, data).done(function() {
|
instance.session.rpc(url, data, options).done(function() {
|
||||||
if (!self.isDestroyed())
|
if (!self.isDestroyed())
|
||||||
def.resolve.apply(def, arguments);
|
def.resolve.apply(def, arguments);
|
||||||
}).fail(function() {
|
}).fail(function() {
|
||||||
|
@ -1231,12 +1231,14 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
*
|
*
|
||||||
* @param {String} url RPC endpoint
|
* @param {String} url RPC endpoint
|
||||||
* @param {Object} params call parameters
|
* @param {Object} params call parameters
|
||||||
|
* @param {Object} options additional options for rpc call
|
||||||
* @param {Function} success_callback function to execute on RPC call success
|
* @param {Function} success_callback function to execute on RPC call success
|
||||||
* @param {Function} error_callback function to execute on RPC call failure
|
* @param {Function} error_callback function to execute on RPC call failure
|
||||||
* @returns {jQuery.Deferred} jquery-provided ajax deferred
|
* @returns {jQuery.Deferred} jquery-provided ajax deferred
|
||||||
*/
|
*/
|
||||||
rpc: function(url, params) {
|
rpc: function(url, params, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
options = options || {};
|
||||||
// url can be an $.ajax option object
|
// url can be an $.ajax option object
|
||||||
if (_.isString(url)) {
|
if (_.isString(url)) {
|
||||||
url = { url: url };
|
url = { url: url };
|
||||||
|
@ -1251,9 +1253,11 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
id: _.uniqueId('r')
|
id: _.uniqueId('r')
|
||||||
};
|
};
|
||||||
var deferred = $.Deferred();
|
var deferred = $.Deferred();
|
||||||
|
if (! options.shadow)
|
||||||
this.trigger('request', url, payload);
|
this.trigger('request', url, payload);
|
||||||
var request = this.rpc_function(url, payload).done(
|
var request = this.rpc_function(url, payload).done(
|
||||||
function (response, textStatus, jqXHR) {
|
function (response, textStatus, jqXHR) {
|
||||||
|
if (! options.shadow)
|
||||||
self.trigger('response', response);
|
self.trigger('response', response);
|
||||||
if (!response.error) {
|
if (!response.error) {
|
||||||
if (url.url === '/web/session/eval_domain_and_context') {
|
if (url.url === '/web/session/eval_domain_and_context') {
|
||||||
|
@ -1268,6 +1272,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
}
|
}
|
||||||
).fail(
|
).fail(
|
||||||
function(jqXHR, textStatus, errorThrown) {
|
function(jqXHR, textStatus, errorThrown) {
|
||||||
|
if (! options.shadow)
|
||||||
self.trigger('response_failed', jqXHR);
|
self.trigger('response_failed', jqXHR);
|
||||||
var error = {
|
var error = {
|
||||||
code: -32098,
|
code: -32098,
|
||||||
|
@ -1276,7 +1281,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
};
|
};
|
||||||
deferred.reject(error, $.Event());
|
deferred.reject(error, $.Event());
|
||||||
});
|
});
|
||||||
// Allow deferred user to disable on_rpc_error in fail
|
// Allow deferred user to disable rpc_error call in fail
|
||||||
deferred.fail(function() {
|
deferred.fail(function() {
|
||||||
deferred.fail(function(error, event) {
|
deferred.fail(function(error, event) {
|
||||||
if (!event.isDefaultPrevented()) {
|
if (!event.isDefaultPrevented()) {
|
||||||
|
@ -1309,9 +1314,18 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
// extracted from payload to set on the url
|
// extracted from payload to set on the url
|
||||||
var data = {
|
var data = {
|
||||||
session_id: this.session_id,
|
session_id: this.session_id,
|
||||||
id: payload.id
|
id: payload.id,
|
||||||
|
sid: this.httpsessionid,
|
||||||
};
|
};
|
||||||
url.url = this.get_url(url.url);
|
|
||||||
|
var set_sid = function (response, textStatus, jqXHR) {
|
||||||
|
// If response give us the http session id, we store it for next requests...
|
||||||
|
if (response.httpsessionid) {
|
||||||
|
self.httpsessionid = response.httpsessionid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
url.url = this.url(url.url, null);
|
||||||
var ajax = _.extend({
|
var ajax = _.extend({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
dataType: 'jsonp',
|
dataType: 'jsonp',
|
||||||
|
@ -1326,7 +1340,7 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
if(payload_url.length < 2000) {
|
if(payload_url.length < 2000) {
|
||||||
// Direct jsonp request
|
// Direct jsonp request
|
||||||
ajax.data.r = payload_str;
|
ajax.data.r = payload_str;
|
||||||
return $.ajax(ajax);
|
return $.ajax(ajax).done(set_sid);
|
||||||
} else {
|
} else {
|
||||||
// Indirect jsonp request
|
// Indirect jsonp request
|
||||||
var ifid = _.uniqueId('oe_rpc_iframe');
|
var ifid = _.uniqueId('oe_rpc_iframe');
|
||||||
|
@ -1364,11 +1378,20 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
});
|
});
|
||||||
// append the iframe to the DOM (will trigger the first load)
|
// append the iframe to the DOM (will trigger the first load)
|
||||||
$form.after($iframe);
|
$form.after($iframe);
|
||||||
return deferred;
|
return deferred.done(set_sid);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get_url: function (file) {
|
|
||||||
return this.prefix + file;
|
url: function(path, params) {
|
||||||
|
var qs = '';
|
||||||
|
if (!_.isNull(params)) {
|
||||||
|
params = _.extend(params || {}, {session_id: this.session_id});
|
||||||
|
if (this.httpsessionid) {
|
||||||
|
params.sid = this.httpsessionid;
|
||||||
|
}
|
||||||
|
qs = '?' + $.param(params);
|
||||||
|
}
|
||||||
|
return this.prefix + path + qs;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,13 @@
|
||||||
*--------------------------------------------------------*/
|
*--------------------------------------------------------*/
|
||||||
var console;
|
var console;
|
||||||
if (!console) {
|
if (!console) {
|
||||||
console = {log: function () {}};
|
// Even IE9 only exposes console object if debug window opened
|
||||||
}
|
console = {};
|
||||||
if (!console.debug) {
|
('log error debug info warn assert clear dir dirxml trace group'
|
||||||
console.debug = console.log;
|
+ ' groupCollapsed groupEnd time timeEnd profile profileEnd count'
|
||||||
|
+ ' exception').split(/\s+/).forEach(function(property) {
|
||||||
|
console[property] = _.identity;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openerp.web.coresetup = function(instance) {
|
openerp.web.coresetup = function(instance) {
|
||||||
|
@ -19,9 +22,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
|
||||||
this.name = instance._session_id;
|
this.name = instance._session_id;
|
||||||
this.qweb_mutex = new $.Mutex();
|
this.qweb_mutex = new $.Mutex();
|
||||||
},
|
},
|
||||||
rpc: function(url, params) {
|
rpc: function(url, params, options) {
|
||||||
params.session_id = this.session_id;
|
params.session_id = this.session_id;
|
||||||
return this._super(url, params);
|
return this._super(url, params, options);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Setup a sessionm
|
* Setup a sessionm
|
||||||
|
@ -198,7 +201,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
|
||||||
var self = this;
|
var self = this;
|
||||||
_.each(files, function (file) {
|
_.each(files, function (file) {
|
||||||
$('head').append($('<link>', {
|
$('head').append($('<link>', {
|
||||||
'href': self.get_url(file),
|
'href': self.url(file, null),
|
||||||
'rel': 'stylesheet',
|
'rel': 'stylesheet',
|
||||||
'type': 'text/css'
|
'type': 'text/css'
|
||||||
}));
|
}));
|
||||||
|
@ -207,11 +210,11 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
|
||||||
load_js: function(files) {
|
load_js: function(files) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var d = $.Deferred();
|
var d = $.Deferred();
|
||||||
if(files.length != 0) {
|
if(files.length !== 0) {
|
||||||
var file = files.shift();
|
var file = files.shift();
|
||||||
var tag = document.createElement('script');
|
var tag = document.createElement('script');
|
||||||
tag.type = 'text/javascript';
|
tag.type = 'text/javascript';
|
||||||
tag.src = self.get_url(file);
|
tag.src = self.url(file, null);
|
||||||
tag.onload = tag.onreadystatechange = function() {
|
tag.onload = tag.onreadystatechange = function() {
|
||||||
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
|
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
|
||||||
return;
|
return;
|
||||||
|
@ -457,6 +460,16 @@ $.fn.getAttributes = function() {
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
$.fn.openerpClass = function(additionalClass) {
|
||||||
|
// This plugin should be applied on top level elements
|
||||||
|
additionalClass = additionalClass || '';
|
||||||
|
if (!!$.browser.msie) {
|
||||||
|
additionalClass += ' openerp_ie';
|
||||||
|
}
|
||||||
|
return this.each(function() {
|
||||||
|
$(this).addClass('openerp ' + additionalClass);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/** Jquery extentions */
|
/** Jquery extentions */
|
||||||
$.Mutex = (function() {
|
$.Mutex = (function() {
|
||||||
|
|
|
@ -278,9 +278,10 @@ instance.web.Model = instance.web.Class.extend({
|
||||||
* @param {String} method name of the method to call
|
* @param {String} method name of the method to call
|
||||||
* @param {Array} [args] positional arguments
|
* @param {Array} [args] positional arguments
|
||||||
* @param {Object} [kwargs] keyword arguments
|
* @param {Object} [kwargs] keyword arguments
|
||||||
|
* @param {Object} [options] additional options for the rpc() method
|
||||||
* @returns {jQuery.Deferred<>} call result
|
* @returns {jQuery.Deferred<>} call result
|
||||||
*/
|
*/
|
||||||
call: function (method, args, kwargs) {
|
call: function (method, args, kwargs, options) {
|
||||||
args = args || [];
|
args = args || [];
|
||||||
kwargs = kwargs || {};
|
kwargs = kwargs || {};
|
||||||
if (!_.isArray(args)) {
|
if (!_.isArray(args)) {
|
||||||
|
@ -294,7 +295,7 @@ instance.web.Model = instance.web.Class.extend({
|
||||||
method: method,
|
method: method,
|
||||||
args: args,
|
args: args,
|
||||||
kwargs: kwargs
|
kwargs: kwargs
|
||||||
});
|
}, options);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Fetches a Query instance bound to this model, for searching
|
* Fetches a Query instance bound to this model, for searching
|
||||||
|
@ -721,10 +722,10 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
||||||
this.last_default_get = {};
|
this.last_default_get = {};
|
||||||
},
|
},
|
||||||
default_get: function(fields, options) {
|
default_get: function(fields, options) {
|
||||||
return this._super(fields, options).done(this.on_default_get);
|
var self = this;
|
||||||
},
|
return this._super(fields, options).done(function(res) {
|
||||||
on_default_get: function(res) {
|
self.last_default_get = res;
|
||||||
this.last_default_get = res;
|
});
|
||||||
},
|
},
|
||||||
create: function(data) {
|
create: function(data) {
|
||||||
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
|
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
|
||||||
|
|
|
@ -83,6 +83,42 @@ instance.web.strip_raw_chars = function (value) {
|
||||||
var normalize_format = function (format) {
|
var normalize_format = function (format) {
|
||||||
return Date.normalizeFormat(instance.web.strip_raw_chars(format));
|
return Date.normalizeFormat(instance.web.strip_raw_chars(format));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check with a scary heuristic if the value is a bin_size or not.
|
||||||
|
* If not, compute an approximate size out of the base64 encoded string.
|
||||||
|
*
|
||||||
|
* @param {String} value original format
|
||||||
|
*/
|
||||||
|
instance.web.binary_to_binsize = function (value) {
|
||||||
|
if (!value) {
|
||||||
|
return instance.web.human_size(0);
|
||||||
|
}
|
||||||
|
if (value.substr(0, 10).indexOf(' ') == -1) {
|
||||||
|
// Computing approximate size out of base64 encoded string
|
||||||
|
// http://en.wikipedia.org/wiki/Base64#MIME
|
||||||
|
return instance.web.human_size(value.length / 1.37);
|
||||||
|
} else {
|
||||||
|
// already bin_size
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human readable size
|
||||||
|
*
|
||||||
|
* @param {Number} numner of bytes
|
||||||
|
*/
|
||||||
|
instance.web.human_size = function(size) {
|
||||||
|
var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(',');
|
||||||
|
var i = 0;
|
||||||
|
while (size >= 1024) {
|
||||||
|
size /= 1024;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return size.toFixed(2) + ' ' + units[i];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a single atomic value based on a field descriptor
|
* Formats a single atomic value based on a field descriptor
|
||||||
*
|
*
|
||||||
|
|
|
@ -349,7 +349,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
$.when(load_view)
|
$.when(load_view)
|
||||||
.then(function(r) {
|
.then(function(r) {
|
||||||
self.search_view_loaded(r)
|
self.search_view_loaded(r)
|
||||||
}).fail(function () {
|
}, function () {
|
||||||
self.ready.reject.apply(null, arguments);
|
self.ready.reject.apply(null, arguments);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -459,7 +459,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
complete_global_search: function (req, resp) {
|
complete_global_search: function (req, resp) {
|
||||||
$.when.apply(null, _(this.inputs).chain()
|
$.when.apply(null, _(this.inputs).chain()
|
||||||
.invoke('complete', req.term)
|
.invoke('complete', req.term)
|
||||||
.value()).done(function () {
|
.value()).then(function () {
|
||||||
resp(_(_(arguments).compact()).flatten(true));
|
resp(_(_(arguments).compact()).flatten(true));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -527,7 +527,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
childView.on('blurred', self, self.proxy('childBlurred'));
|
childView.on('blurred', self, self.proxy('childBlurred'));
|
||||||
});
|
});
|
||||||
|
|
||||||
$.when.apply(null, started).done(function () {
|
$.when.apply(null, started).then(function () {
|
||||||
var input_to_focus;
|
var input_to_focus;
|
||||||
// options.at: facet inserted at given index, focus next input
|
// options.at: facet inserted at given index, focus next input
|
||||||
// otherwise just focus last input
|
// otherwise just focus last input
|
||||||
|
@ -608,7 +608,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
// add Filters to this.inputs, need view.controls filled
|
// add Filters to this.inputs, need view.controls filled
|
||||||
(new instance.web.search.Filters(this));
|
(new instance.web.search.Filters(this));
|
||||||
// add custom filters to this.inputs
|
// add custom filters to this.inputs
|
||||||
(new instance.web.search.CustomFilters(this));
|
this.custom_filters = new instance.web.search.CustomFilters(this);
|
||||||
// add Advanced to this.inputs
|
// add Advanced to this.inputs
|
||||||
(new instance.web.search.Advanced(this));
|
(new instance.web.search.Advanced(this));
|
||||||
},
|
},
|
||||||
|
@ -635,9 +635,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
|
|
||||||
// load defaults
|
// load defaults
|
||||||
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
|
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
|
||||||
'facet_for_defaults', this.defaults)).done(function () {
|
'facet_for_defaults', this.defaults))
|
||||||
self.query.reset(_(arguments).compact(), {preventSearch: true});
|
.then(this.proxy('setup_default_query'));
|
||||||
});
|
|
||||||
|
|
||||||
return $.when(drawer_started, defaults_fetched)
|
return $.when(drawer_started, defaults_fetched)
|
||||||
.done(function () {
|
.done(function () {
|
||||||
|
@ -645,6 +644,32 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
||||||
self.ready.resolve();
|
self.ready.resolve();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
setup_default_query: function () {
|
||||||
|
// Hacky implementation of CustomFilters#facet_for_defaults ensure
|
||||||
|
// CustomFilters will be ready (and CustomFilters#filters will be
|
||||||
|
// correctly filled) by the time this method executes.
|
||||||
|
var custom_filters = this.custom_filters.filters;
|
||||||
|
if (!_(custom_filters).isEmpty()) {
|
||||||
|
// Check for any is_default custom filter
|
||||||
|
var personal_filter = _(custom_filters).find(function (filter) {
|
||||||
|
return filter.user_id && filter.is_default;
|
||||||
|
});
|
||||||
|
if (personal_filter) {
|
||||||
|
this.custom_filters.enable_filter(personal_filter, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var global_filter = _(custom_filters).find(function (filter) {
|
||||||
|
return !filter.user_id && filter.is_default;
|
||||||
|
});
|
||||||
|
if (global_filter) {
|
||||||
|
this.custom_filters.enable_filter(global_filter, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No custom filter, or no is_default custom filter, apply view defaults
|
||||||
|
this.query.reset(_(arguments).compact(), {preventSearch: true});
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Extract search data from the view's facets.
|
* Extract search data from the view's facets.
|
||||||
*
|
*
|
||||||
|
@ -1460,10 +1485,15 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
|
||||||
instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
||||||
template: 'SearchView.CustomFilters',
|
template: 'SearchView.CustomFilters',
|
||||||
_in_drawer: true,
|
_in_drawer: true,
|
||||||
|
init: function () {
|
||||||
|
this.is_ready = $.Deferred();
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
start: function () {
|
start: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.model = new instance.web.Model('ir.filters');
|
this.model = new instance.web.Model('ir.filters');
|
||||||
this.filters = {};
|
this.filters = {};
|
||||||
|
this.$filters = {};
|
||||||
this.view.query
|
this.view.query
|
||||||
.on('remove', function (facet) {
|
.on('remove', function (facet) {
|
||||||
if (!facet.get('is_custom_filter')) {
|
if (!facet.get('is_custom_filter')) {
|
||||||
|
@ -1479,38 +1509,49 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
||||||
// FIXME: local eval of domain and context to get rid of special endpoint
|
// FIXME: local eval of domain and context to get rid of special endpoint
|
||||||
return this.rpc('/web/searchview/get_filters', {
|
return this.rpc('/web/searchview/get_filters', {
|
||||||
model: this.view.model
|
model: this.view.model
|
||||||
}).then(this.proxy('set_filters'));
|
|
||||||
},
|
|
||||||
clear_selection: function () {
|
|
||||||
this.$('li.oe_selected').removeClass('oe_selected');
|
|
||||||
},
|
|
||||||
append_filter: function (filter) {
|
|
||||||
var self = this;
|
|
||||||
var key = _.str.sprintf('(%s)%s', filter.user_id, filter.name);
|
|
||||||
|
|
||||||
var $filter;
|
|
||||||
if (key in this.filters) {
|
|
||||||
$filter = this.filters[key];
|
|
||||||
} else {
|
|
||||||
var id = filter.id;
|
|
||||||
$filter = this.filters[key] = $('<li></li>')
|
|
||||||
.appendTo(this.$('.oe_searchview_custom_list'))
|
|
||||||
.addClass(filter.user_id ? 'oe_searchview_custom_private'
|
|
||||||
: 'oe_searchview_custom_public')
|
|
||||||
.text(filter.name);
|
|
||||||
|
|
||||||
$('<a class="oe_searchview_custom_delete">x</a>')
|
|
||||||
.click(function (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
self.model.call('unlink', [id]).done(function () {
|
|
||||||
$filter.remove();
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.appendTo($filter);
|
.then(this.proxy('set_filters'))
|
||||||
}
|
.then(function () {
|
||||||
|
self.is_ready.resolve(null);
|
||||||
$filter.unbind('click').click(function () {
|
}, function () {
|
||||||
self.view.query.reset([{
|
self.is_ready.reject();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Special implementation delaying defaults until CustomFilters is loaded
|
||||||
|
*/
|
||||||
|
facet_for_defaults: function () {
|
||||||
|
return this.is_ready;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates a mapping key (in the filters and $filter mappings) for the
|
||||||
|
* filter descriptor object provided (as returned by ``get_filters``).
|
||||||
|
*
|
||||||
|
* The mapping key is guaranteed to be unique for a given (user_id, name)
|
||||||
|
* pair.
|
||||||
|
*
|
||||||
|
* @param {Object} filter
|
||||||
|
* @param {String} filter.name
|
||||||
|
* @param {Number|Pair<Number, String>} [filter.user_id]
|
||||||
|
* @return {String} mapping key corresponding to the filter
|
||||||
|
*/
|
||||||
|
key_for: function (filter) {
|
||||||
|
var user_id = filter.user_id;
|
||||||
|
var uid = (user_id instanceof Array) ? user_id[0] : user_id;
|
||||||
|
return _.str.sprintf('(%s)%s', uid, filter.name);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Generates a :js:class:`~instance.web.search.Facet` descriptor from a
|
||||||
|
* filter descriptor
|
||||||
|
*
|
||||||
|
* @param {Object} filter
|
||||||
|
* @param {String} filter.name
|
||||||
|
* @param {Object} [filter.context]
|
||||||
|
* @param {Array} [filter.domain]
|
||||||
|
* @return {Object}
|
||||||
|
*/
|
||||||
|
facet_for: function (filter) {
|
||||||
|
return {
|
||||||
category: _t("Custom Filter"),
|
category: _t("Custom Filter"),
|
||||||
icon: 'M',
|
icon: 'M',
|
||||||
field: {
|
field: {
|
||||||
|
@ -1520,9 +1561,48 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
||||||
},
|
},
|
||||||
is_custom_filter: true,
|
is_custom_filter: true,
|
||||||
values: [{label: filter.name, value: null}]
|
values: [{label: filter.name, value: null}]
|
||||||
}]);
|
};
|
||||||
$filter.addClass('oe_selected');
|
},
|
||||||
|
clear_selection: function () {
|
||||||
|
this.$('li.oe_selected').removeClass('oe_selected');
|
||||||
|
},
|
||||||
|
append_filter: function (filter) {
|
||||||
|
var self = this;
|
||||||
|
var key = this.key_for(filter);
|
||||||
|
|
||||||
|
var $filter;
|
||||||
|
if (key in this.$filters) {
|
||||||
|
$filter = this.$filters[key];
|
||||||
|
} else {
|
||||||
|
var id = filter.id;
|
||||||
|
this.filters[key] = filter;
|
||||||
|
$filter = this.$filters[key] = $('<li></li>')
|
||||||
|
.appendTo(this.$('.oe_searchview_custom_list'))
|
||||||
|
.addClass(filter.user_id ? 'oe_searchview_custom_private'
|
||||||
|
: 'oe_searchview_custom_public')
|
||||||
|
.toggleClass('oe_searchview_custom_default', filter.is_default)
|
||||||
|
.text(filter.name);
|
||||||
|
|
||||||
|
$('<a class="oe_searchview_custom_delete">x</a>')
|
||||||
|
.click(function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
self.model.call('unlink', [id]).done(function () {
|
||||||
|
$filter.remove();
|
||||||
|
delete self.$filters[key];
|
||||||
|
delete self.filters[key];
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.appendTo($filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter.unbind('click').click(function () {
|
||||||
|
self.enable_filter(filter);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
enable_filter: function (filter, preventSearch) {
|
||||||
|
this.view.query.reset([this.facet_for(filter)], {
|
||||||
|
preventSearch: preventSearch || false});
|
||||||
|
this.$filters[this.key_for(filter)].addClass('oe_selected');
|
||||||
},
|
},
|
||||||
set_filters: function (filters) {
|
set_filters: function (filters) {
|
||||||
_(filters).map(_.bind(this.append_filter, this));
|
_(filters).map(_.bind(this.append_filter, this));
|
||||||
|
@ -1530,7 +1610,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
||||||
save_current: function () {
|
save_current: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $name = this.$('input:first');
|
var $name = this.$('input:first');
|
||||||
var private_filter = !this.$('input:last').prop('checked');
|
var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
|
||||||
|
var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
|
||||||
|
|
||||||
var search = this.view.build_search_data();
|
var search = this.view.build_search_data();
|
||||||
this.rpc('/web/session/eval_domain_and_context', {
|
this.rpc('/web/session/eval_domain_and_context', {
|
||||||
|
@ -1546,7 +1627,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
||||||
user_id: private_filter ? instance.session.uid : false,
|
user_id: private_filter ? instance.session.uid : false,
|
||||||
model_id: self.view.model,
|
model_id: self.view.model,
|
||||||
context: results.context,
|
context: results.context,
|
||||||
domain: results.domain
|
domain: results.domain,
|
||||||
|
is_default: set_as_default
|
||||||
};
|
};
|
||||||
// FIXME: current context?
|
// FIXME: current context?
|
||||||
return self.model.call('create_or_replace', [filter]).done(function (id) {
|
return self.model.call('create_or_replace', [filter]).done(function (id) {
|
||||||
|
|
|
@ -0,0 +1,393 @@
|
||||||
|
// Test support structures and methods for OpenERP
|
||||||
|
openerp.testing = {};
|
||||||
|
(function (testing) {
|
||||||
|
var dependencies = {
|
||||||
|
corelib: [],
|
||||||
|
coresetup: ['corelib'],
|
||||||
|
data: ['corelib', 'coresetup'],
|
||||||
|
dates: [],
|
||||||
|
formats: ['coresetup', 'dates'],
|
||||||
|
chrome: ['corelib', 'coresetup'],
|
||||||
|
views: ['corelib', 'coresetup', 'data', 'chrome'],
|
||||||
|
search: ['data', 'coresetup', 'formats'],
|
||||||
|
list: ['views', 'data'],
|
||||||
|
form: ['data', 'views', 'list', 'formats'],
|
||||||
|
list_editable: ['list', 'form', 'data'],
|
||||||
|
};
|
||||||
|
|
||||||
|
testing.dependencies = window['oe_all_dependencies'] || [];
|
||||||
|
testing.current_module = null;
|
||||||
|
testing.templates = { };
|
||||||
|
testing.add_template = function (name) {
|
||||||
|
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||||
|
xhr.open('GET', name, false);
|
||||||
|
xhr.send(null);
|
||||||
|
(testing.templates[testing.current_module] =
|
||||||
|
testing.templates[testing.current_module] || [])
|
||||||
|
.push(xhr.responseXML);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Function which does not do anything
|
||||||
|
*/
|
||||||
|
testing.noop = function () { };
|
||||||
|
/**
|
||||||
|
* Alter provided instance's ``session`` attribute to make response
|
||||||
|
* mockable:
|
||||||
|
*
|
||||||
|
* * The ``responses`` parameter can be used to provide a map of (RPC)
|
||||||
|
* paths (e.g. ``/web/view/load``) to a function returning a response
|
||||||
|
* to the query.
|
||||||
|
* * ``instance.session`` grows a ``responses`` attribute which is
|
||||||
|
* a map of the same (and is in fact initialized to the ``responses``
|
||||||
|
* parameter if one is provided)
|
||||||
|
*
|
||||||
|
* Note that RPC requests to un-mocked URLs will be rejected with an
|
||||||
|
* error message: only explicitly specified urls will get a response.
|
||||||
|
*
|
||||||
|
* Mocked sessions will *never* perform an actual RPC connection.
|
||||||
|
*
|
||||||
|
* @param instance openerp instance being initialized
|
||||||
|
* @param {Object} [responses]
|
||||||
|
*/
|
||||||
|
testing.mockifyRPC = function (instance, responses) {
|
||||||
|
var session = instance.session;
|
||||||
|
session.responses = responses || {};
|
||||||
|
session.rpc_function = function (url, payload) {
|
||||||
|
var fn, params;
|
||||||
|
var needle = payload.params.model + ':' + payload.params.method;
|
||||||
|
if (url.url === '/web/dataset/call_kw'
|
||||||
|
&& needle in this.responses) {
|
||||||
|
fn = this.responses[needle];
|
||||||
|
params = [
|
||||||
|
payload.params.args || [],
|
||||||
|
payload.params.kwargs || {}
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
fn = this.responses[url.url];
|
||||||
|
params = [payload];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fn) {
|
||||||
|
return $.Deferred().reject({}, 'failed',
|
||||||
|
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
|
||||||
|
url.url, JSON.stringify(payload.params))
|
||||||
|
).promise();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return $.when(fn.apply(null, params)).then(function (result) {
|
||||||
|
// Wrap for RPC layer unwrapper thingy
|
||||||
|
return {result: result};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// not sure why this looks like that
|
||||||
|
return $.Deferred().reject({}, 'failed', String(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var StackProto = {
|
||||||
|
execute: function (fn) {
|
||||||
|
var args = [].slice.call(arguments, 1);
|
||||||
|
// Warning: here be dragons
|
||||||
|
var i = 0, setups = this.setups, teardowns = this.teardowns;
|
||||||
|
var d = $.Deferred();
|
||||||
|
|
||||||
|
var succeeded, failed;
|
||||||
|
var success = function () {
|
||||||
|
succeeded = _.toArray(arguments);
|
||||||
|
return teardown();
|
||||||
|
};
|
||||||
|
var failure = function () {
|
||||||
|
// save first failure
|
||||||
|
if (!failed) {
|
||||||
|
failed = _.toArray(arguments);
|
||||||
|
}
|
||||||
|
// chain onto next teardown
|
||||||
|
return teardown();
|
||||||
|
};
|
||||||
|
|
||||||
|
var setup = function () {
|
||||||
|
// if setup to execute
|
||||||
|
if (i < setups.length) {
|
||||||
|
var f = setups[i] || testing.noop;
|
||||||
|
$.when(f.apply(null, args)).then(function () {
|
||||||
|
++i;
|
||||||
|
setup();
|
||||||
|
}, failure);
|
||||||
|
} else {
|
||||||
|
$.when(fn.apply(null, args)).then(success, failure);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var teardown = function () {
|
||||||
|
// if teardown to execute
|
||||||
|
if (i > 0) {
|
||||||
|
var f = teardowns[--i] || testing.noop;
|
||||||
|
$.when(f.apply(null, args)).then(teardown, failure);
|
||||||
|
} else {
|
||||||
|
if (failed) {
|
||||||
|
d.reject.apply(d, failed);
|
||||||
|
} else if (succeeded) {
|
||||||
|
d.resolve.apply(d, succeeded);
|
||||||
|
} else {
|
||||||
|
throw new Error("Didn't succeed or fail?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setup();
|
||||||
|
|
||||||
|
return d;
|
||||||
|
},
|
||||||
|
push: function (setup, teardown) {
|
||||||
|
return _.extend(Object.create(StackProto), {
|
||||||
|
setups: this.setups.concat([setup]),
|
||||||
|
teardowns: this.teardowns.concat([teardown])
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unshift: function (setup, teardown) {
|
||||||
|
return _.extend(Object.create(StackProto), {
|
||||||
|
setups: [setup].concat(this.setups),
|
||||||
|
teardowns: [teardown].concat(this.teardowns)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Function} [setup]
|
||||||
|
* @param {Function} [teardown]
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
testing.Stack = function (setup, teardown) {
|
||||||
|
return _.extend(Object.create(StackProto), {
|
||||||
|
setups: setup ? [setup] : [],
|
||||||
|
teardowns: teardown ? [teardown] : []
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var db = window['oe_db_info'];
|
||||||
|
testing.section = function (name, options, body) {
|
||||||
|
if (_.isFunction(options)) {
|
||||||
|
body = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
_.defaults(options, {
|
||||||
|
setup: testing.noop,
|
||||||
|
teardown: testing.noop
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.module(testing.current_module + '.' + name, {_oe: options});
|
||||||
|
body(testing.case);
|
||||||
|
};
|
||||||
|
testing.case = function (name, options, callback) {
|
||||||
|
if (_.isFunction(options)) {
|
||||||
|
callback = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var module = testing.current_module;
|
||||||
|
var module_index = _.indexOf(testing.dependencies, module);
|
||||||
|
var module_deps = testing.dependencies.slice(
|
||||||
|
// If module not in deps (because only tests, no JS) -> indexOf
|
||||||
|
// returns -1 -> index becomes 0 -> replace with ``undefined`` so
|
||||||
|
// Array#slice returns a full copy
|
||||||
|
0, module_index + 1 || undefined);
|
||||||
|
|
||||||
|
// Serialize options for this precise test case
|
||||||
|
// WARNING: typo is from jquery, do not fix!
|
||||||
|
var env = QUnit.config.currentModuleTestEnviroment;
|
||||||
|
// section setup
|
||||||
|
// case setup
|
||||||
|
// test
|
||||||
|
// case teardown
|
||||||
|
// section teardown
|
||||||
|
var case_stack = testing.Stack()
|
||||||
|
.push(env._oe.setup, env._oe.teardown)
|
||||||
|
.push(options.setup, options.teardown);
|
||||||
|
var opts = _.defaults({}, options, env._oe);
|
||||||
|
// FIXME: if this test is ignored, will still query
|
||||||
|
if (opts.rpc === 'rpc' && !db) {
|
||||||
|
QUnit.config.autostart = false;
|
||||||
|
db = {
|
||||||
|
source: null,
|
||||||
|
supadmin: null,
|
||||||
|
password: null
|
||||||
|
};
|
||||||
|
var $msg = $('<form style="margin: 0 1em 1em;">')
|
||||||
|
.append('<h3>A test needs to clone a database</h3>')
|
||||||
|
.append('<h4>Please provide the source clone information</h4>')
|
||||||
|
.append(' Source DB: ').append('<input name="source">').append('<br>')
|
||||||
|
.append(' DB Password: ').append('<input name="supadmin">').append('<br>')
|
||||||
|
.append('Admin Password: ').append('<input name="password">').append('<br>')
|
||||||
|
.append('<input type="submit" value="OK"/>')
|
||||||
|
.submit(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
db.source = $msg.find('input[name=source]').val();
|
||||||
|
db.supadmin = $msg.find('input[name=supadmin]').val();
|
||||||
|
db.password = $msg.find('input[name=password]').val();
|
||||||
|
QUnit.start();
|
||||||
|
$.unblockUI();
|
||||||
|
});
|
||||||
|
$.blockUI({
|
||||||
|
message: $msg,
|
||||||
|
css: {
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
textAlign: 'left',
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
cursor: 'default'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QUnit.test(name, function () {
|
||||||
|
var instance;
|
||||||
|
if (!opts.dependencies) {
|
||||||
|
instance = openerp.init(module_deps);
|
||||||
|
} else {
|
||||||
|
// empty-but-specified dependencies actually allow running
|
||||||
|
// without loading any module into the instance
|
||||||
|
|
||||||
|
// TODO: clean up this mess
|
||||||
|
var d = opts.dependencies.slice();
|
||||||
|
// dependencies list should be in deps order, reverse to make
|
||||||
|
// loading order from last
|
||||||
|
d.reverse();
|
||||||
|
var di = 0;
|
||||||
|
while (di < d.length) {
|
||||||
|
var m = /^web\.(\w+)$/.exec(d[di]);
|
||||||
|
if (m) {
|
||||||
|
d[di] = m[1];
|
||||||
|
}
|
||||||
|
d.splice.apply(d, [di+1, 0].concat(
|
||||||
|
_(dependencies[d[di]]).reverse()));
|
||||||
|
++di;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = openerp.init("fuck your shit, don't load anything you cunt");
|
||||||
|
_(d).chain()
|
||||||
|
.reverse()
|
||||||
|
.uniq()
|
||||||
|
.each(function (module) {
|
||||||
|
openerp.web[module](instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (_.isNumber(opts.asserts)) {
|
||||||
|
expect(opts.asserts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.templates) {
|
||||||
|
for(var i=0; i<module_deps.length; ++i) {
|
||||||
|
var dep = module_deps[i];
|
||||||
|
var templates = testing.templates[dep];
|
||||||
|
if (_.isEmpty(templates)) { continue; }
|
||||||
|
|
||||||
|
for (var j=0; j < templates.length; ++j) {
|
||||||
|
instance.web.qweb.add_template(templates[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var $fixture = $('#qunit-fixture');
|
||||||
|
|
||||||
|
var mock, async = false;
|
||||||
|
switch (opts.rpc) {
|
||||||
|
case 'mock':
|
||||||
|
async = true;
|
||||||
|
testing.mockifyRPC(instance);
|
||||||
|
mock = function (spec, handler) {
|
||||||
|
instance.session.responses[spec] = handler;
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'rpc':
|
||||||
|
async = true;
|
||||||
|
(function () {
|
||||||
|
// Bunch of random base36 characters
|
||||||
|
var dbname = 'test_' + Math.random().toString(36).slice(2);
|
||||||
|
// Add db setup/teardown at the start of the stack
|
||||||
|
case_stack = case_stack.unshift(function (instance) {
|
||||||
|
// FIXME hack: don't want the session to go through shitty loading process of everything
|
||||||
|
instance.session.session_init = testing.noop;
|
||||||
|
instance.session.load_modules = testing.noop;
|
||||||
|
instance.session.session_bind();
|
||||||
|
return instance.session.rpc('/web/database/duplicate', {
|
||||||
|
fields: [
|
||||||
|
{name: 'super_admin_pwd', value: db.supadmin},
|
||||||
|
{name: 'db_original_name', value: db.source},
|
||||||
|
{name: 'db_name', value: dbname}
|
||||||
|
]
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.error) {
|
||||||
|
return $.Deferred().reject(result.error).promise();
|
||||||
|
}
|
||||||
|
return instance.session.session_authenticate(
|
||||||
|
dbname, 'admin', db.password, true);
|
||||||
|
});
|
||||||
|
}, function (instance) {
|
||||||
|
return instance.session.rpc('/web/database/drop', {
|
||||||
|
fields: [
|
||||||
|
{name: 'drop_pwd', value: db.supadmin},
|
||||||
|
{name: 'drop_db', value: dbname}
|
||||||
|
]
|
||||||
|
}).then(function (result) {
|
||||||
|
if (result.error) {
|
||||||
|
return $.Deferred().reject(result.error).promise();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always execute tests asynchronously
|
||||||
|
stop();
|
||||||
|
var timeout;
|
||||||
|
case_stack.execute(function () {
|
||||||
|
var result = callback.apply(null, arguments);
|
||||||
|
if (!(result && _.isFunction(result.then))) {
|
||||||
|
if (async) {
|
||||||
|
ok(false, "asynchronous test cases must return a promise");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_.isNumber(opts.asserts)) {
|
||||||
|
ok(false, "asynchronous test cases must specify the "
|
||||||
|
+ "number of assertions they expect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.Deferred(function (d) {
|
||||||
|
$.when(result).then(function () {
|
||||||
|
d.resolve.apply(d, arguments)
|
||||||
|
}, function () {
|
||||||
|
d.reject.apply(d, arguments);
|
||||||
|
});
|
||||||
|
if (async || (result && result.then)) {
|
||||||
|
// async test can be either implicit async (rpc) or
|
||||||
|
// promise-returning
|
||||||
|
timeout = setTimeout(function () {
|
||||||
|
QUnit.config.semaphore = 1;
|
||||||
|
d.reject({message: "Test timed out"});
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, instance, $fixture, mock).always(function () {
|
||||||
|
if (timeout) { clearTimeout(timeout); }
|
||||||
|
start();
|
||||||
|
}).fail(function (error) {
|
||||||
|
if (options.fail_on_rejection === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var message;
|
||||||
|
if (typeof error !== 'object'
|
||||||
|
|| typeof error.message !== 'string') {
|
||||||
|
message = JSON.stringify([].slice.apply(arguments));
|
||||||
|
} else {
|
||||||
|
message = error.message;
|
||||||
|
if (error.data && error.data.debug) {
|
||||||
|
message += '\n\n' + error.data.debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(false, message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})(openerp.testing);
|
|
@ -173,10 +173,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||||
} else {
|
} else {
|
||||||
this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
|
this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
|
||||||
}
|
}
|
||||||
this.$buttons.on('click', '.oe_form_button_create', this.on_button_create);
|
this.$buttons.on('click', '.oe_form_button_create',
|
||||||
this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit);
|
this.guard_active(this.on_button_create));
|
||||||
this.$buttons.on('click', '.oe_form_button_save', this.on_button_save);
|
this.$buttons.on('click', '.oe_form_button_edit',
|
||||||
this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel);
|
this.guard_active(this.on_button_edit));
|
||||||
|
this.$buttons.on('click', '.oe_form_button_save',
|
||||||
|
this.guard_active(this.on_button_save));
|
||||||
|
this.$buttons.on('click', '.oe_form_button_cancel',
|
||||||
|
this.guard_active(this.on_button_cancel));
|
||||||
if (this.options.footer_to_buttons) {
|
if (this.options.footer_to_buttons) {
|
||||||
this.$el.find('footer').appendTo(this.$buttons);
|
this.$el.find('footer').appendTo(this.$buttons);
|
||||||
}
|
}
|
||||||
|
@ -618,6 +622,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||||
return self._process_save(save_obj).then(function() {
|
return self._process_save(save_obj).then(function() {
|
||||||
save_obj.ret = _.toArray(arguments);
|
save_obj.ret = _.toArray(arguments);
|
||||||
return iterate();
|
return iterate();
|
||||||
|
}, function() {
|
||||||
|
save_obj.error = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return $.when();
|
return $.when();
|
||||||
|
@ -801,6 +807,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||||
var save_obj = {prepend_on_create: prepend_on_create, ret: null};
|
var save_obj = {prepend_on_create: prepend_on_create, ret: null};
|
||||||
this.save_list.push(save_obj);
|
this.save_list.push(save_obj);
|
||||||
return this._process_operations().then(function() {
|
return this._process_operations().then(function() {
|
||||||
|
if (save_obj.error)
|
||||||
|
return $.Deferred().reject();
|
||||||
return $.when.apply($, save_obj.ret);
|
return $.when.apply($, save_obj.ret);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1187,14 +1195,36 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
view_arch_to_dom_node: function(arch) {
|
||||||
|
// Historic mess for views arch
|
||||||
|
//
|
||||||
|
// server:
|
||||||
|
// -> got xml as string
|
||||||
|
// -> parse to xml and manipulate domains and contexts
|
||||||
|
// -> convert to json
|
||||||
|
// client:
|
||||||
|
// -> got view as json
|
||||||
|
// -> convert back to xml as string
|
||||||
|
// -> parse it as xml doc (manipulate button@type for IE)
|
||||||
|
// -> convert back to string
|
||||||
|
// -> parse it as dom element with jquery
|
||||||
|
// -> for each widget, convert node to json
|
||||||
|
//
|
||||||
|
// Wow !!!
|
||||||
|
var xml = instance.web.json_node_to_xml(arch);
|
||||||
|
|
||||||
|
var doc = $.parseXML('<div class="oe_form">' + xml + '</div>');
|
||||||
|
$('button', doc).each(function() {
|
||||||
|
$(this).attr('data-button-type', $(this).attr('type'));
|
||||||
|
});
|
||||||
|
xml = instance.web.xml_to_str(doc);
|
||||||
|
return $(xml);
|
||||||
|
},
|
||||||
render_to: function($target) {
|
render_to: function($target) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.$target = $target;
|
this.$target = $target;
|
||||||
|
|
||||||
// TODO: I know this will save the world and all the kitten for a moment,
|
this.$form = this.view_arch_to_dom_node(this.fvg.arch);
|
||||||
// but one day, we will have to get rid of xml2json
|
|
||||||
var xml = instance.web.json_node_to_xml(this.fvg.arch);
|
|
||||||
this.$form = $('<div class="oe_form">' + xml + '</div>');
|
|
||||||
|
|
||||||
this.process_version();
|
this.process_version();
|
||||||
|
|
||||||
|
@ -1863,6 +1893,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
|
||||||
instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
|
instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
|
||||||
template: 'WidgetButton',
|
template: 'WidgetButton',
|
||||||
init: function(field_manager, node) {
|
init: function(field_manager, node) {
|
||||||
|
node.attrs.type = node.attrs['data-button-type'];
|
||||||
this._super(field_manager, node);
|
this._super(field_manager, node);
|
||||||
this.force_disabled = false;
|
this.force_disabled = false;
|
||||||
this.string = (this.node.attrs.string || '').replace(/_/g, '');
|
this.string = (this.node.attrs.string || '').replace(/_/g, '');
|
||||||
|
@ -2152,17 +2183,10 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
||||||
},
|
},
|
||||||
|
|
||||||
set_dimensions: function (height, width) {
|
set_dimensions: function (height, width) {
|
||||||
// remove width css property
|
this.$el.css({
|
||||||
this.$el.css('width', '');
|
width: width,
|
||||||
// extract style (without width)
|
minHeight: height
|
||||||
var old_style = this.$el.attr('style');
|
});
|
||||||
// jQuery doesn't understand/use !important
|
|
||||||
var style = 'width:' + width + 'px !important;';
|
|
||||||
if (old_style) {
|
|
||||||
style += old_style
|
|
||||||
}
|
|
||||||
this.$el.attr('style', style);
|
|
||||||
this.$el.css('minHeight', height);
|
|
||||||
},
|
},
|
||||||
commit_value: function() {
|
commit_value: function() {
|
||||||
return $.when();
|
return $.when();
|
||||||
|
@ -2386,7 +2410,7 @@ instance.web.DateTimeWidget = instance.web.Widget.extend({
|
||||||
self.$input.focus();
|
self.$input.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.picker('setDate', self.value ? instance.web.auto_str_to_date(self.value) : new Date());
|
self.picker('setDate', self.get('value') ? instance.web.auto_str_to_date(self.get('value')) : new Date());
|
||||||
self.$input_picker.show();
|
self.$input_picker.show();
|
||||||
self.picker('show');
|
self.picker('show');
|
||||||
self.$input_picker.hide();
|
self.$input_picker.hide();
|
||||||
|
@ -3102,7 +3126,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
delay: 0
|
delay: 0
|
||||||
});
|
});
|
||||||
this.$input.autocomplete("widget").addClass("openerp");
|
this.$input.autocomplete("widget").openerpClass();
|
||||||
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
|
||||||
this.$input.keyup(function(e) {
|
this.$input.keyup(function(e) {
|
||||||
if (e.which === 13) { // ENTER
|
if (e.which === 13) { // ENTER
|
||||||
|
@ -3813,26 +3837,13 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
do_delete: function (ids) {
|
do_delete: function (ids) {
|
||||||
var self = this;
|
|
||||||
var next = $.when();
|
|
||||||
var _super = this._super;
|
|
||||||
// handle deletion of an item which does not exist
|
|
||||||
// TODO: better handle that in the editable list?
|
|
||||||
var false_id_index = _(ids).indexOf(false);
|
|
||||||
if (false_id_index !== -1) {
|
|
||||||
ids.splice(false_id_index, 1);
|
|
||||||
next = this.cancel_edition(true);
|
|
||||||
}
|
|
||||||
return next.then(function () {
|
|
||||||
// wheeee
|
|
||||||
var confirm = window.confirm;
|
var confirm = window.confirm;
|
||||||
window.confirm = function () { return true; };
|
window.confirm = function () { return true; };
|
||||||
try {
|
try {
|
||||||
return _super.call(self, ids);
|
return this._super(ids);
|
||||||
} finally {
|
} finally {
|
||||||
window.confirm = confirm;
|
window.confirm = confirm;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
instance.web.form.One2ManyGroups = instance.web.ListView.Groups.extend({
|
instance.web.form.One2ManyGroups = instance.web.ListView.Groups.extend({
|
||||||
|
@ -3995,8 +4006,13 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
|
||||||
self._drop_shown = true;
|
self._drop_shown = true;
|
||||||
});
|
});
|
||||||
self.tags = self.$text.textext()[0].tags();
|
self.tags = self.$text.textext()[0].tags();
|
||||||
self.$text.focusout(function() {
|
self.$text
|
||||||
|
.focusin(function () {
|
||||||
|
self.trigger('focused');
|
||||||
|
})
|
||||||
|
.focusout(function() {
|
||||||
self.$text.trigger("setInputData", "");
|
self.$text.trigger("setInputData", "");
|
||||||
|
self.trigger('blurred');
|
||||||
}).keydown(function(e) {
|
}).keydown(function(e) {
|
||||||
if (e.which === $.ui.keyCode.TAB && self._drop_shown) {
|
if (e.which === $.ui.keyCode.TAB && self._drop_shown) {
|
||||||
self.$text.textext()[0].autocomplete().selectFromDropdown();
|
self.$text.textext()[0].autocomplete().selectFromDropdown();
|
||||||
|
@ -4790,15 +4806,6 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
|
||||||
this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
|
this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
|
||||||
this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
|
this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
|
||||||
},
|
},
|
||||||
human_filesize : function(size) {
|
|
||||||
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
|
||||||
var i = 0;
|
|
||||||
while (size >= 1024) {
|
|
||||||
size /= 1024;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return size.toFixed(2) + ' ' + units[i];
|
|
||||||
},
|
|
||||||
on_file_change: function(e) {
|
on_file_change: function(e) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var file_node = e.target;
|
var file_node = e.target;
|
||||||
|
@ -4846,6 +4853,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
|
||||||
link.href = "data:application/octet-stream;base64," + value;
|
link.href = "data:application/octet-stream;base64," + value;
|
||||||
} else {
|
} else {
|
||||||
instance.web.blockUI();
|
instance.web.blockUI();
|
||||||
|
var c = instance.webclient.crashmanager;
|
||||||
this.session.get_file({
|
this.session.get_file({
|
||||||
url: '/web/binary/saveas_ajax',
|
url: '/web/binary/saveas_ajax',
|
||||||
data: {data: JSON.stringify({
|
data: {data: JSON.stringify({
|
||||||
|
@ -4856,7 +4864,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
|
||||||
context: this.view.dataset.get_context()
|
context: this.view.dataset.get_context()
|
||||||
})},
|
})},
|
||||||
complete: instance.web.unblockUI,
|
complete: instance.web.unblockUI,
|
||||||
error: instance.webclient.crashmanager.on_rpc_error
|
error: c.rpc_error.bind(c)
|
||||||
});
|
});
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
|
@ -4915,7 +4923,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
|
||||||
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
|
||||||
this.binary_value = true;
|
this.binary_value = true;
|
||||||
this.internal_set_value(file_base64);
|
this.internal_set_value(file_base64);
|
||||||
var show_value = name + " (" + this.human_filesize(size) + ")";
|
var show_value = name + " (" + instance.web.human_size(size) + ")";
|
||||||
this.$el.find('input').eq(0).val(show_value);
|
this.$el.find('input').eq(0).val(show_value);
|
||||||
this.set_filename(name);
|
this.set_filename(name);
|
||||||
},
|
},
|
||||||
|
@ -4935,12 +4943,16 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
|
||||||
if (this.get('value') && ! /^\d+(\.\d*)? \w+$/.test(this.get('value'))) {
|
if (this.get('value') && ! /^\d+(\.\d*)? \w+$/.test(this.get('value'))) {
|
||||||
url = 'data:image/png;base64,' + this.get('value');
|
url = 'data:image/png;base64,' + this.get('value');
|
||||||
} else if (this.get('value')) {
|
} else if (this.get('value')) {
|
||||||
var id = escape(JSON.stringify(this.view.datarecord.id || null));
|
var id = JSON.stringify(this.view.datarecord.id || null);
|
||||||
var field = this.name;
|
var field = this.name;
|
||||||
if (this.options.preview_image)
|
if (this.options.preview_image)
|
||||||
field = this.options.preview_image;
|
field = this.options.preview_image;
|
||||||
url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
|
url = this.session.url('/web/binary/image', {
|
||||||
this.view.dataset.model +'&id=' + id + '&field=' + field + '&t=' + (new Date().getTime());
|
model: this.view.dataset.model,
|
||||||
|
id: id,
|
||||||
|
field: field,
|
||||||
|
t: (new Date().getTime()),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
url = this.placeholder;
|
url = this.placeholder;
|
||||||
}
|
}
|
||||||
|
@ -4979,14 +4991,14 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
|
||||||
* Options on attribute ; "blockui" {Boolean} block the UI or not
|
* Options on attribute ; "blockui" {Boolean} block the UI or not
|
||||||
* during the file is uploading
|
* during the file is uploading
|
||||||
*/
|
*/
|
||||||
instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractField.extend({
|
instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractField.extend({
|
||||||
template: "FieldBinaryFileUploader",
|
template: "FieldBinaryFileUploader",
|
||||||
init: function(field_manager, node) {
|
init: function(field_manager, node) {
|
||||||
this._super(field_manager, node);
|
this._super(field_manager, node);
|
||||||
this.field_manager = field_manager;
|
this.field_manager = field_manager;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
if(this.field.type != "one2many" || this.field.relation != 'ir.attachment') {
|
if(this.field.type != "many2many" || this.field.relation != 'ir.attachment') {
|
||||||
throw "The type of the field '"+this.field.string+"' must be a one2many field with a relation to 'ir.attachment' model.";
|
throw "The type of the field '"+this.field.string+"' must be a many2many field with a relation to 'ir.attachment' model.";
|
||||||
}
|
}
|
||||||
this.ds_file = new instance.web.DataSetSearch(this, 'ir.attachment');
|
this.ds_file = new instance.web.DataSetSearch(this, 'ir.attachment');
|
||||||
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
|
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
|
||||||
|
@ -4996,21 +5008,85 @@ instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractFiel
|
||||||
this._super(this);
|
this._super(this);
|
||||||
this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
|
this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
|
||||||
},
|
},
|
||||||
|
set_value: function(value_) {
|
||||||
|
var value_ = value_ || [];
|
||||||
|
var self = this;
|
||||||
|
var ids = [];
|
||||||
|
_.each(value_, function(command) {
|
||||||
|
if (isNaN(command) && command.id == undefined) {
|
||||||
|
switch (command[0]) {
|
||||||
|
case commands.CREATE:
|
||||||
|
ids = ids.concat(command[2]);
|
||||||
|
return;
|
||||||
|
case commands.REPLACE_WITH:
|
||||||
|
ids = ids.concat(command[2]);
|
||||||
|
return;
|
||||||
|
case commands.UPDATE:
|
||||||
|
ids = ids.concat(command[2]);
|
||||||
|
return;
|
||||||
|
case commands.LINK_TO:
|
||||||
|
ids = ids.concat(command[1]);
|
||||||
|
return;
|
||||||
|
case commands.DELETE:
|
||||||
|
ids = _.filter(ids, function (id) { return id != command[1];});
|
||||||
|
return;
|
||||||
|
case commands.DELETE_ALL:
|
||||||
|
ids = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ids.push(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._super( ids );
|
||||||
|
},
|
||||||
get_value: function() {
|
get_value: function() {
|
||||||
return _.map(this.get('value'), function (value) { return commands.link_to( value.id ); });
|
return _.map(this.get('value'), function (value) { return commands.link_to( value.id ); });
|
||||||
},
|
},
|
||||||
get_file_url: function (attachment) {
|
get_file_url: function (attachment) {
|
||||||
return instance.origin + '/web/binary/saveas?session_id=' + this.session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
|
return this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: attachment['id']});
|
||||||
|
},
|
||||||
|
read_name_values : function () {
|
||||||
|
var self = this;
|
||||||
|
// select the list of id for a get_name
|
||||||
|
var values = [];
|
||||||
|
_.each(this.get('value'), function (val) {
|
||||||
|
if (typeof val != 'object') {
|
||||||
|
values.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// send request for get_name
|
||||||
|
if (values.length) {
|
||||||
|
return this.ds_file.call('read', [values, ['id', 'name', 'datas_fname']]).done(function (datas) {
|
||||||
|
_.each(datas, function (data) {
|
||||||
|
data.no_unlink = true;
|
||||||
|
data.url = self.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: data.id});
|
||||||
|
|
||||||
|
_.each(self.get('value'), function (val, key) {
|
||||||
|
if(val == data.id) {
|
||||||
|
self.get('value')[key] = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return $.when(this.get('value'));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
render_value: function () {
|
render_value: function () {
|
||||||
var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': this}));
|
var self = this;
|
||||||
render.on('click', '.oe_delete', _.bind(this.on_file_delete, this));
|
this.read_name_values().then(function (datas) {
|
||||||
this.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
|
|
||||||
|
var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self}));
|
||||||
|
render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
|
||||||
|
self.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
|
||||||
|
|
||||||
// reinit input type file
|
// reinit input type file
|
||||||
var $input = this.$('input.oe_form_binary_file');
|
var $input = self.$('input.oe_form_binary_file');
|
||||||
$input.after($input.clone(true)).remove();
|
$input.after($input.clone(true)).remove();
|
||||||
this.$(".oe_fileupload").show();
|
self.$(".oe_fileupload").show();
|
||||||
|
|
||||||
|
});
|
||||||
},
|
},
|
||||||
on_file_change: function (event) {
|
on_file_change: function (event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -5026,7 +5102,7 @@ instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractFiel
|
||||||
}
|
}
|
||||||
|
|
||||||
// block UI or not
|
// block UI or not
|
||||||
if(this.node.attrs.blockui) {
|
if(this.node.attrs.blockui>0) {
|
||||||
instance.web.blockUI();
|
instance.web.blockUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5060,7 +5136,7 @@ instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractFiel
|
||||||
},
|
},
|
||||||
on_file_loaded: function (event, result) {
|
on_file_loaded: function (event, result) {
|
||||||
// unblock UI
|
// unblock UI
|
||||||
if(this.node.attrs.blockui) {
|
if(this.node.attrs.blockui>0) {
|
||||||
instance.web.unblockUI();
|
instance.web.unblockUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5090,7 +5166,7 @@ instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractFiel
|
||||||
if(file_id != this.get('value')[i].id){
|
if(file_id != this.get('value')[i].id){
|
||||||
files.push(this.get('value')[i]);
|
files.push(this.get('value')[i]);
|
||||||
}
|
}
|
||||||
else {
|
else if(!this.get('value')[i].no_unlink) {
|
||||||
this.ds_file.unlink([file_id]);
|
this.ds_file.unlink([file_id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5252,7 +5328,7 @@ instance.web.form.widgets = new instance.web.Registry({
|
||||||
'progressbar': 'instance.web.form.FieldProgressBar',
|
'progressbar': 'instance.web.form.FieldProgressBar',
|
||||||
'image': 'instance.web.form.FieldBinaryImage',
|
'image': 'instance.web.form.FieldBinaryImage',
|
||||||
'binary': 'instance.web.form.FieldBinaryFile',
|
'binary': 'instance.web.form.FieldBinaryFile',
|
||||||
'one2many_binary': 'instance.web.form.FieldOne2ManyBinaryMultiFiles',
|
'many2many_binary': 'instance.web.form.FieldMany2ManyBinaryMultiFiles',
|
||||||
'statusbar': 'instance.web.form.FieldStatus',
|
'statusbar': 'instance.web.form.FieldStatus',
|
||||||
'monetary': 'instance.web.form.FieldMonetary',
|
'monetary': 'instance.web.form.FieldMonetary',
|
||||||
});
|
});
|
||||||
|
|
|
@ -503,12 +503,17 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
||||||
return this.reload_content();
|
return this.reload_content();
|
||||||
},
|
},
|
||||||
reload_record: function (record) {
|
reload_record: function (record) {
|
||||||
|
var self = this;
|
||||||
return this.dataset.read_ids(
|
return this.dataset.read_ids(
|
||||||
[record.get('id')],
|
[record.get('id')],
|
||||||
_.pluck(_(this.columns).filter(function (r) {
|
_.pluck(_(this.columns).filter(function (r) {
|
||||||
return r.tag === 'field';
|
return r.tag === 'field';
|
||||||
}), 'name')
|
}), 'name')
|
||||||
).done(function (records) {
|
).done(function (records) {
|
||||||
|
if (!records[0]) {
|
||||||
|
self.records.remove(record);
|
||||||
|
return;
|
||||||
|
}
|
||||||
_(records[0]).each(function (value, key) {
|
_(records[0]).each(function (value, key) {
|
||||||
record.set(key, value, {silent: true});
|
record.set(key, value, {silent: true});
|
||||||
});
|
});
|
||||||
|
@ -2151,7 +2156,7 @@ instance.web.list.Boolean = instance.web.list.Column.extend({
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_format: function (row_data, options) {
|
_format: function (row_data, options) {
|
||||||
return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
|
return _.str.sprintf('<input type="checkbox" %s readonly="readonly"/>',
|
||||||
row_data[this.id].value ? 'checked="checked"' : '');
|
row_data[this.id].value ? 'checked="checked"' : '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2163,20 +2168,24 @@ instance.web.list.Binary = instance.web.list.Column.extend({
|
||||||
*/
|
*/
|
||||||
_format: function (row_data, options) {
|
_format: function (row_data, options) {
|
||||||
var text = _t("Download");
|
var text = _t("Download");
|
||||||
var download_url = _.str.sprintf(
|
var value = row_data[this.id].value;
|
||||||
'/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
|
var download_url;
|
||||||
instance.session.session_id, options.model, this.id, options.id);
|
if (value && value.substr(0, 10).indexOf(' ') == -1) {
|
||||||
|
download_url = "data:application/octet-stream;base64," + value;
|
||||||
|
} else {
|
||||||
|
download_url = this.session.url('/web/binary/saveas', {model: options.model, field: this.id, id: options.id});
|
||||||
if (this.filename) {
|
if (this.filename) {
|
||||||
download_url += '&filename_field=' + this.filename;
|
download_url += '&filename_field=' + this.filename;
|
||||||
if (row_data[this.filename]) {
|
}
|
||||||
|
}
|
||||||
|
if (this.filename && row_data[this.filename]) {
|
||||||
text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
|
text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
|
||||||
row_data[this.filename].value, {type: 'char'}));
|
row_data[this.filename].value, {type: 'char'}));
|
||||||
}
|
}
|
||||||
}
|
return _.template('<a href="<%-href%>"><%-text%></a> (<%-size%>)', {
|
||||||
return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
|
|
||||||
text: text,
|
text: text,
|
||||||
href: download_url,
|
href: download_url,
|
||||||
size: row_data[this.id].value
|
size: instance.web.binary_to_binsize(value),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,6 +77,15 @@ openerp.web.list_editable = function (instance) {
|
||||||
do_edit: function (index, id, dataset) {
|
do_edit: function (index, id, dataset) {
|
||||||
_.extend(this.dataset, dataset);
|
_.extend(this.dataset, dataset);
|
||||||
},
|
},
|
||||||
|
do_delete: function (ids) {
|
||||||
|
var _super = this._super.bind(this);
|
||||||
|
var next = this.editor.is_editing()
|
||||||
|
? this.cancel_edition(true)
|
||||||
|
: $.when();
|
||||||
|
return next.then(function () {
|
||||||
|
return _super(ids);
|
||||||
|
});
|
||||||
|
},
|
||||||
editable: function () {
|
editable: function () {
|
||||||
return this.fields_view.arch.attrs.editable
|
return this.fields_view.arch.attrs.editable
|
||||||
|| this._context_editable
|
|| this._context_editable
|
||||||
|
@ -117,13 +126,6 @@ openerp.web.list_editable = function (instance) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
self.cancel_edition();
|
self.cancel_edition();
|
||||||
});
|
});
|
||||||
this.$el
|
|
||||||
.off('click', 'tbody td:not(.oe_list_field_cell)')
|
|
||||||
.on('click', 'tbody td:not(.oe_list_field_cell)', function () {
|
|
||||||
if (!self.editor.is_editing()) {
|
|
||||||
self.start_edition();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.editor.destroy();
|
this.editor.destroy();
|
||||||
// Editor is not restartable due to formview not being
|
// Editor is not restartable due to formview not being
|
||||||
// restartable
|
// restartable
|
||||||
|
@ -164,13 +166,23 @@ openerp.web.list_editable = function (instance) {
|
||||||
* @returns {$.Deferred}
|
* @returns {$.Deferred}
|
||||||
*/
|
*/
|
||||||
ensure_saved: function () {
|
ensure_saved: function () {
|
||||||
var self = this;
|
return this.save_edition();
|
||||||
return this.saving_mutex.exec(function() {
|
},
|
||||||
if (!self.editor.is_editing()) {
|
/**
|
||||||
return $.when();
|
* Builds a record with the provided id (``false`` for a creation),
|
||||||
}
|
* setting all columns with ``false`` value so code which relies on
|
||||||
return self.save_edition();
|
* having an actual value behaves correctly
|
||||||
});
|
*
|
||||||
|
* @param {*} id
|
||||||
|
* @return {instance.web.list.Record}
|
||||||
|
*/
|
||||||
|
make_empty_record: function (id) {
|
||||||
|
var attrs = {id: id};
|
||||||
|
_(this.columns).chain()
|
||||||
|
.filter(function (x) { return x.tag === 'field'})
|
||||||
|
.pluck('name')
|
||||||
|
.each(function (field) { attrs[field] = false; });
|
||||||
|
return new instance.web.list.Record(attrs);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set up the edition of a record of the list view "inline"
|
* Set up the edition of a record of the list view "inline"
|
||||||
|
@ -186,12 +198,7 @@ openerp.web.list_editable = function (instance) {
|
||||||
if (record) {
|
if (record) {
|
||||||
item = record.attributes;
|
item = record.attributes;
|
||||||
} else {
|
} else {
|
||||||
var attrs = {id: false};
|
record = this.make_empty_record(false);
|
||||||
_(this.columns).chain()
|
|
||||||
.filter(function (x) { return x.tag === 'field'})
|
|
||||||
.pluck('name')
|
|
||||||
.each(function (field) { attrs[field] = false; });
|
|
||||||
record = new instance.web.list.Record(attrs);
|
|
||||||
this.records.add(record, {
|
this.records.add(record, {
|
||||||
at: this.prepends_on_create() ? 0 : null});
|
at: this.prepends_on_create() ? 0 : null});
|
||||||
}
|
}
|
||||||
|
@ -269,12 +276,16 @@ openerp.web.list_editable = function (instance) {
|
||||||
*/
|
*/
|
||||||
save_edition: function () {
|
save_edition: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.with_event('save', {
|
return self.saving_mutex.exec(function() {
|
||||||
editor: this.editor,
|
if (!self.editor.is_editing()) {
|
||||||
form: this.editor.form,
|
return $.when();
|
||||||
|
}
|
||||||
|
return self.with_event('save', {
|
||||||
|
editor: self.editor,
|
||||||
|
form: self.editor.form,
|
||||||
cancel: false
|
cancel: false
|
||||||
}, function () {
|
}, function () {
|
||||||
return this.editor.save().then(function (attrs) {
|
return self.editor.save().then(function (attrs) {
|
||||||
var created = false;
|
var created = false;
|
||||||
var record = self.records.get(attrs.id);
|
var record = self.records.get(attrs.id);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
|
@ -294,6 +305,7 @@ openerp.web.list_editable = function (instance) {
|
||||||
return { created: created, record: record }; });
|
return { created: created, record: record }; });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @param {Boolean} [force=false] discards the data even if the form has been edited
|
* @param {Boolean} [force=false] discards the data even if the form has been edited
|
||||||
|
@ -391,7 +403,7 @@ openerp.web.list_editable = function (instance) {
|
||||||
if (!record) {
|
if (!record) {
|
||||||
// insert after the source record
|
// insert after the source record
|
||||||
var index = this.records.indexOf(source_record) + 1;
|
var index = this.records.indexOf(source_record) + 1;
|
||||||
record = new instance.web.list.Record({id: id});
|
record = this.make_empty_record(id);
|
||||||
this.records.add(record, {at: index});
|
this.records.add(record, {at: index});
|
||||||
this.dataset.ids.splice(index, 0, id);
|
this.dataset.ids.splice(index, 0, id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,8 +213,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
||||||
if (run_action) {
|
if (run_action) {
|
||||||
this.null_action();
|
this.null_action();
|
||||||
action_loaded = this.do_action(state.action);
|
action_loaded = this.do_action(state.action);
|
||||||
|
$.when(action_loaded || null).done(function() {
|
||||||
instance.webclient.menu.has_been_loaded.done(function() {
|
instance.webclient.menu.has_been_loaded.done(function() {
|
||||||
instance.webclient.menu.open_action(state.action);
|
if (self.inner_action && self.inner_action.id) {
|
||||||
|
instance.webclient.menu.open_action(self.inner_action.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,15 +310,15 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
||||||
}
|
}
|
||||||
var widget = executor.widget();
|
var widget = executor.widget();
|
||||||
if (executor.action.target === 'new') {
|
if (executor.action.target === 'new') {
|
||||||
if (this.dialog_widget && ! this.dialog_widget.isDestroyed())
|
if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
|
||||||
this.dialog_widget.destroy();
|
this.dialog_widget.destroy();
|
||||||
if (this.dialog === null || this.dialog.isDestroyed()) {
|
}
|
||||||
|
this.dialog_stop();
|
||||||
this.dialog = new instance.web.Dialog(this, {
|
this.dialog = new instance.web.Dialog(this, {
|
||||||
dialogClass: executor.klass,
|
dialogClass: executor.klass,
|
||||||
});
|
});
|
||||||
this.dialog.on("closing", null, options.on_close);
|
this.dialog.on("closing", null, options.on_close);
|
||||||
this.dialog.init_dialog();
|
this.dialog.init_dialog();
|
||||||
}
|
|
||||||
this.dialog.dialog_title = executor.action.name;
|
this.dialog.dialog_title = executor.action.name;
|
||||||
if (widget instanceof instance.web.ViewManager) {
|
if (widget instanceof instance.web.ViewManager) {
|
||||||
_.extend(widget.flags, {
|
_.extend(widget.flags, {
|
||||||
|
@ -397,6 +401,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
||||||
}).done(function(res) {
|
}).done(function(res) {
|
||||||
action = _.clone(action);
|
action = _.clone(action);
|
||||||
action.context = res.context;
|
action.context = res.context;
|
||||||
|
var c = instance.webclient.crashmanager;
|
||||||
self.session.get_file({
|
self.session.get_file({
|
||||||
url: '/web/report',
|
url: '/web/report',
|
||||||
data: {action: JSON.stringify(action)},
|
data: {action: JSON.stringify(action)},
|
||||||
|
@ -407,7 +412,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
||||||
}
|
}
|
||||||
self.dialog_stop();
|
self.dialog_stop();
|
||||||
},
|
},
|
||||||
error: instance.webclient.crashmanager.on_rpc_error
|
error: c.rpc_error.bind(c)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -504,7 +509,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
||||||
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
|
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
|
||||||
.parent().addClass('active');
|
.parent().addClass('active');
|
||||||
|
|
||||||
r = $.when(view_promise).done(function () {
|
return $.when(view_promise).done(function () {
|
||||||
_.each(_.keys(self.views), function(view_name) {
|
_.each(_.keys(self.views), function(view_name) {
|
||||||
var controller = self.views[view_name].controller;
|
var controller = self.views[view_name].controller;
|
||||||
if (controller) {
|
if (controller) {
|
||||||
|
@ -520,7 +525,6 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
||||||
});
|
});
|
||||||
self.trigger('switch_mode', view_type, no_store, view_options);
|
self.trigger('switch_mode', view_type, no_store, view_options);
|
||||||
});
|
});
|
||||||
return r;
|
|
||||||
},
|
},
|
||||||
do_create_view: function(view_type) {
|
do_create_view: function(view_type) {
|
||||||
// Lazy loading of views
|
// Lazy loading of views
|
||||||
|
@ -785,8 +789,8 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
|
||||||
name: "JS Tests",
|
name: "JS Tests",
|
||||||
target: 'new',
|
target: 'new',
|
||||||
type : 'ir.actions.act_url',
|
type : 'ir.actions.act_url',
|
||||||
url: '/web/static/test/test.html'
|
url: '/web/tests?mod=*'
|
||||||
})
|
});
|
||||||
break;
|
break;
|
||||||
case 'perm_read':
|
case 'perm_read':
|
||||||
var ids = current_view.get_selected_ids();
|
var ids = current_view.get_selected_ids();
|
||||||
|
@ -1098,11 +1102,11 @@ instance.web.Sidebar = instance.web.Widget.extend({
|
||||||
on_attachments_loaded: function(attachments) {
|
on_attachments_loaded: function(attachments) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var items = [];
|
var items = [];
|
||||||
var prefix = this.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id=';
|
var prefix = this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'name'});
|
||||||
_.each(attachments,function(a) {
|
_.each(attachments,function(a) {
|
||||||
a.label = a.name;
|
a.label = a.name;
|
||||||
if(a.type === "binary") {
|
if(a.type === "binary") {
|
||||||
a.url = prefix + a.id + '&t=' + (new Date().getTime());
|
a.url = prefix + '&id=' + a.id + '&t=' + (new Date().getTime());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.items['files'] = attachments;
|
self.items['files'] = attachments;
|
||||||
|
@ -1235,11 +1239,12 @@ instance.web.View = instance.web.Widget.extend({
|
||||||
});
|
});
|
||||||
}, null);
|
}, null);
|
||||||
} else {
|
} else {
|
||||||
|
self.do_action({"type":"ir.actions.act_window_close"});
|
||||||
return result_handler();
|
return result_handler();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (action_data.special) {
|
if (action_data.special === 'cancel') {
|
||||||
return handler({"type":"ir.actions.act_window_close"});
|
return handler({"type":"ir.actions.act_window_close"});
|
||||||
} else if (action_data.type=="object") {
|
} else if (action_data.type=="object") {
|
||||||
var args = [[record_id]], additional_args = [];
|
var args = [[record_id]], additional_args = [];
|
||||||
|
@ -1277,6 +1282,27 @@ instance.web.View = instance.web.Widget.extend({
|
||||||
do_hide: function () {
|
do_hide: function () {
|
||||||
this.$el.hide();
|
this.$el.hide();
|
||||||
},
|
},
|
||||||
|
is_active: function () {
|
||||||
|
var manager = this.getParent();
|
||||||
|
return !manager || !manager.active_view
|
||||||
|
|| manager.views[manager.active_view].controller === this;
|
||||||
|
}, /**
|
||||||
|
* Wraps fn to only call it if the current view is the active one. If the
|
||||||
|
* current view is not active, doesn't call fn.
|
||||||
|
*
|
||||||
|
* fn can not return anything, as a non-call to fn can't return anything
|
||||||
|
* either
|
||||||
|
*
|
||||||
|
* @param {Function} fn function to wrap in the active guard
|
||||||
|
*/
|
||||||
|
guard_active: function (fn) {
|
||||||
|
var self = this;
|
||||||
|
return function () {
|
||||||
|
if (self.is_active()) {
|
||||||
|
fn.apply(self, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
do_push_state: function(state) {
|
do_push_state: function(state) {
|
||||||
if (this.getParent() && this.getParent().do_push_state) {
|
if (this.getParent() && this.getParent().do_push_state) {
|
||||||
this.getParent().do_push_state(state);
|
this.getParent().do_push_state(state);
|
||||||
|
@ -1384,14 +1410,16 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) {
|
||||||
} else {
|
} else {
|
||||||
return r + '/>';
|
return r + '/>';
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
instance.web.xml_to_str = function(node) {
|
instance.web.xml_to_str = function(node) {
|
||||||
if (window.ActiveXObject) {
|
if (window.XMLSerializer) {
|
||||||
|
return (new XMLSerializer()).serializeToString(node);
|
||||||
|
} else if (window.ActiveXObject) {
|
||||||
return node.xml;
|
return node.xml;
|
||||||
} else {
|
} else {
|
||||||
return (new XMLSerializer()).serializeToString(node);
|
throw new Error("Could not serialize XML");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
instance.web.str_to_xml = function(s) {
|
instance.web.str_to_xml = function(s) {
|
||||||
if (window.DOMParser) {
|
if (window.DOMParser) {
|
||||||
var dp = new DOMParser();
|
var dp = new DOMParser();
|
||||||
|
|
|
@ -152,6 +152,32 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
<form id="db_duplicate" name="duplicate_db_form" style="display: none;">
|
||||||
|
<div class="oe_view_manager oe_view_manager_current">
|
||||||
|
<div class="oe_view_manager_header" style="padding: 8px;">
|
||||||
|
<div class="oe_header_row">
|
||||||
|
<h2 class="oe_view_title">
|
||||||
|
<span class="oe_view_title_text oe_breadcrumb_title">Duplicate Database</span>
|
||||||
|
</h2>
|
||||||
|
<button type="submit" class="oe_button oe_highlight db_duplicate">Duplicate</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table align="center" class="db_option_table">
|
||||||
|
<tr>
|
||||||
|
<td><label for="super_admin_pwd">Master password:</label></td>
|
||||||
|
<td><input type="password" name="super_admin_pwd" class="required" value="admin" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="db_original_name">Original database name:</label></td>
|
||||||
|
<td><input type="text" name="db_original_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_-]+$" autofocus="true"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="db_name">New database name:</label></td>
|
||||||
|
<td><input type="text" name="db_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_-]+$" /></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
<form id="db_drop" name="drop_db_form" style="display: none; ">
|
<form id="db_drop" name="drop_db_form" style="display: none; ">
|
||||||
<div class="oe_view_manager oe_view_manager_current">
|
<div class="oe_view_manager oe_view_manager_current">
|
||||||
<div class="oe_view_manager_header" style="padding: 8px;">
|
<div class="oe_view_manager_header" style="padding: 8px;">
|
||||||
|
@ -279,6 +305,7 @@
|
||||||
<div class="oe_secondary_menu_section">Database Management</div>
|
<div class="oe_secondary_menu_section">Database Management</div>
|
||||||
<ul class="oe_secondary_submenu">
|
<ul class="oe_secondary_submenu">
|
||||||
<li><a href="#db_create">Create</a></li>
|
<li><a href="#db_create">Create</a></li>
|
||||||
|
<li><a href="#db_duplicate">Duplicate</a></li>
|
||||||
<li><a href="#db_drop">Drop</a></li>
|
<li><a href="#db_drop">Drop</a></li>
|
||||||
<li><a href="#db_backup">Backup</a></li>
|
<li><a href="#db_backup">Backup</a></li>
|
||||||
<li><a href="#db_restore">Restore</a></li>
|
<li><a href="#db_restore">Restore</a></li>
|
||||||
|
@ -1182,6 +1209,7 @@
|
||||||
</t>
|
</t>
|
||||||
<t t-name="FieldBinaryFileUploader.files">
|
<t t-name="FieldBinaryFileUploader.files">
|
||||||
<div class="oe_attachments">
|
<div class="oe_attachments">
|
||||||
|
<t t-if="widget.get('value')">
|
||||||
<t t-if="!widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
|
<t t-if="!widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
|
||||||
<div class="oe_attachment">
|
<div class="oe_attachment">
|
||||||
<span t-if="(file.upload or file.percent_loaded<100)" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}" t-attf-name="{file.name || file.filename}">
|
<span t-if="(file.upload or file.percent_loaded<100)" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}" t-attf-name="{file.name || file.filename}">
|
||||||
|
@ -1203,6 +1231,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
</t>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
<t t-name="FieldBinaryFileUploader">
|
<t t-name="FieldBinaryFileUploader">
|
||||||
|
@ -1515,14 +1544,16 @@
|
||||||
<h4>Save current filter</h4>
|
<h4>Save current filter</h4>
|
||||||
<form>
|
<form>
|
||||||
<p><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
|
<p><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
|
||||||
<p><input id="oe_searchview_custom_public" type="checkbox"/>
|
<p>
|
||||||
<label for="oe_searchview_custom_public">Share with all users</label></p>
|
<input id="oe_searchview_custom_public" type="checkbox"/>
|
||||||
|
<label for="oe_searchview_custom_public">Share with all users</label>
|
||||||
|
<input id="oe_searchview_custom_default" type="checkbox"/>
|
||||||
|
<label for="oe_searchview_custom_default">Use by default</label>
|
||||||
|
</p>
|
||||||
<button>Save</button>
|
<button>Save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div t-name="SearchView.advanced" class="oe_searchview_advanced">
|
<div t-name="SearchView.advanced" class="oe_searchview_advanced">
|
||||||
|
|
|
@ -1,32 +1,7 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('Widget.proxy', {
|
||||||
var $fix = $('#qunit-fixture');
|
dependencies: ['web.corelib']
|
||||||
var mod = {
|
}, function (test) {
|
||||||
setup: function () {
|
test('(String)', function (instance) {
|
||||||
instance = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(instance);
|
|
||||||
|
|
||||||
instance.web.qweb = new QWeb2.Engine();
|
|
||||||
instance.web.qweb.add_template(
|
|
||||||
'<no>' +
|
|
||||||
'<t t-name="test.widget.template">' +
|
|
||||||
'<ol>' +
|
|
||||||
'<li t-foreach="5" t-as="counter" ' +
|
|
||||||
't-attf-class="class-#{counter}">' +
|
|
||||||
'<input/>' +
|
|
||||||
'<t t-esc="counter"/>' +
|
|
||||||
'</li>' +
|
|
||||||
'</ol>' +
|
|
||||||
'</t>' +
|
|
||||||
'<t t-name="test.widget.template-value">' +
|
|
||||||
'<p><t t-esc="widget.value"/></p>' +
|
|
||||||
'</t>' +
|
|
||||||
'</no>');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var instance;
|
|
||||||
|
|
||||||
module('Widget.proxy', mod);
|
|
||||||
test('(String)', function () {
|
|
||||||
var W = instance.web.Widget.extend({
|
var W = instance.web.Widget.extend({
|
||||||
exec: function () {
|
exec: function () {
|
||||||
this.executed = true;
|
this.executed = true;
|
||||||
|
@ -37,7 +12,7 @@ $(document).ready(function () {
|
||||||
fn();
|
fn();
|
||||||
ok(w.executed, 'should execute the named method in the right context');
|
ok(w.executed, 'should execute the named method in the right context');
|
||||||
});
|
});
|
||||||
test('(String)(*args)', function () {
|
test('(String)(*args)', function (instance) {
|
||||||
var W = instance.web.Widget.extend({
|
var W = instance.web.Widget.extend({
|
||||||
exec: function (arg) {
|
exec: function (arg) {
|
||||||
this.executed = arg;
|
this.executed = arg;
|
||||||
|
@ -49,7 +24,7 @@ $(document).ready(function () {
|
||||||
ok(w.executed, "should execute the named method in the right context");
|
ok(w.executed, "should execute the named method in the right context");
|
||||||
equal(w.executed, 42, "should be passed the proxy's arguments");
|
equal(w.executed, 42, "should be passed the proxy's arguments");
|
||||||
});
|
});
|
||||||
test('(String), include', function () {
|
test('(String), include', function (instance) {
|
||||||
// the proxy function should handle methods being changed on the class
|
// the proxy function should handle methods being changed on the class
|
||||||
// and should always proxy "by name", to the most recent one
|
// and should always proxy "by name", to the most recent one
|
||||||
var W = instance.web.Widget.extend({
|
var W = instance.web.Widget.extend({
|
||||||
|
@ -67,23 +42,43 @@ $(document).ready(function () {
|
||||||
equal(w.executed, 2, "should be lazily resolved");
|
equal(w.executed, 2, "should be lazily resolved");
|
||||||
});
|
});
|
||||||
|
|
||||||
test('(Function)', function () {
|
test('(Function)', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({ }));
|
var w = new (instance.web.Widget.extend({ }));
|
||||||
|
|
||||||
var fn = w.proxy(function () { this.executed = true; });
|
var fn = w.proxy(function () { this.executed = true; });
|
||||||
fn();
|
fn();
|
||||||
ok(w.executed, "should set the function's context (like Function#bind)");
|
ok(w.executed, "should set the function's context (like Function#bind)");
|
||||||
});
|
});
|
||||||
test('(Function)(*args)', function () {
|
test('(Function)(*args)', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({ }));
|
var w = new (instance.web.Widget.extend({ }));
|
||||||
|
|
||||||
var fn = w.proxy(function (arg) { this.executed = arg; });
|
var fn = w.proxy(function (arg) { this.executed = arg; });
|
||||||
fn(42);
|
fn(42);
|
||||||
equal(w.executed, 42, "should be passed the proxy's arguments");
|
equal(w.executed, 42, "should be passed the proxy's arguments");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
module('Widget.renderElement', mod);
|
openerp.testing.section('Widget.renderElement', {
|
||||||
test('no template, default', function () {
|
dependencies: ['web.corelib'],
|
||||||
|
setup: function (instance) {
|
||||||
|
instance.web.qweb = new QWeb2.Engine();
|
||||||
|
instance.web.qweb.add_template(
|
||||||
|
'<no>' +
|
||||||
|
'<t t-name="test.widget.template">' +
|
||||||
|
'<ol>' +
|
||||||
|
'<li t-foreach="5" t-as="counter" ' +
|
||||||
|
't-attf-class="class-#{counter}">' +
|
||||||
|
'<input/>' +
|
||||||
|
'<t t-esc="counter"/>' +
|
||||||
|
'</li>' +
|
||||||
|
'</ol>' +
|
||||||
|
'</t>' +
|
||||||
|
'<t t-name="test.widget.template-value">' +
|
||||||
|
'<p><t t-esc="widget.value"/></p>' +
|
||||||
|
'</t>' +
|
||||||
|
'</no>');
|
||||||
|
}
|
||||||
|
}, function (test) {
|
||||||
|
test('no template, default', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({ }));
|
var w = new (instance.web.Widget.extend({ }));
|
||||||
|
|
||||||
var $original = w.$el;
|
var $original = w.$el;
|
||||||
|
@ -98,7 +93,7 @@ $(document).ready(function () {
|
||||||
equal(w.el.attributes.length, 0, "should not have generated any attribute");
|
equal(w.el.attributes.length, 0, "should not have generated any attribute");
|
||||||
ok(_.isEmpty(w.$el.html(), "should not have generated any content"));
|
ok(_.isEmpty(w.$el.html(), "should not have generated any content"));
|
||||||
});
|
});
|
||||||
test('no template, custom tag', function () {
|
test('no template, custom tag', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
tagName: 'ul'
|
tagName: 'ul'
|
||||||
}));
|
}));
|
||||||
|
@ -106,7 +101,7 @@ $(document).ready(function () {
|
||||||
|
|
||||||
equal(w.el.nodeName, 'UL', "should have generated the custom element tag");
|
equal(w.el.nodeName, 'UL', "should have generated the custom element tag");
|
||||||
});
|
});
|
||||||
test('no template, @id', function () {
|
test('no template, @id', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
id: 'foo'
|
id: 'foo'
|
||||||
}));
|
}));
|
||||||
|
@ -116,7 +111,7 @@ $(document).ready(function () {
|
||||||
equal(w.$el.attr('id'), 'foo', "should have generated the id attribute");
|
equal(w.$el.attr('id'), 'foo', "should have generated the id attribute");
|
||||||
equal(w.el.id, 'foo', "should also be available via property");
|
equal(w.el.id, 'foo', "should also be available via property");
|
||||||
});
|
});
|
||||||
test('no template, @className', function () {
|
test('no template, @className', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
className: 'oe_some_class'
|
className: 'oe_some_class'
|
||||||
}));
|
}));
|
||||||
|
@ -125,7 +120,7 @@ $(document).ready(function () {
|
||||||
equal(w.el.className, 'oe_some_class', "should have the right property");
|
equal(w.el.className, 'oe_some_class', "should have the right property");
|
||||||
equal(w.$el.attr('class'), 'oe_some_class', "should have the right attribute");
|
equal(w.$el.attr('class'), 'oe_some_class', "should have the right attribute");
|
||||||
});
|
});
|
||||||
test('no template, bunch of attributes', function () {
|
test('no template, bunch of attributes', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
attributes: {
|
attributes: {
|
||||||
'id': 'some_id',
|
'id': 'some_id',
|
||||||
|
@ -152,7 +147,7 @@ $(document).ready(function () {
|
||||||
equal(w.$el.attr('spoiler'), 'snape kills dumbledore');
|
equal(w.$el.attr('spoiler'), 'snape kills dumbledore');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('template', function () {
|
test('template', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
template: 'test.widget.template'
|
template: 'test.widget.template'
|
||||||
}));
|
}));
|
||||||
|
@ -162,9 +157,41 @@ $(document).ready(function () {
|
||||||
equal(w.$el.children().length, 5);
|
equal(w.$el.children().length, 5);
|
||||||
equal(w.el.textContent, '01234');
|
equal(w.el.textContent, '01234');
|
||||||
});
|
});
|
||||||
|
test('repeated', { asserts: 4 }, function (instance, $fix) {
|
||||||
module('Widget.$', mod);
|
var w = new (instance.web.Widget.extend({
|
||||||
test('basic-alias', function () {
|
template: 'test.widget.template-value'
|
||||||
|
}));
|
||||||
|
w.value = 42;
|
||||||
|
return w.appendTo($fix)
|
||||||
|
.done(function () {
|
||||||
|
equal($fix.find('p').text(), '42', "DOM fixture should contain initial value");
|
||||||
|
equal(w.$el.text(), '42', "should set initial value");
|
||||||
|
w.value = 36;
|
||||||
|
w.renderElement();
|
||||||
|
equal($fix.find('p').text(), '36', "DOM fixture should use new value");
|
||||||
|
equal(w.$el.text(), '36', "should set new value");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
openerp.testing.section('Widget.$', {
|
||||||
|
dependencies: ['web.corelib'],
|
||||||
|
setup: function (instance) {
|
||||||
|
instance.web.qweb = new QWeb2.Engine();
|
||||||
|
instance.web.qweb.add_template(
|
||||||
|
'<no>' +
|
||||||
|
'<t t-name="test.widget.template">' +
|
||||||
|
'<ol>' +
|
||||||
|
'<li t-foreach="5" t-as="counter" ' +
|
||||||
|
't-attf-class="class-#{counter}">' +
|
||||||
|
'<input/>' +
|
||||||
|
'<t t-esc="counter"/>' +
|
||||||
|
'</li>' +
|
||||||
|
'</ol>' +
|
||||||
|
'</t>' +
|
||||||
|
'</no>');
|
||||||
|
}
|
||||||
|
}, function (test) {
|
||||||
|
test('basic-alias', function (instance) {
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
template: 'test.widget.template'
|
template: 'test.widget.template'
|
||||||
}));
|
}));
|
||||||
|
@ -173,9 +200,26 @@ $(document).ready(function () {
|
||||||
ok(w.$('li:eq(3)').is(w.$el.find('li:eq(3)')),
|
ok(w.$('li:eq(3)').is(w.$el.find('li:eq(3)')),
|
||||||
"should do the same thing as calling find on the widget root");
|
"should do the same thing as calling find on the widget root");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
module('Widget.events', mod);
|
openerp.testing.section('Widget.events', {
|
||||||
test('delegate', function () {
|
dependencies: ['web.corelib'],
|
||||||
|
setup: function (instance) {
|
||||||
|
instance.web.qweb = new QWeb2.Engine();
|
||||||
|
instance.web.qweb.add_template(
|
||||||
|
'<no>' +
|
||||||
|
'<t t-name="test.widget.template">' +
|
||||||
|
'<ol>' +
|
||||||
|
'<li t-foreach="5" t-as="counter" ' +
|
||||||
|
't-attf-class="class-#{counter}">' +
|
||||||
|
'<input/>' +
|
||||||
|
'<t t-esc="counter"/>' +
|
||||||
|
'</li>' +
|
||||||
|
'</ol>' +
|
||||||
|
'</t>' +
|
||||||
|
'</no>');
|
||||||
|
}
|
||||||
|
}, function (test) {
|
||||||
|
test('delegate', function (instance) {
|
||||||
var a = [];
|
var a = [];
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
template: 'test.widget.template',
|
template: 'test.widget.template',
|
||||||
|
@ -199,7 +243,7 @@ $(document).ready(function () {
|
||||||
ok(a[i], "should pass test " + i);
|
ok(a[i], "should pass test " + i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
test('undelegate', function () {
|
test('undelegate', function (instance) {
|
||||||
var clicked = false, newclicked = false;
|
var clicked = false, newclicked = false;
|
||||||
var w = new (instance.web.Widget.extend({
|
var w = new (instance.web.Widget.extend({
|
||||||
template: 'test.widget.template',
|
template: 'test.widget.template',
|
||||||
|
@ -218,22 +262,4 @@ $(document).ready(function () {
|
||||||
ok(!clicked, "undelegate should unbind events delegated");
|
ok(!clicked, "undelegate should unbind events delegated");
|
||||||
ok(newclicked, "undelegate should only unbind events it created");
|
ok(newclicked, "undelegate should only unbind events it created");
|
||||||
});
|
});
|
||||||
|
|
||||||
module('Widget.renderElement', mod);
|
|
||||||
asyncTest('repeated', 4, function () {
|
|
||||||
var w = new (instance.web.Widget.extend({
|
|
||||||
template: 'test.widget.template-value'
|
|
||||||
}));
|
|
||||||
w.value = 42;
|
|
||||||
w.appendTo($fix)
|
|
||||||
.always(start)
|
|
||||||
.done(function () {
|
|
||||||
equal($fix.find('p').text(), '42', "DOM fixture should contain initial value");
|
|
||||||
equal(w.$el.text(), '42', "should set initial value");
|
|
||||||
w.value = 36;
|
|
||||||
w.renderElement();
|
|
||||||
equal($fix.find('p').text(), '36', "DOM fixture should use new value");
|
|
||||||
equal(w.$el.text(), '36', "should set new value");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,30 +1,25 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('class', {
|
||||||
var openerp;
|
dependencies: ['web.corelib']
|
||||||
module('web-class', {
|
}, function (test) {
|
||||||
setup: function () {
|
test('Basic class creation', function (instance) {
|
||||||
openerp = window.openerp.init([]);
|
var C = instance.web.Class.extend({
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test('Basic class creation', function () {
|
|
||||||
var C = openerp.web.Class.extend({
|
|
||||||
foo: function () {
|
foo: function () {
|
||||||
return this.somevar;
|
return this.somevar;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var instance = new C();
|
var i = new C();
|
||||||
instance.somevar = 3;
|
i.somevar = 3;
|
||||||
|
|
||||||
ok(instance instanceof C);
|
ok(i instanceof C);
|
||||||
strictEqual(instance.foo(), 3);
|
strictEqual(i.foo(), 3);
|
||||||
});
|
});
|
||||||
test('Class initialization', function () {
|
test('Class initialization', function (instance) {
|
||||||
var C1 = openerp.web.Class.extend({
|
var C1 = instance.web.Class.extend({
|
||||||
init: function () {
|
init: function () {
|
||||||
this.foo = 3;
|
this.foo = 3;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var C2 = openerp.web.Class.extend({
|
var C2 = instance.web.Class.extend({
|
||||||
init: function (arg) {
|
init: function (arg) {
|
||||||
this.foo = arg;
|
this.foo = arg;
|
||||||
}
|
}
|
||||||
|
@ -36,8 +31,8 @@ $(document).ready(function () {
|
||||||
strictEqual(i1.foo, 3);
|
strictEqual(i1.foo, 3);
|
||||||
strictEqual(i2.foo, 42);
|
strictEqual(i2.foo, 42);
|
||||||
});
|
});
|
||||||
test('Inheritance', function () {
|
test('Inheritance', function (instance) {
|
||||||
var C0 = openerp.web.Class.extend({
|
var C0 = instance.web.Class.extend({
|
||||||
foo: function () {
|
foo: function () {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -57,8 +52,8 @@ $(document).ready(function () {
|
||||||
strictEqual(new C1().foo(), 2);
|
strictEqual(new C1().foo(), 2);
|
||||||
strictEqual(new C2().foo(), 3);
|
strictEqual(new C2().foo(), 3);
|
||||||
});
|
});
|
||||||
test('In-place extension', function () {
|
test('In-place extension', function (instance) {
|
||||||
var C0 = openerp.web.Class.extend({
|
var C0 = instance.web.Class.extend({
|
||||||
foo: function () {
|
foo: function () {
|
||||||
return 3;
|
return 3;
|
||||||
},
|
},
|
||||||
|
@ -83,8 +78,8 @@ $(document).ready(function () {
|
||||||
strictEqual(new C0().foo(), 5);
|
strictEqual(new C0().foo(), 5);
|
||||||
strictEqual(new C0().qux(), 5);
|
strictEqual(new C0().qux(), 5);
|
||||||
});
|
});
|
||||||
test('In-place extension and inheritance', function () {
|
test('In-place extension and inheritance', function (instance) {
|
||||||
var C0 = openerp.web.Class.extend({
|
var C0 = instance.web.Class.extend({
|
||||||
foo: function () { return 1; },
|
foo: function () { return 1; },
|
||||||
bar: function () { return 1; }
|
bar: function () { return 1; }
|
||||||
});
|
});
|
||||||
|
@ -101,24 +96,24 @@ $(document).ready(function () {
|
||||||
strictEqual(new C1().foo(), 4);
|
strictEqual(new C1().foo(), 4);
|
||||||
strictEqual(new C1().bar(), 2);
|
strictEqual(new C1().bar(), 2);
|
||||||
});
|
});
|
||||||
test('In-place extensions alter existing instances', function () {
|
test('In-place extensions alter existing instances', function (instance) {
|
||||||
var C0 = openerp.web.Class.extend({
|
var C0 = instance.web.Class.extend({
|
||||||
foo: function () { return 1; },
|
foo: function () { return 1; },
|
||||||
bar: function () { return 1; }
|
bar: function () { return 1; }
|
||||||
});
|
});
|
||||||
var instance = new C0();
|
var i = new C0();
|
||||||
strictEqual(instance.foo(), 1);
|
strictEqual(i.foo(), 1);
|
||||||
strictEqual(instance.bar(), 1);
|
strictEqual(i.bar(), 1);
|
||||||
|
|
||||||
C0.include({
|
C0.include({
|
||||||
foo: function () { return 2; },
|
foo: function () { return 2; },
|
||||||
bar: function () { return 2 + this._super(); }
|
bar: function () { return 2 + this._super(); }
|
||||||
});
|
});
|
||||||
strictEqual(instance.foo(), 2);
|
strictEqual(i.foo(), 2);
|
||||||
strictEqual(instance.bar(), 3);
|
strictEqual(i.bar(), 3);
|
||||||
});
|
});
|
||||||
test('In-place extension of subclassed types', function () {
|
test('In-place extension of subclassed types', function (instance) {
|
||||||
var C0 = openerp.web.Class.extend({
|
var C0 = instance.web.Class.extend({
|
||||||
foo: function () { return 1; },
|
foo: function () { return 1; },
|
||||||
bar: function () { return 1; }
|
bar: function () { return 1; }
|
||||||
});
|
});
|
||||||
|
@ -126,13 +121,13 @@ $(document).ready(function () {
|
||||||
foo: function () { return 1 + this._super(); },
|
foo: function () { return 1 + this._super(); },
|
||||||
bar: function () { return 1 + this._super(); }
|
bar: function () { return 1 + this._super(); }
|
||||||
});
|
});
|
||||||
var instance = new C1();
|
var i = new C1();
|
||||||
strictEqual(instance.foo(), 2);
|
strictEqual(i.foo(), 2);
|
||||||
C0.include({
|
C0.include({
|
||||||
foo: function () { return 2; },
|
foo: function () { return 2; },
|
||||||
bar: function () { return 2 + this._super(); }
|
bar: function () { return 2 + this._super(); }
|
||||||
});
|
});
|
||||||
strictEqual(instance.foo(), 3);
|
strictEqual(i.foo(), 3);
|
||||||
strictEqual(instance.bar(), 4);
|
strictEqual(i.bar(), 4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('eval.contexts', {
|
||||||
var openerp;
|
dependencies: ['web.coresetup']
|
||||||
|
}, function (test) {
|
||||||
module("eval.contexts", {
|
test('context_sequences', function (instance) {
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test('context_sequences', function () {
|
|
||||||
// Context n should have base evaluation context + all of contexts
|
// Context n should have base evaluation context + all of contexts
|
||||||
// 0..n-1 in its own evaluation context
|
// 0..n-1 in its own evaluation context
|
||||||
var active_id = 4;
|
var active_id = 4;
|
||||||
var result = openerp.session.test_eval_contexts([
|
var result = instance.session.test_eval_contexts([
|
||||||
{
|
{
|
||||||
"__contexts": [
|
"__contexts": [
|
||||||
{
|
{
|
||||||
|
@ -55,8 +48,8 @@ $(document).ready(function () {
|
||||||
record_id: active_id
|
record_id: active_id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('non-literal_eval_contexts', function () {
|
test('non-literal_eval_contexts', function (instance) {
|
||||||
var result = openerp.session.test_eval_contexts([{
|
var result = instance.session.test_eval_contexts([{
|
||||||
"__ref": "compound_context",
|
"__ref": "compound_context",
|
||||||
"__contexts": [
|
"__contexts": [
|
||||||
{"__ref": "context", "__debug": "{'type':parent.type}",
|
{"__ref": "context", "__debug": "{'type':parent.type}",
|
||||||
|
@ -133,17 +126,15 @@ $(document).ready(function () {
|
||||||
}]);
|
}]);
|
||||||
deepEqual(result, {type: 'out_invoice'});
|
deepEqual(result, {type: 'out_invoice'});
|
||||||
});
|
});
|
||||||
module('eval.domains', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.testing.instanceFor('coresetup');
|
|
||||||
window.openerp.web.dates(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('current_date', function () {
|
openerp.testing.section('eval.contexts', {
|
||||||
var current_date = openerp.web.date_to_str(new Date());
|
dependencies: ['web.coresetup', 'web.dates']
|
||||||
var result = openerp.session.test_eval_domains(
|
}, function (test) {
|
||||||
|
test('current_date', function (instance) {
|
||||||
|
var current_date = instance.web.date_to_str(new Date());
|
||||||
|
var result = instance.session.test_eval_domains(
|
||||||
[[],{"__ref":"domain","__debug":"[('name','>=',current_date),('name','<=',current_date)]","__id":"5dedcfc96648"}],
|
[[],{"__ref":"domain","__debug":"[('name','>=',current_date),('name','<=',current_date)]","__id":"5dedcfc96648"}],
|
||||||
openerp.session.test_eval_get_context());
|
instance.session.test_eval_get_context());
|
||||||
deepEqual(result, [
|
deepEqual(result, [
|
||||||
['name', '>=', current_date],
|
['name', '>=', current_date],
|
||||||
['name', '<=', current_date]
|
['name', '<=', current_date]
|
||||||
|
|
|
@ -1,33 +1,21 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('compute_domain', {
|
||||||
var openerp;
|
dependencies: ['web.form']
|
||||||
module("form.widget", {
|
}, function (test) {
|
||||||
setup: function () {
|
test("basic", function (instance) {
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
window.openerp.web.form(openerp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test("compute_domain", function () {
|
|
||||||
var fields = {
|
var fields = {
|
||||||
'a': {value: 3},
|
'a': {value: 3},
|
||||||
'group_method': {value: 'line'},
|
'group_method': {value: 'line'},
|
||||||
'select1': {value: 'day'},
|
'select1': {value: 'day'},
|
||||||
'rrule_type': {value: 'monthly'}
|
'rrule_type': {value: 'monthly'}
|
||||||
};
|
};
|
||||||
ok(openerp.web.form.compute_domain(
|
ok(instance.web.form.compute_domain(
|
||||||
[['a', '=', 3]], fields));
|
[['a', '=', 3]], fields));
|
||||||
ok(openerp.web.form.compute_domain(
|
ok(instance.web.form.compute_domain(
|
||||||
[['group_method','!=','count']], fields));
|
[['group_method','!=','count']], fields));
|
||||||
ok(openerp.web.form.compute_domain(
|
ok(instance.web.form.compute_domain(
|
||||||
[['select1','=','day'], ['rrule_type','=','monthly']], fields));
|
[['select1','=','day'], ['rrule_type','=','monthly']], fields));
|
||||||
});
|
});
|
||||||
test("compute_domain or", function () {
|
test("or", function (instance) {
|
||||||
var web = {
|
var web = {
|
||||||
'section_id': {value: null},
|
'section_id': {value: null},
|
||||||
'user_id': {value: null},
|
'user_id': {value: null},
|
||||||
|
@ -38,22 +26,22 @@ $(document).ready(function () {
|
||||||
'|', ['user_id','=',3],
|
'|', ['user_id','=',3],
|
||||||
['member_ids', 'in', [3]]];
|
['member_ids', 'in', [3]]];
|
||||||
|
|
||||||
ok(openerp.web.form.compute_domain(domain, _.extend(
|
ok(instance.web.form.compute_domain(domain, _.extend(
|
||||||
{}, web, {'section_id': {value: 42}})));
|
{}, web, {'section_id': {value: 42}})));
|
||||||
ok(openerp.web.form.compute_domain(domain, _.extend(
|
ok(instance.web.form.compute_domain(domain, _.extend(
|
||||||
{}, web, {'user_id': {value: 3}})));
|
{}, web, {'user_id': {value: 3}})));
|
||||||
|
|
||||||
ok(openerp.web.form.compute_domain(domain, _.extend(
|
ok(instance.web.form.compute_domain(domain, _.extend(
|
||||||
{}, web, {'member_ids': {value: 3}})));
|
{}, web, {'member_ids': {value: 3}})));
|
||||||
});
|
});
|
||||||
test("compute_domain not", function () {
|
test("not", function (instance) {
|
||||||
var fields = {
|
var fields = {
|
||||||
'a': {value: 5},
|
'a': {value: 5},
|
||||||
'group_method': {value: 'line'}
|
'group_method': {value: 'line'}
|
||||||
};
|
};
|
||||||
ok(openerp.web.form.compute_domain(
|
ok(instance.web.form.compute_domain(
|
||||||
['!', ['a', '=', 3]], fields));
|
['!', ['a', '=', 3]], fields));
|
||||||
ok(openerp.web.form.compute_domain(
|
ok(instance.web.form.compute_domain(
|
||||||
['!', ['group_method','=','count']], fields));
|
['!', ['group_method','=','count']], fields));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('server-formats', {
|
||||||
var openerp;
|
dependencies: ['web.coresetup', 'web.dates']
|
||||||
|
}, function (test) {
|
||||||
module('server-formats', {
|
test('Parse server datetime', function (instance) {
|
||||||
setup: function () {
|
var date = instance.web.str_to_datetime("2009-05-04 12:34:23");
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.dates(openerp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test('Parse server datetime', function () {
|
|
||||||
var date = openerp.web.str_to_datetime("2009-05-04 12:34:23");
|
|
||||||
deepEqual(
|
deepEqual(
|
||||||
[date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
|
[date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
|
||||||
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()],
|
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()],
|
||||||
|
@ -20,92 +12,86 @@ $(document).ready(function () {
|
||||||
date.getHours(), date.getMinutes(), date.getSeconds()],
|
date.getHours(), date.getMinutes(), date.getSeconds()],
|
||||||
[2009, 5 - 1, 4, 12 - (date.getTimezoneOffset() / 60), 34, 23]);
|
[2009, 5 - 1, 4, 12 - (date.getTimezoneOffset() / 60), 34, 23]);
|
||||||
|
|
||||||
var date2 = openerp.web.str_to_datetime('2011-12-10 00:00:00');
|
var date2 = instance.web.str_to_datetime('2011-12-10 00:00:00');
|
||||||
deepEqual(
|
deepEqual(
|
||||||
[date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate(),
|
[date2.getUTCFullYear(), date2.getUTCMonth(), date2.getUTCDate(),
|
||||||
date2.getUTCHours(), date2.getUTCMinutes(), date2.getUTCSeconds()],
|
date2.getUTCHours(), date2.getUTCMinutes(), date2.getUTCSeconds()],
|
||||||
[2011, 12 - 1, 10, 0, 0, 0]);
|
[2011, 12 - 1, 10, 0, 0, 0]);
|
||||||
});
|
});
|
||||||
test('Parse server date', function () {
|
test('Parse server date', function (instance) {
|
||||||
var date = openerp.web.str_to_date("2009-05-04");
|
var date = instance.web.str_to_date("2009-05-04");
|
||||||
deepEqual(
|
deepEqual(
|
||||||
[date.getFullYear(), date.getMonth(), date.getDate()],
|
[date.getFullYear(), date.getMonth(), date.getDate()],
|
||||||
[2009, 5 - 1, 4]);
|
[2009, 5 - 1, 4]);
|
||||||
});
|
});
|
||||||
test('Parse server time', function () {
|
test('Parse server time', function (instance) {
|
||||||
var date = openerp.web.str_to_time("12:34:23");
|
var date = instance.web.str_to_time("12:34:23");
|
||||||
deepEqual(
|
deepEqual(
|
||||||
[date.getHours(), date.getMinutes(), date.getSeconds()],
|
[date.getHours(), date.getMinutes(), date.getSeconds()],
|
||||||
[12, 34, 23]);
|
[12, 34, 23]);
|
||||||
});
|
});
|
||||||
|
|
||||||
module('web-formats', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.dates(openerp);
|
|
||||||
window.openerp.web.formats(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test("format_datetime", function () {
|
openerp.testing.section('web-formats', {
|
||||||
var date = openerp.web.str_to_datetime("2009-05-04 12:34:23");
|
dependencies: ['web.formats']
|
||||||
var str = openerp.web.format_value(date, {type:"datetime"});
|
}, function (test) {
|
||||||
|
test("format_datetime", function (instance) {
|
||||||
|
var date = instance.web.str_to_datetime("2009-05-04 12:34:23");
|
||||||
|
var str = instance.web.format_value(date, {type:"datetime"});
|
||||||
equal(str, date.toString("MM/dd/yyyy HH:mm:ss"));
|
equal(str, date.toString("MM/dd/yyyy HH:mm:ss"));
|
||||||
});
|
});
|
||||||
test("format_date", function () {
|
test("format_date", function (instance) {
|
||||||
var date = openerp.web.str_to_datetime("2009-05-04 12:34:23");
|
var date = instance.web.str_to_datetime("2009-05-04 12:34:23");
|
||||||
var str = openerp.web.format_value(date, {type:"date"});
|
var str = instance.web.format_value(date, {type:"date"});
|
||||||
equal(str, date.toString("MM/dd/yyyy"));
|
equal(str, date.toString("MM/dd/yyyy"));
|
||||||
});
|
});
|
||||||
test("format_time", function () {
|
test("format_time", function (instance) {
|
||||||
var date = openerp.web.str_to_datetime("2009-05-04 12:34:23");
|
var date = instance.web.str_to_datetime("2009-05-04 12:34:23");
|
||||||
var str = openerp.web.format_value(date, {type:"time"});
|
var str = instance.web.format_value(date, {type:"time"});
|
||||||
equal(str, date.toString("HH:mm:ss"));
|
equal(str, date.toString("HH:mm:ss"));
|
||||||
});
|
});
|
||||||
test("format_float_time", function () {
|
test("format_float_time", function (instance) {
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(1.0, {type:'float', widget:'float_time'}),
|
instance.web.format_value(1.0, {type:'float', widget:'float_time'}),
|
||||||
'01:00');
|
'01:00');
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(0.9853, {type:'float', widget:'float_time'}),
|
instance.web.format_value(0.9853, {type:'float', widget:'float_time'}),
|
||||||
'00:59');
|
'00:59');
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(0.0085, {type:'float', widget:'float_time'}),
|
instance.web.format_value(0.0085, {type:'float', widget:'float_time'}),
|
||||||
'00:01');
|
'00:01');
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(-1.0, {type:'float', widget:'float_time'}),
|
instance.web.format_value(-1.0, {type:'float', widget:'float_time'}),
|
||||||
'-01:00');
|
'-01:00');
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(-0.9853, {type:'float', widget:'float_time'}),
|
instance.web.format_value(-0.9853, {type:'float', widget:'float_time'}),
|
||||||
'-00:59');
|
'-00:59');
|
||||||
strictEqual(
|
strictEqual(
|
||||||
openerp.web.format_value(-0.0085, {type:'float', widget:'float_time'}),
|
instance.web.format_value(-0.0085, {type:'float', widget:'float_time'}),
|
||||||
'-00:01');
|
'-00:01');
|
||||||
});
|
});
|
||||||
test("format_float", function () {
|
test("format_float", function (instance) {
|
||||||
var fl = 12.1234;
|
var fl = 12.1234;
|
||||||
var str = openerp.web.format_value(fl, {type:"float"});
|
var str = instance.web.format_value(fl, {type:"float"});
|
||||||
equal(str, "12.12");
|
equal(str, "12.12");
|
||||||
equal(openerp.web.format_value(12.02, {type: 'float'}),
|
equal(instance.web.format_value(12.02, {type: 'float'}),
|
||||||
'12.02');
|
'12.02');
|
||||||
equal(openerp.web.format_value(0.0002, {type: 'float', digits: [1, 3]}),
|
equal(instance.web.format_value(0.0002, {type: 'float', digits: [1, 3]}),
|
||||||
'0.000');
|
'0.000');
|
||||||
equal(openerp.web.format_value(0.0002, {type: 'float', digits: [1, 4]}),
|
equal(instance.web.format_value(0.0002, {type: 'float', digits: [1, 4]}),
|
||||||
'0.0002');
|
'0.0002');
|
||||||
equal(openerp.web.format_value(0.0002, {type: 'float', digits: [1, 6]}),
|
equal(instance.web.format_value(0.0002, {type: 'float', digits: [1, 6]}),
|
||||||
'0.000200');
|
'0.000200');
|
||||||
equal(openerp.web.format_value(1, {type: 'float', digits: [1, 6]}),
|
equal(instance.web.format_value(1, {type: 'float', digits: [1, 6]}),
|
||||||
'1.000000');
|
'1.000000');
|
||||||
equal(openerp.web.format_value(1, {type: 'float'}),
|
equal(instance.web.format_value(1, {type: 'float'}),
|
||||||
'1.00');
|
'1.00');
|
||||||
equal(openerp.web.format_value(-11.25, {type: 'float'}),
|
equal(instance.web.format_value(-11.25, {type: 'float'}),
|
||||||
"-11.25");
|
"-11.25");
|
||||||
openerp.web._t.database.parameters.grouping = [1, 2, -1];
|
instance.web._t.database.parameters.grouping = [1, 2, -1];
|
||||||
equal(openerp.web.format_value(1111111.25, {type: 'float'}),
|
equal(instance.web.format_value(1111111.25, {type: 'float'}),
|
||||||
"1111,11,1.25");
|
"1111,11,1.25");
|
||||||
openerp.web._t.database.parameters.grouping = [1, 0];
|
instance.web._t.database.parameters.grouping = [1, 0];
|
||||||
equal(openerp.web.format_value(-11.25, {type: 'float'}),
|
equal(instance.web.format_value(-11.25, {type: 'float'}),
|
||||||
"-1,1.25");
|
"-1,1.25");
|
||||||
});
|
});
|
||||||
// test("parse_datetime", function () {
|
// test("parse_datetime", function () {
|
||||||
|
@ -123,29 +109,29 @@ $(document).ready(function () {
|
||||||
// var res = openerp.web.parse_value(val.toString("HH:mm:ss"), {type:"time"});
|
// var res = openerp.web.parse_value(val.toString("HH:mm:ss"), {type:"time"});
|
||||||
// equal(val.toString("HH:mm:ss"), res.toString("HH:mm:ss"));
|
// equal(val.toString("HH:mm:ss"), res.toString("HH:mm:ss"));
|
||||||
// });
|
// });
|
||||||
test('parse_integer', function () {
|
test('parse_integer', function (instance) {
|
||||||
var val = openerp.web.parse_value('123,456', {type: 'integer'});
|
var val = instance.web.parse_value('123,456', {type: 'integer'});
|
||||||
equal(val, 123456);
|
equal(val, 123456);
|
||||||
openerp.web._t.database.parameters.thousands_sep = '|';
|
instance.web._t.database.parameters.thousands_sep = '|';
|
||||||
var val2 = openerp.web.parse_value('123|456', {type: 'integer'});
|
var val2 = instance.web.parse_value('123|456', {type: 'integer'});
|
||||||
equal(val2, 123456);
|
equal(val2, 123456);
|
||||||
});
|
});
|
||||||
test("parse_float", function () {
|
test("parse_float", function (instance) {
|
||||||
var str = "134,112.1234";
|
var str = "134,112.1234";
|
||||||
var val = openerp.web.parse_value(str, {type:"float"});
|
var val = instance.web.parse_value(str, {type:"float"});
|
||||||
equal(val, 134112.1234);
|
equal(val, 134112.1234);
|
||||||
var str = "-134,112.1234";
|
var str = "-134,112.1234";
|
||||||
var val = openerp.web.parse_value(str, {type:"float"});
|
var val = instance.web.parse_value(str, {type:"float"});
|
||||||
equal(val, -134112.1234);
|
equal(val, -134112.1234);
|
||||||
_.extend(openerp.web._t.database.parameters, {
|
_.extend(instance.web._t.database.parameters, {
|
||||||
decimal_point: ',',
|
decimal_point: ',',
|
||||||
thousands_sep: '.'
|
thousands_sep: '.'
|
||||||
});
|
});
|
||||||
var val3 = openerp.web.parse_value('123.456,789', {type: 'float'});
|
var val3 = instance.web.parse_value('123.456,789', {type: 'float'});
|
||||||
equal(val3, 123456.789);
|
equal(val3, 123456.789);
|
||||||
});
|
});
|
||||||
test('intersperse', function () {
|
test('intersperse', function (instance) {
|
||||||
var g = openerp.web.intersperse;
|
var g = instance.web.intersperse;
|
||||||
equal(g("", []), "");
|
equal(g("", []), "");
|
||||||
equal(g("0", []), "0");
|
equal(g("0", []), "0");
|
||||||
equal(g("012", []), "012");
|
equal(g("012", []), "012");
|
||||||
|
@ -176,60 +162,61 @@ $(document).ready(function () {
|
||||||
equal(g("12345678", [3,3,3,3], '.'), '12.345.678');
|
equal(g("12345678", [3,3,3,3], '.'), '12.345.678');
|
||||||
equal(g("12345678", [3,0], '.'), '12.345.678');
|
equal(g("12345678", [3,0], '.'), '12.345.678');
|
||||||
});
|
});
|
||||||
test('format_integer', function () {
|
test('format_integer', function (instance) {
|
||||||
openerp.web._t.database.parameters.grouping = [3, 3, 3, 3];
|
instance.web._t.database.parameters.grouping = [3, 3, 3, 3];
|
||||||
equal(openerp.web.format_value(1000000, {type: 'integer'}),
|
equal(instance.web.format_value(1000000, {type: 'integer'}),
|
||||||
'1,000,000');
|
'1,000,000');
|
||||||
openerp.web._t.database.parameters.grouping = [3, 2, -1];
|
instance.web._t.database.parameters.grouping = [3, 2, -1];
|
||||||
equal(openerp.web.format_value(106500, {type: 'integer'}),
|
equal(instance.web.format_value(106500, {type: 'integer'}),
|
||||||
'1,06,500');
|
'1,06,500');
|
||||||
openerp.web._t.database.parameters.grouping = [1, 2, -1];
|
instance.web._t.database.parameters.grouping = [1, 2, -1];
|
||||||
equal(openerp.web.format_value(106500, {type: 'integer'}),
|
equal(instance.web.format_value(106500, {type: 'integer'}),
|
||||||
'106,50,0');
|
'106,50,0');
|
||||||
});
|
});
|
||||||
test('format_float', function () {
|
test('format_float', function (instance) {
|
||||||
openerp.web._t.database.parameters.grouping = [3, 3, 3, 3];
|
instance.web._t.database.parameters.grouping = [3, 3, 3, 3];
|
||||||
equal(openerp.web.format_value(1000000, {type: 'float'}),
|
equal(instance.web.format_value(1000000, {type: 'float'}),
|
||||||
'1,000,000.00');
|
'1,000,000.00');
|
||||||
openerp.web._t.database.parameters.grouping = [3, 2, -1];
|
instance.web._t.database.parameters.grouping = [3, 2, -1];
|
||||||
equal(openerp.web.format_value(106500, {type: 'float'}),
|
equal(instance.web.format_value(106500, {type: 'float'}),
|
||||||
'1,06,500.00');
|
'1,06,500.00');
|
||||||
openerp.web._t.database.parameters.grouping = [1, 2, -1];
|
instance.web._t.database.parameters.grouping = [1, 2, -1];
|
||||||
equal(openerp.web.format_value(106500, {type: 'float'}),
|
equal(instance.web.format_value(106500, {type: 'float'}),
|
||||||
'106,50,0.00');
|
'106,50,0.00');
|
||||||
|
|
||||||
_.extend(openerp.web._t.database.parameters, {
|
_.extend(instance.web._t.database.parameters, {
|
||||||
grouping: [3, 0],
|
grouping: [3, 0],
|
||||||
decimal_point: ',',
|
decimal_point: ',',
|
||||||
thousands_sep: '.'
|
thousands_sep: '.'
|
||||||
});
|
});
|
||||||
equal(openerp.web.format_value(6000, {type: 'float'}),
|
equal(instance.web.format_value(6000, {type: 'float'}),
|
||||||
'6.000,00');
|
'6.000,00');
|
||||||
});
|
});
|
||||||
module('custom-date-formats', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.dates(openerp);
|
|
||||||
window.openerp.web.formats(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('format stripper', function () {
|
openerp.testing.section('web-formats', {
|
||||||
strictEqual(openerp.web.strip_raw_chars('%a, %Y %b %d'), '%a, %Y %b %d');
|
dependencies: ['web.formats']
|
||||||
strictEqual(openerp.web.strip_raw_chars('%a, %Y.eko %bren %da'), '%a, %Y. %b %d');
|
}, function (test) {
|
||||||
|
test('format stripper', function (instance) {
|
||||||
|
strictEqual(instance.web.strip_raw_chars('%a, %Y %b %d'),
|
||||||
|
'%a, %Y %b %d');
|
||||||
|
strictEqual(instance.web.strip_raw_chars('%a, %Y.eko %bren %da'),
|
||||||
|
'%a, %Y. %b %d');
|
||||||
});
|
});
|
||||||
test('ES date format', function () {
|
test('ES date format', function (instance) {
|
||||||
openerp.web._t.database.parameters.date_format = '%a, %Y %b %d';
|
instance.web._t.database.parameters.date_format = '%a, %Y %b %d';
|
||||||
var date = openerp.web.str_to_date("2009-05-04");
|
var date = instance.web.str_to_date("2009-05-04");
|
||||||
strictEqual(openerp.web.format_value(date, {type:"date"}), 'Mon, 2009 May 04');
|
strictEqual(instance.web.format_value(date, {type:"date"}),
|
||||||
strictEqual(openerp.web.parse_value('Mon, 2009 May 04', {type: 'date'}), '2009-05-04');
|
'Mon, 2009 May 04');
|
||||||
|
strictEqual(instance.web.parse_value('Mon, 2009 May 04', {type: 'date'}),
|
||||||
|
'2009-05-04');
|
||||||
});
|
});
|
||||||
test('extended ES date format', function () {
|
test('extended ES date format', function (instance) {
|
||||||
openerp.web._t.database.parameters.date_format = '%a, %Y.eko %bren %da';
|
instance.web._t.database.parameters.date_format = '%a, %Y.eko %bren %da';
|
||||||
var date = openerp.web.str_to_date("2009-05-04");
|
var date = instance.web.str_to_date("2009-05-04");
|
||||||
strictEqual(openerp.web.format_value(date, {type:"date"}), 'Mon, 2009. May 04');
|
strictEqual(instance.web.format_value(date, {type:"date"}),
|
||||||
strictEqual(openerp.web.parse_value('Mon, 2009. May 04', {type: 'date'}), '2009-05-04');
|
'Mon, 2009. May 04');
|
||||||
|
strictEqual(instance.web.parse_value('Mon, 2009. May 04', {type: 'date'}),
|
||||||
|
'2009-05-04');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('editor', {
|
||||||
var $fix = $('#qunit-fixture');
|
dependencies: ['web.list_editable'],
|
||||||
|
rpc: 'mock',
|
||||||
var instance;
|
templates: true,
|
||||||
var baseSetup = function () {
|
setup: function (instance, $s, mock) {
|
||||||
instance = openerp.testing.instanceFor('list_editable');
|
mock('test.model:create', function () {
|
||||||
|
return 42;
|
||||||
openerp.testing.loadTemplate(instance);
|
});
|
||||||
|
}
|
||||||
openerp.testing.mockifyRPC(instance);
|
}, function (test) {
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {String} name
|
* @param {String} name
|
||||||
|
@ -30,7 +27,7 @@ $(document).ready(function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array} fields
|
* @param {Array} [fields]
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
function makeFormView(fields) {
|
function makeFormView(fields) {
|
||||||
|
@ -67,46 +64,37 @@ $(document).ready(function () {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module('editor', {
|
test('base-state', {asserts: 2}, function (instance, $fix) {
|
||||||
setup: baseSetup
|
|
||||||
});
|
|
||||||
asyncTest('base-state', 2, function () {
|
|
||||||
var e = new instance.web.list.Editor({
|
var e = new instance.web.list.Editor({
|
||||||
dataset: {ids: []},
|
dataset: {ids: []},
|
||||||
edition_view: function () {
|
edition_view: function () {
|
||||||
return makeFormView();
|
return makeFormView();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e.appendTo($fix)
|
return e.appendTo($fix)
|
||||||
.always(start)
|
|
||||||
.fail(function (error) { ok(false, error && error.message); })
|
|
||||||
.done(function () {
|
.done(function () {
|
||||||
ok(!e.is_editing(), "should not be editing");
|
ok(!e.is_editing(), "should not be editing");
|
||||||
ok(e.form instanceof instance.web.FormView,
|
ok(e.form instanceof instance.web.FormView,
|
||||||
"should use default form type");
|
"should use default form type");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
asyncTest('toggle-edition-save', 4, function () {
|
test('toggle-edition-save', {
|
||||||
instance.session.responses['/web/dataset/call_kw:create'] = function () {
|
asserts: 4,
|
||||||
return { result: 42 };
|
setup: function (instance, $s, mock) {
|
||||||
};
|
mock('test.model:read', function () {
|
||||||
instance.session.responses['/web/dataset/call_kw:read'] = function () {
|
return [{id: 42, a: false, b: false, c: false}];
|
||||||
return { result: [{
|
});
|
||||||
id: 42,
|
}
|
||||||
a: false,
|
}, function (instance, $fix) {
|
||||||
b: false,
|
|
||||||
c: false
|
|
||||||
}]};
|
|
||||||
};
|
|
||||||
var e = new instance.web.list.Editor({
|
var e = new instance.web.list.Editor({
|
||||||
dataset: new instance.web.DataSetSearch(),
|
dataset: new instance.web.DataSetSearch(null, 'test.model'),
|
||||||
prepends_on_create: function () { return false; },
|
prepends_on_create: function () { return false; },
|
||||||
edition_view: function () {
|
edition_view: function () {
|
||||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
e.appendTo($fix)
|
return e.appendTo($fix)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return e.edit({}, function () {
|
return e.edit({}, function () {
|
||||||
++counter;
|
++counter;
|
||||||
|
@ -117,26 +105,21 @@ $(document).ready(function () {
|
||||||
equal(counter, 3, "should have configured all fields");
|
equal(counter, 3, "should have configured all fields");
|
||||||
return e.save();
|
return e.save();
|
||||||
})
|
})
|
||||||
.always(start)
|
|
||||||
.fail(function (error) { ok(false, error && error.message); })
|
|
||||||
.done(function (record) {
|
.done(function (record) {
|
||||||
ok(!e.is_editing(), "should have stopped editing");
|
ok(!e.is_editing(), "should have stopped editing");
|
||||||
equal(record.id, 42, "should have newly created id");
|
equal(record.id, 42, "should have newly created id");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
asyncTest('toggle-edition-cancel', 2, function () {
|
test('toggle-edition-cancel', { asserts: 2 }, function (instance, $fix) {
|
||||||
instance.session.responses['/web/dataset/call_kw:create'] = function () {
|
|
||||||
return { result: 42 };
|
|
||||||
};
|
|
||||||
var e = new instance.web.list.Editor({
|
var e = new instance.web.list.Editor({
|
||||||
dataset: new instance.web.DataSetSearch(),
|
dataset: new instance.web.DataSetSearch(null, 'test.model'),
|
||||||
prepends_on_create: function () { return false; },
|
prepends_on_create: function () { return false; },
|
||||||
edition_view: function () {
|
edition_view: function () {
|
||||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
e.appendTo($fix)
|
return e.appendTo($fix)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return e.edit({}, function () {
|
return e.edit({}, function () {
|
||||||
++counter;
|
++counter;
|
||||||
|
@ -145,22 +128,20 @@ $(document).ready(function () {
|
||||||
.then(function (form) {
|
.then(function (form) {
|
||||||
return e.cancel();
|
return e.cancel();
|
||||||
})
|
})
|
||||||
.always(start)
|
|
||||||
.fail(function (error) { ok(false, error && error.message); })
|
|
||||||
.done(function (record) {
|
.done(function (record) {
|
||||||
ok(!e.is_editing(), "should have stopped editing");
|
ok(!e.is_editing(), "should have stopped editing");
|
||||||
ok(!record.id, "should have no id");
|
ok(!record.id, "should have no id");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
asyncTest('toggle-save-required', 2, function () {
|
test('toggle-save-required', {
|
||||||
instance.session.responses['/web/dataset/call_kw:create'] = function () {
|
asserts: 2,
|
||||||
return { result: 42 };
|
fail_on_rejection: false
|
||||||
};
|
}, function (instance, $fix) {
|
||||||
var e = new instance.web.list.Editor({
|
var e = new instance.web.list.Editor({
|
||||||
do_warn: function () {
|
do_warn: function () {
|
||||||
warnings++;
|
warnings++;
|
||||||
},
|
},
|
||||||
dataset: new instance.web.DataSetSearch(),
|
dataset: new instance.web.DataSetSearch(null, 'test.model'),
|
||||||
prepends_on_create: function () { return false; },
|
prepends_on_create: function () { return false; },
|
||||||
edition_view: function () {
|
edition_view: function () {
|
||||||
return makeFormView([
|
return makeFormView([
|
||||||
|
@ -169,7 +150,7 @@ $(document).ready(function () {
|
||||||
});
|
});
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
var warnings = 0;
|
var warnings = 0;
|
||||||
e.appendTo($fix)
|
return e.appendTo($fix)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return e.edit({}, function () {
|
return e.edit({}, function () {
|
||||||
++counter;
|
++counter;
|
||||||
|
@ -178,22 +159,32 @@ $(document).ready(function () {
|
||||||
.then(function (form) {
|
.then(function (form) {
|
||||||
return e.save();
|
return e.save();
|
||||||
})
|
})
|
||||||
.always(start)
|
|
||||||
.done(function () { ok(false, "cancel should not succeed"); })
|
.done(function () { ok(false, "cancel should not succeed"); })
|
||||||
.fail(function () {
|
.fail(function () {
|
||||||
equal(warnings, 1, "should have been warned");
|
equal(warnings, 1, "should have been warned");
|
||||||
ok(e.is_editing(), "should have kept editing");
|
ok(e.is_editing(), "should have kept editing");
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
module('list-edition', {
|
});
|
||||||
setup: function () {
|
openerp.testing.section('list.edition', {
|
||||||
baseSetup();
|
dependencies: ['web.list_editable'],
|
||||||
|
rpc: 'mock',
|
||||||
|
templates: true,
|
||||||
|
setup: function (instance, $s, mock) {
|
||||||
var records = {};
|
var records = {};
|
||||||
_.extend(instance.session.responses, {
|
mock('demo:create', function (args) {
|
||||||
'/web/view/load': function () {
|
records[42] = _.extend({}, args[0]);
|
||||||
return {result: {
|
return 42;
|
||||||
|
});
|
||||||
|
mock('demo:read', function (args) {
|
||||||
|
var id = args[0][0];
|
||||||
|
if (id in records) {
|
||||||
|
return [records[id]];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
mock('/web/view/load', function () {
|
||||||
|
return {
|
||||||
type: 'tree',
|
type: 'tree',
|
||||||
fields: {
|
fields: {
|
||||||
a: {type: 'char', string: "A"},
|
a: {type: 'char', string: "A"},
|
||||||
|
@ -209,47 +200,32 @@ $(document).ready(function () {
|
||||||
{tag: 'field', attrs: {name: 'c'}}
|
{tag: 'field', attrs: {name: 'c'}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}};
|
};
|
||||||
},
|
|
||||||
'/web/dataset/call_kw:create': function (params) {
|
|
||||||
records[42] = _.extend({}, params.params.args[0]);
|
|
||||||
return {result: 42};
|
|
||||||
},
|
|
||||||
'/web/dataset/call_kw:read': function (params) {
|
|
||||||
var id = params.params.args[0][0];
|
|
||||||
if (id in records) {
|
|
||||||
return {result: [records[id]]};
|
|
||||||
}
|
|
||||||
return {result: []};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
asyncTest('newrecord', 6, function () {
|
}
|
||||||
|
}, function (test) {
|
||||||
|
test('newrecord', {asserts: 6}, function (instance, $fix, mock) {
|
||||||
var got_defaults = false;
|
var got_defaults = false;
|
||||||
instance.session.responses['/web/dataset/call_kw:default_get'] = function (params) {
|
mock('demo:default_get', function (args) {
|
||||||
var fields = params.params.args[0];
|
var fields = args[0];
|
||||||
deepEqual(
|
deepEqual(
|
||||||
fields, ['a', 'b', 'c'],
|
fields, ['a', 'b', 'c'],
|
||||||
"should ask defaults for all fields");
|
"should ask defaults for all fields");
|
||||||
got_defaults = true;
|
got_defaults = true;
|
||||||
return {result: {
|
return { a: "qux", b: "quux" };
|
||||||
a: "qux",
|
});
|
||||||
b: "quux"
|
|
||||||
}};
|
|
||||||
};
|
|
||||||
|
|
||||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||||
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
||||||
|
|
||||||
l.appendTo($fix)
|
return l.appendTo($fix)
|
||||||
.then(l.proxy('reload_content'))
|
.then(l.proxy('reload_content'))
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return l.start_edition();
|
return l.start_edition();
|
||||||
})
|
})
|
||||||
.always(start)
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
ok(got_defaults, "should have fetched default values for form");
|
ok(got_defaults, "should have fetched default values for form");
|
||||||
|
|
||||||
return l.save_edition();
|
return l.save_edition();
|
||||||
})
|
})
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
|
@ -260,16 +236,19 @@ $(document).ready(function () {
|
||||||
"should have used default values");
|
"should have used default values");
|
||||||
ok(!result.record.get('c'),
|
ok(!result.record.get('c'),
|
||||||
"should have no value if there was no default");
|
"should have no value if there was no default");
|
||||||
})
|
|
||||||
.fail(function (e) { ok(false, e && e.message || e); });
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
module('list-edition-events', {
|
});
|
||||||
setup: function () {
|
openerp.testing.section('list.edition.events', {
|
||||||
baseSetup();
|
dependencies: ['web.list_editable'],
|
||||||
_.extend(instance.session.responses, {
|
rpc: 'mock',
|
||||||
'/web/view/load': function () {
|
templates: true,
|
||||||
return {result: {
|
setup: function (instance, $s, mock) {
|
||||||
|
mock('demo:read', function () {
|
||||||
|
return [{ id: 1, a: 'foo', b: 'bar', c: 'baz' }];
|
||||||
|
});
|
||||||
|
mock('/web/view/load', function () {
|
||||||
|
return {
|
||||||
type: 'tree',
|
type: 'tree',
|
||||||
fields: {
|
fields: {
|
||||||
a: {type: 'char', string: "A"},
|
a: {type: 'char', string: "A"},
|
||||||
|
@ -285,20 +264,11 @@ $(document).ready(function () {
|
||||||
{tag: 'field', attrs: {name: 'c'}}
|
{tag: 'field', attrs: {name: 'c'}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}};
|
};
|
||||||
},
|
|
||||||
'/web/dataset/call_kw:read': function (params) {
|
|
||||||
return {result: [{
|
|
||||||
id: 1,
|
|
||||||
a: 'foo',
|
|
||||||
b: 'bar',
|
|
||||||
c: 'baz'
|
|
||||||
}]};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}, function (test) {
|
||||||
asyncTest('edition events', 4, function () {
|
test('edition events', {asserts: 4}, function (instance, $fix) {
|
||||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||||
var o = {
|
var o = {
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
@ -306,9 +276,8 @@ $(document).ready(function () {
|
||||||
};
|
};
|
||||||
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
||||||
l.on('edit:before edit:after', o, o.onEvent);
|
l.on('edit:before edit:after', o, o.onEvent);
|
||||||
l.appendTo($fix)
|
return l.appendTo($fix)
|
||||||
.then(l.proxy('reload_content'))
|
.then(l.proxy('reload_content'))
|
||||||
.always(start)
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
ok(l.options.editable, "should be editable");
|
ok(l.options.editable, "should be editable");
|
||||||
equal(o.counter, 0, "should have seen no event yet");
|
equal(o.counter, 0, "should have seen no event yet");
|
||||||
|
@ -317,11 +286,10 @@ $(document).ready(function () {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
ok(l.editor.is_editing(), "should be editing");
|
ok(l.editor.is_editing(), "should be editing");
|
||||||
equal(o.counter, 2, "should have seen two edition events");
|
equal(o.counter, 2, "should have seen two edition events");
|
||||||
})
|
});
|
||||||
.fail(function (e) { ok(false, e && e.message); });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest('edition events: cancelling', 3, function () {
|
test('edition events: cancelling', {asserts: 3}, function (instance, $fix) {
|
||||||
var edit_after = false;
|
var edit_after = false;
|
||||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||||
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
||||||
|
@ -331,9 +299,8 @@ $(document).ready(function () {
|
||||||
l.on('edit:after', {}, function () {
|
l.on('edit:after', {}, function () {
|
||||||
edit_after = true;
|
edit_after = true;
|
||||||
});
|
});
|
||||||
l.appendTo($fix)
|
return l.appendTo($fix)
|
||||||
.then(l.proxy('reload_content'))
|
.then(l.proxy('reload_content'))
|
||||||
.always(start)
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
ok(l.options.editable, "should be editable");
|
ok(l.options.editable, "should be editable");
|
||||||
return l.start_edition();
|
return l.start_edition();
|
||||||
|
@ -343,7 +310,71 @@ $(document).ready(function () {
|
||||||
ok(!l.editor.is_editing(), "should not be editing");
|
ok(!l.editor.is_editing(), "should not be editing");
|
||||||
ok(!edit_after, "should not have fired the edit:after event");
|
ok(!edit_after, "should not have fired the edit:after event");
|
||||||
return $.when();
|
return $.when();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
openerp.testing.section('list.edition.onwrite', {
|
||||||
|
dependencies: ['web.list_editable'],
|
||||||
|
rpc: 'mock',
|
||||||
|
templates: true,
|
||||||
|
}, function (test) {
|
||||||
|
test('record-to-read', {asserts: 4}, function (instance, $fix, mock) {
|
||||||
|
mock('/web/view/load', function () {
|
||||||
|
return {
|
||||||
|
type: 'tree',
|
||||||
|
fields: {
|
||||||
|
a: {type: 'char', string: "A"}
|
||||||
|
},
|
||||||
|
arch: {
|
||||||
|
tag: 'tree',
|
||||||
|
attrs: { on_write: 'on_write', colors: 'red:a == "foo"' },
|
||||||
|
children: [
|
||||||
|
{tag: 'field', attrs: {name: 'a'}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
mock('demo:read', function (args, kwargs) {
|
||||||
|
if (_.isEmpty(args[0])) {
|
||||||
|
return [];
|
||||||
|
} else if (_.isEqual(args[0], [1])) {
|
||||||
|
return [
|
||||||
|
{id: 1, a: 'some value'}
|
||||||
|
];
|
||||||
|
} else if (_.isEqual(args[0], [42])) {
|
||||||
|
return [ {id: 42, a: 'foo'} ];
|
||||||
|
}
|
||||||
|
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||||
|
});
|
||||||
|
mock('demo:default_get', function () { return {}; });
|
||||||
|
mock('demo:create', function () { return 1; });
|
||||||
|
mock('demo:on_write', function () { return [42]; });
|
||||||
|
|
||||||
|
var ds = new instance.web.DataSetStatic(null, 'demo', null, []);
|
||||||
|
var l = new instance.web.ListView({}, ds, false, {editable: 'top'});
|
||||||
|
return l.appendTo($fix)
|
||||||
|
.then(l.proxy('reload_content'))
|
||||||
|
.then(function () {
|
||||||
|
return l.start_edition();
|
||||||
})
|
})
|
||||||
.fail(function (e) { ok(false, e && e.message || e); });
|
.then(function () {
|
||||||
|
$fix.find('.oe_form_field input').val("some value").change();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return l.save_edition();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
strictEqual(ds.ids.length, 2,
|
||||||
|
'should have id of created + on_write');
|
||||||
|
strictEqual(l.records.length, 2,
|
||||||
|
'should have record of created + on_write');
|
||||||
|
strictEqual(
|
||||||
|
$fix.find('tbody tr:eq(1)').css('color'), 'rgb(255, 0, 0)',
|
||||||
|
'shoud have color applied');
|
||||||
|
strictEqual(
|
||||||
|
$fix.find('tbody tr:eq(2)').css('color'), 'rgb(0, 0, 0)',
|
||||||
|
'should have default color applied');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('list.events', {
|
||||||
var openerp,
|
dependencies: ['web.list']
|
||||||
create = function (o) {
|
}, function (test) {
|
||||||
|
var create = function (o) {
|
||||||
if (typeof Object.create === 'function') {
|
if (typeof Object.create === 'function') {
|
||||||
return Object.create(o);
|
return Object.create(o);
|
||||||
}
|
}
|
||||||
|
@ -8,38 +9,26 @@ $(document).ready(function () {
|
||||||
Cls.prototype = o;
|
Cls.prototype = o;
|
||||||
return new Cls;
|
return new Cls;
|
||||||
};
|
};
|
||||||
module('list-events', {
|
test('Simple event triggering', function (instance) {
|
||||||
setup: function () {
|
var e = create(instance.web.list.Events), passed = false;
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
test('Simple event triggering', function () {
|
|
||||||
var e = create(openerp.web.list.Events), passed = false;
|
|
||||||
e.bind('foo', function () { passed = true; });
|
e.bind('foo', function () { passed = true; });
|
||||||
e.trigger('foo');
|
e.trigger('foo');
|
||||||
ok(passed);
|
ok(passed);
|
||||||
});
|
});
|
||||||
test('Bind all', function () {
|
test('Bind all', function (instance) {
|
||||||
var e = create(openerp.web.list.Events), event = null;
|
var e = create(instance.web.list.Events), event = null;
|
||||||
e.bind(null, function (ev) { event = ev; });
|
e.bind(null, function (ev) { event = ev; });
|
||||||
e.trigger('foo');
|
e.trigger('foo');
|
||||||
strictEqual(event, 'foo');
|
strictEqual(event, 'foo');
|
||||||
});
|
});
|
||||||
test('Propagate trigger params', function () {
|
test('Propagate trigger params', function (instance) {
|
||||||
var e = create(openerp.web.list.Events), p = false;
|
var e = create(instance.web.list.Events), p = false;
|
||||||
e.bind(null, function (_, param) { p = param });
|
e.bind(null, function (_, param) { p = param });
|
||||||
e.trigger('foo', true);
|
e.trigger('foo', true);
|
||||||
strictEqual(p, true)
|
strictEqual(p, true)
|
||||||
});
|
});
|
||||||
test('Bind multiple callbacks', function () {
|
test('Bind multiple callbacks', function (instance) {
|
||||||
var e = create(openerp.web.list.Events), count;
|
var e = create(instance.web.list.Events), count;
|
||||||
e.bind('foo', function () { count++; })
|
e.bind('foo', function () { count++; })
|
||||||
.bind('bar', function () { count++; })
|
.bind('bar', function () { count++; })
|
||||||
.bind(null, function () { count++; })
|
.bind(null, function () { count++; })
|
||||||
|
@ -59,20 +48,20 @@ $(document).ready(function () {
|
||||||
e.trigger('baz');
|
e.trigger('baz');
|
||||||
strictEqual(count, 3);
|
strictEqual(count, 3);
|
||||||
});
|
});
|
||||||
test('Mixin events', function () {
|
test('Mixin events', function (instance) {
|
||||||
var cls = openerp.web.Class.extend({
|
var cls = instance.web.Class.extend({
|
||||||
method: function () { this.trigger('e'); }
|
method: function () { this.trigger('e'); }
|
||||||
});
|
});
|
||||||
cls.include(openerp.web.list.Events);
|
cls.include(instance.web.list.Events);
|
||||||
var instance = new cls, triggered = false;
|
var i = new cls, triggered = false;
|
||||||
|
|
||||||
instance.bind('e', function () { triggered = true; });
|
i.bind('e', function () { triggered = true; });
|
||||||
instance.method();
|
i.method();
|
||||||
|
|
||||||
ok(triggered);
|
ok(triggered);
|
||||||
});
|
});
|
||||||
test('Unbind all handlers', function () {
|
test('Unbind all handlers', function (instance) {
|
||||||
var e = create(openerp.web.list.Events), passed = 0;
|
var e = create(instance.web.list.Events), passed = 0;
|
||||||
e.bind('foo', function () { passed++; });
|
e.bind('foo', function () { passed++; });
|
||||||
e.trigger('foo');
|
e.trigger('foo');
|
||||||
strictEqual(passed, 1);
|
strictEqual(passed, 1);
|
||||||
|
@ -80,8 +69,8 @@ $(document).ready(function () {
|
||||||
e.trigger('foo');
|
e.trigger('foo');
|
||||||
strictEqual(passed, 1);
|
strictEqual(passed, 1);
|
||||||
});
|
});
|
||||||
test('Unbind one handler', function () {
|
test('Unbind one handler', function (instance) {
|
||||||
var e = create(openerp.web.list.Events), p1 = 0, p2 = 0,
|
var e = create(instance.web.list.Events), p1 = 0, p2 = 0,
|
||||||
h1 = function () { p1++; }, h2 = function () { p2++; };
|
h1 = function () { p1++; }, h2 = function () { p2++; };
|
||||||
e.bind('foo', h1);
|
e.bind('foo', h1);
|
||||||
e.bind('foo', h2);
|
e.bind('foo', h2);
|
||||||
|
@ -93,29 +82,20 @@ $(document).ready(function () {
|
||||||
strictEqual(p1, 1);
|
strictEqual(p1, 1);
|
||||||
strictEqual(p2, 2);
|
strictEqual(p2, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
module('list-records', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('Basic record initialization', function () {
|
openerp.testing.section('list.records', {
|
||||||
var r = new openerp.web.list.Record({qux: 3});
|
dependencies: ['web.list']
|
||||||
|
}, function (test) {
|
||||||
|
test('Basic record initialization', function (instance) {
|
||||||
|
var r = new instance.web.list.Record({qux: 3});
|
||||||
r.set('foo', 1);
|
r.set('foo', 1);
|
||||||
r.set('bar', 2);
|
r.set('bar', 2);
|
||||||
strictEqual(r.get('foo'), 1);
|
strictEqual(r.get('foo'), 1);
|
||||||
strictEqual(r.get('bar'), 2);
|
strictEqual(r.get('bar'), 2);
|
||||||
strictEqual(r.get('qux'), 3);
|
strictEqual(r.get('qux'), 3);
|
||||||
});
|
});
|
||||||
test('Change all the things', function () {
|
test('Change all the things', function (instance) {
|
||||||
var r = new openerp.web.list.Record(), changed = false, field;
|
var r = new instance.web.list.Record(), changed = false, field;
|
||||||
r.bind('change', function () { changed = true; });
|
r.bind('change', function () { changed = true; });
|
||||||
r.bind(null, function (e) { field = field || e.split(':')[1]});
|
r.bind(null, function (e) { field = field || e.split(':')[1]});
|
||||||
r.set('foo', 1);
|
r.set('foo', 1);
|
||||||
|
@ -123,8 +103,8 @@ $(document).ready(function () {
|
||||||
ok(changed);
|
ok(changed);
|
||||||
strictEqual(field, 'foo');
|
strictEqual(field, 'foo');
|
||||||
});
|
});
|
||||||
test('Change single field', function () {
|
test('Change single field', function (instance) {
|
||||||
var r = new openerp.web.list.Record(), changed = 0;
|
var r = new instance.web.list.Record(), changed = 0;
|
||||||
r.bind('change:foo', function () { changed++; });
|
r.bind('change:foo', function () { changed++; });
|
||||||
r.set('foo', 1);
|
r.set('foo', 1);
|
||||||
r.set('bar', 1);
|
r.set('bar', 1);
|
||||||
|
@ -132,21 +112,12 @@ $(document).ready(function () {
|
||||||
strictEqual(r.get('bar'), 1);
|
strictEqual(r.get('bar'), 1);
|
||||||
strictEqual(changed, 1);
|
strictEqual(changed, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
module('list-collections', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('degenerate-fetch', function () {
|
openerp.testing.section('list.collections', {
|
||||||
var c = new openerp.web.list.Collection();
|
dependencies: ['web.list']
|
||||||
|
}, function (test) {
|
||||||
|
test('degenerate-fetch', function (instance) {
|
||||||
|
var c = new instance.web.list.Collection();
|
||||||
strictEqual(c.length, 0);
|
strictEqual(c.length, 0);
|
||||||
c.add({id: 1, value: 2});
|
c.add({id: 1, value: 2});
|
||||||
c.add({id: 2, value: 3});
|
c.add({id: 2, value: 3});
|
||||||
|
@ -155,16 +126,16 @@ $(document).ready(function () {
|
||||||
strictEqual(c.length, 4);
|
strictEqual(c.length, 4);
|
||||||
var r = c.at(2), r2 = c.get(1);
|
var r = c.at(2), r2 = c.get(1);
|
||||||
|
|
||||||
ok(r instanceof openerp.web.list.Record);
|
ok(r instanceof instance.web.list.Record);
|
||||||
strictEqual(r.get('id'), 3);
|
strictEqual(r.get('id'), 3);
|
||||||
strictEqual(r.get('value'), 5);
|
strictEqual(r.get('value'), 5);
|
||||||
|
|
||||||
ok(r2 instanceof openerp.web.list.Record);
|
ok(r2 instanceof instance.web.list.Record);
|
||||||
strictEqual(r2.get('id'), 1);
|
strictEqual(r2.get('id'), 1);
|
||||||
strictEqual(r2.get('value'), 2);
|
strictEqual(r2.get('value'), 2);
|
||||||
});
|
});
|
||||||
test('degenerate-indexed-add', function () {
|
test('degenerate-indexed-add', function (instance) {
|
||||||
var c = new openerp.web.list.Collection([
|
var c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -175,8 +146,8 @@ $(document).ready(function () {
|
||||||
strictEqual(c.at(1).get('value'), 55);
|
strictEqual(c.at(1).get('value'), 55);
|
||||||
strictEqual(c.at(3).get('value'), 20);
|
strictEqual(c.at(3).get('value'), 20);
|
||||||
});
|
});
|
||||||
test('degenerate-remove', function () {
|
test('degenerate-remove', function (instance) {
|
||||||
var c = new openerp.web.list.Collection([
|
var c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -188,9 +159,9 @@ $(document).ready(function () {
|
||||||
equal(c.get(2), undefined);
|
equal(c.get(2), undefined);
|
||||||
strictEqual(c.at(1).get('value'), 20);
|
strictEqual(c.at(1).get('value'), 20);
|
||||||
});
|
});
|
||||||
test('degenerate-remove-bound', function () {
|
test('degenerate-remove-bound', function (instance) {
|
||||||
var changed = false,
|
var changed = false,
|
||||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
c = new instance.web.list.Collection([ {id: 1, value: 5} ]);
|
||||||
c.bind('change', function () { changed = true; });
|
c.bind('change', function () { changed = true; });
|
||||||
var record = c.get(1);
|
var record = c.get(1);
|
||||||
c.remove(record);
|
c.remove(record);
|
||||||
|
@ -198,8 +169,8 @@ $(document).ready(function () {
|
||||||
ok(!changed, 'removed records should not trigger events in their ' +
|
ok(!changed, 'removed records should not trigger events in their ' +
|
||||||
'parent collection');
|
'parent collection');
|
||||||
});
|
});
|
||||||
test('degenerate-reset', function () {
|
test('degenerate-reset', function (instance) {
|
||||||
var event, obj, c = new openerp.web.list.Collection([
|
var event, obj, c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -218,9 +189,9 @@ $(document).ready(function () {
|
||||||
strictEqual(c.length, 1);
|
strictEqual(c.length, 1);
|
||||||
strictEqual(c.get(42).get('value'), 55);
|
strictEqual(c.get(42).get('value'), 55);
|
||||||
});
|
});
|
||||||
test('degenerate-reset-bound', function () {
|
test('degenerate-reset-bound', function (instance) {
|
||||||
var changed = false,
|
var changed = false,
|
||||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
c = new instance.web.list.Collection([ {id: 1, value: 5} ]);
|
||||||
c.bind('change', function () { changed = true; });
|
c.bind('change', function () { changed = true; });
|
||||||
var record = c.get(1);
|
var record = c.get(1);
|
||||||
c.reset();
|
c.reset();
|
||||||
|
@ -229,9 +200,9 @@ $(document).ready(function () {
|
||||||
'parent collection');
|
'parent collection');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('degenerate-propagations', function () {
|
test('degenerate-propagations', function (instance) {
|
||||||
var values = [];
|
var values = [];
|
||||||
var c = new openerp.web.list.Collection([
|
var c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -244,8 +215,8 @@ $(document).ready(function () {
|
||||||
c.get(3).set('value', 21);
|
c.get(3).set('value', 21);
|
||||||
deepEqual(values, [6, 11, 21]);
|
deepEqual(values, [6, 11, 21]);
|
||||||
});
|
});
|
||||||
test('BTree', function () {
|
test('BTree', function (instance) {
|
||||||
var root = new openerp.web.list.Collection(),
|
var root = new instance.web.list.Collection(),
|
||||||
c = root.proxy('admin'),
|
c = root.proxy('admin'),
|
||||||
total = 0;
|
total = 0;
|
||||||
c.add({id: 1, name: "Administrator", login: 'admin'});
|
c.add({id: 1, name: "Administrator", login: 'admin'});
|
||||||
|
@ -260,8 +231,8 @@ $(document).ready(function () {
|
||||||
c.at(1).set('wealth', 5);
|
c.at(1).set('wealth', 5);
|
||||||
strictEqual(total, 47);
|
strictEqual(total, 47);
|
||||||
});
|
});
|
||||||
test('degenerate-successor', function () {
|
test('degenerate-successor', function (instance) {
|
||||||
var root = new openerp.web.list.Collection([
|
var root = new instance.web.list.Collection([
|
||||||
{id: 1, value: 1},
|
{id: 1, value: 1},
|
||||||
{id: 2, value: 2},
|
{id: 2, value: 2},
|
||||||
{id: 3, value: 3},
|
{id: 3, value: 3},
|
||||||
|
@ -282,8 +253,8 @@ $(document).ready(function () {
|
||||||
root.at(3).attributes,
|
root.at(3).attributes,
|
||||||
"wraparound should have no effect if not succ(last_record)");
|
"wraparound should have no effect if not succ(last_record)");
|
||||||
});
|
});
|
||||||
test('successor', function () {
|
test('successor', function (instance) {
|
||||||
var root = new openerp.web.list.Collection();
|
var root = new instance.web.list.Collection();
|
||||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||||
|
@ -298,8 +269,8 @@ $(document).ready(function () {
|
||||||
root.get(3).attributes,
|
root.get(3).attributes,
|
||||||
"should wraparound within a collection");
|
"should wraparound within a collection");
|
||||||
});
|
});
|
||||||
test('degenerate-predecessor', function () {
|
test('degenerate-predecessor', function (instance) {
|
||||||
var root = new openerp.web.list.Collection([
|
var root = new instance.web.list.Collection([
|
||||||
{id: 1, value: 1},
|
{id: 1, value: 1},
|
||||||
{id: 2, value: 2},
|
{id: 2, value: 2},
|
||||||
{id: 3, value: 3},
|
{id: 3, value: 3},
|
||||||
|
@ -320,8 +291,8 @@ $(document).ready(function () {
|
||||||
root.at(0).attributes,
|
root.at(0).attributes,
|
||||||
"wraparound should have no effect if not pred(first_record)");
|
"wraparound should have no effect if not pred(first_record)");
|
||||||
});
|
});
|
||||||
test('predecessor', function () {
|
test('predecessor', function (instance) {
|
||||||
var root = new openerp.web.list.Collection();
|
var root = new instance.web.list.Collection();
|
||||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||||
|
@ -336,21 +307,12 @@ $(document).ready(function () {
|
||||||
root.get(4).attributes,
|
root.get(4).attributes,
|
||||||
"should wraparound within a collection");
|
"should wraparound within a collection");
|
||||||
});
|
});
|
||||||
|
|
||||||
module('list-hofs', {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('each, degenerate', function () {
|
openerp.testing.section('list.collections.hom', {
|
||||||
var c = new openerp.web.list.Collection([
|
dependencies: ['web.list']
|
||||||
|
}, function (test) {
|
||||||
|
test('each, degenerate', function (instance) {
|
||||||
|
var c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -362,8 +324,8 @@ $(document).ready(function () {
|
||||||
ids, [1, 2, 3],
|
ids, [1, 2, 3],
|
||||||
'degenerate collections should be iterated in record order');
|
'degenerate collections should be iterated in record order');
|
||||||
});
|
});
|
||||||
test('each, deep', function () {
|
test('each, deep', function (instance) {
|
||||||
var root = new openerp.web.list.Collection(),
|
var root = new instance.web.list.Collection(),
|
||||||
ids = [];
|
ids = [];
|
||||||
root.proxy('foo').add([
|
root.proxy('foo').add([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
|
@ -382,8 +344,8 @@ $(document).ready(function () {
|
||||||
ids, [1, 2, 3, 10, 20, 30],
|
ids, [1, 2, 3, 10, 20, 30],
|
||||||
'tree collections should be deeply iterated');
|
'tree collections should be deeply iterated');
|
||||||
});
|
});
|
||||||
test('map, degenerate', function () {
|
test('map, degenerate', function (instance) {
|
||||||
var c = new openerp.web.list.Collection([
|
var c = new instance.web.list.Collection([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
{id: 3, value: 20}
|
{id: 3, value: 20}
|
||||||
|
@ -395,8 +357,8 @@ $(document).ready(function () {
|
||||||
ids, [1, 2, 3],
|
ids, [1, 2, 3],
|
||||||
'degenerate collections should be iterated in record order');
|
'degenerate collections should be iterated in record order');
|
||||||
});
|
});
|
||||||
test('map, deep', function () {
|
test('map, deep', function (instance) {
|
||||||
var root = new openerp.web.list.Collection();
|
var root = new instance.web.list.Collection();
|
||||||
root.proxy('foo').add([
|
root.proxy('foo').add([
|
||||||
{id: 1, value: 5},
|
{id: 1, value: 5},
|
||||||
{id: 2, value: 10},
|
{id: 2, value: 10},
|
||||||
|
@ -414,29 +376,20 @@ $(document).ready(function () {
|
||||||
ids, [1, 2, 3, 10, 20, 30],
|
ids, [1, 2, 3, 10, 20, 30],
|
||||||
'tree collections should be deeply iterated');
|
'tree collections should be deeply iterated');
|
||||||
});
|
});
|
||||||
|
|
||||||
module("list-weirds", {
|
|
||||||
setup: function () {
|
|
||||||
openerp = window.openerp.init([]);
|
|
||||||
window.openerp.web.corelib(openerp);
|
|
||||||
window.openerp.web.coresetup(openerp);
|
|
||||||
window.openerp.web.chrome(openerp);
|
|
||||||
// views loader stuff
|
|
||||||
window.openerp.web.data(openerp);
|
|
||||||
window.openerp.web.views(openerp);
|
|
||||||
window.openerp.web.list(openerp);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
test('set-from-noid', function () {
|
openerp.testing.section('list.collection.weirdoes', {
|
||||||
var root = new openerp.web.list.Collection();
|
dependencies: ['web.list']
|
||||||
|
}, function (test) {
|
||||||
|
test('set-from-noid', function (instance) {
|
||||||
|
var root = new instance.web.list.Collection();
|
||||||
root.add({v: 3});
|
root.add({v: 3});
|
||||||
root.at(0).set('id', 42);
|
root.at(0).set('id', 42);
|
||||||
var record = root.get(42);
|
var record = root.get(42);
|
||||||
equal(root.length, 1);
|
equal(root.length, 1);
|
||||||
equal(record.get('v'), 3, "should have fetched the original record");
|
equal(record.get('v'), 3, "should have fetched the original record");
|
||||||
});
|
});
|
||||||
test('set-from-previd', function () {
|
test('set-from-previd', function (instance) {
|
||||||
var root = new openerp.web.list.Collection();
|
var root = new instance.web.list.Collection();
|
||||||
root.add({id: 1, v: 2});
|
root.add({id: 1, v: 2});
|
||||||
root.get(1).set('id', 42);
|
root.get(1).set('id', 42);
|
||||||
var record = root.get(42);
|
var record = root.get(42);
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
openerp.testing.section('list.buttons', {
|
||||||
|
dependencies: ['web.list', 'web.form'],
|
||||||
|
rpc: 'mock',
|
||||||
|
templates: true
|
||||||
|
}, function (test) {
|
||||||
|
test('record-deletion', {asserts: 2}, function (instance, $fix, mock) {
|
||||||
|
mock('/web/view/load', function () {
|
||||||
|
return {
|
||||||
|
type: 'tree',
|
||||||
|
fields: {
|
||||||
|
a: {type: 'char', string: "A"}
|
||||||
|
},
|
||||||
|
arch: {
|
||||||
|
tag: 'tree',
|
||||||
|
attrs: { },
|
||||||
|
children: [
|
||||||
|
{tag: 'field', attrs: {name: 'a'}},
|
||||||
|
{tag: 'button', attrs: {type: 'object', name: 'foo'}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
mock('demo:read', function (args, kwargs) {
|
||||||
|
if (_.isEqual(args[0], [1, 2, 3])) {
|
||||||
|
return [
|
||||||
|
{id: 1, a: 'foo'}, {id: 2, a: 'bar'}, {id: 3, a: 'baz'}
|
||||||
|
];
|
||||||
|
} else if (_.isEqual(args[0], [2])) {
|
||||||
|
// button action virtually removed record
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
throw new Error(JSON.stringify(_.toArray(arguments)));
|
||||||
|
});
|
||||||
|
mock('/web/dataset/call_button', function () { return false; });
|
||||||
|
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1, 2, 3]);
|
||||||
|
var l = new instance.web.ListView({
|
||||||
|
do_action: openerp.testing.noop
|
||||||
|
}, ds, false, {editable: 'top'});
|
||||||
|
return l.appendTo($fix)
|
||||||
|
.then(l.proxy('reload_content'))
|
||||||
|
.then(function () {
|
||||||
|
var d = $.Deferred();
|
||||||
|
l.records.bind('remove', function () {
|
||||||
|
d.resolve();
|
||||||
|
});
|
||||||
|
$fix.find('table tbody tr:eq(1) button').click();
|
||||||
|
return d.promise();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
strictEqual(l.records.length, 2,
|
||||||
|
"should have 2 records left");
|
||||||
|
strictEqual($fix.find('table tbody tr[data-id]').length, 2,
|
||||||
|
"should have 2 rows left");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,60 @@
|
||||||
|
openerp.testing.section('mutex', {
|
||||||
|
dependencies: ['web.coresetup'],
|
||||||
|
setup: function (instance) {
|
||||||
|
}
|
||||||
|
}, function (test) {
|
||||||
|
test('simpleScheduling', function (instance) {
|
||||||
|
var m = new $.Mutex();
|
||||||
|
var def1 = $.Deferred();
|
||||||
|
var def2 = $.Deferred();
|
||||||
|
var p1 = m.exec(function() { return def1; });
|
||||||
|
var p2 = m.exec(function() { return def2; });
|
||||||
|
equal(p1.state(), "pending");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
def1.resolve();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
def2.resolve();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "resolved");
|
||||||
|
});
|
||||||
|
test('simpleScheduling2', function (instance) {
|
||||||
|
var m = new $.Mutex();
|
||||||
|
var def1 = $.Deferred();
|
||||||
|
var def2 = $.Deferred();
|
||||||
|
var p1 = m.exec(function() { return def1; });
|
||||||
|
var p2 = m.exec(function() { return def2; });
|
||||||
|
equal(p1.state(), "pending");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
def2.resolve();
|
||||||
|
equal(p1.state(), "pending");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
def1.resolve();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "resolved");
|
||||||
|
});
|
||||||
|
test('reject', function (instance) {
|
||||||
|
var m = new $.Mutex();
|
||||||
|
var def1 = $.Deferred();
|
||||||
|
var def2 = $.Deferred();
|
||||||
|
var def3 = $.Deferred();
|
||||||
|
var p1 = m.exec(function() {return def1;});
|
||||||
|
var p2 = m.exec(function() {return def2;});
|
||||||
|
var p3 = m.exec(function() {return def3;});
|
||||||
|
equal(p1.state(), "pending");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
equal(p3.state(), "pending");
|
||||||
|
def1.resolve();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "pending");
|
||||||
|
equal(p3.state(), "pending");
|
||||||
|
def2.reject();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "rejected");
|
||||||
|
equal(p3.state(), "pending");
|
||||||
|
def3.resolve();
|
||||||
|
equal(p1.state(), "resolved");
|
||||||
|
equal(p2.state(), "rejected");
|
||||||
|
equal(p3.state(), "resolved");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,58 +1,55 @@
|
||||||
$(document).ready(function () {
|
openerp.testing.section('registry', {
|
||||||
var openerp;
|
dependencies: ['web.corelib'],
|
||||||
module('Registry', {
|
setup: function (instance) {
|
||||||
setup: function () {
|
instance.web.Foo = {};
|
||||||
openerp = window.openerp.init([]);
|
instance.web.Bar = {};
|
||||||
window.openerp.web.corelib(openerp);
|
instance.web.Foo2 = {};
|
||||||
openerp.web.Foo = {};
|
|
||||||
openerp.web.Bar = {};
|
|
||||||
openerp.web.Foo2 = {};
|
|
||||||
}
|
}
|
||||||
});
|
}, function (test) {
|
||||||
test('key set', function () {
|
test('key set', function (instance) {
|
||||||
var reg = new openerp.web.Registry();
|
var reg = new instance.web.Registry();
|
||||||
|
|
||||||
reg.add('foo', 'openerp.web.Foo')
|
reg.add('foo', 'instance.web.Foo')
|
||||||
.add('bar', 'openerp.web.Bar');
|
.add('bar', 'instance.web.Bar');
|
||||||
strictEqual(reg.get_object('bar'), openerp.web.Bar);
|
strictEqual(reg.get_object('bar'), instance.web.Bar);
|
||||||
});
|
});
|
||||||
test('extension', function () {
|
test('extension', function (instance) {
|
||||||
var reg = new openerp.web.Registry({
|
var reg = new instance.web.Registry({
|
||||||
foo: 'openerp.web.Foo',
|
foo: 'instance.web.Foo',
|
||||||
bar: 'openerp.web.Bar'
|
bar: 'instance.web.Bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
var reg2 = reg.extend({ 'foo': 'openerp.web.Foo2' });
|
var reg2 = reg.extend({ 'foo': 'instance.web.Foo2' });
|
||||||
strictEqual(reg.get_object('foo'), openerp.web.Foo);
|
strictEqual(reg.get_object('foo'), instance.web.Foo);
|
||||||
strictEqual(reg2.get_object('foo'), openerp.web.Foo2);
|
strictEqual(reg2.get_object('foo'), instance.web.Foo2);
|
||||||
});
|
});
|
||||||
test('remain-linked', function () {
|
test('remain-linked', function (instance) {
|
||||||
var reg = new openerp.web.Registry({
|
var reg = new instance.web.Registry({
|
||||||
foo: 'openerp.web.Foo',
|
foo: 'instance.web.Foo',
|
||||||
bar: 'openerp.web.Bar'
|
bar: 'instance.web.Bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
var reg2 = reg.extend();
|
var reg2 = reg.extend();
|
||||||
reg.add('foo2', 'openerp.web.Foo2');
|
reg.add('foo2', 'instance.web.Foo2');
|
||||||
strictEqual(reg.get_object('foo2'), openerp.web.Foo2);
|
strictEqual(reg.get_object('foo2'), instance.web.Foo2);
|
||||||
strictEqual(reg2.get_object('foo2'), openerp.web.Foo2);
|
strictEqual(reg2.get_object('foo2'), instance.web.Foo2);
|
||||||
});
|
});
|
||||||
test('multiget', function () {
|
test('multiget', function (instance) {
|
||||||
var reg = new openerp.web.Registry({
|
var reg = new instance.web.Registry({
|
||||||
foo: 'openerp.web.Foo',
|
foo: 'instance.web.Foo',
|
||||||
bar: 'openerp.web.Bar'
|
bar: 'instance.web.Bar'
|
||||||
});
|
});
|
||||||
|
|
||||||
strictEqual(reg.get_any(['qux', 'grault', 'bar', 'foo']),
|
strictEqual(reg.get_any(['qux', 'grault', 'bar', 'foo']),
|
||||||
openerp.web.Bar);
|
instance.web.Bar);
|
||||||
});
|
});
|
||||||
test('extended-multiget', function () {
|
test('extended-multiget', function (instance) {
|
||||||
var reg = new openerp.web.Registry({
|
var reg = new instance.web.Registry({
|
||||||
foo: 'openerp.web.Foo',
|
foo: 'instance.web.Foo',
|
||||||
bar: 'openerp.web.Bar'
|
bar: 'instance.web.Bar'
|
||||||
});
|
});
|
||||||
var reg2 = reg.extend();
|
var reg2 = reg.extend();
|
||||||
strictEqual(reg2.get_any(['qux', 'grault', 'bar', 'foo']),
|
strictEqual(reg2.get_any(['qux', 'grault', 'bar', 'foo']),
|
||||||
openerp.web.Bar);
|
instance.web.Bar);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue