From 3d51c256e0b92795514b46ba5940f3a5fa0fa8bd Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 6 Dec 2013 13:54:38 +0100 Subject: [PATCH 01/47] [CHG] rename web_translate_dialog_page to web_translate_dialog --- web_translate_dialog/__init__.py | 22 ++ web_translate_dialog/__openerp__.py | 45 ++++ web_translate_dialog/orm.py | 79 +++++++ web_translate_dialog/static/src/css/base.css | 6 + .../static/src/js/web_translate_dialog.js | 202 ++++++++++++++++++ web_translate_dialog/static/src/xml/base.xml | 34 +++ 6 files changed, 388 insertions(+) create mode 100644 web_translate_dialog/__init__.py create mode 100644 web_translate_dialog/__openerp__.py create mode 100644 web_translate_dialog/orm.py create mode 100644 web_translate_dialog/static/src/css/base.css create mode 100644 web_translate_dialog/static/src/js/web_translate_dialog.js create mode 100644 web_translate_dialog/static/src/xml/base.xml diff --git a/web_translate_dialog/__init__.py b/web_translate_dialog/__init__.py new file mode 100644 index 00000000..06331652 --- /dev/null +++ b/web_translate_dialog/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 . +# +############################################################################## + +from . import orm diff --git a/web_translate_dialog/__openerp__.py b/web_translate_dialog/__openerp__.py new file mode 100644 index 00000000..be80746a --- /dev/null +++ b/web_translate_dialog/__openerp__.py @@ -0,0 +1,45 @@ +# -*- 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", + "category": "Hidden", + "description": """ +Replace the standard translation view by an alternative one: + + * Add a "Translate" button item in the "More" menu + * The translations are displayed in a dialog (much like the OpenERP + 6.1's one) + * The translation dialog displays empty fields for the untranslated fields, + instead of the source values. + * Support HTML fields + +""", + "version": "1.0", + "depends": ['web', + ], + 'js': ['static/src/js/web_translate_dialog.js', + ], + 'css': ['static/src/css/base.css', + ], + 'qweb': ["static/src/xml/base.xml", + ], + 'auto_install': False, + } diff --git a/web_translate_dialog/orm.py b/web_translate_dialog/orm.py new file mode 100644 index 00000000..f8e395d3 --- /dev/null +++ b/web_translate_dialog/orm.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Author: Guewen Baconnier +# 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 +# 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 + + +# add the method in the orm so we can use it from the TranslateDialog of the +# 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. + + :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_access_rights(cr, user, 'read') + fields = self.check_field_access_rights(cr, user, 'read', fields) + 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) + + 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] + 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/static/src/css/base.css b/web_translate_dialog/static/src/css/base.css new file mode 100644 index 00000000..82fe854c --- /dev/null +++ b/web_translate_dialog/static/src/css/base.css @@ -0,0 +1,6 @@ +.openerp .oe_trad_field { + width: 95%; +} +.openerp .oe_trad_field.touched { + border: 1px solid green !important; +} diff --git a/web_translate_dialog/static/src/js/web_translate_dialog.js b/web_translate_dialog/static/src/js/web_translate_dialog.js new file mode 100644 index 00000000..ef153787 --- /dev/null +++ b/web_translate_dialog/static/src/js/web_translate_dialog.js @@ -0,0 +1,202 @@ +openerp.web_translate_dialog = function (instance) { + + "use strict"; + + var QWeb = instance.web.qweb, + _t = instance.web._t, + _lt = instance.web._lt; + + instance.web.FormView.include({ + load_form: function(data) { + var self = this; + this._super(data); + this.sidebar.add_items('other', _.compact([ + self.is_action_enabled('edit') && { label: _t('Translate'), callback: self.on_button_translate }, + ])); + }, + on_button_translate: function() { + var self = this; + $.when(this.has_been_loaded).then(function() { + self.open_translate_dialog(this); + }); + }, + }); + + instance.web.View.include({ + open_translate_dialog: function() { + new instance.web_translate_dialog.TranslateDialog(this).open(); + } + }); + + instance.web_translate_dialog.TranslateDialog = instance.web.Dialog.extend({ + template: "TranslateDialog", + dialog_title: {toString: function () { return _t("Translations"); }}, + init: function(parent, options, content) { + this._super(parent, + {width: '90%', + height: '80%'}, + 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 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(); + }, + open: function() { + var self = this, + sup = this._super; + // the template needs the languages + $.when(this.languages_loaded).then(function() { + // if (self.view.translatable_fields && self.view.translatable_fields.length) { + return sup.call(self); + }); + }, + start: function() { + var self = this; + this.$el.find('.oe_trad_field').change(function() { + $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value'))); + }); + this.$buttons.html(QWeb.render("TranslateDialog.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(); + }); + var $textarea = self.$el.find('textarea.oe_trad_field'); + $textarea.css({minHeight:'100px'}); + $textarea.autosize(); + this.initialize_html_fields(); + + 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; // forced to fixed size on initialization + // will be changed to percentage right after + // the creation + 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" + }); + + var $cleditor = $textarea.cleditor()[0]; + // Down to -- end, this is a workaround for the bug + // https://bugs.launchpad.net/openerp-web/+bug/1258463 + // The editor is initially created with a fixed size so + // the buggy event is not bound to $(window), then we restore + // a percentage width and bind the "normal" event without the + // CHM's buggy change. + $cleditor.$main.width('95%'); + $cleditor.options.width = '95%'; + $(window).resize(function() { + //Forcefully blurred iframe contentWindow, chrome, IE, safari doesn't trigger blur on window resize and due to which text disappears + var contentWindow = $cleditor.$frame[0].contentWindow; + if(!$.browser.mozilla && contentWindow){ + $(contentWindow).trigger('blur'); + } + }); + $cleditor.refresh(); + // -- end + + $cleditor.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 + // translation for a field instead of taking the src field + do_load_fields_values: function(callback) { + var self = this, + deferred = []; + + this.$el.find('.oe_trad_field').val('').removeClass('touched'); + _.each(self.languages, function(lg) { + var deff = $.Deferred(); + deferred.push(deff); + var callback = function(values) { + }; + self.view.dataset.call( + 'read_translations', + [[self.view.datarecord.id], + self.translatable_fields_keys, + self.view.dataset.get_context({ + 'lang': lg.code + })]).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] || ''); + + 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(); + }); + }); + return deferred; + }, + on_button_save: function() { + var trads = {}, + self = this, + trads_mutex = new $.Mutex(); + self.$el.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 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_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/static/src/xml/base.xml b/web_translate_dialog/static/src/xml/base.xml new file mode 100644 index 00000000..2a90f7fb --- /dev/null +++ b/web_translate_dialog/static/src/xml/base.xml @@ -0,0 +1,34 @@ + + + + + + + + + + +
+
Field
+
+
+
+ + + + +
+ + +
- -
- +
+ -
- +
+ -
- +
+