[Merge] Merge from openerp-web.

bzr revid: jra@tinyerp.com-20111014051952-a5emayrxco8oztyy
This commit is contained in:
Jiten (OpenERP) 2011-10-14 10:49:52 +05:30
commit 4c15f26980
19 changed files with 580 additions and 310 deletions

View File

@ -227,7 +227,7 @@ class HttpRequest(WebRequest):
_logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw)
r = method(controller, self, **self.params)
if self.debug or 1:
if isinstance(r, werkzeug.wrappers.BaseResponse):
if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
_logger.debug('<-- %s', r)
else:
_logger.debug("<-- size: %s", len(r))
@ -395,7 +395,8 @@ class Root(object):
else:
response = result
response.set_cookie(self.session_cookie, session.sid)
if hasattr(response, 'set_cookie'):
response.set_cookie(self.session_cookie, session.sid)
return response(environ, start_response)

View File

@ -5,15 +5,16 @@ openerp.web.chrome = function(openerp) {
var QWeb = openerp.web.qweb;
openerp.web.Notification = openerp.web.Widget.extend(/** @lends openerp.web.Notification# */{
/**
* @constructs openerp.web.Notification
* @extends openerp.web.Widget
*
* @param parent
* @param element_id
*/
init: function(parent, element_id) {
this._super(parent, element_id);
template: 'Notification',
identifier_prefix: 'notification-',
init: function() {
this._super.apply(this, arguments);
openerp.notification = this;
},
start: function() {
this._super.apply(this, arguments);
this.$element.notify({
speed: 500,
expires: 1500
@ -28,9 +29,12 @@ openerp.web.Notification = openerp.web.Widget.extend(/** @lends openerp.web.Not
warn: function(title, text) {
this.$element.notify('create', 'oe_notification_alert', {
title: title,
text: text
text: text,
}, {
expires: false,
});
}
},
});
openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{
@ -369,7 +373,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
}
$db_list.find(':selected').remove();
self.db_list.splice(_.indexOf(self.db_list, db, true), 1);
self.notification.notify("Dropping database", "The database '" + db + "' has been dropped");
self.do_notify("Dropping database", "The database '" + db + "' has been dropped");
});
}
});
@ -454,7 +458,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database
self.display_error(result);
return;
}
self.notification.notify("Changed Password", "Password has been changed successfully");
self.do_notify("Changed Password", "Password has been changed successfully");
});
}
});
@ -487,6 +491,15 @@ openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{
this.selected_password = localStorage.getItem('last_password_login_success');
}
}
var qs = jQuery.deparam(jQuery.param.querystring());
if (qs.db) {
this.selected_db = qs.db;
}
if (qs.login) {
this.selected_login = qs.login;
}
},
start: function() {
var self = this;
@ -779,9 +792,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
},
start: function() {
this.$secondary_menu.addClass(this.folded ? 'oe_folded' : 'oe_unfolded');
this.reload();
},
reload: function() {
do_reload: function() {
this.rpc("/web/menu/load", {}, this.on_loaded);
},
on_loaded: function(data) {
@ -823,7 +835,9 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
this.session.active_id = id;
this.rpc('/web/menu/action', {'menu_id': id}, this.on_menu_action_loaded);
}
ev.stopPropagation();
if (ev) {
ev.stopPropagation();
}
return false;
},
do_menu_click: function($clicked_menu, manual) {
@ -866,6 +880,7 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
$sub_menu.hide();
return true;
}
return manual;
} else {
return true;
}
@ -927,14 +942,12 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
}
this.$element.html(QWeb.render("Interface", params));
this.notification = new openerp.web.Notification();
this.session = new openerp.web.Session();
this.loading = new openerp.web.Loading(this,"oe_loading");
this.crashmanager = new openerp.web.CrashManager(this);
this.crashmanager.start();
// Do you autorize this ? will be replaced by notify() in controller
openerp.web.Widget.prototype.notification = new openerp.web.Notification(this, "oe_notification");
this.header = new openerp.web.Header(this);
this.login = new openerp.web.Login(this);
this.header.on_logout.add(this.login.on_logout);
@ -954,12 +967,27 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
},
start: function() {
this._super.apply(this, arguments);
this.notification.prependTo(this.$element);
this.header.appendTo($("#oe_header"));
this.session.start();
this.login.appendTo($('#oe_login'));
this.menu.start();
},
do_reload: function() {
this.session.session_restore();
this.menu.do_reload();
},
do_notify: function() {
var n = this.notification;
n.notify.apply(n, arguments);
},
do_warn: function() {
var n = this.notification;
n.warn.apply(n, arguments);
},
on_logged: function() {
this.menu.do_reload();
if(this.action_manager)
this.action_manager.stop();
this.action_manager = new openerp.web.ActionManager(this);
@ -1046,7 +1074,8 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie
this.action_manager.do_action(action);
},
do_about: function() {
}
},
});
};

View File

@ -74,7 +74,6 @@ openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1);
}
return this;
}
// This should NOT be used, like callbackenable it's too hackish not enough javasish
Class.include = function (properties) {
for (var name in properties) {
if (typeof properties[name] !== 'function'
@ -288,14 +287,12 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
try {
return this.get_object(keys[i]);
} catch (e) {
if (e instanceof openerp.web.KeyNotFound) {
continue;
}
throw e;
var key = keys[i];
if (key === undefined || !(key in this.map)) {
continue;
}
return this.get_object(key);
}
throw new openerp.web.KeyNotFound(keys.join(','));
},

View File

@ -610,44 +610,120 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({
on_default_get: function(res) {
this.last_default_get = res;
},
/**
* Makes sure this dataset has the fields_get for its model stored locally,
* so create/write methods are able to determine if written fields are m2os
* and can name_get those fields if that's the case (in order to cache a
* correct m2o value for them)
*
* @returns {$.Deferred} whether the fields_get is done executing, provides the fields_get value to its callbacks
*/
ensure_has_fields: function () {
var self = this;
if (this.fields_get) {
var d = $.Deferred();
setTimeout(function () { d.resolve(self.fields_get, true); }, 0);
return d.promise();
} else {
return self.call('fields_get', [], function (fields_get) {
self.fields_get = fields_get;
});
}
},
/**
* Relational fields may not be written in the same format they are read.
*
* Since the BufferedDataSet is a bit dumb, it returns what it's got stored
* in its cache, which for modified value is what it was given. This breaks
* some basic contracts of openerp such as m2o read being in the
* ``name_get`` format.
*
* Because create & write are supposed to be asynchronous, though, it
* should be easy to just name_get all incorrect values.
*
* @param {Object} data form-data to write to the cache
* @returns {$.Deferred} resolved with a fixed data dictionary
*/
fix_relational: function (data) {
var self = this;
var results = $.Deferred();
this.ensure_has_fields().then(function (fields) {
var fields_to_fix = _(fields).chain()
.map(function (d, k) { return {key: k, descriptor: d}; })
.filter(function (field) {
// keep m2o fields which are in the data dict
return field.descriptor.type === 'many2one' &&
data[field.key];
}).pluck('key')
.value();
var name_gets = _(fields_to_fix).map(function (field) {
return new openerp.web.DataSet(self, self.fields_get[field].relation)
.name_get([data[field]], null);
});
// null-concat forces stupid bastard `when` to always return an
// array containing the Array-ified results of all the name_gets.
// otherwise, if there's a single field to fix it returns the
// results of the unique name_get directly.
$.when.apply(null, name_gets.concat([null])).then(function () {
var record = _.extend({}, data);
for(var i=0; i<fields_to_fix.length; ++i) {
// Each argument is [[name_get], success, jqXhr] and we
// want just the name_get pair
record[fields_to_fix[i]] = arguments[i][0][0];
}
results.resolve(record);
});
});
return results;
},
create: function(data, callback, error_callback) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
this.on_change();
var self = this;
var prom = $.Deferred().then(callback);
prom.resolve({result: cached.id});
this.fix_relational(data).then(function (fixed_m2o_data) {
var cached = {
id:_.uniqueId(self.virtual_id_prefix),
values: fixed_m2o_data,
defaults: self.last_default_get
};
self.to_create.push(_.extend(_.clone(cached), {
values: _.clone(data)}));
self.cache.push(cached);
self.on_change();
prom.resolve({result: cached.id});
});
return prom.promise();
},
write: function (id, data, options, callback) {
var self = this;
var record = _.detect(this.to_create, function(x) {return x.id === id;});
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
var dirty = false;
if (record) {
for (var k in data) {
if (record.values[k] === undefined || record.values[k] !== data[k]) {
dirty = true;
break;
}
}
$.extend(record.values, data);
} else {
dirty = true;
record = {id: id, values: data};
self.to_write.push(record);
}
var cached = _.detect(this.cache, function(x) {return x.id === id;});
if (!cached) {
cached = {id: id, values: {}};
this.cache.push(cached);
}
$.extend(cached.values, record.values);
if (dirty)
this.on_change();
var to_return = $.Deferred().then(callback);
to_return.resolve({result: true});
this.fix_relational(data).then(function (fixed_m2o_data) {
var record = _.detect(self.to_create, function(x) {return x.id === id;});
record = record || _.detect(self.to_write, function(x) {return x.id === id;});
var dirty = false;
if (record) {
for (var k in data) {
if (record.values[k] === undefined || record.values[k] !== data[k]) {
dirty = true;
break;
}
}
_.extend(record.values, data);
} else {
dirty = true;
record = {id: id, values: data};
self.to_write.push(record);
}
var cached = _.detect(self.cache, function(x) {return x.id === id;});
if (!cached) {
cached = {id: id, values: {}};
this.cache.push(cached);
}
_.extend(cached.values, fixed_m2o_data);
if (dirty)
self.on_change();
to_return.resolve({result: true});
});
return to_return.promise();
},
unlink: function(ids, callback, error_callback) {

View File

@ -69,6 +69,13 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) {
} catch (e) {
return value.format("%H:%M:%S");
}
case 'selection':
// Each choice is [value, label]
var result = _(descriptor.selection).detect(function (choice) {
return choice[0] === value;
});
if (result) { return result[1]; }
return;
default:
return value;
}

View File

@ -336,7 +336,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search
* @param {Array} errors a never-empty array of error objects
*/
on_invalid: function (errors) {
this.notification.notify("Invalid Search", "triggered from search view");
this.do_notify("Invalid Search", "triggered from search view");
},
do_clear: function () {
this.$element.find('.filter_label, .filter_icon').removeClass('enabled');

View File

@ -33,7 +33,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.widgets_counter = 0;
this.fields = {};
this.datarecord = {};
this.ready = false;
this.show_invalid = true;
this.dirty_for_user = false;
this.default_focus_field = null;
@ -44,7 +43,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.translatable_fields = [];
_.defaults(this.options, {"always_show_new_button": true,
"not_interactible_on_create": false});
this.save_lock = $.Deferred().resolve();
this.mutating_lock = $.Deferred();
this.initial_mutating_lock = this.mutating_lock;
this.on_change_lock = $.Deferred().resolve();
this.reload_lock = $.Deferred().resolve();
},
start: function() {
this._super();
@ -120,7 +122,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.do_unfold();
this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this.sidebar.add_section('attachments', "Attachments"), this);
this.sidebar.attachments = new openerp.web.form.SidebarAttachments(this.sidebar, this);
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
}
@ -181,7 +183,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
}
this.on_form_changed();
this.show_invalid = this.ready = true;
this.initial_mutating_lock.resolve();
this.show_invalid = true;
this.do_update_pager(record.id == null);
if (this.sidebar) {
this.sidebar.attachments.do_update();
@ -224,69 +227,78 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
$pager.find('span.oe_pager_count').html(this.dataset.ids.length);
},
do_onchange: function(widget, processed) {
processed = processed || [];
if (widget.node.attrs.on_change) {
var self = this;
this.ready = false;
var onchange = _.trim(widget.node.attrs.on_change);
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
if (call) {
var method = call[1], args = [];
var context_index = null;
var argument_replacement = {
'False' : function() {return false;},
'True' : function() {return true;},
'None' : function() {return null;},
'context': function(i) {
context_index = i;
var ctx = widget.build_context ? widget.build_context() : {};
return ctx;
}
};
var parent_fields = null;
_.each(call[2].split(','), function(a, i) {
var field = _.trim(a);
if (field in argument_replacement) {
args.push(argument_replacement[field](i));
return;
} else if (self.fields[field]) {
var value = self.fields[field].get_on_change_value();
args.push(value == null ? false : value);
return;
} else {
var splitted = field.split('.');
if (splitted.length > 1 && _.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
if (parent_fields === null) {
parent_fields = self.dataset.parent_view.get_fields_values();
}
var p_val = parent_fields[_.trim(splitted[1])];
if (p_val !== undefined) {
args.push(p_val == null ? false : p_val);
return;
var self = this;
var act = function() {
try {
processed = processed || [];
if (widget.node.attrs.on_change) {
var onchange = _.trim(widget.node.attrs.on_change);
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
if (call) {
var method = call[1], args = [];
var context_index = null;
var argument_replacement = {
'False' : function() {return false;},
'True' : function() {return true;},
'None' : function() {return null;},
'context': function(i) {
context_index = i;
var ctx = widget.build_context ? widget.build_context() : {};
return ctx;
}
};
var parent_fields = null;
_.each(call[2].split(','), function(a, i) {
var field = _.trim(a);
if (field in argument_replacement) {
args.push(argument_replacement[field](i));
return;
} else if (self.fields[field]) {
var value = self.fields[field].get_on_change_value();
args.push(value == null ? false : value);
return;
} else {
var splitted = field.split('.');
if (splitted.length > 1 && _.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
if (parent_fields === null) {
parent_fields = self.dataset.parent_view.get_fields_values();
}
var p_val = parent_fields[_.trim(splitted[1])];
if (p_val !== undefined) {
args.push(p_val == null ? false : p_val);
return;
}
}
}
}
throw "Could not get field with name '" + field +
"' for onchange '" + onchange + "'";
});
var ajax = {
url: '/web/dataset/call',
async: false
};
return this.rpc(ajax, {
model: this.dataset.model,
method: method,
args: [(this.datarecord.id == null ? [] : [this.datarecord.id])].concat(args),
context_id: context_index === null ? null : context_index + 1
}, function(response) {
self.on_processed_onchange(response, processed);
});
} else {
console.log("Wrong on_change format", on_change);
throw "Could not get field with name '" + field +
"' for onchange '" + onchange + "'";
});
var ajax = {
url: '/web/dataset/call',
async: false
};
return self.rpc(ajax, {
model: self.dataset.model,
method: method,
args: [(self.datarecord.id == null ? [] : [self.datarecord.id])].concat(args),
context_id: context_index === null ? null : context_index + 1
}).pipe(function(response) {
return self.on_processed_onchange(response, processed);
});
} else {
console.log("Wrong on_change format", on_change);
}
}
}
} catch(e) {
console.error(e);
return $.Deferred().reject();
}
};
this.on_change_lock = this.on_change_lock.pipe(act, act);
return this.on_change_lock;
},
on_processed_onchange: function(response, processed) {
try {
var result = response;
if (result.value) {
for (var f in result.value) {
@ -319,7 +331,11 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
if (result.domain) {
// TODO:
}
this.ready = true;
return $.Deferred().resolve();
} catch(e) {
console.error(e);
return $.Deferred().reject();
}
},
on_button_new: function() {
var self = this;
@ -367,9 +383,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
do_save: function(success, prepend_on_create) {
var self = this;
var action = function() {
if (!self.ready) {
return $.Deferred().reject();
}
try {
if (!self.initial_mutating_lock.isResolved() && !self.initial_mutating_lock.isRejected())
return;
var form_dirty = false,
form_invalid = false,
values = {},
@ -403,8 +419,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}).then(success);
}
}
} catch (e) {
console.error(e);
return $.Deferred().reject();
}
};
this.save_lock = this.save_lock.pipe(action, action);
this.mutating_lock = this.mutating_lock.pipe(action, action);
return this.mutating_lock;
},
do_save_edit: function() {
this.do_save();
@ -422,14 +443,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
}
});
msg += "</ul>";
this.notification.warn("The following fields are invalid :", msg);
this.do_warn("The following fields are invalid :", msg);
},
on_saved: function(r, success) {
if (!r.result) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
return this.reload().then(success);
this.reload();
return $.when(r).then(success);
}
},
/**
@ -463,9 +485,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
this.sidebar.attachments.do_update();
}
console.debug("The record has been created with id #" + this.datarecord.id);
return this.reload().pipe(function() {
return _.extend(r, {created: true});
}).then(success);
this.reload();
return $.when(_.extend(r, {created: true})).then(success);
}
},
on_action: function (action) {
@ -475,11 +496,16 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView#
console.debug("Cancelling form");
},
reload: function() {
if (this.dataset.index == null || this.dataset.index < 0) {
return $.when(this.on_button_new());
} else {
return this.dataset.read_index(_.keys(this.fields_view.fields), this.on_record_loaded);
}
var self = this;
var act = function() {
if (self.dataset.index == null || self.dataset.index < 0) {
return $.when(self.on_button_new());
} else {
return self.dataset.read_index(_.keys(self.fields_view.fields), self.on_record_loaded);
}
};
this.reload_lock = this.reload_lock.pipe(act, act);
return this.reload_lock;
},
get_fields_values: function() {
var values = {};
@ -545,8 +571,12 @@ openerp.web.FormDialog = openerp.web.Dialog.extend({
openerp.web.form = {};
openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({
init: function(parent, element_id, form_view) {
this._super(parent, element_id);
init: function(parent, form_view) {
var $section = parent.add_section(_t('Attachments'), 'attachments');
this.$div = $('<div class="oe-sidebar-attachments"></div>');
$section.append(this.$div);
this._super(parent, $section.attr('id'));
this.view = form_view;
},
do_update: function() {
@ -564,7 +594,7 @@ openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({
},
on_attachments_loaded: function(attachments) {
this.attachments = attachments;
this.$element.html(QWeb.render('FormView.sidebar.attachments', this));
this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
},
@ -586,7 +616,7 @@ openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({
ids: [parseInt($e.attr('data-id'))]
}, function(r) {
$e.parent().remove();
self.notification.notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
});
}
}
@ -705,6 +735,51 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.
render: function() {
var template = this.template;
return QWeb.render(template, { "widget": this });
},
_build_view_fields_values: function() {
var a_dataset = this.view.dataset;
var fields_values = this.view.get_fields_values();
var parent_values = a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {};
fields_values.parent = parent_values;
return fields_values;
},
_build_eval_context: function() {
var a_dataset = this.view.dataset;
return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
},
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
*/
build_context: function() {
var f_context = (this.field || {}).context || {};
if (!!f_context.__ref) {
var fields_values = this._build_eval_context();
f_context = new openerp.web.CompoundDomain(f_context).set_eval_context(fields_values);
}
// maybe the default_get should only be used when we do a default_get?
var v_contexts = _.compact([this.node.attrs.default_get || null,
this.node.attrs.context || null]);
var v_context = new openerp.web.CompoundContext();
_.each(v_contexts, function(x) {v_context.add(x);});
if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
var fields_values = this._build_eval_context();
v_context.set_eval_context(fields_values);
}
// if there is a context on the node, overrides the model's context
var ctx = v_contexts.length > 0 ? v_context : f_context;
return ctx;
},
build_domain: function() {
var f_domain = this.field.domain || [];
var n_domain = this.node.attrs.domain || null;
// if there is a domain on the node, overrides the model's domain
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array)) {
var fields_values = this._build_eval_context();
final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
}
});
@ -918,7 +993,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
return self.on_confirmed();
}
};
if (!this.node.attrs.special && (this.view.dirty_for_user || !this.view.datarecord.id)) {
if (!this.node.attrs.special) {
return this.view.recursive_save().pipe(exec_action);
} else {
return exec_action();
@ -926,9 +1001,16 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
},
on_confirmed: function() {
var self = this;
var context = this.node.attrs.context;
if (context && context.__ref) {
context = new openerp.web.CompoundContext(context);
context.set_eval_context(this._build_eval_context());
}
return this.view.do_execute_action(
this.node.attrs, this.view.dataset, this.view.datarecord.id, function () {
_.extend({}, this.node.attrs, {context: context}),
this.view.dataset, this.view.datarecord.id, function () {
self.view.reload();
});
},
@ -1075,51 +1157,6 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f
this.invalid = false;
},
focus: function() {
},
_build_view_fields_values: function() {
var a_dataset = this.view.dataset;
var fields_values = this.view.get_fields_values();
var parent_values = a_dataset.parent_view ? a_dataset.parent_view.get_fields_values() : {};
fields_values.parent = parent_values;
return fields_values;
},
_build_eval_context: function() {
var a_dataset = this.view.dataset;
return new openerp.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values());
},
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
*/
build_context: function() {
var f_context = this.field.context || {};
if (!!f_context.__ref) {
var fields_values = this._build_eval_context();
f_context = new openerp.web.CompoundDomain(f_context).set_eval_context(fields_values);
}
// maybe the default_get should only be used when we do a default_get?
var v_contexts = _.compact([this.node.attrs.default_get || null,
this.node.attrs.context || null]);
var v_context = new openerp.web.CompoundContext();
_.each(v_contexts, function(x) {v_context.add(x);});
if (_.detect(v_contexts, function(x) {return !!x.__ref;})) {
var fields_values = this._build_eval_context();
v_context.set_eval_context(fields_values);
}
// if there is a context on the node, overrides the model's context
var ctx = v_contexts.length > 0 ? v_context : f_context;
return ctx;
},
build_domain: function() {
var f_domain = this.field.domain || [];
var n_domain = this.node.attrs.domain || null;
// if there is a domain on the node, overrides the model's domain
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array)) {
var fields_values = this._build_eval_context();
final_domain = new openerp.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
}
});
@ -1165,7 +1202,7 @@ openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
},
on_button_clicked: function() {
if (!this.value || !this.is_valid()) {
this.notification.warn("E-mail error", "Can't send email to invalid e-mail address");
this.do_warn("E-mail error", "Can't send email to invalid e-mail address");
} else {
location.href = 'mailto:' + this.value;
}
@ -1180,7 +1217,7 @@ openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
},
on_button_clicked: function() {
if (!this.value) {
this.notification.warn("Resource error", "This resource is empty");
this.do_warn("Resource error", "This resource is empty");
} else {
window.open(this.value);
}
@ -1857,6 +1894,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
this._super(view, node);
this.is_started = $.Deferred();
this.form_last_update = $.Deferred();
this.init_form_last_update = this.form_last_update;
this.disable_utility_classes = true;
},
start: function() {
@ -1898,7 +1936,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
form: 'openerp.web.form.One2ManyFormView'
});
var once = $.Deferred().then(function() {
self.form_last_update.resolve();
self.init_form_last_update.resolve();
});
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
if (view_type == "list") {
@ -1932,9 +1970,10 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
if (this.dataset.index === null && this.dataset.ids.length >= 1) {
this.dataset.index = 0;
}
this.form_last_update.then(function() {
this.form_last_update = view.do_show();
});
var act = function() {
return view.do_show();
}
this.form_last_update = this.form_last_update.pipe(act, act);;
} else if (self.viewmanager.active_view === "graph") {
view.do_search(this.build_domain(), this.dataset.get_context(), []);
}
@ -2025,9 +2064,10 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") {
var res = $.when(view.do_save());
if (!res.isResolved() && !res.isRejected()) {
throw "Asynchronous get_value() is not supported in form view.";
}
// it seems line there are some cases when this happens
/*if (!res.isResolved() && !res.isRejected()) {
console.warn("Asynchronous get_value() is not supported in form view.");
}*/
return res;
}
}
@ -2039,6 +2079,8 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
},
validate: function() {
this.invalid = false;
if (!this.viewmanager.views[this.viewmanager.active_view])
return;
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
if (this.viewmanager.active_view === "form") {
for (var f in view.fields) {
@ -2362,13 +2404,17 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new");
$nbutton.click(function() {
$.when(self.view_form.do_save()).then(function() {
self.view_form.on_button_new();
self.view_form.reload_lock.then(function() {
self.view_form.on_button_new();
});
});
});
var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save");
$nbutton.click(function() {
$.when(self.view_form.do_save()).then(function() {
self.check_exit();
self.view_form.reload_lock.then(function() {
self.check_exit();
});
});
});
var $cbutton = $buttons.find(".oe_selectcreatepopup-form-close");
@ -2437,7 +2483,6 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
this.setup_form_view();
},
on_write: function(id, data) {
this.stop();
if (!this.options.auto_write)
return;
var self = this;
@ -2460,7 +2505,9 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp
$buttons.html(QWeb.render("FormOpenPopup.form.buttons"));
var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
$nbutton.click(function() {
self.view_form.do_save();
self.view_form.do_save().then(function() {
self.stop();
});
});
var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
$cbutton.click(function() {
@ -2587,7 +2634,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
on_file_uploaded: function(size, name, content_type, file_base64) {
delete(window[this.iframe]);
if (size === false) {
this.notification.warn("File Upload", "There was a problem while uploading your file");
this.do_warn("File Upload", "There was a problem while uploading your file");
// TODO: use openerp web crashmanager
console.warn("Error while uploading file : ", name);
} else {
@ -2601,7 +2648,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
},
on_save_as: function() {
if (!this.view.datarecord.id) {
this.notification.warn("Can't save file", "The record has not yet been saved");
this.do_warn("Can't save file", "The record has not yet been saved");
} else {
var url = '/web/binary/saveas?session_id=' + this.session.session_id + '&model=' +
this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name +

View File

@ -194,7 +194,7 @@ openerp.web.list_editable = function (openerp) {
}
self.edition = true;
self.edition_id = record_id;
self.edition_form = _.extend(new openerp.web.ListEditableFormView(self, self.dataset, false), {
self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), {
form_template: 'ListView.row.form',
registry: openerp.web.list.form.widgets,
$element: $new_row

View File

@ -110,7 +110,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
}
});
if (this.fields_view.arch.attrs.colors) {
if (!this.fields_view.arch.attrs.colors) {
return;
}
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()

View File

@ -121,6 +121,12 @@ db.web.ActionManager = db.web.Widget.extend({
if (!this.dialog && on_closed) {
on_closed();
}
if (this.dialog && action.context) {
var model = action.context.active_model;
if (model === 'base.module.upgrade' || model === 'base.setup.installer' || model === 'base.module.upgrade') {
db.webclient.do_reload();
}
}
this.dialog_stop();
},
ir_actions_server: function (action, on_closed) {
@ -514,9 +520,57 @@ db.web.Sidebar = db.web.Widget.extend({
self.do_toggle();
});
},
call_default_on_sidebar: function(item) {
var func_name = 'on_sidebar_' + _.underscored(item.label);
var fn = this.widget_parent[func_name];
if(typeof fn === 'function') {
fn(item);
}
},
add_default_sections: function() {
this.add_section(_t('Customize'), 'customize');
this.add_items('customize', [
{
label: _t("Manage Views"),
callback: this.call_default_on_sidebar,
title: _t("Manage views of the current object"),
}, {
label: _t("Edit Workflow"),
callback: this.call_default_on_sidebar,
title: _t("Manage views of the current object"),
classname: 'oe_hide oe_sidebar_edit_workflow'
}, {
label: _t("Customize Object"),
callback: this.call_default_on_sidebar,
title: _t("Manage views of the current object"),
}
]);
this.add_section(_t('Other Options'), 'other');
this.add_items('other', [
{
label: _t("Import"),
callback: this.call_default_on_sidebar,
}, {
label: _t("Export"),
callback: this.call_default_on_sidebar,
}, {
label: _t("Translate"),
callback: this.call_default_on_sidebar,
classname: 'oe_sidebar_translate oe_hide'
}, {
label: _t("View Log"),
callback: this.call_default_on_sidebar,
classname: 'oe_hide oe_sidebar_view_log'
}
]);
},
add_toolbar: function(toolbar) {
var self = this;
_.each([['print', "Reports"], ['action', "Actions"], ['relate', "Links"]], function(type) {
_.each([['print', _t("Reports")], ['action', _t("Actions")], ['relate', _t("Links")]], function(type) {
var items = toolbar[type[0]];
if (items.length) {
for (var i = 0; i < items.length; i++) {
@ -526,15 +580,30 @@ db.web.Sidebar = db.web.Widget.extend({
classname: 'oe_sidebar_' + type[0]
}
}
self.add_section(type[0], type[1], items);
self.add_section(type[1], type[0]);
self.add_items(type[0], items);
}
});
},
add_section: function(code, name, items) {
// For each section, we pass a name/label and optionally an array of items.
// If no items are passed, then the section will be created as a custom section
// returning back an element_id to be used by a custom controller.
// Else, the section is a standard section with items displayed as links.
add_section: function(name, code) {
if(!code) code = _.underscored(name);
var $section = this.sections[code];
if(!$section) {
section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
var $section = $(db.web.qweb.render("Sidebar.section", {
section_id: section_id,
name: name,
classname: 'oe_sidebar_' + code,
}));
$section.appendTo(this.$element.find('div.sidebar-actions'));
this.sections[code] = $section;
}
return $section;
},
add_items: function(section_code, items) {
// An item is a dictonary : {
// label: label to be displayed for the link,
// action: action to be launch when the link is clicked,
@ -543,25 +612,24 @@ db.web.Sidebar = db.web.Widget.extend({
// title: optional title for the link
// }
// Note: The item should have one action or/and a callback
//
var self = this,
section_id = _.uniqueId(this.element_id + '_section_' + code + '_');
$section = this.add_section(_.titleize(section_code.replace('_', ' ')), section_code),
section_id = $section.attr('id');
if (items) {
for (var i = 0; i < items.length; i++) {
items[i].element_id = _.uniqueId(section_id + '_item_');
this.items[items[i].element_id] = items[i];
}
}
var $section = $(db.web.qweb.render("Sidebar.section", {
section_id: section_id,
name: name,
classname: 'oe_sidebar_' + code,
items: items
}));
if (items) {
$section.find('a.oe_sidebar_action_a').click(function() {
var $items = $(db.web.qweb.render("Sidebar.section.items", {items: items}));
$items.find('a.oe_sidebar_action_a').click(function() {
var item = self.items[$(this).attr('id')];
if (item.callback) {
item.callback();
item.callback.apply(self, [item]);
}
if (item.action) {
var ids = self.widget_parent.get_selected_ids();
@ -591,10 +659,13 @@ db.web.Sidebar = db.web.Widget.extend({
}
return false;
});
var $ul = $section.find('ul');
if(!$ul.length) {
$ul = $('<ul/>').appendTo($section);
}
$items.appendTo($ul);
}
$section.appendTo(this.$element.find('div.sidebar-actions'));
this.sections[code] = $section;
return section_id;
},
do_fold: function() {
this.$element.addClass('closed-sidebar').removeClass('open-sidebar');
@ -823,46 +894,15 @@ db.web.View = db.web.Widget.extend(/** @lends db.web.View# */{
},
do_search: function(view) {
},
set_common_sidebar_sections: function(sidebar) {
sidebar.add_section('customize', "Customize", [
{
label: "Manage Views",
callback: this.on_sidebar_manage_view,
title: "Manage views of the current object"
}, {
label: "Edit Workflow",
callback: this.on_sidebar_edit_workflow,
title: "Manage views of the current object",
classname: 'oe_hide oe_sidebar_edit_workflow'
}, {
label: "Customize Object",
callback: this.on_sidebar_customize_object,
title: "Manage views of the current object"
}
]);
sidebar.add_section('other', "Other Options", [
{
label: "Import",
callback: this.on_sidebar_import
}, {
label: "Export",
callback: this.on_sidebar_export
}, {
label: "Translate",
callback: this.on_sidebar_translate,
classname: 'oe_sidebar_translate oe_hide'
}, {
label: "View Log",
callback: this.on_sidebar_view_log,
classname: 'oe_hide oe_sidebar_view_log'
}
]);
sidebar.add_default_sections();
},
on_sidebar_manage_view: function() {
on_sidebar_manage_views: function() {
if (this.fields_view && this.fields_view.arch) {
$('<xmp>' + db.web.json_node_to_xml(this.fields_view.arch, true) + '</xmp>').dialog({ width: '95%', height: 600});
} else {
this.notification.warn("Manage Views", "Could not find current view declaration");
this.do_warn("Manage Views", "Could not find current view declaration");
}
},
on_sidebar_edit_workflow: function() {

View File

@ -2,9 +2,8 @@
<!-- vim:fdl=1:
-->
<templates id="template" xml:space="preserve">
<t t-name="Interface">
<div id="oe_loading" class="loading"></div>
<div id="oe_notification" class="oe_notification">
<t t-name="Notification">
<div class="oe_notification">
<div id="oe_notification_default">
<a class="ui-notify-cross ui-notify-close" href="#">x</a>
<h1>#{title}</h1>
@ -17,6 +16,9 @@
<p>#{text}</p>
</div>
</div>
</t>
<t t-name="Interface">
<div id="oe_loading" class="loading"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
<tr>
<td colspan="2" valign="top">
@ -467,17 +469,20 @@
</div>
</t>
<t t-name="Sidebar.section">
<h2><t t-esc="name"/></h2>
<div t-att-id="section_id" t-att-class="classname">
<ul t-if="items">
<h2><t t-esc="name"/></h2>
</div>
</t>
<t t-name="Sidebar.section.items">
<li t-foreach="items" t-as="item" t-att-class="item.classname">
<a class="oe_sidebar_action_a" t-att-id="item.element_id" t-att-title="item.title" href="#">
<t t-esc="item.label"/>
</a>
</li>
</ul>
</div>
</t>
<t t-name="TranslateDialog">
<ul class="oe_translate_tabs">
<li><a t-attf-href="##{widget.element_id}_fields">Fields</a></li>

View File

@ -0,0 +1,22 @@
# French translation for openerp-web
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-10-07 10:38+0200\n"
"PO-Revision-Date: 2011-10-12 05:03+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-10-13 04:46+0000\n"
"X-Generator: Launchpad (build 14124)\n"
#: addons/web_calendar/static/src/xml/web_calendar.xml:0
msgid "&nbsp;"
msgstr ""

View File

@ -3,6 +3,7 @@
*---------------------------------------------------------*/
openerp.web_calendar = function(openerp) {
var _t = openerp.web._t;
var QWeb = openerp.web.qweb;
QWeb.add_template('/web_calendar/static/src/xml/web_calendar.xml');
openerp.web.views.add('calendar', 'openerp.web_calendar.CalendarView');
@ -84,8 +85,8 @@ openerp.web_calendar.CalendarView = openerp.web.View.extend({
if (this.options.sidebar && this.options.sidebar_id) {
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
this.sidebar.start();
this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this.sidebar.add_section('navigator', "Navigator"), this);
this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this.sidebar.add_section('responsible', "Responsible"), this);
this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this);
this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this);
this.sidebar.add_toolbar(this.fields_view.toolbar);
this.set_common_sidebar_sections(this.sidebar);
this.sidebar.do_unfold();
@ -362,13 +363,16 @@ openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({
});
openerp.web_calendar.SidebarResponsible = openerp.web.Widget.extend({
init: function(parent, element_id, view) {
this._super(parent, element_id);
init: function(parent, view) {
var $section = parent.add_section(_t('Responsible'), 'responsible');
this.$div = $('<div></div>');
$section.append(this.$div);
this._super(parent, $section.attr('id'));
this.view = view;
this.$element.delegate('input:checkbox', 'change', this.on_filter_click);
},
on_events_loaded: function(filters) {
this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
this.$div.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
},
on_filter_click: function(e) {
var responsibles = [],
@ -388,8 +392,9 @@ openerp.web_calendar.SidebarResponsible = openerp.web.Widget.extend({
});
openerp.web_calendar.SidebarNavigator = openerp.web.Widget.extend({
init: function(parent, element_id, view) {
this._super(parent, element_id);
init: function(parent, view) {
var $section = parent.add_section(_t('Navigator'), 'navigator');
this._super(parent, $section.attr('id'));
this.view = view;
},
on_events_loaded: function(events) {

View File

@ -22,11 +22,7 @@ class Widgets(openerpweb.Controller):
_cp_path = '/web_dashboard/widgets'
@openerpweb.httprequest
def content(self, req, widget_id):
Widget = req.session.model('res.widget')
w = Widget.read([widget_id], ['content'], req.session.eval_context(req.context))
if w:
r = WIDGET_CONTENT_PATTERN % w[0]
else:
r = "Widget unavailable"
return r
def content(self, request, widget_id):
return WIDGET_CONTENT_PATTERN % request.session.model('res.widget').read(
[int(widget_id)], ['content'], request.session.eval_context(request.context)
)[0]

View File

@ -0,0 +1,46 @@
# French translation for openerp-web
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-10-07 10:39+0200\n"
"PO-Revision-Date: 2011-10-12 05:10+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-10-13 04:46+0000\n"
"X-Generator: Launchpad (build 14124)\n"
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "Reset"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "Undo"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "Add Widget"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "Change layout"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "Choose dashboard layout"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "progress:"
msgstr ""
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:0
msgid "%"
msgstr ""

View File

@ -34,6 +34,10 @@
-webkit-border-top-right-radius: 3px;
border-top-right-radius: 3px;
}
.openerp h2.oe-dashboard-action-header-empty {
padding-top: 0;
padding-bottom: 2px;
}
.openerp a.oe-dashboard-action-rename {
float: left;

View File

@ -420,18 +420,15 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.View.extend({
// Check for installed application
var Installer = new openerp.web.DataSet(this, 'base.setup.installer');
Installer.call('default_get', [], function (installed_modules) {
var installed = false;
_.each(installed_modules, function(v,k) {
if(_.startsWith(k,"cat")) {
installed =installed || v;
}
});
var installed = _(installed_modules).any(function (active, name) {
return _.startsWith(name, 'cat') && active; });
if(installed) {
self.do_display_root_menu();
} else {
self.do_display_installer();
}
} );
});
},
do_display_root_menu: function() {
var self = this;
@ -446,10 +443,8 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.View.extend({
self.$element.append(tiles)
.find('.oe-dashboard-home-tile')
.click(function () {
var $this = $(this);
$this.closest('.openerp')
.find('.menu a[data-menu=' + $this.data('menuid') + ']')
.click();});
openerp.webclient.menu.on_menu_click(null, $(this).data('menuid'))
});
});
return r;
},
@ -490,9 +485,8 @@ openerp.web_dashboard.ApplicationTiles = openerp.web.View.extend({
var self = this;
new openerp.web.DataSet(this, 'res.config').call('start', [[]], function (action) {
$.unblockUI();
self.do_action(action, function () {
// TODO: less brutal reloading
window.location.reload(true);
self.widget_parent.widget_parent.do_action(action, function () {
openerp.webclient.do_reload();
});
});
}

View File

@ -30,9 +30,10 @@
</t>
<t t-name="DashBoard.action">
<div t-att-data-id="action.attrs.name" class="oe-dashboard-action">
<h2 class="oe-dashboard-action-header oe_view_title">
<h2 t-attf-class="oe-dashboard-action-header oe_view_title #{action.attrs.string ? '' : 'oe-dashboard-action-header-empty'}">
<input class="oe-dashboard-action-input" type="text" name="title" value="" style="display: none"/>
<t t-esc="action.attrs.string"/>
<t t-if="!action.attrs.string">&amp;nbsp;</t>
<span class='ui-icon ui-icon-closethick'></span>
<span class='ui-icon ui-icon-minusthick oe-dashboard-fold' t-if="!action.attrs.fold"></span>
<span class='ui-icon ui-icon-plusthick oe-dashboard-fold' t-if="action.attrs.fold"></span>

View File

@ -1,4 +1,4 @@
# Danish translation for openerp-web
# French translation for openerp-web
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
@ -8,31 +8,31 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-10-07 10:39+0200\n"
"PO-Revision-Date: 2011-10-11 14:02+0000\n"
"Last-Translator: Jonas Mortensen <Unknown>\n"
"Language-Team: Danish <da@li.org>\n"
"PO-Revision-Date: 2011-10-12 05:07+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-10-12 04:44+0000\n"
"X-Launchpad-Export-Date: 2011-10-13 04:46+0000\n"
"X-Generator: Launchpad (build 14124)\n"
#: addons/web_default_home/static/src/xml/web_default_home.xml:0
msgid "Welcome to your new OpenERP instance."
msgstr "Velkommen til din nye OpenERP instans."
msgstr ""
#: addons/web_default_home/static/src/xml/web_default_home.xml:0
msgid "Remember to bookmark this page."
msgstr "Husk at tilføje denne side til dine favoritter."
msgstr ""
#: addons/web_default_home/static/src/xml/web_default_home.xml:0
msgid "Remember your login:"
msgstr "Husk dit log ind:"
msgstr ""
#: addons/web_default_home/static/src/xml/web_default_home.xml:0
msgid "Choose the first OpenERP Application you want to install.."
msgstr "Vælg den første OpenERP Applikation som du ønsker at installere."
msgstr ""
#: addons/web_default_home/static/src/xml/web_default_home.xml:0
msgid "Install"
msgstr "Installér"
msgstr ""