diff --git a/packages/ckeditor5-ckbox/src/ckboxcommand.ts b/packages/ckeditor5-ckbox/src/ckboxcommand.ts index d63613c0711..0da5872fc47 100644 --- a/packages/ckeditor5-ckbox/src/ckboxcommand.ts +++ b/packages/ckeditor5-ckbox/src/ckboxcommand.ts @@ -19,6 +19,7 @@ import type { CKBoxAssetImageDefinition, CKBoxAssetLinkAttributesDefinition, CKBoxAssetLinkDefinition, + CKBoxConfig, CKBoxRawAssetDefinition } from './ckboxconfig.js'; @@ -189,6 +190,7 @@ export default class CKBoxCommand extends Command { const editor = this.editor; const model = editor.model; const shouldInsertDataId = !editor.config.get( 'ckbox.ignoreDataId' ); + const downloadableFilesConfig = editor.config.get( 'ckbox.downloadableFiles' ); // Refresh the command after firing the `ckbox:*` event. this.on( 'ckbox', () => { @@ -230,6 +232,7 @@ export default class CKBoxCommand extends Command { const assetsToProcess = prepareAssets( { assets, + downloadableFilesConfig, isImageAllowed: imageCommand.isEnabled, isLinkAllowed: linkCommand.isEnabled } ); @@ -379,7 +382,8 @@ export default class CKBoxCommand extends Command { * Parses the chosen assets into the internal data format. Filters out chosen assets that are not allowed. */ function prepareAssets( - { assets, isImageAllowed, isLinkAllowed }: { + { downloadableFilesConfig, assets, isImageAllowed, isLinkAllowed }: { + downloadableFilesConfig: CKBoxConfig['downloadableFiles']; assets: Array; isImageAllowed: boolean; isLinkAllowed: boolean; @@ -395,7 +399,7 @@ function prepareAssets( { id: asset.data.id, type: 'link', - attributes: prepareLinkAssetAttributes( asset ) + attributes: prepareLinkAssetAttributes( downloadableFilesConfig, asset ) } as const ) .filter( asset => asset.type === 'image' ? isImageAllowed : isLinkAllowed ); @@ -424,12 +428,16 @@ export function prepareImageAssetAttributes( asset: CKBoxRawAssetDefinition ): C /** * Parses the assets attributes into the internal data format. * - * @param origin The base URL for assets inserted into the editor. + * @param config The CKBox download asset configuration. + * @param asset The asset to prepare the attributes for. */ -function prepareLinkAssetAttributes( asset: CKBoxRawAssetDefinition ): CKBoxAssetLinkAttributesDefinition { +function prepareLinkAssetAttributes( + config: CKBoxConfig['downloadableFiles'], + asset: CKBoxRawAssetDefinition +): CKBoxAssetLinkAttributesDefinition { return { linkName: asset.data.name, - linkHref: getAssetUrl( asset ) + linkHref: getAssetUrl( config, asset ) }; } @@ -449,16 +457,54 @@ function isImage( asset: CKBoxRawAssetDefinition ) { /** * Creates the URL for the asset. * - * @param origin The base URL for assets inserted into the editor. + * @param config The CKBox download asset configuration. + * @param asset The asset to create the URL for. */ -function getAssetUrl( asset: CKBoxRawAssetDefinition ) { +function getAssetUrl( config: CKBoxConfig['downloadableFiles'], asset: CKBoxRawAssetDefinition ) { const url = new URL( asset.data.url ); - url.searchParams.set( 'download', 'true' ); + if ( isDownloadableAsset( config, asset ) ) { + url.searchParams.set( 'download', 'true' ); + } return url.toString(); } +/** + * Determines if download should be enabled for given asset based on configuration. + * + * @param config The CKBox download asset configuration. + * @param asset The asset to check. + */ +function isDownloadableAsset( + config: CKBoxConfig['downloadableFiles'], + asset: CKBoxRawAssetDefinition +): boolean { + // If the configuration is not provided, the asset is always downloadable. + if ( config === undefined ) { + return true; + } + + // If the configuration is a boolean, it's the global setting for all assets. + if ( typeof config === 'boolean' ) { + return config; + } + + // If the configuration is an array, it's a list of file extensions that should be downloadable. + if ( Array.isArray( config ) ) { + const extension = asset.data.name.split( '.' ).pop()?.toLowerCase(); + + return extension ? config.includes( extension ) : false; + } + + // If the configuration is a function, it's a custom logic to determine if the asset is downloadable. + if ( typeof config === 'function' ) { + return config( asset ); + } + + return false; +} + /** * Fired when the command is executed, the dialog is closed or the assets are chosen. * diff --git a/packages/ckeditor5-ckbox/src/ckboxconfig.ts b/packages/ckeditor5-ckbox/src/ckboxconfig.ts index f53cd9c363a..341af48e025 100644 --- a/packages/ckeditor5-ckbox/src/ckboxconfig.ts +++ b/packages/ckeditor5-ckbox/src/ckboxconfig.ts @@ -173,6 +173,31 @@ export interface CKBoxConfig { * ``` */ choosableFileExtensions?: Array; + + /** + * Configures how the download attribute should be handled for inserted links. + * + * You can set this to: + * * `false` - to disable download attribute for all assets + * * `true` - to enable download attribute for all assets (default) + * * an array of extensions (e.g. ['pdf', 'zip']) - to enable download only for specific file types + * * a function that takes asset data and returns boolean - for custom logic + * + * ```ts + * const ckboxConfig = { + * // Enable download for specific extensions + * downloadableFiles: ['zip', 'rar', '7z', 'pdf'], + * + * // Or use a custom function + * downloadableFiles: ( asset ) => { + * return asset.data.name.endsWith( '.pdf' ); + * } + * }; + * ``` + * + * @default true + */ + downloadableFiles?: boolean | Array | ( ( asset: CKBoxRawAssetDefinition ) => boolean ); } export interface CKBoxDialogConfig {