[MERGE] from trunk

bzr revid: xmo@openerp.com-20110606113621-r3q0otfjdu7l0ubp
This commit is contained in:
Xavier Morel 2011-06-06 13:36:21 +02:00
commit c452d85a7a
24 changed files with 649 additions and 271 deletions

View File

@ -241,7 +241,10 @@ def clean_action(action, session):
action['domain'],
session.evaluation_context(
action['context'])) or []
fix_view_modes(action)
if not action.has_key('flags'):
# Set empty flags dictionary for web client.
action['flags'] = dict()
return fix_view_modes(action)
def fix_view_modes(action):
""" For historical reasons, OpenERP has weird dealings in relation to
@ -271,6 +274,7 @@ def fix_view_modes(action):
[id, mode if mode != 'tree' else 'list']
for id, mode in action['views']
]
return action
class Menu(openerpweb.Controller):
_cp_path = "/base/menu"
@ -452,20 +456,49 @@ class DataGroup(openerpweb.Controller):
dict(request.context, group_by=group_by_fields))
class View(openerpweb.Controller):
_cp_path = "/base/view"
def fields_view_get(self, request, model, view_id, view_type,
transform=True, toolbar=False, submenu=False):
Model = request.session.model(model)
fvg = Model.fields_view_get(view_id, view_type, request.context,
toolbar, submenu)
self.process_view(request.session, fvg, request.context, transform)
return fvg
def process_view(self, session, fvg, context, transform):
if transform:
evaluation_context = request.session.evaluation_context(
request.context or {})
xml = self.transform_view(
fvg['arch'], request.session, evaluation_context)
evaluation_context = session.evaluation_context(context or {})
xml = self.transform_view(fvg['arch'], session, evaluation_context)
else:
xml = ElementTree.fromstring(fvg['arch'])
fvg['arch'] = Xml2Json.convert_element(xml)
return fvg
for field in fvg['fields'].values():
if field.has_key('views') and field['views']:
for view in field["views"].values():
self.process_view(session, view, None, transform)
@openerpweb.jsonrequest
def add_custom(self, request, view_id, arch):
CustomView = request.session.model('ir.ui.view.custom')
CustomView.create({
'user_id': request.session._uid,
'ref_id': view_id,
'arch': arch
})
return {'result': True}
@openerpweb.jsonrequest
def undo_custom(self, request, view_id, reset=False):
CustomView = request.session.model('ir.ui.view.custom')
vcustom = CustomView.search([('user_id', '=', request.session._uid), ('ref_id' ,'=', view_id)])
if vcustom:
if reset:
CustomView.unlink(vcustom)
else:
CustomView.unlink([vcustom[0]])
return {'result': True}
return {'result': False}
def normalize_attrs(self, elem, context):
""" Normalize @attrs, @invisible, @required, @readonly and @states, so
@ -693,12 +726,12 @@ class Action(openerpweb.Controller):
_cp_path = "/base/action"
@openerpweb.jsonrequest
def load(self, req, action_id, context={}):
def load(self, req, action_id):
Actions = req.session.model('ir.actions.actions')
value = False
action_type = Actions.read([action_id], ['type'], context)
action_type = Actions.read([action_id], ['type'], req.session.context)
if action_type:
action = req.session.model(action_type[0]['type']).read([action_id], False, context)
action = req.session.model(action_type[0]['type']).read([action_id], False, req.session.context)
if action:
value = action[0]
value = clean_action(action[0], req.session)
return {'result': value}

View File

