.../modules/media/js/plugins/drupalmedia/plugin.js | 18 +++++ .../media_library/css/media_library.module.css | 5 ++ .../js/plugins/drupalmedialibrary/plugin.js | 51 ++++++++++++ .../media_library/media_library.services.yml | 2 + .../media_library/src/MediaLibraryEditorOpener.php | 46 +++++++++++ .../Plugin/CKEditorPlugin/DrupalMediaLibrary.php | 91 ++++++++++++++++++++++ 6 files changed, 213 insertions(+) diff --git a/core/modules/media/js/plugins/drupalmedia/plugin.js b/core/modules/media/js/plugins/drupalmedia/plugin.js index 88fcedd13b..bf42b179ae 100644 --- a/core/modules/media/js/plugins/drupalmedia/plugin.js +++ b/core/modules/media/js/plugins/drupalmedia/plugin.js @@ -108,6 +108,18 @@ }, data: function (event) { + if (this.oldData) { + // In the CKEditor Widget, we use `hasCaption` to track whether the embedded media has a caption or not. The + // filter on the other hand simply uses an empty or non-existing `data-caption` attribute. So some + // conversion work is necessary to allow an empty caption with placeholder text. + if (!this.data.hasCaption && this.oldData.hasCaption) { + delete this.data.attributes['data-caption']; + } + else if (this.data.hasCaption && !this.oldData.hasCaption) { + this.data.attributes['data-caption'] = ' '; + } + } + if (this._previewNeedsServersideUpdate()) { editor.fire('lockSnapshot'); this._tearDownDynamicEditables(); @@ -151,6 +163,12 @@ widget.setData('attributes', mediaAttributes); }); this.captionEditableMutationObserver.observe(captionEditable.$, config); + // When starting out with an empty caption, CKEditor automatically injects a
that we need to delete. + // @see core/modules/ckeditor/js/plugins/drupalimagecaption/plugin.es6.js + if (captionEditable.$.childNodes.length === 1 && captionEditable.$.childNodes.item(0).nodeName === 'BR') { + captionEditable.$.removeChild(captionEditable.$.childNodes.item(0)); + } + } }, diff --git a/core/modules/media_library/css/media_library.module.css b/core/modules/media_library/css/media_library.module.css index 3e2cdde7d1..e95f1ca329 100644 --- a/core/modules/media_library/css/media_library.module.css +++ b/core/modules/media_library/css/media_library.module.css @@ -2,6 +2,11 @@ * @file media_library.module.css */ +/* Work around dialogOptions imposed by Drupal.ckeditor.openDialog(). */ +.ui-dialog--narrow.media-library-widget-modal { + max-width: 75%; +} + .media-library-wrapper { display: flex; } diff --git a/core/modules/media_library/js/plugins/drupalmedialibrary/plugin.js b/core/modules/media_library/js/plugins/drupalmedialibrary/plugin.js new file mode 100644 index 0000000000..4095e7add4 --- /dev/null +++ b/core/modules/media_library/js/plugins/drupalmedialibrary/plugin.js @@ -0,0 +1,51 @@ +/** + * @file + * Drupal Media Library plugin. + */ + +(function (Drupal, CKEDITOR) { + + "use strict"; + + CKEDITOR.plugins.add('drupalmedialibrary', { + requires: 'drupalmedia', + + beforeInit: function (editor) { + editor.addCommand('drupalmedialibrary', { + allowedContent: 'drupal-media[!data-entity-type,!data-entity-uuid,data-align,data-caption,alt,title]', + requiredContent: 'drupal-media[data-entity-type,data-entity-uuid]', + modes: { wysiwyg: 1 }, + canUndo: true, + exec: function (editor) { + var saveCallback = function (values) { + editor.fire('saveSnapshot'); + var mediaElement = editor.document.createElement('drupal-media'); + var attributes = values.attributes; + for (var key in attributes) { + mediaElement.setAttribute(key, attributes[key]); + } + editor.insertHtml(mediaElement.getOuterHtml()); + editor.fire('saveSnapshot'); + }; + + // @see \Drupal\media_library\MediaLibraryUiBuilder::dialogOptions() + Drupal.ckeditor.openDialog( + editor, + editor.config.DrupalMediaLibrary_url, + {}, + saveCallback, + editor.config.DrupalMediaLibrary_dialogOptions + ); + } + }); + + if (editor.ui.addButton) { + editor.ui.addButton('DrupalMediaLibrary', { + label: Drupal.t('Insert from Media Library'), + command: 'drupalmedialibrary' + }); + } + } + }); + +})(Drupal, CKEDITOR); diff --git a/core/modules/media_library/media_library.services.yml b/core/modules/media_library/media_library.services.yml index b2d06643b5..ff50eeb42e 100644 --- a/core/modules/media_library/media_library.services.yml +++ b/core/modules/media_library/media_library.services.yml @@ -13,3 +13,5 @@ services: media_library.opener.field_widget: class: Drupal\media_library\MediaLibraryFieldWidgetOpener arguments: ['@entity_type.manager'] + media_library.opener.editor: + class: Drupal\media_library\MediaLibraryEditorOpener diff --git a/core/modules/media_library/src/MediaLibraryEditorOpener.php b/core/modules/media_library/src/MediaLibraryEditorOpener.php new file mode 100644 index 0000000000..622737a37d --- /dev/null +++ b/core/modules/media_library/src/MediaLibraryEditorOpener.php @@ -0,0 +1,46 @@ +getOpenerParameters()['filter_format_id']; + return FilterFormat::load($filter_format_id) + ->access('use', $account, TRUE); + } + + /** + * {@inheritdoc} + */ + public function getSelectionResponse(MediaLibraryState $state, array $selected_ids) { + $selected_media = Media::load(reset($selected_ids)); + + $response = new AjaxResponse(); + $values = [ + 'attributes' => [ + 'data-entity-type' => 'media', + 'data-entity-uuid' => $selected_media->uuid(), + 'data-align' => 'center', + ], + ]; + $response->addCommand(new EditorDialogSave($values)); + + return $response; + } + +} diff --git a/core/modules/media_library/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php b/core/modules/media_library/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php new file mode 100644 index 0000000000..4630d1c380 --- /dev/null +++ b/core/modules/media_library/src/Plugin/CKEditorPlugin/DrupalMediaLibrary.php @@ -0,0 +1,91 @@ + $editor->getFilterFormat()->id()] + ); + + return [ + 'DrupalMediaLibrary_url' => Url::fromRoute('media_library.ui') + ->setOption('query', $state->all()) + ->toString(TRUE) + ->getGeneratedUrl(), + 'DrupalMediaLibrary_dialogOptions' => MediaLibraryUiBuilder::dialogOptions(), + ]; + } + + /** + * {@inheritdoc} + */ + public function getButtons() { + return [ + 'DrupalMediaLibrary' => [ + 'label' => $this->t('Insert from Media Library'), + // @todo: new icon! + 'image' => '', + ], + ]; + } + +}