[MERGE]
bzr revid: jra@tinyerp.com-20110623124509-gd2u46y7qtuertg8 bzr revid: jra@tinyerp.com-20110701051155-mnxx6haa51ltmq3b
This commit is contained in:
commit
e06a526385
|
@ -171,8 +171,9 @@ class Session(openerpweb.Controller):
|
|||
a list of fields to group by, potentially empty (in which case
|
||||
no group by should be performed)
|
||||
"""
|
||||
context = req.session.eval_context(openerpweb.nonliterals.CompoundContext(*contexts))
|
||||
domain = req.session.eval_domain(openerpweb.nonliterals.CompoundDomain(*(domains or [])), context)
|
||||
context, domain = eval_context_and_domain(req.session,
|
||||
openerpweb.nonliterals.CompoundContext(*(contexts or [])),
|
||||
openerpweb.nonliterals.CompoundDomain(*(domains or [])))
|
||||
|
||||
group_by_sequence = []
|
||||
for candidate in (group_by_seq or []):
|
||||
|
@ -233,37 +234,75 @@ class Session(openerpweb.Controller):
|
|||
|
||||
def eval_context_and_domain(session, context, domain=None):
|
||||
e_context = session.eval_context(context)
|
||||
e_domain = session.eval_domain(domain or [], e_context)
|
||||
# should we give the evaluated context as an evaluation context to the domain?
|
||||
e_domain = session.eval_domain(domain or [])
|
||||
|
||||
return (e_context, e_domain)
|
||||
return e_context, e_domain
|
||||
|
||||
def load_actions_from_ir_values(req, key, key2, models, meta, context):
|
||||
context['bin_size'] = False # Possible upstream bug. Antony says not to loose time on this.
|
||||
Values = req.session.model('ir.values')
|
||||
actions = Values.get(key, key2, models, meta, context)
|
||||
|
||||
for _, _, action in actions:
|
||||
clean_action(action, req.session)
|
||||
|
||||
return actions
|
||||
return [(id, name, clean_action(action, req.session))
|
||||
for id, name, action in actions]
|
||||
|
||||
def clean_action(action, session):
|
||||
if action['type'] != 'ir.actions.act_window':
|
||||
return action
|
||||
# values come from the server, we can just eval them
|
||||
if isinstance(action['context'], basestring):
|
||||
if isinstance(action.get('context', None), basestring):
|
||||
action['context'] = eval(
|
||||
action['context'],
|
||||
session.evaluation_context()) or {}
|
||||
|
||||
if isinstance(action['domain'], basestring):
|
||||
if isinstance(action.get('domain', None), basestring):
|
||||
action['domain'] = eval(
|
||||
action['domain'],
|
||||
session.evaluation_context(
|
||||
action['context'])) or []
|
||||
if not action.has_key('flags'):
|
||||
if 'flags' not in action:
|
||||
# Set empty flags dictionary for web client.
|
||||
action['flags'] = dict()
|
||||
return fix_view_modes(action)
|
||||
|
||||
def generate_views(action):
|
||||
"""
|
||||
While the server generates a sequence called "views" computing dependencies
|
||||
between a bunch of stuff for views coming directly from the database
|
||||
(the ``ir.actions.act_window model``), it's also possible for e.g. buttons
|
||||
to return custom view dictionaries generated on the fly.
|
||||
|
||||
In that case, there is no ``views`` key available on the action.
|
||||
|
||||
Since the web client relies on ``action['views']``, generate it here from
|
||||
``view_mode`` and ``view_id``.
|
||||
|
||||
Currently handles two different cases:
|
||||
|
||||
* no view_id, multiple view_mode
|
||||
* single view_id, single view_mode
|
||||
|
||||
:param dict action: action descriptor dictionary to generate a views key for
|
||||
"""
|
||||
view_id = action.get('view_id', False)
|
||||
if isinstance(view_id, (list, tuple)):
|
||||
view_id = view_id[0]
|
||||
|
||||
# providing at least one view mode is a requirement, not an option
|
||||
view_modes = action['view_mode'].split(',')
|
||||
|
||||
if len(view_modes) > 1:
|
||||
if view_id:
|
||||
raise ValueError('Non-db action dictionaries should provide '
|
||||
'either multiple view modes or a single view '
|
||||
'mode and an optional view id.\n\n Got view '
|
||||
'modes %r and view id %r for action %r' % (
|
||||
view_modes, view_id, action))
|
||||
action['views'] = [(False, mode) for mode in view_modes]
|
||||
return
|
||||
action['views'] = [(view_id, view_modes[0])]
|
||||
|
||||
|
||||
def fix_view_modes(action):
|
||||
""" For historical reasons, OpenERP has weird dealings in relation to
|
||||
view_mode and the view_type attribute (on window actions):
|
||||
|
@ -282,16 +321,17 @@ def fix_view_modes(action):
|
|||
:param dict action: an action descriptor
|
||||
:returns: nothing, the action is modified in place
|
||||
"""
|
||||
if 'views' not in action:
|
||||
generate_views(action)
|
||||
|
||||
if action.pop('view_type') != 'form':
|
||||
return
|
||||
|
||||
action['view_mode'] = ','.join(
|
||||
mode if mode != 'tree' else 'list'
|
||||
for mode in action['view_mode'].split(','))
|
||||
action['views'] = [
|
||||
[id, mode if mode != 'tree' else 'list']
|
||||
for id, mode in action['views']
|
||||
]
|
||||
|
||||
return action
|
||||
|
||||
class Menu(openerpweb.Controller):
|
||||
|
@ -383,11 +423,6 @@ class DataSet(openerpweb.Controller):
|
|||
reads = Model.read(ids, fields or False, context)
|
||||
reads.sort(key=lambda obj: ids.index(obj['id']))
|
||||
return reads
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def read(self, request, model, ids, fields=False):
|
||||
return self.do_search_read(request, model, ids, fields,
|
||||
request.session.eval_context(request.context))
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def get(self, request, model, ids, fields=False):
|
||||
|
@ -440,23 +475,31 @@ class DataSet(openerpweb.Controller):
|
|||
return {'result': r}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def unlink(self, request, model, ids=[]):
|
||||
def unlink(self, request, model, ids=()):
|
||||
Model = request.session.model(model)
|
||||
return Model.unlink(ids, request.session.eval_context(request.context))
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def call(self, req, model, method, args, domain_id=None, context_id=None):
|
||||
def call_common(self, req, model, method, args, domain_id=None, context_id=None):
|
||||
domain = args[domain_id] if domain_id and len(args) - 1 >= domain_id else []
|
||||
context = args[context_id] if context_id and len(args) - 1 >= context_id else {}
|
||||
c, d = eval_context_and_domain(req.session, context, domain);
|
||||
if(domain_id and len(args) - 1 >= domain_id):
|
||||
c, d = eval_context_and_domain(req.session, context, domain)
|
||||
if domain_id and len(args) - 1 >= domain_id:
|
||||
args[domain_id] = d
|
||||
if(context_id and len(args) - 1 >= context_id):
|
||||
if context_id and len(args) - 1 >= context_id:
|
||||
args[context_id] = c
|
||||
|
||||
m = req.session.model(model)
|
||||
r = getattr(m, method)(*args)
|
||||
return {'result': r}
|
||||
|
||||
return getattr(req.session.model(model), method)(*args)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def call(self, req, model, method, args, domain_id=None, context_id=None):
|
||||
return {'result': self.call_common(req, model, method, args, domain_id, context_id)}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def call_button(self, req, model, method, args, domain_id=None, context_id=None):
|
||||
action = self.call_common(req, model, method, args, domain_id, context_id)
|
||||
if isinstance(action, dict) and action.get('type') != '':
|
||||
return {'result': clean_action(action, req.session)}
|
||||
return {'result': False}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def exec_workflow(self, req, model, id, signal):
|
||||
|
@ -493,11 +536,23 @@ class View(openerpweb.Controller):
|
|||
return fvg
|
||||
|
||||
def process_view(self, session, fvg, context, transform):
|
||||
# depending on how it feels, xmlrpclib.ServerProxy can translate
|
||||
# XML-RPC strings to ``str`` or ``unicode``. ElementTree does not
|
||||
# enjoy unicode strings which can not be trivially converted to
|
||||
# strings, and it blows up during parsing.
|
||||
|
||||
# So ensure we fix this retardation by converting view xml back to
|
||||
# bit strings.
|
||||
if isinstance(fvg['arch'], unicode):
|
||||
arch = fvg['arch'].encode('utf-8')
|
||||
else:
|
||||
arch = fvg['arch']
|
||||
|
||||
if transform:
|
||||
evaluation_context = session.evaluation_context(context or {})
|
||||
xml = self.transform_view(fvg['arch'], session, evaluation_context)
|
||||
xml = self.transform_view(arch, session, evaluation_context)
|
||||
else:
|
||||
xml = ElementTree.fromstring(fvg['arch'])
|
||||
xml = ElementTree.fromstring(arch)
|
||||
fvg['arch'] = Xml2Json.convert_element(xml)
|
||||
for field in fvg['fields'].values():
|
||||
if field.has_key('views') and field['views']:
|
||||
|
@ -606,15 +661,16 @@ class View(openerpweb.Controller):
|
|||
"""
|
||||
self.parse_domain(elem, 'domain', session)
|
||||
self.parse_domain(elem, 'filter_domain', session)
|
||||
context_string = elem.get('context', '').strip()
|
||||
if context_string:
|
||||
try:
|
||||
elem.set('context',
|
||||
openerpweb.ast.literal_eval(context_string))
|
||||
except ValueError:
|
||||
elem.set('context',
|
||||
openerpweb.nonliterals.Context(
|
||||
session, context_string))
|
||||
for el in ['context', 'default_get']:
|
||||
context_string = elem.get(el, '').strip()
|
||||
if context_string:
|
||||
try:
|
||||
elem.set(el,
|
||||
openerpweb.ast.literal_eval(context_string))
|
||||
except ValueError:
|
||||
elem.set(el,
|
||||
openerpweb.nonliterals.Context(
|
||||
session, context_string))
|
||||
|
||||
class FormView(View):
|
||||
_cp_path = "/base/formview"
|
||||
|
@ -706,7 +762,7 @@ class Binary(openerpweb.Controller):
|
|||
for key, val in cherrypy.request.headers.iteritems():
|
||||
headers[key.lower()] = val
|
||||
size = int(headers.get('content-length', 0))
|
||||
# TODO: might be usefull to have a configuration flag for max-lenght file uploads
|
||||
# TODO: might be useful to have a configuration flag for max-length file uploads
|
||||
try:
|
||||
out = """<script language="javascript" type="text/javascript">
|
||||
var win = window.top.window,
|
||||
|
@ -761,7 +817,6 @@ class Action(openerpweb.Controller):
|
|||
Actions = req.session.model('ir.actions.actions')
|
||||
value = False
|
||||
context = req.session.eval_context(req.context)
|
||||
context["bin_size"] = False
|
||||
action_type = Actions.read([action_id], ['type'], context)
|
||||
if action_type:
|
||||
action = req.session.model(action_type[0]['type']).read([action_id], False,
|
||||
|
@ -769,3 +824,8 @@ class Action(openerpweb.Controller):
|
|||
if action:
|
||||
value = clean_action(action[0], req.session)
|
||||
return {'result': value}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def run(self, req, action_id):
|
||||
return clean_action(req.session.model('ir.actions.server').run(
|
||||
[action_id], req.session.eval_context(req.context)), req.session)
|
||||
|
|
|
@ -35,10 +35,10 @@ var QWeb2 = {
|
|||
return (noquotes ? '' : "'") + s.replace(/\r?\n/g, "\\n").replace(/'/g, "\\'") + (noquotes ? '' : "'");
|
||||
},
|
||||
html_escape: function(s, attribute) {
|
||||
if (s === null || s === undefined) {
|
||||
if (s == null) {
|
||||
return '';
|
||||
}
|
||||
s = (new String(s)).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
s = String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
if (attribute) {
|
||||
s = s.replace(/"/g, '"');
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ var QWeb2 = {
|
|||
if (o !== null && o !== undefined) {
|
||||
if (o.constructor === Array) {
|
||||
if (o[1] !== null && o[1] !== undefined) {
|
||||
return ' ' + o[0] + '="' + this.html_escape(o[1]) + '"';
|
||||
return ' ' + o[0] + '="' + this.html_escape(o[1], true) + '"';
|
||||
}
|
||||
} else if (typeof o === 'object') {
|
||||
var r = '';
|
||||
|
@ -670,15 +670,10 @@ QWeb2.Element = (function() {
|
|||
this.top("(function(" + value + ") {");
|
||||
this.bottom("})(dict);");
|
||||
this.indent();
|
||||
if (this.children.length === 1) {
|
||||
var lines = this.children[0].node.data.split(/\r?\n/);
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
this.top(lines[i]);
|
||||
}
|
||||
} else {
|
||||
this.engine.tools.exception("'js' code block contains " + this.children.length + " nodes instead of 1");
|
||||
var lines = this.engine.tools.xml_node_to_string(this.node, true).split(/\r?\n/);
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
this.top(lines[i]);
|
||||
}
|
||||
// Maybe I could handle the children ?
|
||||
this.process_children = false;
|
||||
},
|
||||
compile_action_debug : function(value) {
|
||||
|
|
|
@ -474,9 +474,10 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,
|
|||
background: #AAAAAA;
|
||||
}
|
||||
.openerp .filter_icon {
|
||||
height: 22px;
|
||||
padding: 1px 2px 0 2px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin: 0;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.openerp .filter_label {
|
||||
font-weight: bold;
|
||||
|
@ -537,21 +538,29 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,
|
|||
.openerp .searchview_group_content {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.openerp .searchview_group_content .oe-searchview-render-line {
|
||||
width:0;
|
||||
}
|
||||
|
||||
|
||||
.openerp .oe-searchview-render-line {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.openerp .searchview_extended_group {
|
||||
border: #696969 solid 1px;
|
||||
padding: 3px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.openerp .searchview_extended_group .oe_adv_filters_and {
|
||||
border-bottom: 1px solid #8E8E8E;
|
||||
text-align: center;
|
||||
margin-top: -10px;
|
||||
}
|
||||
.openerp .searchview_extended_group .oe_adv_filters_and span {
|
||||
background: #F0EEEE;
|
||||
position: relative;
|
||||
top: 0.5em;
|
||||
padding: 0 1em 0 1em;
|
||||
color: #8E8E8E;
|
||||
}
|
||||
|
||||
.openerp .searchview_extended_group.last_group .oe_adv_filters_and {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openerp .oe_search-view-custom-filter-btn span {
|
||||
background: url(../img/icons/gtk-add.png) repeat-y;
|
||||
padding-left: 18px;
|
||||
|
@ -565,6 +574,7 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,
|
|||
|
||||
.openerp .searchview_extended_delete_group {
|
||||
float:right;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openerp .searchview_extended_delete_group span,
|
||||
|
@ -613,6 +623,12 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,
|
|||
.openerp .oe-listview .oe-field-cell button:active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.openerp .oe-listview .oe-field-cell button img {
|
||||
cursor: pointer;
|
||||
}
|
||||
.openerp .oe-listview .oe-field-cell button img:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.openerp .oe-listview th.oe-actions {
|
||||
text-align: left;
|
||||
|
@ -757,13 +773,14 @@ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,
|
|||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
.openerp input.field_date, .openerp input.field_datetime {
|
||||
background: #fff url('../img/ui/field_calendar.png') no-repeat right center;
|
||||
background-origin: content-box;
|
||||
-moz-background-origin: content;
|
||||
-moz-background-origin: content-box;
|
||||
-webkit-background-origin: content-box;
|
||||
.openerp img.ui-datepicker-trigger, .openerp img.ui-datepicker-trigger {
|
||||
margin-left: -20px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
/* http://www.quirksmode.org/dom/inputfile.html
|
||||
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
|
||||
*/
|
||||
|
@ -1020,6 +1037,12 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
|
|||
background: #828282;
|
||||
border-color: #828282;
|
||||
}
|
||||
.openerp .oe_necessary_team_song {
|
||||
display: none;
|
||||
}
|
||||
.openerp.kitten-mode-activated .oe_necessary_team_song {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.openerp .oe-m2o {
|
||||
padding-right: 16px;
|
||||
|
@ -1032,7 +1055,14 @@ background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
|
|||
.openerp .oe-m2o-drop-down-button img,
|
||||
.openerp .oe-m2o-cm-button img {
|
||||
margin-bottom: -4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.openerp .oe-m2o-disabled-cm {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.openerp .oe-dialog-warning p {
|
||||
padding-left: 1em;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -11,8 +11,8 @@ openerp.base.callback = function(obj, method) {
|
|||
for(var i = 0; i < callback.callback_chain.length; i++) {
|
||||
var c = callback.callback_chain[i];
|
||||
if(c.unique) {
|
||||
// al: obscure but shortening C-style hack, sorry
|
||||
callback.callback_chain.pop(i--);
|
||||
callback.callback_chain.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
r = c.callback.apply(c.self, c.args.concat(args));
|
||||
// TODO special value to stop the chain
|
||||
|
@ -137,6 +137,29 @@ openerp.base.Registry = Class.extend( /** @lends openerp.base.Registry# */ {
|
|||
}
|
||||
return object_match;
|
||||
},
|
||||
/**
|
||||
* Tries a number of keys, and returns the first object matching one of
|
||||
* the keys.
|
||||
*
|
||||
* @param {Array} keys a sequence of keys to fetch the object for
|
||||
* @returns {Class} the first class found matching an object
|
||||
*
|
||||
* @throws {openerp.base.KeyNotFound} if none of the keys was in the mapping
|
||||
* @trows {openerp.base.ObjectNotFound} if a found object path was invalid
|
||||
*/
|
||||
get_any: function (keys) {
|
||||
for (var i=0; i<keys.length; ++i) {
|
||||
try {
|
||||
return this.get_object(keys[i]);
|
||||
} catch (e) {
|
||||
if (e instanceof openerp.base.KeyNotFound) {
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw new openerp.base.KeyNotFound(keys.join(','));
|
||||
},
|
||||
/**
|
||||
* Adds a new key and value to the registry.
|
||||
*
|
||||
|
@ -261,13 +284,12 @@ openerp.base.generate_null_object_class = function(claz, add) {
|
|||
}
|
||||
if (prototype.prototype)
|
||||
copy_proto(prototype.prototype);
|
||||
}
|
||||
};
|
||||
copy_proto(claz.prototype);
|
||||
var init = openerp.base.BasicController.prototype.init;
|
||||
newer.init = init;
|
||||
newer.init = openerp.base.BasicController.prototype.init;
|
||||
var tmpclass = claz.extend(newer);
|
||||
return tmpclass.extend(add || {});
|
||||
}
|
||||
};
|
||||
|
||||
openerp.base.Notification = openerp.base.BasicController.extend({
|
||||
init: function(element_id) {
|
||||
|
@ -328,30 +350,39 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
|
|||
* @param {Object} params call parameters
|
||||
* @param {Function} success_callback function to execute on RPC call success
|
||||
* @param {Function} error_callback function to execute on RPC call failure
|
||||
* one
|
||||
* @returns {jQuery.Deferred} jquery-provided ajax deferred
|
||||
*/
|
||||
rpc: function(url, params, success_callback, error_callback) {
|
||||
var self = this;
|
||||
// Construct a JSON-RPC2 request, method is currently unused
|
||||
params.session_id = this.session_id;
|
||||
params.context = typeof(params.context) != "undefined" ? params.context : this.context;
|
||||
|
||||
// Use a default error handler unless defined
|
||||
error_callback = typeof(error_callback) != "undefined" ? error_callback : this.on_rpc_error;
|
||||
// niv: wtf?
|
||||
//params.context = typeof(params.context) != "undefined" ? params.context : this.context;
|
||||
|
||||
// Call using the rpc_mode
|
||||
return this.rpc_ajax(url, {
|
||||
var deferred = $.Deferred();
|
||||
this.rpc_ajax(url, {
|
||||
jsonrpc: "2.0",
|
||||
method: "call",
|
||||
params: params,
|
||||
id:null
|
||||
}, success_callback, error_callback);
|
||||
}).then(function () {deferred.resolve.apply(deferred, arguments);},
|
||||
function(error) {deferred.reject(error, $.Event());});
|
||||
return deferred.fail(function() {
|
||||
deferred.fail(function(error, event) {
|
||||
if (!event.isDefaultPrevented()) {
|
||||
self.on_rpc_error(error, event);
|
||||
}
|
||||
});
|
||||
}).then(success_callback, error_callback).promise();
|
||||
},
|
||||
/**
|
||||
* Raw JSON-RPC call
|
||||
*
|
||||
* @returns {jQuery.Deferred} ajax-based deferred object
|
||||
*/
|
||||
rpc_ajax: function(url, payload, success_callback, error_callback) {
|
||||
rpc_ajax: function(url, payload) {
|
||||
var self = this;
|
||||
this.on_rpc_request();
|
||||
// url can be an $.ajax option object
|
||||
|
@ -366,40 +397,41 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
|
|||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(payload),
|
||||
processData: false,
|
||||
success: function(response, textStatus, jqXHR) {
|
||||
processData: false
|
||||
}, url);
|
||||
var deferred = $.Deferred();
|
||||
$.ajax(ajax).done(function(response, textStatus, jqXHR) {
|
||||
self.on_rpc_response();
|
||||
if (response.error) {
|
||||
if (response.error.data.type == "session_invalid") {
|
||||
self.uid = false;
|
||||
self.on_session_invalid(function() {
|
||||
self.rpc(url, payload.params, success_callback, error_callback);
|
||||
self.rpc(url, payload.params,
|
||||
function() {deferred.resolve.apply(deferred, arguments);},
|
||||
function() {deferred.reject.apply(deferred, arguments);});
|
||||
});
|
||||
} else {
|
||||
error_callback(response.error);
|
||||
deferred.reject(response.error);
|
||||
}
|
||||
} else if (success_callback) {
|
||||
success_callback(response["result"], textStatus, jqXHR);
|
||||
} else {
|
||||
deferred.resolve(response["result"], textStatus, jqXHR);
|
||||
}
|
||||
},
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
self.on_rpc_response();
|
||||
var error = {
|
||||
code: -32098,
|
||||
message: "XmlHttpRequestError " + errorThrown,
|
||||
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
|
||||
};
|
||||
error_callback(error);
|
||||
}
|
||||
}, url);
|
||||
return $.ajax(ajax);
|
||||
deferred.reject(error);
|
||||
});
|
||||
return deferred.promise();
|
||||
},
|
||||
on_rpc_request: function() {
|
||||
},
|
||||
on_rpc_response: function() {
|
||||
},
|
||||
on_rpc_error: function(error) {
|
||||
this.on_log(error.message, error.data.debug);
|
||||
},
|
||||
/**
|
||||
* The session is validated either by login or by restoration of a previous session
|
||||
|
@ -558,7 +590,7 @@ openerp.base.Controller = openerp.base.BasicController.extend( /** @lends opener
|
|||
*/
|
||||
controller_get: function(key) {
|
||||
return this.controller_registry[key];
|
||||
// OR should contrustct it ? setting parent correctly ?
|
||||
// OR should build it ? setting parent correctly ?
|
||||
// function construct(constructor, args) {
|
||||
// function F() {
|
||||
// return constructor.apply(this, args);
|
||||
|
@ -618,7 +650,7 @@ openerp.base.Controller = openerp.base.BasicController.extend( /** @lends opener
|
|||
}
|
||||
// TODO if post prefix
|
||||
//this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
|
||||
};
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Performs a JSON-RPC call
|
||||
|
@ -739,36 +771,47 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
|||
identifier_prefix: 'dialog',
|
||||
init: function (session, options) {
|
||||
this._super(null, session);
|
||||
options = _.extend({
|
||||
this.options = {
|
||||
modal: true,
|
||||
title: this.dialog_title,
|
||||
width: '700px',
|
||||
width: 'auto',
|
||||
min_width: 0,
|
||||
max_width: '100%',
|
||||
height: '500px',
|
||||
height: 'auto',
|
||||
min_height: 0,
|
||||
max_height: '100%',
|
||||
autoOpen: false,
|
||||
buttons: {}
|
||||
}, options);
|
||||
|
||||
options.width = this.get_width(options.width);
|
||||
options.min_width = this.get_width(options.min_width);
|
||||
options.max_width = this.get_width(options.max_width);
|
||||
options.height = this.get_height(options.height);
|
||||
options.min_height = this.get_height(options.min_height);
|
||||
options.max_height = this.get_height(options.max_height);
|
||||
|
||||
if (options.width > options.max_width) options.width = options.max_width;
|
||||
if (options.width < options.min_width) options.width = options.min_width;
|
||||
if (options.height > options.max_height) options.height = options.max_height;
|
||||
if (options.height < options.min_height) options.height = options.min_height;
|
||||
|
||||
};
|
||||
for (var f in this) {
|
||||
if (f.substr(0, 10) == 'on_button_') {
|
||||
options.buttons[f.substr(10)] = this[f];
|
||||
this.options.buttons[f.substr(10)] = this[f];
|
||||
}
|
||||
}
|
||||
this.options = options;
|
||||
if (options) {
|
||||
this.set_options(options);
|
||||
}
|
||||
},
|
||||
set_options: function(options) {
|
||||
options = options || {};
|
||||
options.width = this.get_width(options.width || this.options.width);
|
||||
options.min_width = this.get_width(options.min_width || this.options.min_width);
|
||||
options.max_width = this.get_width(options.max_width || this.options.max_width);
|
||||
options.height = this.get_height(options.height || this.options.height);
|
||||
options.min_height = this.get_height(options.min_height || this.options.min_height);
|
||||
options.max_height = this.get_height(options.max_height || this.options.max_width);
|
||||
|
||||
if (options.width !== 'auto') {
|
||||
if (options.width > options.max_width) options.width = options.max_width;
|
||||
if (options.width < options.min_width) options.width = options.min_width;
|
||||
}
|
||||
if (options.height !== 'auto') {
|
||||
if (options.height > options.max_height) options.height = options.max_height;
|
||||
if (options.height < options.min_height) options.height = options.min_height;
|
||||
}
|
||||
if (!options.title && this.dialog_title) {
|
||||
options.title = this.dialog_title;
|
||||
}
|
||||
_.extend(this.options, options);
|
||||
},
|
||||
get_width: function(val) {
|
||||
return this.get_size(val.toString(), $(window.top).width());
|
||||
|
@ -777,7 +820,7 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
|||
return this.get_size(val.toString(), $(window.top).height());
|
||||
},
|
||||
get_size: function(val, available_size) {
|
||||
if (val == 'auto') {
|
||||
if (val === 'auto') {
|
||||
return val;
|
||||
} else if (val.slice(-1) == "%") {
|
||||
return Math.round(available_size / 100 * parseInt(val.slice(0, -1), 10));
|
||||
|
@ -785,33 +828,62 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
|
|||
return parseInt(val, 10);
|
||||
}
|
||||
},
|
||||
start: function () {
|
||||
start: function (auto_open) {
|
||||
this.$dialog = $('<div id="' + this.element_id + '"></div>').dialog(this.options);
|
||||
if (auto_open !== false) {
|
||||
this.open();
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
open: function(options) {
|
||||
// TODO fme: bind window on resize
|
||||
if (this.template) {
|
||||
this.$element.html(this.render());
|
||||
}
|
||||
this.set_options(options);
|
||||
this.$dialog.dialog(this.options).dialog('open');
|
||||
},
|
||||
close: function(options) {
|
||||
this.$dialog.dialog('close');
|
||||
},
|
||||
stop: function () {
|
||||
this.$dialog("destroy").remove();
|
||||
this.$dialog.dialog('destroy');
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.CrashManager = openerp.base.Controller.extend({
|
||||
init: function(session, element_id) {
|
||||
this._super(session, element_id);
|
||||
openerp.base.CrashManager = openerp.base.Dialog.extend({
|
||||
identifier_prefix: 'dialog_crash',
|
||||
init: function(session) {
|
||||
this._super(session);
|
||||
this.session.on_rpc_error.add(this.on_rpc_error);
|
||||
},
|
||||
on_rpc_error: function(error) {
|
||||
var msg = error.message + "\n" + error.data.debug;
|
||||
this.display_error(msg);
|
||||
on_button_Ok: function() {
|
||||
this.close();
|
||||
},
|
||||
display_error: function(message) {
|
||||
$('<pre></pre>').text(message).dialog({
|
||||
modal: true,
|
||||
buttons: {
|
||||
OK: function() {
|
||||
$(this).dialog("close");
|
||||
}
|
||||
on_rpc_error: function(error) {
|
||||
this.error = error;
|
||||
if (error.data.fault_code) {
|
||||
var split = error.data.fault_code.split('\n')[0].split(' -- ');
|
||||
if (split.length > 1) {
|
||||
error.type = split.shift();
|
||||
error.data.fault_code = error.data.fault_code.substr(error.type.length + 4);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (error.code === 200 && error.type) {
|
||||
this.dialog_title = "OpenERP " + _.capitalize(error.type);
|
||||
this.template = 'DialogWarning';
|
||||
this.open({
|
||||
width: 'auto',
|
||||
height: 'auto'
|
||||
});
|
||||
} else {
|
||||
this.dialog_title = "OpenERP Error";
|
||||
this.template = 'DialogTraceback';
|
||||
this.open({
|
||||
width: '80%',
|
||||
height: '80%'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1041,6 +1113,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
|
|||
this.session = new openerp.base.Session("oe_errors");
|
||||
this.loading = new openerp.base.Loading(this.session, "oe_loading");
|
||||
this.crashmanager = new openerp.base.CrashManager(this.session);
|
||||
this.crashmanager.start(false);
|
||||
|
||||
// Do you autorize this ?
|
||||
openerp.base.Controller.prototype.notification = new openerp.base.Notification("oe_notification");
|
||||
|
|
|
@ -196,9 +196,7 @@ openerp.base.GrouplessDataGroup = openerp.base.DataGroup.extend(
|
|||
this._super(session, model, domain, context, null, level);
|
||||
},
|
||||
list: function (ifGroups, ifRecords) {
|
||||
ifRecords(_.extend(
|
||||
new openerp.base.DataSetSearch(this.session, this.model),
|
||||
{domain: this.domain, context: this.context}));
|
||||
ifRecords(new openerp.base.DataSetSearch(this.session, this.model, this.context, this.domain));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -235,21 +233,20 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
this._super(session);
|
||||
this.model = model;
|
||||
this.context = context || {};
|
||||
this.index = 0;
|
||||
this.count = 0;
|
||||
this.index = null;
|
||||
},
|
||||
start: function() {
|
||||
},
|
||||
previous: function () {
|
||||
this.index -= 1;
|
||||
if (this.index < 0) {
|
||||
this.index = this.count - 1;
|
||||
this.index = this.ids.length - 1;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
next: function () {
|
||||
this.index += 1;
|
||||
if (this.index >= this.count) {
|
||||
if (this.index >= this.ids.length) {
|
||||
this.index = 0;
|
||||
}
|
||||
return this;
|
||||
|
@ -262,7 +259,8 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
return this.rpc('/base/dataset/get', {
|
||||
model: this.model,
|
||||
ids: ids,
|
||||
fields: fields
|
||||
fields: fields,
|
||||
context: this.get_context()
|
||||
}, callback);
|
||||
},
|
||||
/**
|
||||
|
@ -291,14 +289,14 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
return this.rpc('/base/dataset/default_get', {
|
||||
model: this.model,
|
||||
fields: fields,
|
||||
context: this.context
|
||||
context: this.get_context()
|
||||
}, callback);
|
||||
},
|
||||
create: function(data, callback, error_callback) {
|
||||
return this.rpc('/base/dataset/create', {
|
||||
model: this.model,
|
||||
data: data,
|
||||
context: this.context
|
||||
context: this.get_context()
|
||||
}, callback, error_callback);
|
||||
},
|
||||
write: function (id, data, callback) {
|
||||
|
@ -306,12 +304,13 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
model: this.model,
|
||||
id: id,
|
||||
data: data,
|
||||
context: this.context
|
||||
context: this.get_context()
|
||||
}, callback);
|
||||
},
|
||||
unlink: function(ids) {
|
||||
// to implement in children
|
||||
this.notification.notify("Unlink", ids);
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
return this.call_and_eval("unlink", [ids, this.get_context()], null, 1,
|
||||
callback, error_callback);
|
||||
},
|
||||
call: function (method, args, callback, error_callback) {
|
||||
return this.rpc('/base/dataset/call', {
|
||||
|
@ -329,14 +328,28 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
args: args || []
|
||||
}, callback, error_callback);
|
||||
},
|
||||
/**
|
||||
* Arguments:
|
||||
* name='', args=[], operator='ilike', context=None, limit=100
|
||||
call_button: function (method, args, callback, error_callback) {
|
||||
return this.rpc('/base/dataset/call_button', {
|
||||
model: this.model,
|
||||
method: method,
|
||||
domain_id: null,
|
||||
context_id: 1,
|
||||
args: args || []
|
||||
}, callback, error_callback);
|
||||
},
|
||||
name_get: function(ids, callback) {
|
||||
return this.call_and_eval('name_get', [ids, this.get_context()], null, 1, callback);
|
||||
},
|
||||
/*
|
||||
* args = domain
|
||||
*/
|
||||
name_search: function (args, callback, error_callback) {
|
||||
name_search: function (name, args, operator, limit, callback) {
|
||||
return this.call_and_eval('name_search',
|
||||
args, 1, 3,
|
||||
callback, error_callback);
|
||||
[name || '', args || false, operator || 'ilike', this.get_context(), limit || 100],
|
||||
1, 3, callback);
|
||||
},
|
||||
name_create: function(name, callback) {
|
||||
return this.call_and_eval('name_create', [name, this.get_context()], null, 1, callback);
|
||||
},
|
||||
exec_workflow: function (id, signal, callback) {
|
||||
return this.rpc('/base/dataset/exec_workflow', {
|
||||
|
@ -344,26 +357,35 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
|||
id: id,
|
||||
signal: signal
|
||||
}, callback);
|
||||
},
|
||||
get_context: function() {
|
||||
return this.context;
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.DataSetStatic = openerp.base.DataSet.extend({
|
||||
init: function(session, model, ids) {
|
||||
this._super(session, model);
|
||||
init: function(session, model, context, ids) {
|
||||
this._super(session, model, context);
|
||||
// all local records
|
||||
this.ids = ids || [];
|
||||
this.count = this.ids.length;
|
||||
if (this.ids.length) {
|
||||
this.index = 0;
|
||||
}
|
||||
},
|
||||
read_slice: function (fields, offset, limit, callback) {
|
||||
var self = this;
|
||||
offset = offset || 0;
|
||||
var end_pos = limit && limit !== -1 ? offset + limit : undefined;
|
||||
this.read_ids(this.ids.slice(offset, end_pos), fields, callback);
|
||||
},
|
||||
set_ids: function (ids) {
|
||||
this.ids = ids;
|
||||
this.count = this.ids.length;
|
||||
this.index = this.index <= this.ids.length - 1 ?
|
||||
this.index : (this.ids.length > 0 ? this.length - 1 : 0);
|
||||
},
|
||||
unlink: function(ids) {
|
||||
this.on_unlink(ids);
|
||||
return $.Deferred().resolve({result: true});
|
||||
},
|
||||
on_unlink: function(ids) {
|
||||
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
|
||||
|
@ -395,14 +417,13 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
|
|||
model: this.model,
|
||||
fields: fields,
|
||||
domain: this.domain,
|
||||
context: this.context,
|
||||
context: this.get_context(),
|
||||
sort: this.sort(),
|
||||
offset: offset,
|
||||
limit: limit
|
||||
}, function (records) {
|
||||
self.ids.splice(0, self.ids.length);
|
||||
self.offset = offset;
|
||||
self.count = records.length; // TODO: get real count
|
||||
for (var i=0; i < records.length; i++ ) {
|
||||
self.ids.push(records[i].id);
|
||||
}
|
||||
|
@ -437,39 +458,190 @@ openerp.base.DataSetSearch = openerp.base.DataSet.extend({
|
|||
|
||||
this._sort.unshift((reverse ? '-' : '') + field);
|
||||
return undefined;
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
return this._super(ids, function(result) {
|
||||
self.ids = _.without.apply(_, [self.ids].concat(ids));
|
||||
self.index = self.index <= self.ids.length - 1 ?
|
||||
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
|
||||
if (callback)
|
||||
callback(result);
|
||||
}, error_callback);
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.BufferedDataSet = openerp.base.DataSetStatic.extend({
|
||||
virtual_id_prefix: "one2many_v_id_",
|
||||
virtual_id_regex: /one2many_v_id_.*/,
|
||||
debug_mode: true,
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.reset_ids([]);
|
||||
},
|
||||
create: function(data, callback, error_callback) {
|
||||
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data};
|
||||
this.to_create.push(cached);
|
||||
this.cache.push(cached);
|
||||
this.on_change();
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function() {to_return.resolve({result: cached.id});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
write: function (id, data, callback) {
|
||||
var self = this;
|
||||
var record = _.detect(this.to_create, function(x) {return x.id === id;});
|
||||
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
|
||||
if (record) {
|
||||
$.extend(record.values, data);
|
||||
} else {
|
||||
record = {id: id, values: data};
|
||||
self.to_write.push(record);
|
||||
}
|
||||
var cached = _.detect(this.cache, function(x) {return x.id === id;});
|
||||
$.extend(cached.values, record.values);
|
||||
this.on_change();
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function () {to_return.resolve({result: true});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
var self = this;
|
||||
_.each(ids, function(id) {
|
||||
if (! _.detect(self.to_create, function(x) { return x.id === id; })) {
|
||||
self.to_delete.push({id: id})
|
||||
}
|
||||
});
|
||||
this.to_create = _.reject(this.to_create, function(x) { return _.include(ids, x.id);});
|
||||
this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);});
|
||||
this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);});
|
||||
this.set_ids(_.without.apply(_, [this.ids].concat(ids)));
|
||||
this.on_change();
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function () {to_return.resolve({result: true});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
reset_ids: function(ids) {
|
||||
this.set_ids(ids);
|
||||
this.to_delete = [];
|
||||
this.to_create = [];
|
||||
this.to_write = [];
|
||||
this.cache = [];
|
||||
this.delete_all = false;
|
||||
},
|
||||
on_change: function() {},
|
||||
read_ids: function (ids, fields, callback) {
|
||||
var self = this;
|
||||
var to_get = [];
|
||||
_.each(ids, function(id) {
|
||||
var cached = _.detect(self.cache, function(x) {return x.id === id;});
|
||||
var created = _.detect(self.to_create, function(x) {return x.id === id;});
|
||||
if (created) {
|
||||
_.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;});
|
||||
} else {
|
||||
if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined}))
|
||||
to_get.push(id);
|
||||
}
|
||||
});
|
||||
var completion = $.Deferred().then(callback);
|
||||
var return_records = function() {
|
||||
var records = _.map(ids, function(id) {
|
||||
return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id});
|
||||
});
|
||||
if (self.debug_mode) {
|
||||
if (_.include(records, undefined)) {
|
||||
throw "Record not correctly loaded";
|
||||
}
|
||||
}
|
||||
setTimeout(function () {completion.resolve(records);}, 0);
|
||||
}
|
||||
if(to_get.length > 0) {
|
||||
var rpc_promise = this._super(to_get, fields, function(records) {
|
||||
_.each(records, function(record, index) {
|
||||
var id = to_get[index];
|
||||
var cached = _.detect(self.cache, function(x) {return x.id === id;});
|
||||
if (!cached) {
|
||||
self.cache.push({id: id, values: record});
|
||||
} else {
|
||||
// I assume cache value is prioritary
|
||||
_.defaults(cached.values, record);
|
||||
}
|
||||
});
|
||||
return_records();
|
||||
});
|
||||
$.when(rpc_promise).fail(function() {completion.reject();});
|
||||
} else {
|
||||
return_records();
|
||||
}
|
||||
return completion.promise();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.ReadOnlyDataSetSearch = openerp.base.DataSetSearch.extend({
|
||||
create: function(data, callback, error_callback) {
|
||||
this.on_create(data);
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function () {to_return.resolve({"result": undefined});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
on_create: function(data) {},
|
||||
write: function (id, data, callback) {
|
||||
this.on_write(id);
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function () {to_return.resolve({"result": true});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
on_write: function(id) {},
|
||||
unlink: function(ids, callback, error_callback) {
|
||||
this.on_unlink(ids);
|
||||
var to_return = $.Deferred().then(callback);
|
||||
setTimeout(function () {to_return.resolve({"result": true});}, 0);
|
||||
return to_return.promise();
|
||||
},
|
||||
on_unlink: function(ids) {}
|
||||
});
|
||||
|
||||
openerp.base.CompoundContext = function() {
|
||||
this.__ref = "compound_context";
|
||||
this.__contexts = [];
|
||||
this.__eval_context = null;
|
||||
var self = this;
|
||||
_.each(arguments, function(x) {
|
||||
self.add(x);
|
||||
});
|
||||
};
|
||||
openerp.base.CompoundContext.prototype.add = function(context) {
|
||||
if (context.__ref === "compound_context")
|
||||
this.__contexts = this.__contexts.concat(context.__contexts);
|
||||
else
|
||||
this.__contexts.push(context);
|
||||
this.__contexts.push(context);
|
||||
return this;
|
||||
};
|
||||
openerp.base.CompoundContext.prototype.set_eval_context = function(eval_context) {
|
||||
this.__eval_context = eval_context;
|
||||
return this;
|
||||
};
|
||||
openerp.base.CompoundContext.prototype.get_eval_context = function() {
|
||||
return this.__eval_context;
|
||||
};
|
||||
|
||||
openerp.base.CompoundDomain = function() {
|
||||
this.__ref = "compound_domain";
|
||||
this.__domains = [];
|
||||
this.__eval_context = null;
|
||||
var self = this;
|
||||
_.each(arguments, function(x) {
|
||||
self.add(x);
|
||||
});
|
||||
};
|
||||
openerp.base.CompoundDomain.prototype.add = function(domain) {
|
||||
if (domain.__ref === "compound_domain")
|
||||
this.__domains = this.__domains.concat(domain.__domains);
|
||||
else
|
||||
this.__domains.push(domain);
|
||||
this.__domains.push(domain);
|
||||
return this;
|
||||
};
|
||||
openerp.base.CompoundDomain.prototype.set_eval_context = function(eval_context) {
|
||||
this.__eval_context = eval_context;
|
||||
return this;
|
||||
};
|
||||
openerp.base.CompoundDomain.prototype.get_eval_context = function() {
|
||||
return this.__eval_context;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
|||
* @param {Boolean} [options.selectable=true] determines whether view rows are selectable (e.g. via a checkbox)
|
||||
* @param {Boolean} [options.header=true] should the list's header be displayed
|
||||
* @param {Boolean} [options.deletable=true] are the list rows deletable
|
||||
* @param {null|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button.
|
||||
* @param {void|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button.
|
||||
* @param {Boolean} [options.sortable=true] is it possible to sort the table by clicking on column headers
|
||||
* @param {Boolean} [options.reorderable=true] is it possible to reorder list rows
|
||||
*
|
||||
|
@ -232,7 +232,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
|||
* If the index is null, ``switch_to_record`` asks for the creation of a
|
||||
* new record.
|
||||
*
|
||||
* @param {Number|null} index the record index (in the current dataset) to switch to
|
||||
* @param {Number|void} index the record index (in the current dataset) to switch to
|
||||
* @param {String} [view="form"] the view type to switch to
|
||||
*/
|
||||
select_record:function (index, view) {
|
||||
|
@ -336,25 +336,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
|||
}
|
||||
var self = this;
|
||||
return $.when(this.dataset.unlink(ids)).then(function () {
|
||||
_(self.rows).chain()
|
||||
.map(function (row, index) {
|
||||
return {
|
||||
index: index,
|
||||
id: row.data.id.value
|
||||
};})
|
||||
.filter(function (record) {
|
||||
return _.contains(ids, record.id);
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
// sort in reverse index order, so we delete from the end
|
||||
// and don't blow up the following indexes (leading to
|
||||
// removing the wrong records from the visible list)
|
||||
return b.index - a.index;
|
||||
})
|
||||
.each(function (record) {
|
||||
self.rows.splice(record.index, 1);
|
||||
});
|
||||
// TODO only refresh modified rows
|
||||
self.reload_content();
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
@ -571,7 +553,7 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
|
|||
this.$current.remove();
|
||||
}
|
||||
this.$current = this.$_element.clone(true);
|
||||
this.$current.empty().append($(QWeb.render('ListView.rows', this)));
|
||||
this.$current.empty().append(QWeb.render('ListView.rows', this));
|
||||
},
|
||||
get_fields_view: function () {
|
||||
// deep copy of view
|
||||
|
@ -895,7 +877,7 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
|
|||
|
||||
var d = new $.Deferred();
|
||||
dataset.read_slice(
|
||||
_.filter(_.pluck(this.columns, 'name'), _.identity),
|
||||
_.filter(_.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name'), _.identity),
|
||||
0, false,
|
||||
function (records) {
|
||||
var form_records = _(records).map(
|
||||
|
|
|
@ -377,6 +377,7 @@ openerp.base.search.FilterGroup = openerp.base.search.Widget.extend({
|
|||
init: function (filters, view) {
|
||||
this._super(view);
|
||||
this.filters = filters;
|
||||
this.length = filters.length;
|
||||
},
|
||||
start: function () {
|
||||
this._super();
|
||||
|
@ -731,6 +732,7 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
|
|||
var render = group.render();
|
||||
this.$element.find('.searchview_extended_groups_list').append(render);
|
||||
group.start();
|
||||
this.check_last_element();
|
||||
},
|
||||
start: function () {
|
||||
this._super();
|
||||
|
@ -758,11 +760,9 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
|
|||
on_activate: function() {
|
||||
this.add_group();
|
||||
var table = this.$element.closest("table.oe-searchview-render-line");
|
||||
if(table.css("display") == "none") {
|
||||
table.css("display", "");
|
||||
if(this.$element.hasClass("folded")) {
|
||||
this.$element.toggleClass("folded expanded");
|
||||
}
|
||||
table.css("display", "");
|
||||
if(this.$element.hasClass("folded")) {
|
||||
this.$element.toggleClass("folded expanded");
|
||||
}
|
||||
},
|
||||
hide: function() {
|
||||
|
@ -771,6 +771,10 @@ openerp.base.search.ExtendedSearch = openerp.base.BaseWidget.extend({
|
|||
if(this.$element.hasClass("expanded")) {
|
||||
this.$element.toggleClass("folded expanded");
|
||||
}
|
||||
},
|
||||
check_last_element: function() {
|
||||
_.each(this.children, function(x) {x.set_last_group(false);});
|
||||
this.children[this.children.length - 1].set_last_group(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -796,9 +800,6 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
|
|||
});
|
||||
var delete_btn = this.$element.find('.searchview_extended_delete_group');
|
||||
delete_btn.click(function (e) {
|
||||
if (_this.parent.children.length == 1) {
|
||||
_this.parent.hide();
|
||||
}
|
||||
_this.stop();
|
||||
});
|
||||
},
|
||||
|
@ -811,6 +812,19 @@ openerp.base.search.ExtendedSearchGroup = openerp.base.BaseWidget.extend({
|
|||
return [].concat(choice == "none" ? ['!'] : [],
|
||||
_.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
|
||||
props);
|
||||
},
|
||||
stop: function() {
|
||||
var parent = this.parent;
|
||||
if (this.parent.children.length == 1)
|
||||
this.parent.hide();
|
||||
this._super();
|
||||
parent.check_last_element();
|
||||
},
|
||||
set_last_group: function(is_last) {
|
||||
if(is_last)
|
||||
this.$element.addClass("last_group");
|
||||
else
|
||||
this.$element.removeClass("last_group");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -838,6 +852,11 @@ openerp.base.search.ExtendedSearchProposition = openerp.base.BaseWidget.extend({
|
|||
_this.stop();
|
||||
});
|
||||
},
|
||||
stop: function() {
|
||||
if (this.parent.children.length == 1)
|
||||
this.parent.stop();
|
||||
this._super();
|
||||
},
|
||||
changed: function() {
|
||||
var nval = this.$element.find(".searchview_extended_prop_field").val();
|
||||
if(this.attrs.selected == null || nval != this.attrs.selected.name) {
|
||||
|
|
|
@ -18,7 +18,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
|||
* Process an action
|
||||
* Supported actions: act_window
|
||||
*/
|
||||
do_action: function(action) {
|
||||
do_action: function(action, on_closed) {
|
||||
var self = this;
|
||||
action.flags = _.extend({
|
||||
sidebar : action.target != 'new',
|
||||
|
@ -31,6 +31,9 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
|||
// instantiate the right controllers by understanding the action
|
||||
switch (action.type) {
|
||||
case 'ir.actions.act_window':
|
||||
if (!action.target && this.dialog_stack.length) {
|
||||
action.flags.new_window = true;
|
||||
}
|
||||
if (action.target == 'new') {
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
$('<div>', {id: element_id}).dialog({
|
||||
|
@ -44,6 +47,8 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
|||
});
|
||||
var viewmanager = new openerp.base.ViewManagerAction(this.session, element_id, action);
|
||||
viewmanager.start();
|
||||
viewmanager.on_act_window_closed.add(on_closed);
|
||||
viewmanager.is_dialog = true;
|
||||
this.dialog_stack.push(viewmanager);
|
||||
} else if (action.flags.new_window) {
|
||||
action.flags.new_window = false;
|
||||
|
@ -62,9 +67,20 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
|||
break;
|
||||
case 'ir.actions.act_window_close':
|
||||
var dialog = this.dialog_stack.pop();
|
||||
if (!action.special) {
|
||||
dialog.on_act_window_closed();
|
||||
}
|
||||
dialog.$element.dialog('destroy');
|
||||
dialog.stop();
|
||||
break;
|
||||
case 'ir.actions.server':
|
||||
this.rpc('/base/action/run', {
|
||||
action_id: action.id,
|
||||
context: {active_id: 66, active_ids: [66], active_model: 'ir.ui.menu'}
|
||||
}).then(function (action) {
|
||||
self.do_action(action, on_closed)
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.log("Action manager can't handle action of type " + action.type, action);
|
||||
}
|
||||
|
@ -84,6 +100,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
|||
this.flags = this.flags || {};
|
||||
this.sidebar = new openerp.base.NullSidebar();
|
||||
this.registry = openerp.base.views;
|
||||
this.is_dialog = false;
|
||||
},
|
||||
/**
|
||||
* @returns {jQuery.Deferred} initial view loading promise
|
||||
|
@ -113,7 +130,8 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
|||
* @returns {jQuery.Deferred} new view loading promise
|
||||
*/
|
||||
on_mode_switch: function(view_type) {
|
||||
var view_promise;
|
||||
var self = this,
|
||||
view_promise;
|
||||
this.active_view = view_type;
|
||||
var view = this.views[view_type];
|
||||
if (!view.controller) {
|
||||
|
@ -124,14 +142,28 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
|||
if (view.embedded_view) {
|
||||
controller.set_embedded_view(view.embedded_view);
|
||||
}
|
||||
if (view_type === 'list' && this.flags.search_view === false && this.action && this.action['auto_search']) {
|
||||
// In case the search view is not instantiated: manually call ListView#search
|
||||
var domains = !_(self.action.domain).isEmpty()
|
||||
? [self.action.domain] : [],
|
||||
contexts = !_(self.action.context).isEmpty()
|
||||
? [self.action.context] : [];
|
||||
controller.on_loaded.add({
|
||||
callback: function () {
|
||||
controller.do_search(domains, contexts, []);
|
||||
},
|
||||
position: 'last',
|
||||
unique: true
|
||||
});
|
||||
}
|
||||
view_promise = controller.start();
|
||||
var self = this;
|
||||
$.when(view_promise).then(function() {
|
||||
self.on_controller_inited(view_type, controller);
|
||||
});
|
||||
this.views[view_type].controller = controller;
|
||||
}
|
||||
|
||||
|
||||
if (this.searchview) {
|
||||
if (view.controller.searchable === false) {
|
||||
this.searchview.hide();
|
||||
|
@ -145,12 +177,13 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
|||
.filter('[data-view-type="' + view_type + '"]')
|
||||
.attr('disabled', true);
|
||||
|
||||
for (var i in this.views) {
|
||||
if (this.views[i].controller) {
|
||||
if (i === view_type) {
|
||||
$.when(view_promise).then(this.views[i].controller.do_show);
|
||||
for (var view_name in this.views) {
|
||||
if (!this.views.hasOwnProperty(view_name)) { continue; }
|
||||
if (this.views[view_name].controller) {
|
||||
if (view_name === view_type) {
|
||||
$.when(view_promise).then(this.views[view_name].controller.do_show);
|
||||
} else {
|
||||
this.views[i].controller.do_hide();
|
||||
this.views[view_name].controller.do_hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,6 +218,11 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
|||
});
|
||||
return this.searchview.start();
|
||||
},
|
||||
/**
|
||||
* Called when this view manager has been created by an action 'act_window@target=new' is closed
|
||||
*/
|
||||
on_act_window_closed : function() {
|
||||
},
|
||||
/**
|
||||
* Called when one of the view want to execute an action
|
||||
*/
|
||||
|
@ -226,12 +264,14 @@ openerp.base.NullViewManager = openerp.base.generate_null_object_class(openerp.b
|
|||
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, action) {
|
||||
var dataset;
|
||||
if(!action.res_id) {
|
||||
dataset = new openerp.base.DataSetSearch(session, action.res_model);
|
||||
if (!action.res_id) {
|
||||
dataset = new openerp.base.DataSetSearch(session, action.res_model, action.context || null);
|
||||
} else {
|
||||
dataset = new openerp.base.DataSetStatic(session, action.res_model);
|
||||
dataset.ids = [action.res_id];
|
||||
dataset.count = 1;
|
||||
dataset = new openerp.base.DataSetStatic(session, action.res_model, {}, [action.res_id]);
|
||||
if (action.context) {
|
||||
// TODO fme: should normalize all DataSets constructors to (session, model, context, ...)
|
||||
dataset.context = action.context;
|
||||
}
|
||||
}
|
||||
this._super(session, element_id, dataset, action.views);
|
||||
this.action = action;
|
||||
|
@ -261,16 +301,18 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
|||
}
|
||||
});
|
||||
|
||||
// init search view
|
||||
var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
|
||||
if (this.flags.search_view !== false) {
|
||||
// init search view
|
||||
var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
|
||||
|
||||
var searchview_loaded = this.setup_search_view(
|
||||
searchview_id || false, search_defaults);
|
||||
var searchview_loaded = this.setup_search_view(
|
||||
searchview_id || false, search_defaults);
|
||||
|
||||
// schedule auto_search
|
||||
if (searchview_loaded != null && this.action['auto_search']) {
|
||||
$.when(searchview_loaded, inital_view_loaded)
|
||||
.then(this.searchview.do_search);
|
||||
// schedule auto_search
|
||||
if (searchview_loaded != null && this.action['auto_search']) {
|
||||
$.when(searchview_loaded, inital_view_loaded)
|
||||
.then(this.searchview.do_search);
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: function() {
|
||||
|
@ -322,16 +364,12 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
|||
},
|
||||
do_refresh: function(new_view) {
|
||||
var view = this.view_manager.active_view;
|
||||
the_condition = this.sections.length > 0 && _.detect(this.sections,
|
||||
var the_condition = this.sections.length > 0 && _.detect(this.sections,
|
||||
function(x) {return x.elements.length > 0;}) != undefined
|
||||
&& (!new_view || view != 'list');
|
||||
if (!the_condition) {
|
||||
this.$element.addClass('closed-sidebar');
|
||||
this.$element.removeClass('open-sidebar');
|
||||
} else {
|
||||
this.$element.addClass('open-sidebar');
|
||||
this.$element.removeClass('closed-sidebar');
|
||||
}
|
||||
|
||||
this.$element.toggleClass('open-sidebar', the_condition)
|
||||
.toggleClass('closed-sidebar', !the_condition);
|
||||
|
||||
this.$element.html(QWeb.render("ViewManager.sidebar.internal", { sidebar: this, view: view }));
|
||||
|
||||
|
@ -362,6 +400,25 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
|||
|
||||
openerp.base.NullSidebar = openerp.base.generate_null_object_class(openerp.base.Sidebar);
|
||||
|
||||
openerp.base.Export = openerp.base.Dialog.extend({
|
||||
dialog_title: "Export",
|
||||
template: 'ExportDialog',
|
||||
identifier_prefix: 'export_dialog',
|
||||
init: function (session, model, domain) {
|
||||
this._super();
|
||||
},
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.html(this.render());
|
||||
},
|
||||
on_button_Export: function() {
|
||||
console.log("Export")
|
||||
},
|
||||
on_button_Cancel: function() {
|
||||
this.$element.dialog("close");
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.View = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
|
@ -376,26 +433,46 @@ openerp.base.View = openerp.base.Controller.extend({
|
|||
* @param {Object} [record_id] the identifier of the object on which the action is to be applied
|
||||
* @param {Function} on_no_action callback to execute if the action does not generate any result (no new action)
|
||||
*/
|
||||
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action) {
|
||||
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action, on_closed) {
|
||||
var self = this;
|
||||
var handler = function (r) {
|
||||
if (r.result && r.result.constructor == Object) {
|
||||
action_manager.do_action(r.result);
|
||||
var action = r.result;
|
||||
if (action && action.constructor == Object) {
|
||||
action.context = action.context || {};
|
||||
_.extend(action.context, {
|
||||
active_id: record_id || false,
|
||||
active_ids: [record_id || false],
|
||||
active_model: dataset.model
|
||||
});
|
||||
action.flags = {
|
||||
sidebar : false,
|
||||
search_view : false,
|
||||
views_switcher : false,
|
||||
action_buttons : false,
|
||||
pager : false
|
||||
};
|
||||
action_manager.do_action(action, on_closed);
|
||||
if (self.view_manager.is_dialog && action.type != 'ir.actions.act_window_close') {
|
||||
handler({
|
||||
result : { type: 'ir.actions.act_window_close' }
|
||||
});
|
||||
}
|
||||
} else {
|
||||
on_no_action(r.result);
|
||||
on_no_action(action);
|
||||
}
|
||||
};
|
||||
|
||||
if (action_data.special) {
|
||||
handler({
|
||||
result : { type: 'ir.actions.act_window_close' }
|
||||
result : { type: 'ir.actions.act_window_close', special: action_data.special }
|
||||
});
|
||||
} else {
|
||||
var context = _.extend({}, dataset.context, action_data.context || {});
|
||||
var context = new openerp.base.CompoundContext(dataset.context, action_data.context || {});
|
||||
switch(action_data.type) {
|
||||
case 'object':
|
||||
return dataset.call(action_data.name, [[record_id], context], handler);
|
||||
return dataset.call_button(action_data.name, [[record_id], context], handler);
|
||||
case 'action':
|
||||
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10) }, handler);
|
||||
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10), context: context }, handler);
|
||||
default:
|
||||
return dataset.exec_workflow(record_id, action_data.name, handler);
|
||||
}
|
||||
|
@ -424,6 +501,41 @@ openerp.base.ProcessView = openerp.base.Controller.extend({
|
|||
openerp.base.HelpView = openerp.base.Controller.extend({
|
||||
});
|
||||
|
||||
openerp.base.json_node_to_xml = function(node, single_quote, indent) {
|
||||
// For debugging purpose, this function will convert a json node back to xml
|
||||
// Maybe usefull for xml view editor
|
||||
if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
|
||||
throw("Node a json node");
|
||||
}
|
||||
indent = indent || 0;
|
||||
var sindent = new Array(indent + 1).join('\t'),
|
||||
r = sindent + '<' + node.tag;
|
||||
for (var attr in node.attrs) {
|
||||
var vattr = node.attrs[attr];
|
||||
if (typeof(vattr) !== 'string') {
|
||||
// domains, ...
|
||||
vattr = JSON.stringify(vattr);
|
||||
}
|
||||
vattr = vattr.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
if (single_quote) {
|
||||
vattr = vattr.replace(/"/g, "'");
|
||||
}
|
||||
r += ' ' + attr + '="' + vattr + '"';
|
||||
}
|
||||
if (node.children.length) {
|
||||
r += '>\n';
|
||||
var childs = [];
|
||||
for (var i = 0, ii = node.children.length; i < ii; i++) {
|
||||
childs.push(openerp.base.json_node_to_xml(node.children[i], single_quote, indent + 1));
|
||||
}
|
||||
r += childs.join('\n');
|
||||
r += '\n' + sindent + '</' + node.tag + '>';
|
||||
return r;
|
||||
} else {
|
||||
return r + '/>';
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
|
|
|
@ -42,6 +42,9 @@
|
|||
<td colspan="2">
|
||||
<div id="oe_footer" class="oe_footer">
|
||||
<p class="oe_footer_powered">Powered by <a href="http://www.openerp.com">openerp.com</a>.</p>
|
||||
<iframe class="oe_necessary_team_song" style="height:1;" height="1"
|
||||
src="http://www.youtube.com/embed/QH2-TGUlwu4?autoplay=1"
|
||||
frameborder="0"></iframe>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -90,7 +93,7 @@
|
|||
t-att-value="selected_password || ''"/></td>
|
||||
</tr>
|
||||
<tr t-if="has_local_storage">
|
||||
<td><label for="remember">Rember password:</label></td>
|
||||
<td><label for="remember">Remember password:</label></td>
|
||||
<td class="oe_remember">
|
||||
<t t-if="remember">
|
||||
<input type="checkbox" name="remember" checked="yes"/>
|
||||
|
@ -341,7 +344,7 @@
|
|||
<t t-set="value" t-value="row['data'][column.id].value"/>
|
||||
<t t-esc="value instanceof Array ? value[1] : value"/>
|
||||
</t>
|
||||
<button type="button" t-att-title="column.help"
|
||||
<button type="button" t-att-title="column.string"
|
||||
t-if="is_button">
|
||||
<img t-att-src="'/base/static/src/img/icons/' + column.icon + '.png'"
|
||||
t-att-alt="column.string"/>
|
||||
|
@ -360,8 +363,14 @@
|
|||
<h2 class="oe_view_title"><t t-esc="view.fields_view.arch.attrs.string"/></h2>
|
||||
<div class="oe_form_header" t-att-id="view.element_id + '_header'">
|
||||
<div class="oe_form_buttons" t-if="view.flags.action_buttons !== false">
|
||||
<!--<button type="button" class="oe_form_button_save">Save</button>-->
|
||||
<button type="button" class="oe_form_button_save_edit">Save & Edit</button>
|
||||
<!--<button type="button" class="oe_form_button_save">
|
||||
<span class="oe_form_on_update">Save</span>
|
||||
<span class="oe_form_on_create">Create</span>
|
||||
</button>-->
|
||||
<button type="button" class="oe_form_button_save_edit">
|
||||
<span class="oe_form_on_update">Save & Edit</span>
|
||||
<span class="oe_form_on_create">Create & Edit</span>
|
||||
</button>
|
||||
<!--<button type="button" class="oe_form_button_cancel">Cancel</button>-->
|
||||
<button type="button" class="oe_form_button_new">New</button>
|
||||
</div>
|
||||
|
@ -462,6 +471,9 @@
|
|||
<t t-if="widget.string and widget.node.tag != 'label'">:</t>
|
||||
</label>
|
||||
</t>
|
||||
<t t-name="WidgetParagraph">
|
||||
<p class="oe_form_paragraph"><t t-esc="widget.string"/></p>
|
||||
</t>
|
||||
<t t-name="FieldChar">
|
||||
<input type="text"
|
||||
t-att-name="widget.name"
|
||||
|
@ -804,6 +816,7 @@
|
|||
</div>
|
||||
<a class="searchview_extended_add_proposition" href="javascript:void(0)">
|
||||
<span>Add condition</span></a>
|
||||
<div class="oe_adv_filters_and"><span>and</span></div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="SearchView.extended_search.proposition">
|
||||
|
@ -870,12 +883,24 @@
|
|||
</div>
|
||||
</t>
|
||||
<t t-name="DialogWarning">
|
||||
<div id="dialog-message" t-att-title="title">
|
||||
<p>
|
||||
<span class="ui-icon ui-icon-circle-check" style="float:left; margin:0 7px 50px 0;"></span>
|
||||
<t t-esc="message"/>
|
||||
</p>
|
||||
</div>
|
||||
<table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
|
||||
<tr>
|
||||
<td><img src="/base/static/src/img/warning.png" class="oe-dialog-icon"/></td>
|
||||
<td>
|
||||
<p>
|
||||
<t t-js="d">
|
||||
d.html_error = context.engine.tools.html_escape(d.error.data.fault_code).replace(/\n/g, '<br/>');
|
||||
</t>
|
||||
<t t-raw="html_error"/>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-name="DialogTraceback">
|
||||
<pre><t t-esc="error.message"/></pre>
|
||||
<hr/>
|
||||
<pre><t t-esc="error.data.debug"/></pre>
|
||||
</t>
|
||||
<t t-name="SelectCreatePopup">
|
||||
<div t-att-id="element_id">
|
||||
|
|
|
@ -59,8 +59,6 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
|||
res_model : 'ir.actions.actions',
|
||||
views : [[false, 'list']],
|
||||
type : 'ir.actions.act_window',
|
||||
view_type : 'list',
|
||||
view_mode : 'list',
|
||||
limit : 80,
|
||||
auto_search : true,
|
||||
domain : [['type', '=', 'ir.actions.act_window']],
|
||||
|
@ -227,10 +225,10 @@ openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
|
|||
views_switcher : false,
|
||||
action_buttons : false,
|
||||
pager: false
|
||||
}
|
||||
var element_id = this.view.element_id + '_action_' + action.id;
|
||||
var view = new openerp.base.ViewManagerAction(this.session, element_id, action);
|
||||
view.start();
|
||||
};
|
||||
new openerp.base.ActionManager(
|
||||
this.session, this.view.element_id + '_action_' + action.id)
|
||||
.do_action(action);
|
||||
},
|
||||
render: function() {
|
||||
// We should start with three columns available
|
||||
|
|
|
@ -6,10 +6,11 @@ can't be sent there themselves).
|
|||
"""
|
||||
import binascii
|
||||
import hashlib
|
||||
import simplejson.decoder
|
||||
import simplejson.encoder
|
||||
import time
|
||||
import datetime
|
||||
|
||||
__all__ = ['Domain', 'Context', 'NonLiteralEncoder, non_literal_decoder']
|
||||
__all__ = ['Domain', 'Context', 'NonLiteralEncoder, non_literal_decoder', 'CompoundDomain', 'CompoundContext']
|
||||
|
||||
#: 48 bits should be sufficient to have almost no chance of collision
|
||||
#: with a million hashes, according to hg@67081329d49a
|
||||
|
@ -17,6 +18,8 @@ SHORT_HASH_BYTES_SIZE = 6
|
|||
|
||||
class NonLiteralEncoder(simplejson.encoder.JSONEncoder):
|
||||
def default(self, object):
|
||||
if not isinstance(object, (BaseDomain, BaseContext)):
|
||||
return super(NonLiteralEncoder, self).default(object)
|
||||
if isinstance(object, Domain):
|
||||
return {
|
||||
'__ref': 'domain',
|
||||
|
@ -30,14 +33,16 @@ class NonLiteralEncoder(simplejson.encoder.JSONEncoder):
|
|||
elif isinstance(object, CompoundDomain):
|
||||
return {
|
||||
'__ref': 'compound_domain',
|
||||
'__domains': [self.default(el) for el in object.domains]
|
||||
'__domains': object.domains,
|
||||
'__eval_context': object.get_eval_context()
|
||||
}
|
||||
elif isinstance(object, CompoundContext):
|
||||
return {
|
||||
'__ref': 'compound_context',
|
||||
'__contexts': [self.default(el) for el in object.contexts]
|
||||
'__contexts': object.contexts,
|
||||
'__eval_context': object.get_eval_context()
|
||||
}
|
||||
return super(NonLiteralEncoder, self).default(object)
|
||||
raise TypeError('Could not encode unknown non-literal %s' % object)
|
||||
|
||||
def non_literal_decoder(dct):
|
||||
""" Decodes JSON dicts into :class:`Domain` and :class:`Context` based on
|
||||
|
@ -60,16 +65,27 @@ def non_literal_decoder(dct):
|
|||
elif dct["__ref"] == "compound_domain":
|
||||
cdomain = CompoundDomain()
|
||||
for el in dct["__domains"]:
|
||||
cdomain.domains.append(non_literal_decoder(el))
|
||||
cdomain.domains.append(el)
|
||||
cdomain.set_eval_context(dct.get("__eval_context"))
|
||||
return cdomain
|
||||
elif dct["__ref"] == "compound_context":
|
||||
ccontext = CompoundContext()
|
||||
for el in dct["__contexts"]:
|
||||
ccontext.contexts.append(non_literal_decoder(el))
|
||||
ccontext.contexts.append(el)
|
||||
ccontext.set_eval_context(dct.get("__eval_context"))
|
||||
return ccontext
|
||||
return dct
|
||||
|
||||
class Domain(object):
|
||||
# TODO: use abstract base classes if 2.6+?
|
||||
class BaseDomain(object):
|
||||
def evaluate(self, context=None):
|
||||
raise NotImplementedError('Non literals must implement evaluate()')
|
||||
|
||||
class BaseContext(object):
|
||||
def evaluate(self, context=None):
|
||||
raise NotImplementedError('Non literals must implement evaluate()')
|
||||
|
||||
class Domain(BaseDomain):
|
||||
def __init__(self, session, domain_string=None, key=None):
|
||||
""" Uses session information to store the domain string and map it to a
|
||||
domain key, which can be safely round-tripped to the client.
|
||||
|
@ -111,9 +127,9 @@ class Domain(object):
|
|||
ctx = self.session.evaluation_context(context)
|
||||
if self.own:
|
||||
ctx.update(self.own)
|
||||
return eval(self.get_domain_string(), ctx)
|
||||
return eval(self.get_domain_string(), SuperDict(ctx))
|
||||
|
||||
class Context(object):
|
||||
class Context(BaseContext):
|
||||
def __init__(self, session, context_string=None, key=None):
|
||||
""" Uses session information to store the context string and map it to
|
||||
a key (stored in a secret location under a secret mountain), which can
|
||||
|
@ -157,27 +173,41 @@ class Context(object):
|
|||
if self.own:
|
||||
ctx.update(self.own)
|
||||
return eval(self.get_context_string(),
|
||||
ctx)
|
||||
SuperDict(ctx))
|
||||
|
||||
class SuperDict(dict):
|
||||
def __getattr__(self, name):
|
||||
try:
|
||||
return self[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
def __getitem__(self, key):
|
||||
tmp = super(type(self), self).__getitem__(key)
|
||||
if isinstance(tmp, dict):
|
||||
return SuperDict(tmp)
|
||||
return tmp
|
||||
|
||||
class CompoundDomain:
|
||||
class CompoundDomain(BaseDomain):
|
||||
def __init__(self, *domains):
|
||||
self.domains = []
|
||||
self.session = None
|
||||
self.eval_context = None
|
||||
for domain in domains:
|
||||
self.add(domain)
|
||||
|
||||
def evaluate(self, context=None):
|
||||
final_domain = []
|
||||
for domain in self.domains:
|
||||
if not isinstance(domain, (list, Domain, CompoundDomain)):
|
||||
raise TypeError("Domain %r is not a list or a nonliteral Domain",
|
||||
domain)
|
||||
|
||||
if not isinstance(domain, (list, BaseDomain)):
|
||||
raise TypeError(
|
||||
"Domain %r is not a list or a nonliteral Domain" % domain)
|
||||
|
||||
if isinstance(domain, list):
|
||||
final_domain.extend(domain)
|
||||
continue
|
||||
|
||||
ctx = dict(context or {})
|
||||
ctx.update(self.get_eval_context() or {})
|
||||
ctx['context'] = ctx
|
||||
|
||||
domain.session = self.session
|
||||
|
@ -187,22 +217,31 @@ class CompoundDomain:
|
|||
def add(self, domain):
|
||||
self.domains.append(domain)
|
||||
return self
|
||||
|
||||
def set_eval_context(self, eval_context):
|
||||
self.eval_context = eval_context
|
||||
return self
|
||||
|
||||
def get_eval_context(self):
|
||||
return self.eval_context
|
||||
|
||||
class CompoundContext:
|
||||
class CompoundContext(BaseContext):
|
||||
def __init__(self, *contexts):
|
||||
self.contexts = []
|
||||
self.eval_context = None
|
||||
self.session = None
|
||||
for context in contexts:
|
||||
self.add(context)
|
||||
|
||||
def evaluate(self, context=None):
|
||||
ctx = dict(context or {})
|
||||
ctx.update(self.get_eval_context() or {})
|
||||
final_context = {}
|
||||
for context_to_eval in self.contexts:
|
||||
if not isinstance(context_to_eval, (dict, Context, CompoundContext)):
|
||||
raise TypeError("Context %r is not a dict or a nonliteral Context",
|
||||
context_to_eval)
|
||||
|
||||
if not isinstance(context_to_eval, (dict, BaseContext)):
|
||||
raise TypeError(
|
||||
"Context %r is not a dict or a nonliteral Context" % context_to_eval)
|
||||
|
||||
if isinstance(context_to_eval, dict):
|
||||
final_context.update(context_to_eval)
|
||||
continue
|
||||
|
@ -217,4 +256,10 @@ class CompoundContext:
|
|||
def add(self, context):
|
||||
self.contexts.append(context)
|
||||
return self
|
||||
|
||||
def set_eval_context(self, eval_context):
|
||||
self.eval_context = eval_context
|
||||
return self
|
||||
|
||||
def get_eval_context(self):
|
||||
return self.eval_context
|
||||
|
|
|
@ -138,15 +138,13 @@ class OpenERPSession(object):
|
|||
"""
|
||||
assert self._uid, "The user needs to be logged-in to initialize his context"
|
||||
self.context = self.model('res.users').context_get(self.context)
|
||||
# set bin_size to True all the time
|
||||
self.context = self.context or {}
|
||||
self.context["bin_size"] = True
|
||||
|
||||
|
||||
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:
|
||||
|
@ -203,7 +201,7 @@ class OpenERPSession(object):
|
|||
ctx['context'] = ctx
|
||||
|
||||
# adding the context of the session to send to the openerp server
|
||||
ccontext = nonliterals.CompoundContext(self.context, context_to_eval)
|
||||
ccontext = nonliterals.CompoundContext(self.context, context_to_eval or {})
|
||||
ccontext.session = self
|
||||
return ccontext.evaluate(ctx)
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ class TestOpenERPSession(unittest2.TestCase):
|
|||
|
||||
def test_eval_empty_domains(self):
|
||||
self.assertEqual(
|
||||
self.session.eval_domain(CompoundDomain(*[])),
|
||||
self.session.eval_domain(CompoundDomain()),
|
||||
[])
|
||||
|
||||
def test_eval_literal_domains(self):
|
||||
|
|
Loading…
Reference in New Issue