Skip to content
Permalink
Browse files

Merge pull request #446 from ezsystems/ezp-25161_publish_richtext

Fix EZP-25161: Impossible to publish a content with a Richtext field with Firefox
  • Loading branch information...
dpobel committed Dec 2, 2015
2 parents b1bdcfd + 03796ba commit b3459045b7b5064de0221a034f7b007da540b982
@@ -29,6 +29,7 @@ module.exports = function(grunt) {
"./Resources/public/js/alloyeditor/buttons/*.js",
"./Resources/public/js/alloyeditor/buttons/mixins/*.js",
"./Resources/public/js/alloyeditor/plugins/*.js",
"./Resources/public/js/alloyeditor/processors/*.js",
],
testFiles = [
"./Tests/js/**/*.js",
@@ -92,6 +92,14 @@ system:
ez-alloyeditor-button-mixin-embedimage:
requires: ['ez-alloyeditor']
path: %ez_platformui.public_dir%/js/alloyeditor/buttons/mixins/embedimage.js
ez-editorcontentprocessorbase:
path: %ez_platformui.public_dir%/js/alloyeditor/processors/base.js
ez-editorcontentprocessorxhtml5edit:
requires: ['ez-editorcontentprocessorbase']
path: %ez_platformui.public_dir%/js/alloyeditor/processors/xhtml5edit.js
ez-editorcontentprocessorremoveids:
requires: ['ez-editorcontentprocessorbase']
path: %ez_platformui.public_dir%/js/alloyeditor/processors/removeids.js
ez-platformuiapp:
requires:
- 'app'
@@ -576,6 +584,8 @@ system:
- 'ez-alloyeditor-button-blockremove'
- 'ez-alloyeditor-button-embedhref'
- 'ez-alloyeditor-button-imagehref'
- 'ez-editorcontentprocessorxhtml5edit'
- 'ez-editorcontentprocessorremoveids'
- 'richtexteditview-ez-template'
path: %ez_platformui.public_dir%/js/views/fields/ez-richtext-editview.js
richtexteditview-ez-template:
@@ -0,0 +1,36 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorbase', function (Y) {
"use strict";
/**
* Provides the base EditorContentProcessor
*
* @module ez-editorcontentprocessorbase
*/

Y.namespace('eZ');

/**
* The base EditorContentProcessor.
*
* @namespace eZ
* @class EditorContentProcessorBase
* @constructor
*/
var Processor = function () {};

/**
* Process the `data` and returns a new string version of.
*
* @method process
* @param {String} data the data to process
* @return {String}
*/
Processor.prototype.process = function (data) {
return data;
};

Y.eZ.EditorContentProcessorBase = Processor;
});
@@ -0,0 +1,51 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorremoveids', function (Y) {
"use strict";
/**
* Provides the removeids EditorContentProcessor
*
* @module ez-editorcontentprocessorremoveids
*/

Y.namespace('eZ');

/**
* removeids EditorContentProcessor.
*
* @namespace eZ
* @class EditorContentProcessorRemoveIds
* @constructor
* @extends eZ.EditorContentProcessorBase
*/
var RemoveIds = function () {};

Y.extend(RemoveIds, Y.eZ.EditorContentProcessorBase);

/**
* Removes the `id` attributes in `data`.
*
* @method process
* @param {String} data
* @return {String}
*/
RemoveIds.prototype.process = function (data) {
var doc = document.createDocumentFragment(),
root = document.createElement('div'),
list, i;

root.innerHTML = data;
doc.appendChild(root);

list = doc.querySelectorAll('[id]');
for (i = 0; i != list.length; ++i) {
list[i].removeAttribute("id");
}

return root.innerHTML;
};

Y.eZ.EditorContentProcessorRemoveIds = RemoveIds;
});
@@ -0,0 +1,47 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorxhtml5edit', function (Y) {
"use strict";
/**
* Provides the xhtml5edit EditorContentProcessor
*
* @module ez-editorcontentprocessorxhtml5edit
*/

Y.namespace('eZ');

var NAMESPACE = 'http://ez.no/namespaces/ezpublish5/xhtml5/edit',
ROOT_TAG = 'section';

/**
* xhtml5edit EditorContentProcessor.
*
* @namespace eZ
* @class EditorContentProcessorXHTML5Edit
* @constructor
* @extends eZ.EditorContentProcessorBase
*/
var XHTML5Edit = function () {};

Y.extend(XHTML5Edit, Y.eZ.EditorContentProcessorBase);

/**
* Transforms `data` into a XHTML5Edit document.
*
* @method process
* @param {String} data
* @return {String}
*/
XHTML5Edit.prototype.process = function (data) {
// building the XML document by concatening strings is required to get
// the xhtml5edit format expected by the RichText parser where the
// section root element has a custom default namespace but it's content
// is supposed to be a valid XHTML document but in that namespace as
// well...
return '<' + ROOT_TAG + ' xmlns="' + NAMESPACE + '">' + data + '</' + ROOT_TAG + '>';
};

