[MERGE] Merged latest trunk branch.Improved dashboard using addwidget api.

bzr revid: vda@tinyerp.com-20110427102456-icjhuk24ke8x8cbg
This commit is contained in:
vda (OpenERP)
2011-04-27 15:54:56 +05:30
14 changed files with 310 additions and 193 deletions
+24 -9
View File
@@ -409,6 +409,11 @@ class DataSet(openerpweb.Controller):
r = getattr(m, method)(ids, *args)
return {'result': r}
@openerpweb.jsonrequest
def exec_workflow(self, req, model, id, signal):
r = req.session.exec_workflow(model, id, signal)
return {'result': r}
@openerpweb.jsonrequest
def default_get(self, req, model, fields, context={}):
m = req.session.model(model)
@@ -573,19 +578,22 @@ class ListView(View):
:param int limit: search limit, for pagination
:returns: hell if I have any idea yet
"""
view = self.fields_view_get(request, model, id)
view = self.fields_view_get(request, model, id, toolbar=True)
rows = DataSet().do_search_read(request, model,
offset=offset, limit=limit,
domain=domain)
eval_context = request.session.evaluation_context(
request.context)
return [
{'data': dict((key, {'value': value})
for key, value in row.iteritems()),
'color': self.process_colors(view, row, eval_context)}
for row in rows
]
return {
'view': view,
'records': [
{'data': dict((key, {'value': value})
for key, value in row.iteritems()),
'color': self.process_colors(view, row, eval_context)}
for row in rows
]
}
def process_colors(self, view, row, context):
colors = view['arch']['attrs'].get('colors')
@@ -618,5 +626,12 @@ class Action(openerpweb.Controller):
_cp_path = "/base/action"
@openerpweb.jsonrequest
def load(self, req, action_id):
return {}
def load(self, req, action_id, context={}):
Actions = req.session.model('ir.actions.actions')
value = False
action_type = Actions.read([action_id], ['type'], context)
if action_type:
action = req.session.model(action_type[0]['type']).read([action_id], False, context)
if action:
value = action[0]
return {'result': value}
+11 -5
View File
@@ -336,8 +336,9 @@ body.openerp {
}
/* Main Application */
.openerp .application {
.openerp .oe-application {
padding: 0px;
height: 100%;
}
.openerp h2.oe_view_title {
@@ -486,6 +487,9 @@ body.openerp {
vertical-align: middle;
}
.openerp .oe-listview .oe-field-cell {
cursor: pointer;
}
.openerp .oe-listview .oe-field-cell button {
padding: 0;
border: none;
@@ -636,7 +640,7 @@ body.openerp {
.openerp td.required input, .openerp td.required select {
background-color: #D2D2FF;
}
.openerp td.invalid input, .openerp td.invalid select {
.openerp td.invalid input, .openerp td.invalid select, .openerp td.invalid textarea {
background-color: #F66;
border: 1px solid #D00;
}
@@ -673,13 +677,14 @@ body.openerp {
}
.openerp .view-manager-main-content {
width: 100%;
padding: 6px;
}
.openerp .view-manager-main-sidebar {
width:0;
padding:0;
margin:0;
width: 180px;
padding: 0;
margin: 0;
}
.openerp .sidebar-main-div {
@@ -746,6 +751,7 @@ body.openerp {
}
.openerp .toggle-sidebar {
cursor: pointer;
border-color: #D2CFCF;
border-style: solid;
border-width: 1px;
+7
View File
@@ -118,6 +118,13 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
ids: ids,
args: args
}, callback);
},
exec_workflow: function (id, signal, callback) {
return this.rpc('/base/dataset/exec_workflow', {
model: this.model,
id: id,
signal: signal
}, callback);
}
});
+16 -18
View File
@@ -457,16 +457,14 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({
}
this.add_widget(widget);
},
add_widget: function(w) {
if (!w.invisible) {
var current_row = this.table[this.table.length - 1];
if (current_row.length && (this.x + w.colspan) > this.columns) {
current_row = this.add_row();
}
current_row.push(w);
this.x += w.colspan;
add_widget: function(widget) {
var current_row = this.table[this.table.length - 1];
if (current_row.length && (this.x + widget.colspan) > this.columns) {
current_row = this.add_row();
}
return w;
current_row.push(widget);
this.x += widget.colspan;
return widget;
}
});
@@ -534,7 +532,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
on_confirmed: function() {
var attrs = this.node.attrs;
if (attrs.special) {
this.on_button_object({
this.on_button_action({
result : { type: 'ir.actions.act_window_close' }
});
} else {
@@ -542,19 +540,20 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
var context = _.extend({}, this.view.dataset.context, attrs.context || {});
switch(type) {
case 'object':
return this.view.dataset.call(attrs.name, [this.view.datarecord.id], [context], this.on_button_object);
break;
return this.view.dataset.call(attrs.name, [this.view.datarecord.id], [context], this.on_button_action);
case 'action':
return this.rpc('/base/action/load', { action_id: parseInt(attrs.name) }, this.on_button_action);
default:
this.log(_.sprintf("Unsupported button type : %s", type));
return this.view.dataset.exec_workflow(this.view.datarecord.id, attrs.name, this.on_button_action);
}
}
},
on_button_object: function(r) {
if (r.result === false) {
this.log("Button object returns false");
} else if (r.result.constructor == Object) {
on_button_action: function(r) {
console.log("Got reesonse button", r)
if (r.result && r.result.constructor == Object) {
this.session.action_manager.do_action(r.result);
} else {
this.log("Button returned", r.result);
this.view.reload();
}
}
@@ -911,7 +910,6 @@ openerp.base.form.FieldReference = openerp.base.form.Field.extend({
* Registry of form widgets, called by :js:`openerp.base.FormView`
*/
openerp.base.form.widgets = new openerp.base.Registry({
'hpaned': 'openerp.base.form.Hpaned',
'group' : 'openerp.base.form.WidgetFrame',
'notebook' : 'openerp.base.form.WidgetNotebook',
'separator' : 'openerp.base.form.WidgetSeparator',
+96 -14
View File
@@ -10,9 +10,23 @@ openerp.base.ListView = openerp.base.Controller.extend(
// whether the column headers should be displayed
'header': true,
// display addition button, with that label
'addable': "New"
'addable': "New",
// whether the list view can be sorted, note that once a view has been
// sorted it can not be reordered anymore
'sortable': true,
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true
},
/**
* Core class for list-type displays.
*
* As a view, needs a number of view-related parameters to be correctly
* instantiated, provides options and overridable methods for behavioral
* customization.
*
* See constructor parameters and method documentations for information on
* the default behaviors and possible options for the list view.
*
* @constructs
* @param view_manager
* @param session An OpenERP session object
@@ -24,6 +38,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
* @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 {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
*/
init: function(view_manager, session, element_id, dataset, view_id, options) {
this._super(session, element_id);
@@ -37,11 +53,42 @@ openerp.base.ListView = openerp.base.Controller.extend(
this.options = _.extend({}, this.defaults, options || {});
},
/**
* View startup method, the default behavior is to set the ``oe-listview``
* class on its root element and to perform an RPC load call.
*
* @returns {$.Deferred} loading promise
*/
start: function() {
this.$element.addClass('oe-listview');
return this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id,
toolbar:!!this.view_manager.sidebar}, this.on_loaded);
return this.rpc("/base/listview/load", {
model: this.model,
view_id: this.view_id,
toolbar: !!this.view_manager.sidebar
}, this.on_loaded);
},
/**
* Called after loading the list view's description, sets up such things
* as the view table's columns, renders the table itself and hooks up the
* various table-level and row-level DOM events (action buttons, deletion
* buttons, selection of records, [New] button, selection of a given
* record, ...)
*
* Sets up the following:
*
* * Processes arch and fields to generate a complete field descriptor for each field
* * Create the table itself and allocate visible columns
* * Hook in the top-level (header) [New|Add] and [Delete] button
* * Sets up showing/hiding the top-level [Delete] button based on records being selected or not
* * Sets up event handlers for action buttons and per-row deletion button
* * Hooks global callback for clicking on a row
* * Sets up its sidebar, if any
*
* @param {Object} data wrapped fields_view_get result
* @param {Object} data.fields_view fields_view_get result (processed)
* @param {Object} data.fields_view.fields mapping of fields for the current model
* @param {Object} data.fields_view.arch current list view descriptor
*/
on_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
@@ -108,8 +155,9 @@ openerp.base.ListView = openerp.base.Controller.extend(
var context = _.extend(
{}, self.dataset.context, field.context || {});
self.dataset.call(action, [row.data.id.value], [context],
self.do_reload);
self.notification.notify(
'[Dummy] Execute action', self.dataset.model + '#' + action + "([" + row.data.id.value + '])');
// TODO: implement actions correctly once the corresponding manager has been extracted (and filled)
e.stopImmediatePropagation();
});
$table.delegate(
@@ -127,10 +175,18 @@ openerp.base.ListView = openerp.base.Controller.extend(
/**
* Fills the table with the provided records after emptying it
*
* @param {Array} records the records to fill the list view with
* TODO: should also re-load the table itself, as e.g. columns may have changed
*
* @param {Object} result filling result
* @param {Array} [result.view] the new view (wrapped fields_view_get result)
* @param {Array} result.records records the records to fill the list view with
* @returns {Promise} promise to the end of view rendering (list views are asynchronously filled for improved responsiveness)
*/
do_fill_table: function(records) {
do_fill_table: function(result) {
if (result.view) {
this.on_loaded({fields_view: result.view});
}
var records = result.records;
var $table = this.$element.find('table');
this.rows = records;
@@ -188,14 +244,19 @@ openerp.base.ListView = openerp.base.Controller.extend(
});
},
/**
* Asks the view manager to switch to a different view, using the provided
* record index (within the current dataset).
* Used to handle a click on a table row, if no other handler caught the
* event.
*
* The default implementation asks the list view's view manager to switch
* to a different view (by calling
* :js:func:`~openerp.base.ViewManager.on_mode_switch`), using the
* provided record index (within the current list view's dataset).
*
* 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 {String} [view="form"] the view to switch to
* @param {String} [view="form"] the view type to switch to
*/
switch_to_record:function (index, view) {
view = view || 'form';
@@ -204,6 +265,16 @@ openerp.base.ListView = openerp.base.Controller.extend(
this.view_manager.on_mode_switch(view);
}, this));
},
/**
* Base handler for clicking on a row, discovers the index of the record
* corresponding to the clicked row in the list view's dataset.
*
* Should not be overridden, use
* :js:func:`~openerp.base.ListView.switch_to_record` to customize the
* behavior of the list view when clicking on a row instead.
*
* @param {Object} event jQuery DOM event object
*/
on_select_row: function (event) {
var $target = $(event.currentTarget);
if (!$target.parent().is('tbody')) {
@@ -216,8 +287,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
if (index == undefined || index === -1) {
return;
}
this.switch_to_record(index);
},
this.switch_to_record(index);
},
do_show: function () {
this.$element.show();
if (this.hidden) {
@@ -246,6 +317,15 @@ openerp.base.ListView = openerp.base.Controller.extend(
'domain': this.dataset.domain
}, this.do_fill_table);
},
/**
* Event handler for a search, asks for the computation/folding of domains
* and contexts (and group-by), then reloads the view's content.
*
* @param {Array} domains a sequence of literal and non-literal domains
* @param {Array} contexts a sequence of literal and non-literal contexts
* @param {Array} groupbys a sequence of literal and non-literal group-by contexts
* @returns {$.Deferred} fold request evaluation promise
*/
do_search: function (domains, contexts, groupbys) {
var self = this;
return this.rpc('/base/session/eval_domain_and_context', {
@@ -266,7 +346,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
/**
* Handles the signal to delete a line from the DOM
*
* @param e
* @param e jQuery event object
*/
do_delete: function (e) {
// don't link to forms
@@ -295,7 +375,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
},
/**
* Gets the ids of all currently selected records, if any
* @returns a list of ids, empty if no record is selected (or the list view is not selectable
* @returns {Array} empty if no record is selected (or the list view is not selectable)
*/
get_selection: function () {
if (!this.options.selectable) {
@@ -307,6 +387,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
return rows[$(this).prevAll().length].data.id.value;
}).get();
}
// TODO: implement sort (click on column headers), if sorted, the list can not be reordered anymore
// TODO: implement reorder (drag and drop rows)
});
openerp.base.TreeView = openerp.base.Controller.extend({
+12 -10
View File
@@ -19,7 +19,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
* Supported actions: act_window
*/
do_action: function(action) {
this.log(action);
var self = this;
// instantiate the right controllers by understanding the action
switch (action.type) {
@@ -36,14 +35,14 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
// When dialog is closed with ESC key or close manually, branch to act_window_close logic
self.do_action({ type: 'ir.actions.act_window_close' });
});
var viewmanager = new openerp.base.ViewManagerAction(this.session ,element_id, action, false);
var viewmanager = new openerp.base.ViewManagerAction(this.session, element_id, action, false);
viewmanager.start();
this.dialog_stack.push(viewmanager);
} else if (action.target == "current") {
} else {
if (this.viewmanager) {
this.viewmanager.stop();
}
this.viewmanager = new openerp.base.ViewManagerAction(this.session,this.element_id, action, true);
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action, true);
this.viewmanager.start();
}
break;
@@ -211,14 +210,17 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
// 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);
if (searchview_id) {
var searchview_loaded = this.setup_search_view(
searchview_id, 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 (this.action['auto_search']) {
$.when(searchview_loaded, inital_view_loaded)
.then(this.searchview.do_search);
}
}
},
stop: function() {
// should be replaced by automatic destruction implemented in BaseWidget
+21 -26
View File
@@ -18,9 +18,6 @@
</div>
</div>
<div id="oe_login" class="login"></div>
<div style="position: absolute; right: 2px; top: 38px;">
<button onclick="QWeb.add_template('base.xml'); $('body').css('background-color', '#FFFF9C'); setTimeout(function() { $('body').css('background-color', '#FFF'); }, 500);">Reload QWEB</button>
</div>
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"
t-att-class="'main_table' + (typeof kitten == 'undefined' ? '' : ' kitten-mode-activated')">
<tr>
@@ -33,10 +30,7 @@
<td valign="top" id="oe_secondary_menu" class="secondary_menu">
</td>
<td valign="top" width="100%" height="100%">
<div id="oe_main" class="main">
<div id="oe_app" class="application"></div>
<div id="oe_sidebar"></div>
</div>
<div id="oe_app" class="oe-application"></div>
</td>
</tr>
<tr>
@@ -135,23 +129,23 @@
<t t-name="ViewManager">
<table class="view-manager-main-table">
<tr>
<td class="view-manager-main-content">
<!-- TODO prefix id with the element_id of the controller t-attf-id="#{prefix}_localid" -->
<div class="oe_vm_switch">
<t t-foreach="views" t-as="view">
<button type="button" t-att-data-view-type="view[1]">
<t t-esc="view[1]"/>
</button>
</t>
</div>
<div t-attf-id="#{prefix}_search"/>
<t t-foreach="views" t-as="view">
<div t-attf-id="#{prefix}_view_#{view[1]}"/>
</t>
</td>
<td class="view-manager-main-sidebar">
</td>
</tr>
<td class="view-manager-main-content">
<!-- TODO prefix id with the element_id of the controller t-attf-id="#{prefix}_localid" -->
<div class="oe_vm_switch">
<t t-foreach="views" t-as="view">
<button type="button" t-att-data-view-type="view[1]">
<t t-esc="view[1]"/>
</button>
</t>
</div>
<div t-attf-id="#{prefix}_search" t-opentag="true"/>
<t t-foreach="views" t-as="view">
<div t-attf-id="#{prefix}_view_#{view[1]}"/>
</t>
</td>
<td class="view-manager-main-sidebar">
</td>
</tr>
</table>
</t>
<table t-name="ListView">
@@ -217,7 +211,8 @@
<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">
<t t-esc="row['data'][column.id].value"/>
<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"
t-if="is_button">
@@ -299,7 +294,7 @@
t-att-ondblclick="'console.log(\'' + widget.element_id + '\', openerp.screen.' + widget.element_id + ')'">
<t t-esc="widget.string"/>
<span t-if="widget.help">?</span>
<t t-if="widget.string">:</t>
<t t-if="widget.string and widget.node.tag != 'label'">:</t>
</label>
</t>
<t t-name="FieldChar">
+18 -18
View File
@@ -34,15 +34,15 @@ $(document).ready(function () {
asyncTest('render selection checkboxes', 2, function () {
var listview = new openerp.base.ListView(
{}, null,
'qunit-fixture', {model: null});
'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: null}}},
{data: {id: {value: null}}},
{data: {id: {value: null}}}
]).then(function () {
]}).then(function () {
ok(are(listview.$element.find('tbody th'),
'.oe-record-selector'));
ok(are(listview.$element.find('tbody th input'),
@@ -53,30 +53,30 @@ $(document).ready(function () {
asyncTest('render no checkbox if selectable=false', 1, function () {
var listview = new openerp.base.ListView(
{}, null,
'qunit-fixture', {model: null}, false,
'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false,
{selectable: false});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: null}}},
{data: {id: {value: null}}},
{data: {id: {value: null}}}
]).then(function () {
]}).then(function () {
equal(listview.$element.find('tbody th').length, 0);
start();
});
});
asyncTest('select a bunch of records', 2, function () {
var listview = new openerp.base.ListView(
{}, null, 'qunit-fixture', {model: null});
{}, null, 'qunit-fixture', {model: null, ids: [1, 2, 3], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: 1}}},
{data: {id: {value: 2}}},
{data: {id: {value: 3}}}
]).then(function () {
]}).then(function () {
listview.$element.find('tbody th input:eq(2)')
.attr('checked', true);
deepEqual(listview.get_selection(), [3]);
@@ -88,15 +88,15 @@ $(document).ready(function () {
});
asyncTest('render deletion button if list is deletable', 1, function () {
var listview = new openerp.base.ListView(
{}, null, 'qunit-fixture', {model: null});
{}, null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: null}}},
{data: {id: {value: null}}},
{data: {id: {value: null}}}
]).then(function () {
]}).then(function () {
equal(
listview.$element.find('tbody tr td.oe-record-delete button').length,
3);
@@ -109,15 +109,15 @@ $(document).ready(function () {
var listview = new openerp.base.ListView(
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
deleted = ids;
}});
}, ids: [1, 2, 3], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: 1}}},
{data: {id: {value: 2}}},
{data: {id: {value: 3}}}
]).then(function () {
]}).then(function () {
listview.$element.find('tbody td.oe-record-delete:eq(2) button').click();
deepEqual(deleted, [3]);
listview.$element.find('tbody td.oe-record-delete:eq(0) button').click();
@@ -130,15 +130,15 @@ $(document).ready(function () {
var listview = new openerp.base.ListView(
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
deleted = ids;
}});
}, ids: [1, 2, 3], index: 0});
listview.on_loaded(fvg);
listview.do_fill_table([
listview.do_fill_table({records: [
{data: {id: {value: 1}}},
{data: {id: {value: 2}}},
{data: {id: {value: 3}}}
]).then(function () {
]}).then(function () {
listview.$element.find('tbody th input:eq(2)')
.attr('checked', true);
listview.$element.find('tbody th input:eq(1)')
+10 -2
View File
@@ -5,5 +5,13 @@ class Dashboard(View):
_cp_path = "/base_dashboard/dashboard"
@openerpweb.jsonrequest
def load(self, req, node):
return
def load(self, req, node_attrs):
self.action_id = int(node_attrs['name'])
actions = req.session.model('ir.actions.actions')
result = actions.read([self.action_id],['type'], req.session.context)
if not result:
raise _('Action not found!')
self.action = req.session.model(result[0]['type']).read([self.action_id], False, req.session.context)[0]
return {'action': self.action}
@@ -1,89 +1,70 @@
openerp.base_dashboard = function(openerp) {
openerp.base_dashboard = function(openerp){
QWeb.add_template('/base_dashboard/static/src/xml/base_dashboard.xml');
openerp.base.form.Hpaned = openerp.base.form.Widget.extend({
init: function(view, node) {
openerp.base.form.Board = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "Hpaned";
this.template = "Board";
},
start: function() {
this._super.apply(this, arguments);
this.$element.html(QWeb.render(this.template))
this.$element.html(QWeb.render(this.template));
},
render: function() {
var self = this;
jQuery('body').append(
jQuery('<div>', {'id': 'dashboard_templates'}).hide()
);
jQuery('<div>', {'id': 'dashboard_template'}).load('/base_dashboard/static/src/dashboard_template.html',self.on_loaded).hide()
)
$("#dashboard_templates").load("/base_dashboard/static/src/dashboard_template.html", function(result){
self.render_dashboard()
});
},
render_dashboard: function() {
var self = this;
var data = {"layout": "layout2","data": []}
var get_column = ['first', 'second', 'third'];
on_loaded: function() {
var children = this.node.children;
var action = []
for (child in children) {
action.push({})
var ch_widget = children[child]['children'];
for (ch in ch_widget) {
if (ch_widget[ch].tag == 'action') {
// var _act = new openerp.base.form.Action(this.view, ch_widget[ch]);
// action[child][ch] = _act.render();
data['data'].push({
"title": ch_widget[ch].attrs.string,
"id": ch_widget[ch].attrs.name,
"column": get_column[child],
"url": "/base_dashboard/static/data.html",
"open": true
})
}
}
}
var get_column = ['first', 'second', 'third'];
var board = jQuery('#dashboard').dashboard({
layoutClass:'layout',
json_data: data
layoutClass:'layout'
});
board.init();
for(var ch = 0; ch < children.length; ch++) {
var ch_widgets = children[ch].children;
for(var chld = 0; chld < ch_widgets.length; chld++) {
var widget_type = ch_widgets[chld].tag;
var board_element = board.element.find('[id=column-'+get_column[chld]+']');
var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, ch_widgets[chld], board_element);
board.addWidget({
'id': ch_widgets[chld].attrs.name,
'title': ch_widgets[chld].attrs.string,
'url': widget.start()
}, board_element)
}
}
}
});
openerp.base.form.Action = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
init: function(view, node, column) {
this._super(view, node, column);
this.template = "Action";
},
start: function() {
this._super.apply(this, arguments);
this.rpc('/base_dashboard/dashboard/load',{
node_attrs: this.node.attrs
},
this.on_load_action);
},
render: function(){
return QWeb.render(this.template, {'node': this.node});
on_load_action: function(result) {
var action = result.action;
action_manager = new openerp.base.ActionManager(this.session, this.$element.attr('id'));
action_manager.start();
action_manager.do_action(action);
}
});
openerp.base.form.Vpaned = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "Vpaned";
},
start: function() {
this._super.apply(this, arguments);
},
render: function(){
return QWeb.render(this.template);
}
});
}
})
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
}
@@ -1,17 +1,12 @@
<template>
<t t-name="Hpaned">
<div>HPANED>>>>>>>>>>>>>>>>>>>>>>>>></div>
<t t-name="Board">
<div id="dashboard" class="dashboard">
<!-- this HTML covers all layouts. The 5 different layouts are handled by setting another layout classname -->
<div class="layout">
<div class="column first column-first"></div>
<div class="column second column-second"></div>
<div class="column third column-third"></div>
<div id="column-first" class="column first column-first"></div>
<div id="column-second" class="column second column-second"></div>
<div id="column-third" class="column third column-third"></div>
</div>
</div>
</t>
<t t-name="Vpaned">
</t>
<t t-name="Action">
<t t-esc="node.attrs.string"/>
</t>
</template>
+14 -13
View File
@@ -55,7 +55,7 @@ Events
Event triggered after a user asked for a search. The search view
fires this event after collecting all input data (contexts, domains
and group_by contexts). Note that the search view does *not* merge
those (or therwise evaluate them), they are returned as provided by
those (or otherwise evaluate them), they are returned as provided by
the various inputs within the view.
``on_clear``
@@ -71,7 +71,7 @@ An important concept in the search view is that of input. It is both
an informal protocol and an abstract type that can be inherited from.
Inputs are widgets which can contain user data (a char widget for
instance, or a selection box). They are able of action and of
instance, or a selection box). They are capable of action and of
reaction:
.. _views-search-registration:
@@ -93,9 +93,9 @@ reaction:
* Return a context (an object), this is the "normal" response if the
input holds a value.
* Return a falsy value (generally ``null``). This value indicates
the input does not contain any value and will not take part in the
research.
* Return a value that evaluates as false (generally ``null``). This
value indicates the input does not contain any value and will not
affect the results of the search.
* Raise :js:class:`openerp.base.search.Invalid` to indicate that it
holds a value but this value can not be used in the search
@@ -104,7 +104,7 @@ reaction:
the search process.
:js:class:`~openerp.base.search.Invalid` takes three mandatory
arguments: an indentifier (a name for instance), the invalid value
arguments: an identifier (a name for instance), the invalid value,
and a validation message indicating the issue.
``get_domain``
@@ -114,11 +114,11 @@ reaction:
The :js:class:`openerp.base.search.Input` type implements registration
on its own, but its implementations of ``get_context`` and
``get_domain`` simply raise errors and *have* to be overridden.
``get_domain`` simply raise errors and *must* be overridden.
One last action is for filters, as an activation order has to be kept
on them for some controls (establish the correct grouping sequence for
instance).
on them for some controls (to establish the correct grouping sequence,
for instance).
To that end, filters can call
:js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
@@ -137,7 +137,7 @@ Life cycle
The search view has a pretty simple and linear life cycle, in three main steps:
:js:class:`init <openerp.base.SearchView>`
:js:class:`~openerp.base.SearchView.init`
Nothing interesting happens here
@@ -211,7 +211,7 @@ called in this order:
Gives the widget the opportunity to unbind its events, remove itself
from the DOM and perform any other cleanup task it may have.
Event if the widget does not do anything itself, it is responsible
Even if the widget does not do anything itself, it is responsible
for shutting down its children.
An abstract type is available and can be inherited from, to simplify
@@ -232,7 +232,8 @@ abstract types, used to implement input widgets:
<views-search-registration>`.
If inherited from, descendant classes should not call its
implementations of ``get_context`` and ``get_domain``.
implementations of :js:func:`~openerp.base.search.Input.get_context`
and :js:func:`~openerp.base.search.Input.get_domain`.
* :js:class:`openerp.base.search.Field` is used to implement more
"field" widgets (which allow the user to input potentially complex
@@ -251,7 +252,7 @@ abstract types, used to implement input widgets:
:js:func:`~openerp.base.search.Widget.make_id`.
* It sets up a basic (overridable)
:js:attr:`~opererp.base.search.Field.template` attribute, combined
:js:attr:`~openerp.base.search.Field.template` attribute, combined
with the previous tasks, this makes subclasses of
:js:class:`~openerp.base.search.Field` render themselves "for
free".
+3 -3
View File
@@ -31,7 +31,7 @@ Guides and main documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The meat and most important part of all documentation. Should be
written in plain english, using reStructuredText_ and taking advantage
written in plain English, using reStructuredText_ and taking advantage
of `Sphinx's extensions`_, especially `cross-references`_.
Python API Documentation
@@ -65,7 +65,7 @@ RST, using Sphinx's `Python domain`_ [#]_:
and ``__new__``), using the `info fields`_ as well.
* Attributes (class and instance) should be documented in their
class's docstrin via the ``.. attribute::`` directiveg, following
class's docstring via the ``.. attribute::`` directive, following
the class's own documentation.
* The relation between modules and module-level attributes is similar:
@@ -220,7 +220,7 @@ Roadmap
Release notes
+++++++++++++
.. [#] because Python is the default domain, the ``py:`` markup prefix
.. [#] Because Python is the default domain, the ``py:`` markup prefix
is optional and should be left out.
.. [#] Resig's Class still uses prototypes under the hood, it doesn't
+33 -6
View File
@@ -116,6 +116,12 @@ class OpenERPSession(object):
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
return r
def exec_workflow(self, model, id, signal):
if not (self._db and self._uid and self._password):
raise OpenERPUnboundException()
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
return r
def model(self, model):
""" Get an RPC proxy for the object ``model``, bound to this session.
@@ -289,13 +295,34 @@ class JsonRequest(object):
* the json string is passed as a form parameter named "request"
* method is currently ignored
Sucessful request:
--> {"jsonrpc": "2.0", "method": "call", "params": {"session_id": "SID", "context": {}, "arg1": "val1" }, "id": null}
<-- {"jsonrpc": "2.0", "result": { "res1": "val1" }, "id": null}
Sucessful request::
Request producing a error:
--> {"jsonrpc": "2.0", "method": "call", "params": {"session_id": "SID", "context": {}, "arg1": "val1" }, "id": null}
<-- {"jsonrpc": "2.0", "error": {"code": 1, "message": "End user error message.", "data": {"code": "codestring", "debug": "traceback" } }, "id": null}
--> {"jsonrpc": "2.0",
"method": "call",
"params": {"session_id": "SID",
"context": {},
"arg1": "val1" },
"id": null}
<-- {"jsonrpc": "2.0",
"result": { "res1": "val1" },
"id": null}
Request producing a error::
--> {"jsonrpc": "2.0",
"method": "call",
"params": {"session_id": "SID",
"context": {},
"arg1": "val1" },
"id": null}
<-- {"jsonrpc": "2.0",
"error": {"code": 1,
"message": "End user error message.",
"data": {"code": "codestring",
"debug": "traceback" } },
"id": null}
"""