dropzone management
This commit is contained in:
3
src/Umbraco.Web.UI.Client/package-lock.json
generated
3
src/Umbraco.Web.UI.Client/package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"element-internals-polyfill": "^1.3.10",
|
||||
"lit": "^3.1.2",
|
||||
"marked": "^12.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"tinymce": "^6.8.3",
|
||||
@@ -15849,7 +15850,6 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -15858,7 +15858,6 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"./external/dompurify": "./dist-cms/external/dompurify/index.js",
|
||||
"./external/lit": "./dist-cms/external/lit/index.js",
|
||||
"./external/marked": "./dist-cms/external/marked/index.js",
|
||||
"./external/mime-types": "./dist-cms/external/mime-types/index.js",
|
||||
"./external/monaco-editor": "./dist-cms/external/monaco-editor/index.js",
|
||||
"./external/openid": "./dist-cms/external/openid/index.js",
|
||||
"./external/router-slot": "./dist-cms/external/router-slot/index.js",
|
||||
@@ -179,6 +180,7 @@
|
||||
"element-internals-polyfill": "^1.3.10",
|
||||
"lit": "^3.1.2",
|
||||
"marked": "^12.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"tinymce": "^6.8.3",
|
||||
|
||||
@@ -625,8 +625,7 @@ updater?: string | null
|
||||
};
|
||||
|
||||
export type DocumentConfigurationResponseModel = {
|
||||
sanitizeTinyMce: boolean
|
||||
disableDeleteWhenReferenced: boolean
|
||||
disableDeleteWhenReferenced: boolean
|
||||
disableUnpublishWhenReferenced: boolean
|
||||
allowEditInvariantFromNonDefault: boolean
|
||||
allowNonExistingSegmentsCreation: boolean
|
||||
@@ -1124,7 +1123,6 @@ mediaType: MediaTypeCollectionReferenceResponseModel
|
||||
export type MediaConfigurationResponseModel = {
|
||||
disableDeleteWhenReferenced: boolean
|
||||
disableUnpublishWhenReferenced: boolean
|
||||
sanitizeTinyMce: boolean
|
||||
reservedFieldNames: Array<string>
|
||||
};
|
||||
|
||||
@@ -1788,6 +1786,11 @@ export type PagedSearcherResponseModel = {
|
||||
items: Array<SearcherResponseModel>
|
||||
};
|
||||
|
||||
export type PagedSegmentResponseModel = {
|
||||
total: number
|
||||
items: Array<SegmentResponseModel>
|
||||
};
|
||||
|
||||
export type PagedTagResponseModel = {
|
||||
total: number
|
||||
items: Array<TagResponseModel>
|
||||
@@ -1894,6 +1897,13 @@ memberUserNames: Array<string>
|
||||
memberGroupNames: Array<string>
|
||||
};
|
||||
|
||||
export type PublicAccessResponseModel = {
|
||||
loginDocument: ReferenceByIdModel
|
||||
errorDocument: ReferenceByIdModel
|
||||
members: Array<MemberItemResponseModel>
|
||||
groups: Array<MemberGroupItemResponseModel>
|
||||
};
|
||||
|
||||
export type PublishDocumentRequestModel = {
|
||||
publishSchedules: Array<CultureAndScheduleRequestModel>
|
||||
};
|
||||
@@ -2052,6 +2062,11 @@ export type SecurityConfigurationResponseModel = {
|
||||
passwordConfiguration: PasswordConfigurationResponseModel
|
||||
};
|
||||
|
||||
export type SegmentResponseModel = {
|
||||
name: string
|
||||
alias: string
|
||||
};
|
||||
|
||||
export type ServerConfigurationItemResponseModel = {
|
||||
name: string
|
||||
data: string
|
||||
@@ -3397,7 +3412,7 @@ take?: number
|
||||
,PutDocumentByIdNotifications: string
|
||||
,PostDocumentByIdPublicAccess: string
|
||||
,DeleteDocumentByIdPublicAccess: string
|
||||
,GetDocumentByIdPublicAccess: void
|
||||
,GetDocumentByIdPublicAccess: PublicAccessResponseModel
|
||||
,PutDocumentByIdPublicAccess: string
|
||||
,PutDocumentByIdPublish: string
|
||||
,PutDocumentByIdPublishWithDescendants: string
|
||||
@@ -3583,6 +3598,7 @@ requestBody?: UpdateLanguageRequestModel
|
||||
|
||||
responses: {
|
||||
GetItemLanguage: Array<LanguageItemResponseModel>
|
||||
,GetItemLanguageDefault: LanguageItemResponseModel
|
||||
,GetLanguage: PagedLanguageResponseModel
|
||||
,PostLanguage: string
|
||||
,GetLanguageByIsoCode: LanguageResponseModel
|
||||
@@ -3681,6 +3697,12 @@ export type MediaTypeData = {
|
||||
GetItemMediaType: {
|
||||
id?: Array<string>
|
||||
|
||||
};
|
||||
GetItemMediaTypeAllowed: {
|
||||
fileExtension?: string
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
};
|
||||
GetItemMediaTypeSearch: {
|
||||
query?: string
|
||||
@@ -3773,6 +3795,7 @@ take?: number
|
||||
|
||||
responses: {
|
||||
GetItemMediaType: Array<MediaTypeItemResponseModel>
|
||||
,GetItemMediaTypeAllowed: PagedModelMediaTypeItemResponseModel
|
||||
,GetItemMediaTypeSearch: PagedModelMediaTypeItemResponseModel
|
||||
,PostMediaType: string
|
||||
,GetMediaTypeById: MediaTypeResponseModel
|
||||
@@ -3996,10 +4019,10 @@ take?: number
|
||||
responses: {
|
||||
GetItemMemberGroup: Array<MemberGroupItemResponseModel>
|
||||
,GetMemberGroup: PagedMemberGroupResponseModel
|
||||
,PostMemberGroup: MemberGroupResponseModel
|
||||
,PostMemberGroup: string
|
||||
,GetMemberGroupById: MemberGroupResponseModel
|
||||
,DeleteMemberGroupById: string
|
||||
,PutMemberGroupById: MemberGroupResponseModel
|
||||
,PutMemberGroupById: string
|
||||
,GetTreeMemberGroupRoot: PagedNamedEntityTreeItemResponseModel
|
||||
|
||||
}
|
||||
@@ -4591,6 +4614,24 @@ PostSecurityForgotPasswordVerify: {
|
||||
|
||||
}
|
||||
|
||||
export type SegmentData = {
|
||||
|
||||
payloads: {
|
||||
GetSegment: {
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
responses: {
|
||||
GetSegment: PagedSegmentResponseModel
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type ServerData = {
|
||||
|
||||
|
||||
@@ -4879,10 +4920,6 @@ export type UserDataData = {
|
||||
PostUserData: {
|
||||
requestBody?: CreateUserDataRequestModel
|
||||
|
||||
};
|
||||
PutUserData: {
|
||||
requestBody?: UpdateUserDataRequestModel
|
||||
|
||||
};
|
||||
GetUserData: {
|
||||
groups?: Array<string>
|
||||
@@ -4890,6 +4927,10 @@ identifiers?: Array<string>
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
};
|
||||
PutUserData: {
|
||||
requestBody?: UpdateUserDataRequestModel
|
||||
|
||||
};
|
||||
GetUserDataById: {
|
||||
id: string
|
||||
@@ -4900,8 +4941,8 @@ GetUserDataById: {
|
||||
|
||||
responses: {
|
||||
PostUserData: string
|
||||
,PutUserData: string
|
||||
,GetUserData: PagedUserDataResponseModel
|
||||
,PutUserData: string
|
||||
,GetUserDataById: UserDataModel
|
||||
|
||||
}
|
||||
@@ -5152,7 +5193,11 @@ PostUserUnlock: {
|
||||
export type WebhookData = {
|
||||
|
||||
payloads: {
|
||||
GetWebhook: {
|
||||
GetItemWebhook: {
|
||||
id?: Array<string>
|
||||
|
||||
};
|
||||
GetWebhook: {
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
@@ -5173,21 +5218,17 @@ requestBody?: UpdateWebhookRequestModel
|
||||
DeleteWebhookById: {
|
||||
id: string
|
||||
|
||||
};
|
||||
GetWebhookItem: {
|
||||
ids?: Array<string>
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
responses: {
|
||||
GetWebhook: PagedWebhookResponseModel
|
||||
GetItemWebhook: Array<WebhookItemResponseModel>
|
||||
,GetWebhook: PagedWebhookResponseModel
|
||||
,PostWebhook: string
|
||||
,GetWebhookById: WebhookResponseModel
|
||||
,PutWebhookById: string
|
||||
,DeleteWebhookById: string
|
||||
,GetWebhookItem: Array<WebhookItemResponseModel>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CancelablePromise } from './core/CancelablePromise';
|
||||
import { OpenAPI } from './core/OpenAPI';
|
||||
import { request as __request } from './core/request';
|
||||
import type { AuditLogData, CultureData, DataTypeData, DictionaryData, DocumentBlueprintData, DocumentTypeData, DocumentVersionData, DocumentData, DynamicRootData, HealthCheckData, HelpData, IndexerData, InstallData, LanguageData, LogViewerData, ManifestData, MediaTypeData, MediaData, MemberGroupData, MemberTypeData, MemberData, ModelsBuilderData, ObjectTypesData, PackageData, PartialViewData, PreviewData, ProfilingData, PropertyTypeData, PublishedCacheData, RedirectManagementData, RelationTypeData, RelationData, ScriptData, SearcherData, SecurityData, ServerData, StaticFileData, StylesheetData, TagData, TelemetryData, TemplateData, TemporaryFileData, UpgradeData, UserDataData, UserGroupData, UserData, WebhookData } from './models';
|
||||
import type { AuditLogData, CultureData, DataTypeData, DictionaryData, DocumentBlueprintData, DocumentTypeData, DocumentVersionData, DocumentData, DynamicRootData, HealthCheckData, HelpData, IndexerData, InstallData, LanguageData, LogViewerData, ManifestData, MediaTypeData, MediaData, MemberGroupData, MemberTypeData, MemberData, ModelsBuilderData, ObjectTypesData, PackageData, PartialViewData, PreviewData, ProfilingData, PropertyTypeData, PublishedCacheData, RedirectManagementData, RelationTypeData, RelationData, ScriptData, SearcherData, SecurityData, SegmentData, ServerData, StaticFileData, StylesheetData, TagData, TelemetryData, TemplateData, TemporaryFileData, UpgradeData, UserDataData, UserGroupData, UserData, WebhookData } from './models';
|
||||
|
||||
export class AuditLogService {
|
||||
|
||||
@@ -2220,6 +2220,7 @@ requestBody
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getDocumentByIdPublicAccess(data: DocumentData['payloads']['GetDocumentByIdPublicAccess']): CancelablePromise<DocumentData['responses']['GetDocumentByIdPublicAccess']> {
|
||||
@@ -3129,6 +3130,22 @@ export class LanguageService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getItemLanguageDefault(): CancelablePromise<LanguageData['responses']['GetItemLanguageDefault']> {
|
||||
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/item/language/default',
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -3546,6 +3563,30 @@ export class MediaTypeService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getItemMediaTypeAllowed(data: MediaTypeData['payloads']['GetItemMediaTypeAllowed'] = {}): CancelablePromise<MediaTypeData['responses']['GetItemMediaTypeAllowed']> {
|
||||
const {
|
||||
|
||||
fileExtension,
|
||||
skip,
|
||||
take
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/item/media-type/allowed',
|
||||
query: {
|
||||
fileExtension, skip, take
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -4679,7 +4720,7 @@ take
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @returns string Created
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static postMemberGroup(data: MemberGroupData['payloads']['PostMemberGroup'] = {}): CancelablePromise<MemberGroupData['responses']['PostMemberGroup']> {
|
||||
@@ -4692,6 +4733,7 @@ take
|
||||
url: '/umbraco/management/api/v1/member-group',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
responseHeader: 'Umb-Generated-Resource',
|
||||
errors: {
|
||||
400: `Bad Request`,
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
@@ -4749,7 +4791,7 @@ take
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @returns string Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putMemberGroupById(data: MemberGroupData['payloads']['PutMemberGroupById']): CancelablePromise<MemberGroupData['responses']['PutMemberGroupById']> {
|
||||
@@ -4766,6 +4808,7 @@ requestBody
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
responseHeader: 'Umb-Notifications',
|
||||
errors: {
|
||||
400: `Bad Request`,
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
@@ -6756,6 +6799,34 @@ export class SecurityService {
|
||||
|
||||
}
|
||||
|
||||
export class SegmentService {
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getSegment(data: SegmentData['payloads']['GetSegment'] = {}): CancelablePromise<SegmentData['responses']['GetSegment']> {
|
||||
const {
|
||||
|
||||
skip,
|
||||
take
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/segment',
|
||||
query: {
|
||||
skip, take
|
||||
},
|
||||
errors: {
|
||||
400: `Bad Request`,
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ServerService {
|
||||
|
||||
/**
|
||||
@@ -7726,29 +7797,6 @@ export class UserDataService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns string Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putUserData(data: UserDataData['payloads']['PutUserData'] = {}): CancelablePromise<UserDataData['responses']['PutUserData']> {
|
||||
const {
|
||||
|
||||
requestBody
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/umbraco/management/api/v1/user-data',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
responseHeader: 'Umb-Notifications',
|
||||
errors: {
|
||||
400: `Bad Request`,
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
404: `Not Found`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -7773,6 +7821,29 @@ take
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns string Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putUserData(data: UserDataData['payloads']['PutUserData'] = {}): CancelablePromise<UserDataData['responses']['PutUserData']> {
|
||||
const {
|
||||
|
||||
requestBody
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/umbraco/management/api/v1/user-data',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
responseHeader: 'Umb-Notifications',
|
||||
errors: {
|
||||
400: `Bad Request`,
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
404: `Not Found`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -8829,6 +8900,28 @@ requestBody
|
||||
|
||||
export class WebhookService {
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getItemWebhook(data: WebhookData['payloads']['GetItemWebhook'] = {}): CancelablePromise<WebhookData['responses']['GetItemWebhook']> {
|
||||
const {
|
||||
|
||||
id
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/item/webhook',
|
||||
query: {
|
||||
id
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -8950,26 +9043,4 @@ requestBody
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getWebhookItem(data: WebhookData['payloads']['GetWebhookItem'] = {}): CancelablePromise<WebhookData['responses']['GetWebhookItem']> {
|
||||
const {
|
||||
|
||||
ids
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/webhook/item',
|
||||
query: {
|
||||
ids
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
1
src/Umbraco.Web.UI.Client/src/external/mime-types/index.ts
vendored
Normal file
1
src/Umbraco.Web.UI.Client/src/external/mime-types/index.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * as mime from 'mime-types';
|
||||
@@ -4,8 +4,22 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContentTypeStructureRepositoryBase } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export class UmbMediaTypeStructureRepository extends UmbContentTypeStructureRepositoryBase<UmbAllowedMediaTypeModel> {
|
||||
#dataSource;
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UmbMediaTypeStructureServerDataSource);
|
||||
this.#dataSource = new UmbMediaTypeStructureServerDataSource(host);
|
||||
}
|
||||
|
||||
async requestMediaTypesOf({
|
||||
fileExtension,
|
||||
skip = 0,
|
||||
take = 100,
|
||||
}: {
|
||||
fileExtension: string;
|
||||
skip?: number;
|
||||
take?: number;
|
||||
}) {
|
||||
return this.#dataSource.getMediaTypesOfFileExtension({ fileExtension, skip, take });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ export class UmbMediaTypeStructureServerDataSource extends UmbContentTypeStructu
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, { getAllowedChildrenOf, mapper });
|
||||
}
|
||||
|
||||
getMediaTypesOfFileExtension({ fileExtension, skip, take }: { fileExtension: string; skip: number; take: number }) {
|
||||
return getAllowedMediaTypesOfExtension({ fileExtension, skip, take });
|
||||
}
|
||||
}
|
||||
|
||||
const getAllowedChildrenOf = (unique: string | null) => {
|
||||
@@ -37,3 +41,17 @@ const mapper = (item: AllowedMediaTypeModel): UmbAllowedMediaTypeModel => {
|
||||
icon: item.icon || null,
|
||||
};
|
||||
};
|
||||
|
||||
const getAllowedMediaTypesOfExtension = async ({
|
||||
fileExtension,
|
||||
skip,
|
||||
take,
|
||||
}: {
|
||||
fileExtension: string;
|
||||
skip: number;
|
||||
take: number;
|
||||
}) => {
|
||||
// eslint-disable-next-line local-rules/no-direct-api-import
|
||||
const { items } = await MediaTypeService.getItemMediaTypeAllowed({ fileExtension, skip, take });
|
||||
return items.map((item) => mapper(item));
|
||||
};
|
||||
|
||||
@@ -1,28 +1,7 @@
|
||||
export enum UmbMediaTypeFileType {
|
||||
SVG = 'Vector Graphics (SVG)',
|
||||
IMAGE = 'Image',
|
||||
AUDIO = 'Audio',
|
||||
VIDEO = 'Video',
|
||||
ARTICLE = 'Article',
|
||||
FILE = 'File',
|
||||
}
|
||||
import { mime } from '@umbraco-cms/backoffice/external/mime-types';
|
||||
|
||||
export function getMediaTypeByFileExtension(extension: string) {
|
||||
if (extension === 'svg') return UmbMediaTypeFileType.SVG;
|
||||
if (['jpg', 'jpeg', 'gif', 'bmp', 'png', 'tiff', 'tif', 'webp'].includes(extension))
|
||||
return UmbMediaTypeFileType.IMAGE;
|
||||
if (['mp3', 'weba', 'oga', 'opus'].includes(extension)) return UmbMediaTypeFileType.AUDIO;
|
||||
if (['mp4', 'webm', 'ogv'].includes(extension)) return UmbMediaTypeFileType.VIDEO;
|
||||
if (['pdf', 'docx', 'doc'].includes(extension)) return UmbMediaTypeFileType.ARTICLE;
|
||||
return UmbMediaTypeFileType.FILE;
|
||||
}
|
||||
|
||||
export function getMediaTypeByFileMimeType(mimetype: string) {
|
||||
if (mimetype === 'image/svg+xml') return UmbMediaTypeFileType.SVG;
|
||||
const [type, extension] = mimetype.split('/');
|
||||
if (type === 'image') return UmbMediaTypeFileType.IMAGE;
|
||||
if (type === 'audio') return UmbMediaTypeFileType.AUDIO;
|
||||
if (type === 'video') return UmbMediaTypeFileType.VIDEO;
|
||||
if (['pdf', 'docx', 'doc'].includes(extension)) return UmbMediaTypeFileType.ARTICLE;
|
||||
return UmbMediaTypeFileType.FILE;
|
||||
export function getExtensionFromMime(mimeType: string): string | undefined {
|
||||
const extension = mime.extension(mimeType);
|
||||
if (!extension) return; // extension doesn't exist.
|
||||
return extension;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { UmbContentTypeUploadableStructureRepositoryBase } from './repository/content-type-uploadable-structure-repository-base.js';
|
||||
import { UmbId } from '@umbraco-cms/backoffice/id';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import type { UmbMediaTypeItemModel } from '@umbraco-cms/backoffice/media-type';
|
||||
import { UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file';
|
||||
|
||||
export class UmbDropzoneManager<T extends UmbMediaTypeItemModel = UmbMediaTypeItemModel> extends UmbControllerBase {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#fileManager = new UmbTemporaryFileManager(this);
|
||||
#repository: UmbContentTypeUploadableStructureRepositoryBase<T>;
|
||||
|
||||
#parentUnique: string | null = null;
|
||||
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
typeRepository: UmbContentTypeUploadableStructureRepositoryBase<T>,
|
||||
parentUnique: string | null,
|
||||
) {
|
||||
super(host);
|
||||
this.#repository = typeRepository;
|
||||
this.#parentUnique = parentUnique;
|
||||
}
|
||||
|
||||
public async dropOneFile(file: File) {
|
||||
const matchingMediaTypes = await this.#repository.requestAllowedMediaTypesOf(file.type);
|
||||
//const options = this.#allowedMediaTypes.filter((allowedMediaType) => matchingMediaTypes.includes(allowedMediaType));
|
||||
}
|
||||
|
||||
public async dropFiles(files: Array<File>) {}
|
||||
|
||||
async #requestAllowedMediaTypesOf(fileExtension: string) {
|
||||
const { data } = await this.#repository.requestAllowedMediaTypesOf(fileExtension);
|
||||
//const mediaTypes = data?.filter((option) => this.#allowedMediaTypes.includes(option));
|
||||
//return { fileExtension, mediaTypes };
|
||||
}
|
||||
|
||||
private _reset() {
|
||||
//
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
import { UmbMediaDetailRepository } from '../../repository/index.js';
|
||||
import type { UmbMediaDetailModel } from '../../types.js';
|
||||
import { css, html, customElement, state, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import type { UUIFileDropzoneElement, UUIFileDropzoneEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import {
|
||||
type UmbAllowedMediaTypeModel,
|
||||
UmbMediaTypeStructureRepository,
|
||||
UmbMediaTypeDetailRepository,
|
||||
getExtensionFromMime,
|
||||
} from '@umbraco-cms/backoffice/media-type';
|
||||
import {
|
||||
UmbTemporaryFileManager,
|
||||
type UmbTemporaryFileQueueModel,
|
||||
type UmbTemporaryFileModel,
|
||||
} from '@umbraco-cms/backoffice/temporary-file';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
|
||||
interface MediaTypeOptions {
|
||||
fileExtension: string;
|
||||
mediaTypes: Array<UmbAllowedMediaTypeModel>;
|
||||
}
|
||||
|
||||
interface UploadableFile {
|
||||
file: File;
|
||||
mediaType: UmbAllowedMediaTypeModel;
|
||||
regularUploadField?: boolean;
|
||||
temporaryUnique?: string;
|
||||
}
|
||||
|
||||
@customElement('umb-dropzone')
|
||||
export class UmbDropzoneElement extends UmbLitElement {
|
||||
#fileManager = new UmbTemporaryFileManager(this);
|
||||
#mediaTypeStructure = new UmbMediaTypeStructureRepository(this);
|
||||
#mediaDetailRepository = new UmbMediaDetailRepository(this);
|
||||
#mediaTypeDetailRepository = new UmbMediaTypeDetailRepository(this);
|
||||
|
||||
#allowedMediaTypes: Array<UmbAllowedMediaTypeModel> = [];
|
||||
|
||||
@state()
|
||||
private queue: Array<UmbTemporaryFileModel> = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
parentUnique: string | null = null;
|
||||
|
||||
public browse() {
|
||||
const element = this.shadowRoot?.querySelector('#dropzone') as UUIFileDropzoneElement;
|
||||
return element.browse();
|
||||
}
|
||||
|
||||
async #getAllowedMediaTypes(): Promise<UmbAllowedMediaTypeModel[]> {
|
||||
const { data } = await this.#mediaTypeStructure.requestAllowedChildrenOf(this.parentUnique);
|
||||
return data?.items ?? [];
|
||||
}
|
||||
|
||||
async #getAllowedMediaTypesOf(fileExtension: string): Promise<MediaTypeOptions> {
|
||||
const options = await this.#mediaTypeStructure.requestMediaTypesOf({ fileExtension });
|
||||
const mediaTypes = options.filter((option) => this.#allowedMediaTypes.includes(option));
|
||||
return { fileExtension, mediaTypes };
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.addEventListener('dragenter', this.#handleDragEnter.bind(this));
|
||||
document.addEventListener('dragleave', this.#handleDragLeave.bind(this));
|
||||
document.addEventListener('drop', this.#handleDrop.bind(this));
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener('dragenter', this.#handleDragEnter.bind(this));
|
||||
document.removeEventListener('dragleave', this.#handleDragLeave.bind(this));
|
||||
document.removeEventListener('drop', this.#handleDrop.bind(this));
|
||||
}
|
||||
|
||||
#handleDragEnter() {
|
||||
this.toggleAttribute('dragging', true);
|
||||
}
|
||||
|
||||
#handleDragLeave() {
|
||||
this.toggleAttribute('dragging', false);
|
||||
}
|
||||
|
||||
#handleDrop(event: DragEvent) {
|
||||
event.preventDefault();
|
||||
this.toggleAttribute('dragging', false);
|
||||
}
|
||||
|
||||
async #onDropFiles(event: UUIFileDropzoneEvent) {
|
||||
// TODO Handle of folder uploads.
|
||||
|
||||
const files: Array<File> = event.detail.files;
|
||||
if (!files.length) return;
|
||||
|
||||
this.#allowedMediaTypes = await this.#getAllowedMediaTypes();
|
||||
if (!this.#allowedMediaTypes.length) return;
|
||||
// If we have files that are not allowed to be uploaded, we show those in a dialog to the user?
|
||||
|
||||
if (files.length === 1) {
|
||||
this.#handleOneFile(files[0]);
|
||||
} else {
|
||||
this.#handleMultipleFiles(files);
|
||||
}
|
||||
}
|
||||
|
||||
async #handleOneFile(file: File) {
|
||||
const extension = getExtensionFromMime(file.type);
|
||||
if (!extension) return; // Extension doesn't exist.
|
||||
|
||||
const options = await this.#getAllowedMediaTypesOf(extension);
|
||||
if (!options.mediaTypes.length) return; // File type not allowed in current dropzone.
|
||||
|
||||
if (options.mediaTypes.length === 1) {
|
||||
this.#uploadFiles([{ file, mediaType: options.mediaTypes[0] }]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple options, show a dialog to the user to pick one.
|
||||
//TODO: Implement dialog.
|
||||
}
|
||||
|
||||
async #handleMultipleFiles(files: Array<File>) {
|
||||
// removes duplicate file types so we don't call the endpoint unnecessarily for every file.
|
||||
const types = [...new Set(files.map<string>((file) => file.type))];
|
||||
const options: Array<MediaTypeOptions> = [];
|
||||
|
||||
for (const type of types) {
|
||||
const extension = getExtensionFromMime(type);
|
||||
if (!extension) return; // Extension doesn't exist.
|
||||
|
||||
options.push(await this.#getAllowedMediaTypesOf(extension));
|
||||
}
|
||||
|
||||
// We are just going to automatically pick the first possible media type option for now, but consider an option dialog in the future.
|
||||
const uploadable: Array<UploadableFile> = [];
|
||||
files.forEach((file) => {
|
||||
const mediaType = options.find((option) => option.fileExtension === file.type)?.mediaTypes[0] ?? undefined;
|
||||
if (mediaType) uploadable.push({ file, mediaType });
|
||||
});
|
||||
|
||||
this.#uploadFiles(uploadable);
|
||||
}
|
||||
|
||||
async #uploadFiles(uploadable: Array<UploadableFile>) {
|
||||
const queue = uploadable.map(
|
||||
(item): UmbTemporaryFileQueueModel => ({ file: item.file, unique: item.temporaryUnique }),
|
||||
);
|
||||
|
||||
const uploaded = await this.#fileManager.upload(queue);
|
||||
|
||||
for (const upload of uploaded) {
|
||||
const mediaType = uploadable.find((item) => item.temporaryUnique === upload.unique)?.mediaType;
|
||||
const value = mediaType?.unique === UmbMediaTypeFileType.IMAGE ? { src: upload.unique } : upload.unique;
|
||||
|
||||
if (!mediaType) return;
|
||||
|
||||
const preset: Partial<UmbMediaDetailModel> = {
|
||||
mediaType: {
|
||||
unique: mediaType.unique,
|
||||
collection: null,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
culture: null,
|
||||
segment: null,
|
||||
name: upload.file.name,
|
||||
createDate: null,
|
||||
updateDate: null,
|
||||
},
|
||||
],
|
||||
values: upload.file.type
|
||||
? [
|
||||
{
|
||||
alias: 'umbracoFile',
|
||||
value,
|
||||
culture: null,
|
||||
segment: null,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
|
||||
const { data } = await this.#mediaDetailRepository.createScaffold(preset);
|
||||
if (!data) return;
|
||||
|
||||
await this.#mediaDetailRepository.create(data, this.parentUnique);
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
async #uploadHandler(files: Array<File>) {
|
||||
//TODO: Folders uploaded via UUIDropzone are always empty. Investigate why.
|
||||
|
||||
const folders = files.filter((item) => !item.type).map((file): UmbTemporaryFileQueueModel => ({ file }));
|
||||
const mediaItems = files.filter((item) => item.type);
|
||||
|
||||
const queue = mediaItems.map((file): UmbTemporaryFileQueueModel => ({ file }));
|
||||
|
||||
const uploaded = await this.#fileManager.upload(queue);
|
||||
return [...folders, ...uploaded];
|
||||
}
|
||||
|
||||
async #onFileUpload(event: UUIFileDropzoneEvent) {
|
||||
const files: Array<File> = event.detail.files;
|
||||
|
||||
if (!files.length) return;
|
||||
const uploads = await this.#uploadHandler(files);
|
||||
|
||||
for (const upload of uploads) {
|
||||
const mediaType = /*this.#getMediaTypeFromMime(upload.file.type); */ '' as any;
|
||||
const value = mediaType.unique === UmbMediaTypeFileType.IMAGE ? { src: upload.unique } : upload.unique;
|
||||
|
||||
const preset: Partial<UmbMediaDetailModel> = {
|
||||
mediaType: {
|
||||
unique: mediaType.unique,
|
||||
collection: null,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
culture: null,
|
||||
segment: null,
|
||||
name: upload.file.name,
|
||||
createDate: null,
|
||||
updateDate: null,
|
||||
},
|
||||
],
|
||||
values: upload.file.type
|
||||
? [
|
||||
{
|
||||
alias: 'umbracoFile',
|
||||
value,
|
||||
culture: null,
|
||||
segment: null,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
};
|
||||
|
||||
const { data } = await this.#mediaDetailRepository.createScaffold(preset);
|
||||
if (!data) return;
|
||||
|
||||
await this.#mediaDetailRepository.create(data, this.parentUnique);
|
||||
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-file-dropzone
|
||||
id="dropzone"
|
||||
multiple
|
||||
@change=${this.#onFileUpload}
|
||||
label="${this.localize.term('media_dragAndDropYourFilesIntoTheArea')}"
|
||||
accept=""></uui-file-dropzone>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
css`
|
||||
:host([dragging]) #dropzone {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
[dropzone] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#dropzone {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
inset: 0px;
|
||||
z-index: 100;
|
||||
backdrop-filter: opacity(1); /* Removes the built in blur effect */
|
||||
border-radius: var(--uui-border-radius);
|
||||
overflow: clip;
|
||||
border: 1px solid var(--uui-color-focus);
|
||||
}
|
||||
#dropzone:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: var(--uui-border-radius);
|
||||
background-color: var(--uui-color-focus);
|
||||
opacity: 0.2;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbDropzoneElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-dropzone': UmbDropzoneElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './dropzone.element.js';
|
||||
export * from './dropzone-manager.class.js';
|
||||
@@ -0,0 +1,16 @@
|
||||
import type {
|
||||
UmbContentTypeStructureDataSource,
|
||||
UmbContentTypeStructureDataSourceConstructor,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface UmbContentTypeUploadableStructureDataSourceConstructor<ItemUploadableType>
|
||||
extends UmbContentTypeStructureDataSourceConstructor<ItemUploadableType> {
|
||||
new (host: UmbControllerHost): UmbContentTypeUploadableStructureDataSource<ItemUploadableType>;
|
||||
}
|
||||
|
||||
export interface UmbContentTypeUploadableStructureDataSource<ItemUploadableType>
|
||||
extends UmbContentTypeStructureDataSource<ItemUploadableType> {
|
||||
getAllowedMediaTypesOf(fileExtension: string | null): Promise<UmbDataSourceResponse<ItemUploadableType>>;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type {
|
||||
UmbContentTypeUploadableStructureDataSource,
|
||||
UmbContentTypeUploadableStructureDataSourceConstructor,
|
||||
} from './content-type-uploadable-structure-data-source.interface.js';
|
||||
import type { UmbContentTypeUploadableStructureRepository } from './content-type-uploadable-structure-repository.interface.js';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbContentTypeStructureRepositoryBase } from '@umbraco-cms/backoffice/content-type';
|
||||
|
||||
export abstract class UmbContentTypeUploadableStructureRepositoryBase<ItemUploadableType>
|
||||
extends UmbContentTypeStructureRepositoryBase<ItemUploadableType>
|
||||
implements UmbContentTypeUploadableStructureRepository<ItemUploadableType>
|
||||
{
|
||||
#structureSource: UmbContentTypeUploadableStructureDataSource<ItemUploadableType>;
|
||||
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
structureSource: UmbContentTypeUploadableStructureDataSourceConstructor<ItemUploadableType>,
|
||||
) {
|
||||
super(host, structureSource);
|
||||
this.#structureSource = new structureSource(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise with the allowed media-types of a uploadable content type.
|
||||
* @param {string} unique
|
||||
* @return {*}
|
||||
* @memberof UmbContentTypeUploadableStructureRepositoryBase
|
||||
*/
|
||||
requestAllowedMediaTypesOf(fileExtension: string | null) {
|
||||
return this.#structureSource.getAllowedMediaTypesOf(fileExtension);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { UmbContentTypeStructureRepository } from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface UmbContentTypeUploadableStructureRepository<ItemUploadableType>
|
||||
extends UmbContentTypeStructureRepository<ItemUploadableType> {
|
||||
requestAllowedMediaTypesOf(unique: string): Promise<UmbDataSourceResponse<ItemUploadableType>>;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
type UmbContentTypeStructureDataSource,
|
||||
UmbContentTypeStructureServerDataSourceBase,
|
||||
type UmbContentTypeStructureServerDataSourceBaseArgs,
|
||||
} from '@umbraco-cms/backoffice/content-type';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
// Temp type
|
||||
type AllowedContentTypeModel = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
};
|
||||
|
||||
export interface UmbContentTypeUploadableStructureServerDataSourceBaseArgs<
|
||||
ServerItemType extends AllowedContentTypeModel,
|
||||
ClientItemType extends { unique: string },
|
||||
> extends UmbContentTypeStructureServerDataSourceBaseArgs<ServerItemType, ClientItemType> {
|
||||
getAllowedMediaTypesOf: (fileExtension: string | null) => Promise<ServerItemType>;
|
||||
}
|
||||
|
||||
export abstract class UmbContentTypeUploadableStructureServerDataSourceBase<
|
||||
ServerItemType extends AllowedContentTypeModel,
|
||||
ClientItemType extends { unique: string },
|
||||
>
|
||||
extends UmbContentTypeStructureServerDataSourceBase<ServerItemType, ClientItemType>
|
||||
implements UmbContentTypeStructureDataSource<ClientItemType>
|
||||
{
|
||||
#host;
|
||||
#getAllowedChildrenOf;
|
||||
#mapper;
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbContentTypeStructureServerDataSourceBase.
|
||||
* @param {UmbControllerHost} host
|
||||
* @memberof UmbItemServerDataSourceBase
|
||||
*/
|
||||
constructor(
|
||||
host: UmbControllerHost,
|
||||
args: UmbContentTypeUploadableStructureServerDataSourceBaseArgs<ServerItemType, ClientItemType>,
|
||||
) {
|
||||
super(host, args);
|
||||
this.#host = host;
|
||||
this.#getAllowedChildrenOf = args.getAllowedChildrenOf;
|
||||
this.#mapper = args.mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise with the allowed content types for the given unique
|
||||
* @param {string} unique
|
||||
* @return {*}
|
||||
* @memberof UmbContentTypeStructureServerDataSourceBase
|
||||
*/
|
||||
async getAllowedMediaTypesOf(fileExtension: string | null) {
|
||||
const { data, error } = await tryExecuteAndNotify(this.#host, this.#getAllowedChildrenOf(fileExtension));
|
||||
|
||||
if (data) {
|
||||
const items = data.items.map((item) => this.#mapper(item));
|
||||
return { data: { items, total: data.total } };
|
||||
}
|
||||
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,7 @@
|
||||
"@umbraco-cms/backoffice/external/dompurify": ["./src/external/dompurify/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/lit": ["./src/external/lit/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/marked": ["./src/external/marked/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/mime-types": ["./src/external/mime-types/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/monaco-editor": ["./src/external/monaco-editor/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/openid": ["./src/external/openid/index.ts"],
|
||||
"@umbraco-cms/backoffice/external/router-slot": ["./src/external/router-slot/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user