Skip to content

Commit

Permalink
chore(edit-content): Validate proper files #30061
Browse files Browse the repository at this point in the history
  • Loading branch information
nicobytes committed Oct 8, 2024
1 parent a13a99e commit 5aa1686
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ export interface FormImportUrlState {
status: 'init' | 'uploading' | 'done' | 'error';
error: string | null;
uploadType: UPLOAD_TYPE;
acceptedFiles: string[];
}

const initialState: FormImportUrlState = {
file: null,
status: 'init',
error: null,
uploadType: 'temp'
uploadType: 'temp',
acceptedFiles: []
};

export const FormImportUrlStore = signalStore(
Expand All @@ -42,11 +44,14 @@ export const FormImportUrlStore = signalStore(
return uploadService
.uploadFile({
file: fileUrl,
uploadType: store.uploadType()
uploadType: store.uploadType(),
acceptedFiles: store.acceptedFiles()
})
.pipe(
tapResponse({
next: (file) => patchState(store, { file, status: 'done' }),
next: (file) => {
patchState(store, { file, status: 'done' });
},
error: console.error
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
OnInit,
OnDestroy
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

import { ButtonModule } from 'primeng/button';
Expand Down Expand Up @@ -234,14 +233,9 @@ export class DotEditContentFileFieldComponent implements ControlValueAccessor, O
}
});

this.#dialogRef.onClose
.pipe(
filter((file) => !!file),
takeUntilDestroyed()
)
.subscribe((file) => {
this.store.setPreviewFile(file);
});
this.#dialogRef.onClose.pipe(filter((file) => !!file)).subscribe((file) => {
this.store.setPreviewFile(file);
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { from, Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';

import { map, switchMap } from 'rxjs/operators';
import { map, switchMap, tap } from 'rxjs/operators';

import { DotUploadFileService, DotUploadService } from '@dotcms/data-access';
import { DotCMSContentlet } from '@dotcms/dotcms-models';
import { DotCMSContentlet, DotCMSTempFile } from '@dotcms/dotcms-models';

import { DotEditContentService } from '../../../../services/dot-edit-content.service';
import { checkMimeType } from '../../../dot-edit-content-host-folder-field/utils';
import { UploadedFile, UPLOAD_TYPE } from '../../models';
import { getFileMetadata, getFileVersion } from '../../utils';

Expand All @@ -33,27 +34,55 @@ export class DotFileFieldUploadService {
*/
uploadFile({
file,
uploadType
uploadType,
acceptedFiles
}: {
file: File | string;
uploadType: UPLOAD_TYPE;
acceptedFiles: string[];
}): Observable<UploadedFile> {
console.log('uploadFile');
console.log('file', file);
console.log('uploadType', uploadType);
console.log('acceptedFiles', acceptedFiles);

if (uploadType === 'temp') {
return from(this.#tempFileService.uploadFile({ file })).pipe(
map((tempFile) => ({ source: 'temp', file: tempFile }))
);
} else {
if (file instanceof File) {
return this.uploadDotAsset(file).pipe(
map((file) => ({ source: 'contentlet', file }))
);
}

return from(this.#tempFileService.uploadFile({ file })).pipe(
switchMap((tempFile) => this.uploadDotAsset(tempFile.id)),
map((file) => ({ source: 'contentlet', file }))
return this.uploadTempFile(file, acceptedFiles).pipe(
map((file) => ({ source: 'temp', file }))
);
}

const uploadProcess =
file instanceof File
? this.uploadDotAssetByFile(file, acceptedFiles)
: this.uploadDotAssetByUrl(file, acceptedFiles);

return uploadProcess.pipe(map((file) => ({ source: 'contentlet', file })));
}
uploadTempFile(file: File | string, acceptedFiles: string[]): Observable<DotCMSTempFile> {
return from(this.#tempFileService.uploadFile({ file })).pipe(
tap((tempFile) => {
if (!checkMimeType(tempFile, acceptedFiles)) {
throw new Error('Invalid file type');
}
})
);
}

uploadDotAssetByFile(file: File, acceptedFiles: string[]): Observable<DotCMSContentlet> {
return this.uploadDotAsset(file).pipe(
tap((file) => {
if (!checkMimeType(file, acceptedFiles)) {
throw new Error('Invalid file type');
}
})
);
}

uploadDotAssetByUrl(file: string, acceptedFiles: string[]): Observable<DotCMSContentlet> {
return this.uploadTempFile(file, acceptedFiles).pipe(
switchMap((tempFile) => this.uploadDotAsset(tempFile.id))
);
}
/**
* Uploads a file and returns a contentlet with the file metadata and id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,23 +167,32 @@ export const FileFieldStore = signalStore(
return true;
}),
switchMap((file) => {
return uploadService.uploadDotAsset(file).pipe(
tapResponse({
next: (file) => {
patchState(store, {
fileStatus: 'preview',
value: file.identifier,
uploadedFile: { source: 'contentlet', file }
});
},
error: () => {
patchState(store, {
fileStatus: 'init',
uiMessage: getUiMessage('SERVER_ERROR')
});
}
return uploadService
.uploadFile({
file,
uploadType: 'dotasset',
acceptedFiles: store.acceptedFiles()
})
);
.pipe(
tapResponse({
next: (uploadedFile) => {
patchState(store, {
fileStatus: 'preview',
value:
uploadedFile.source === 'temp'
? uploadedFile.file.id
: uploadedFile.file.identifier,
uploadedFile
});
},
error: () => {
patchState(store, {
fileStatus: 'init',
uiMessage: getUiMessage('SERVER_ERROR')
});
}
})
);
})
)
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { DotCMSContentlet, DotCMSTempFile } from '@dotcms/dotcms-models';

/**
* Given an array of mime types, this function will convert all of them to
* lower case and remove any asterisks from the mime type. It will then
* filter out any empty strings from the array.
*
* @param mimeTypes an array of mime types
* @returns an array of cleaned mime types
*/
export function cleanMimeTypes(mimeTypes: string[]): string[] {
return mimeTypes
.map((type) => {
return type.toLowerCase().replace(/\*/g, '');
})
.filter((type) => type !== '');
}

/**
* Checks if a file's mime type is in a list of accepted mime types.
*
* If the acceptedFiles array is empty, then this function will return true.
*
* @param file the file to check, either a DotCMSTempFile or DotCMSContentlet
* @param acceptedFiles an array of mime types to check against
* @returns true if the file's mime type is in the list of accepted mime types, false otherwise
*/
export function checkMimeType(
file: DotCMSTempFile | DotCMSContentlet,
acceptedFiles: string[]
): boolean {
if (acceptedFiles.length === 0) {
return true;
}

const mimeTypes = cleanMimeTypes(acceptedFiles);
console.log('file', file);
console.log('acceptedFiles', acceptedFiles);
console.log('mimeTypes', mimeTypes);

if (file.mimeType) {
return mimeTypes.includes(file.mimeType);
}

return false;
}

0 comments on commit 5aa1686

Please sign in to comment.