diff --git a/composer.json b/composer.json
index dba36b34cea4cdc3adcc84cab54dd2034920c30c..9c7e329b12e500ed1be85965e95ffbc93c5606ea 100644
--- a/composer.json
+++ b/composer.json
@@ -58,11 +58,30 @@
                     "type": "zip"
                 }
             }
+        },
+        {
+            "type": "package",
+            "package": {
+              "name": "ckeditor/indentblock",
+              "version": "4.8.0",
+              "type": "drupal-library",
+              "extra": {
+                "installer-name": "ckeditor.indentblock"
+              },
+              "dist": {
+                "url": "https://download.ckeditor.com/indentblock/releases/indentblock_4.8.0.zip",
+                "type": "zip"
+              },
+              "require": {
+                "composer/installers": "~1.0"
+              }
+            }
         }
     ],
     "require": {
         "php": ">=7.0.8",
         "browserstate/history.js": "^1.8",
+        "ckeditor/indentblock": "4.8.0",
         "composer/installers": "^1.0.20",
         "cweagans/composer-patches": "^1.0",
         "desandro/imagesloaded": "4.1.4",
diff --git a/composer.lock b/composer.lock
index 34c2d9ad4b9fb63a919519ed454f8e2345c183b7..265e32e693ed73033aa8ae10f3ec000bc7f91c17 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "24aed79b1c402975953b673070fe8a7e",
+    "content-hash": "1c264aa2c7957e793131a928d4e80a02",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -132,6 +132,23 @@
             },
             "type": "drupal-library"
         },
+        {
+            "name": "ckeditor/indentblock",
+            "version": "4.8.0",
+            "dist": {
+                "type": "zip",
+                "url": "https://download.ckeditor.com/indentblock/releases/indentblock_4.8.0.zip",
+                "reference": null,
+                "shasum": null
+            },
+            "require": {
+                "composer/installers": "~1.0"
+            },
+            "type": "drupal-library",
+            "extra": {
+                "installer-name": "ckeditor.indentblock"
+            }
+        },
         {
             "name": "commerceguys/addressing",
             "version": "v1.0.0",
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 3b6483fb1c85fedce7e718743f44e68ba91162af..42f12c17650196a3c03746eb6ae2a5b6cee85eeb 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -131,6 +131,25 @@
         "type": "drupal-library",
         "installation-source": "source"
     },
