[MERGE] Merge with trunk
bzr revid: fme@openerp.com-20111003135613-8d2ep2fv3bv713t7
This commit is contained in:
commit
82006438fe
|
@ -9,4 +9,3 @@ RE:^include/
|
|||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
logging.cfg
|
||||
|
|
|
@ -16,6 +16,7 @@ def wsgi_postload():
|
|||
_logger.info("embedded mode")
|
||||
o = Options()
|
||||
o.dbfilter = openerp.tools.config['dbfilter']
|
||||
o.server_wide_modules = openerp.conf.server_wide_modules or ['web']
|
||||
o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions")
|
||||
o.addons_path = openerp.modules.module.ad_paths
|
||||
o.serve_static = True
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
"depends" : [],
|
||||
'active': True,
|
||||
'post_load' : 'wsgi_postload',
|
||||
'web_preload' : True,
|
||||
'js' : [
|
||||
"static/lib/datejs/globalization/en-US.js",
|
||||
"static/lib/datejs/core.js",
|
||||
|
@ -27,6 +26,7 @@
|
|||
"static/lib/underscore/underscore.js",
|
||||
"static/lib/underscore/underscore.string.js",
|
||||
"static/lib/labjs/LAB.src.js",
|
||||
"static/lib/py.parse/lib/py.js",
|
||||
"static/src/js/boot.js",
|
||||
"static/src/js/core.js",
|
||||
"static/src/js/dates.js",
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import with_statement
|
|||
|
||||
import functools
|
||||
import logging
|
||||
import urllib
|
||||
import os
|
||||
import pprint
|
||||
import sys
|
||||
|
@ -13,7 +14,6 @@ import xmlrpclib
|
|||
import simplejson
|
||||
import werkzeug.datastructures
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.urls
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
|
@ -98,7 +98,8 @@ class WebRequest(object):
|
|||
self.params = dict(params)
|
||||
# OpenERP session setup
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession(self.config))
|
||||
self.session = self.httpsession.setdefault(self.session_id, session.OpenERPSession())
|
||||
self.session.config = self.config
|
||||
self.context = self.params.pop('context', None)
|
||||
self.debug = self.params.pop('debug', False) != False
|
||||
|
||||
|
@ -309,9 +310,16 @@ class Root(object):
|
|||
by the server, will be filtered by this pattern
|
||||
"""
|
||||
def __init__(self, options):
|
||||
self.root = werkzeug.urls.Href('/web/webclient/home')
|
||||
self.root = '/web/webclient/home'
|
||||
self.config = options
|
||||
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connector(protocol='local')
|
||||
else:
|
||||
conn = openerplib.get_connector(hostname=self.config.server_host,
|
||||
port=self.config.server_port)
|
||||
self.config.connector = conn
|
||||
|
||||
self.session_cookie = 'sessionid'
|
||||
self.addons = {}
|
||||
|
||||
|
@ -341,13 +349,12 @@ class Root(object):
|
|||
request.parameter_storage_class = werkzeug.datastructures.ImmutableDict
|
||||
|
||||
if request.path == '/':
|
||||
return werkzeug.utils.redirect(
|
||||
self.root(dict(request.args, debug='')), 301)(
|
||||
environ, start_response)
|
||||
params = urllib.urlencode(dict(request.args, debug=''))
|
||||
return werkzeug.utils.redirect(self.root + '?' + params, 301)(
|
||||
environ, start_response)
|
||||
elif request.path == '/mobile':
|
||||
return werkzeug.utils.redirect(
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(
|
||||
environ, start_response)
|
||||
'/web_mobile/static/src/web_mobile.html', 301)(environ, start_response)
|
||||
|
||||
handler = self.find_handler(*(request.path.split('/')[1:]))
|
||||
|
||||
|
|
|
@ -71,6 +71,14 @@ class Connector(object):
|
|||
self.hostname = hostname
|
||||
self.port = port
|
||||
|
||||
def get_service(self, service_name):
|
||||
"""
|
||||
Returns a Service instance to allow easy manipulation of one of the services offered by the remote server.
|
||||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return Service(self, service_name)
|
||||
|
||||
class XmlRPCConnector(Connector):
|
||||
"""
|
||||
A type of connector that uses the XMLRPC protocol.
|
||||
|
@ -321,7 +329,7 @@ class Connection(object):
|
|||
|
||||
:param service_name: The name of the service.
|
||||
"""
|
||||
return Service(self.connector, service_name)
|
||||
return self.connector.get_service(service_name)
|
||||
|
||||
class AuthenticationError(Exception):
|
||||
"""
|
||||
|
|
|
@ -5,6 +5,9 @@ import time
|
|||
import openerplib
|
||||
|
||||
import nonliterals
|
||||
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
#----------------------------------------------------------
|
||||
# OpenERPSession RPC openerp backend access
|
||||
#----------------------------------------------------------
|
||||
|
@ -26,27 +29,24 @@ class OpenERPSession(object):
|
|||
Used to store references to non-literal domains which need to be
|
||||
round-tripped to the client browser.
|
||||
"""
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
def __init__(self):
|
||||
self.config = None
|
||||
self._db = False
|
||||
self._uid = False
|
||||
self._login = False
|
||||
self._password = False
|
||||
self._locale = 'en_US'
|
||||
self.context = {}
|
||||
self.contexts_store = {}
|
||||
self.domains_store = {}
|
||||
self._lang = {}
|
||||
self.remote_timezone = 'utc'
|
||||
self.client_timezone = False
|
||||
|
||||
def __getstate__(self):
|
||||
state = dict(self.__dict__)
|
||||
if "config" in state:
|
||||
del state['config']
|
||||
return state
|
||||
|
||||
def build_connection(self):
|
||||
if self.config.backend == 'local':
|
||||
conn = openerplib.get_connection(protocol='local', database=self._db,
|
||||
login=self._login, user_id=self._uid, password=self._password)
|
||||
else:
|
||||
conn = openerplib.get_connection(hostname=self.config.server_host,
|
||||
port=self.config.server_port, database=self._db, login=self._login,
|
||||
conn = openerplib.Connection(self.config.connector, database=self._db, login=self._login,
|
||||
user_id=self._uid, password=self._password)
|
||||
return conn
|
||||
|
||||
|
@ -103,15 +103,6 @@ class OpenERPSession(object):
|
|||
self.context = self.model('res.users').context_get(self.context)
|
||||
self.context = self.context or {}
|
||||
|
||||
self.client_timezone = self.context.get("tz", False)
|
||||
# invalid code, anyway we decided the server will be in UTC
|
||||
#if self.client_timezone:
|
||||
# self.remote_timezone = self.execute('common', 'timezone_get')
|
||||
|
||||
self._locale = self.context.get('lang','en_US')
|
||||
lang_ids = self.execute('res.lang','search', [('code', '=', self._locale)])
|
||||
if lang_ids:
|
||||
self._lang = self.execute('res.lang', 'read',lang_ids[0], [])
|
||||
return self.context
|
||||
|
||||
@property
|
||||
|
|
|
@ -68,29 +68,6 @@ class Xml2Json:
|
|||
# OpenERP Web web Controllers
|
||||
#----------------------------------------------------------
|
||||
|
||||
def manifest_preload():
|
||||
modules = [k for k,v in openerpweb.addons_manifest.items() if v.get('web_preload')]
|
||||
return modules
|
||||
|
||||
def manifest_addons(addons):
|
||||
if addons==None:
|
||||
addons = manifest_preload()
|
||||
else:
|
||||
addons = addons.split(',')
|
||||
return addons
|
||||
|
||||
def manifest_glob(addons, key):
|
||||
addons = manifest_addons(addons)
|
||||
files = []
|
||||
for addon in addons:
|
||||
manifest = openerpweb.addons_manifest.get(addon, {})
|
||||
addons_path = manifest['addons_path']
|
||||
globlist = manifest.get(key, [])
|
||||
for pattern in globlist:
|
||||
for path in glob.glob(os.path.join(addons_path, addon, pattern)):
|
||||
files.append(path[len(addons_path):])
|
||||
return files
|
||||
|
||||
# TODO change into concat_file(addons,key) taking care of addons_path
|
||||
def concat_files(addons_path, file_list):
|
||||
""" Concatenate file content
|
||||
|
@ -131,24 +108,45 @@ home_template = textwrap.dedent("""<!DOCTYPE html>
|
|||
class WebClient(openerpweb.Controller):
|
||||
_cp_path = "/web/webclient"
|
||||
|
||||
def server_wide_modules(self, req):
|
||||
addons = [i for i in req.config.server_wide_modules if i in openerpweb.addons_manifest]
|
||||
return addons
|
||||
|
||||
def manifest_glob(self, req, addons, key):
|
||||
if addons==None:
|
||||
addons = self.server_wide_modules(req)
|
||||
else:
|
||||
addons = addons.split(',')
|
||||
files = []
|
||||
for addon in addons:
|
||||
manifest = openerpweb.addons_manifest.get(addon, None)
|
||||
if not manifest:
|
||||
continue
|
||||
addons_path = manifest['addons_path']
|
||||
globlist = manifest.get(key, [])
|
||||
for pattern in globlist:
|
||||
for path in glob.glob(os.path.join(addons_path, addon, pattern)):
|
||||
files.append(path[len(addons_path):])
|
||||
return files
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def csslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'css')
|
||||
return self.manifest_glob(req, mods, 'css')
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def jslist(self, req, mods=None):
|
||||
return manifest_glob(mods, 'js')
|
||||
return self.manifest_glob(req, mods, 'js')
|
||||
|
||||
@openerpweb.httprequest
|
||||
def css(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'css')
|
||||
files = self.manifest_glob(req, mods, 'css')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'text/css')])
|
||||
|
||||
@openerpweb.httprequest
|
||||
def js(self, req, mods=None):
|
||||
files = manifest_glob(mods, 'js')
|
||||
files = self.manifest_glob(req, mods, 'js')
|
||||
content,timestamp = concat_files(req.config.addons_path, files)
|
||||
# TODO request set the Date of last modif and Etag
|
||||
return req.make_response(content, [('Content-Type', 'application/javascript')])
|
||||
|
@ -158,19 +156,19 @@ class WebClient(openerpweb.Controller):
|
|||
# script tags
|
||||
jslist = ['/web/webclient/js']
|
||||
if req.debug:
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'js')]
|
||||
jslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, None, 'js')]
|
||||
js = "\n ".join(['<script type="text/javascript" src="%s"></script>'%i for i in jslist])
|
||||
|
||||
# css tags
|
||||
csslist = ['/web/webclient/css']
|
||||
if req.debug:
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'css')]
|
||||
csslist = [i + '?debug=' + str(time.time()) for i in self.manifest_glob(req, None, 'css')]
|
||||
css = "\n ".join(['<link rel="stylesheet" href="%s">'%i for i in csslist])
|
||||
|
||||
r = home_template % {
|
||||
'javascript': js,
|
||||
'css': css,
|
||||
'modules': simplejson.dumps(manifest_preload()),
|
||||
'modules': simplejson.dumps(self.server_wide_modules(req)),
|
||||
}
|
||||
return r
|
||||
|
||||
|
@ -364,7 +362,7 @@ class Session(openerpweb.Controller):
|
|||
mods = []
|
||||
for name, manifest in openerpweb.addons_manifest.items():
|
||||
# TODO replace by ir.module.module installed web
|
||||
if not manifest.get('web_preload') and manifest.get('active', True):
|
||||
if name not in req.config.server_wide_modules and manifest.get('active', True):
|
||||
mods.append(name)
|
||||
return mods
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: 87fb1b67d6a13f10a1a328104ee4d4b2c36801ec
|
||||
branch: default
|
||||
latesttag: 0.2
|
||||
latesttagdistance: 1
|
|
@ -0,0 +1 @@
|
|||
Parser and evaluator of Python expressions
|
|
@ -0,0 +1,14 @@
|
|||
* Parser
|
||||
since parsing expressions, try with a pratt parser
|
||||
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
|
||||
http://effbot.org/zone/simple-top-down-parsing.htm
|
||||
|
||||
Evaluator
|
||||
---------
|
||||
|
||||
* Stop busyworking trivial binary operator
|
||||
* Make it *trivial* to build Python type-wrappers
|
||||
* Implement Python's `data model
|
||||
protocols<http://docs.python.org/reference/datamodel.html#basic-customization>`_
|
||||
for *all* supported operations, optimizations can come later
|
||||
* Automatically type-wrap everything (for now anyway)
|
|
@ -0,0 +1,546 @@
|
|||
var py = {};
|
||||
(function (exports) {
|
||||
var NUMBER = /^\d$/,
|
||||
NAME_FIRST = /^[a-zA-Z_]$/,
|
||||
NAME = /^[a-zA-Z0-9_]$/;
|
||||
|
||||
var create = function (o, props) {
|
||||
function F() {}
|
||||
F.prototype = o;
|
||||
var inst = new F;
|
||||
for(var name in props) {
|
||||
if(!props.hasOwnProperty(name)) { continue; }
|
||||
inst[name] = props[name];
|
||||
}
|
||||
return inst;
|
||||
};
|
||||
|
||||
var symbols = {};
|
||||
var comparators = {};
|
||||
var Base = {
|
||||
nud: function () { throw new Error(this.id + " undefined as prefix"); },
|
||||
led: function (led) { throw new Error(this.id + " undefined as infix"); },
|
||||
toString: function () {
|
||||
if (this.id === '(constant)' || this.id === '(number)' || this.id === '(name)' || this.id === '(string)') {
|
||||
return [this.id.slice(0, this.id.length-1), ' ', this.value, ')'].join('');
|
||||
} else if (this.id === '(end)') {
|
||||
return '(end)';
|
||||
} else if (this.id === '(comparator)' ) {
|
||||
var repr = ['(comparator', this.expressions[0]];
|
||||
for (var i=0;i<this.operators.length; ++i) {
|
||||
repr.push(this.operators[i], this.expressions[i+1]);
|
||||
}
|
||||
return repr.join(' ') + ')';
|
||||
}
|
||||
var out = [this.id, this.first, this.second, this.third]
|
||||
.filter(function (r){return r}).join(' ');
|
||||
return '(' + out + ')';
|
||||
}
|
||||
};
|
||||
function symbol(id, bp) {
|
||||
bp = bp || 0;
|
||||
var s = symbols[id];
|
||||
if (s) {
|
||||
if (bp > s.lbp) {
|
||||
s.lbp = bp;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
return symbols[id] = create(Base, {
|
||||
id: id,
|
||||
lbp: bp
|
||||
});
|
||||
}
|
||||
function constant(id) {
|
||||
symbol(id).nud = function () {
|
||||
this.id = "(constant)";
|
||||
this.value = id;
|
||||
return this;
|
||||
};
|
||||
}
|
||||
function prefix(id, bp, nud) {
|
||||
symbol(id).nud = nud || function () {
|
||||
this.first = expression(bp);
|
||||
return this
|
||||
}
|
||||
}
|
||||
function infix(id, bp, led) {
|
||||
symbol(id, bp).led = led || function (left) {
|
||||
this.first = left;
|
||||
this.second = expression(bp);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
function infixr(id, bp) {
|
||||
symbol(id, bp).led = function (left) {
|
||||
this.first = left;
|
||||
this.second = expression(bp - 1);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
function comparator(id) {
|
||||
comparators[id] = true;
|
||||
var bp = 60;
|
||||
infix(id, bp, function (left) {
|
||||
this.id = '(comparator)';
|
||||
this.operators = [id];
|
||||
this.expressions = [left, expression(bp)];
|
||||
while (token.id in comparators) {
|
||||
this.operators.push(token.id);
|
||||
advance();
|
||||
this.expressions.push(
|
||||
expression(bp));
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
constant('None'); constant('False'); constant('True');
|
||||
|
||||
symbol('(number)').nud = function () { return this; };
|
||||
symbol('(name)').nud = function () { return this; };
|
||||
symbol('(string)').nud = function () { return this; };
|
||||
symbol('(end)');
|
||||
|
||||
symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(',');
|
||||
symbol('else');
|
||||
|
||||
symbol('lambda', 20).nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== ':') {
|
||||
for(;;) {
|
||||
if (token.id !== '(name)') {
|
||||
throw new Error('Excepted an argument name');
|
||||
}
|
||||
this.first.push(token);
|
||||
advance();
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(':');
|
||||
this.second = expression();
|
||||
return this;
|
||||
};
|
||||
infix('if', 20, function (left) {
|
||||
this.first = left;
|
||||
this.second = expression();
|
||||
advance('else');
|
||||
this.third = expression();
|
||||
return this;
|
||||
});
|
||||
|
||||
infixr('or', 30); infixr('and', 40); prefix('not', 50);
|
||||
|
||||
comparator('in'); comparator('not in');
|
||||
comparator('is'); comparator('is not');
|
||||
comparator('<'); comparator('<=');
|
||||
comparator('>'); comparator('>=');
|
||||
comparator('<>'); comparator('!='); comparator('==');
|
||||
|
||||
infix('|', 70); infix('^', 80), infix('&', 90);
|
||||
|
||||
infix('<<', 100); infix('>>', 100);
|
||||
|
||||
infix('+', 110); infix('-', 110);
|
||||
|
||||
infix('*', 120); infix('/', 120);
|
||||
infix('//', 120), infix('%', 120);
|
||||
|
||||
prefix('-', 130); prefix('+', 130); prefix('~', 130);
|
||||
|
||||
infixr('**', 140);
|
||||
|
||||
infix('.', 150, function (left) {
|
||||
if (token.id !== '(name)') {
|
||||
throw new Error('Expected attribute name, got ', token.id);
|
||||
}
|
||||
this.first = left;
|
||||
this.second = token;
|
||||
advance();
|
||||
return this;
|
||||
});
|
||||
symbol('(', 150).nud = function () {
|
||||
this.first = [];
|
||||
var comma = false;
|
||||
if (token.id !== ')') {
|
||||
while (true) {
|
||||
if (token.id === ')') {
|
||||
break;
|
||||
}
|
||||
this.first.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
comma = true;
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(')');
|
||||
if (!this.first.length || comma) {
|
||||
return this;
|
||||
} else {
|
||||
return this.first[0];
|
||||
}
|
||||
};
|
||||
symbol('(').led = function (left) {
|
||||
this.first = left;
|
||||
this.second = [];
|
||||
if (token.id !== ")") {
|
||||
for(;;) {
|
||||
this.second.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(")");
|
||||
return this;
|
||||
|
||||
};
|
||||
infix('[', 150, function (left) {
|
||||
this.first = left;
|
||||
this.second = expression();
|
||||
advance("]");
|
||||
return this;
|
||||
});
|
||||
symbol('[').nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== ']') {
|
||||
for (;;) {
|
||||
if (token.id === ']') {
|
||||
break;
|
||||
}
|
||||
this.first.push(expression());
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance(']');
|
||||
return this;
|
||||
};
|
||||
|
||||
symbol('{').nud = function () {
|
||||
this.first = [];
|
||||
if (token.id !== '}') {
|
||||
for(;;) {
|
||||
if (token.id === '}') {
|
||||
break;
|
||||
}
|
||||
var key = expression();
|
||||
advance(':');
|
||||
var value = expression();
|
||||
this.first.push([key, value]);
|
||||
if (token.id !== ',') {
|
||||
break;
|
||||
}
|
||||
advance(',');
|
||||
}
|
||||
}
|
||||
advance('}');
|
||||
return this;
|
||||
};
|
||||
|
||||
var longops = {
|
||||
'*': ['*'],
|
||||
'<': ['<', '=', '>'],
|
||||
'>': ['=', '>'],
|
||||
'!': ['='],
|
||||
'=': ['='],
|
||||
'/': ['/']
|
||||
};
|
||||
function Tokenizer() {
|
||||
this.states = ['initial'];
|
||||
this.tokens = [];
|
||||
}
|
||||
Tokenizer.prototype = {
|
||||
builder: function (empty) {
|
||||
var key = this.states[0] + '_builder';
|
||||
if (empty) {
|
||||
var value = this[key];
|
||||
delete this[key];
|
||||
return value;
|
||||
} else {
|
||||
return this[key] = this[key] || [];
|
||||
}
|
||||
},
|
||||
simple: function (type) {
|
||||
this.tokens.push({type: type});
|
||||
},
|
||||
push: function (new_state) {
|
||||
this.states.push(new_state);
|
||||
},
|
||||
pop: function () {
|
||||
this.states.pop();
|
||||
},
|
||||
|
||||
feed: function (str, index) {
|
||||
var s = this.states;
|
||||
return this[s[s.length - 1]](str, index);
|
||||
},
|
||||
|
||||
initial: function (str, index) {
|
||||
var character = str[index];
|
||||
|
||||
if (character in longops) {
|
||||
var follow = longops[character];
|
||||
for(var i=0, len=follow.length; i<len; ++i) {
|
||||
if (str[index+1] === follow[i]) {
|
||||
character += follow[i];
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character === ' ') {
|
||||
return index+1;
|
||||
} else if (character === '\0') {
|
||||
this.tokens.push(symbols['(end)']);
|
||||
return index + 1
|
||||
} else if (character === '"' || character === "'") {
|
||||
this.push('string');
|
||||
return index + 1;
|
||||
} else if (NUMBER.test(character)) {
|
||||
this.push('number');
|
||||
return index;
|
||||
} else if (NAME_FIRST.test(character)) {
|
||||
this.push('name');
|
||||
return index;
|
||||
} else if (character in symbols) {
|
||||
this.tokens.push(create(symbols[character]));
|
||||
return index + 1;
|
||||
}
|
||||
throw new Error("Tokenizing failure of <<" + str + ">> at index " + index
|
||||
+ ", character [[" + character + "]]"
|
||||
+ "; parsed so far: " + this.tokens);
|
||||
},
|
||||
string: function (str, index) {
|
||||
var character = str[index];
|
||||
if (character === '"' || character === "'") {
|
||||
this.tokens.push(create(symbols['(string)'], {
|
||||
value: this.builder(true).join('')
|
||||
}));
|
||||
this.pop();
|
||||
return index + 1;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
},
|
||||
number: function (str, index) {
|
||||
var character = str[index];
|
||||
if (!NUMBER.test(character)) {
|
||||
this.tokens.push(create(symbols['(number)'], {
|
||||
value: parseFloat(this.builder(true).join(''))
|
||||
}));
|
||||
this.pop();
|
||||
return index;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
},
|
||||
name: function (str, index) {
|
||||
var character = str[index];
|
||||
if (!NAME.test(character)) {
|
||||
var name = this.builder(true).join('');
|
||||
var symbol = symbols[name];
|
||||
if (symbol) {
|
||||
if (name === 'in' && this.tokens[this.tokens.length-1].id === 'not') {
|
||||
symbol = symbols['not in'];
|
||||
this.tokens.pop();
|
||||
} else if (name === 'not' && this.tokens[this.tokens.length-1].id === 'is') {
|
||||
symbol = symbols['is not'];
|
||||
this.tokens.pop();
|
||||
}
|
||||
this.tokens.push(create(symbol));
|
||||
} else {
|
||||
this.tokens.push(create(symbols['(name)'], {
|
||||
value: name
|
||||
}));
|
||||
}
|
||||
this.pop();
|
||||
return index;
|
||||
}
|
||||
this.builder().push(character);
|
||||
return index + 1;
|
||||
}
|
||||
};
|
||||
|
||||
exports.tokenize = function tokenize(str) {
|
||||
var index = 0,
|
||||
tokenizer = new Tokenizer(str);
|
||||
str += '\0';
|
||||
|
||||
do {
|
||||
index = tokenizer.feed(str, index);
|
||||
} while (index !== str.length);
|
||||
return tokenizer.tokens;
|
||||
};
|
||||
|
||||
var token, next;
|
||||
function expression(rbp) {
|
||||
rbp = rbp || 0;
|
||||
var t = token;
|
||||
token = next();
|
||||
var left = t.nud();
|
||||
while (rbp < token.lbp) {
|
||||
t = token;
|
||||
token = next();
|
||||
left = t.led(left);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
function advance(id) {
|
||||
if (id && token.id !== id) {
|
||||
throw new Error(
|
||||
'Expected "' + id + '", got "' + token.id + '"');
|
||||
}
|
||||
token = next();
|
||||
}
|
||||
|
||||
exports.object = create({}, {});
|
||||
exports.bool = function (arg) { return !!arg; };
|
||||
exports.tuple = create(exports.object, {
|
||||
__contains__: function (value) {
|
||||
for(var i=0, len=this.values.length; i<len; ++i) {
|
||||
if (this.values[i] === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
toJSON: function () {
|
||||
return this.values;
|
||||
}
|
||||
});
|
||||
exports.list = exports.tuple;
|
||||
exports.dict = create(exports.object, {
|
||||
toJSON: function () {
|
||||
return this.values;
|
||||
}
|
||||
});
|
||||
|
||||
exports.parse = function (toks) {
|
||||
var index = 0;
|
||||
token = toks[0];
|
||||
next = function () { return toks[++index]; };
|
||||
return expression();
|
||||
};
|
||||
var evaluate_operator = function (operator, a, b) {
|
||||
switch (operator) {
|
||||
case '==': case 'is': return a === b;
|
||||
case '!=': case 'is not': return a !== b;
|
||||
case '<': return a < b;
|
||||
case '<=': return a <= b;
|
||||
case '>': return a > b;
|
||||
case '>=': return a >= b;
|
||||
case 'in':
|
||||
if (typeof b === 'string') {
|
||||
return b.indexOf(a) !== -1;
|
||||
}
|
||||
return b.__contains__(a);
|
||||
case 'not in':
|
||||
if (typeof b === 'string') {
|
||||
return b.indexOf(a) === -1;
|
||||
}
|
||||
return !b.__contains__(a);
|
||||
}
|
||||
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
|
||||
};
|
||||
exports.evaluate = function (expr, context) {
|
||||
switch (expr.id) {
|
||||
case '(name)':
|
||||
var val = context[expr.value];
|
||||
if (val === undefined) {
|
||||
throw new Error("NameError: name '" + expr.value + "' is not defined");
|
||||
}
|
||||
return val;
|
||||
case '(string)':
|
||||
case '(number)':
|
||||
return expr.value;
|
||||
case '(constant)':
|
||||
if (expr.value === 'None')
|
||||
return null;
|
||||
else if (expr.value === 'False')
|
||||
return false;
|
||||
else if (expr.value === 'True')
|
||||
return true;
|
||||
throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
|
||||
case '(comparator)':
|
||||
var result, left = exports.evaluate(expr.expressions[0], context);
|
||||
for(var i=0; i<expr.operators.length; ++i) {
|
||||
result = evaluate_operator(
|
||||
expr.operators[i],
|
||||
left,
|
||||
left = exports.evaluate(expr.expressions[i+1], context));
|
||||
if (!result) { return false; }
|
||||
}
|
||||
return true;
|
||||
case '-':
|
||||
if (expr.second) {
|
||||
throw new Error('SyntaxError: binary [-] not implemented yet');
|
||||
}
|
||||
return -(exports.evaluate(expr.first, context));
|
||||
case 'not':
|
||||
return !(exports.evaluate(expr.first, context));
|
||||
case 'and':
|
||||
return (exports.evaluate(expr.first, context)
|
||||
&& exports.evaluate(expr.second, context));
|
||||
case 'or':
|
||||
return (exports.evaluate(expr.first, context)
|
||||
|| exports.evaluate(expr.second, context));
|
||||
case '(':
|
||||
if (expr.second) {
|
||||
var fn = exports.evaluate(expr.first, context), args=[];
|
||||
for (var jj=0; jj<expr.second.length; ++jj) {
|
||||
args.push(exports.evaluate(
|
||||
expr.second[jj], context));
|
||||
}
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
var tuple_exprs = expr.first,
|
||||
tuple_values = [];
|
||||
for (var j=0, len=tuple_exprs.length; j<len; ++j) {
|
||||
tuple_values.push(exports.evaluate(
|
||||
tuple_exprs[j], context));
|
||||
}
|
||||
return create(exports.tuple, {values: tuple_values});
|
||||
case '[':
|
||||
if (expr.second) {
|
||||
throw new Error('SyntaxError: indexing not implemented yet');
|
||||
}
|
||||
var list_exprs = expr.first, list_values = [];
|
||||
for (var k=0; k<list_exprs.length; ++k) {
|
||||
list_values.push(exports.evaluate(
|
||||
list_exprs[k], context));
|
||||
}
|
||||
return create(exports.list, {values: list_values});
|
||||
case '{':
|
||||
var dict_exprs = expr.first, dict_values = {};
|
||||
for(var l=0; l<dict_exprs.length; ++l) {
|
||||
dict_values[exports.evaluate(dict_exprs[l][0], context)] =
|
||||
exports.evaluate(dict_exprs[l][1], context);
|
||||
}
|
||||
return create(exports.dict, {values: dict_values});
|
||||
case '.':
|
||||
if (expr.second.id !== '(name)') {
|
||||
throw new Error('SyntaxError: ' + expr);
|
||||
}
|
||||
return exports.evaluate(expr.first, context)[expr.second.value];
|
||||
default:
|
||||
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
|
||||
}
|
||||
};
|
||||
exports.eval = function (str, context) {
|
||||
return exports.evaluate(
|
||||
exports.parse(
|
||||
exports.tokenize(
|
||||
str)),
|
||||
context);
|
||||
}
|
||||
})(typeof exports === 'undefined' ? py : exports);
|
|
@ -0,0 +1,90 @@
|
|||
var py = require('../lib/py.js'),
|
||||
assert = require('assert');
|
||||
|
||||
// Literals
|
||||
assert.strictEqual(py.eval('1'), 1);
|
||||
assert.strictEqual(py.eval('None'), null);
|
||||
assert.strictEqual(py.eval('False'), false);
|
||||
assert.strictEqual(py.eval('True'), true);
|
||||
assert.strictEqual(py.eval('"somestring"'), 'somestring');
|
||||
assert.strictEqual(py.eval("'somestring'"), 'somestring');
|
||||
assert.deepEqual(py.eval("()").toJSON(), []);
|
||||
assert.deepEqual(py.eval("[]").toJSON(), []);
|
||||
assert.deepEqual(py.eval("{}").toJSON(), {});
|
||||
assert.deepEqual(py.eval("(None, True, False, 0, 1, 'foo')").toJSON(),
|
||||
[null, true, false, 0, 1, 'foo']);
|
||||
assert.deepEqual(py.eval("[None, True, False, 0, 1, 'foo']").toJSON(),
|
||||
[null, true, false, 0, 1, 'foo']);
|
||||
assert.deepEqual(py.eval("{'foo': 1, foo: 2}", {foo: 'bar'}).toJSON(),
|
||||
{foo: 1, bar: 2});
|
||||
|
||||
// Equality tests
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo'", {foo: 'foo'}));
|
||||
// Inequality
|
||||
assert.ok(py.eval(
|
||||
"foo != bar", {foo: 'foo', bar: 'bar'}));
|
||||
|
||||
// Comparisons
|
||||
assert.ok(py.eval('3 < 5'));
|
||||
assert.ok(py.eval('5 >= 3'));
|
||||
assert.ok(py.eval('3 >= 3'));
|
||||
assert.ok(!py.eval('5 < 3'));
|
||||
assert.ok(py.eval('1 < 3 < 5'));
|
||||
assert.ok(py.eval('5 > 3 > 1'));
|
||||
assert.ok(py.eval('1 < 3 > 2 == 2 > -2 not in (0, 1, 2)'));
|
||||
// string rich comparisons
|
||||
assert.ok(py.eval(
|
||||
'date >= current', {date: '2010-06-08', current: '2010-06-05'}));
|
||||
|
||||
// Boolean operators
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' or foo == 'bar'", {foo: 'bar'}));
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' and bar == 'bar'", {foo: 'foo', bar: 'bar'}));
|
||||
// - lazyness, second clauses NameError if not short-circuited
|
||||
assert.ok(py.eval(
|
||||
"foo == 'foo' or bar == 'bar'", {foo: 'foo'}));
|
||||
assert.ok(!py.eval(
|
||||
"foo == 'foo' and bar == 'bar'", {foo: 'bar'}));
|
||||
|
||||
// contains (in)
|
||||
assert.ok(py.eval(
|
||||
"foo in ('foo', 'bar')", {foo: 'bar'}));
|
||||
assert.ok(py.eval('1 in (1, 2, 3, 4)'));
|
||||
assert.ok(!py.eval('1 in (2, 3, 4)'));
|
||||
assert.ok(py.eval('type in ("url",)', {type: 'url'}));
|
||||
assert.ok(!py.eval('type in ("url",)', {type: 'ur'}));
|
||||
assert.ok(py.eval('1 not in (2, 3, 4)'));
|
||||
assert.ok(py.eval('type not in ("url",)', {type: 'ur'}));
|
||||
|
||||
assert.ok(py.eval(
|
||||
"foo in ['foo', 'bar']", {foo: 'bar'}));
|
||||
// string contains
|
||||
assert.ok(py.eval('type in "view"', {type: 'view'}));
|
||||
assert.ok(!py.eval('type in "view"', {type: 'bob'}));
|
||||
assert.ok(py.eval('type in "url"', {type: 'ur'}));
|
||||
|
||||
// Literals
|
||||
assert.strictEqual(py.eval('False'), false);
|
||||
assert.strictEqual(py.eval('True'), true);
|
||||
assert.strictEqual(py.eval('None'), null);
|
||||
assert.ok(py.eval('foo == False', {foo: false}));
|
||||
assert.ok(!py.eval('foo == False', {foo: true}));
|
||||
|
||||
// conversions
|
||||
assert.strictEqual(
|
||||
py.eval('bool(date_deadline)', {bool: py.bool, date_deadline: '2008'}),
|
||||
true);
|
||||
|
||||
// getattr
|
||||
assert.ok(py.eval('foo.bar', {foo: {bar: true}}));
|
||||
assert.ok(!py.eval('foo.bar', {foo: {bar: false}}));
|
||||
|
||||
// complex expressions
|
||||
assert.ok(py.eval(
|
||||
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
|
||||
{state: 'pending', date_deadline: false}));
|
||||
assert.ok(py.eval(
|
||||
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
|
||||
{state: 'pending', date_deadline: '2010-05-08', current_date: '2010-05-08'}));;
|
|
@ -125,7 +125,7 @@ body.openerp, .openerp textarea, .openerp input, .openerp select, .openerp optio
|
|||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
.openerp .login.login_invalid .login_error_message {
|
||||
.openerp .login .login_invalid .login_error_message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* OpenERP Web chrome
|
||||
*---------------------------------------------------------*/
|
||||
|
||||
openerp.web.chrome = function(openerp) {
|
||||
var QWeb = openerp.web.qweb;
|
||||
|
||||
|
@ -300,9 +299,9 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
|
|||
|
||||
var admin = result[1][0];
|
||||
setTimeout(function () {
|
||||
self.stop();
|
||||
self.widget_parent.do_login(
|
||||
info.db, admin.login, admin.password);
|
||||
self.stop();
|
||||
$.unblockUI();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -486,8 +486,6 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web
|
|||
self.user_context = result.context;
|
||||
self.db = result.db;
|
||||
self.session_save();
|
||||
if (self.uid)
|
||||
self.on_session_valid();
|
||||
return true;
|
||||
}).then(success_callback);
|
||||
},
|
||||
|
|
|
@ -598,7 +598,6 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D
|
|||
});
|
||||
openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
||||
virtual_id_prefix: "one2many_v_id_",
|
||||
virtual_id_regex: /one2many_v_id_.*/,
|
||||
debug_mode: true,
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -719,6 +718,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
|
|||
return completion.promise();
|
||||
}
|
||||
});
|
||||
openerp.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
|
||||
|
||||
openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({
|
||||
default_get: function(fields, callback) {
|
||||
return this._super(fields, callback).then(this.on_default_get);
|
||||
|
|
|
@ -41,7 +41,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
this.has_been_loaded = $.Deferred();
|
||||
this.$form_header = null;
|
||||
this.translatable_fields = [];
|
||||
_.defaults(this.options, {"always_show_new_button": true});
|
||||
_.defaults(this.options, {"always_show_new_button": true,
|
||||
"not_interactible_on_create": false});
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
|
@ -74,16 +75,23 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
});
|
||||
this._super();
|
||||
},
|
||||
reposition: function ($e) {
|
||||
this.$element = $e;
|
||||
this.on_loaded();
|
||||
},
|
||||
on_loaded: function(data) {
|
||||
var self = this;
|
||||
this.fields_view = data;
|
||||
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
||||
if (data) {
|
||||
this.fields_view = data;
|
||||
var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
|
||||
|
||||
this.$element.html(QWeb.render(this.form_template, { 'frame': frame, 'view': this }));
|
||||
this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'view': this });
|
||||
}
|
||||
this.$element.html(this.rendered);
|
||||
_.each(this.widgets, function(w) {
|
||||
w.start();
|
||||
});
|
||||
this.$form_header = this.$element.find('#' + this.element_id + '_header');
|
||||
this.$form_header = this.$element.find('.oe_form_header:first');
|
||||
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
|
||||
var action = $(this).data('pager-action');
|
||||
self.on_pager_action(action);
|
||||
|
@ -94,6 +102,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
this.$form_header.find('button.oe_form_button_cancel').click(this.do_cancel);
|
||||
this.$form_header.find('button.oe_form_button_new').click(this.on_button_new);
|
||||
this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate);
|
||||
this.$form_header.find('button.oe_form_button_toggle').click(function () {
|
||||
self.translatable_fields = [];
|
||||
self.widgets = {};
|
||||
self.fields = {};
|
||||
self.$form_header.find('button').unbind('click');
|
||||
self.registry = self.registry === openerp.web.form.widgets
|
||||
? openerp.web.form.readonly
|
||||
: openerp.web.form.widgets;
|
||||
self.on_loaded(self.fields_view);
|
||||
self.reload();
|
||||
});
|
||||
|
||||
if (this.options.sidebar && this.options.sidebar_id) {
|
||||
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
|
||||
|
@ -197,7 +216,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
}
|
||||
},
|
||||
do_update_pager: function(hide_index) {
|
||||
var $pager = this.$element.find('#' + this.element_id + '_header div.oe_form_pager');
|
||||
var $pager = this.$form_header.find('div.oe_form_pager');
|
||||
var index = hide_index ? '-' : this.dataset.index + 1;
|
||||
$pager.find('span.oe_pager_index').html(index);
|
||||
$pager.find('span.oe_pager_count').html(this.dataset.ids.length);
|
||||
|
@ -475,6 +494,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
|
|||
if (self.dataset.parent_view)
|
||||
return self.dataset.parent_view.recursive_save();
|
||||
});
|
||||
},
|
||||
is_interactible_record: function() {
|
||||
var id = this.datarecord.id;
|
||||
if (!id) {
|
||||
if (this.options.not_interactible_on_create)
|
||||
return false;
|
||||
} else if (typeof(id) === "string") {
|
||||
if(openerp.web.BufferedDataSet.virtual_id_regex.test(id))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
openerp.web.FormDialog = openerp.web.Dialog.extend({
|
||||
|
@ -622,6 +652,7 @@ openerp.web.form.compute_domain = function(expr, fields) {
|
|||
|
||||
openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{
|
||||
template: 'Widget',
|
||||
identifier_prefix: 'formview-widget-',
|
||||
/**
|
||||
* @constructs openerp.web.form.Widget
|
||||
* @extends openerp.web.Widget
|
||||
|
@ -635,11 +666,13 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
|
|||
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
|
||||
this.type = this.type || node.tag;
|
||||
this.element_name = this.element_name || this.type;
|
||||
this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
|
||||
this.element_class = [
|
||||
'formview', this.view.view_id, this.element_name,
|
||||
this.view.widgets_counter++].join("_");
|
||||
|
||||
this._super(view, this.element_id);
|
||||
this._super(view);
|
||||
|
||||
this.view.widgets[this.element_id] = this;
|
||||
this.view.widgets[this.element_class] = this;
|
||||
this.children = node.children;
|
||||
this.colspan = parseInt(node.attrs.colspan || 1, 10);
|
||||
this.decrease_max_width = 0;
|
||||
|
@ -652,7 +685,7 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
|
|||
this.width = this.node.attrs.width;
|
||||
},
|
||||
start: function() {
|
||||
this.$element = $('#' + this.element_id);
|
||||
this.$element = this.view.$element.find('.' + this.element_class);
|
||||
},
|
||||
process_modifiers: function() {
|
||||
var compute_domain = openerp.web.form.compute_domain;
|
||||
|
@ -756,13 +789,24 @@ openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
|
|||
for (var i = 0; i < node.children.length; i++) {
|
||||
var n = node.children[i];
|
||||
if (n.tag == "page") {
|
||||
var page = new openerp.web.form.WidgetNotebookPage(this.view, n, this, this.pages.length);
|
||||
var page = new openerp.web.form.WidgetNotebookPage(
|
||||
this.view, n, this, this.pages.length);
|
||||
this.pages.push(page);
|
||||
}
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('> ul > li').each(function (index, tab_li) {
|
||||
var page = self.pages[index],
|
||||
id = _.uniqueId(self.element_name + '-');
|
||||
page.element_id = id;
|
||||
$(tab_li).find('a').attr('href', '#' + id);
|
||||
});
|
||||
this.$element.find('> div').each(function (index, page) {
|
||||
page.id = self.pages[index].element_id;
|
||||
});
|
||||
this.$element.tabs();
|
||||
this.view.on_button_new.add_last(this.do_select_first_visible_tab);
|
||||
},
|
||||
|
@ -784,11 +828,11 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
|||
this.index = index;
|
||||
this.element_name = 'page_' + index;
|
||||
this._super(view, node);
|
||||
this.element_tab_id = this.element_id + '_tab';
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element_tab = $('#' + this.element_tab_id);
|
||||
this.$element_tab = this.notebook.$element.find(
|
||||
'> ul > li:eq(' + this.index + ')');
|
||||
},
|
||||
update_dom: function() {
|
||||
if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
|
||||
|
@ -800,9 +844,9 @@ openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetSeparator',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "WidgetSeparator";
|
||||
this.orientation = node.attrs.orientation || 'horizontal';
|
||||
if (this.orientation === 'vertical') {
|
||||
this.width = '1';
|
||||
|
@ -812,9 +856,10 @@ openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetButton',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "WidgetButton";
|
||||
this.force_disabled = false;
|
||||
if (this.string) {
|
||||
// We don't have button key bindings in the webclient
|
||||
this.string = this.string.replace(/_/g, '');
|
||||
|
@ -826,46 +871,74 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
|
|||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.click(this.on_click);
|
||||
this.$element.find("button").click(this.on_click);
|
||||
},
|
||||
on_click: function() {
|
||||
var self = this;
|
||||
this.force_disabled = true;
|
||||
this.check_disable();
|
||||
this.execute_action().always(function() {
|
||||
self.force_disabled = false;
|
||||
self.check_disable();
|
||||
});
|
||||
},
|
||||
execute_action: function() {
|
||||
var self = this;
|
||||
var exec_action = function() {
|
||||
if (self.node.attrs.confirm) {
|
||||
var def = $.Deferred();
|
||||
var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
|
||||
title: 'Confirm',
|
||||
modal: true,
|
||||
buttons: {
|
||||
Ok: function() {
|
||||
self.on_confirmed();
|
||||
self.on_confirmed().then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
$(self).dialog("close");
|
||||
},
|
||||
Cancel: function() {
|
||||
def.resolve();
|
||||
$(self).dialog("close");
|
||||
}
|
||||
}
|
||||
});
|
||||
return def.promise();
|
||||
} else {
|
||||
self.on_confirmed();
|
||||
return self.on_confirmed();
|
||||
}
|
||||
};
|
||||
if ((!this.node.attrs.special && this.view.dirty_for_user) || !this.view.datarecord.id) {
|
||||
this.view.recursive_save().then(exec_action);
|
||||
return this.view.recursive_save().pipe(exec_action);
|
||||
} else {
|
||||
exec_action();
|
||||
return exec_action();
|
||||
}
|
||||
},
|
||||
on_confirmed: function() {
|
||||
var self = this;
|
||||
|
||||
this.view.do_execute_action(
|
||||
return this.view.do_execute_action(
|
||||
this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
|
||||
self.view.reload();
|
||||
});
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super();
|
||||
this.check_disable();
|
||||
},
|
||||
check_disable: function() {
|
||||
if (this.force_disabled || !this.view.is_interactible_record()) {
|
||||
this.$element.find("button").attr("disabled", "disabled");
|
||||
this.$element.find("button").css("color", "grey");
|
||||
} else {
|
||||
this.$element.find("button").removeAttr("disabled");
|
||||
this.$element.find("button").css("color", "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
||||
template: 'WidgetLabel',
|
||||
init: function(view, node) {
|
||||
this.element_name = 'label_' + node.attrs.name;
|
||||
|
||||
|
@ -876,7 +949,6 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
|||
this.template = "WidgetParagraph";
|
||||
this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
|
||||
} else {
|
||||
this.template = "WidgetLabel";
|
||||
this.colspan = 1;
|
||||
this.width = '1%';
|
||||
this.decrease_max_width = 1;
|
||||
|
@ -895,7 +967,7 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
|
|||
var self = this;
|
||||
this.$element.find("label").dblclick(function() {
|
||||
var widget = self['for'] || self;
|
||||
console.log(widget.element_id , widget);
|
||||
console.log(widget.element_class , widget);
|
||||
window.w = widget;
|
||||
});
|
||||
}
|
||||
|
@ -1037,10 +1109,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
|
|||
});
|
||||
|
||||
openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldChar";
|
||||
},
|
||||
template: 'FieldChar',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('input').change(this.on_ui_change);
|
||||
|
@ -1049,6 +1118,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
|||
this._super.apply(this, arguments);
|
||||
var show_value = openerp.web.format_value(value, this, '');
|
||||
this.$element.find('input').val(show_value);
|
||||
return show_value;
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -1073,10 +1143,7 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldEmail";
|
||||
},
|
||||
template: 'FieldEmail',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
|
@ -1087,18 +1154,11 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
|
|||
} else {
|
||||
location.href = 'mailto:' + this.value;
|
||||
}
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val());
|
||||
}
|
||||
});
|
||||
|
||||
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldUrl";
|
||||
},
|
||||
template: 'FieldUrl',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
|
@ -1249,10 +1309,7 @@ openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldText";
|
||||
},
|
||||
template: 'FieldText',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('textarea').change(this.on_ui_change);
|
||||
|
@ -1285,10 +1342,7 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBoolean";
|
||||
},
|
||||
template: 'FieldBoolean',
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -1319,10 +1373,7 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldProgressBar";
|
||||
},
|
||||
template: 'FieldProgressBar',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('div').progressbar({
|
||||
|
@ -1345,10 +1396,10 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
|
||||
template: 'FieldSelection',
|
||||
init: function(view, node) {
|
||||
var self = this;
|
||||
this._super(view, node);
|
||||
this.template = "FieldSelection";
|
||||
this.values = this.field.selection;
|
||||
_.each(this.values, function(v, i) {
|
||||
if (v[0] === false && v[1] === '') {
|
||||
|
@ -1455,9 +1506,9 @@ openerp.web.form.dialog = function(content, options) {
|
|||
};
|
||||
|
||||
openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2One',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2One";
|
||||
this.limit = 7;
|
||||
this.value = null;
|
||||
this.cm_id = _.uniqueId('m2o_cm_');
|
||||
|
@ -1782,10 +1833,10 @@ var commands = {
|
|||
}
|
||||
};
|
||||
openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldOne2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldOne2Many";
|
||||
this.is_started = $.Deferred();
|
||||
this.form_last_update = $.Deferred();
|
||||
this.disable_utility_classes = true;
|
||||
|
@ -1816,6 +1867,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
|||
}
|
||||
if(view.view_type === "list") {
|
||||
view.options.selectable = self.multi_selection;
|
||||
} else if (view.view_type === "form") {
|
||||
view.options.not_interactible_on_create = true;
|
||||
}
|
||||
views.push(view);
|
||||
});
|
||||
|
@ -1964,9 +2017,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
|
|||
},
|
||||
validate: function() {
|
||||
this.invalid = false;
|
||||
var self = this;
|
||||
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
|
||||
if (self.viewmanager.active_view === "form") {
|
||||
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
|
||||
if (this.viewmanager.active_view === "form") {
|
||||
for (var f in view.fields) {
|
||||
f = view.fields[f];
|
||||
if (!f.is_valid()) {
|
||||
|
@ -2013,7 +2065,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
|||
self.o2m.dataset.on_change();
|
||||
});
|
||||
},
|
||||
parent_view: self.o2m.view
|
||||
parent_view: self.o2m.view,
|
||||
form_view_options: {'not_interactible_on_create':true}
|
||||
}, self.o2m.build_domain(), self.o2m.build_context());
|
||||
pop.on_select_elements.add_last(function() {
|
||||
self.o2m.reload_current_view();
|
||||
|
@ -2029,7 +2082,8 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
|||
parent_view: self.o2m.view,
|
||||
read_function: function() {
|
||||
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
|
||||
}
|
||||
},
|
||||
form_view_options: {'not_interactible_on_create':true}
|
||||
});
|
||||
pop.on_write.add(function(id, data) {
|
||||
self.o2m.dataset.write(id, data, {}, function(r) {
|
||||
|
@ -2040,10 +2094,10 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
|
||||
template: 'FieldMany2Many',
|
||||
multi_selection: false,
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2Many";
|
||||
this.list_id = _.uniqueId("many2many");
|
||||
this.is_started = $.Deferred();
|
||||
},
|
||||
|
@ -2144,6 +2198,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
* - alternative_form_view
|
||||
* - create_function (defaults to a naive saving behavior)
|
||||
* - parent_view
|
||||
* - form_view_options
|
||||
* - list_view_options
|
||||
*/
|
||||
select_element: function(model, options, domain, context) {
|
||||
var self = this;
|
||||
|
@ -2208,7 +2264,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
});
|
||||
self.view_list = new openerp.web.form.SelectCreateListView(self,
|
||||
self.dataset, false,
|
||||
{'deletable': false});
|
||||
_.extend({'deletable': false}, self.options.list_view_options || {}));
|
||||
self.view_list.popup = self;
|
||||
self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
|
||||
self.view_list.do_show();
|
||||
|
@ -2244,7 +2300,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
|
|||
this.view_list.$element.hide();
|
||||
}
|
||||
this.dataset.index = null;
|
||||
this.view_form = new openerp.web.FormView(this, this.dataset, false);
|
||||
this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
|
||||
if (this.options.alternative_form_view) {
|
||||
this.view_form.set_embedded_view(this.options.alternative_form_view);
|
||||
}
|
||||
|
@ -2319,6 +2375,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
|||
* - auto_write (default true)
|
||||
* - read_function
|
||||
* - parent_view
|
||||
* - form_view_options
|
||||
*/
|
||||
show_element: function(model, row_id, context, options) {
|
||||
this.model = model;
|
||||
|
@ -2354,7 +2411,7 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
|
|||
on_write_completed: function() {},
|
||||
setup_form_view: function() {
|
||||
var self = this;
|
||||
this.view_form = new openerp.web.FormView(this, this.dataset, false);
|
||||
this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options);
|
||||
if (this.options.alternative_form_view) {
|
||||
this.view_form.set_embedded_view(this.options.alternative_form_view);
|
||||
}
|
||||
|
@ -2387,9 +2444,9 @@ openerp.web.form.FormOpenDataset = openerp.web.ReadOnlyDataSetSearch.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldReference = openerp.web.form.Field.extend({
|
||||
template: 'FieldReference',
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldReference";
|
||||
this.fields_view = {
|
||||
fields: {
|
||||
selection: {
|
||||
|
@ -2524,10 +2581,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBinaryFile";
|
||||
},
|
||||
template: 'FieldBinaryFile',
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = (value != null && value !== false) ? value : '';
|
||||
|
@ -2555,10 +2609,7 @@ openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
|
|||
});
|
||||
|
||||
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldBinaryImage";
|
||||
},
|
||||
template: 'FieldBinaryImage',
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$image = this.$element.find('img.oe-binary-image');
|
||||
|
@ -2633,6 +2684,93 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
|
|||
}
|
||||
});
|
||||
|
||||
openerp.web.form.WidgetNotebookReadonly = openerp.web.form.WidgetNotebook.extend({
|
||||
template: 'WidgetNotebook.readonly'
|
||||
});
|
||||
openerp.web.form.FieldReadonly = openerp.web.form.Field.extend({
|
||||
|
||||
});
|
||||
openerp.web.form.FieldCharReadonly = openerp.web.form.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
set_value: function (value) {
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = openerp.web.format_value(value, this, '');
|
||||
this.$element.find('div').text(show_value);
|
||||
return show_value;
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldURIReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
template: 'FieldURI.readonly',
|
||||
scheme: null,
|
||||
set_value: function (value) {
|
||||
var displayed = this._super.apply(this, arguments);
|
||||
this.$element.find('a')
|
||||
.attr('href', this.scheme + ':' + displayed)
|
||||
.text(displayed);
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldEmailReadonly = openerp.web.form.FieldURIReadonly.extend({
|
||||
scheme: 'mailto'
|
||||
});
|
||||
openerp.web.form.FieldUrlReadonly = openerp.web.form.FieldURIReadonly.extend({
|
||||
set_value: function (value) {
|
||||
var s = /(\w+):(\.+)/.match(value);
|
||||
if (!(s[0] === 'http' || s[0] === 'https')) { return; }
|
||||
this.scheme = s[0];
|
||||
this._super(s[1]);
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldBooleanReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
set_value: function (value) {
|
||||
this._super(value ? '\u2714' : '\u2718');
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldSelectionReadonly = openerp.web.form.FieldReadonly.extend({
|
||||
template: 'FieldChar.readonly',
|
||||
init: function(view, node) {
|
||||
// lifted straight from r/w version
|
||||
var self = this;
|
||||
this._super(view, node);
|
||||
this.values = this.field.selection;
|
||||
_.each(this.values, function(v, i) {
|
||||
if (v[0] === false && v[1] === '') {
|
||||
self.values.splice(i, 1);
|
||||
}
|
||||
});
|
||||
this.values.unshift([false, '']);
|
||||
},
|
||||
set_value: function (value) {
|
||||
value = value === null ? false : value;
|
||||
value = value instanceof Array ? value[0] : value;
|
||||
var option = _(this.values)
|
||||
.detect(function (record) { return record[0] === value; });
|
||||
this._super(value);
|
||||
this.$element.find('div').text(option ? option[1] : this.values[0][1]);
|
||||
}
|
||||
});
|
||||
openerp.web.form.FieldMany2OneReadonly = openerp.web.form.FieldCharReadonly.extend({
|
||||
set_value: function (value) {
|
||||
value = value || null;
|
||||
this.invalid = false;
|
||||
var self = this;
|
||||
this.tmp_value = value;
|
||||
self.update_dom();
|
||||
self.on_value_changed();
|
||||
var real_set_value = function(rval) {
|
||||
self.$element.find('div').text(rval ? rval[1] : '');
|
||||
};
|
||||
if(typeof(value) === "number") {
|
||||
var dataset = new openerp.web.DataSetStatic(
|
||||
this, this.field.relation, self.build_context());
|
||||
dataset.name_get([value], function(data) {
|
||||
real_set_value(data[0]);
|
||||
}).fail(function() {self.tmp_value = undefined;});
|
||||
} else {
|
||||
setTimeout(function() {real_set_value(value);}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Registry of form widgets, called by :js:`openerp.web.FormView`
|
||||
*/
|
||||
|
@ -2665,6 +2803,22 @@ openerp.web.form.widgets = new openerp.web.Registry({
|
|||
'binary': 'openerp.web.form.FieldBinaryFile',
|
||||
'statusbar': 'openerp.web.form.FieldStatus'
|
||||
});
|
||||
openerp.web.form.readonly = openerp.web.form.widgets.clone({
|
||||
'notebook': 'openerp.web.form.WidgetNotebookReadonly',
|
||||
'char': 'openerp.web.form.FieldCharReadonly',
|
||||
'email': 'openerp.web.form.FieldEmailReadonly',
|
||||
'url': 'openerp.web.form.FieldUrlReadonly',
|
||||
'text': 'openerp.web.form.FieldCharReadonly',
|
||||
'text_wiki' : 'openerp.web.form.FieldCharReadonly',
|
||||
'date': 'openerp.web.form.FieldCharReadonly',
|
||||
'datetime': 'openerp.web.form.FieldCharReadonly',
|
||||
'selection' : 'openerp.web.form.FieldSelectionReadonly',
|
||||
'many2one': 'openerp.web.form.FieldMany2OneReadonly',
|
||||
'boolean': 'openerp.web.form.FieldBooleanReadonly',
|
||||
'float': 'openerp.web.form.FieldCharReadonly',
|
||||
'integer': 'openerp.web.form.FieldCharReadonly',
|
||||
'float_time': 'openerp.web.form.FieldCharReadonly'
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
this.previous_colspan = null;
|
||||
this.colors = null;
|
||||
|
||||
this.columns = [];
|
||||
|
||||
|
@ -75,6 +76,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
}
|
||||
self.compute_aggregates();
|
||||
});
|
||||
|
||||
},
|
||||
/**
|
||||
* Retrieves the view's number of records per page (|| section)
|
||||
|
@ -131,6 +133,31 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
this.$element.addClass('oe-listview');
|
||||
return this.reload_view(null, null, true);
|
||||
},
|
||||
/**
|
||||
* Returns the color for the provided record in the current view (from the
|
||||
* ``@colors`` attribute)
|
||||
*
|
||||
* @param {Record} record record for the current row
|
||||
* @returns {String} CSS color declaration
|
||||
*/
|
||||
color_for: function (record) {
|
||||
if (!this.colors) { return ''; }
|
||||
var context = _.extend({}, record.attributes, {
|
||||
uid: this.session.uid,
|
||||
current_date: new Date().toString('yyyy-MM-dd')
|
||||
// TODO: time, datetime, relativedelta
|
||||
});
|
||||
for(var i=0, len=this.colors.length; i<len; ++i) {
|
||||
var pair = this.colors[i],
|
||||
color = pair[0],
|
||||
expression = pair[1];
|
||||
if (py.evaluate(expression, context)) {
|
||||
return 'color: ' + color + ';';
|
||||
}
|
||||
// TODO: handle evaluation errors
|
||||
}
|
||||
return '';
|
||||
},
|
||||
/**
|
||||
* Called after loading the list view's description, sets up such things
|
||||
* as the view table's columns, renders the table itself and hooks up the
|
||||
|
@ -159,6 +186,17 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
this.fields_view = data;
|
||||
this.name = "" + this.fields_view.arch.attrs.string;
|
||||
|
||||
if (this.fields_view.arch.attrs.colors) {
|
||||
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
|
||||
.compact()
|
||||
.map(function(color_pair) {
|
||||
var pair = color_pair.split(':'),
|
||||
color = pair[0],
|
||||
expr = pair[1];
|
||||
return [color, py.parse(py.tokenize(expr)), expr];
|
||||
}).value();
|
||||
}
|
||||
|
||||
this.setup_columns(this.fields_view.fields, grouped);
|
||||
|
||||
this.$element.html(QWeb.render("ListView", this));
|
||||
|
@ -445,7 +483,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
|
|||
do_select: function (ids, records) {
|
||||
this.$element.find('.oe-list-delete')
|
||||
.attr('disabled', !ids.length);
|
||||
|
||||
if(ids.length) {
|
||||
this.sidebar.do_unfold();
|
||||
} else {
|
||||
this.sidebar.do_fold();
|
||||
}
|
||||
if (!records.length) {
|
||||
this.compute_aggregates();
|
||||
return;
|
||||
|
@ -1333,10 +1375,10 @@ var Record = openerp.web.Class.extend(/** @lends Record# */{
|
|||
* @returns {Object} record displayable in a form view
|
||||
*/
|
||||
toForm: function () {
|
||||
var form_data = {};
|
||||
_(this.attributes).each(function (value, key) {
|
||||
form_data[key] = {value: value};
|
||||
});
|
||||
var form_data = {}, attrs = this.attributes;
|
||||
for(var k in attrs) {
|
||||
form_data[k] = {value: attrs[k]};
|
||||
}
|
||||
|
||||
return {data: form_data};
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ db.web.ActionManager = db.web.Widget.extend({
|
|||
console.log("Action manager can't handle action of type " + action.type, action);
|
||||
return;
|
||||
}
|
||||
this[type](action, on_close);
|
||||
return this[type](action, on_close);
|
||||
},
|
||||
ir_actions_act_window: function (action, on_close) {
|
||||
if (action.target === 'new') {
|
||||
|
@ -468,7 +468,7 @@ db.web.ViewManagerAction = db.web.ViewManager.extend(/** @lends oepnerp.web.View
|
|||
* Intercept do_action resolution from children views
|
||||
*/
|
||||
on_action_executed: function () {
|
||||
new db.web.DataSet(this, 'res.log')
|
||||
return new db.web.DataSet(this, 'res.log')
|
||||
.call('get', [], this.do_display_log);
|
||||
},
|
||||
/**
|
||||
|
@ -768,31 +768,31 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
|
|||
var self = this;
|
||||
var result_handler = function () {
|
||||
if (on_closed) { on_closed.apply(null, arguments); }
|
||||
self.widget_parent.on_action_executed.apply(null, arguments);
|
||||
return self.widget_parent.on_action_executed.apply(null, arguments);
|
||||
};
|
||||
var handler = function (r) {
|
||||
var action = r.result;
|
||||
if (action && action.constructor == Object) {
|
||||
self.rpc('/web/session/eval_domain_and_context', {
|
||||
return self.rpc('/web/session/eval_domain_and_context', {
|
||||
contexts: [dataset.get_context(), action.context || {}, {
|
||||
active_id: record_id || false,
|
||||
active_ids: [record_id || false],
|
||||
active_model: dataset.model
|
||||
}],
|
||||
domains: []
|
||||
}, function (results) {
|
||||
}).pipe(function (results) {
|
||||
action.context = results.context
|
||||
self.do_action(action, result_handler);
|
||||
return self.do_action(action, result_handler);
|
||||
});
|
||||
} else {
|
||||
result_handler();
|
||||
return result_handler();
|
||||
}
|
||||
};
|
||||
|
||||
var context = new db.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
||||
|
||||
if (action_data.special) {
|
||||
handler({result: {"type":"ir.actions.act_window_close"}});
|
||||
return handler({result: {"type":"ir.actions.act_window_close"}});
|
||||
} else if (action_data.type=="object") {
|
||||
return dataset.call_button(action_data.name, [[record_id], context], handler);
|
||||
} else if (action_data.type=="action") {
|
||||
|
|
|
@ -616,7 +616,8 @@
|
|||
</t>
|
||||
</t>
|
||||
<tr t-name="ListView.row" t-att-class="row_parity"
|
||||
t-att-data-id="record.get('id')">
|
||||
t-att-data-id="record.get('id')"
|
||||
t-att-style="view.color_for(record)">
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
|
@ -641,7 +642,7 @@
|
|||
<t t-raw="frame.render()"/>
|
||||
</t>
|
||||
<t t-name="FormView">
|
||||
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
|
||||
<div class="oe_form_header">
|
||||
<div class="oe_form_buttons" t-if="view.options.action_buttons !== false">
|
||||
<!--<button type="button" class="oe_form_button_save">
|
||||
<span class="oe_form_on_update">Save</span>
|
||||
|
@ -654,6 +655,7 @@
|
|||
<!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
|
||||
<button type="button" class="oe_form_button_new">New</button>
|
||||
<button type="button" class="oe_form_button_duplicate oe_form_on_update">Duplicate</button>
|
||||
<button type="button" class="oe_form_button_toggle">Readonly/Editable</button>
|
||||
</div>
|
||||
<div class="oe_form_pager" t-if="view.options.pager !== false">
|
||||
<button type="button" data-pager-action="first">First</button>
|
||||
|
@ -713,8 +715,7 @@
|
|||
t-att-width="td.width"
|
||||
t-att-nowrap="td.nowrap or td.is_field_m2o? 'true' : undefined"
|
||||
t-att-valign="td.table ? 'top' : undefined"
|
||||
t-att-id="td.element_id"
|
||||
t-attf-class="oe_form_frame_cell #{td.classname}"
|
||||
t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}"
|
||||
>
|
||||
<t t-raw="td.render()"/>
|
||||
</td>
|
||||
|
@ -724,8 +725,8 @@
|
|||
</t>
|
||||
<t t-name="WidgetNotebook">
|
||||
<ul>
|
||||
<li t-foreach="widget.pages" t-as="page" t-att-id="page.element_tab_id">
|
||||
<a t-att-href="'#' + page.element_id">
|
||||
<li t-foreach="widget.pages" t-as="page">
|
||||
<a href="#">
|
||||
<t t-esc="page.string"/>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -735,17 +736,23 @@
|
|||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetNotebookPage">
|
||||
<div t-att-id="widget.element_id">
|
||||
<div>
|
||||
<t t-call="WidgetFrame"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="WidgetNotebook.readonly">
|
||||
<t t-foreach="widget.pages" t-as="page">
|
||||
<h3><t t-esc="page.string"/></h3>
|
||||
<t t-raw="page.render()"/>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="WidgetSeparator">
|
||||
<div t-if="widget.orientation !== 'vertical'" t-att-class="'separator ' + widget.orientation">
|
||||
<t t-esc="widget.string"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="WidgetLabel">
|
||||
<label t-att-for="widget.element_id + '_field'"
|
||||
<label t-att-for="widget.element_id"
|
||||
t-att-class="'oe_label' + (widget.help ? '_help' : '')"
|
||||
t-att-title="widget.help">
|
||||
<t t-esc="widget.string"/>
|
||||
|
@ -759,12 +766,22 @@
|
|||
<t t-name="FieldChar">
|
||||
<input type="text" size="1"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-id="widget.element_id"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
/>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
</t>
|
||||
<t t-name="FieldChar.readonly">
|
||||
<div
|
||||
t-att-id="widget.element_id"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%">
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldURI.readonly">
|
||||
<a href="#">#</a>
|
||||
</t>
|
||||
<t t-name="FieldEmail">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tr>
|
||||
|
@ -796,8 +813,8 @@
|
|||
<t t-name="FieldText">
|
||||
<textarea rows="6"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-id="widget.element_id"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"
|
||||
></textarea>
|
||||
<img class="oe_field_translate" t-if="widget.field.translate" src="/web/static/src/img/icons/terp-translate.png" width="16" height="16" border="0"/>
|
||||
|
@ -817,7 +834,7 @@
|
|||
<select
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
style="width: 100%">
|
||||
<t t-foreach="widget.values" t-as="option">
|
||||
<option><t t-esc="option[1]"/></option>
|
||||
|
@ -825,9 +842,9 @@
|
|||
</select>
|
||||
</t>
|
||||
<t t-name="FieldMany2One">
|
||||
<div t-att-id="widget.element_id" class="oe-m2o">
|
||||
<input t-att-id="widget.element_id + '_input'" type="text" size="1" style="width: 100%;"/>
|
||||
<span class="oe-m2o-drop-down-button" t-att-id="widget.element_id + '_drop_down'">
|
||||
<div t-att-class="widget.element_class" class="oe-m2o">
|
||||
<input type="text" size="1" style="width: 100%;"/>
|
||||
<span class="oe-m2o-drop-down-button">
|
||||
<img src="/web/static/src/img/down-arrow.png" /></span>
|
||||
<span class="oe-m2o-cm-button" t-att-id="widget.name + '_open'">
|
||||
<img src="/web/static/src/img/icons/gtk-index.png"/></span>
|
||||
|
@ -850,8 +867,6 @@
|
|||
</ul>
|
||||
</t>
|
||||
<t t-name="FieldOne2Many">
|
||||
<div t-att-id="widget.element_id">
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldMany2Many">
|
||||
<div t-att-id="widget.list_id"></div>
|
||||
|
@ -859,10 +874,10 @@
|
|||
<t t-name="FieldReference">
|
||||
<table border="0" width="100%" cellpadding="0" cellspacing="0" class="oe_frame oe_forms">
|
||||
<tr>
|
||||
<td t-att-id="widget.selection.element_id" class="oe_form_frame_cell oe_form_selection">
|
||||
<td t-attf-class="oe_form_frame_cell oe_form_selection #{widget.selection.element_class}">
|
||||
<t t-raw="widget.selection.render()"/>
|
||||
</td>
|
||||
<td t-att-id="widget.m2o.element_id" class="oe_form_frame_cell oe_form_many2one" nowrap="true">
|
||||
<td class="oe_form_frame_cell oe_form_many2one #{widget.selection.element_class}" nowrap="true">
|
||||
<t t-raw="widget.m2o.render()"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -872,7 +887,7 @@
|
|||
<input type="checkbox"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"/>
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"/>
|
||||
</t>
|
||||
<t t-name="FieldProgressBar">
|
||||
<div t-opentag="true" class="oe-progressbar">
|
||||
|
@ -887,7 +902,7 @@
|
|||
t-att-border="widget.readonly ? 0 : 1"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-name="widget.name"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}"
|
||||
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
|
||||
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
|
||||
/>
|
||||
|
@ -935,7 +950,7 @@
|
|||
<input type="text" size="1"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type" style="width: 100%"
|
||||
t-attf-class="field_#{widget.type} #{widget.element_class}" style="width: 100%"
|
||||
/>
|
||||
</td>
|
||||
<td class="oe-binary" nowrap="true">
|
||||
|
@ -980,7 +995,7 @@
|
|||
</t>
|
||||
<t t-name="WidgetButton">
|
||||
<button type="button"
|
||||
t-att-id="widget.element_id + '_button'"
|
||||
t-attf-class="#{widget.element_class}"
|
||||
t-att-title="widget.help"
|
||||
style="width: 100%" class="button">
|
||||
<img t-if="widget.node.attrs.icon" t-att-src="'/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
|
||||
|
|
|
@ -94,8 +94,10 @@ openerp.web_default_home = function (openerp) {
|
|||
})
|
||||
},
|
||||
install_module: function (module_name) {
|
||||
var self = this;
|
||||
var Modules = new openerp.web.DataSetSearch(
|
||||
this, 'ir.module.module', null, [['name', '=', module_name], ['state', '=', 'uninstalled']]);
|
||||
this, 'ir.module.module', null,
|
||||
[['name', '=', module_name], ['state', '=', 'uninstalled']]);
|
||||
var Upgrade = new openerp.web.DataSet(this, 'base.module.upgrade');
|
||||
|
||||
$.blockUI({message:'<img src="/web/static/src/img/throbber2.gif">'});
|
||||
|
@ -105,13 +107,21 @@ openerp.web_default_home = function (openerp) {
|
|||
[_.pluck(records, 'id'), 'to install', ['uninstalled']],
|
||||
function () {
|
||||
Upgrade.call('upgrade_module', [[]], function () {
|
||||
$.unblockUI();
|
||||
// TODO: less brutal reloading
|
||||
window.location.reload(true);
|
||||
self.run_configuration_wizards();
|
||||
});
|
||||
}
|
||||
)
|
||||
});
|
||||
},
|
||||
run_configuration_wizards: function () {
|
||||
var self = this;
|
||||
new openerp.web.DataSet(this, 'res.config').call('start', [[]], function (action) {
|
||||
$.unblockUI();
|
||||
self.do_action(action, function () {
|
||||
// TODO: less brutal reloading
|
||||
window.location.reload(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# -*- coding: utf-8 -*-
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "Tests",
|
||||
"version": "2.0",
|
||||
"depends": [],
|
||||
"js": ["static/src/js/*.js"],
|
||||
"css": ['static/src/css/*.css'],
|
||||
'active': True,
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.oe-bunchaforms > div {
|
||||
float: left;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
openerp.web_tests = function (db) {
|
||||
db.web.client_actions.add(
|
||||
'buncha-forms', 'instance.web_tests.BunchaForms');
|
||||
db.web_tests = {};
|
||||
db.web_tests.BunchaForms = db.web.Widget.extend({
|
||||
init: function (parent) {
|
||||
this._super(parent);
|
||||
this.dataset = new db.web.DataSetSearch(this, 'test.listview.relations');
|
||||
this.form = new db.web.FormView(this, this.dataset, false, {
|
||||
action_buttons: false,
|
||||
pager: false
|
||||
});
|
||||
this.form.registry = db.web.form.readonly;
|
||||
},
|
||||
render: function () {
|
||||
return '<div class="oe-bunchaforms"></div>';
|
||||
},
|
||||
start: function () {
|
||||
$.when(
|
||||
this.dataset.read_slice(),
|
||||
this.form.appendTo(this.$element)).then(this.on_everything_loaded);
|
||||
},
|
||||
on_everything_loaded: function (slice) {
|
||||
var records = slice[0].records;
|
||||
if (!records.length) {
|
||||
this.form.on_record_loaded({});
|
||||
return;
|
||||
}
|
||||
this.form.on_record_loaded(records[0]);
|
||||
_(records.slice(1)).each(function (record, index) {
|
||||
this.dataset.index = index+1;
|
||||
this.form.reposition($('<div>').appendTo(this.$element));
|
||||
this.form.on_record_loaded(record);
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"simple": {
|
||||
"format": "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": "DEBUG",
|
||||
"formatter": "simple",
|
||||
"stream": "ext://sys.stdout"
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"web": {
|
||||
},
|
||||
"web.common.openerplib": {
|
||||
"level": "INFO"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console"]
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import tempfile
|
||||
import logging
|
||||
import logging.config
|
||||
|
@ -26,6 +27,8 @@ optparser.add_option("--db-filter", dest="dbfilter", default='.*',
|
|||
help="Filter listed database", metavar="REGEXP")
|
||||
optparser.add_option('--addons-path', dest='addons_path', default=[path_addons], action='append',
|
||||
help="Path do addons directory", metavar="PATH")
|
||||
optparser.add_option('--load', dest='server_wide_modules', default=['web'], action='append',
|
||||
help="Load a additional module before login (by default only 'web' is loaded)", metavar="MODULE")
|
||||
|
||||
server_options = optparse.OptionGroup(optparser, "Server configuration")
|
||||
server_options.add_option("-p", "--port", dest="socket_port", default=8002,
|
||||
|
@ -48,7 +51,7 @@ logging_opts = optparse.OptionGroup(optparser, "Logging")
|
|||
logging_opts.add_option("--log-level", dest="log_level", type="choice",
|
||||
default='debug', help="Global logging level", metavar="LOG_LEVEL",
|
||||
choices=['debug', 'info', 'warning', 'error', 'critical'])
|
||||
logging_opts.add_option("--log-config", dest="log_config",
|
||||
logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(os.path.dirname(__file__), "logging.json"),
|
||||
help="Logging configuration file", metavar="FILE")
|
||||
optparser.add_option_group(logging_opts)
|
||||
|
||||
|
@ -60,10 +63,13 @@ if __name__ == "__main__":
|
|||
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
if not options.log_config:
|
||||
logging.basicConfig(level=getattr(logging, options.log_level.upper()))
|
||||
if sys.version_info >= (2, 7):
|
||||
with open(options.log_config) as file:
|
||||
dct = json.load(file)
|
||||
logging.config.dictConfig(dct)
|
||||
logging.getLogger("").setLevel(getattr(logging, options.log_level.upper()))
|
||||
else:
|
||||
logging.config.fileConfig(options.log_config)
|
||||
logging.basicConfig(level=getattr(logging, options.log_level.upper()))
|
||||
|
||||
app = web.common.dispatch.Root(options)
|
||||
|
||||
|
|
Loading…
Reference in New Issue