Skip to content

Commit

Permalink
Allow to specify which files choosen from CKBox are downloadable.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati365 committed Jan 8, 2025
1 parent 4896a62 commit 351251d
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 9 deletions.
44 changes: 36 additions & 8 deletions packages/ckeditor5-ckbox/src/ckboxcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
CKBoxAssetImageDefinition,
CKBoxAssetLinkAttributesDefinition,
CKBoxAssetLinkDefinition,
CKBoxConfig,
CKBoxRawAssetDefinition
} from './ckboxconfig.js';

Expand Down Expand Up @@ -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<CKBoxEvent>( 'ckbox', () => {
Expand Down Expand Up @@ -230,6 +232,7 @@ export default class CKBoxCommand extends Command {

const assetsToProcess = prepareAssets( {
assets,
downloadableFilesConfig,
isImageAllowed: imageCommand.isEnabled,
isLinkAllowed: linkCommand.isEnabled
} );
Expand Down Expand Up @@ -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<CKBoxRawAssetDefinition>;
isImageAllowed: boolean;
isLinkAllowed: boolean;
Expand All @@ -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 );
Expand Down Expand Up @@ -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 )
};
}

Expand All @@ -449,16 +457,36 @@ 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 ( typeof config === 'function' ) {
return config( asset );
}

return true;
}

/**
* Fired when the command is executed, the dialog is closed or the assets are chosen.
*
Expand Down
18 changes: 18 additions & 0 deletions packages/ckeditor5-ckbox/src/ckboxconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,19 @@ export interface CKBoxConfig {
* ```
*/
choosableFileExtensions?: Array<string>;

/**
* Controls when to enable the download attribute for inserted links.
*
* By default, files are downloadable.
*
* ```ts
* const ckboxConfig = {
* downloadableFiles: asset => asset.data.extension !== 'pdf'
* };
* ```
*/
downloadableFiles?: ( asset: CKBoxRawAssetDefinition ) => boolean;
}

export interface CKBoxDialogConfig {
Expand Down Expand Up @@ -461,6 +474,11 @@ export interface CKBoxRawAssetDataDefinition {
* The asset location.
*/
url: string;

/**
* The asset type.
*/
extension?: string;
}

/**
Expand Down
138 changes: 138 additions & 0 deletions packages/ckeditor5-ckbox/tests/ckboxcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,144 @@ describe( 'CKBoxCommand', () => {

sinon.assert.calledOnce( focusSpy );
} );

describe( 'downloadable files configuration', () => {
let command;

beforeEach( async () => {
assets = {
images: [
{
data: {
id: 'image-id1',
extension: 'png',
metadata: {
width: 100,
height: 100
},
name: 'image1',
imageUrls: {
100: 'https://example.com/workspace1/assets/image-id1/images/100.webp',
default: 'https://example.com/workspace1/assets/image-id1/images/100.png'
},
url: 'https://example.com/workspace1/assets/image-id1/file'
}
}
],
links: [
{
data: {
id: 'link-id1',
extension: 'pdf',
name: 'file1',
url: 'https://example.com/workspace1/assets/link-id1/file'
}
},
{
data: {
id: 'link-id2',
extension: 'zip',
name: 'file2',
url: 'https://example.com/workspace1/assets/link-id2/file'
}
}
]
};
} );

it( 'should add download parameter to URLs by default', async () => {
const editor = await createTestEditor( {
ckbox: {
tokenUrl: 'foo'
}
} );

command = editor.commands.get( 'ckbox' );
onChoose = command._prepareOptions().assets.onChoose;

onChoose( [ assets.links[ 1 ] ] );

expect( getModelData( editor.model ) ).to.equal(
'<paragraph>' +
'[<$text ' +
'ckboxLinkId="link-id2" ' +
'linkHref="https://example.com/workspace1/assets/link-id2/file?download=true">' +
'file2' +
'</$text>]' +
'</paragraph>'
);

await editor.destroy();
} );

it( 'should allow custom function for determining downloadable files', async () => {
const editor = await createTestEditor( {
ckbox: {
tokenUrl: 'foo',
downloadableFiles: asset => asset.data.name === 'file1'
}
} );

const command = editor.commands.get( 'ckbox' );
const onChoose = command._prepareOptions().assets.onChoose;

// file1 should have download parameter
onChoose( [ assets.links[ 0 ] ] );

expect( getModelData( editor.model ) ).to.equal(
'<paragraph>' +
'[<$text ' +
'ckboxLinkId="link-id1" ' +
'linkHref="https://example.com/workspace1/assets/link-id1/file?download=true">' +
'file1' +
'</$text>]' +
'</paragraph>'
);

// file2 should not have download parameter
editor.setData( '' );
onChoose( [ assets.links[ 1 ] ] );

expect( getModelData( editor.model ) ).to.equal(
'<paragraph>' +
'[<$text ' +
'ckboxLinkId="link-id2" ' +
'linkHref="https://example.com/workspace1/assets/link-id2/file">' +
'file2' +
'</$text>]' +
'</paragraph>'
);

await editor.destroy();
} );

it( 'should not affect image assets', async () => {
const editor = await createTestEditor( {
ckbox: {
tokenUrl: 'foo',
downloadableFiles: false
}
} );

const command = editor.commands.get( 'ckbox' );
const onChoose = command._prepareOptions().assets.onChoose;

onChoose( [ assets.images[ 0 ] ] );

expect( getModelData( editor.model ) ).to.equal(
'[<imageBlock ' +
'alt="" ' +
'ckboxImageId="image-id1" ' +
'height="100" ' +
'sources="[object Object]" ' +
'src="https://example.com/workspace1/assets/image-id1/images/100.png" ' +
'width="100">' +
'</imageBlock>]'
);

await editor.destroy();
} );
} );
} );
} );
} );
Expand Down
3 changes: 2 additions & 1 deletion packages/ckeditor5-ckbox/tests/manual/ckbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ ClassicEditor
ckbox: {
tokenUrl: TOKEN_URL,
forceDemoLabel: true,
allowExternalImagesEditing: [ /^data:/, /^i.imgur.com\//, 'origin' ]
allowExternalImagesEditing: [ /^data:/, /^i.imgur.com\//, 'origin' ],
downloadableFiles: [ /^(?!pdf$).*$/ ]
}
} )
.then( editor => {
Expand Down

0 comments on commit 351251d

Please sign in to comment.