+    {
+        "name": "ckeditor/indentblock",
+        "version": "4.8.0",
+        "version_normalized": "4.8.0.0",
+        "dist": {
+            "type": "zip",
+            "url": "https://download.ckeditor.com/indentblock/releases/indentblock_4.8.0.zip",
+            "reference": null,
+            "shasum": null
+        },
+        "require": {
+            "composer/installers": "~1.0"
+        },
+        "type": "drupal-library",
+        "extra": {
+            "installer-name": "ckeditor.indentblock"
+        },
+        "installation-source": "dist"
+    },
     {
         "name": "commerceguys/addressing",
         "version": "v1.0.0",
diff --git a/web/libraries/ckeditor.indentblock/plugin.js b/web/libraries/ckeditor.indentblock/plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..e501941de5a272297091bdc97016466d2cd0722a
--- /dev/null
+++ b/web/libraries/ckeditor.indentblock/plugin.js
@@ -0,0 +1,312 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @fileOverview Handles the indentation of block elements.
+ */
+
+( function() {
+	'use strict';
+
+	var $listItem = CKEDITOR.dtd.$listItem,
+		$list = CKEDITOR.dtd.$list,
+		TRISTATE_DISABLED = CKEDITOR.TRISTATE_DISABLED,
+		TRISTATE_OFF = CKEDITOR.TRISTATE_OFF;
+
+	CKEDITOR.plugins.add( 'indentblock', {
+		requires: 'indent',
+		init: function( editor ) {
+			var globalHelpers = CKEDITOR.plugins.indent,
+				classes = editor.config.indentClasses;
+
+			// Register commands.
+			globalHelpers.registerCommands( editor, {
+				indentblock: new commandDefinition( editor, 'indentblock', true ),
+				outdentblock: new commandDefinition( editor, 'outdentblock' )
+			} );
+
+			function commandDefinition() {
+				globalHelpers.specificDefinition.apply( this, arguments );
+
+				this.allowedContent = {
+					'div h1 h2 h3 h4 h5 h6 ol p pre ul': {
+						// Do not add elements, but only text-align style if element is validated by other rule.
+						propertiesOnly: true,
+						styles: !classes ? 'margin-left,margin-right' : null,
+						classes: classes || null
+					}
+				};
+
+				this.contentTransformations = [
+					[ 'div: splitMarginShorthand' ],
+					[ 'h1: splitMarginShorthand' ],
+					[ 'h2: splitMarginShorthand' ],
+					[ 'h3: splitMarginShorthand' ],
+					[ 'h4: splitMarginShorthand' ],
+					[ 'h5: splitMarginShorthand' ],
+					[ 'h6: splitMarginShorthand' ],
+					[ 'ol: splitMarginShorthand' ],
+					[ 'p: splitMarginShorthand' ],
+					[ 'pre: splitMarginShorthand' ],
+					[ 'ul: splitMarginShorthand' ]
+				];
+
+				if ( this.enterBr )
+					this.allowedContent.div = true;
+
+				this.requiredContent = ( this.enterBr ? 'div' : 'p' ) +
+					( classes ? '(' + classes.join( ',' ) + ')' : '{margin-left}' );
+
+				this.jobs = {
+					'20': {
+						refresh: function( editor, path ) {
+							var firstBlock = path.block || path.blockLimit;
+
+							// Switch context from somewhere inside list item to list item,
+							// if not found just assign self (doing nothing).
+							if ( !firstBlock.is( $listItem ) ) {
+								var ascendant = firstBlock.getAscendant( $listItem );
+
+								firstBlock = ( ascendant && path.contains( ascendant ) ) || firstBlock;
+							}
+
+							// Switch context from list item to list
+							// because indentblock can indent entire list
+							// but not a single list element.
+
+							if ( firstBlock.is( $listItem ) )
+								firstBlock = firstBlock.getParent();
+
+							//	[-] Context in the path or ENTER_BR
+							//
+							//		Don't try to indent if the element is out of
+							//		this plugin's scope. This assertion is omitted
+							//		if ENTER_BR is in use since there may be no block
+							//		in the path.
+
+							if ( !this.enterBr && !this.getContext( path ) )
+								return TRISTATE_DISABLED;
+
+							else if ( classes ) {
+
+								//	[+] Context in the path or ENTER_BR
+								//	[+] IndentClasses
+								//
+								//		If there are indentation classes, check if reached
+								//		the highest level of indentation. If so, disable
+								//		the command.
+
+								if ( indentClassLeft.call( this, firstBlock, classes ) )
+									return TRISTATE_OFF;
+								else
+									return TRISTATE_DISABLED;
+							} else {
+
+								//	[+] Context in the path or ENTER_BR
+								//	[-] IndentClasses
+								//	[+] Indenting
+								//
+								//		No indent-level limitations due to indent classes.
+								//		Indent-like command can always be executed.
+
+								if ( this.isIndent )
+									return TRISTATE_OFF;
+
+								//	[+] Context in the path or ENTER_BR
+								//	[-] IndentClasses
+								//	[-] Indenting
+								//	[-] Block in the path
+								//
+								//		No block in path. There's no element to apply indentation
+								//		so disable the command.
+
+								else if ( !firstBlock )
+									return TRISTATE_DISABLED;
+
+								//	[+] Context in the path or ENTER_BR
+								//	[-] IndentClasses
+								//	[-] Indenting
+								//	[+] Block in path.
+								//
+								//		Not using indentClasses but there is firstBlock.
+								//		We can calculate current indentation level and
+								//		try to increase/decrease it.
+
+								else {
+									return CKEDITOR[
+										( getIndent( firstBlock ) || 0 ) <= 0 ? 'TRISTATE_DISABLED' : 'TRISTATE_OFF'
+									];
+								}
+							}
+						},
+
+						exec: function( editor ) {
+							var selection = editor.getSelection(),
+								range = selection && selection.getRanges()[ 0 ],
+								nearestListBlock;
+
+							// If there's some list in the path, then it will be
+							// a full-list indent by increasing or decreasing margin property.
+							if ( ( nearestListBlock = editor.elementPath().contains( $list ) ) )
+								indentElement.call( this, nearestListBlock, classes );
+
+							// If no list in the path, use iterator to indent all the possible
+							// paragraphs in the range, creating them if necessary.
+							else {
+								var iterator = range.createIterator(),
+									enterMode = editor.config.enterMode,
+									block;
+
+								iterator.enforceRealBlocks = true;
+								iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
+
+								while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) ) {
+									if ( !block.isReadOnly() )
+										indentElement.call( this, block, classes );
+								}
+							}
+
+							return true;
+						}
+					}
+				};
+			}
+
+			CKEDITOR.tools.extend( commandDefinition.prototype, globalHelpers.specificDefinition.prototype, {
+				// Elements that, if in an elementpath, will be handled by this
+				// command. They restrict the scope of the plugin.
+				context: { div: 1, dl: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, ul: 1, ol: 1, p: 1, pre: 1, table: 1 },
+
+				// A regex built on config#indentClasses to detect whether an
+				// element has some indentClass or not.
+				classNameRegex: classes ? new RegExp( '(?:^|\\s+)(' + classes.join( '|' ) + ')(?=$|\\s)' ) : null
+			} );
+		}
+	} );
+
+	// Generic indentation procedure for indentation of any element
+	// either with margin property or config#indentClass.
+	function indentElement( element, classes, dir ) {
+		if ( element.getCustomData( 'indent_processed' ) )
+			return;
+
+		var editor = this.editor,
+			isIndent = this.isIndent;
+
+		if ( classes ) {
+			// Transform current class f to indent step index.
+			var indentClass = element.$.className.match( this.classNameRegex ),
+				indentStep = 0;
+
+			if ( indentClass ) {
+				indentClass = indentClass[ 1 ];
+				indentStep = CKEDITOR.tools.indexOf( classes, indentClass ) + 1;
+			}
+
+			// Operate on indent step index, transform indent step index
+			// back to class name.
+			if ( ( indentStep += isIndent ? 1 : -1 ) < 0 )
+				return;
+
+			indentStep = Math.min( indentStep, classes.length );
+			indentStep = Math.max( indentStep, 0 );
+			element.$.className = CKEDITOR.tools.ltrim( element.$.className.replace( this.classNameRegex, '' ) );
+
+			if ( indentStep > 0 )
+				element.addClass( classes[ indentStep - 1 ] );
+		} else {
+			var indentCssProperty = getIndentCss( element, dir ),
+				currentOffset = parseInt( element.getStyle( indentCssProperty ), 10 ),
+				indentOffset = editor.config.indentOffset || 40;
+
+			if ( isNaN( currentOffset ) )
+				currentOffset = 0;
+
+			currentOffset += ( isIndent ? 1 : -1 ) * indentOffset;
+
+			if ( currentOffset < 0 )
+				return;
+
+			currentOffset = Math.max( currentOffset, 0 );
+			currentOffset = Math.ceil( currentOffset / indentOffset ) * indentOffset;
+
+			element.setStyle(
+				indentCssProperty,
+				currentOffset ? currentOffset + ( editor.config.indentUnit || 'px' ) : ''
+			);
+
+			if ( element.getAttribute( 'style' ) === '' )
+				element.removeAttribute( 'style' );
+		}
+
+		CKEDITOR.dom.element.setMarker( this.database, element, 'indent_processed', 1 );
+
+		return;
+	}
+
+	// Method that checks if current indentation level for an element
+	// reached the limit determined by config#indentClasses.
+	function indentClassLeft( node, classes ) {
+		var indentClass = node.$.className.match( this.classNameRegex ),
+			isIndent = this.isIndent;
+
+		// If node has one of the indentClasses:
+		//	* If it holds the topmost indentClass, then
+		//	  no more classes have left.
+		//	* If it holds any other indentClass, it can use the next one
+		//	  or the previous one.
+		//	* Outdent is always possible. We can remove indentClass.
+		if ( indentClass )
+			return isIndent ? indentClass[ 1 ] != classes.slice( -1 ) : true;
+
+		// If node has no class which belongs to indentClasses,
+		// then it is at 0-level. It can be indented but not outdented.
+		else
+			return isIndent;
+	}
+
+	// Determines indent CSS property for an element according to
+	// what is the direction of such element. It can be either `margin-left`
+	// or `margin-right`.
+	function getIndentCss( element, dir ) {
+		return ( dir || element.getComputedStyle( 'direction' ) ) == 'ltr' ? 'margin-left' : 'margin-right';
+	}
+
+	// Return the numerical indent value of margin-left|right of an element,
+	// considering element's direction. If element has no margin specified,
+	// NaN is returned.
+	function getIndent( element ) {
+		return parseInt( element.getStyle( getIndentCss( element ) ), 10 );
+	}
+} )();
+
+/**
+ * A list of classes to use for indenting the contents. If set to `null`, no classes will be used
+ * and instead the {@link #indentUnit} and {@link #indentOffset} properties will be used.
+ *
+ *		// Use the 'Indent1', 'Indent2', 'Indent3' classes.
+ *		config.indentClasses = ['Indent1', 'Indent2', 'Indent3'];
+ *
+ * @cfg {Array} [indentClasses=null]
+ * @member CKEDITOR.config
+ */
+
+/**
+ * The size in {@link CKEDITOR.config#indentUnit indentation units} of each indentation step.
+ *
+ *		config.indentOffset = 4;
+ *
+ * @cfg {Number} [indentOffset=40]
+ * @member CKEDITOR.config
+ */
+
+/**
+ * The unit used for {@link CKEDITOR.config#indentOffset indentation offset}.
+ *
+ *		config.indentUnit = 'em';
+ *
+ * @cfg {String} [indentUnit='px']
+ * @member CKEDITOR.config
+ */