diff --git a/build/ctct-sanitize-content-handler-changelog.md b/build/ctct-sanitize-content-handler-changelog.md
new file mode 100644
index 0000000000..7fbda00b20
--- /dev/null
+++ b/build/ctct-sanitize-content-handler-changelog.md
@@ -0,0 +1,6 @@
+- **ENHANCEMENT**: Sanitize Content Handler
+ Adds the ability to configure and run the Sanitize Content Handler by editable element
+ tag name, in addition to element ID and class attributes. Refactored the Sanitize Content
+ Handler initialization and setup to cache Sanitizers per config. Also, cleanup up
+ relevant section in plugin_contenthandler guide page to match released implementation and
+ this enhancement.
\ No newline at end of file
diff --git a/doc/guides/source/plugin_contenthandler.textile b/doc/guides/source/plugin_contenthandler.textile
index 385d7ae849..41bcbf2087 100644
--- a/doc/guides/source/plugin_contenthandler.textile
+++ b/doc/guides/source/plugin_contenthandler.textile
@@ -19,6 +19,7 @@ There are some Content Handler available:
* Word
* oEmbed
* Sanitize
+* Style Attribute
Plugins also provide Content Handler:
* Block +common/block+ plugin
@@ -88,12 +89,12 @@ h4. Writing your own Content Handler
['aloha', 'jquery', 'aloha/contenthandlermanager'],
function(Aloha, jQuery, ContentHandlerManager) {
"use strict";
-
+
var MyContentHandler = ContentHandlerManager.createHandler({
handleContent: function( content ) {
-
+
// do something with the content
-
+
return content; // return as HTML text not jQuery/DOM object
}
});
@@ -123,6 +124,7 @@ The Contenthandler Plugin has no user interface and is used in conjunction with
* word
* generic
* sanitize
+* style attribute
h4. Word Content Handler
@@ -164,7 +166,7 @@ WARNING: The Sanitize Content Handler does not work reliably on IE7, and will th
The Sanitize Content Handler will remove all dom elements and attributes not covered by it's configuration. You may specify your own configuration based on these default settings:
-Aloha.settings.contentHandler.sanitize = {
+Aloha.settings.contentHandler.allows = {
// elements allowed in the content
elements: [
'a', 'abbr', 'b', 'blockquote', 'br', 'cite', 'code', 'dd', 'del', 'dl', 'dt', 'em',
@@ -184,4 +186,17 @@ Aloha.settings.contentHandler.sanitize = {
'q' : {'cite': ['http', 'https', '__relative__']}
}
}
+
+// OR
+
+Aloha.settings.contentHandler.sanitize = 'relaxed'; // 'basic'|'restricted'|'relaxed'
+
+// OR
+
+Aloha.settings.contentHandler.sanitize = {
+ '#myId': { elements: [...], attributes: {...}, protocols;{...} },
+ '.myclass': { elements: [...], attributes: {...}, protocols;{...} },,
+ 'p': { elements: [...], attributes: {...}, protocols;{...} },
+ 'div': { elements: [...], attributes: {...}, protocols;{...} }
+}
diff --git a/src/plugins/common/contenthandler/lib/sanitizecontenthandler.js b/src/plugins/common/contenthandler/lib/sanitizecontenthandler.js
index 2886df4694..d47c9bb89b 100644
--- a/src/plugins/common/contenthandler/lib/sanitizecontenthandler.js
+++ b/src/plugins/common/contenthandler/lib/sanitizecontenthandler.js
@@ -1,9 +1,9 @@
/* sanitizecontenthandler.js is part of Aloha Editor project http://aloha-editor.org
*
- * Aloha Editor is a WYSIWYG HTML5 inline editing library and editor.
+ * Aloha Editor is a WYSIWYG HTML5 inline editing library and editor.
* Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
- * Contributors http://aloha-editor.org/contribution.php
- *
+ * Contributors http://aloha-editor.org/contribution.php
+ *
* Aloha Editor is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
+ *
* As an additional permission to the GNU GPL version 2, you may distribute
* non-source (e.g., minimized or compacted) forms of the Aloha-Editor
* source code without the copy of the GNU GPL normally required,
@@ -31,161 +31,199 @@ define([
'aloha/plugin',
'aloha/console',
'vendor/sanitize'
-],
-function( Aloha, jQuery, ContentHandlerManager, Plugin, console ) {
- "use strict";
-
- var sanitize;
-
- // predefined set of sanitize options if no dynamic or custom config is used
- if( !Aloha.defaults.sanitize ) {
- Aloha.defaults.sanitize = {}
- }
-
- // very restricted sanitize config
- Aloha.defaults.sanitize.restricted = {
- elements: [ 'b', 'em', 'i', 'strong', 'u', 'del', 'p', 'span', 'div', 'br' ]
- }
-
- // sanitize config allowing a bit more (no tables)
- Aloha.defaults.sanitize.basic = {
- elements: [
- 'a', 'abbr', 'b', 'blockquote', 'br', 'cite', 'code', 'dd', 'del', 'dl', 'dt', 'em',
- 'i', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong', 'sub',
- 'sup', 'u', 'ul' ],
-
- attributes: {
- 'a' : ['href'],
- 'blockquote' : ['cite'],
- 'q' : ['cite'],
- 'abbr': ['title']
- },
-
- //add_attributes: {
- // 'a': {'rel': 'nofollow'}
- //},
-
- protocols: {
- 'a' : {'href': ['ftp', 'http', 'https', 'mailto', '__relative__']},
- 'blockquote' : {'cite': ['http', 'https', '__relative__']},
- 'q' : {'cite': ['http', 'https', '__relative__']}
- }
- }
-
- // relaxed sanitize config allows also tables
- Aloha.defaults.sanitize.relaxed = {
- elements: [
- 'a', 'abbr', 'b', 'blockquote', 'br', 'caption', 'cite', 'code', 'col',
- 'colgroup', 'dd', 'del', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
- 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong',
- 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'u',
- 'ul', 'span', 'hr', 'object', 'div'
- ],
-
- attributes: {
- 'a': ['href', 'title', 'id', 'class', 'target', 'data-gentics-aloha-repository', 'data-gentics-aloha-object-id'],
- 'div': ['id','class','style'],
- 'abbr': ['title'],
- 'blockquote': ['cite'],
- 'br': ['class'],
- 'col': ['span', 'width'],
- 'colgroup': ['span', 'width'],
- 'img': ['align', 'alt', 'height', 'src', 'title', 'width', 'class', 'data-caption', 'data-align', 'data-width', 'data-original-image'],
- 'ol': ['start', 'type'],
- 'p': ['class', 'style', 'id'],
- 'q': ['cite'],
- 'table': ['summary', 'width'],
- // For IE7 it matters the uppercase 'S' in rowSpan, colSpan
- 'td': ['abbr', 'axis', 'colSpan', 'rowSpan', 'colspan', 'rowspan', 'width'],
- 'th': ['abbr', 'axis', 'colSpan', 'rowSpan', 'colspan', 'rowspan', 'scope', 'width'],
- 'ul': ['type'],
- 'span': ['class','style','lang','xml:lang','role']
- },
-
- protocols: {
- 'a': {'href': ['ftp', 'http', 'https', 'mailto', '__relative__']},
- 'blockquote': {'cite': ['http', 'https', '__relative__']},
- 'img': {'src' : ['http', 'https', '__relative__']},
- 'q': {'cite': ['http', 'https', '__relative__']}
- }
- }
-
- function initSanitize (configAllows) {
- var
- filter = [ 'restricted', 'basic', 'relaxed' ],
- config = Aloha.defaults.supports; // @TODO: needs to be implemented into all plugins
-
- // @TODO think about Aloha.settings.contentHandler.sanitize name/options
- if (Aloha.settings.contentHandler.sanitize &&
- jQuery.inArray(Aloha.settings.contentHandler.sanitize, filter) > -1) {
- config = Aloha.defaults.sanitize[Aloha.settings.contentHandler.sanitize];
- } else {
- // use relaxed filter by default
- config = Aloha.defaults.sanitize.relaxed;
- }
-
- // @TODO move to Aloha.settings.contentHandler.sanitize.allows ?
- if (Aloha.settings.contentHandler.allows) {
- config = Aloha.settings.contentHandler.allows;
- }
-
- if (configAllows) {
- config = configAllows;
- }
-
- // add a filter to stop cleaning elements with contentEditable "false"
- config.filters = [function( elem ) {
- return elem.contentEditable != "false";
- }];
- sanitize = new Sanitize( config, jQuery );
- }
-
- var SanitizeContentHandler = ContentHandlerManager.createHandler({
- /**
- * Handle the content from eg. paste action and sanitize the html
- * @param content
- */
- handleContent: function (content, options, editable) {
- if (!editable) {
- return content;
- }
-
- var sanitizeConfig;
- var contentHandlerConfig;
-
- if (Aloha.settings.contentHandler &&
- Aloha.settings.contentHandler.handler &&
- Aloha.settings.contentHandler.handler.sanitize) {
- // individual sanitize config per editable -- should support merging of configs from other plugins ...
- if (Aloha.settings.contentHandler.handler.sanitize) {
- contentHandlerConfig = Aloha.settings.contentHandler.handler.sanitize;
- }
- var containerId = contentHandlerConfig['#' + editable.getId()];
- if (typeof containerId !== 'undefined') {
- sanitizeConfig = contentHandlerConfig;
- } else {
- var containerClasses = editable.obj.attr('class').split(' ');
- for (var i = 0; i < containerClasses.length; i++) {
- if (typeof contentHandlerConfig['.' + containerClasses[i]] !== 'undefined') {
- sanitizeConfig = contentHandlerConfig['.' + containerClasses[i]];
- }
- }
- }
- }
-
- if ( typeof sanitize === 'undefined' || typeof sanitizeConfig !== 'undefined') {
- initSanitize(sanitizeConfig);
- }
-
- if (typeof content === 'string'){
- content = jQuery('
' + content + '
').get(0);
- } else if (content instanceof jQuery) {
- content = jQuery('').append(content).get(0);
- }
-
- return jQuery('
').append(sanitize.clean_node(content)).html();
- }
- });
-
- return SanitizeContentHandler;
-});
+], function(Aloha, jQuery, ContentHandlerManager, Plugin, console) {
+ "use strict";
+
+ var sanitize;
+
+ // predefined set of sanitize options if no dynamic or custom config is used
+ if( !Aloha.defaults.sanitize ) {
+ Aloha.defaults.sanitize = {}
+ }
+
+ // very restricted sanitize config
+ Aloha.defaults.sanitize.restricted = {
+ elements: [ 'b', 'em', 'i', 'strong', 'u', 'del', 'p', 'span', 'div', 'br' ]
+ }
+
+ // sanitize config allowing a bit more (no tables)
+ Aloha.defaults.sanitize.basic = {
+ elements: [
+ 'a', 'abbr', 'b', 'blockquote', 'br', 'cite', 'code', 'dd', 'del', 'dl', 'dt', 'em',
+ 'i', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong', 'sub',
+ 'sup', 'u', 'ul' ],
+
+ attributes: {
+ 'a' : ['href'],
+ 'blockquote' : ['cite'],
+ 'q' : ['cite'],
+ 'abbr': ['title']
+ },
+
+ //add_attributes: {
+ // 'a': {'rel': 'nofollow'}
+ //},
+
+ protocols: {
+ 'a' : {'href': ['ftp', 'http', 'https', 'mailto', '__relative__']},
+ 'blockquote' : {'cite': ['http', 'https', '__relative__']},
+ 'q' : {'cite': ['http', 'https', '__relative__']}
+ }
+ }
+
+ // relaxed sanitize config allows also tables
+ Aloha.defaults.sanitize.relaxed = {
+ elements: [
+ 'a', 'abbr', 'b', 'blockquote', 'br', 'caption', 'cite', 'code', 'col',
+ 'colgroup', 'dd', 'del', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+ 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong',
+ 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'u',
+ 'ul', 'span', 'hr', 'object', 'div'
+ ],
+
+ attributes: {
+ 'a': ['href', 'title', 'id', 'class', 'target', 'data-gentics-aloha-repository', 'data-gentics-aloha-object-id'],
+ 'div': ['id','class','style'],
+ 'abbr': ['title'],
+ 'blockquote': ['cite'],
+ 'br': ['class'],
+ 'col': ['span', 'width'],
+ 'colgroup': ['span', 'width'],
+ 'img': ['align', 'alt', 'height', 'src', 'title', 'width', 'class', 'data-caption', 'data-align', 'data-width', 'data-original-image'],
+ 'ol': ['start', 'type'],
+ 'p': ['class', 'style', 'id'],
+ 'q': ['cite'],
+ 'table': ['summary', 'width'],
+ // For IE7 it matters the uppercase 'S' in rowSpan, colSpan
+ 'td': ['abbr', 'axis', 'colSpan', 'rowSpan', 'colspan', 'rowspan', 'width'],
+ 'th': ['abbr', 'axis', 'colSpan', 'rowSpan', 'colspan', 'rowspan', 'scope', 'width'],
+ 'ul': ['type'],
+ 'span': ['class','style','lang','xml:lang','role']
+ },
+
+ protocols: {
+ 'a': {'href': ['ftp', 'http', 'https', 'mailto', '__relative__']},
+ 'blockquote': {'cite': ['http', 'https', '__relative__']},
+ 'img': {'src' : ['http', 'https', '__relative__']},
+ 'q': {'cite': ['http', 'https', '__relative__']}
+ }
+ }
+
+ var sanitizers = (function() {
+ var idOrClassRegExp = /^[\.#]/;
+ var defaultKey = '_default_';
+ var map = {};
+ // Filter to stop cleaning elements with contentEditable "false", added to all configs
+ var filters = [function(elem) {
+ return elem.contentEditable != "false";
+ }];
+
+ var normalizeTagName = function(editableSelector) {
+ var isIdOrClass = function(selector) {
+ return idOrClassRegExp.test(selector);
+ };
+ return isIdOrClass(editableSelector) ? editableSelector : editableSelector.toLowerCase();
+ };
+
+ var initDefault = function() {
+ var config = Aloha.defaults.sanitize.relaxed;
+
+ if (Aloha.settings.contentHandler) {
+ if (Aloha.settings.contentHandler.allows) {
+ config = Aloha.settings.contentHandler.allows;
+ } else if (Aloha.settings.contentHandler.sanitize && Aloha.defaults.sanitize[Aloha.settings.contentHandler.sanitize]) {
+ config = Aloha.defaults.sanitize[Aloha.settings.contentHandler.sanitize];
+ }
+ }
+
+ config.filters = filters;
+
+ map[defaultKey] = new Sanitize(config, jQuery);
+ };
+
+ var initEditableSpecific = function() {
+ if (Aloha.settings.contentHandler &&
+ Aloha.settings.contentHandler.handler &&
+ Aloha.settings.contentHandler.handler.sanitize) {
+ var config, editableSelector;
+ for (editableSelector in Aloha.settings.contentHandler.handler.sanitize) {
+ if (Aloha.settings.contentHandler.handler.sanitize.hasOwnProperty(editableSelector)) {
+ config = Aloha.settings.contentHandler.handler.sanitize[editableSelector];
+ config.filters = filters;
+ editableSelector = normalizeTagName(editableSelector);
+ editableSelector = /^[\.#]/.test(editableSelector) ? editableSelector : editableSelector.toLowerCase();
+ map[editableSelector] = new Sanitize(config, jQuery);
+ }
+ }
+ }
+ };
+
+ return {
+ init: function() {
+ if (!jQuery.isEmptyObject(map)) { return; }
+
+ initDefault();
+ initEditableSpecific();
+ },
+
+ instanceForEditable: function(editable) {
+ var editableSelector = defaultKey;
+ if (editable) {
+ var selector = null;
+
+ var containerId = '#' + editable.getId();
+ if (map[containerId]) {
+ selector = containerId;
+ }
+
+ if (!selector) {
+ var containerClasses = editable.obj.attr('class').split(' ');
+ for (var i=0; i < containerClasses.length; i++) {
+ if (map['.' + containerClasses[i]]) {
+ selector = containerClasses[i];
+ break;
+ }
+ }
+ }
+
+ if (!selector) {
+ var containerTag = editable.obj.prop('tagName').toLowerCase();
+ if (map[containerTag]) {
+ selector = containerTag;
+ }
+ }
+
+ if (selector) {
+ editableSelector = selector;
+ }
+ }
+
+ return map[editableSelector];
+ }
+ };
+ })();
+
+ var SanitizeContentHandler = ContentHandlerManager.createHandler({
+ /**
+ * Handle the content from eg. paste action and sanitize the html
+ * @param content
+ */
+ handleContent: function(content, options, editable) {
+ if (!editable) {
+ return content;
+ }
+
+ sanitizers.init();
+
+ if ( typeof content === 'string' ){
+ content = jQuery( '
' + content + '
' ).get(0);
+ } else if ( content instanceof jQuery ) {
+ content = jQuery( '
' ).append(content).get(0);
+ }
+
+ return jQuery('
').append(sanitizers.instanceForEditable(editable).clean_node(content)).html();
+ }
+ });
+
+ return SanitizeContentHandler;
+});
\ No newline at end of file