ckfindercommand.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global window */
  6. /**
  7. * @module ckfinder/ckfindercommand
  8. */
  9. import Command from '@ckeditor/ckeditor5-core/src/command';
  10. import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
  11. import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  12. import { findOptimalInsertionPosition } from '@ckeditor/ckeditor5-widget/src/utils';
  13. /**
  14. * The CKFinder command. It is used by the {@link module:ckfinder/ckfinderediting~CKFinderEditing ckfinder editng feature}
  15. * to open a CKFinder file browser to insert an image or a link to a file into content.
  16. *
  17. * editor.execute( 'ckfinder' );
  18. *
  19. * @extends module:core/command~Command
  20. */
  21. export default class CKFinderCommand extends Command {
  22. /**
  23. * @inheritDoc
  24. */
  25. constructor( editor ) {
  26. super( editor );
  27. // Remove default document listener to lower its priority.
  28. this.stopListening( this.editor.model.document, 'change' );
  29. // Lower this command listener priority to be sure that refresh() will be called after link & image refresh.
  30. this.listenTo( this.editor.model.document, 'change', () => this.refresh(), { priority: 'low' } );
  31. }
  32. /**
  33. * @inheritDoc
  34. */
  35. refresh() {
  36. const imageCommand = this.editor.commands.get( 'imageUpload' );
  37. const linkCommand = this.editor.commands.get( 'link' );
  38. // The CKFinder command is enabled when one of image or link command is enabled.
  39. this.isEnabled = imageCommand && linkCommand && ( imageCommand.isEnabled || linkCommand.isEnabled );
  40. }
  41. /**
  42. * @inheritDoc
  43. */
  44. execute() {
  45. const editor = this.editor;
  46. const openerMethod = this.editor.config.get( 'ckfinder.openerMethod' ) || 'modal';
  47. if ( openerMethod != 'popup' && openerMethod != 'modal' ) {
  48. throw new CKEditorError( 'ckfinder-unknown-openerMethod: The openerMethod config option must by "popup" or "modal".' );
  49. }
  50. const options = this.editor.config.get( 'ckfinder.options' ) || {};
  51. options.chooseFiles = true;
  52. // Pass the lang code to the CKFinder if not defined by user.
  53. if ( !options.language ) {
  54. options.language = editor.locale.language;
  55. }
  56. // The onInit method allows to extend CKFinder's behavior. It is used to attach event listeners to file choosing related events.
  57. options.onInit = finder => {
  58. finder.on( 'files:choose', evt => {
  59. const files = evt.data.files.toArray();
  60. // Insert links
  61. const links = files.filter( file => !file.isImage() );
  62. const images = files.filter( file => file.isImage() );
  63. for ( const linkFile of links ) {
  64. editor.execute( 'link', linkFile.getUrl() );
  65. }
  66. const imagesUrls = [];
  67. for ( const image of images ) {
  68. const url = image.getUrl();
  69. imagesUrls.push( url ? url : finder.request( 'file:getProxyUrl', { file: image } ) );
  70. }
  71. if ( imagesUrls.length ) {
  72. insertImages( editor, imagesUrls );
  73. }
  74. } );
  75. finder.on( 'file:choose:resizedImage', evt => {
  76. const resizedUrl = evt.data.resizedUrl;
  77. if ( !resizedUrl ) {
  78. const notification = editor.plugins.get( Notification );
  79. const t = editor.locale.t;
  80. notification.showWarning( t( 'Could not obtain resized image URL.' ), {
  81. title: t( 'Selecting resized image failed' ),
  82. namespace: 'ckfinder'
  83. } );
  84. return;
  85. }
  86. insertImages( editor, [ resizedUrl ] );
  87. } );
  88. };
  89. window.CKFinder[ openerMethod ]( options );
  90. }
  91. }
  92. function insertImages( editor, urls ) {
  93. const imageCommand = editor.commands.get( 'imageUpload' );
  94. // Check if inserting an image is actually possible - it might be possible to only insert a link.
  95. if ( !imageCommand.isEnabled ) {
  96. const notification = editor.plugins.get( Notification );
  97. const t = editor.locale.t;
  98. notification.showWarning( t( 'Could not insert image at the current position.' ), {
  99. title: t( 'Inserting image failed' ),
  100. namespace: 'ckfinder'
  101. } );
  102. return;
  103. }
  104. const model = editor.model;
  105. // The first image will be inserted according to image inserting algorithm. Next one after the previous one.
  106. let insertAt = findOptimalInsertionPosition( model.document.selection, model );
  107. model.change( writer => {
  108. for ( const url of urls ) {
  109. const imageElement = writer.createElement( 'image', { src: url } );
  110. // Insert image & update the selection.
  111. model.insertContent( imageElement, insertAt );
  112. // Inserting an image might've failed due to schema regulations.
  113. if ( imageElement.parent ) {
  114. writer.setSelection( imageElement, 'on' );
  115. insertAt = writer.createPositionAfter( imageElement );
  116. }
  117. }
  118. } );
  119. }