From 366843b0020ce4296b60d38ff3cc6a2344ce1bf8 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 23 Sep 2013 13:50:54 +0200 Subject: [PATCH 01/26] [ADD] website.reload() bzr revid: fme@openerp.com-20130923115054-bvx1ssa3pag6d56x --- addons/website/static/src/js/website.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 8f2bd483ca8..594b0a645eb 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -37,7 +37,9 @@ }); return $.when.apply(null, dones); }; - + website.reload = function () { + window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, ''); + }; var all_ready = null; var dom_ready = website.dom_ready = $.Deferred(); From b783cbf4a628cbab5cc3bc7ec4a368970b6fcdd1 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 23 Sep 2013 13:54:17 +0200 Subject: [PATCH 02/26] [ADD] Translator bzr revid: fme@openerp.com-20130923115417-bq20piq7f11pyjve --- addons/website/static/src/css/editor.css | 21 +++ addons/website/static/src/css/editor.sass | 16 ++ .../static/src/js/website.translator.js | 151 ++++++++++++++++++ .../static/src/xml/website.translator.xml | 53 ++++++ 4 files changed, 241 insertions(+) create mode 100644 addons/website/static/src/js/website.translator.js create mode 100644 addons/website/static/src/xml/website.translator.xml diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 127939f6caf..b6cc8505fed 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -107,6 +107,27 @@ table.editorbar-panel td.selected { background-color: #b1c9d9; } +.oe_translate_examples li { + margin: 10px; + padding: 4px; +} + +.oe_translatable_text { + outline: 1px solid black; +} + +.oe_translatable_field { + outline: 1px dashed black; +} + +.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty { + outline-color: red; +} + +.oe_translatable_todo { + background: #ffffb6; +} + /* ---- RTE ---- */ .oe_editable .btn { -webkit-user-select: auto; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 6eb47eab614..b5c0b21a197 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -93,6 +93,20 @@ table.editorbar-panel td.selected background-color: #b1c9d9 +// ---- TRANSLATIONS ---- {{{ +.oe_translate_examples li + margin: 10px + padding: 4px +.oe_translatable_text + outline: 1px solid black +.oe_translatable_field + outline: 1px dashed black +.oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty + outline-color: red +.oe_translatable_todo + background: rgb(255, 255, 182) +// }}} + /* ---- RTE ---- */ // bootstrap makes .btn elements unselectable -> RTE double-click can't know @@ -481,3 +495,5 @@ $navbar_height: 51px &.oe_ace_closed width: 0 opacity: 0 + +// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker: diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js new file mode 100644 index 00000000000..ac4c6d298d2 --- /dev/null +++ b/addons/website/static/src/js/website.translator.js @@ -0,0 +1,151 @@ +(function () { + 'use strict'; + + var website = openerp.website; + website.templates.push('/website/static/src/xml/website.translator.xml'); + + website.EditorBar.include({ + events: _.extend({}, website.EditorBar.prototype.events, { + 'click a[data-action=js_website_translator]': function (ev) { + var self = this; + ev.preventDefault(); + if (!localStorage['website_translator_nodialog']) { + var dialog = new website.TranslatorDialog(); + dialog.appendTo($(document.body)); + dialog.on('activate', this, function () { + dialog.$el.modal('hide'); + this.translateMode(); + }); + } else { + this.translateMode(); + } + }, + }), + temporaryHack: function() { + // TODO: refactor once xmo's feature branch is merged + debugger + this.$buttons.edit.prop('disabled', true); + this.$('#website-top-view').hide(); + this.$('#website-top-edit').show(); + $('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden"); + this.$el.find('[data-action=snippet]').hide(); + this.save = this.saveTranslations; + }, + translateMode: function () { + var self = this; + this.temporaryHack(); + // this.edit(); -- Will need a translation mode + this.translations = null; + openerp.jsonRpc('/website/get_view_translations', 'call', { + 'xml_id': $(document.documentElement).data('view-xmlid'), + 'context': website.get_context(), + }).then(function (translations) { + self.translations = translations; + self.processTranslatableNodes(); + }); + }, + processTranslatableNodes: function () { + var self = this; + var $editables = $('[data-oe-model]') + .not('link, script') + .not('.oe_snippets,.oe_snippet, .oe_snippet *') + .not('[data-oe-type]'); + $editables.each(function () { + self.transNode(this); + }); + $('.oe_translatable_text').prop('contenteditable', true).on('paste', function () { + var node = $(this); + setTimeout(function () { + self.sanitizeNode(node); + }, 0); + }); + $(document).on('blur keyup paste', '.oe_translatable_text[contenteditable]', function(ev) { + var $node = $(this); + setTimeout(function () { + // Doing stuff next tick because past and keyup are + // fired before the content is changed + if (ev.type == 'paste') { + self.sanitizeNode($node[0]); + } + if ($node.data('initial_content') !== $node.text()) { + $node.addClass('oe_dirty').removeClass('oe_translatable_todo'); + } + }, 0); + }); + }, + sanitizeNode: function (node) { + node.text(node.text()); + }, + isTextNode: function (node) { + return node.nodeType === 3 || node.nodeType === 4; + }, + isTranslatable: function (text) { + return text && _.str.trim(text) !== ''; + }, + markTranslatableNode: function (node) { + // TODO: link nodes with same content + node.className += ' oe_translatable_text oe_translatable_todo'; + node.contentEditable = true; + $(node).data('initial_content', node.childNodes[0].data); + }, + saveTranslations: function () { + var trans = {}; + $('.oe_translatable_text.oe_dirty').each(function () { + var $node = $(this); + var text = $node.text(); + var view_id = $(this).parents('[data-oe-model="ir.ui.view"]') + .not('[data-oe-type]').first().data().oeId; + if (!trans[view_id]) { + trans[view_id] = []; + } + trans[view_id].push({ + initial_content: $node.data('initial_content'), + new_content: text + }); + }); + openerp.jsonRpc('/website/set_translations', 'call', { + 'data': trans, + 'lang': website.get_context()['lang'], + }).then(function () { + website.reload(); + }).fail(function () { + // TODO: bootstrap alert with error message + alert("Could not save translation"); + }); + }, + transNode: function (node, context) { + if (node.childNodes.length === 1 + && this.isTextNode(node.childNodes[0]) + && !node.getAttribute('data-oe-model')) { + this.markTranslatableNode(node); + } else { + for (var i = 0, l = node.childNodes.length; i < l; i ++) { + var n = node.childNodes[i]; + if (this.isTextNode(n)) { + if (this.isTranslatable(n.data)) { + var container = document.createElement('span'); + node.insertBefore(container, n); + container.appendChild(n); + this.markTranslatableNode(container); + } + } else { + this.transNode(n); + } + } + } + }, + }); + + website.TranslatorDialog = openerp.Widget.extend({ + events: _.extend({}, website.EditorBar.prototype.events, { + 'hidden.bs.modal': 'destroy', + 'click button[data-action=activate]': function (ev) { + this.trigger('activate'); + }, + }), + template: 'website.TranslatorDialog', + start: function () { + this.$el.modal(); + }, + }); +})(); diff --git a/addons/website/static/src/xml/website.translator.xml b/addons/website/static/src/xml/website.translator.xml new file mode 100644 index 00000000000..f35cf583da9 --- /dev/null +++ b/addons/website/static/src/xml/website.translator.xml @@ -0,0 +1,53 @@ + + + + + + From 3dc22cb54f94c65ee6cab322efc4c4b6b2e42c5b Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Mon, 23 Sep 2013 13:56:41 +0200 Subject: [PATCH 03/26] [FIX] Do not use text nodes if unnecessary bzr revid: fme@openerp.com-20130923115641-2d4o4y3a7jkl5h9o --- addons/website_contract/views/website_contract.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_contract/views/website_contract.xml b/addons/website_contract/views/website_contract.xml index 040f2090eb9..36390ea05b2 100644 --- a/addons/website_contract/views/website_contract.xml +++ b/addons/website_contract/views/website_contract.xml @@ -39,7 +39,7 @@ @@ -264,8 +264,8 @@

                     
                 
               
@@ -288,7 +288,7 @@
                                 

We'll do our best to get back to you as soon as possible.

From bcd3f471ad16f4a9a2ebe7c75e12d3cc1eaf6e73 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 24 Sep 2013 12:23:05 +0200 Subject: [PATCH 10/26] [IMP] persistence of dialog opt out bzr revid: fme@openerp.com-20130924102305-dxyxq56imz1xeul8 --- addons/website/static/src/js/website.translator.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js index a4a5344aa85..d1701db5b2f 100644 --- a/addons/website/static/src/js/website.translator.js +++ b/addons/website/static/src/js/website.translator.js @@ -3,6 +3,7 @@ var website = openerp.website; website.templates.push('/website/static/src/xml/website.translator.xml'); + var nodialog = 'website_translator_nodialog'; website.EditorBar.include({ start: function () { @@ -17,10 +18,11 @@ }, edit: function () { var self = this; - if (!localStorage['website_translator_nodialog']) { + if (!localStorage[nodialog]) { var dialog = new website.TranslatorDialog(); dialog.appendTo($(document.body)); dialog.on('activate', this, function () { + localStorage[nodialog] = dialog.$('input[name=do_not_show]').prop('checked') || ''; dialog.$el.modal('hide'); this.translate(); }); From f79d27e9a3a9fb1d16d2af44d3f0f8a1eb4abf1a Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 24 Sep 2013 21:06:37 +0200 Subject: [PATCH 11/26] [FIX] Update translations bzr revid: fme@openerp.com-20130924190637-0qgzp58lzan8j1oc --- addons/website/controllers/main.py | 35 ++++++++------ addons/website/static/src/css/editor.css | 4 ++ addons/website/static/src/css/editor.sass | 2 + .../static/src/js/website.translator.js | 48 +++++++++++-------- addons/website/views/views.xml | 1 + 5 files changed, 57 insertions(+), 33 deletions(-) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 39361cc1917..99ee5c9fb26 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -161,10 +161,13 @@ class Website(openerp.addons.web.controllers.main.Home): return result @website.route('/website/get_view_translations', type='json', auth='admin') - def get_view_translations(self, xml_id, optional=False): - view = request.registry.get("ir.ui.view") - views = view._views_get(request.cr, request.uid, xml_id, request.context) - return [] + def get_view_translations(self, xml_id, lang=None): + lang = lang or request.context.get('lang') + views = self.customize_template_get(xml_id, optional=False) + views_ids = [view.get('id') for view in views if view.get('active')] + domain = [('type', '=', 'view'), ('res_id', 'in', views_ids), ('lang', '=', lang)] + irt = request.registry.get('ir.translation') + return irt.search_read(request.cr, request.uid, domain, ['id', 'res_id', 'value'], context=request.context) @website.route('/website/set_translations', type='json', auth='admin') def set_translations(self, data, lang): @@ -174,17 +177,21 @@ class Website(openerp.addons.web.controllers.main.Home): for t in trans: initial_content = t['initial_content'].strip() new_content = t['new_content'].strip() - old_trans = irt.search_read( - request.cr, request.uid, - [ - ('type', '=', 'view'), - ('res_id', '=', view_id), - ('lang', '=', lang), - ('src', '=', initial_content), - ]) - if old_trans: + tid = t['translation_id'] + if not tid: + old_trans = irt.search_read( + request.cr, request.uid, + [ + ('type', '=', 'view'), + ('res_id', '=', view_id), + ('lang', '=', lang), + ('src', '=', initial_content), + ]) + if old_trans: + tid = old_trans[0]['id'] + if tid: vals = {'value': new_content} - irt.write(request.cr, request.uid, [old_trans[0]['id']], vals) + irt.write(request.cr, request.uid, [tid], vals) else: new_trans = { 'name': 'website', diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index b6cc8505fed..da15826cd2d 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -124,6 +124,10 @@ table.editorbar-panel td.selected { outline-color: red; } +.oe_translatable_text.oe_dirty:empty { + padding: 0 10px; +} + .oe_translatable_todo { background: #ffffb6; } diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index b5c0b21a197..00ee3df5014 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -103,6 +103,8 @@ table.editorbar-panel outline: 1px dashed black .oe_translatable_text.oe_dirty, .oe_translatable_field.oe_dirty outline-color: red +.oe_translatable_text.oe_dirty:empty + padding: 0 10px .oe_translatable_todo background: rgb(255, 255, 182) // }}} diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js index d1701db5b2f..39408234b6f 100644 --- a/addons/website/static/src/js/website.translator.js +++ b/addons/website/static/src/js/website.translator.js @@ -40,7 +40,7 @@ this.translations = null; openerp.jsonRpc('/website/get_view_translations', 'call', { 'xml_id': $(document.documentElement).data('view-xmlid'), - 'context': website.get_context(), + 'lang': website.get_context().lang, }).then(function (translations) { self.translations = translations; self.processTranslatableNodes(); @@ -48,12 +48,13 @@ }, processTranslatableNodes: function () { var self = this; - var $editables = $('[data-oe-model]') + var $editables = $('[data-oe-model="ir.ui.view"]') .not('link, script') .not('.oe_snippets,.oe_snippet, .oe_snippet *') .not('[data-oe-type]'); + $editables.each(function () { - self.transNode(this); + self.transNode(this, $(this).attr('data-oe-id')|0); }); $('.oe_translatable_text').prop('contenteditable', true).on('paste', function () { var node = $(this); @@ -64,7 +65,7 @@ $(document).on('blur keyup paste', '.oe_translatable_text[contenteditable]', function(ev) { var $node = $(this); setTimeout(function () { - // Doing stuff next tick because past and keyup are + // Doing stuff next tick because paste and keyup events are // fired before the content is changed if (ev.type == 'paste') { self.sanitizeNode($node[0]); @@ -84,26 +85,35 @@ isTranslatable: function (text) { return text && _.str.trim(text) !== ''; }, - markTranslatableNode: function (node) { + markTranslatableNode: function (node, view_id) { // TODO: link nodes with same content - node.className += ' oe_translatable_text oe_translatable_todo'; + node.className += ' oe_translatable_text'; + node.setAttribute('data-oe-translation-view-id', view_id); + var content = node.childNodes[0].data.trim(); + var trans = this.translations.filter(function (t) { + return t.res_id === view_id && t.value === content; + }); + if (trans.length) { + node.setAttribute('data-oe-translation-id', trans[0].id); + } else { + node.className += ' oe_translatable_todo'; + } node.contentEditable = true; - $(node).data('initial_content', node.childNodes[0].data); + $(node).data('initial_content', content); }, save: function () { var trans = {}; // this._super.apply(this, arguments); $('.oe_translatable_text.oe_dirty').each(function () { var $node = $(this); - var text = $node.text(); - var view_id = $(this).parents('[data-oe-model="ir.ui.view"]') - .not('[data-oe-type]').first().data().oeId; - if (!trans[view_id]) { - trans[view_id] = []; + var data = $node.data(); + if (!trans[data.oeTranslationViewId]) { + trans[data.oeTranslationViewId] = []; } - trans[view_id].push({ - initial_content: $node.data('initial_content'), - new_content: text + trans[data.oeTranslationViewId].push({ + initial_content: data.initial_content, + new_content: $node.text(), + translation_id: data.oeTranslationId || null }); }); openerp.jsonRpc('/website/set_translations', 'call', { @@ -116,11 +126,11 @@ alert("Could not save translation"); }); }, - transNode: function (node, context) { + transNode: function (node, view_id) { if (node.childNodes.length === 1 && this.isTextNode(node.childNodes[0]) && !node.getAttribute('data-oe-model')) { - this.markTranslatableNode(node); + this.markTranslatableNode(node, view_id); } else { for (var i = 0, l = node.childNodes.length; i < l; i ++) { var n = node.childNodes[i]; @@ -129,10 +139,10 @@ var container = document.createElement('span'); node.insertBefore(container, n); container.appendChild(n); - this.markTranslatableNode(container); + this.markTranslatableNode(container, view_id); } } else { - this.transNode(n); + this.transNode(n, view_id); } } } diff --git a/addons/website/views/views.xml b/addons/website/views/views.xml index b52cd5a5a9f..ca65fce6cf2 100644 --- a/addons/website/views/views.xml +++ b/addons/website/views/views.xml @@ -22,6 +22,7 @@ + From 172ca5f213f50e2f0910c256508a54bb2c38725d Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 25 Sep 2013 11:31:57 +0200 Subject: [PATCH 12/26] [FIX] url_for on single language bzr revid: fme@openerp.com-20130925093157-2rhvvhe2qsy9e68s --- addons/website/website.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/addons/website/website.py b/addons/website/website.py index 3530b8b77e2..a957117e15b 100644 --- a/addons/website/website.py +++ b/addons/website/website.py @@ -51,13 +51,15 @@ http.auth_methods['public'] = auth_method_public def url_for(path, lang=None): if request: path = urljoin(request.httprequest.path, path) - ps = path.split('/') - lang = lang or request.context.get('lang') - if ps[1] in request.context.get('langs'): - ps[1] = lang - if ps[1] not in request.context.get('langs'): - ps.insert(1, lang) - path = '/'.join(ps) + langs = request.context.get('langs') + if path[0] == '/' and len(langs) > 1: + ps = path.split('/') + lang = lang or request.context.get('lang') + if ps[1] in langs: + ps[1] = lang + else: + ps.insert(1, lang) + path = '/'.join(ps) return path def urlplus(url, params): From e64c07f049a66bb4a689fe9d00db861749ddde91 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 25 Sep 2013 13:44:57 +0200 Subject: [PATCH 13/26] [FIX] Language in context for qweb translations bzr revid: fme@openerp.com-20130925114457-ct5v0gefg6gd6ua8 --- addons/website/website.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/website/website.py b/addons/website/website.py index a957117e15b..35631a835f6 100644 --- a/addons/website/website.py +++ b/addons/website/website.py @@ -138,9 +138,10 @@ class website(osv.osv): user_id=user.browse(cr, openerp.SUPERUSER_ID, uid), ) - context = { - 'inherit_branding': qweb_context.setdefault('editable', False), - } + context = request.context.copy() + context.update( + inherit_branding=qweb_context.setdefault('editable', False), + ) # check if xmlid of the template exists try: From 0a679573fdc3e8b4faee9936e59d314edcb16a81 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 25 Sep 2013 17:42:14 +0200 Subject: [PATCH 14/26] [FIX] Use sourc-id in client translator bzr revid: fme@openerp.com-20130925154214-mrktzf256zjkscy2 --- addons/website/static/src/js/website.translator.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js index 39408234b6f..df0ef288b4f 100644 --- a/addons/website/static/src/js/website.translator.js +++ b/addons/website/static/src/js/website.translator.js @@ -54,7 +54,8 @@ .not('[data-oe-type]'); $editables.each(function () { - self.transNode(this, $(this).attr('data-oe-id')|0); + var view_id = $(this).attr('data-oe-source-id') || $(this).attr('data-oe-id'); + self.transNode(this, view_id|0); }); $('.oe_translatable_text').prop('contenteditable', true).on('paste', function () { var node = $(this); From 7e085d4d5b2e05fd11b6d6b5dd51bb1a5b600f88 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 26 Sep 2013 11:25:43 +0200 Subject: [PATCH 15/26] [FIX] Do not activate language switcher on non multilang controllers bzr revid: fme@openerp.com-20130926092543-79b1e4v3gdttok6h --- addons/website/views/views.xml | 2 +- addons/website/website.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/website/views/views.xml b/addons/website/views/views.xml index c8f1cbbff30..3ce9260046e 100644 --- a/addons/website/views/views.xml +++ b/addons/website/views/views.xml @@ -78,7 +78,7 @@
  • Contact us
  • Sign in
  • -