From aa3c03b85c91c26a645ac1290092e56b9dd92c44 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 24 Sep 2012 14:28:34 +0200 Subject: [PATCH 01/22] [ADD] web_translate_dialog_page module (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 3.1.1) --- web_translate_dialog_page/__init__.py | 23 +++ web_translate_dialog_page/__openerp__.py | 53 ++++++ web_translate_dialog_page/orm.py | 173 ++++++++++++++++++ .../static/src/css/base.css | 4 + .../src/js/web_translate_dialog_page.js | 141 ++++++++++++++ .../static/src/xml/base.xml | 32 ++++ 6 files changed, 426 insertions(+) create mode 100644 web_translate_dialog_page/__init__.py create mode 100644 web_translate_dialog_page/__openerp__.py create mode 100644 web_translate_dialog_page/orm.py create mode 100644 web_translate_dialog_page/static/src/css/base.css create mode 100644 web_translate_dialog_page/static/src/js/web_translate_dialog_page.js create mode 100644 web_translate_dialog_page/static/src/xml/base.xml diff --git a/web_translate_dialog_page/__init__.py b/web_translate_dialog_page/__init__.py new file mode 100644 index 00000000..f1454453 --- /dev/null +++ b/web_translate_dialog_page/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier +# Copyright 2012 Camptocamp SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import orm + diff --git a/web_translate_dialog_page/__openerp__.py b/web_translate_dialog_page/__openerp__.py new file mode 100644 index 00000000..01fa58ee --- /dev/null +++ b/web_translate_dialog_page/__openerp__.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier +# Copyright 2012 Camptocamp SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + "name": "Web Translate Dialog in Page view", + "category": "Hidden", + "description": + """ +Replace the standard translation dialog by an alternative one: + + * Hide the buttons at right of the fields and instead + * Add a "Translate" button in page view, next to "Edit" + * The translation dialog displays empty fields for the untranslated fields, + instead of the source values. + * Autosize the text boxes + + """, + "version": "1.0", + "depends": [ + 'web', + 'web_textarea_autosize', + ], + 'js': [ + 'static/src/js/web_translate_dialog_page.js', + ], + 'css' : [ + 'static/src/css/base.css', + ], + 'qweb' : [ + "static/src/xml/base.xml", + ], + 'auto_install': False, +} + + diff --git a/web_translate_dialog_page/orm.py b/web_translate_dialog_page/orm.py new file mode 100644 index 00000000..03b1f742 --- /dev/null +++ b/web_translate_dialog_page/orm.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier +# Copyright 2012 Camptocamp SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import openerp.osv.orm + + +# Check if we can remove the monkey-patching once the bug: +# https://bugs.launchpad.net/bugs/1053970 +# is resolved. +original_create = openerp.osv.orm.BaseModel.create +def create(self, cr, uid, vals, context=None): + """ + Monkey-patch the create of BaseModel in order to create translation lines + on translatable fields. + + Actually, the original behavior is quite strange. Here it is: + I'm logged in with en_US language. + I create a record, with a (translatable) title 'My title' + I check the source in database (table of the object), that's 'My title' + I check the translation lines for the en_US language, no line + I write on my record the title 'My title updated' + I check the source in database, that's 'My title updated' + I check the translation lines for the en_US language, no line + + I'm logged in with fr_FR language + I create a record, with a (translatable) title 'Mon titre' + I check the source in database (table of the object), that's 'Mon titre' + I check the translation lines for the fr_FR language, no line + I write on my record the title 'Mon titre mis à jour' + I check the source in database, that's 'Mon titre' (unchanged) + I check the translation lines for the fr_FR language, I have a line with 'Mon titre mis à jour' + + As you can see, the write method create translation lines for other + languages than en_US, that's correct. The create method does not, + and it has to do it. + + OpenERP seems to assume that the en_US should be the reference + language, so lets assume it completely, and generate the french + translation line directly when we enter the value. + + That's weird, because, if I create a record in french, the source + will be the french value (of course), but programmatically, I do not + have any means to know that someone entered a french translation. + + A simple scenario where the bug will occurs: + + User A is logged in with fr_FR + User A creates a product with a name 'Marteau' + User B is logged in with en_US + User B modifies the product 'Marteau' to be 'Hammer' + => The french translation is lost. + + It won't occurs in this slightly modified scenario: + + User A is logged in with fr_FR + User A creates a product with a name 'Martea' (typo) + User A modifies the product 'Martea' to be 'Marteau' + User B is logged in with en_US + User B modifies the product 'Marteau' to be 'Hammer' + => The french translation isn't lost, because the write has + correctly generated the french translation line + + + Bug reported : https://bugs.launchpad.net/bugs/1053970 + + """ + if context is None: + context = {} + + record_id = original_create(self, cr, uid, vals, context=context) + + if context.get('lang') and context['lang'] != 'en_US': + translate_fields = [field for field in vals if + field in self._columns and + self._columns[field].translate and + self._columns[field]._classic_write and + not hasattr(self._columns[field], '_fnct_inv')] + + for field in translate_fields: + src_trans = self.read(cr, uid, record_id, [field])[field] + if not src_trans: + src_trans = vals[field] + # Inserting value to DB + self.write(cr, uid, record_id, {field: vals[field]}) + self.pool.get('ir.translation')._set_ids( + cr, uid, + self._name + ',' + field, + 'model', + context['lang'], + [record_id], + vals[field], + src_trans) + + return record_id + +openerp.osv.orm.BaseModel.create = create + + +# add the method in the orm so we can use it from the TranslateDialog of the +# webclient +def read_translations(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + """ Read records with given ids with the given fields, if a field is not + translated, its value will be False instead of the source language's value. + + :param fields: optional list of field names to return (default: all fields would be returned) + :type fields: list (example ['field_name_1', ...]) + :return: list of dictionaries((dictionary per record asked)) with requested field values + :rtype: [{‘name_of_the_field’: value, ...}, ...] + :raise AccessError: * if user has no read rights on the requested object + * if user tries to bypass access rules for read on the requested object + + """ + + if context is None: + context = {} + self.check_read(cr, user) + if not fields: + fields = list(set(self._columns.keys() + self._inherit_fields.keys())) + if isinstance(ids, (int, long)): + select = [ids] + else: + select = ids + select = map(lambda x: isinstance(x, dict) and x['id'] or x, select) + result = self._read_flat(cr, user, select, fields, context, load) + + fields_pre = [f for f in fields if + (f in self._columns and + getattr(self._columns[f], '_classic_write'))] + \ + self._inherits.values() + + if context.get('lang') and context['lang'] != 'en_US': + for f in fields_pre: + if self._columns[f].translate: + res_ids = [x['id'] for x in result] + res_trans = self.pool.get('ir.translation')._get_ids( + cr, user, + self._name + ',' + f, + 'model', + context['lang'], + res_ids) + for r in result: + if not res_trans.get(r['id']): + r[f] = None + + for r in result: + for key, v in r.iteritems(): + if v is None: + r[key] = False + + if isinstance(ids, (int, long, dict)): + return result and result[0] or False + return result + +openerp.osv.orm.BaseModel.read_translations = read_translations + diff --git a/web_translate_dialog_page/static/src/css/base.css b/web_translate_dialog_page/static/src/css/base.css new file mode 100644 index 00000000..8f0eaeca --- /dev/null +++ b/web_translate_dialog_page/static/src/css/base.css @@ -0,0 +1,4 @@ +.oe_field_translate { + visibility: hidden; +} + diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js new file mode 100644 index 00000000..3dd918a8 --- /dev/null +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -0,0 +1,141 @@ +openerp.web_translate_dialog_page = function (openerp) { + + var _t = openerp.web._t; + + openerp.web.PageView.include({ + on_loaded: function(data) { + this._super(data); + this.$form_header.find('button.oe_form_button_translate').click(this.on_button_translate); + }, + on_button_translate: function() { + var self = this; + $.when(this.has_been_loaded).then(function() { + self.open_translate_dialog(this); + }); + } + }); + + openerp.web.View.include({ + // Replace the translation dialog by the new one + open_translate_dialog: function(field) { + if (!this.translate_dialog) { + this.translate_dialog = new openerp.web_translate_dialog_page.TranslateDialogPage(this).start(); + } + this.translate_dialog.open(field); + } + }); + + // completely redefine the translation dialog because we can + // not completely tie the standard one to our needs by sub-classing + openerp.web_translate_dialog_page.TranslateDialogPage = openerp.web.Dialog.extend({ + dialog_title: {toString: function () { return _t("Translations"); }}, + init: function(view) { + this.view_language = view.session.user_context.lang; + this['on_button_' + _t("Save")] = this.on_btn_save; + this['on_button_' + _t("Close")] = this.on_btn_close; + this._super(view, { + width: '80%', + height: '90%' + }); + this.view = view; + this.view_type = view.fields_view.type || ''; + this.$fields_form = null; + this.$view_form = null; + this.$sidebar_form = null; + this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name;}); + this.languages = null; + this.languages_loaded = $.Deferred(); + (new openerp.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), + [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded); + }, + on_languages_loaded: function(langs) { + this.languages = langs; + this.languages_loaded.resolve(); + }, + start: function() { + var self = this; + this._super(); + $.when(this.languages_loaded).then(function() { + self.$element.html(QWeb.render('TranslateDialogPage', { widget: self })); + self.$fields_form = self.$element.find('.oe_translation_form'); + self.$fields_form.find('.oe_trad_field').change(function() { + $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); + }); + var $textarea = self.$fields_form.find('textarea.oe_trad_field'); + $textarea.autosize(); + $textarea.css({minHeight:'100px'}); + }); + return this; + }, + + // use a `read_translations` method instead of a `read` + // this latter leave the fields empty if there is no + // translation for a field instead of taking the src field + do_load_fields_values: function(callback) { + var self = this, + deffered = []; + + this.$fields_form.find('.oe_trad_field').val('').removeClass('touched'); + _.each(self.languages, function(lg) { + var deff = $.Deferred(); + deffered.push(deff); + var callback = function(values) { + _.each(self.translatable_fields_keys, function(f) { + self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || '').attr('data-value', values[0][f] || ''); + }); + deff.resolve(); + }; + self.view.dataset.call( + 'read_translations', + [[self.view.datarecord.id], + self.translatable_fields_keys, + self.view.dataset.get_context({ + 'lang': lg.code + })], callback); + }); + $.when.apply(null, deffered).then(callback); + }, + open: function(field) { + var self = this, + sup = this._super; + $.when(this.languages_loaded).then(function() { + if (self.view.translatable_fields && self.view.translatable_fields.length) { + self.do_load_fields_values(function() { + sup.call(self); + $(window).resize(); + }); + } else { + sup.call(self); + } + }); + }, + on_btn_save: function() { + var trads = {}, + self = this, + trads_mutex = new $.Mutex(); + self.$fields_form.find('.oe_trad_field.touched').each(function() { + var field = $(this).attr('name').split('-'); + if (!trads[field[0]]) { + trads[field[0]] = {}; + } + trads[field[0]][field[1]] = $(this).val(); + }); + _.each(trads, function(data, code) { + if (code === self.view_language) { + _.each(data, function(value, field) { + self.view.fields[field].set_value(value); + }); + } + trads_mutex.exec(function() { + return new openerp.web.DataSet(self, self.view.dataset.model, self.view.dataset.get_context()).write(self.view.datarecord.id, data, { context : { 'lang': code }}); + }); + }); + this.close(); + }, + on_btn_close: function() { + this.close(); + } + + }); +}; + diff --git a/web_translate_dialog_page/static/src/xml/base.xml b/web_translate_dialog_page/static/src/xml/base.xml new file mode 100644 index 00000000..c834bd10 --- /dev/null +++ b/web_translate_dialog_page/static/src/xml/base.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + +
+
Field
+
+
+
+ + + + +
+
+ + + + + + + +
+ From 47a96f6c514fb01ecd06d6a76ff4d7a7a0fd9a55 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Wed, 26 Sep 2012 09:33:12 +0200 Subject: [PATCH 02/22] [IMP] do not get the fields when that is not needed (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 3.1.7) --- web_translate_dialog_page/orm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web_translate_dialog_page/orm.py b/web_translate_dialog_page/orm.py index 03b1f742..8ede8518 100644 --- a/web_translate_dialog_page/orm.py +++ b/web_translate_dialog_page/orm.py @@ -141,12 +141,12 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla select = map(lambda x: isinstance(x, dict) and x['id'] or x, select) result = self._read_flat(cr, user, select, fields, context, load) - fields_pre = [f for f in fields if - (f in self._columns and - getattr(self._columns[f], '_classic_write'))] + \ - self._inherits.values() - if context.get('lang') and context['lang'] != 'en_US': + fields_pre = [f for f in fields if + (f in self._columns and + getattr(self._columns[f], '_classic_write'))] + \ + self._inherits.values() + for f in fields_pre: if self._columns[f].translate: res_ids = [x['id'] for x in result] From 086d67f64be4bbea873e58e1581cd43774824763 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 24 Sep 2012 14:47:09 +0200 Subject: [PATCH 03/22] [ADD] web_translate_dialog_page: alternative translation dialog, opened from a button in page view. It display empty fields for untranslated fields. (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 5) --- web_translate_dialog_page/orm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web_translate_dialog_page/orm.py b/web_translate_dialog_page/orm.py index 8ede8518..03b1f742 100644 --- a/web_translate_dialog_page/orm.py +++ b/web_translate_dialog_page/orm.py @@ -141,12 +141,12 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla select = map(lambda x: isinstance(x, dict) and x['id'] or x, select) result = self._read_flat(cr, user, select, fields, context, load) - if context.get('lang') and context['lang'] != 'en_US': - fields_pre = [f for f in fields if - (f in self._columns and - getattr(self._columns[f], '_classic_write'))] + \ - self._inherits.values() + fields_pre = [f for f in fields if + (f in self._columns and + getattr(self._columns[f], '_classic_write'))] + \ + self._inherits.values() + if context.get('lang') and context['lang'] != 'en_US': for f in fields_pre: if self._columns[f].translate: res_ids = [x['id'] for x in result] From cf18060ad227c27e51454d7b80a95180ad22595d Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Wed, 26 Sep 2012 09:33:39 +0200 Subject: [PATCH 04/22] [IMP] do not get the fields when that is not needed (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 8) --- web_translate_dialog_page/orm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web_translate_dialog_page/orm.py b/web_translate_dialog_page/orm.py index 03b1f742..8ede8518 100644 --- a/web_translate_dialog_page/orm.py +++ b/web_translate_dialog_page/orm.py @@ -141,12 +141,12 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla select = map(lambda x: isinstance(x, dict) and x['id'] or x, select) result = self._read_flat(cr, user, select, fields, context, load) - fields_pre = [f for f in fields if - (f in self._columns and - getattr(self._columns[f], '_classic_write'))] + \ - self._inherits.values() - if context.get('lang') and context['lang'] != 'en_US': + fields_pre = [f for f in fields if + (f in self._columns and + getattr(self._columns[f], '_classic_write'))] + \ + self._inherits.values() + for f in fields_pre: if self._columns[f].translate: res_ids = [x['id'] for x in result] From 557baeea4b05c708c3c1b3fdd4bb380dd98592c3 Mon Sep 17 00:00:00 2001 From: "Guewen Baconnier @ Camptocamp" Date: Mon, 8 Oct 2012 08:04:42 +0200 Subject: [PATCH 05/22] [IMP] web_translate_dialog_page: animate autosize of textareas (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 12) --- .../static/src/js/web_translate_dialog_page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js index 3dd918a8..ff0ad999 100644 --- a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -62,7 +62,7 @@ openerp.web_translate_dialog_page = function (openerp) { $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); }); var $textarea = self.$fields_form.find('textarea.oe_trad_field'); - $textarea.autosize(); + $textarea.addClass('autosizeAnimated').autosize({append: "\n"}); $textarea.css({minHeight:'100px'}); }); return this; From d295150d56a483f6a520ba6a60996d02d92aa646 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 18 Dec 2012 11:51:31 +0100 Subject: [PATCH 06/22] [FIX] web_translate_dialog_page - QWeb was referencing a global variable of base_geoengine module (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 12.1.1) --- .../static/src/js/web_translate_dialog_page.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js index ff0ad999..9aee3941 100644 --- a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -54,6 +54,7 @@ openerp.web_translate_dialog_page = function (openerp) { }, start: function() { var self = this; + var QWeb = openerp.web.qweb; this._super(); $.when(this.languages_loaded).then(function() { self.$element.html(QWeb.render('TranslateDialogPage', { widget: self })); From 59cb3403d874738a74bb910962403cdcf6fe1c32 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Tue, 18 Dec 2012 18:27:35 +0100 Subject: [PATCH 07/22] [IMP] web_translate_dialog_page - move QWeb variable at top level of module namespace for convention (/home/gbaconnier/code/openerp/c2c-web-addons/lp6.1-main rev 12.1.2) --- .../static/src/js/web_translate_dialog_page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js index 9aee3941..d5e9b8c8 100644 --- a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -1,6 +1,7 @@ openerp.web_translate_dialog_page = function (openerp) { var _t = openerp.web._t; + var QWeb = openerp.web.qweb; openerp.web.PageView.include({ on_loaded: function(data) { @@ -54,7 +55,6 @@ openerp.web_translate_dialog_page = function (openerp) { }, start: function() { var self = this; - var QWeb = openerp.web.qweb; this._super(); $.when(this.languages_loaded).then(function() { self.$element.html(QWeb.render('TranslateDialogPage', { widget: self })); From 6854196cfd40d5fea2d7b6bdf54a2861d8294419 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 6 Dec 2013 09:12:43 +0100 Subject: [PATCH 08/22] [CHG] restored the translate dialog, still needs UI tweaks --- web_translate_dialog_page/__init__.py | 3 +- web_translate_dialog_page/__openerp__.py | 1 - web_translate_dialog_page/orm.py | 108 +------------ .../static/src/css/base.css | 3 +- .../src/js/web_translate_dialog_page.js | 143 +++++++++--------- .../static/src/xml/base.xml | 15 +- 6 files changed, 93 insertions(+), 180 deletions(-) diff --git a/web_translate_dialog_page/__init__.py b/web_translate_dialog_page/__init__.py index f1454453..06331652 100644 --- a/web_translate_dialog_page/__init__.py +++ b/web_translate_dialog_page/__init__.py @@ -19,5 +19,4 @@ # ############################################################################## -import orm - +from . import orm diff --git a/web_translate_dialog_page/__openerp__.py b/web_translate_dialog_page/__openerp__.py index 01fa58ee..121583a5 100644 --- a/web_translate_dialog_page/__openerp__.py +++ b/web_translate_dialog_page/__openerp__.py @@ -36,7 +36,6 @@ Replace the standard translation dialog by an alternative one: "version": "1.0", "depends": [ 'web', - 'web_textarea_autosize', ], 'js': [ 'static/src/js/web_translate_dialog_page.js', diff --git a/web_translate_dialog_page/orm.py b/web_translate_dialog_page/orm.py index 8ede8518..f8e395d3 100644 --- a/web_translate_dialog_page/orm.py +++ b/web_translate_dialog_page/orm.py @@ -2,7 +2,7 @@ ############################################################################## # # Author: Guewen Baconnier -# Copyright 2012 Camptocamp SA +# Copyright 2012-2013 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,100 +22,8 @@ import openerp.osv.orm -# Check if we can remove the monkey-patching once the bug: -# https://bugs.launchpad.net/bugs/1053970 -# is resolved. -original_create = openerp.osv.orm.BaseModel.create -def create(self, cr, uid, vals, context=None): - """ - Monkey-patch the create of BaseModel in order to create translation lines - on translatable fields. - - Actually, the original behavior is quite strange. Here it is: - I'm logged in with en_US language. - I create a record, with a (translatable) title 'My title' - I check the source in database (table of the object), that's 'My title' - I check the translation lines for the en_US language, no line - I write on my record the title 'My title updated' - I check the source in database, that's 'My title updated' - I check the translation lines for the en_US language, no line - - I'm logged in with fr_FR language - I create a record, with a (translatable) title 'Mon titre' - I check the source in database (table of the object), that's 'Mon titre' - I check the translation lines for the fr_FR language, no line - I write on my record the title 'Mon titre mis à jour' - I check the source in database, that's 'Mon titre' (unchanged) - I check the translation lines for the fr_FR language, I have a line with 'Mon titre mis à jour' - - As you can see, the write method create translation lines for other - languages than en_US, that's correct. The create method does not, - and it has to do it. - - OpenERP seems to assume that the en_US should be the reference - language, so lets assume it completely, and generate the french - translation line directly when we enter the value. - - That's weird, because, if I create a record in french, the source - will be the french value (of course), but programmatically, I do not - have any means to know that someone entered a french translation. - - A simple scenario where the bug will occurs: - - User A is logged in with fr_FR - User A creates a product with a name 'Marteau' - User B is logged in with en_US - User B modifies the product 'Marteau' to be 'Hammer' - => The french translation is lost. - - It won't occurs in this slightly modified scenario: - - User A is logged in with fr_FR - User A creates a product with a name 'Martea' (typo) - User A modifies the product 'Martea' to be 'Marteau' - User B is logged in with en_US - User B modifies the product 'Marteau' to be 'Hammer' - => The french translation isn't lost, because the write has - correctly generated the french translation line - - - Bug reported : https://bugs.launchpad.net/bugs/1053970 - - """ - if context is None: - context = {} - - record_id = original_create(self, cr, uid, vals, context=context) - - if context.get('lang') and context['lang'] != 'en_US': - translate_fields = [field for field in vals if - field in self._columns and - self._columns[field].translate and - self._columns[field]._classic_write and - not hasattr(self._columns[field], '_fnct_inv')] - - for field in translate_fields: - src_trans = self.read(cr, uid, record_id, [field])[field] - if not src_trans: - src_trans = vals[field] - # Inserting value to DB - self.write(cr, uid, record_id, {field: vals[field]}) - self.pool.get('ir.translation')._set_ids( - cr, uid, - self._name + ',' + field, - 'model', - context['lang'], - [record_id], - vals[field], - src_trans) - - return record_id - -openerp.osv.orm.BaseModel.create = create - - # add the method in the orm so we can use it from the TranslateDialog of the -# webclient +# webclient instead of the normal read def read_translations(self, cr, user, ids, fields=None, context=None, load='_classic_read'): """ Read records with given ids with the given fields, if a field is not translated, its value will be False instead of the source language's value. @@ -131,9 +39,8 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla if context is None: context = {} - self.check_read(cr, user) - if not fields: - fields = list(set(self._columns.keys() + self._inherit_fields.keys())) + self.check_access_rights(cr, user, 'read') + fields = self.check_field_access_rights(cr, user, 'read', fields) if isinstance(ids, (int, long)): select = [ids] else: @@ -143,9 +50,9 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla if context.get('lang') and context['lang'] != 'en_US': fields_pre = [f for f in fields if - (f in self._columns and - getattr(self._columns[f], '_classic_write'))] + \ - self._inherits.values() + (f in self._columns and + getattr(self._columns[f], '_classic_write'))] + \ + self._inherits.values() for f in fields_pre: if self._columns[f].translate: @@ -170,4 +77,3 @@ def read_translations(self, cr, user, ids, fields=None, context=None, load='_cla return result openerp.osv.orm.BaseModel.read_translations = read_translations - diff --git a/web_translate_dialog_page/static/src/css/base.css b/web_translate_dialog_page/static/src/css/base.css index 8f0eaeca..29220ff9 100644 --- a/web_translate_dialog_page/static/src/css/base.css +++ b/web_translate_dialog_page/static/src/css/base.css @@ -1,4 +1,3 @@ .oe_field_translate { - visibility: hidden; + /* visibility: hidden; */ } - diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js index d5e9b8c8..18144f27 100644 --- a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -1,90 +1,95 @@ -openerp.web_translate_dialog_page = function (openerp) { +openerp.web_translate_dialog_page = function (instance) { - var _t = openerp.web._t; - var QWeb = openerp.web.qweb; + "use strict"; - openerp.web.PageView.include({ - on_loaded: function(data) { + var QWeb = instance.web.qweb, + _t = instance.web._t, + _lt = instance.web._lt; + + instance.web.FormView.include({ + load_form: function(data) { this._super(data); - this.$form_header.find('button.oe_form_button_translate').click(this.on_button_translate); + this.$buttons.on('click', '.oe_form_button_translate', + this.guard_active(this.on_button_translate)); }, on_button_translate: function() { var self = this; $.when(this.has_been_loaded).then(function() { self.open_translate_dialog(this); }); - } + }, }); - openerp.web.View.include({ - // Replace the translation dialog by the new one - open_translate_dialog: function(field) { - if (!this.translate_dialog) { - this.translate_dialog = new openerp.web_translate_dialog_page.TranslateDialogPage(this).start(); - } - this.translate_dialog.open(field); + instance.web.View.include({ + open_translate_dialog: function() { + new instance.web_translate_dialog_page.TranslateDialogPage(this).open(); } }); - // completely redefine the translation dialog because we can - // not completely tie the standard one to our needs by sub-classing - openerp.web_translate_dialog_page.TranslateDialogPage = openerp.web.Dialog.extend({ + instance.web_translate_dialog_page.TranslateDialogPage = instance.web.Dialog.extend({ + template: "TranslateDialogPage", dialog_title: {toString: function () { return _t("Translations"); }}, - init: function(view) { - this.view_language = view.session.user_context.lang; - this['on_button_' + _t("Save")] = this.on_btn_save; - this['on_button_' + _t("Close")] = this.on_btn_close; - this._super(view, { - width: '80%', - height: '90%' - }); - this.view = view; - this.view_type = view.fields_view.type || ''; - this.$fields_form = null; + init: function(parent, options, content) { + this._super(parent, options, content); + this.view_language = this.session.user_context.lang; + this.view = parent; + this.view_type = parent.fields_view.type || ''; this.$view_form = null; this.$sidebar_form = null; this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name;}); this.languages = null; this.languages_loaded = $.Deferred(); - (new openerp.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), - [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded); + (new instance.web.DataSetSearch(this, + 'res.lang', + this.view.dataset.get_context(), + [['translatable', '=', '1']])) + .read_slice(['code', 'name'], { sort: 'id' }) + .then(this.on_languages_loaded); }, on_languages_loaded: function(langs) { this.languages = langs; this.languages_loaded.resolve(); }, - start: function() { - var self = this; - this._super(); + open: function() { + var self = this, + sup = this._super; + // the template needs the languages $.when(this.languages_loaded).then(function() { - self.$element.html(QWeb.render('TranslateDialogPage', { widget: self })); - self.$fields_form = self.$element.find('.oe_translation_form'); - self.$fields_form.find('.oe_trad_field').change(function() { - $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); - }); - var $textarea = self.$fields_form.find('textarea.oe_trad_field'); - $textarea.addClass('autosizeAnimated').autosize({append: "\n"}); - $textarea.css({minHeight:'100px'}); + // if (self.view.translatable_fields && self.view.translatable_fields.length) { + return sup.call(self); }); - return this; }, + start: function() { + var self = this; + self.$el.find('.oe_trad_field').change(function() { + $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); + }); + this.$buttons.html(QWeb.render("TranslateDialogPage.buttons")); + this.$buttons.find(".oe_form_translate_dialog_save_button").click(function(){ + self.on_button_save(); + self.on_button_close(); + }); + this.$buttons.find(".oe_form_translate_dialog_cancel_button").click(function(){ + self.on_button_close(); + }); + self.do_load_fields_values(); + // var $textarea = self.$el.find('textarea.oe_trad_field'); + // $textarea.addClass('autosizeAnimated').autosize({append: "\n"}); + // $textarea.css({minHeight:'100px'}); + }, // use a `read_translations` method instead of a `read` // this latter leave the fields empty if there is no // translation for a field instead of taking the src field do_load_fields_values: function(callback) { var self = this, - deffered = []; + deferred = []; - this.$fields_form.find('.oe_trad_field').val('').removeClass('touched'); + this.$el.find('.oe_trad_field').val('').removeClass('touched'); _.each(self.languages, function(lg) { var deff = $.Deferred(); - deffered.push(deff); + deferred.push(deff); var callback = function(values) { - _.each(self.translatable_fields_keys, function(f) { - self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || '').attr('data-value', values[0][f] || ''); - }); - deff.resolve(); }; self.view.dataset.call( 'read_translations', @@ -92,29 +97,22 @@ openerp.web_translate_dialog_page = function (openerp) { self.translatable_fields_keys, self.view.dataset.get_context({ 'lang': lg.code - })], callback); + })]).done(function (values) { + _.each(self.translatable_fields_keys, function(f) { + self.$el.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]') + .val(values[0][f] || '') + .attr('data-value', values[0][f] || ''); + }); + deff.resolve(); + }); }); - $.when.apply(null, deffered).then(callback); + return deferred; }, - open: function(field) { - var self = this, - sup = this._super; - $.when(this.languages_loaded).then(function() { - if (self.view.translatable_fields && self.view.translatable_fields.length) { - self.do_load_fields_values(function() { - sup.call(self); - $(window).resize(); - }); - } else { - sup.call(self); - } - }); - }, - on_btn_save: function() { + on_button_save: function() { var trads = {}, self = this, trads_mutex = new $.Mutex(); - self.$fields_form.find('.oe_trad_field.touched').each(function() { + self.$el.find('.oe_trad_field.touched').each(function() { var field = $(this).attr('name').split('-'); if (!trads[field[0]]) { trads[field[0]] = {}; @@ -128,15 +126,22 @@ openerp.web_translate_dialog_page = function (openerp) { }); } trads_mutex.exec(function() { - return new openerp.web.DataSet(self, self.view.dataset.model, self.view.dataset.get_context()).write(self.view.datarecord.id, data, { context : { 'lang': code }}); + return new instance.web.DataSet(self, self.view.dataset.model, self.view.dataset.get_context()).write(self.view.datarecord.id, data, { context : { 'lang': code }}); }); }); this.close(); }, - on_btn_close: function() { + on_button_close: function() { this.close(); } }); + + instance.web.form.AbstractField.include({ + on_translate: function() { + // the image next to the fields opens the translate dialog + this.view.open_translate_dialog(); + }, + }); }; diff --git a/web_translate_dialog_page/static/src/xml/base.xml b/web_translate_dialog_page/static/src/xml/base.xml index c834bd10..10dad07d 100644 --- a/web_translate_dialog_page/static/src/xml/base.xml +++ b/web_translate_dialog_page/static/src/xml/base.xml @@ -15,18 +15,23 @@ - - + + - - + + + + + +
+ - +
From 47bd0ecf3f9a10038b559377f2e8fead25acf997 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 6 Dec 2013 10:58:18 +0100 Subject: [PATCH 09/22] [ADD] widget for html fields --- .../src/js/web_translate_dialog_page.js | 41 ++++++++++++++++--- .../static/src/xml/base.xml | 3 ++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js index 18144f27..c0b689f4 100644 --- a/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js +++ b/web_translate_dialog_page/static/src/js/web_translate_dialog_page.js @@ -61,7 +61,7 @@ openerp.web_translate_dialog_page = function (instance) { }, start: function() { var self = this; - self.$el.find('.oe_trad_field').change(function() { + this.$el.find('.oe_trad_field').change(function() { $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); }); this.$buttons.html(QWeb.render("TranslateDialogPage.buttons")); @@ -72,11 +72,35 @@ openerp.web_translate_dialog_page = function (instance) { this.$buttons.find(".oe_form_translate_dialog_cancel_button").click(function(){ self.on_button_close(); }); + var $textarea = self.$el.find('textarea.oe_trad_field'); + $textarea.css({minHeight:'100px'}); + $textarea.autosize(); + this.initialize_html_fields(); - self.do_load_fields_values(); - // var $textarea = self.$el.find('textarea.oe_trad_field'); - // $textarea.addClass('autosizeAnimated').autosize({append: "\n"}); - // $textarea.css({minHeight:'100px'}); + this.do_load_fields_values(); + }, + initialize_html_fields: function() { + this.$el.find('.oe_form_field_html textarea').each(function() { + var $textarea = $(this); + var width = '100%'; + var height = 250; + $textarea.cleditor({ + width: width, // width not including margins, borders or padding + height: height, // height not including margins, borders or padding + controls: // controls to add to the toolbar + "bold italic underline strikethrough " + + "| removeformat | bullets numbering | outdent " + + "indent | link unlink | source", + bodyStyle: // style to assign to document body contained within the editor + "margin:4px; color:#4c4c4c; font-size:13px; font-family:'Lucida Grande',Helvetica,Verdana,Arial,sans-serif; cursor:text" + }); + + $textarea.cleditor()[0].change(function() { + this.updateTextArea(); + this.$area.toggleClass('touched', + (this.$area.val() != this.$area.attr('data-value'))); + }); + }); }, // use a `read_translations` method instead of a `read` // this latter leave the fields empty if there is no @@ -102,6 +126,11 @@ openerp.web_translate_dialog_page = function (instance) { self.$el.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]') .val(values[0][f] || '') .attr('data-value', values[0][f] || ''); + + var $tarea = self.$el.find('.oe_form_field_html .oe_trad_field[name="' + lg.code + '-' + f + '"]'); + if ($tarea.length) { + $tarea.cleditor()[0].updateFrame(); + } }); deff.resolve(); }); @@ -133,7 +162,7 @@ openerp.web_translate_dialog_page = function (instance) { }, on_button_close: function() { this.close(); - } + }, }); diff --git a/web_translate_dialog_page/static/src/xml/base.xml b/web_translate_dialog_page/static/src/xml/base.xml index 10dad07d..d321dddd 100644 --- a/web_translate_dialog_page/static/src/xml/base.xml +++ b/web_translate_dialog_page/static/src/xml/base.xml @@ -17,6 +17,9 @@ +
+ + +
+ +
-