Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Image Linking *without* Image Uploading #15291

Closed
roberestarkk opened this issue Nov 1, 2023 · 10 comments
Closed

Enable Image Linking *without* Image Uploading #15291

roberestarkk opened this issue Nov 1, 2023 · 10 comments
Labels
package:image package:upload resolution:resolved This issue was already resolved (e.g. by another ticket). type:feature This issue reports a feature request (an idea for a new functionality or a missing option).

Comments

@roberestarkk
Copy link

📝 Provide a description of the new feature

For my use-case, images being used must be uploaded separately ahead of time, and only ever referenced as a URL by the content being edited in CKEditor.
We don't want to have to handle uploading via CKEditor as there are other things that must be done at upload time for an image, and the workflows for handling that stuff after the fact for one or more uploads via the editor would be... Vexing.
Consequently, we don't want to provide the option for upload to the users, if they're not going to be allowed to use it and/or it's not going to work.

Currently it is impossible to facilitate the concept of linking to an image that already exists, without having a big dedicated button for uploading (as far as I can tell).
So ideally, we would like to be able to indicate that image uploading is not allowed, and default the 'ImageRelatedThings' button to showing the Image Linking mechanism as happens with the dropdown beside it.

Especially as we're upgrading from CKEditor3 where the Image button manages the image URL and doesn't allow for Upload (in our configuration at least), so clicking the 'image' button is what they're used to doing


If you'd like to see this feature implemented, add a 👍 reaction to this post.

@roberestarkk roberestarkk added the type:feature This issue reports a feature request (an idea for a new functionality or a missing option). label Nov 1, 2023
@niegowski
Copy link
Contributor

You can use the ImageInsertViaUrl plugin instead of the ImageUpload and you should be able to use only URL to insert an image. For example:

ClassicEditor.create( document.querySelector( '#editor' ), {
	plugins: [
		Essentials,
		Autoformat,
		Bold,
		Italic,
		Heading,
		Image,
		ImageCaption,
		ImageStyle,
		ImageToolbar,
		ImageInsertViaUrl,
		AutoImage,
		Link,
		LinkImage,
		Paragraph
	],
	toolbar: [
		'heading',
		'|',
		'bold', 'italic', 'link',
		'|',
		'insertImage',
		'|',
		'undo', 'redo'
	],
	image: {
		toolbar: [
			'imageStyle:inline', 'imageStyle:block', 'imageStyle:side',
			'|',
			'toggleImageCaption', 'imageTextAlternative'
		]
	}
} )

@Witoso Witoso added package:image package:upload pending:feedback This issue is blocked by necessary feedback. labels Nov 2, 2023
@roberestarkk
Copy link
Author

I've attempted something very similar, unfortunately the ImageUpload plugin is a dependency of InsertImageViaUrl, so I can't just leave it out of the build entirely

