[Merge]Merge trunk

bzr revid: vja@tinyerp.com-20121008065006-b83jugs57vbsfr70
This commit is contained in:
Vishmita
2012-10-08 12:20:06 +05:30
34 changed files with 771 additions and 529 deletions
+65 -36
View File
@@ -227,6 +227,8 @@ def module_installed_bypass_session(dbname):
return sorted_modules
def module_boot(req):
return [m for m in req.config.server_wide_modules if m in openerpweb.addons_manifest]
# TODO the following will be enabled once we separate the module code and translation loading
serverside = []
dbside = []
for i in req.config.server_wide_modules:
@@ -531,6 +533,20 @@ def parse_context(context, session):
except ValueError:
return common.nonliterals.Context(session, context)
def _local_web_translations(trans_file):
messages = []
try:
with open(trans_file) as t_file:
po = babel.messages.pofile.read_po(t_file)
except Exception:
return
for x in po:
if x.id and x.string and "openerp-web" in x.auto_comments:
messages.append({'id': x.id, 'string': x.string})
return messages
#----------------------------------------------------------
# OpenERP Web web Controllers
#----------------------------------------------------------
@@ -576,6 +592,7 @@ class Home(openerpweb.Controller):
def login(self, req, db, login, key):
return login_and_redirect(req, db, login, key)
class WebClient(openerpweb.Controller):
_cp_path = "/web/webclient"
@@ -658,41 +675,53 @@ class WebClient(openerpweb.Controller):
last_modified, checksum)
@openerpweb.jsonrequest
def translations(self, req, mods, lang):
lang_model = req.session.model('res.lang')
ids = lang_model.search([("code", "=", lang)])
if ids:
lang_obj = lang_model.read(ids[0], ["direction", "date_format", "time_format",
"grouping", "decimal_point", "thousands_sep"])
else:
lang_obj = None
def bootstrap_translations(self, req, mods):
""" Load local translations from *.po files, as a temporary solution
until we have established a valid session. This is meant only
for translating the login page and db management chrome, using
the browser's language. """
lang = req.httprequest.accept_languages.best or 'en'
# For performance reasons we only load a single translation, so for
# sub-languages (that should only be partially translated) we load the
# main language PO instead - that should be enough for the login screen.
if '-' in lang: # RFC2616 uses '-' separators for sublanguages
lang = lang.split('-')[0]
if "_" in lang:
separator = "_"
else:
separator = "@"
langs = lang.split(separator)
langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
transs = {}
translations_per_module = {}
for addon_name in mods:
transl = {"messages":[]}
transs[addon_name] = transl
addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
for l in langs:
f_name = os.path.join(addons_path, addon_name, "i18n", l + ".po")
if not os.path.exists(f_name):
continue
try:
with open(f_name) as t_file:
po = babel.messages.pofile.read_po(t_file)
except Exception:
continue
for x in po:
if x.id and x.string and "openerp-web" in x.auto_comments:
transl["messages"].append({'id': x.id, 'string': x.string})
return {"modules": transs,
"lang_parameters": lang_obj}
f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po")
if not os.path.exists(f_name):
continue
translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)}
return {"modules": translations_per_module,
"lang_parameters": None}
@openerpweb.jsonrequest
def translations(self, req, mods, lang):
res_lang = req.session.model('res.lang')
ids = res_lang.search([("code", "=", lang)])
lang_params = None
if ids:
lang_params = res_lang.read(ids[0], ["direction", "date_format", "time_format",
"grouping", "decimal_point", "thousands_sep"])
# Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is
# done server-side when the language is loaded, so we only need to load the user's lang.
ir_translation = req.session.model('ir.translation')
translations_per_module = {}
messages = ir_translation.search_read([('module','in',mods),('lang','=',lang),
('comments','like','openerp-web'),('value','!=',False),
('value','!=','')],
['module','src','value','lang'], order='module')
for mod, msg_group in itertools.groupby(messages, key=operator.itemgetter('module')):
translations_per_module.setdefault(mod,{'messages':[]})
translations_per_module[mod]['messages'].extend({'id': m['src'],
'string': m['value']} \
for m in msg_group)
return {"modules": translations_per_module,
"lang_parameters": lang_params}
@openerpweb.jsonrequest
def version_info(self, req):
@@ -769,7 +798,7 @@ class Database(openerpweb.Controller):
{'fileToken': int(token)}
)
except xmlrpclib.Fault, e:
return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]])
return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]])
@openerpweb.httprequest
def restore(self, req, db_file, restore_pwd, new_db):
@@ -1180,8 +1209,8 @@ class DataSet(openerpweb.Controller):
def call_button(self, req, model, method, args, domain_id=None, context_id=None):
action = self.call_common(req, model, method, args, domain_id, context_id)
if isinstance(action, dict) and action.get('type') != '':
return {'result': clean_action(req, action)}
return {'result': False}
return clean_action(req, action)
return False
@openerpweb.jsonrequest
def exec_workflow(self, req, model, id, signal):
@@ -1593,7 +1622,7 @@ class Action(openerpweb.Controller):
action = req.session.model(action_type).read([action_id], False, ctx)
if action:
value = clean_action(req, action[0], do_not_eval)
return {'result': value}
return value
@openerpweb.jsonrequest
def run(self, req, action_id):
+16 -11
View File
@@ -1103,9 +1103,8 @@
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
}
.openerp .oe_menu > li > .oe_active {
background: rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.3);
text-shadow: black 0px 0px 3px;
font-weight: bold;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
@@ -1198,7 +1197,7 @@
color: white;
padding: 2px 4px;
margin: 1px 6px 0 0;
border: 1px solid lightGray;
border: 1px solid lightgrey;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
@@ -1223,7 +1222,7 @@
transform: scale(1.1);
}
.openerp .oe_secondary_submenu .oe_active {
border-top: 1px solid lightGray;
border-top: 1px solid lightgrey;
border-bottom: 1px solid #dedede;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2);
@@ -1865,6 +1864,13 @@
margin: 0;
white-space: nowrap;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or {
visibility: hidden;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or {
opacity: 0.5;
margin-left: -14px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_opened h4:before {
content: "▾ ";
position: relative;
@@ -2117,7 +2123,7 @@
}
.openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span {
font-size: 80%;
color: darkGreen;
color: darkgreen;
vertical-align: top;
position: relative;
top: -4px;
@@ -2206,16 +2212,12 @@
min-width: 60px;
color: #1f1f1f;
}
.openerp .oe_form textarea {
height: 32px;
}
.openerp .oe_form input[readonly], .openerp .oe_form select[readonly], .openerp .oe_form textarea[readonly], .openerp .oe_form input[disabled], .openerp .oe_form select[disabled] {
background: #e5e5e5 !important;
color: #666666;
}
.openerp .oe_form textarea[disabled] {
border: none;
border-left: 8px solid #eeeeee;
padding-left: 8px;
-moz-box-shadow: none;
-webkit-box-shadow: none;
@@ -2224,6 +2226,9 @@
-webkit-border-radius: 0px;
border-radius: 0px;
}
.openerp .oe_form textarea.oe_inline[disabled] {
border-left: 8px solid #eeeeee;
}
.openerp .oe_form .oe_form_field_url button img {
vertical-align: top;
}
@@ -2241,7 +2246,7 @@
.openerp .oe_form .oe_datepicker_root {
display: inline-block;
}
.openerp .oe_form .oe_form_required input, .openerp .oe_form .oe_form_required select, .openerp .oe_form .oe_form_required textarea {
.openerp .oe_form .oe_form_required input:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required select:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required textarea:not([disabled]):not([readonly]) {
background-color: #d2d2ff !important;
}
.openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea {
@@ -2279,7 +2284,7 @@
position: relative;
overflow: hidden;
}
.openerp .oe_form .oe_form_field_html {
.openerp .oe_form .oe_form_embedded_html {
position: relative;
width: 600px;
margin-left: 130px;
+11 -8
View File
@@ -891,9 +891,8 @@ $sheet-max-width: 860px
color: white
@include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
> .oe_active
background: rgba(0,0,0,0.2)
background: rgba(0,0,0,0.30)
text-shadow: black 0px 0px 3px
font-weight: bold
@include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
.oe_user_menu
@@ -1482,6 +1481,11 @@ $sheet-max-width: 860px
list-style: none
margin: 0
white-space: nowrap
&:first-child .searchview_extended_prop_or
visibility: hidden
.searchview_extended_prop_or
opacity: 0.5
margin-left: -14px
.oe_opened
h4:before
content: "▾ "
@@ -1753,17 +1757,16 @@ $sheet-max-width: 860px
background: white
min-width: 60px
color: #1f1f1f
textarea
height: 32px
input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled]
background: #E5E5E5 !important
color: #666
textarea[disabled]
border: none
border-left: 8px solid #eee
padding-left: 8px
@include box-shadow(none)
@include radius(0px)
textarea.oe_inline[disabled]
border-left: 8px solid #eee
.oe_form_field_url button img
vertical-align: top
.oe_form_field_date,
@@ -1776,8 +1779,8 @@ $sheet-max-width: 860px
display: none
.oe_datepicker_root
display: inline-block
.oe_form_required
input, select, textarea
.oe_form_required
input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])
background-color: #D2D2FF !important
.oe_form_invalid
input, select, textarea
@@ -1808,7 +1811,7 @@ $sheet-max-width: 860px
> div
position: relative
overflow: hidden
.oe_form_field_html
.oe_form_embedded_html
position: relative
width: 600px
margin-left: 130px
+10 -10
View File
@@ -146,8 +146,8 @@ instance.web.Dialog = instance.web.Widget.extend({
this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
this.$el.dialog("widget").append(this.$buttons);
}
var res = this.start();
this.dialog_inited = true;
var res = this.start();
return res;
},
close: function() {
@@ -392,7 +392,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
do_create: function(form) {
var self = this;
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}, function(result) {
self.rpc("/web/database/create", {'fields': fields}).then(function(result) {
var form_obj = self.to_object(fields);
var client_action = {
type: 'ir.actions.client',
@@ -418,7 +418,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
return;
}
self.rpc("/web/database/drop", {'fields': fields}, function(result) {
self.rpc("/web/database/drop", {'fields': fields}).then(function(result) {
if (result.error) {
self.display_error(result);
return;
@@ -481,7 +481,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
var self = this;
self.rpc("/web/database/change_password", {
'fields': $(form).serializeArray()
}, function(result) {
}).then(function(result) {
if (result.error) {
self.display_error(result);
return;
@@ -660,7 +660,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({
submitHandler: function (form) {
self.rpc("/web/session/change_password",{
'fields': $(form).serializeArray()
}, function(result) {
}).then(function(result) {
if (result.error) {
self.display_error(result);
return;
@@ -886,8 +886,8 @@ instance.web.UserMenu = instance.web.Widget.extend({
var self = this;
if (!this.getParent().has_uncommitted_changes()) {
self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) {
result.result.res_id = instance.session.uid;
self.getParent().action_manager.do_action(result.result);
result.res_id = instance.session.uid;
self.getParent().action_manager.do_action(result);
});
}
},
@@ -1121,7 +1121,7 @@ instance.web.WebClient = instance.web.Client.extend({
var self = this;
return this.rpc("/web/action/load", { action_id: options.action_id })
.pipe(function (result) {
var action = result.result;
var action = result;
if (options.needaction) {
action.context.search_default_needaction_pending = true;
}
@@ -1165,8 +1165,8 @@ instance.web.EmbeddedClient = instance.web.Client.extend({
var self = this;
return $.when(this._super()).pipe(function() {
return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) {
var action = result.result;
return self.rpc("/web/action/load", { action_id: self.action_id }).then(function(result) {
var action = result;
action.flags = _.extend({
//views_switcher : false,
search_view : false,
+7 -18
View File
@@ -820,9 +820,10 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
* the action manager can be found amongst the ancestors of the current widget.
* If that's not the case this method will simply return `false`.
*/
do_action: function(action, on_finished) {
if (this.getParent()) {
return this.getParent().do_action(action, on_finished);
do_action: function() {
var parent = this.getParent();
if (parent) {
return parent.do_action.apply(parent, arguments);
}
return false;
},
@@ -841,7 +842,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
instance.session.rpc(url, data). then(function() {
instance.session.rpc(url, data).then(function() {
if (!self.isDestroyed())
def.resolve.apply(def, arguments);
}, function() {
@@ -1299,7 +1300,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params, success_callback, error_callback) {
rpc: function(url, params) {
var self = this;
// url can be an $.ajax option object
if (_.isString(url)) {
@@ -1316,8 +1317,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
};
var deferred = $.Deferred();
this.trigger('request', url, payload);
var aborter = params.aborter;
delete params.aborter;
var request = this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
self.trigger('response', response);
@@ -1341,16 +1340,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
};
deferred.reject(error, $.Event());
});
if (aborter) {
aborter.abort_last = function () {
if (!(request.isResolved() || request.isRejected())) {
deferred.fail(function (error, event) {
event.preventDefault();
});
request.abort();
}
};
}
// Allow deferred user to disable on_rpc_error in fail
deferred.fail(function() {
deferred.fail(function(error, event) {
@@ -1358,7 +1347,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
self.on_rpc_error(error, event);
}
});
}).then(success_callback, error_callback).promise();
});
return deferred;
},
/**
+12 -12
View File
@@ -19,9 +19,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
rpc: function(url, params, success_callback, error_callback) {
rpc: function(url, params) {
params.session_id = this.session_id;
return this._super(url, params, success_callback, error_callback);
return this._super(url, params);
},
/**
* Setup a sessionm
@@ -57,7 +57,12 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
}
return deferred;
return $.when(
deferred,
self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) {
instance.web._t.database.set_bundle(trans);
})
);
});
},
/**
@@ -537,13 +542,9 @@ instance.web.qweb.preprocess_node = function() {
if (translation && translation.value === 'off') {
return;
}
var ts = _.str.trim(this.node.data);
if (ts.length === 0) {
return;
}
var tr = instance.web._t(ts);
if (tr !== ts) {
this.node.data = tr;
var match = /^(\s*)(.+?)(\s*)$/.exec(this.node.data);
if (match) {
this.node.data = match[1] + instance.web._t(match[2]) + match[3];
}
break;
case 1:
@@ -600,8 +601,7 @@ var messages_by_seconds = function() {
[120, _t("Don't leave yet,<br />it's still loading...")],
[300, _t("You may not believe it,<br />but the application is actually loading...")],
[420, _t("Take a minute to get a coffee,<br />because it's loading...")],
[600, _t("It's loading...<br />By the way, did you tried the kitten mode?")],
[3600, _t("Maybe you should consider pressing F5...")],
[3600, _t("Maybe you should consider reloading the application by pressing F5...")],
];
};
+2 -20
View File
@@ -586,26 +586,8 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({
* @param {Function} error_callback
* @returns {$.Deferred}
*/
call: function (method, args, callback, error_callback) {
return this._model.call(method, args).then(callback, error_callback);
},
/**
* Calls an arbitrary method, with more crazy
*
* @param {String} method
* @param {Array} [args]
* @param {Number} [domain_index] index of a domain to evaluate in the args array
* @param {Number} [context_index] index of a context to evaluate in the args array
* @returns {$.Deferred}
*/
call_and_eval: function (method, args, domain_index, context_index) {
return instance.session.rpc('/web/dataset/call', {
model: this.model,
method: method,
domain_id: domain_index == undefined ? null : domain_index,
context_id: context_index == undefined ? null : context_index,
args: args || []
});
call: function (method, args) {
return this._model.call(method, args);
},
/**
* Calls a button method, usually returning some sort of action
+14 -12
View File
@@ -5,7 +5,15 @@ instance.web.DataExport = instance.web.Dialog.extend({
template: 'ExportTreeView',
dialog_title: {toString: function () { return _t("Export Data"); }},
init: function(parent, dataset) {
this._super(parent);
var self = this;
options = {
buttons : [
{text: _t("Close"), click: function() { self.close(); }},
{text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
],
close: function(event, ui){ self.close();}
}
this._super(parent, options);
this.records = {};
this.dataset = dataset;
this.exports = new instance.web.DataSetSearch(
@@ -14,13 +22,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
this.open({
buttons : [
{text: _t("Close"), click: function() { self.close(); }},
{text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
],
close: function(event, ui){ self.close();}
});
this.open();
self.$el.removeClass('ui-dialog-content ui-widget-content');
self.$el.find('#add_field').click(function() {
if ($('#field-tree-structure tr.ui-selected')) {
@@ -49,7 +51,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.rpc("/web/export/get_fields", {
model: self.dataset.model,
import_compat: Boolean(import_comp)
}, function (records) {
}).then(function (records) {
got_fields.resolve();
self.on_show_data(records);
});
@@ -57,7 +59,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
return $.when(
got_fields,
this.rpc('/web/export/formats', {}, this.do_setup_export_formats),
this.rpc('/web/export/formats', {}).then(this.do_setup_export_formats),
this.show_exports_list());
},
do_setup_export_formats: function (formats) {
@@ -91,7 +93,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
self.$el.find('#fields_list option').remove();
var export_id = self.$el.find('#saved_export_list option:selected').val();
if (export_id) {
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field);
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).then(self.do_load_export_field);
}
});
self.$el.find('#delete_export_list').click(function() {
@@ -181,7 +183,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
import_compat: Boolean(import_comp),
parent_field_type : record['field_type'],
exclude: exclude_fields
}, function(results) {
}).then(function(results) {
record.loaded = true;
self.on_show_data(results, record.id);
});
+3 -3
View File
@@ -1494,7 +1494,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
return $.when(facet_from(this, value));
}
assert(value.length <= 1,
_("M2O search fields do not currently handle multiple default values"));
_t("M2O search fields do not currently handle multiple default values"));
// there are many cases of {search_default_$m2ofield: [id]}, need
// to handle this as if it were a single value.
value = value[0];
@@ -1578,7 +1578,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
$filter.unbind('click').click(function () {
self.view.query.reset([{
category: _("Custom Filter"),
category: _t("Custom Filter"),
icon: 'M',
field: {
get_context: function () { return filter.context; },
@@ -1693,7 +1693,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
});
return $.when(
this._super(),
this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) {
this.rpc("/web/searchview/fields_get", {model: this.view.model}).then(function(data) {
self.fields = _.extend({
id: { string: 'ID', type: 'id' }
}, data.fields);
+144 -82
View File
@@ -30,7 +30,7 @@ instance.web.form.FieldManagerMixin = {
/**
* Must return the asked field as in fields_get.
*/
get_field: function(field_name) {},
get_field_desc: function(field_name) {},
/**
* Returns the current value of a field present in the view. See the get_value() method
* method in FieldInterface for further information.
@@ -44,6 +44,21 @@ instance.web.form.FieldManagerMixin = {
@return (Deferred) Is resolved after all the values are setted.
*/
set_values: function(values) {},
/**
Computes an OpenERP domain.
@param (list) expression An OpenERP domain.
@return (boolean) The computed value of the domain.
*/
compute_domain: function(expression) {},
/**
Builds an evaluation context for the resolution of the fields' contexts. Please note
the field are only supposed to use this context to evualuate their own, they should not
extend it.
@return (CompoundContext) An OpenERP context.
*/
build_eval_context: function() {},
};
instance.web.views.add('form', 'instance.web.FormView');
@@ -1085,7 +1100,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
field.on('focused', null, this.proxy('widgetFocused'))
.on('blurred', null, this.proxy('widgetBlurred'));
if (this.get_field(name).translate) {
if (this.get_field_desc(name).translate) {
this.translatable_fields.push(field);
}
field.on('changed_value', this, function() {
@@ -1103,12 +1118,34 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
});
},
get_field: function(field_name) {
get_field_desc: function(field_name) {
return this.fields_view.fields[field_name];
},
get_field_value: function(field_name) {
return this.fields[field_name].get_value();
},
compute_domain: function(expression) {
return instance.web.form.compute_domain(expression, this.fields);
},
_build_view_fields_values: function(blacklist) {
var a_dataset = this.dataset;
var fields_values = this.get_fields_values(blacklist);
var active_id = a_dataset.ids[a_dataset.index];
_.extend(fields_values, {
active_id: active_id || false,
active_ids: active_id ? [active_id] : [],
active_model: a_dataset.model,
parent: {}
});
if (a_dataset.parent_view) {
fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
}
return fields_values;
},
build_eval_context: function(blacklist) {
var a_dataset = this.dataset;
return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
},
});
/**
@@ -1175,7 +1212,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
this.$form.appendTo(this.$target);
_.each(this.fields_to_init, function($elem) {
var ws = _.map(this.fields_to_init, function($elem) {
var name = $elem.attr("name");
if (!self.fvg.fields[name]) {
throw new Error("Field '" + name + "' specified in view could not be found.");
@@ -1191,7 +1228,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
self.alter_field(w);
self.view.register_field(w, $elem.attr("name"));
w.replace($elem);
return [w, $elem];
});
_.each(ws, function(w) {
w[0].replace(w[1]);
});
_.each(this.tags_to_init, function($elem) {
var tag_name = $elem[0].tagName.toLowerCase();
@@ -1528,6 +1568,52 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
},
});
/**
Welcome.
If you read this documentation, it probably means that you were asked to use a form view widget outside of
a form view. Before going further, you must understand that those fields were never really created for
that usage. Don't think that this class will hold the answer to all your problems, at best it will allow
you to hack the system with more style.
*/
instance.web.form.DefaultFieldManager = instance.web.Widget.extend({
init: function(parent, eval_context) {
this._super(parent);
this.field_descs = {};
this.eval_context = eval_context || {};
this.set({
display_invalid_fields: false,
actual_mode: 'create',
});
},
get_field_desc: function(field_name) {
if (this.field_descs[field_name] === undefined) {
this.field_descs[field_name] = {
string: field_name,
};
}
return this.field_descs[field_name];
},
extend_field_desc: function(fields) {
var self = this;
_.each(fields, function(v, k) {
_.extend(self.get_field_desc(k), v);
});
},
get_field_value: function(field_name) {
return false;
},
set_values: function(values) {
// nothing
},
compute_domain: function(expression) {
return instance.web.form.compute_domain(expression, {});
},
build_eval_context: function() {
return new instance.web.CompoundContext(this.eval_context);
},
});
instance.web.form.FormDialog = instance.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
@@ -1641,9 +1727,7 @@ instance.web.form.InvisibilityChangerMixin = {
this._ic_invisible_modifier = invisible_domain;
this._ic_field_manager.on("view_content_has_changed", this, function() {
var result = self._ic_invisible_modifier === undefined ? false :
instance.web.form.compute_domain(
self._ic_invisible_modifier,
self._ic_field_manager.fields);
self._ic_field_manager.compute_domain(self._ic_invisible_modifier);
self.set({"invisible": result});
});
this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
@@ -1694,19 +1778,22 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
*/
init: function(field_manager, node) {
this._super(field_manager);
this.view = field_manager;
this.field_manager = field_manager;
if (this.field_manager instanceof instance.web.FormView)
this.view = this.field_manager;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible);
this.field_manager.on("view_content_has_changed", this, this.process_modifiers);
this.set({required: this.modifiers['required'] === true});
this.set({
required: false,
readonly: false,
});
// some events to make the property "effective_readonly" sync automatically with "readonly" and
// "mode" on field_manager
var self = this;
this.set({"readonly": this.modifiers['readonly'] === true});
var test_effective_readonly = function() {
self.set({"effective_readonly": self.get("readonly") || self.field_manager.get("actual_mode") === "view"});
};
@@ -1715,6 +1802,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
test_effective_readonly.call(this);
},
renderElement: function() {
this.process_modifiers();
this._super();
this.$el.addClass(this.node.attrs["class"] || "");
},
@@ -1737,12 +1825,11 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
});
},
process_modifiers: function() {
var compute_domain = instance.web.form.compute_domain;
var to_set = {};
for (var a in this.modifiers) {
if (!this.modifiers.hasOwnProperty(a)) { continue; }
if (!_.include(["invisible"], a)) {
var val = compute_domain(this.modifiers[a], this.view.fields);
var val = this.field_manager.compute_domain(this.modifiers[a]);
to_set[a] = val;
}
}
@@ -1771,25 +1858,6 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
}, options || {});
$(trigger).tipsy(options);
},
_build_view_fields_values: function(blacklist) {
var a_dataset = this.view.dataset;
var fields_values = this.view.get_fields_values(blacklist);
var active_id = a_dataset.ids[a_dataset.index];
_.extend(fields_values, {
active_id: active_id || false,
active_ids: active_id ? [active_id] : [],
active_model: a_dataset.model,
parent: {}
});
if (a_dataset.parent_view) {
fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
}
return fields_values;
},
_build_eval_context: function(blacklist) {
var a_dataset = this.view.dataset;
return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
},
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
@@ -1802,7 +1870,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
}
if (v_context.__ref || true) { //TODO: remove true
var fields_values = this._build_eval_context(blacklist);
var fields_values = this.field_manager.build_eval_context(blacklist);
v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
}
return v_context;
@@ -1813,7 +1881,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
// 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) || true) { //TODO: remove true
var fields_values = this._build_eval_context();
var fields_values = this.field_manager.build_eval_context();
final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
return final_domain;
@@ -2001,7 +2069,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
var self = this
this._super(field_manager, node);
this.name = this.node.attrs.name;
this.field = this.field_manager.get_field(this.name);
this.field = this.field_manager.get_field_desc(this.name);
this.widget = this.node.attrs.widget;
this.string = this.node.attrs.string || this.field.string || this.name;
this.options = JSON.parse(this.node.attrs.options || '{}');
@@ -2015,11 +2083,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
renderElement: function() {
var self = this;
this._super();
if (this.field.translate) {
if (this.field.translate && this.view) {
this.$el.addClass('oe_form_field_translatable');
this.$el.find('.oe_field_translate').click(this.on_translate);
}
this.$label = this.view.$el.find('label[for=' + this.id_for_label + ']');
this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
if (instance.session.debug) {
this.do_attach_tooltip(this, this.$label[0] || this.$el);
this.$label.off('dblclick').on('dblclick', function() {
@@ -2081,7 +2149,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
var self = this;
var trans = new instance.web.DataSet(this, 'ir.translation');
return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) {
self.do_action(r.result);
self.do_action(r);
});
},
});
@@ -2884,7 +2952,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
self.focus();
return;
}
var pop = new instance.web.form.FormOpenPopup(self.view);
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.field.relation,
self.get("value"),
@@ -3479,7 +3547,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
}
var self = this;
var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null;
var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.o2m.string,
create_function: function(data) {
@@ -3595,7 +3663,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
},
do_activate_record: function(index, id) {
var self = this;
var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.o2m.string,
write_function: function(id, data) {
@@ -3623,7 +3691,10 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
var parent_form = this.o2m.view;
var self = this;
this.ensure_saved().pipe(function () {
return parent_form.do_save();
if (parent_form)
return parent_form.do_save();
else
return $.when();
}).then(function () {
self.handle_button(name, id, callback);
});
@@ -3879,7 +3950,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
},
render_value: function() {
var self = this;
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
var values = self.get("value")
var handle_names = function(data) {
var indexed = {};
@@ -3914,6 +3985,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
disable_utility_classes: true,
init: function(field_manager, node) {
this._super(field_manager, node);
this.set({"value": []});
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
@@ -4149,7 +4221,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
} else {
var id = self.dataset.ids[self.dataset.index];
var pop = new instance.web.form.FormOpenPopup(self.view);
var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(self.field.relation, id, self.build_context(), {
title: _t("Open: ") + self.string,
write_function: function(id, data, options) {
@@ -4282,7 +4354,7 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
this.dataset.create_function = function(data, sup) {
var fct = self.options.create_function || sup;
return fct.call(this, data).then(function(r) {
self.created_elements.push(r.result);
self.created_elements.push(r);
});
};
this.dataset.write_function = function(id, data, options, sup) {
@@ -4417,7 +4489,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.rpc('/web/session/eval_domain_and_context', {
domains: [],
contexts: [this.context]
}, function (results) {
}).then(function (results) {
var search_defaults = {};
_.each(results.context, function (value_, key) {
var match = /^search_default_(.*)$/.exec(key);
@@ -4485,7 +4557,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
domains: domains || [],
contexts: contexts || [],
group_by_seq: groupbys || []
}, function (results) {
}).then(function (results) {
self.view_list.do_search(results.domain, results.context, results.group_by);
});
},
@@ -4540,22 +4612,28 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
}
},
destroy_content: function() {
if (this.selection) {
this.selection.destroy();
this.selection = undefined;
}
if (this.m2o) {
this.m2o.destroy();
this.m2o = undefined;
if (this.fm) {
this.fm.destroy();
}
},
initialize_content: function() {
var self = this;
this.selection = new instance.web.form.FieldSelection(this, { attrs: {
var fm = new instance.web.form.DefaultFieldManager(this);
this.fm = fm;
fm.extend_field_desc({
"selection": {
selection: this.field_manager.get_field_desc(this.name).selection,
type: "selection",
},
"m2o": {
relation: null,
type: "many2one",
},
});
this.selection = new instance.web.form.FieldSelection(fm, { attrs: {
name: 'selection',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.selection.view = this.view;
this.selection.on("change:value", this, this.on_selection_changed);
this.selection.setElement(this.$(".oe_form_view_reference_selection"));
this.selection.renderElement();
@@ -4564,11 +4642,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
.on('focused', null, function () {self.trigger('focused')})
.on('blurred', null, function () {self.trigger('blurred')});
this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
name: 'm2o',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.view = this.view;
this.m2o.on("change:value", this, this.data_changed);
this.m2o.setElement(this.$(".oe_form_view_reference_m2o"));
this.m2o.renderElement();
@@ -4608,20 +4685,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
this.set({'value': false});
}
},
get_field: function(name) {
if (name === "selection") {
return {
selection: this.view.fields_view.fields[this.name].selection,
type: "selection",
};
} else if (name === "m2o") {
return {
relation: null,
type: "many2one",
};
}
throw Exception("Should not happen");
},
});
instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
@@ -4709,9 +4772,10 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
},
set_filename: function(value) {
var filename = this.node.attrs.filename;
if (this.view.fields[filename]) {
this.view.fields[filename].set_value(value);
this.view.fields[filename].on_ui_change();
if (filename) {
var tmp = {};
tmp[filename] = value;
this.field_manager.set_values(tmp);
}
},
on_clear: function() {
@@ -4753,7 +4817,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
} else {
this.$el.find('a').show(!!this.get('value'));
if (this.get('value')) {
var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
var show_value = _t("Download")
if (this.view)
show_value += " " + (this.view.datarecord[this.node.attrs.filename] || '');
this.$el.find('a').text(show_value);
}
}
@@ -4765,12 +4831,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
this.$el.find('input').eq(0).val(show_value);
this.set_filename(name);
},
set_filename: function(value_) {
var filename = this.node.attrs.filename;
if (this.view.fields[filename]) {
this.view.fields[filename].set({value: value_});
}
},
on_clear: function() {
this._super.apply(this, arguments);
this.$el.find('input').eq(0).val('');
@@ -4819,10 +4879,12 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
this.set({'value': file_base64});
this.binary_value = true;
this.render_value();
this.set_filename(name);
},
on_clear: function() {
this._super.apply(this, arguments);
this.render_value();
this.set_filename('');
}
});
+1 -1
View File
@@ -482,7 +482,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
view_type: "tree",
context: this.dataset.get_context(context),
toolbar: !!this.options.$sidebar
}, callback);
}).then(callback);
}
},
/**
+1 -1
View File
@@ -45,7 +45,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
view_type: "tree",
toolbar: this.view_manager ? !!this.view_manager.sidebar : false,
context: this.dataset.get_context()
}, this.on_loaded);
}).then(this.on_loaded);
},
/**
* Returns the list of fields needed to correctly read objects.
+28 -24
View File
@@ -99,6 +99,10 @@ instance.web.ActionManager = instance.web.Widget.extend({
this.select_breadcrumb(index, subindex);
},
select_breadcrumb: function(index, subindex) {
var next_item = this.breadcrumbs[index + 1];
if (next_item && next_item.on_reverse_breadcrumb) {
next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget);
}
for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
if (i > index) {
if (this.remove_breadcrumb(i) === false) {
@@ -220,14 +224,14 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
});
},
do_action: function(action, on_close, clear_breadcrumbs) {
do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
if (_.isString(action) && instance.web.client_actions.contains(action)) {
var action_client = { type: "ir.actions.client", tag: action };
return this.do_action(action_client, on_close, clear_breadcrumbs);
return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
} else if (_.isNumber(action) || _.isString(action)) {
var self = this;
return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) {
return self.do_action(result.result, on_close, clear_breadcrumbs);
return self.do_action(result, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
});
}
if (!action.type) {
@@ -249,7 +253,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
console.error("Action manager can't handle action of type " + action.type, action);
return $.Deferred().reject();
}
return this[type](action, on_close, clear_breadcrumbs);
return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
},
null_action: function() {
this.dialog_stop();
@@ -300,24 +304,24 @@ instance.web.ActionManager = instance.web.Widget.extend({
return this.inner_widget.appendTo(this.$el);
}
},
ir_actions_act_window: function (action, on_close, clear_breadcrumbs) {
ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
var self = this;
return this.ir_actions_common({
widget: function () { return new instance.web.ViewManagerAction(self, action); },
action: action,
klass: 'oe_act_window',
post_process: function (widget) { widget.add_breadcrumb(); }
}, on_close, clear_breadcrumbs);
post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); }
}, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
},
ir_actions_client: function (action, on_close, clear_breadcrumbs) {
ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) {
var self = this;
var ClientWidget = instance.web.client_actions.get_object(action.tag);
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
var next;
if (next = ClientWidget(this, action.params)) {
return this.do_action(next, on_close, clear_breadcrumbs);
return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
}
return $.when();
}
@@ -329,13 +333,14 @@ instance.web.ActionManager = instance.web.Widget.extend({
post_process: function(widget) {
self.push_breadcrumb({
widget: widget,
title: action.name
title: action.name,
on_reverse_breadcrumb: on_reverse_breadcrumb,
});
if (action.tag !== 'reload') {
self.do_push_state({});
}
}
}, on_close, clear_breadcrumbs);
}, on_close, clear_breadcrumbs, on_reverse_breadcrumb);
},
ir_actions_act_window_close: function (action, on_closed) {
if (!this.dialog && on_closed) {
@@ -343,13 +348,13 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
this.dialog_stop();
},
ir_actions_server: function (action, on_closed, clear_breadcrumbs) {
ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) {
var self = this;
this.rpc('/web/action/run', {
action_id: action.id,
context: action.context || {}
}).then(function (action) {
self.do_action(action, on_closed, clear_breadcrumbs)
self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb)
});
},
ir_actions_report_xml: function(action, on_closed) {
@@ -544,7 +549,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
set_title: function(title) {
this.$el.find('.oe_view_title_text:first').text(title);
},
add_breadcrumb: function() {
add_breadcrumb: function(on_reverse_breadcrumb) {
var self = this;
var views = [this.active_view || this.views_src[0].view_type];
this.on_mode_switch.add(function(mode) {
@@ -589,7 +594,8 @@ instance.web.ViewManager = instance.web.Widget.extend({
titles.pop();
}
return titles;
}
},
on_reverse_breadcrumb: on_reverse_breadcrumb,
});
},
/**
@@ -648,7 +654,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
domains: [this.action.domain || []].concat(domains || []),
contexts: [action_context].concat(contexts || []),
group_by_seq: groupbys || []
}, function (results) {
}).then(function (results) {
self.dataset._model = new instance.web.Model(
self.dataset.model, results.context, results.domain);
var groupby = results.group_by.length
@@ -807,13 +813,11 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
});
break;
case 'fields':
this.dataset.call_and_eval(
'fields_get', [false, {}], null, 1).then(function (fields) {
this.dataset.call('fields_get', [false, {}]).then(function (fields) {
var $root = $('<dl>');
_(fields).each(function (attributes, name) {
$root.append($('<dt>').append($('<h4>').text(name)));
var $attrs = $('<dl>').appendTo(
$('<dd>').appendTo($root));
var $attrs = $('<dl>').appendTo($('<dd>').appendTo($root));
_(attributes).each(function (def, name) {
if (def instanceof Object) {
def = JSON.stringify(def);
@@ -1054,7 +1058,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
self.rpc("/web/action/load", {
action_id: item.action.id,
context: additional_context
}, function(result) {
}).then(function(result) {
result.result.context = _.extend(result.result.context || {},
additional_context);
result.result.flags = result.result.flags || {};
@@ -1190,7 +1194,7 @@ instance.web.View = instance.web.Widget.extend({
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
var handler = function (r) {
var action = r.result;
var action = r;
if (action && action.constructor == Object) {
var ncontext = new instance.web.CompoundContext(context);
if (record_id) {
@@ -1217,7 +1221,7 @@ instance.web.View = instance.web.Widget.extend({
};
if (action_data.special) {
return handler({result: {"type":"ir.actions.act_window_close"}});
return handler({"type":"ir.actions.act_window_close"});
} else if (action_data.type=="object") {
var args = [[record_id]], additional_args = [];
if (action_data.args) {
@@ -1233,7 +1237,7 @@ instance.web.View = instance.web.Widget.extend({
args.push(context);
return dataset.call_button(action_data.name, args).then(handler);
} else if (action_data.type=="action") {
return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler);
return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}).then(handler);
} else {
return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
+3 -8
View File
@@ -2,12 +2,6 @@
<!-- vim:fdl=1:
-->
<templates id="template" xml:space="preserve">
<t t-name="ui.Header">
<header>
<t t-raw="__content__"/>
</header>
</t>
<t t-name="EmptyComponent">
<div></div>
</t>
@@ -413,7 +407,7 @@
<a class="oe_logo" href="#"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
<div class="oe_secondary_menus_container"/>
<div class="oe_footer">
Powered by <a href="http://www.openerp.com" target="_blank"><span>Open</span>ERP</a>
Powered by <a href="http://www.openerp.com" target="_blank"><span>OpenERP</span></a>
</div>
</td>
<td class="oe_application">
@@ -1128,7 +1122,7 @@
<td>
<t t-call="HiddenInputFile">
<t t-set="fileupload_id" t-value="widget.fileupload_id"/>
<t t-set="fileupload_style">width: 83px;</t>
<t t-set="fileupload_style" t-translation="off">width: 83px;</t>
<button class="oe_button oe_field_button" type="button">
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
<span>Select</span>
@@ -1491,6 +1485,7 @@
</div>
<t t-name="SearchView.extended_search.proposition">
<li>
<span class="searchview_extended_prop_or">or</span>
<select class="searchview_extended_prop_field">
<t t-foreach="widget.attrs.fields" t-as="field">
<option t-att="{'selected': field === widget.attrs.selected ? 'selected' : null}"
+2 -2
View File
@@ -82,14 +82,14 @@ $(document).ready(function () {
});
t.test('call', function (openerp) {
var ds = new openerp.web.DataSet({session: openerp.session}, 'mod');
t.expect(ds.call('frob', ['a', 'b', 42]), function (r) {
t.expect(ds.call('frob', ['a', 'b', 42]).then(function (r) {
strictEqual(r.method, 'frob');
strictEqual(r.args.length, 3);
deepEqual(r.args, ['a', 'b', 42]);
ok(_.isEmpty(r.kwargs));
});
}));
});
t.test('name_get').then(function (openerp) {
var ds = new openerp.web.DataSet({session: openerp.session}, 'mod');
+144
View File
@@ -0,0 +1,144 @@
# Persian translation for openerp-web
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-03 10:24+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Persian <fa@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: 2012-10-04 05:33+0000\n"
"X-Generator: Launchpad (build 16061)\n"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:11
msgid "Calendar"
msgstr "تقویم"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:70
msgid "Filter"
msgstr "فیلتر"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:144
msgid "Today"
msgstr "امروز"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:145
msgid "Day"
msgstr "روز"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:146
msgid "Week"
msgstr "هفته"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:147
msgid "Month"
msgstr "ماه"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:148
msgid "New event"
msgstr "رویداد جدید"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:149
msgid "Save"
msgstr "ذخیره"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:150
msgid "Cancel"
msgstr "لغو کردن"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:151
msgid "Details"
msgstr "جزئیات ‌"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:152
msgid "Edit"
msgstr "ویرایش"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:153
msgid "Delete"
msgstr "حذف"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:155
msgid "Event will be deleted permanently, are you sure?"
msgstr "رویداد حذف خواهد شد. آیا مطمئن هستید؟"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:156
#: addons/web_calendar/static/src/js/calendar.js:169
msgid "Description"
msgstr "توضیحات"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:157
msgid "Time period"
msgstr "بازه زمانی"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:158
msgid "Full day"
msgstr "تمام روز"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:161
msgid "Do you want to edit the whole set of repeated events?"
msgstr "آیا میخواهید مجموعه کامل رویدادهای تکراری را ویرایش کنید؟"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:162
msgid "Repeat event"
msgstr "تکرار رویداد"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:163
msgid "Disabled"
msgstr "غیرفعال"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:164
msgid "Enabled"
msgstr "فعال"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:167
#: addons/web_calendar/static/src/js/calendar.js:175
msgid "Agenda"
msgstr "دستور کار"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:168
msgid "Date"
msgstr "تاریخ"
#. openerp-web
#: addons/web_calendar/static/src/js/calendar.js:172
msgid "Year"
msgstr "سال"
#. openerp-web
#: addons/web_calendar/static/src/xml/web_calendar.xml:5
#: addons/web_calendar/static/src/xml/web_calendar.xml:6
msgid "&nbsp;"
msgstr ""
#~ msgid "Navigator"
#~ msgstr "هدایتگر"
@@ -997,7 +997,6 @@ div.openerp .dhx_cal_editor textarea {
}
.openerp .dhx_cal_event_selected{
background-color: #757575;
color: ffffff;
}
/* Agenda week end */
.openerp .dhx_scale_bar_header {
@@ -41,7 +41,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({
},
start: function() {
this._super();
return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}, this.on_loaded);
return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}).then(this.on_loaded);
},
destroy: function() {
scheduler.clearAll();
+81
View File
@@ -0,0 +1,81 @@
# Persian translation for openerp-web
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-03 10:07+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Persian <fa@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: 2012-10-04 05:33+0000\n"
"X-Generator: Launchpad (build 16061)\n"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:11
msgid "Diagram"
msgstr "نمودار"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:165
msgid "Are you sure?"
msgstr "آیا مطمئن هستید؟"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:195
msgid ""
"Deleting this node cannot be undone.\n"
"It will also delete all connected transitions.\n"
"\n"
"Are you sure ?"
msgstr ""
"عمل حذف برگشت پذیر نیست.\n"
"همچنین تمام اتصال ها را نیز حذف میکند.\n"
"\n"
"آیا مطمئن هستید؟"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:213
msgid ""
"Deleting this transition cannot be undone.\n"
"\n"
"Are you sure ?"
msgstr ""
"حذف این انتقال برگشت پذیر نیست.\n"
"\n"
"آیا از خذف مطمئن هستید؟"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:224
#: addons/web_diagram/static/src/js/diagram.js:257
msgid "Activity"
msgstr "فعالیت"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:232
#: addons/web_diagram/static/src/js/diagram.js:296
msgid "Open: "
msgstr "باز: "
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:262
#: addons/web_diagram/static/src/js/diagram.js:314
msgid "Create:"
msgstr "ایجاد کردن"
#. openerp-web
#: addons/web_diagram/static/src/js/diagram.js:289
#: addons/web_diagram/static/src/js/diagram.js:308
msgid "Transition"
msgstr "انتقال"
#. openerp-web
#: addons/web_diagram/static/src/xml/base_diagram.xml:6
msgid "New Node"
msgstr "گره جدید"
@@ -49,5 +49,5 @@
-ms-user-select: none;
-o-user-select: none;
user-select: none;
};
}
+1 -2
View File
@@ -105,8 +105,7 @@ instance.web.DiagramView = instance.web.View.extend({
});
this.rpc(
'/web_diagram/diagram/get_diagram_info',params,
function(result) {
'/web_diagram/diagram/get_diagram_info',params).then(function(result) {
self.draw_diagram(result);
}
);
+28
View File
@@ -0,0 +1,28 @@
# Persian translation for openerp-web
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-03 10:11+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Persian <fa@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: 2012-10-04 05:33+0000\n"
"X-Generator: Launchpad (build 16061)\n"
#. openerp-web
#: addons/web_gantt/static/src/js/gantt.js:11
msgid "Gantt"
msgstr "گانت"
#. openerp-web
#: addons/web_gantt/static/src/xml/web_gantt.xml:10
msgid "Create"
msgstr "ایجاد کردن"
+98
View File
@@ -0,0 +1,98 @@
# Persian translation for openerp-web
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-03 10:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Persian <fa@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: 2012-10-04 05:33+0000\n"
"X-Generator: Launchpad (build 16061)\n"
#. openerp-web
#: addons/web_graph/static/src/js/graph.js:19
msgid "Graph"
msgstr "گراف"
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:5
msgid "Graph Options"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:7
msgid "Graph Mode"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:11
msgid "Pie"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:12
msgid "Bars"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:14
msgid "Lines"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:15
msgid "Areas"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:18
msgid "Radar"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:20
msgid "Legend"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:24
msgid "Hidden"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:25
msgid "Inside"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:26
msgid "Top"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:28
msgid "Actions"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:32
msgid "Switch Axis"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:33
msgid "Show Data"
msgstr ""
#. openerp-web
#: addons/web_graph/static/src/xml/web_graph.xml:34
msgid "Download as PNG"
msgstr ""
+66
View File
@@ -0,0 +1,66 @@
# Persian translation for openerp-web
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openerp-web package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
"PO-Revision-Date: 2012-10-03 10:14+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Persian <fa@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: 2012-10-04 05:33+0000\n"
"X-Generator: Launchpad (build 16061)\n"
#. openerp-web
#: addons/web_kanban/static/src/js/kanban.js:10
msgid "Kanban"
msgstr ""
#. openerp-web
#: addons/web_kanban/static/src/js/kanban.js:293
msgid "Undefined"
msgstr "تعریف نشده"
#. openerp-web
#: addons/web_kanban/static/src/js/kanban.js:471
msgid "Are you sure you want to delete this record ?"
msgstr "آیا از حذف این رکورد مطمئن هستید؟"
#. openerp-web
#: addons/web_kanban/static/src/js/kanban.js:839
msgid "Create: "
msgstr ""
#. openerp-web
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
msgid "Show more... ("
msgstr "نمایش بیشتر"
#. openerp-web
#: addons/web_kanban/static/src/xml/web_kanban.xml:41
msgid "remaining)"
msgstr ""
#. openerp-web
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
msgid "Add"
msgstr ""
#. openerp-web
#: addons/web_kanban/static/src/xml/web_kanban.xml:71
msgid "or"
msgstr ""
#. openerp-web
#: addons/web_kanban/static/src/xml/web_kanban.xml:72
msgid "Cancel"
msgstr ""
#~ msgid "Create"
#~ msgstr "ایجاد کردن"
+9 -9
View File
@@ -9,7 +9,6 @@
.openerp .oe_kanban_view .ui-sortable-placeholder {
border: 1px solid rgba(0, 0, 0, 0.1);
visibility: visible !important;
height: 60px !important;
}
.openerp .oe_kanban_view .ui-sortable-helper {
-moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
@@ -19,6 +18,9 @@
-webkit-transform: rotate(3deg);
-o-transform: rotate(3deg);
-ms-transform: rotate(3deg);
-webkit-transition: -webkit-transform 100ms linear;
-moz-transition: -moz-transform 100ms linear;
transition: transform 100ms linear;
}
.openerp .oe_kanban_view .oe_kanban_left {
float: left;
@@ -364,17 +366,18 @@
border-bottom-color: rgba(0, 0, 0, 0.3);
padding: 8px;
display: block;
-webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.openerp .oe_kanban_view .oe_kanban_card:hover, .openerp .oe_kanban_view .oe_kanban_quick_create:hover {
.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover {
border: 1px solid #7c7bad;
-moz-box-shadow: 0 0 4px #7c7bad;
-webkit-box-shadow: 0 0 4px #7c7bad;
-box-shadow: 0 0 4px #7c7bad;
}
.openerp .oe_kanban_view .oe_kanban_card:hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:hover .oe_dropdown_kanban > span {
.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span {
visibility: visible;
}
.openerp .oe_kanban_view .oe_kanban_card h3, .openerp .oe_kanban_view .oe_kanban_quick_create h3 {
@@ -385,9 +388,6 @@
.openerp .oe_kanban_view .oe_kanban_card h3:hover, .openerp .oe_kanban_view .oe_kanban_quick_create h3:hover {
text-decoration: none;
}
.openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban, .openerp .oe_kanban_view .oe_kanban_quick_create .oe_dropdown_kanban {
margin-top: 4px;
}
.openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban .oe_kanban_project_times li, .openerp .oe_kanban_view .oe_kanban_quick_create .oe_dropdown_kanban .oe_kanban_project_times li {
float: left;
}
@@ -480,10 +480,10 @@
white-space: nowrap;
}
.openerp .oe_kanban_view .oe_dropdown_kanban {
position: absolute;
top: -8px;
right: 3px;
float: right;
clear: both;
cursor: pointer;
margin-top: -6px;
}
.openerp .oe_kanban_view .oe_dropdown_kanban:hover {
text-decoration: none;
+15 -6
View File
@@ -37,6 +37,13 @@
-webkit-transform: $transform
-o-transform: $transform
-ms-transform: $transform
@mixin transition($transition: all 200ms linear)
-webkit-transition: $transition
-moz-transition: $transition
-ms-transition: $transition
-o-transition: $transition
transition: $transition
// }}}
.openerp .oe_kanban_view
@@ -49,10 +56,12 @@
.ui-sortable-placeholder
border: 1px solid rgba(0,0,0,0.1)
visibility: visible !important
height: 60px !important
.ui-sortable-helper
@include box-shadow(0 1px 10px rgba(0, 0, 0, 0.3))
@include transform(rotate(3deg))
-webkit-transition: -webkit-transform 100ms linear
-moz-transition: -moz-transform 100ms linear
transition: transform 100ms linear
.oe_kanban_left
float: left
.oe_kanban_right
@@ -328,8 +337,9 @@
border-bottom-color: rgba(0,0,0,0.3)
padding: 8px
display: block
-webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear
@include radius(4px)
&:hover
&:not(.ui-sortable-helper):hover
border: 1px solid #7C7BAD
@include box-shadow(0 0 4px #7C7BAD)
.oe_dropdown_kanban > span
@@ -341,7 +351,6 @@
h3:hover
text-decoration: none
.oe_dropdown_kanban
margin-top: 4px
.oe_kanban_project_times
li
float: left
@@ -404,10 +413,10 @@
// }}}
// KanbanDropDown {{{
.oe_dropdown_kanban
position: absolute
top: -8px
right: 3px
float: right
clear: both
cursor: pointer
margin-top: -6px
&:hover
text-decoration: none
.oe_dropdown_menu
@@ -309,8 +309,14 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
ui.item.find('*').on('click.prevent', function(ev) {
return false;
});
ui.placeholder.height(ui.item.height());
},
revert: 150,
beforeStop: function(event,ui){
console.log('beforeStop:',ui);
},
stop: function(event, ui) {
console.log('stop:',ui);
var record = ui.item.data('widget');
var old_index = self.currently_dragging.index;
var new_index = ui.item.index();
@@ -136,7 +136,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
var model_dataset = new instance.web.DataSetSearch(this, 'ir.model', null, null);
var view_string = "", field_name = false, self = this;
field_dataset.call( 'fields_get', [], function(fields) {
field_dataset.call( 'fields_get', []).then(function(fields) {
_.each(['name', 'x_name'], function(value) {
if (_.include(_.keys(fields), value)) {
field_name = value;
@@ -539,7 +539,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({
var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
property_to_check.push(value);
});
field_dataset.call( 'fields_get', [], function(result) {
field_dataset.call( 'fields_get', []).then(function(result) {
var fields = _.keys(result);
fields.push(" "),fields.sort();
self.on_add_node(property_to_check, fields);
-6
View File
@@ -1,6 +0,0 @@
[extractors]
qweb = npybabel:extract_qweb
javascript = npybabel:extract_javascript
[javascript: static/src/js/**.js]
[qweb: static/src/xml/**.xml]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

-2
View File
@@ -22,8 +22,6 @@ Contents:
list-view
form-notes
templates
guides/client-action
Indices and tables
-45
View File
@@ -1,45 +0,0 @@
.. highlight:: xml
Generic templates
=================
Generic template names should be prefixed by ``ui.`` to indicate their
special role.
Global Header: ``ui.Header``
----------------------------
Role
++++
This template is mostly dedicated to client actions taking over the
whole content area, and full-HTML form views (especially those opening
in the ``inline`` target).
It is used to display a buttons container (and can also be used for
status bars). The buttons should simply be placed inside the
``ui.Header`` body.
Arguments
+++++++++
The template only uses its body as argument.
Example
+++++++
::
<t t-call="ui.Header">
<button string="Apply" type="object" name="execute" class="oe_highlight"/>
or
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</t>
This block demonstrates a common pattern in OpenERP views and widgets:
a highlighted button, and a discard button styled as a link to cancel
the action:
.. image:: ./images/templates/ui.Header.*
In this case, both buttons are OpenERP action buttons.
-66
View File
@@ -1,66 +0,0 @@
#!/bin/sh
usage() {
cat << EOF
usage: $0 -a [DIR]
usage: $0 <ADDON_DIR> <OUTPUT_FILE>
OPTIONS:
-a [DIR] export the .pot files for all web addons found
at target path (default: ./addons)
-h print this message
EOF
exit 0
}
do_all=
while getopts "a" opt
do
case "$opt" in
a)
do_all=true;;
h)
usage;;
\?)
usage;;
esac
done
shift $((OPTIND-1))
if [ -n "$do_all" ]
then
if [ "x$(which msgcat)" = "x" ]
then
echo "The msgcat command from the gettext tools is required in the PATH."
echo "On a Debian/Ubuntu system you may install gettext via 'sudo apt-get install gettext'"
exit 1
fi
echo "Extracting all web addons translations"
executable=$0
target_dir=${1:-./addons}
echo "Using target dir: ${target_dir}"
for mod in $(find ${target_dir} -type d -name 'static' -exec sh -c 'basename $(dirname {})' \;); do
echo ${mod}
mod_pot=${target_dir}/${mod}/i18n/${mod}.pot
web_pot=${mod_pot}.web
mkdir -p $(dirname ${web_pot})
$executable ${target_dir}/${mod} ${web_pot}
if [ -f "${mod_pot}" ]; then
echo "Merging with existing PO file: ${mod_pot}"
msgcat --force-po -o "${mod_pot}.tmp" ${mod_pot} ${web_pot}
mv ${mod_pot}.tmp ${mod_pot}
rm ${web_pot}
else
echo "Renaming to final PO file: ${mod_pot}"
mv ${web_pot} ${mod_pot}
fi
done
elif [ -n "$2" ]
then
./npybabel.py extract -F babel.cfg -o $2 -k _t -k _lt --no-default-keywords $1
else
usage
fi
-140
View File
@@ -1,140 +0,0 @@
#!/usr/bin/env python
# EASY-INSTALL-ENTRY-SCRIPT: 'Babel==0.9.6','console_scripts','pybabel'
__requires__ = 'Babel==0.9.6'
import sys
from pkg_resources import load_entry_point
import re
import json
from lxml import etree as elt
from babel.messages import extract
if __name__ == '__main__':
sys.exit(
load_entry_point('Babel==0.9.6', 'console_scripts', 'pybabel')()
)
XMLJS_EXPR = re.compile(r"""(?:\_t *\( *((?:"(?:[^"\\]|\\.)*")|(?:'(?:[^'\\]|\\.)*')) *\))""")
TRANSLATION_FLAG_COMMENT = "openerp-web"
# List of etree._Element subclasses that we choose to ignore when parsing XML.
# We include the *Base ones just in case, currently they seem to be subclasses of the _* ones.
SKIPPED_ELEMENT_TYPES = (elt._Comment, elt._ProcessingInstruction, elt.CommentBase, elt.PIBase)
def extract_xmljs(fileobj, keywords, comment_tags, options):
"""Extract messages from Javascript code embedded into XML documents.
This complements the ``extract_javascript`` extractor which works
only on pure .js files, and the``extract_qweb`` extractor, which only
extracts XML text.
:param fileobj: the file-like object the messages should be extracted
from
:param keywords: a list of keywords (i.e. function names) that should
be recognized as translation functions
:param comment_tags: a list of translator tags to search for and
include in the results
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)``
tuples
:rtype: ``iterator``
"""
assert False, """ the XMLJS extractor does not work and was removed:
* Babel apparently does not accept two extractors for the same set of files
so it would not run the xmljs extractor at all, extraction of JS stuff
needs to be done from the XML extractor
* The regex above fails up if there are back-slashed quotes within the
translatable string (the string marked with _t), it just won't match the
string
* While extraction succeeds on XML entities (e.g. &quot;), translation
matching will fail if those entities are kept in the PO msgid as the
XML parser will get an un-escaped string, without those entities (so a
text extractor will extract ``Found match &quot;%s&quot;``, but the msgid
of the PO file must be ``Found match "%s"`` or the translation will fail
* single-quoted strings are not valid JSON string, so single-quoted strings
matched by the regex (likely since XML attributes are double-quoted,
single quotes within them don't have to be escaped) will blow up when
json-parsed for their content
I think that's about it.
If this extractor is reimplemented, it should be integrated into
extract_qweb, either in the current pass (probably not a good idea) or as
a separate pass using iterparse, matching either elements with t-js or
some other kinds of t-* directives (@t-esc, @t-raw, @t-att, others?),
shove the attribute content into a StringIO and pass *that* to Babel's
own extract_javascript; then add a line offset in order to yield the
correct line number.
"""
content = fileobj.read()
found = XMLJS_EXPR.finditer(content)
index = 0
line_nbr = 0
for f in found:
msg = f.group(1)
msg = json.loads(msg)
while index < f.start():
if content[index] == "\n":
line_nbr += 1
index += 1
yield (line_nbr, None, msg, [TRANSLATION_FLAG_COMMENT])
def extract_qweb(fileobj, keywords, comment_tags, options):
"""Extract messages from qweb template files.
:param fileobj: the file-like object the messages should be extracted
from
:param keywords: a list of keywords (i.e. function names) that should
be recognized as translation functions
:param comment_tags: a list of translator tags to search for and
include in the results
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)``
tuples
:rtype: ``iterator``
"""
result = []
def handle_text(text, lineno):
text = (text or "").strip()
if len(text) > 1: # Avoid mono-char tokens like ':' ',' etc.
result.append((lineno, None, text, [TRANSLATION_FLAG_COMMENT]))
# not using elementTree.iterparse because we need to skip sub-trees in case
# the ancestor element had a reason to be skipped
def iter_elements(current_element):
for el in current_element:
if isinstance(el, SKIPPED_ELEMENT_TYPES): continue
if "t-js" not in el.attrib and \
not ("t-jquery" in el.attrib and "t-operation" not in el.attrib) and \
not ("t-translation" in el.attrib and el.attrib["t-translation"].strip() == "off"):
handle_text(el.text, el.sourceline)
for att in ('title', 'alt', 'label', 'placeholder'):
if att in el.attrib:
handle_text(el.attrib[att], el.sourceline)
iter_elements(el)
handle_text(el.tail, el.sourceline)
tree = elt.parse(fileobj)
iter_elements(tree.getroot())
return result
def extract_javascript(fileobj, keywords, comment_tags, options):
"""Extract messages from Javascript source files. This extractor delegates
to babel's buit-in javascript extractor, but adds a special comment
used as a flag to identify web translations.
:param fileobj: the file-like object the messages should be extracted
from
:param keywords: a list of keywords (i.e. function names) that should
be recognized as translation functions
:param comment_tags: a list of translator tags to search for and
include in the results
:param options: a dictionary of additional options (optional)
:return: an iterator over ``(lineno, funcname, message, comments)``
tuples
:rtype: ``iterator``
"""
for (message_lineno, funcname, messages, comments) in \
extract.extract_javascript(fileobj, keywords, comment_tags, options):
comments.append(TRANSLATION_FLAG_COMMENT)
yield (message_lineno, funcname, messages, comments)