瀏覽代碼

Disallow image in table.

Maciej Gołaszewski 7 年之前
父節點
當前提交
11d80a0efe

+ 6 - 1
packages/ckeditor5-table/src/converters/upcasttable.js

@@ -116,7 +116,12 @@ export function upcastTableCell( elementName ) {
 			conversionApi.consumable.consume( viewTableCell, { name: true } );
 
 			for ( const child of viewTableCell.getChildren() ) {
-				conversionApi.convertItem( child, ModelPosition.createAt( tableCell, 'end' ) );
+				const { modelCursor } = conversionApi.convertItem( child, ModelPosition.createAt( tableCell, 'end' ) );
+
+				// Ensure empty paragraph in table cell.
+				if ( modelCursor.parent.name == 'tableCell' && !modelCursor.parent.childCount ) {
+					conversionApi.writer.insertElement( 'paragraph', modelCursor );
+				}
 			}
 
 			// Set conversion result range.

+ 7 - 0
packages/ckeditor5-table/src/tableediting.js

@@ -81,6 +81,13 @@ export default class TableEditing extends Plugin {
 			}
 		} );
 
+		// Disallow image in table cell.
+		schema.addChildCheck( ( context, childDefinition ) => {
+			if ( childDefinition.name == 'image' && Array.from( context.getNames() ).includes( 'table' ) ) {
+				return false;
+			}
+		} );
+
 		// Table conversion.
 		conversion.for( 'upcast' ).add( upcastTable() );
 

+ 7 - 0
packages/ckeditor5-table/tests/_utils/utils.js

@@ -185,6 +185,13 @@ export function defaultSchema( schema, registerParagraph = true ) {
 		}
 	} );
 
+	// Disallow image in table.
+	schema.addChildCheck( ( context, childDefinition ) => {
+		if ( childDefinition.name == 'image' && Array.from( context.getNames() ).includes( 'table' ) ) {
+			return false;
+		}
+	} );
+
 	if ( registerParagraph ) {
 		schema.register( 'paragraph', { inheritAllFrom: '$block' } );
 	}

+ 76 - 19
packages/ckeditor5-table/tests/converters/upcasttable.js

@@ -6,10 +6,11 @@
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
 import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
-
-import upcastTable, { upcastTableCell } from '../../src/converters/upcasttable';
-import { defaultSchema, formatTable } from '../_utils/utils';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
+import Widget from '@ckeditor/ckeditor5-widget/src/widget';
+
+import { defaultConversion, defaultSchema, formatTable, modelTable } from '../_utils/utils';
 
 describe( 'upcastTable()', () => {
 	let editor, model;
@@ -17,29 +18,15 @@ describe( 'upcastTable()', () => {
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ Paragraph ]
+				plugins: [ Paragraph, ImageEditing, Widget ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
 				model = editor.model;
 
-				const conversion = editor.conversion;
-
 				defaultSchema( model.schema, false );
 
-				// Table conversion.
-				conversion.for( 'upcast' ).add( upcastTable() );
-
-				// Table row conversion.
-				conversion.for( 'upcast' ).add( upcastElementToElement( { model: 'tableRow', view: 'tr' } ) );
-
-				// Table cell conversion.
-				conversion.for( 'upcast' ).add( upcastTableCell( 'td' ) );
-				conversion.for( 'upcast' ).add( upcastTableCell( 'th' ) );
-
-				// Table attributes conversion.
-				conversion.attributeToAttribute( { model: 'colspan', view: 'colspan' } );
-				conversion.attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
+				defaultConversion( editor.conversion, true );
 
 				// Since this part of test tests only view->model conversion editing pipeline is not necessary
 				// so defining model->view converters won't be necessary.
@@ -397,4 +384,74 @@ describe( 'upcastTable()', () => {
 			);
 		} );
 	} );
+
+	describe( 'block contents', () => {
+		it( 'should upcast table with empty table cell to paragraph', () => {
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'<tr>' +
+							'<td>foo</td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ 'foo' ]
+			] ) );
+		} );
+
+		it( 'should upcast table with <p> in table cell', () => {
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'<tr>' +
+							'<td><p>foo</p></td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ 'foo' ]
+			] ) );
+		} );
+
+		it( 'should upcast table with multiple <p> in table cell', () => {
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'<tr>' +
+							'<td>' +
+								'<p>foo</p>' +
+								'<p>bar</p>' +
+								'<p>baz</p>' +
+							'</td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ '<paragraph>foo</paragraph><paragraph>bar</paragraph><paragraph>baz</paragraph>' ]
+			] ) );
+		} );
+
+		it( 'should upcast table with <img> in table cell to empty table cell', () => {
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'<tr>' +
+							'<td><img src="sample.png"></td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ '' ]
+			] ) );
+		} );
+	} );
 } );

