[MERGE] merge with trunk upto revision no 584.
bzr revid: ysa@tinyerp.com-20110711050616-x9o20ozhjmpdr6f4
This commit is contained in:
@@ -594,36 +594,6 @@ class View(openerpweb.Controller):
|
||||
return {'result': True}
|
||||
return {'result': False}
|
||||
|
||||
def normalize_attrs(self, elem, context):
|
||||
""" Normalize @attrs, @invisible, @required, @readonly and @states, so
|
||||
the client only has to deal with @attrs.
|
||||
|
||||
See `the discoveries pad <http://pad.openerp.com/discoveries>`_ for
|
||||
the rationale.
|
||||
|
||||
:param elem: the current view node (Python object)
|
||||
:type elem: xml.etree.ElementTree.Element
|
||||
:param dict context: evaluation context
|
||||
"""
|
||||
# If @attrs is normalized in json by server, the eval should be replaced by simplejson.loads
|
||||
attrs = openerpweb.ast.literal_eval(elem.get('attrs', '{}'))
|
||||
if 'states' in elem.attrib:
|
||||
attrs.setdefault('invisible', [])\
|
||||
.append(('state', 'not in', elem.attrib.pop('states').split(',')))
|
||||
if attrs:
|
||||
elem.set('attrs', simplejson.dumps(attrs))
|
||||
for a in ['invisible', 'readonly', 'required']:
|
||||
if a in elem.attrib:
|
||||
# In the XML we trust
|
||||
avalue = bool(eval(elem.get(a, 'False'),
|
||||
{'context': context or {}}))
|
||||
if not avalue:
|
||||
del elem.attrib[a]
|
||||
else:
|
||||
elem.attrib[a] = '1'
|
||||
if a == 'invisible' and 'attrs' in elem.attrib:
|
||||
del elem.attrib['attrs']
|
||||
|
||||
def transform_view(self, view_string, session, context=None):
|
||||
# transform nodes on the fly via iterparse, instead of
|
||||
# doing it statically on the parsing result
|
||||
@@ -633,7 +603,6 @@ class View(openerpweb.Controller):
|
||||
if event == "start":
|
||||
if root is None:
|
||||
root = elem
|
||||
self.normalize_attrs(elem, context)
|
||||
self.parse_domains_and_contexts(elem, session)
|
||||
return root
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
|
||||
on_form_changed: function() {
|
||||
for (var w in this.widgets) {
|
||||
w = this.widgets[w];
|
||||
w.process_attrs();
|
||||
w.process_modifiers();
|
||||
w.update_dom();
|
||||
}
|
||||
},
|
||||
@@ -503,7 +503,7 @@ openerp.base.form.compute_domain = function(expr, fields) {
|
||||
stack.push(!_(val).contains(field));
|
||||
break;
|
||||
default:
|
||||
this.log("Unsupported operator in attrs :", op);
|
||||
this.log("Unsupported operator in modifiers :", op);
|
||||
}
|
||||
}
|
||||
return _.all(stack);
|
||||
@@ -514,7 +514,7 @@ openerp.base.form.Widget = openerp.base.Controller.extend({
|
||||
init: function(view, node) {
|
||||
this.view = view;
|
||||
this.node = node;
|
||||
this.attrs = JSON.parse(this.node.attrs.attrs || '{}');
|
||||
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("_");
|
||||
@@ -527,15 +527,15 @@ openerp.base.form.Widget = openerp.base.Controller.extend({
|
||||
|
||||
this.string = this.string || node.attrs.string;
|
||||
this.help = this.help || node.attrs.help;
|
||||
this.invisible = (node.attrs.invisible == '1');
|
||||
this.invisible = this.modifiers['invisible'] === true;
|
||||
},
|
||||
start: function() {
|
||||
this.$element = $('#' + this.element_id);
|
||||
},
|
||||
process_attrs: function() {
|
||||
process_modifiers: function() {
|
||||
var compute_domain = openerp.base.form.compute_domain;
|
||||
for (var a in this.attrs) {
|
||||
this[a] = compute_domain(this.attrs[a], this.view.fields);
|
||||
for (var a in this.modifiers) {
|
||||
this[a] = compute_domain(this.modifiers[a], this.view.fields);
|
||||
}
|
||||
},
|
||||
update_dom: function() {
|
||||
@@ -736,10 +736,9 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
|
||||
this.field = view.fields_view.fields[node.attrs.name] || {};
|
||||
this.string = node.attrs.string || this.field.string;
|
||||
this.help = node.attrs.help || this.field.help;
|
||||
this.invisible = (this.invisible || this.field.invisible == '1');
|
||||
this.nolabel = (this.field.nolabel || node.attrs.nolabel) == '1';
|
||||
this.readonly = (this.field.readonly || node.attrs.readonly) == '1';
|
||||
this.required = (this.field.required || node.attrs.required) == '1';
|
||||
this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1';
|
||||
this.readonly = this.modifiers['readonly'] === true;
|
||||
this.required = this.modifiers['required'] === true;
|
||||
this.invalid = false;
|
||||
this.touched = false;
|
||||
},
|
||||
@@ -1117,28 +1116,54 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldSelection";
|
||||
this.field_index = _.map(this.field.selection, function(x, index) {
|
||||
return {"ikey": "" + index, "ekey": x[0], "label": x[1]};
|
||||
});
|
||||
},
|
||||
start: function() {
|
||||
// Flag indicating whether we're in an event chain containing a change
|
||||
// event on the select, in order to know what to do on keyup[RETURN]:
|
||||
// * If the user presses [RETURN] as part of changing the value of a
|
||||
// selection, we should just let the value change and not let the
|
||||
// event broadcast further (e.g. to validating the current state of
|
||||
// the form in editable list view, which would lead to saving the
|
||||
// current row or switching to the next one)
|
||||
// * If the user presses [RETURN] with a select closed (side-effect:
|
||||
// also if the user opened the select and pressed [RETURN] without
|
||||
// changing the selected value), takes the action as validating the
|
||||
// row
|
||||
var ischanging = false;
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('select').change(this.on_ui_change);
|
||||
this.$element.find('select')
|
||||
.change(this.on_ui_change)
|
||||
.change(function () { ischanging = true; })
|
||||
.click(function () { ischanging = false; })
|
||||
.keyup(function (e) {
|
||||
if (e.which !== 13 || !ischanging) { return; }
|
||||
e.stopPropagation();
|
||||
ischanging = false;
|
||||
});
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
if (value != null && value !== false) {
|
||||
this.$element.find('select').val(value);
|
||||
} else {
|
||||
this.$element.find('select').val('false');
|
||||
}
|
||||
value = value === null ? false : value;
|
||||
value = value instanceof Array ? value[0] : value;
|
||||
this._super(value);
|
||||
var option = _.detect(this.field_index, function(x) {return x.ekey === value;});
|
||||
this.$element.find('select').val(option === undefined ? '' : option.ikey);
|
||||
},
|
||||
set_value_from_ui: function() {
|
||||
this.value = this.$element.find('select').val();
|
||||
var ikey = this.$element.find('select').val();
|
||||
var option = _.detect(this.field_index, function(x) {return x.ikey === ikey;});
|
||||
this.value = option === undefined ? false : option.ekey;
|
||||
},
|
||||
update_dom: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('select').attr('disabled', this.readonly);
|
||||
},
|
||||
validate: function() {
|
||||
this.invalid = this.required && this.$element.find('select').val() === "";
|
||||
var ikey = this.$element.find('select').val();
|
||||
var option = _.detect(this.field_index, function(x) {return x.ikey === ikey;});
|
||||
this.invalid = this.required && (option === undefined || option.ekey === false);
|
||||
},
|
||||
focus: function() {
|
||||
this.$element.find('select').focus();
|
||||
|
||||
@@ -268,10 +268,12 @@ openerp.base.list.editable = function (openerp) {
|
||||
openerp.base.list.form[key] = (form_widgets.get_object(key)).extend({
|
||||
update_dom: function () {
|
||||
this.$element.children().css('visibility', '');
|
||||
if (this.invisible && this.node.attrs.invisible !== '1') {
|
||||
if (this.invisible) {
|
||||
this.$element.children().css('visibility', 'hidden');
|
||||
} else {
|
||||
this.invisible = !!this.modifiers.tree_invisible;
|
||||
this._super();
|
||||
this.invisible = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -252,18 +252,18 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
var name = field.attrs.name;
|
||||
var column = _.extend({id: name, tag: field.tag},
|
||||
field.attrs, fields[name]);
|
||||
// attrs computer
|
||||
if (column.attrs) {
|
||||
var attrs = JSON.parse(column.attrs);
|
||||
column.attrs_for = function (fields) {
|
||||
// modifiers computer
|
||||
if (column.modifiers) {
|
||||
var modifiers = JSON.parse(column.modifiers);
|
||||
column.modifiers_for = function (fields) {
|
||||
var result = {};
|
||||
for (var attr in attrs) {
|
||||
result[attr] = domain_computer(attrs[attr], fields);
|
||||
for (var modifier in modifiers) {
|
||||
result[modifier] = domain_computer(modifiers[modifier], fields);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
column.attrs_for = noop;
|
||||
column.modifiers_for = noop;
|
||||
}
|
||||
return column;
|
||||
};
|
||||
@@ -275,10 +275,10 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
if (grouped) {
|
||||
this.columns.unshift({
|
||||
id: '_group', tag: '', string: "Group", meta: true,
|
||||
attrs_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; }
|
||||
}, {
|
||||
id: '_count', tag: '', string: '#', meta: true,
|
||||
attrs_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -409,9 +409,9 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
$.proxy(this, 'reload_content'));
|
||||
},
|
||||
/**
|
||||
* Handles the signal to delete a line from the DOM
|
||||
* Handles the signal to delete lines from the records list
|
||||
*
|
||||
* @param {Array} ids the id of the object to delete
|
||||
* @param {Array} ids the ids of the records to delete
|
||||
*/
|
||||
do_delete: function (ids) {
|
||||
if (!ids.length) {
|
||||
@@ -419,7 +419,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
|
||||
}
|
||||
var self = this;
|
||||
return $.when(this.dataset.unlink(ids)).then(function () {
|
||||
self.reload_content();
|
||||
self.groups.drop_records(ids);
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -637,7 +637,9 @@ 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', _.extend({
|
||||
render_cell: this.render_cell}, this)));
|
||||
},
|
||||
/**
|
||||
* Gets the ids of all currently selected records, if any
|
||||
@@ -767,8 +769,67 @@ openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List
|
||||
options: this.options,
|
||||
row: this.rows[record_index],
|
||||
row_parity: (record_index % 2 === 0) ? 'even' : 'odd',
|
||||
row_index: record_index
|
||||
row_index: record_index,
|
||||
render_cell: this.render_cell
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Formats the rendring of a given value based on its field type
|
||||
*
|
||||
* @param {Object} row_data record whose values should be displayed in the cell
|
||||
* @param {Object} column column descriptor
|
||||
* @param {"button"|"field"} column.tag base control type
|
||||
* @param {String} column.type widget type for a field control
|
||||
* @param {String} [column.string] button label
|
||||
* @param {String} [column.icon] button icon
|
||||
*/
|
||||
render_cell: function (row_data, column) {
|
||||
var attrs = column.modifiers_for(row_data);
|
||||
if (attrs.invisible) { return ''; }
|
||||
if (column.tag === 'button') {
|
||||
return [
|
||||
'<button type="button" title="', column.string || '', '">',
|
||||
'<img src="/base/static/src/img/icons/', column.icon, '.png"',
|
||||
' alt="', column.string || '', '"/>',
|
||||
'</button>'
|
||||
].join('')
|
||||
}
|
||||
|
||||
var record = row_data[column.id];
|
||||
if (record.value === false) { return ''; }
|
||||
switch (column.widget || column.type) {
|
||||
case 'many2one':
|
||||
// name_get value format
|
||||
return record.value[1];
|
||||
case 'float_time':
|
||||
return _.sprintf("%02d:%02d",
|
||||
Math.floor(record.value),
|
||||
Math.round((record.value % 1) * 60));
|
||||
case 'progressbar':
|
||||
return _.sprintf(
|
||||
'<progress value="%.2f" max="100.0">%.2f%%</progress>',
|
||||
record.value, record.value);
|
||||
default:
|
||||
return record.value;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Stops displaying the records matching the provided ids.
|
||||
*
|
||||
* @param {Array} ids identifiers of the records to remove
|
||||
*/
|
||||
drop_records: function (ids) {
|
||||
var self = this;
|
||||
_(this.rows).chain()
|
||||
.map(function (record, index) {
|
||||
return {index: index, id: record.data.id.value};
|
||||
}).filter(function (record) {
|
||||
return _(ids).contains(record.id);
|
||||
}).reverse()
|
||||
.each(function (record) {
|
||||
self.$current.find('tr:eq(' + record.index + ')').remove();
|
||||
self.rows.splice(record.index, 1);
|
||||
})
|
||||
}
|
||||
// drag and drop
|
||||
});
|
||||
@@ -1062,6 +1123,19 @@ openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Gr
|
||||
.map(function (child) {
|
||||
return child.get_records();
|
||||
}).flatten().value();
|
||||
},
|
||||
/**
|
||||
* Stops displaying the records with the linked ids, assumes these records
|
||||
* were deleted from the DB.
|
||||
*
|
||||
* This is the up-signal from the `deleted` event on groups and lists.
|
||||
*
|
||||
* @param {Array} ids list of identifier of the records to remove.
|
||||
*/
|
||||
drop_records: function (ids) {
|
||||
_.each(this.children, function (child) {
|
||||
child.drop_records(ids);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -459,7 +459,8 @@ openerp.base.View = openerp.base.Controller.extend({
|
||||
search_view : false,
|
||||
views_switcher : false,
|
||||
action_buttons : false,
|
||||
pager : false
|
||||
pager : false,
|
||||
new_window: true
|
||||
};
|
||||
action_manager.do_action(action, on_closed);
|
||||
if (self.view_manager.is_dialog && action.type != 'ir.actions.act_window_close') {
|
||||
|
||||
@@ -324,20 +324,7 @@
|
||||
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
|
||||
t-att-class="'oe-field-cell' + (align ? ' oe-number' : '')"
|
||||
t-att-data-field="column.id">
|
||||
<t t-set="attrs" t-value="column.attrs_for(row.data)"/>
|
||||
<t t-if="!attrs.invisible">
|
||||
<t t-set="is_button" t-value="column.tag === 'button'"/>
|
||||
<!-- TODO: get correct widget from form -->
|
||||
<t t-if="!is_button and row['data'][column.id].value !== false">
|
||||
<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.string"
|
||||
t-if="is_button">
|
||||
<img t-att-src="'/base/static/src/img/icons/' + column.icon + '.png'"
|
||||
t-att-alt="column.string"/>
|
||||
</button>
|
||||
</t>
|
||||
<t t-raw="render_cell(row.data, column)"/>
|
||||
</td>
|
||||
</t>
|
||||
<td t-if="options.deletable" class='oe-record-delete'>
|
||||
@@ -517,9 +504,9 @@
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
style="width: 100%">
|
||||
<t t-foreach="widget.field.selection" t-as="options">
|
||||
<option t-att-value="options[0]">
|
||||
<t t-esc="options[1]"/>
|
||||
<t t-foreach="widget.field_index" t-as="options">
|
||||
<option t-att-value="options.ikey">
|
||||
<t t-esc="options.label"/>
|
||||
</option>
|
||||
</t>
|
||||
</select>
|
||||
|
||||
+22
-23
@@ -74,8 +74,7 @@ class OpenERPSession(object):
|
||||
Used to store references to non-literal domains which need to be
|
||||
round-tripped to the client browser.
|
||||
"""
|
||||
def __init__(self, server='127.0.0.1', port=8069,
|
||||
model_factory=OpenERPModel):
|
||||
def __init__(self, server='127.0.0.1', port=8069, model_factory=OpenERPModel):
|
||||
self._server = server
|
||||
self._port = port
|
||||
self._db = False
|
||||
@@ -234,11 +233,7 @@ class OpenERPSession(object):
|
||||
# OpenERP Web RequestHandler
|
||||
#----------------------------------------------------------
|
||||
class JsonRequest(object):
|
||||
""" JSON-RPC2 over HTTP POST using non standard POST encoding.
|
||||
Difference with the standard:
|
||||
|
||||
* the json string is passed as a form parameter named "request"
|
||||
* method is currently ignored
|
||||
""" JSON-RPC2 over HTTP.
|
||||
|
||||
Sucessful request::
|
||||
|
||||
@@ -278,7 +273,9 @@ class JsonRequest(object):
|
||||
self.httpsession_id = "cookieid"
|
||||
self.httpsession = cherrypy.session
|
||||
self.session_id = self.params.pop("session_id", None) or uuid.uuid4().hex
|
||||
self.session = self.httpsession.setdefault(self.session_id, OpenERPSession())
|
||||
host = cherrypy.config['openerp.server.host']
|
||||
port = cherrypy.config['openerp.server.port']
|
||||
self.session = self.httpsession.setdefault(self.session_id, OpenERPSession(host, port))
|
||||
self.context = self.params.pop('context', None)
|
||||
return self.params
|
||||
|
||||
@@ -369,7 +366,9 @@ class HttpRequest(object):
|
||||
self.httpsession_id = "cookieid"
|
||||
self.httpsession = cherrypy.session
|
||||
self.context = kw.get('context', {})
|
||||
self.session = self.httpsession.setdefault(kw.get('session_id', None), OpenERPSession())
|
||||
host = cherrypy.config['openerp.server.host']
|
||||
port = cherrypy.config['openerp.server.port']
|
||||
self.session = self.httpsession.setdefault(kw.get('session_id', None), OpenERPSession(host, port))
|
||||
self.result = ""
|
||||
if request.method == 'GET':
|
||||
print "GET --> %s.%s %s %r" % (controller.__class__.__name__, f.__name__, request, kw)
|
||||
@@ -454,8 +453,10 @@ class Root(object):
|
||||
def main(argv):
|
||||
# change the timezone of the program to the OpenERP server's assumed timezone
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'openerp.server.host': '127.0.0.1',
|
||||
'openerp.server.port': 8069,
|
||||
'server.socket_port': 8002,
|
||||
'server.socket_host': '0.0.0.0',
|
||||
'tools.sessions.on': True,
|
||||
@@ -463,13 +464,13 @@ def main(argv):
|
||||
'tools.sessions.storage_path': os.path.join(tempfile.gettempdir(), "cpsessions"),
|
||||
'tools.sessions.timeout': 60
|
||||
}
|
||||
|
||||
|
||||
# Parse config
|
||||
op = optparse.OptionParser()
|
||||
op.add_option("-p", "--port", dest="server.socket_port", help="listening port",
|
||||
type="int", metavar="NUMBER")
|
||||
op.add_option("-s", "--session-path", dest="tools.sessions.storage_path",
|
||||
help="directory used for session storage", metavar="DIR")
|
||||
op.add_option("-p", "--port", dest="server.socket_port", help="listening port", type="int", metavar="NUMBER")
|
||||
op.add_option("-s", "--session-path", dest="tools.sessions.storage_path", help="directory used for session storage", metavar="DIR")
|
||||
op.add_option("", "--server-host", dest="openerp.server.host", help="OpenERP server hostname", metavar="HOST")
|
||||
op.add_option("", "--server-port", dest="openerp.server.port", help="OpenERP server port", type="int", metavar="NUMBER")
|
||||
(o, args) = op.parse_args(argv[1:])
|
||||
o = vars(o)
|
||||
for k in o.keys():
|
||||
@@ -478,19 +479,17 @@ def main(argv):
|
||||
|
||||
# Setup and run cherrypy
|
||||
cherrypy.tree.mount(Root())
|
||||
|
||||
|
||||
cherrypy.config.update(config=DEFAULT_CONFIG)
|
||||
if os.path.exists(os.path.join(os.path.dirname(
|
||||
os.path.dirname(__file__)),'openerp-web.cfg')):
|
||||
cherrypy.config.update(os.path.join(os.path.dirname(
|
||||
os.path.dirname(__file__)),'openerp-web.cfg'))
|
||||
if os.path.exists(os.path.join(os.path.dirname( os.path.dirname(__file__)),'openerp-web.cfg')):
|
||||
cherrypy.config.update(os.path.join(os.path.dirname( os.path.dirname(__file__)),'openerp-web.cfg'))
|
||||
if os.path.exists(os.path.expanduser('~/.openerp_webrc')):
|
||||
cherrypy.config.update(os.path.expanduser('~/.openerp_webrc'))
|
||||
cherrypy.config.update(o)
|
||||
|
||||
|
||||
if not os.path.exists(cherrypy.config['tools.sessions.storage_path']):
|
||||
os.mkdir(cherrypy.config['tools.sessions.storage_path'], 0700)
|
||||
|
||||
os.makedirs(cherrypy.config['tools.sessions.storage_path'], 0700)
|
||||
|
||||
cherrypy.server.subscribe()
|
||||
cherrypy.engine.start()
|
||||
cherrypy.engine.block()
|
||||
|
||||
Reference in New Issue
Block a user