[MERGE] Merge with trunk upto revision no 698.

bzr revid: ysa@tinyerp.com-20110728052248-5z3nuv4t8f77haj9
This commit is contained in:
Yogesh (OpenERP)
2011-07-28 10:52:48 +05:30
7 changed files with 362 additions and 126 deletions
+1 -1
View File
@@ -17,7 +17,7 @@
"static/lib/qweb/qweb2.js",
"static/lib/underscore/underscore.js",
"static/lib/underscore/underscore.string.js",
"static/src/js/base.js",
"static/src/js/boot.js",
"static/src/js/chrome.js",
"static/src/js/controller.js",
"static/src/js/views.js",
@@ -1,5 +1,5 @@
//---------------------------------------------------------
// OpenERP initialisation and black magic about the pool
// OpenERP Web Boostrap
//---------------------------------------------------------
/**
@@ -57,7 +57,6 @@
// OpenERP base module split
//---------------------------------------------------------
/** @namespace */
openerp.base = function(instance) {
openerp.base.controller(instance);
openerp.base.dates(instance);
+10 -103
View File
@@ -136,107 +136,6 @@ openerp.base.Registry = openerp.base.Class.extend( /** @lends openerp.base.Regis
}
});
/**
* OpenERP session aware controller
* a controller takes an already existing dom element and manage it
*/
openerp.base.Controller = openerp.base.Controller.extend( /** @lends openerp.base.Controller# */{
init: function(parent, element_id) {
this._super(parent, element_id);
if(this.controller_parent && this.controller_parent.session) {
this.session = this.controller_parent.session;
}
},
/**
* Performs a JSON-RPC call
*
* @param {String} url endpoint url
* @param {Object} data RPC parameters
* @param {Function} success RPC call success callback
* @param {Function} error RPC call error callback
* @returns {jQuery.Deferred} deferred object for the RPC call
*/
rpc: function(url, data, success, error) {
return this.session.rpc(url, data, success, error);
},
do_action: function(action, on_finished) {
return this.parent.do_action(action, on_finished);
}
});
/**
* OpenERP session aware widget
* A widget is a controller that doesnt take an element_id
* it render its own html render() that you should insert into the dom
* and bind it at start()
*/
openerp.base.BaseWidget = openerp.base.Controller.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the render() method can not be used.
*
* @type string
*/
template: null,
/**
* The prefix used to generate an id automatically. Should be redefined in
* subclasses. If it is not defined, a default identifier will be used.
*
* @type string
*/
identifier_prefix: 'generic-identifier',
/**
* Base class for widgets. Handle rendering (based on a QWeb template),
* identifier generation, parenting and destruction of the widget.
* Also initialize the identifier.
*
* @constructs
* @params {openerp.base.search.BaseWidget} parent The parent widget.
*/
init: function (parent) {
this._super(parent);
this.make_id(this.identifier_prefix);
},
/**
* Sets and returns a globally unique identifier for the widget.
*
* If a prefix is appended, the identifier will be appended to it.
*
* @params sections prefix sections, empty/falsy sections will be removed
*/
make_id: function () {
this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
return this.element_id;
},
/**
* Render the widget. This.template must be defined.
* The content of the current object is passed as context to the template.
*
* @param {object} additional Additional context arguments to pass to the template.
*/
render: function (additional) {
return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
},
/**
* "Starts" the widgets. Called at the end of the rendering, this allows
* to get a jQuery object referring to the DOM ($element attribute).
*/
start: function () {
this._super();
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : null;
},
/**
* "Stops" the widgets. Called when the view destroys itself, this
* lets the widgets clean up after themselves.
*/
stop: function () {
if(this.$element != null) {
this.$element.remove();
}
this._super();
}
});
openerp.base.Session = openerp.base.Controller.extend( /** @lends openerp.base.Session# */{
/**
@@ -544,8 +443,8 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
max_height: '100%',
autoOpen: false,
buttons: {},
close: function () {
self.stop();
beforeClose: function () {
self.on_close();
}
};
for (var f in this) {
@@ -609,7 +508,15 @@ openerp.base.Dialog = openerp.base.BaseWidget.extend({
this.set_options(options);
this.$dialog.dialog(this.options).dialog('open');
},
close: function() {
// Closes the dialog but leave it in a state where it could be opened again.
this.$dialog.dialog('close');
},
on_close: function() {
},
stop: function () {
// Destroy widget
this.close();
this.$dialog.dialog('destroy');
}
});
+301
View File
@@ -145,6 +145,10 @@ instance.base.generate_null_object_class = function(claz, add) {
return tmpclass.extend(add || {});
};
// --------------------------------------------------------
// OLD
// --------------------------------------------------------
/**
* OpenERP Controller
* TODO merge BaseWidget with Controller
@@ -243,6 +247,303 @@ instance.base.Controller = instance.base.Class.extend( /** @lends instance.base.
}
});
/**
* OpenERP session aware controller
* a controller takes an already existing dom element and manage it
*/
instance.base.Controller = instance.base.Controller.extend( /** @lends openerp.base.Controller# */{
init: function(parent, element_id) {
this._super(parent, element_id);
if(this.controller_parent && this.controller_parent.session) {
this.session = this.controller_parent.session;
}
},
/**
* Performs a JSON-RPC call
*
* @param {String} url endpoint url
* @param {Object} data RPC parameters
* @param {Function} success RPC call success callback
* @param {Function} error RPC call error callback
* @returns {jQuery.Deferred} deferred object for the RPC call
*/
rpc: function(url, data, success, error) {
return this.session.rpc(url, data, success, error);
},
do_action: function(action, on_finished) {
return this.parent.do_action(action, on_finished);
}
});
/**
* OpenERP session aware widget
* A widget is a controller that doesnt take an element_id
* it render its own html render() that you should insert into the dom
* and bind it at start()
*/
instance.base.BaseWidget = instance.base.Controller.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the render() method can not be used.
*
* @type string
*/
template: null,
/**
* The prefix used to generate an id automatically. Should be redefined in
* subclasses. If it is not defined, a default identifier will be used.
*
* @type string
*/
identifier_prefix: 'generic-identifier',
/**
* Base class for widgets. Handle rendering (based on a QWeb template),
* identifier generation, parenting and destruction of the widget.
* Also initialize the identifier.
*
* @constructs
* @params {openerp.base.search.BaseWidget} parent The parent widget.
*/
init: function (parent) {
this._super(parent);
this.make_id(this.identifier_prefix);
},
/**
* Sets and returns a globally unique identifier for the widget.
*
* If a prefix is appended, the identifier will be appended to it.
*
* @params sections prefix sections, empty/falsy sections will be removed
*/
make_id: function () {
this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
return this.element_id;
},
/**
* Render the widget. This.template must be defined.
* The content of the current object is passed as context to the template.
*
* @param {object} additional Additional context arguments to pass to the template.
*/
render: function (additional) {
return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
},
/**
* "Starts" the widgets. Called at the end of the rendering, this allows
* to get a jQuery object referring to the DOM ($element attribute).
*/
start: function () {
this._super();
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : null;
},
/**
* "Stops" the widgets. Called when the view destroys itself, this
* lets the widgets clean up after themselves.
*/
stop: function () {
if(this.$element != null) {
this.$element.remove();
}
this._super();
}
});
// --------------------------------------------------------
// N-style aka New-Style or Niv-Style
// --------------------------------------------------------
instance.base.NivController = instance.base.Class.extend({
init: function(parent) {
this.controller_parent = parent;
// Take the session of the parent if defined
if(this.controller_parent && this.controller_parent.session) {
this.session = this.controller_parent.session;
}
// Transform on_* method into openerp.base.callbacks
for (var name in this) {
if(typeof(this[name]) == "function") {
this[name].debug_name = name;
// bind ALL function to this not only on_and _do ?
if((/^on_|^do_/).test(name)) {
this[name] = instance.base.callback(this, this[name]);
}
}
}
},
/**
* Event binding, rpc and callback calling required to initialize the
* object should happen here
*
* Returns a promise object letting callers (subclasses and direct callers)
* know when this component is done starting
*
* @returns {jQuery.Deferred}
*/
start: function() {
// returns an already fulfilled promise. Maybe we could return nothing?
// $.when can take non-deferred and in that case it simply considers
// them all as fulfilled promises.
// But in thise case we *have* to ensure callers use $.when and don't
// try to call deferred methods on this return value.
return $.Deferred().done().promise();
},
stop: function() {
},
log: function() {
var args = Array.prototype.slice.call(arguments);
var caller = arguments.callee.caller;
// TODO add support for line number using
// https://github.com/emwendelin/javascript-stacktrace/blob/master/stacktrace.js
// args.unshift("" + caller.debug_name);
this.on_log.apply(this,args);
},
on_log: function() {
if(window.openerp.debug || (window.location.search.indexOf('?debug') !== -1)) {
var notify = false;
var body = false;
if(window.console) {
console.log(arguments);
} else {
body = true;
}
var a = Array.prototype.slice.call(arguments, 0);
for(var i = 0; i < a.length; i++) {
var v = a[i]==null ? "null" : a[i].toString();
if(i==0) {
notify = v.match(/^not/);
body = v.match(/^bod/);
}
if(body) {
$('<pre></pre>').text(v).appendTo($('body'));
}
if(notify && this.notification) {
this.notification.notify("Logging:",v);
}
}
}
},
/**
* Performs a JSON-RPC call
*
* @param {String} url endpoint url
* @param {Object} data RPC parameters
* @param {Function} success RPC call success callback
* @param {Function} error RPC call error callback
* @returns {jQuery.Deferred} deferred object for the RPC call
*/
rpc: function(url, data, success, error) {
return this.session.rpc(url, data, success, error);
},
do_action: function(action, on_finished) {
return this.controller_parent.do_action(action, on_finished);
}
});
instance.base.NivWidget = instance.base.Controller.extend({
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the render() method can not be used.
*
* @type string
*/
template: null,
/**
* The prefix used to generate an id automatically. Should be redefined in
* subclasses. If it is not defined, a default identifier will be used.
*
* @type string
*/
identifier_prefix: 'generic-identifier',
/**
* Base class for widgets. Handle rendering (based on a QWeb template),
* identifier generation, parenting and destruction of the widget.
* Also initialize the identifier.
*
* @constructs
* @params {openerp.base.search.BaseWidget} parent The parent widget.
*/
init: function(parent, element_id) {
this._super(parent);
this.make_id(this.identifier_prefix);
// this.element_id = element_id;
// this.$element = $('#' + element_id);
// if (element_id) {
// instance.screen[element_id] = this;
// }
// save the parent children relationship
this.controller_children = [];
if(parent && parent.controller_children) {
parent.controller_children.push(this);
}
// backward compatibility
this.parent = this.controller_parent;
this.children = this.controller_children;
},
/**
* Event binding, rpc and callback calling required to initialize the
* object should happen here
*
* Returns a promise object letting callers (subclasses and direct callers)
* know when this component is done starting
*
* @returns {jQuery.Deferred}
*/
/**
* "Starts" the widgets. Called at the end of the rendering, this allows
* to get a jQuery object referring to the DOM ($element attribute).
*/
start: function () {
this._super();
var tmp = document.getElementById(this.element_id);
this.$element = tmp ? $(tmp) : null;
},
stop: function() {
if(this.$element != null) {
this.$element.remove();
}
if (this.parent && this.parent.children) {
this.parent.children = _.without(this.parent.children, this);
this.parent.controller_children = this.parent.children;
}
this.parent = null;
this.controller_parent = null;
},
/**
* Sets and returns a globally unique identifier for the widget.
*
* If a prefix is appended, the identifier will be appended to it.
*
* @params sections prefix sections, empty/falsy sections will be removed
*/
make_id: function () {
this.element_id = _.uniqueId(_.toArray(arguments).join('_'));
return this.element_id;
},
/**
* Render the widget. This.template must be defined.
* The content of the current object is passed as context to the template.
*
* @param {object} additional Additional context arguments to pass to the template.
*/
render: function (additional) {
return QWeb.render(this.template, _.extend({}, this, additional != null ? additional : {}));
},
widget_add: function(element, addfunc) {
},
widget_append: function(element) {
},
widget_prepend: function(element) {
},
widget_append2: function(element) {
},
widget_prepend2: function(element) {
},
});
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
+7 -3
View File
@@ -291,14 +291,18 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
* Read the indexed record.
*/
read_index: function (fields, callback) {
var def = $.Deferred().then(callback);
if (_.isEmpty(this.ids)) {
return $.Deferred().reject().promise();
def.reject();
} else {
fields = fields || false;
return this.read_ids([this.ids[this.index]], fields, function(records) {
callback(records[0]);
return this.read_ids([this.ids[this.index]], fields).then(function(records) {
def.resolve(records[0]);
}, function() {
def.reject.apply(def, arguments);
});
}
return def.promise();
},
default_get: function(fields, callback) {
return this.rpc('/base/dataset/default_get', {
+38 -16
View File
@@ -20,7 +20,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
init: function(parent, element_id, dataset, view_id, options) {
this._super(parent, element_id);
this.set_default_options();
this.view_manager = parent || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;
@@ -49,9 +48,6 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi
return def.promise();
} else {
var context = new openerp.base.CompoundContext(this.dataset.get_context());
if (this.view_manager.action && this.view_manager.action.context) {
context.add(this.view_manager.action.context);
}
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
toolbar: this.options.sidebar, context: context}, this.on_loaded);
}
@@ -718,6 +714,10 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "WidgetButton";
if (this.string) {
// We don't have button key bindings in the webclient
this.string = this.string.replace(/_/g, '');
}
if (node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
@@ -774,11 +774,12 @@ openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
if (this.node.tag == 'label' && this.node.attrs.colspan) {
this.is_field_label = false;
this.template = "WidgetParagraph";
this.colspan = this.node.attrs.colspan;
} else {
this.is_field_label = true;
this.template = "WidgetLabel";
this.colspan = 1;
}
this.colspan = 1;
},
render: function () {
if (this['for'] && this.type !== 'label') {
@@ -833,10 +834,12 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({
},
update_dom: function() {
this._super.apply(this, arguments);
this.$element.toggleClass('disabled', this.readonly);
this.$element.toggleClass('required', this.required);
if (this.view.show_invalid) {
this.$element.toggleClass('invalid', !this.is_valid());
if (!this.disable_utility_classes) {
this.$element.toggleClass('disabled', this.readonly);
this.$element.toggleClass('required', this.required);
if (this.view.show_invalid) {
this.$element.toggleClass('invalid', !this.is_valid());
}
}
},
on_ui_change: function() {
@@ -1477,7 +1480,7 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
}, self.build_domain(),
new openerp.base.CompoundContext(self.build_context(), context || {}));
pop.on_select_elements.add(function(element_ids) {
var dataset = new openerp.base.DataSetStatic(this, this.field.relation, self.build_context());
var dataset = new openerp.base.DataSetStatic(self, self.field.relation, self.build_context());
dataset.name_get([element_ids[0]], function(data) {
self._change_int_ext_value(data[0]);
pop.stop();
@@ -1594,6 +1597,7 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this.template = "FieldOne2Many";
this.is_started = $.Deferred();
this.form_last_update = $.Deferred();
this.disable_utility_classes = true;
},
start: function() {
this._super.apply(this, arguments);
@@ -1639,12 +1643,16 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
controller.on_record_loaded.add_last(function() {
once.resolve();
});
controller.on_form_changed.add_last(function() {
controller.on_pager_action.add_first(function() {
self.save_form_view();
});
controller.$element.find(".oe_form_button_save_edit").hide();
}
self.is_started.resolve();
});
this.viewmanager.on_mode_switch.add_first(function() {
self.save_form_view();
});
setTimeout(function () {
self.viewmanager.start();
}, 0);
@@ -1714,6 +1722,9 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
this._super(value);
this.dataset.reset_ids(value);
}
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
}
$.when(this.is_started).then(function() {
self.reload_current_view();
});
@@ -1722,7 +1733,6 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
var self = this;
if (!this.dataset)
return [];
this.save_form_view();
var val = this.dataset.delete_all ? [commands.delete_all()] : [];
val = val.concat(_.map(this.dataset.ids, function(id) {
var alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;});
@@ -1753,16 +1763,20 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
} else if (!res.isResolved()) {
throw "Asynchronous get_value() is not supported in form view.";
}
return res;
}
}
return false;
},
is_valid: function() {
this.validate();
return this._super();
},
validate: function() {
this.invalid = false;
var self = this;
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
if(self.viewmanager.active_view === "list") {
return;
} else if (self.viewmanager.active_view === "form") {
if (self.viewmanager.active_view === "form") {
for (var f in view.fields) {
f = view.fields[f];
if (!f.is_valid()) {
@@ -1771,6 +1785,14 @@ openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
}
}
}
},
is_dirty: function() {
this.save_form_view();
return this._super();
},
update_dom: function() {
this._super.apply(this, arguments);
this.$element.toggleClass('disabled', this.readonly);
}
});
@@ -1837,7 +1859,7 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
var self = this;
this.dataset = new openerp.base.form.Many2ManyDataSet( this, this.field.relation);
this.dataset = new openerp.base.form.Many2ManyDataSet(this, this.field.relation);
this.dataset.m2m = this;
this.dataset.on_unlink.add_last(function(ids) {
self.on_ui_change();
+4 -1
View File
@@ -98,11 +98,14 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
openerp.base.ActionDialog = openerp.base.Dialog.extend({
identifier_prefix: 'action_dialog',
stop: function() {
on_close: function() {
this._super(this, arguments);
if (this.close_callback) {
this.close_callback();
}
},
stop: function() {
this._super(this, arguments);
if (this.viewmanager) {
this.viewmanager.stop();
}