My build also contains a great many plugins that I don't want to bother listing individually, so I've gone the "removePlugins" route instead (which hopefully serves the same purpose):

                    ClassicEditor.create(ele, {
                        removePlugins: ['imageUpload'],
                        toolbar: { 
                            shouldNotGroupWhenFull: true,
                            items: [
                              [... Omitted for brevity ...]
                                '|',
                                'outdent',
                                'indent',
                                '|',
                                'alignment',
                                '|',
                                '-',
                                'insertImage',
                                'horizontalLine',
                                'blockQuote',
                                'insertTable',
                                'htmlEmbed',
                                'mediaEmbed',
                                'undo',
                                'redo'
                            ]
                        }

However, the "InsertImageViaUrl" mechanism seems to work by serving as a drop-down from the 'ImageUpload' button, which I assume is why it's a dependency and why it appears even though I haven't specified it in the toolbar:

It seems to be a generic "Insert Image" control that gets re-used/overloaded/etc. by the different image related plugins:

Which is totally fine of course, but ideally that main image button beside the dropdown shouldn't open up a file select dialog that'll never work because ImageUpload is removed, it should fall back on opening the dropdown (or at least able to be explicitly told to do so).

@Witoso
Copy link
Member

Witoso commented Nov 3, 2023

Hmmm, it's not the case I see in our manual tests, where insert via url can be the only button.
image

LinkImage and ImageInsertViaUrl should be enough and ImageUpload is not required.

@roberestarkk
Copy link
Author

I'm having to use the Web Builder because I'm using it in an ASP application.
So in my situation I can't just leave out ImageUpload, as the WebBuilder enforces its inclusion as a dependency of ImageInsertViaURL as shown above.

Is there a way to extract the list of proper plugin names similar to the toolbar items, so that I can try listing all of them except that one explicitly, rather than removing just that one?

That's the only difference I can see...?

Just in case it's helpful, here's my full list of selected plugins, and the full text of my .create call:

ClassicEditor.create(ele, {
    removePlugins: ['imageUpload'],
    toolbar: {
        shouldNotGroupWhenFull: true,
        items: ['sourceEditing', 'selectAll', 'showBlocks', '|',
            'heading', 'style', 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', '|',
            'findAndReplace', '|',
            '-',
            'bold', 'italic', 'underline', 'strikethrough', 'link', 'bulletedList', 'numberedList', 'subscript', 'superscript', '|',
            'removeFormat', '|',
            'outdent', 'indent', '|',
            'alignment', '|',
            '-',
            'insertImage', 'horizontalLine', 'blockQuote', 'insertTable', 'htmlEmbed', 'mediaEmbed', 'undo', 'redo'
        ]
    },
    image: {
        toolbar: [
            'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|',
            'toggleImageCaption', 'imageTextAlternative'
        ]
    }
}).then(newEditor => {
    editor = newEditor;
}).catch(error => {
    console.error(error);
});

Does the Builder-version even allow you to 'remove' the plugin if it's built with it?
I might've been coasting on cached Javascript this whole time, as now it's throwing this error:

CKEditorError: plugincollection-required {"plugin":"ImageUpload","requiredBy":"ImageInsert"}
Read more: https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html#error-plugincollection-required

@Witoso
Copy link
Member

Witoso commented Nov 6, 2023

Don't load ImageInsert plugin, just load ImageInsertViaUrl.

@roberestarkk
Copy link
Author

If I expand to also doing:

removePlugins: ['ImageUpload', 'ImageInsert'],

Then both 'insertImage' and 'insertImageViaURL' give an error like

toolbarview-item-unavailable Object { item: "insertImageViaUrl" }

If I flip to just specifying the single plugin for testing purposes:

plugins: ['ImageInsertViaURL'],

It gives an error like

CKEditorError: plugincollection-plugin-not-found {"plugin":"ImageInsertViaURL"}

@Witoso
Copy link
Member

Witoso commented Nov 28, 2023

I have trouble with this as well, somehow it works in our manual test 😮‍💨  cc @filipsobol could you check?

Thankfully, #15149 will fix `insertImage` logic.

@filipsobol
Copy link
Member

I think the problem here is that we are mixing the two ways of creating the editor (editor class versus the .create() method).

In the project created using the Online Builder, there's a src/ckeditor.ts file, which is the source code used to generate the JavaScript bundle. If you add a plugin to the editor class in this file, that plugin will be added to the final bundle. Likewise, if you remove a plugin from this file, it will not be included in the bundle.

On the other hand, the .create() method allows you to remove plugins at runtime, but their code will still be in the bundle, wasting precious bandwidth, CPU and RAM resources.

With this in mind, it is best to configure the editor to your needs in src/ckeditor.ts, so that in the final project you just call the Editor.create( HTML_ELEMENT ) method without passing any additional configuration.

These are the steps needed to only allow adding images via URL, using the exact plugin setup you shared before (which was a big help, by the way 🙏).

  1. Remove ImageUpload and ImageInsert from the imports and the plugins array.
  2. Import ImageInsertViaUrl and add it to the plugins array.
  3. Replace 'imageUpload' with 'insertImage' in the toolbar.items array.

Then run the yarn command to install the required dependencies and the yarn build commands to build the new bundle.

Entire setup code
/**
   * @license Copyright (c) 2014-2023, CKSource Holding sp. z o.o. All rights reserved.
   * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
   */

  import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';

  import { Alignment } from '@ckeditor/ckeditor5-alignment';
  import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
  import {
  	Bold,
  	Italic,
  	Strikethrough,
  	Subscript,
  	Superscript,
  	Underline
  } from '@ckeditor/ckeditor5-basic-styles';
  import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
  import type { EditorConfig } from '@ckeditor/ckeditor5-core';
  import { Essentials } from '@ckeditor/ckeditor5-essentials';
  import { FindAndReplace } from '@ckeditor/ckeditor5-find-and-replace';
  import { FontBackgroundColor, FontColor, FontFamily, FontSize } from '@ckeditor/ckeditor5-font';
  import { Heading } from '@ckeditor/ckeditor5-heading';
  import { HorizontalLine } from '@ckeditor/ckeditor5-horizontal-line';
  import { HtmlEmbed } from '@ckeditor/ckeditor5-html-embed';
  import { GeneralHtmlSupport } from '@ckeditor/ckeditor5-html-support';
  import {
  	Image,
  	ImageCaption,
  	ImageResize,
  	ImageStyle,
  	ImageToolbar,
  	ImageInsertViaUrl
  } from '@ckeditor/ckeditor5-image';
  import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
  import { Link } from '@ckeditor/ckeditor5-link';
  import { List } from '@ckeditor/ckeditor5-list';
  import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
  import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
  import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office';
  import { RemoveFormat } from '@ckeditor/ckeditor5-remove-format';
  import { SelectAll } from '@ckeditor/ckeditor5-select-all';
  import { ShowBlocks } from '@ckeditor/ckeditor5-show-blocks';
  import { SourceEditing } from '@ckeditor/ckeditor5-source-editing';
  import { Style } from '@ckeditor/ckeditor5-style';
  import { Table, TableToolbar } from '@ckeditor/ckeditor5-table';
  import { TextTransformation } from '@ckeditor/ckeditor5-typing';

  // You can read more about extending the build with additional plugins in the "Installing plugins" guide.
  // See https://ckeditor.com/docs/ckeditor5/latest/installation/plugins/installing-plugins.html for details.

  class Editor extends ClassicEditor {
  	public static override builtinPlugins = [
  		Alignment,
  		Autoformat,
  		BlockQuote,
  		Bold,
  		Essentials,
  		FindAndReplace,
  		FontBackgroundColor,
  		FontColor,
  		FontFamily,
  		FontSize,
  		GeneralHtmlSupport,
  		Heading,
  		HorizontalLine,
  		HtmlEmbed,
  		Image,
  		ImageCaption,
  		ImageResize,
  		ImageStyle,
  		ImageToolbar,
  		ImageInsertViaUrl,
  		Indent,
  		IndentBlock,
  		Italic,
  		Link,
  		List,
  		MediaEmbed,
  		Paragraph,
  		PasteFromOffice,
  		RemoveFormat,
  		SelectAll,
  		ShowBlocks,
  		SourceEditing,
  		Strikethrough,
  		Style,
  		Subscript,
  		Superscript,
  		Table,
  		TableToolbar,
  		TextTransformation,
  		Underline
  	];

  	public static override defaultConfig: EditorConfig = {
  		toolbar: {
  			items: [
  				'heading',
  				'|',
  				'bold',
  				'italic',
  				'link',
  				'bulletedList',
  				'numberedList',
  				'|',
  				'outdent',
  				'indent',
  				'|',
  				'insertImage',
  				'blockQuote',
  				'insertTable',
  				'mediaEmbed',
  				'undo',
  				'redo'
  			]
  		},
  		language: 'en',
  		image: {
  			toolbar: [
  				'imageTextAlternative',
  				'toggleImageCaption',
  				'imageStyle:inline',
  				'imageStyle:block',
  				'imageStyle:side'
  			]
  		},
  		table: {
  			contentToolbar: [
  				'tableColumn',
  				'tableRow',
  				'mergeTableCells'
  			]
  		}
  	};
  }

  export default Editor;

In the consuming project, all you need to do is:

Editor.create( document.querySelector( '#editor' ) as HTMLElement );

@roberestarkk
Copy link
Author

Thank you both for your detailed response and assistance :)

On the other hand, the .create() method allows you to remove plugins at runtime, but their code will still be in the bundle, wasting precious bandwidth, CPU and RAM resources.

However, it would mean I don't need to go through the rigamarole of having all sorts of Javascript-y development tools and environments and such approved for installation (and installed and worked out how to use them) in the Client's development environment to build the bundle.
Given how infrequently they use the page with CKEditor on it, I was happy with that tradeoff, since the only 'extra' plugin seems to be ImageUpload...
I just can't quite get rid of it enough to make the button only trigger InsertImageViaURL.

Then run the yarn command to install the required dependencies and the yarn build commands to build the new bundle.

So are you basically saying that the only way to properly decouple the ImageUpload and ImageInsert plugin buttons from CKEditor, and retain just the ImageInsertViaUrl button, is to skip the online Builder entirely and do a custom build, and then import that build into my .NET project?

@roberestarkk
Copy link
Author

Assuming that to be the case, I fiddled around with installing NPM and doing the build in Windows Sandbox to get around the approvals issue, and while it threw me for a bit that the build wouldn't replace the ckeditor.js file and I had to delete it first, it has indeed allowed me to customise it as desired.

Many thanks! :)

@Witoso Witoso added resolution:resolved This issue was already resolved (e.g. by another ticket). and removed pending:feedback This issue is blocked by necessary feedback. labels Dec 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:image package:upload resolution:resolved This issue was already resolved (e.g. by another ticket). type:feature This issue reports a feature request (an idea for a new functionality or a missing option).
Projects
None yet
Development

No branches or pull requests

4 participants