[IMP] single whole-page editor, reinstate ability to save only an embedded field (not part of a view)

bzr revid: xmo@openerp.com-20130917085753-aa32xxezhtrfaffj
This commit is contained in:
Xavier Morel 2013-09-17 10:57:53 +02:00
parent f3eec24a6f
commit 757a2dbc4e
3 changed files with 80 additions and 29 deletions

View File

@ -242,14 +242,7 @@
this.$('#website-top-edit').show();
$('.css_non_editable_mode_hidden').removeClass("css_non_editable_mode_hidden");
var $editables = $('[data-oe-model][data-oe-xpath]')
// FIXME: propagation should make "meta" blocks non-editable in the first place...
.not('link, script')
.not('[data-oe-type]')
.not('.oe_snippet_editor')
.prop('contentEditable', true)
.addClass('oe_editable');
this.rte.start_edition($editables);
this.rte.start_edition();
},
rte_changed: function () {
this.$buttons.save.prop('disabled', false);
@ -258,20 +251,26 @@
var self = this;
observer.disconnect();
var defs = _(CKEDITOR.instances).chain()
.filter(function (editor) { return editor.element.hasClass('oe_dirty'); })
.map(function (editor) {
var $el = $(editor.element.$);
var editor = this.rte.editor;
var root = editor.element.$;
editor.destroy();
// FIXME: select editables then filter by dirty?
var defs = this.rte.fetch_editables(root)
.removeClass('oe_editable cke_focus')
.removeAttr('contentEditable')
.filter('.oe_dirty')
.map(function () {
var $el = $(this);
// TODO: Add a queue with concurrency limit in webclient
// https://github.com/medikoo/deferred/blob/master/lib/ext/function/gate.js
return self.saving_mutex.exec(function () {
return self.saveEditor(editor)
return self.saveElement($el)
.fail(function () {
var data = $el.data();
console.error(_.str.sprintf('Could not save %s(%d).%s', data.oeModel, data.oeId, data.oeField));
});
});
}).value();
}).get();
return $.when.apply(null, defs).then(function () {
window.location.href = window.location.href.replace(/unable_editor(=[^&]*)?|#.*/g, '');
});
@ -279,18 +278,15 @@
/**
* Saves an RTE content, which always corresponds to a view section (?).
*/
saveEditor: function (editor) {
var element = editor.element;
editor.destroy();
element.removeClass('cke_focus')
.removeClass('oe_dirty')
.removeClass('oe_editable')
.removeAttribute('contentEditable');
var data = element.getOuterHtml();
saveElement: function ($el) {
$el.removeClass('oe_dirty');
var markup = $el.prop('outerHTML');
return openerp.jsonRpc('/web/dataset/call', 'call', {
model: 'ir.ui.view',
method: 'save',
args: [element.data('oe-id'), data, element.data('oe-xpath'), website.get_context()],
args: [$el.data('oe-id'), markup,
$el.data('oe-xpath') || null,
website.get_context()],
});
},
cancel: function () {
@ -313,22 +309,57 @@
start_edition: function ($elements) {
var self = this;
$elements
// create a single editor for the whole page
// FIXME: is not the whole page, ckeditor can't handle body
var root = document.getElementById('wrap');
root.setAttribute('data-cke-editable', 'true');
this.editor = CKEDITOR.inline(root, self._config());
this.editor.on('instanceReady', function () {
// ckeditor set root to editable, disable it (only inner
// sections are editable)
// FIXME: are there cases where the whole editor is editable?
root.contentEditable = false;
self.setup_editables(root);
});
},
setup_editables: function (root) {
// selection of editable sub-items was previously in
// EditorBar#edit, but for some unknown reason the elements were
// apparently removed and recreated (?) at editor initalization,
// and observer setup was lost.
var self = this;
// setup dirty-marking for each editable element
this.fetch_editables(root)
.prop('contentEditable', true)
.addClass('oe_editable')
.each(function () {
var node = this;
observer.observe(node, OBSERVER_CONFIG);
var $node = $(node);
var editor = CKEDITOR.inline(this, self._config());
editor.on('instanceReady', function () {
self.trigger('instanceReady');
observer.observe(node, OBSERVER_CONFIG);
});
$node.one('content_changed', function () {
console.log("!", $node)
$node.addClass('oe_dirty');
self.trigger('change');
});
});
},
fetch_editables: function (root) {
return $(root).find('[data-oe-model]')
// FIXME: propagation should make "meta" blocks non-editable in the first place...
.not('link, script')
.not('.oe_snippet_editor')
.filter(function () {
var $this = $(this);
// keep view sections and fields which are *not* in
// view sections for toplevel editables
return $this.data('oe-model') === 'ir.ui.view'
|| !$this.closest('[data-oe-model = "ir.ui.view"]').length;
});
},
_current_editor: function () {
return CKEDITOR.currentInstance;
},

View File

@ -154,6 +154,21 @@ class TestViewSaving(common.TransactionCase):
)
)
def test_save_only_embedded(self):
Company = self.registry('res.company')
company_id = 1
Company.write(self.cr, self.uid, company_id, {'name': "Foo Corporation"})
node = html.tostring(h.SPAN(
"Acme Corporation",
attrs(model='res.company', id=company_id, field="name", expression='bob')))
self.registry('ir.ui.view').save(self.cr, self.uid, res_id=company_id,value=node)
company = Company.browse(self.cr, self.uid, company_id)
self.assertEqual(company.name, "Acme Corporation")
def test_field_tail(self):
View = self.registry('ir.ui.view')
replacement = ET.tostring(

View File

@ -91,6 +91,11 @@ class view(osv.osv):
arch_section = html.fromstring(value)
if xpath is None:
# value is an embedded field on its own, not a view section
self.save_embedded_field(cr, uid, arch_section, context=context)
return
for el in self.extract_embedded_fields(cr, uid, arch_section, context=context):
self.save_embedded_field(cr, uid, el, context=context)