@ -61,19 +61,71 @@ body.openerp {
/* Login */
.openerp .login {
display: none;
padding: 6px;
z-index: 1002;
width: 34%;
position: fixed;
top: 0;
left: 33%;
}
.openerp .login form {
float: left;
width: 420px;
margin-left: 40px;
margin-bottom: 60px;
}
.openerp .login fieldset {
padding-bottom: 5px;
min-width: 100px;
margin-top: 60px;
border-radius: 10px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
}
.openerp .login fieldset legend {
padding: 4px;
}
.openerp .login .oe_box2 {
padding: 5px 5px 20px 5px;
}
.openerp .login .oe_box2 table {
width: 100%;
border:none;
}
.openerp .login .oe_box2 td {
padding: 3px;
text-align: right;
}
.openerp .login .oe_box2 td input {
width: 100%;
}
.openerp .login .oe_login_right_pane {
padding:70px 35px 5px 10px;
min-width: 200px;
margin-left: 500px;
}
.openerp .login .login_error_message {
display: none;
background-color: #9A0404;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
color: white;
font-family: Ubuntu, Helvetica, sans-serif;
font-size: 16px;
font-weight: bold;
padding: 5px;
margin-top: 5px;
text-align: center;
}
.openerp .login.login_invalid .login_error_message {
display: block;
}
.openerp.login-mode .login-container {
height: 100%;
}
.openerp .login_valid {
background-color: #8f8;
.openerp.login-mode .login {
display: block;
}
.openerp .login_invalid {
background-color: #f88;
.openerp.login-mode .menu,
.openerp.login-mode .secondary_menu,
.openerp.login-mode .oe-application {
display: none;
}
/* Main*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -172,7 +172,9 @@ openerp.base.BasicController = Class.extend( /** @lends openerp.base.BasicContro
init: function(element_id) {
this.element_id = element_id;
this.$element = $('#' + element_id);
openerp.screen[element_id] = this;
if (element_id) {
openerp.screen[element_id] = this;
}
// Transform on_* method into openerp.base.callbacks
for (var name in this) {
@ -379,7 +381,7 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
} else {
error_callback(response.error);
}
} else {
} else if (success_callback) {
success_callback(response["result"], textStatus, jqXHR);
}
},
@ -449,6 +451,13 @@ openerp.base.Session = openerp.base.BasicController.extend( /** @lends openerp.b
this.set_cookie('uid', this.uid);
this.set_cookie('session_id', this.session_id);
},
logout: function() {
this.uid = this.get_cookie('uid');
this.session_id = this.get_cookie('session_id');
this.set_cookie('uid', '');
this.set_cookie('session_id', '');
this.on_session_invalid(function() {});
},
/**
* Fetches a cookie stored by an openerp session
*
@ -785,14 +794,14 @@ openerp.base.Login = openerp.base.Controller.extend({
on_login_invalid: function() {
this.$element
.removeClass("login_valid")
.addClass("login_invalid")
.show();
.addClass("login_invalid");
this.$element.closest(".openerp").addClass("login-mode");
},
on_login_valid: function() {
this.$element
.removeClass("login_invalid")
.addClass("login_valid")
.hide();
.addClass("login_valid");
this.$element.closest(".openerp").removeClass("login-mode");
},
on_submit: function(ev) {
ev.preventDefault();
@ -818,6 +827,9 @@ openerp.base.Login = openerp.base.Controller.extend({
unique: true,
callback: continuation
});
},
on_logout: function() {
this.session.logout();
}
});
@ -830,7 +842,9 @@ openerp.base.Header = openerp.base.Controller.extend({
},
do_update: function() {
this.$element.html(QWeb.render("Header", this));
}
this.$element.find(".logout").click(this.on_logout);
},
on_logout: function() {}
});
openerp.base.Menu = openerp.base.Controller.extend({
@ -945,6 +959,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
this.header = new openerp.base.Header(this.session, "oe_header");
this.login = new openerp.base.Login(this.session, "oe_login");
this.header.on_logout.add(this.login.on_logout);
this.session.on_session_invalid.add(this.login.do_ask_login);
this.session.on_session_valid.add_last(this.header.do_update);

View File

@ -310,6 +310,7 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
}, callback);
},
unlink: function(ids) {
// to implement in children
this.notification.notify("Unlink", ids);
},
call: function (method, ids, args, callback) {
@ -343,15 +344,25 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
});
openerp.base.DataSetStatic = openerp.base.DataSet.extend({
init: function(session, model) {
init: function(session, model, ids) {
this._super(session, model);
// all local records
this.ids = [];
this.count = 0;
this.ids = ids || [];
this.count = this.ids.length;
},
read_slice: function (fields, offset, limit, callback) {
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;
},
unlink: function(ids) {
this.on_unlink(ids);
},
on_unlink: function(ids) {
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});

View File

@ -37,8 +37,12 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
},
start: function() {
//this.log('Starting FormView '+this.model+this.view_id)
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
toolbar:!!this.flags.sidebar}, this.on_loaded);
if (this.embedded_view) {
return $.Deferred().then(this.on_loaded).resolve({fields_view: this.embedded_view});
} else {
return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id,
toolbar:!!this.flags.sidebar}, this.on_loaded);
}
},
on_loaded: function(data) {
var self = this;
@ -84,9 +88,9 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.datarecord = record;
for (var f in this.fields) {
var field = this.fields[f];
field.touched = false;
field.set_value(this.datarecord[f] || false);
field.validate();
field.touched = false;
}
if (!record.id) {
// New record: Second pass in order to trigger the onchanges
@ -309,7 +313,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormV
this.notification.notify("Cancelling form");
},
do_update_sidebar: function() {
if (this.view_manager.action.flags.sidebar === false) {
if (this.flags.sidebar === false) {
return;
}
if (!this.datarecord.id) {
@ -750,6 +754,7 @@ openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
set_value: function(value) {
if (!value) {
// As in GTK client, floats default to 0
this.touched = this.view.touched = true;
value = 0;
}
this._super.apply(this, [value]);
@ -992,55 +997,67 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
}
});
openerp.base.form.FieldOne2ManyDatasSet = openerp.base.DataSetStatic.extend({
start: function() {
},
write: function (id, data, callback) {
this._super(id, data, callback);
},
unlink: function() {
this.notification.notify('Unlinking o2m ' + this.ids);
}
});
openerp.base.form.FieldOne2ManyViewManager = openerp.base.ViewManager.extend({
init: function(session, element_id, dataset, views) {
this._super(session, element_id, dataset, views);
this.action = {flags:{}};
}
});
openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
init: function(view, node) {
this._super(view, node);
this.template = "FieldOne2Many";
this.operations = [];
this.is_started = $.Deferred();
this.is_setted = $.Deferred();
},
start: function() {
this._super.apply(this, arguments);
this.log("o2m.start");
var views = [ [false,"list"], [false,"form"] ];
this.dataset = new openerp.base.form.FieldOne2ManyDatasSet(this.session, this.field.relation);
this.viewmanager = new openerp.base.form.FieldOne2ManyViewManager(this.view.session, this.element_id, this.dataset, views);
var self = this;
this.dataset = new openerp.base.DataSetStatic(this.session, this.field.relation);
this.dataset.on_unlink.add_last(function(ids) {
// TODO niv check form view
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
view.reload_content();
// TODO niv make real suppression (list or direct)
self.on_ui_change();
});
var modes = this.node.attrs.mode;
modes = !!modes ? modes.split(",") : ["tree", "form"];
var views = [];
_.each(modes, function(mode) {
var view = [false, mode == "tree" ? "list" : mode];
if (self.field.views && self.field.views[mode]) {
view.push(self.field.views[mode]);
}
views.push(view);
});
this.viewmanager = new openerp.base.ViewManager(this.view.session,
this.element_id, this.dataset, views);
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
if (view_type == "list") {
// TODO niv
} else if (view_type == "form") {
// TODO niv
}
self.is_started.resolve();
});
this.viewmanager.start();
$.when(this.is_started, this.is_setted).then(function() {
if (modes[0] == "tree") {
var view = self.viewmanager.views[self.viewmanager.active_view].controller;
view.reload_content();
}
// TODO niv: handle other types of views
});
},
set_value: function(value) {
this.value = value;
if (value != false) {
this.log("o2m.set_value",value);
this.viewmanager.dataset.ids = value;
this.viewmanager.dataset.count = value.length;
if(value != false) {
this.dataset.set_ids(value);
this.is_setted.resolve();
}
},
get_value: function(value) {
return this.operations;
},
update_dom: function() {
this._super.apply(this, arguments);
this.$element.toggleClass('disabled', this.readonly);
this.$element.toggleClass('required', this.required);
},
on_ui_change: function() {
//TODO niv
return [];
}
});
@ -1049,59 +1066,48 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
this._super(view, node);
this.template = "FieldMany2Many";
this.list_id = _.uniqueId("many2many");
this.is_started = false;
this.is_setted = false;
this.is_started = $.Deferred();
this.is_setted = $.Deferred();
},
start: function() {
this._super.apply(this, arguments);
var self = this;
this.dataset = new openerp.base.DataSetStatic(
this.session, this.field.relation);
this.dataset.on_unlink.add_last(function(ids) {
self.list_view.reload_content();
self.on_ui_change();
});
this.list_view = new openerp.base.form.Many2ManyListView(
null, this.view.session, this.list_id, this.dataset, false, {
'selectable': false,
'addable': 'Add'
});
this.list_view.groups.datagroup = (
new openerp.base.StaticDataGroup(this.dataset));
var self = this;
this.list_view.m2m_field = this;
this.list_view.start();
var hack = {loaded: false};
this.list_view.on_loaded.add_last(function() {
if (! hack.loaded) {
self.is_started = true;
self.check_load();
hack.loaded = true;
}
self.is_started.resolve();
});
$.when(this.is_started, this.is_setted).then(function() {
self.list_view.reload_content();
});
},
set_value: function(value) {
if (value != false) {
this.dataset.ids = value;
this.dataset.count = value.length;
this.is_setted = true;
this.check_load();
this.dataset.set_ids(value);
this.is_setted.resolve();
}
},
get_value: function() {
return [[6,false,this.dataset.ids]];
},
check_load: function() {
if(this.is_started && this.is_setted) {
this.list_view.reload_content();
}
}
});
openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
do_delete: function (ids) {
this.dataset.ids = _.without.apply(null, [this.dataset.ids].concat(ids));
this.dataset.count = this.dataset.ids.length;
this.reload_content();
this.m2m_field.on_ui_change();
},
do_add_record: function () {
var pop = new openerp.base.form.Many2XSelectPopup(
null, this.m2m_field.view.session);
@ -1109,8 +1115,7 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
var self = this;
pop.on_select_element.add(function(element_id) {
if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
self.dataset.ids.push(element_id);
self.dataset.count = self.dataset.ids.length;
self.dataset.set_ids([].concat(self.dataset.ids, [element_id]));
self.reload_content();
}
pop.stop();
@ -1178,7 +1183,7 @@ openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
var tmphack = {"loaded": false};
self.view_list.on_loaded.add_last(function() {
if ( !tmphack.loaded ) {
self.view_list.reload_view();
self.searchview.do_search();
tmphack.loaded = true;
};
});

View File

@ -52,9 +52,13 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
this.columns = [];
this.options = _.extend({}, this.defaults, options || {});
this.flags = this.view_manager.action.flags;
this.flags = this.view_manager.flags || {};
this.set_groups(new openerp.base.ListView.Groups(this));
if (this.dataset instanceof openerp.base.DataSetStatic) {
this.groups.datagroup = new openerp.base.StaticDataGroup(this.dataset);
}
},
/**
* Set a custom Group construct as the root of the List View.
@ -254,14 +258,19 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi
var self = this;
this.dataset.offset = 0;
this.dataset.limit = false;
return this.rpc('/base/listview/load', {
model: this.model,
view_id: this.view_id,
toolbar: !!this.flags.sidebar,
context: this.dataset.context
}, function (field_view_get) {
self.on_loaded(field_view_get, grouped);
});
var callback = function (field_view_get) {
self.on_loaded(field_view_get, grouped);
};
if (this.embedded_view) {
return $.Deferred().then(callback).resolve({fields_view: this.embedded_view});
} else {
return this.rpc('/base/listview/load', {
model: this.model,
view_id: this.view_id,
context: this.dataset.context,
toolbar: !!this.flags.sidebar
}, callback);
}
},
/**
* re-renders the content of the list view

View File

@ -3,7 +3,7 @@ openerp.base.search = function(openerp) {
openerp.base.SearchView = openerp.base.Controller.extend({
init: function(view_manager, session, element_id, dataset, view_id, defaults) {
this._super(session, element_id);
this.view_manager = view_manager;
this.view_manager = view_manager || new openerp.base.NullViewManager();
this.dataset = dataset;
this.model = dataset.model;
this.view_id = view_id;

View File

@ -67,7 +67,7 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
dialog.stop();
break;
default:
console.log(_.sprintf("Action manager can't handle action of type %s", action.type), action);
console.log("Action manager can't handle action of type " + action.type, action);
}
}
});
@ -95,7 +95,8 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
self.on_mode_switch($(this).data('view-type'));
});
_.each(this.views_src, function(view) {
self.views[view[1]] = { view_id: view[0], controller: null };
self.views[view[1]] = { view_id: view[0], controller: null,
embedded_view: view[2]};
});
if (this.flags.views_switcher === false) {
this.$element.find('.oe_vm_switch').hide();
@ -119,7 +120,14 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
// Lazy loading of views
var controllerclass = openerp.base.views.get_object(view_type);
var controller = new controllerclass( this, this.session, this.element_id + "_view_" + view_type, this.dataset, view.view_id);
if (view.embedded_view) {
controller.set_embedded_view(view.embedded_view);
}
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;
}
@ -147,6 +155,13 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
}
return view_promise;
},
/**
* Event launched when a controller has been inited.
*
* @param {String} view_type type of view
* @param {String} view the inited controller
*/
on_controller_inited: function(view_type, view) {},
/**
* Sets up the current viewmanager's search view.
*
@ -384,6 +399,16 @@ openerp.base.View = openerp.base.Controller.extend({
return dataset.exec_workflow(record_id, action_data.name, handler);
}
}
},
/**
* Directly set a view to use instead of calling fields_view_get. This method must
* be called before start(). When an embedded view is set, underlying implementations
* of openerp.base.View must use the provided view instead of any other one.
*
* @param embedded_view A view.
*/
set_embedded_view: function(embedded_view) {
this.embedded_view = embedded_view;
}
});

View File

@ -17,7 +17,6 @@
<p>#{text}</p>
</div>
</div>
<div id="oe_login" class="login"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
<tr>
<td colspan="2">
@ -34,6 +33,11 @@
</div>
</td>
</tr>
<tr>
<td valign="top" class="login-container" colspan="2">
<div id="oe_login" class="login"></div>
</td>
</tr>
<tr>
<td colspan="2">
<div id="oe_footer" class="oe_footer">
@ -48,11 +52,74 @@
</t>
<t t-name="Login">
<form>
Database: <input type="text" name="db" value="trunk"/><br/>
Login: <input type="text" name="login" value="admin"/><br/>
Password: <input type="password" name="password" value="a"/><br/>
<input type="submit" name="submit" value="Login"/>
<fieldset>
<legend style="">
<img src="/base/static/src/img/stock_person.png" alt="" />
</legend>
<div class="oe_box2">
<table align="center" cellspacing="2px" cellpadding="0">
<tr>
<td><label for="db">Database:</label></td>
<td class="oe_field_value">
<input type="text" name="db" value="trunk" autofocus="true"/>
</td>
</tr>
<tr>
<td><label for="login">User:</label></td>
<td class="oe_field_value"><input type="text" name="login" value="admin" autofocus="true"/></td>
</tr>
<tr>
<td><label for="password">Password:</label></td>
<td class="oe_field_value"><input type="password" name="password" value="a"/></td>
</tr>
<tr>
<td></td>
<td class="oe_field_value">
<button type="submit" name="submit">Login</button>
</td>
</tr>
</table>
</div>
</fieldset>
<div class="login_error_message">Bad username or password</div>
</form>
<div class="oe_login_right_pane">
<p>We think that daily job activities can be more intuitive, efficient, automated, .. and even fun.</p>
<h3>OpenERP's vision to be:</h3>
<table cellpadding="0" cellspacing="0" width="100%" style="border:none;">
<tbody>
<tr>
<td>
<img src="/base/static/src/img/product.png"/>
</td>
<td>
<strong>Full featured</strong><br />
Today's enterprise challenges are multiple. We provide one module for each need.
</td>
</tr>
<tr>
<td>
<img src="/base/static/src/img/accessories-archiver.png"/>
</td>
<td>
<strong>Open Source</strong><br />
To Build a great product, we rely on the knowledge of thousands of contributors.
</td>
</tr>
<tr>
<td>
<img src="/base/static/src/img/partner.png" />
</td>
<td>
<strong>User Friendly</strong><br />
In order to be productive, people need clean and easy to use interface.
</td>
</tr>
</tbody>
</table>
</div>
</t>
<t t-name="Header">
<a href="/" class="company_logo_link">

View File

@ -1 +1 @@
import controllers
#!/usr/bin/env python

View File

@ -1 +0,0 @@
import main

View File

@ -1,17 +0,0 @@
from base.controllers.main import View, clean_action
import openerpweb
class Dashboard(View):
_cp_path = "/base_dashboard/dashboard"
@openerpweb.jsonrequest
def load(self, req, node_attrs):
action_id = int(node_attrs['name'])
actions = req.session.model('ir.actions.actions')
result = actions.read([action_id],['type'], req.session.context)
if not result:
raise _('Action not found!')
action = req.session.model(result[0]['type']).read([action_id], False, req.session.context)[0]
clean_action(action, req.session)
return {'action': action}

View File

@ -1,36 +1,117 @@
.column {
float: left;
.openerp .oe-dashboard-links {
text-align: right;
margin: -2em 1em 1em 0;
}
.openerp .oe-dashboard-column {
float: left;
padding-bottom: 100px;
}
.portlet {
.openerp .oe-dashboard-action {
margin: 0 1em 1em 0;
}
.portlet-header {
margin: 0.3em;
padding-bottom: 4px;
padding-left: 0.2em;
.openerp .oe-dashboard-action .oe-dashboard-action-header {
margin: 0.3em;
padding-bottom: 4px;
padding-left: 0.2em
}
.portlet-header:hover {
.openerp .oe-dashboard-action .oe-dashboard-action-header:hover {
cursor: move;
}
.openerp .oe-dashboard-action .ui-icon {
cursor: pointer;
}
.openerp .oe-dashboard-action .ui-icon:hover {
background-color: #ccc;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
}
.portlet-header .ui-icon {
.openerp .oe-dashboard-action .oe-dashboard-action-header .ui-icon {
float: right;
}
.portlet-content {
.openerp .oe-dashboard-action .oe-dashboard-action-content {
padding: 0.4em;
}
.ui-sortable-placeholder {
border: 1px dotted black;
visibility: visible !important;
.openerp .oe-dashboard .ui-sortable-placeholder {
border: 1px dotted black;
visibility: visible !important;
height: 50px !important;
}
.ui-sortable-placeholder * {
.openerp .oe-dashboard .ui-sortable-placeholder * {
visibility: hidden;
}
}
/* Layouts */
.openerp .oe-dashboard-layout_1 .oe-dashboard-column {
width: 100%;
}
.openerp .oe-dashboard-layout_1 .oe-dashboard-column.index_1 .oe-dashboard-column.index_2 {
display: none;
}
.openerp .oe-dashboard-layout_1-1 .oe-dashboard-column {
width: 50%;
}
.openerp .oe-dashboard-layout_1-1 .oe-dashboard-column.index_2 {
display: none;
}
.openerp .oe-dashboard-layout_1-1-1 .oe-dashboard-column {
width: 33%;
}
.openerp .oe-dashboard-layout_2-1 .oe-dashboard-column.index_0 {
width: 70%;
}
.openerp .oe-dashboard-layout_2-1 .oe-dashboard-column.index_1 {
width: 30%;
}
.openerp .oe-dashboard-layout_2-1 .oe-dashboard-column.index_2 {
display: none;
}
.openerp .oe-dashboard-layout_1-2 .oe-dashboard-column.index_0 {
width: 30%;
}
.openerp .oe-dashboard-layout_1-2 .oe-dashboard-column.index_1 {
width: 70%;
}
.openerp .oe-dashboard-layout_1-2 .oe-dashboard-column.index_2 {
display: none;
}
.openerp .oe-dashboard-layout-selector {
overflow: auto;
padding: 10px;
}
.openerp .oe-dashboard-layout-selector ul {
margin: 0;
padding: 0;
}
.openerp .oe-dashboard-layout-selector ul li {
position: relative;
float: left;
height: 51px;
list-style-type: none;
margin: 5px;
padding: 0;
width: 82px;
cursor: pointer;
border: 1px solid white;
}
.openerp .oe-dashboard-layout-selector ul li:hover {
border: 1px solid #090;
}
.openerp .oe-dashboard-layout-selector ul li img.oe-selected-layout {
position: absolute;
top: 0px;
right: 0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

View File

@ -1,127 +1,181 @@
openerp.base_dashboard = function(openerp){
openerp.base_dashboard = function(openerp) {
QWeb.add_template('/base_dashboard/static/src/xml/base_dashboard.xml');
openerp.base.form.Board = openerp.base.form.Widget.extend({
openerp.base.form.DashBoard = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
this.template = "Board";
this.template = "DashBoard";
},
start: function() {
var self = this;
this._super.apply(this, arguments);
this.$element.html(QWeb.render(this.template));
var $dashboard = this.$element.find('#dashboard');
var children = this.node.children;
for(var ch=0; ch < children.length; ch++) {
var node = children[ch];
var widget;
if(node.tag.indexOf('child') >= 0) {
widget = new (openerp.base.form.widgets.get_object('child')) (this.view, node, $dashboard);
} else {
//Vpaned
widget = new (openerp.base.form.widgets.get_object(node.tag)) (this.view, node, $dashboard);
}
widget.start();
}
jQuery('.column').css('width', 100/children.length+'%');
},
});
openerp.base.form.Dashbar = openerp.base.form.Widget.extend({
init: function(view, node, dashboard) {
this._super(view, node, dashboard);
this.dashboard = dashboard;
this.template = 'Portlet'
},
start: function() {
var $dashboard = this.dashboard;
var children = this.node.children;
$dashboard.append(QWeb.render(this.template, {widget: this, 'children': children}))
for(var chld=0; chld < children.length;chld++) {
var child = children[chld];
var widget = new (openerp.base.form.widgets.get_object(child.tag)) (this.view, child);
widget.start()
}
$( ".column" ).sortable({
connectWith: ".column",
this.$element.find(".oe-dashboard-column").sortable({
connectWith: ".oe-dashboard-column",
scroll: false
}).disableSelection().bind('sortstop', self.do_save_dashboard);
// Events
this.$element.find('.oe-dashboard-link-undo').click(this.on_undo);
this.$element.find('.oe-dashboard-link-reset').click(this.on_reset);
this.$element.find('.oe-dashboard-link-add_widget').click(this.on_add_widget);
this.$element.find('.oe-dashboard-link-change_layout').click(this.on_change_layout);
this.$element.find('.oe-dashboard-column .ui-icon-minusthick').click(this.on_fold_action);
this.$element.find('.oe-dashboard-column .ui-icon-closethick').click(this.on_close_action);
this.actions_attrs = {};
// Init actions
_.each(this.node.children, function(column) {
_.each(column.children, function(action) {
delete(action.attrs.width);
delete(action.attrs.height);
delete(action.attrs.colspan);
self.actions_attrs[action.attrs.name] = action.attrs;
self.rpc('/base/action/load', {
action_id: parseInt(action.attrs.name, 10)
}, self.on_load_action);
});
});
$( ".portlet" ).addClass( "ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" )
.find( ".portlet-header" )
.addClass( "ui-widget-header ui-corner-all" )
.end()
.find( ".portlet-content" );
$( ".portlet-header .ui-icon" ).click(function() {
$( this ).toggleClass( "ui-icon-minusthick" ).toggleClass( "ui-icon-plusthick" );
$( this ).parents( ".portlet:first" ).find( ".portlet-content" ).toggle();
});
$( ".column" ).disableSelection();
}
})
openerp.base.form.Action = openerp.base.form.Widget.extend({
init: function(view, node) {
this._super(view, node);
},
start: function() {
this._super.apply(this, arguments);
this.rpc('/base_dashboard/dashboard/load',{
node_attrs: this.node.attrs
},
this.on_load_action);
on_undo: function() {
this.rpc('/base/view/undo_custom', {
view_id: this.view.fields_view.view_id
}, this.do_reload);
},
on_reset: function() {
this.rpc('/base/view/undo_custom', {
view_id: this.view.fields_view.view_id,
reset: true
}, this.do_reload);
},
on_add_widget: function() {
},
on_change_layout: function() {
var self = this;
var qdict = {
current_layout : this.$element.find('.oe-dashboard').attr('data-layout')
};
var $dialog = $('<div>').dialog({
modal: true,
title: 'Edit Layout',
width: 'auto',
height: 'auto',
}).html(QWeb.render('DashBoard.layouts', qdict));
$dialog.find('li').click(function() {
var layout = $(this).attr('data-layout');
$dialog.dialog("destroy");
self.do_change_layout(layout);
});
},
do_change_layout: function(new_layout) {
var $dashboard = this.$element.find('.oe-dashboard');
var current_layout = $dashboard.attr('data-layout');
if (current_layout != new_layout) {
var clayout = current_layout.split('-').length,
nlayout = new_layout.split('-').length,
column_diff = clayout - nlayout;
if (column_diff > 0) {
var $last_column = $();
$dashboard.find('.oe-dashboard-column').each(function(k, v) {
if (k >= nlayout) {
$(v).find('.oe-dashboard-action').appendTo($last_column);
} else {
$last_column = $(v);
}
});
}
$dashboard.toggleClass('oe-dashboard-layout_' + current_layout + ' oe-dashboard-layout_' + new_layout);
$dashboard.attr('data-layout', new_layout);
this.do_save_dashboard();
}
},
on_fold_action: function(e) {
var $e = $(e.currentTarget);
$e.toggleClass('ui-icon-minusthick ui-icon-plusthick');
$e.parents('.oe-dashboard-action:first').find('.oe-dashboard-action-content').toggle();
},
on_close_action: function(e) {
$(e.currentTarget).parents('.oe-dashboard-action:first').remove();
this.do_save_dashboard();
},
do_save_dashboard: function() {
var self = this;
var board = {
form_title : this.view.fields_view.arch.attrs.string,
style : this.$element.find('.oe-dashboard').attr('data-layout'),
columns : []
};
this.$element.find('.oe-dashboard-column').each(function() {
var actions = [];
$(this).find('.oe-dashboard-action').each(function() {
var action_id = $(this).attr('data-id');
actions.push(self.actions_attrs[action_id]);
});
board.columns.push(actions);
});
var arch = QWeb.render('DashBoard.xml', board);
this.rpc('/base/view/add_custom', {
view_id: this.view.fields_view.view_id,
arch: arch
}, function() {
self.$element.find('.oe-dashboard-link-undo, .oe-dashboard-link-reset').show();
});
},
on_load_action: function(result) {
var action = result.action;
var action = result.result;
action.flags = {
search_view : false,
sidebar : false,
views_switcher : false,
action_buttons : false
action_buttons : false,
pager: false
}
var node_attrs = this.node.attrs;
var content_id = 'portlet-content-'+node_attrs.name;
var action_manager = new openerp.base.ActionManager(this.session, content_id);
action_manager.start();
action_manager.do_action(action);
var element_id = this.view.element_id + '_action_' + action.id;
var view = new openerp.base.ViewManagerAction(this.session, element_id, action);
view.start();
},
render: function() {
// We should start with three columns available
for (var i = this.node.children.length; i < 3; i++) {
this.node.children.push({
tag: 'column',
attrs: {},
children: []
});
}
return QWeb.render(this.template, this);
},
do_reload: function() {
this.view.view_manager.stop();
this.view.view_manager.start();
}
})
openerp.base.form.Vpaned = openerp.base.form.Widget.extend({
init: function(view, node, board, child_index) {
this._super(view, node, board, child_index);
this.board = board;
this.child_index = child_index;
},
start: function() {
this._super.apply(this, arguments);
var children = this.node.children;
for(var chld=0; chld<children.length; chld++) {
var ch_widget = children[chld].children;
for(var ch=0; ch<ch_widget.length; ch++) {
var widget_type = ch_widget[ch].tag;
var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, ch_widget[ch], this.board, this.child_index);
widget.start();
}
});
openerp.base.form.DashBoardLegacy = openerp.base.form.DashBoard.extend({
render: function() {
if (this.node.tag == 'hpaned') {
this.node.attrs.style = '1-1';
} else if (this.node.tag == 'vpaned') {
this.node.attrs.style = '1';
}
},
})
this.node.tag = 'board';
_.each(this.node.children, function(child) {
if (child.tag.indexOf('child') == 0) {
child.tag = 'column';
var actions = [], first_child = child.children[0];
if (first_child && first_child.tag == 'vpaned') {
_.each(first_child.children, function(subchild) {
actions.push.apply(actions, subchild.children);
});
child.children = actions;
}
}
});
return this._super(this, arguments);
}
});
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
openerp.base.form.widgets.add('child', 'openerp.base.form.Dashbar');
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.Vpaned');
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.DashBoardLegacy');
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.DashBoardLegacy');
openerp.base.form.widgets.add('board', 'openerp.base.form.DashBoard');
}

View File

@ -1,17 +1,61 @@
<template>
<t t-name="Board">
<div id="dashboard" class="demo">
</div>
</t>
<t t-name="Portlet">
<div t-att-id="widget.node.tag" class="column">
<div class="portlet" t-foreach="children" t-as="child" t-att-id="child.attrs.name">
<div class="portlet-header">
<t t-esc="child.attrs.string"/>
<span class="ui-icon ui-icon-minusthick"></span>
</div>
<div class="portlet-content" t-att-id="'portlet-content-'+child.attrs.name"></div>
<t t-name="DashBoard">
<div class="oe-dashboard-links">
<button type="button" class="button oe-dashboard-link-reset" t-att-style="view.fields_view.custom_view_id ? null : 'display: none'">
<img src="/base/static/src/img/icons/STOCK_GOTO_FIRST.png" width="16" height="16"/>
<span>Reset</span>
</button>
<button type="button" class="button oe-dashboard-link-undo" t-att-style="view.fields_view.custom_view_id ? null : 'display: none'">
<img src="/base/static/src/img/icons/gtk-undo.png" width="16" height="16"/>
<span>Undo</span>
</button>
<button type="button" class="button oe-dashboard-link-add_widget">
<img src="/base/static/src/img/icons/STOCK_ADD.png" width="16" height="16"/>
<span>Add Widget</span>
</button>
<button type="button" class="button oe-dashboard-link-change_layout">
<img src="/base/static/src/img/icons/gtk-edit.png" width="16" height="16"/>
<span>Change layout</span>
</button>
</div>
<div t-att-data-layout="node.attrs.style" t-attf-class="oe-dashboard oe-dashboard-layout_#{node.attrs.style}">
<div t-foreach="node.children" t-as="column" t-if="column.tag == 'column'"
t-att-id="view.element_id + '_column_' + column_index" t-attf-class="oe-dashboard-column index_#{column_index}">
<div t-foreach="column.children" t-as="action" t-if="action.tag == 'action'" t-att-data-id="action.attrs.name"
class="oe-dashboard-action ui-widget ui-widget-content ui-helper-clearfix ui-corner-all">
<div class="oe-dashboard-action-header ui-widget-header ui-corner-all">
<t t-esc="action.attrs.string"/>
<span class='ui-icon ui-icon-closethick'></span>
<span class='ui-icon ui-icon-minusthick'></span>
</div>
<div t-att-id="view.element_id + '_action_' + action.attrs.name" class="oe-dashboard-action-content"></div>
</div>
</t>
</template>
</div>
</div>
</t>
<t t-name="DashBoard.layouts">
<div class="oe-dashboard-layout-selector">
<p>
<strong>Choose dashboard layout</strong>
</p>
<ul>
<li t-foreach="'1 1-1 1-1-1 1-2 2-1'.split(' ')" t-as="layout" t-att-data-layout="layout">
<img t-attf-src="/base_dashboard/static/src/img/layout_#{layout}.png"/>
<img t-if="layout == current_layout"
src="/base/static/src/img/icons/gtk-apply.png" width="16" height="16" class="oe-selected-layout"/>
</li>
</ul>
</div>
</t>
<t t-name="DashBoard.xml">
<form t-att-string="form_title">
<board t-att-style="style">
<column t-foreach="columns" t-as="column">
<action t-foreach="column" t-as="action" t-att="action"/>
</column>
</board>
</form>
</t>
</template>