Y.eZ.EditorContentProcessorXHTML5Edit = XHTML5Edit;
});
@@ -193,7 +193,7 @@ YUI.add('ez-richtext-editview', function (Y) {
* @return {Boolean}
*/
_isEmpty: function () {
var section = Y.Node.create(this._getEditorContent()),
var section = Y.Node.create(this._getXHTML5EditValue()),
hasChildNodes = function (element) {
return !!element.get('children').size();
},
@@ -235,15 +235,6 @@ YUI.add('ez-richtext-editview', function (Y) {
return null;
}

/**
* Holds the namespace of the xhtml5edit format
*
* @property _namespace
* @type {String}
* @default undefined
*/
this._namespace = doc.documentElement.getAttribute('xmlns');

fragment.appendChild(root);
for (i = 0; i != doc.documentElement.childNodes.length; i++) {
root.appendChild(doc.documentElement.childNodes.item(i).cloneNode(true));
@@ -283,43 +274,28 @@ YUI.add('ez-richtext-editview', function (Y) {
* @return String
*/
_getFieldValue: function () {
return {xml: this._getEditorContent()};
return {xml: this._getXHTML5EditValue()};
},

/**
* Returns the content of the editor.
* Returns the content of the editor in the XHTML5Edit format. The
* actual editor's content is passed through a set of
* EditorContentProcessors.
*
* @method _getEditorContent
* @method _getXHTML5EditValue
* @protected
* @return {String}
*/
_getEditorContent: function () {
var data = this.get('editor').get('nativeEditor').getData(),
root, i, list,
body,
doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);

// TODO: reorganize this code for instance in a set of
// EditorContentProcessors to do what it's done here, ie:
// * make sure we have an XHTML fragment
// * enclose the code in a section with the correct namespace
// * cleanup the auto-generated ids
body = doc.createElement('body');
doc.documentElement.appendChild(body);
root = doc.createElement('section');
body.appendChild(root);
root.innerHTML = data;
list = root.querySelectorAll('[id]');

for (i = 0; i != list.length; ++i) {
list[i].removeAttribute("id");
}
root.setAttribute('xmlns', this._namespace);
return root.outerHTML.trim();
}
_getXHTML5EditValue: function () {
var data = this.get('editor').get('nativeEditor').getData();

Y.Array.each(this.get('editorContentProcessors'), function (processor) {
data = processor.process(data);
});
return data;
},
}, {
ATTRS: {

/**
* Stores the focus mode state. When true, the rich text UI is
* supposed to be fullscreen with an action bar on the right.
@@ -396,6 +372,23 @@ YUI.add('ez-richtext-editview', function (Y) {
value: ['contentDiscover', 'loadImageVariation'],
readOnly: true,
},

/**
* Hold the list of editor content processors. Those components
* should have a `process` method and are there to clean up the
* editor content before using it through REST.
*
* @attribute editorContentProcessors
* @type Array of {eZ.EditorContentProcessor}
*/
editorContentProcessors: {
valueFn: function () {
return [
new Y.eZ.EditorContentProcessorRemoveIds(),
new Y.eZ.EditorContentProcessorXHTML5Edit(),
];
},
},
}
});

@@ -0,0 +1,32 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorbase-tests', function (Y) {
var processTest,
Assert = Y.Assert;

processTest = new Y.Test.Case({
name: "eZ Editor Content base processor process test",

setUp: function () {
this.processor = new Y.eZ.EditorContentProcessorBase();
},

tearDown: function () {
delete this.processor;
},

"Should return data without change": function () {
var data = "whatever";

Assert.areSame(
data, this.processor.process(data),
"process() should return `data` without change"
);
},
});

Y.Test.Runner.setName("eZ Editor Content base processor tests");
Y.Test.Runner.add(processTest);
}, '', {requires: ['test', 'ez-editorcontentprocessorbase']});
@@ -0,0 +1,43 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorremoveids-tests', function (Y) {
var processTest,
Assert = Y.Assert;

processTest = new Y.Test.Case({
name: "eZ Editor Content removeids processor process test",

setUp: function () {
this.processor = new Y.eZ.EditorContentProcessorRemoveIds();
},

tearDown: function () {
delete this.processor;
},

"Should remove the ids": function () {
var data = "<div><p id='music'>Foo Fighters - <span id='title'>The Neverending Sigh</span></p></div>",
result = this.processor.process(data),
doc = (new DOMParser()).parseFromString(result, 'text/xml');

Assert.isNull(
doc.querySelector('#music'),
"The #music id should be removed"
);
Assert.isNull(
doc.querySelector('#title'),
"The #title id should be removed"
);
Assert.areEqual(
"<div><p>Foo Fighters - <span>The Neverending Sigh</span></p></div>",
result,
"The XML document should be kept"
);
},
});

Y.Test.Runner.setName("eZ Editor Content removeids processor tests");
Y.Test.Runner.add(processTest);
}, '', {requires: ['test', 'ez-editorcontentprocessorremoveids']});
@@ -0,0 +1,43 @@
/*
* Copyright (C) eZ Systems AS. All rights reserved.
* For full copyright and license information view LICENSE file distributed with this source code.
*/
YUI.add('ez-editorcontentprocessorxhtml5edit-tests', function (Y) {
var processTest,
Assert = Y.Assert;

processTest = new Y.Test.Case({
name: "eZ Editor Content xhtml5edit processor process test",

setUp: function () {
this.processor = new Y.eZ.EditorContentProcessorXHTML5Edit();
},

tearDown: function () {
delete this.processor;
},

"Should create a XHTML5Edit document": function () {
var data = "<div><p>Foo Fighters - <span id='title'>The Neverending Sigh</span></p></div>",
result = this.processor.process(data),
doc = (new DOMParser()).parseFromString(result, 'text/xml');

Assert.areEqual(
'http://ez.no/namespaces/ezpublish5/xhtml5/edit',
doc.documentElement.namespaceURI,
"The document should be in the xhtml5edit namespace"
);
Assert.areEqual(
'section', doc.documentElement.tagName,
"The root element should be a <section>"
);
Assert.isNotNull(
doc.querySelector('#title'),
"The document content should be kept"
);
},
});

Y.Test.Runner.setName("eZ Editor Content xhtml5edit processor tests");
Y.Test.Runner.add(processTest);
}, '', {requires: ['test', 'ez-editorcontentprocessorxhtml5edit']});
Oops, something went wrong.

0 comments on commit b345904

Please sign in to comment.
You can’t perform that action at this time.