+ 43 - 44
packages/ckeditor5-table/tests/table-integration.js

@@ -16,7 +16,7 @@ import {
 import { parse as parseView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
 import TableEditing from '../src/tableediting';
-import { formatTable, formattedModelTable, modelTable } from './_utils/utils';
+import { formatTable, formattedModelTable, modelTable, viewTable } from './_utils/utils';
 
 describe( 'Table feature – integration', () => {
 	describe( 'with clipboard', () => {
@@ -58,72 +58,71 @@ describe( 'Table feature – integration', () => {
 	} );
 
 	describe( 'with undo', () => {
-		it( 'fixing empty roots should be transparent to undo', () => {
+		let editor, doc, root;
+
+		beforeEach( () => {
 			return VirtualTestEditor
-				.create( { plugins: [ Paragraph, UndoEditing ] } )
+				.create( { plugins: [ Paragraph, TableEditing, Widget, UndoEditing ] } )
 				.then( newEditor => {
-					const editor = newEditor;
-					const doc = editor.model.document;
-					const root = doc.getRoot();
+					editor = newEditor;
+					doc = editor.model.document;
+					root = doc.getRoot();
+				} );
+		} );
 
-					expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
-					expect( editor.commands.get( 'undo' ).isEnabled ).to.be.false;
+		it( 'fixing empty roots should be transparent to undo', () => {
+			expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
+			expect( editor.commands.get( 'undo' ).isEnabled ).to.be.false;
 
-					editor.setData( '<p>Foobar.</p>' );
+			editor.data.set( viewTable( [ [ 'foo' ] ] ) );
 
-					editor.model.change( writer => {
-						writer.remove( root.getChild( 0 ) );
-					} );
+			expect( editor.getData() ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 
-					expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
+			editor.model.change( writer => {
+				writer.remove( root.getChild( 0 ) );
+			} );
 
-					editor.execute( 'undo' );
+			expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
 
-					expect( editor.getData() ).to.equal( '<p>Foobar.</p>' );
+			editor.execute( 'undo' );
 
-					editor.execute( 'redo' );
+			expect( editor.getData() ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 
-					expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
+			editor.execute( 'redo' );
 
-					editor.execute( 'undo' );
+			expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' );
 
-					expect( editor.getData() ).to.equal( '<p>Foobar.</p>' );
-				} );
+			editor.execute( 'undo' );
+
+			expect( editor.getData() ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 		} );
 
 		it( 'fixing empty roots should be transparent to undo - multiple roots', () => {
-			return VirtualTestEditor
-				.create( { plugins: [ Paragraph, UndoEditing ] } )
-				.then( newEditor => {
-					const editor = newEditor;
-					const doc = editor.model.document;
-					const root = doc.getRoot();
-					const otherRoot = doc.createRoot( '$root', 'otherRoot' );
+			const otherRoot = doc.createRoot( '$root', 'otherRoot' );
 
-					editor.data.set( '<p>Foobar.</p>', 'main' );
-					editor.data.set( '<p>Foobar.</p>', 'otherRoot' );
+			editor.data.set( viewTable( [ [ 'foo' ] ] ), 'main' );
+			editor.data.set( viewTable( [ [ 'foo' ] ] ), 'otherRoot' );
 
-					editor.model.change( writer => {
-						writer.remove( root.getChild( 0 ) );
-					} );
+			editor.model.change( writer => {
+				writer.remove( root.getChild( 0 ) );
+			} );
 
-					editor.model.change( writer => {
-						writer.remove( otherRoot.getChild( 0 ) );
-					} );
+			editor.model.change( writer => {
+				writer.remove( otherRoot.getChild( 0 ) );
+			} );
 
-					expect( editor.data.get( 'main' ) ).to.equal( '<p>&nbsp;</p>' );
-					expect( editor.data.get( 'otherRoot' ) ).to.equal( '<p>&nbsp;</p>' );
+			expect( editor.data.get( 'main' ) ).to.equal( '<p>&nbsp;</p>' );
+			expect( editor.data.get( 'otherRoot' ) ).to.equal( '<p>&nbsp;</p>' );
 
-					editor.execute( 'undo' );
+			editor.execute( 'undo' );
 
-					expect( editor.data.get( 'main' ) ).to.equal( '<p>&nbsp;</p>' );
-					expect( editor.data.get( 'otherRoot' ) ).to.equal( '<p>Foobar.</p>' );
+			expect( editor.data.get( 'main' ) ).to.equal( '<p>&nbsp;</p>' );
+			expect( editor.data.get( 'otherRoot' ) ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 
-					editor.execute( 'undo' );
+			editor.execute( 'undo' );
 
-					expect( editor.data.get( 'main' ) ).to.equal( '<p>Foobar.</p>' );
-					expect( editor.data.get( 'otherRoot' ) ).to.equal( '<p>Foobar.</p>' );
-				} );
+			expect( editor.data.get( 'main' ) ).to.equal( viewTable( [ [ 'foo' ] ] ) );
+			expect( editor.data.get( 'otherRoot' ) ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 		} );
 	} );
 } );

+ 10 - 1
packages/ckeditor5-table/tests/tableediting.js

@@ -7,6 +7,7 @@ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
 
 import TableEditing from '../src/tableediting';
 import { formatTable, formattedModelTable, modelTable } from './_utils/utils';
@@ -26,7 +27,7 @@ describe( 'TableEditing', () => {
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ TableEditing, Paragraph ]
+				plugins: [ TableEditing, Paragraph, ImageEditing ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
@@ -72,6 +73,7 @@ describe( 'TableEditing', () => {
 		expect( model.schema.checkChild( [ '$root', 'table', 'tableRow', 'tableCell' ], '$text' ) ).to.be.false;
 		expect( model.schema.checkChild( [ '$root', 'table', 'tableRow', 'tableCell' ], '$block' ) ).to.be.true;
 		expect( model.schema.checkChild( [ '$root', 'table', 'tableRow', 'tableCell' ], 'table' ) ).to.be.false;
+		expect( model.schema.checkChild( [ '$root', 'table', 'tableRow', 'tableCell' ], 'image' ) ).to.be.false;
 	} );
 
 	it( 'adds insertTable command', () => {
@@ -175,6 +177,13 @@ describe( 'TableEditing', () => {
 				expect( getModelData( model, { withoutSelection: true } ) )
 					.to.equal( '<table><tableRow><tableCell><paragraph>foo</paragraph></tableCell></tableRow></table>' );
 			} );
+
+			it( 'should convert table with image', () => {
+				editor.setData( '<table><tbody><tr><td><img src="sample.png"></td></tr></tbody></table>' );
+
+				expect( getModelData( model, { withoutSelection: true } ) )
+					.to.equal( '<table><tableRow><tableCell><paragraph></paragraph></tableCell></tableRow></table>' );
+			} );
 		} );
 	} );