Merge remote-tracking branch 'origin/main' into feature/general-styling

This commit is contained in:
Jesper Møller Jensen
2023-06-08 18:20:02 +12:00
102 changed files with 1096 additions and 382 deletions

View File

@@ -5,8 +5,10 @@
"Backoffice",
"combobox",
"Elementable",
"Niels",
"pickable",
"templating",
"umbraco",
"variantable"
],
"exportall.config.folderListener": [],

View File

@@ -26,9 +26,13 @@ export type { CopyDataTypeRequestModel } from './models/CopyDataTypeRequestModel
export type { CopyDocumentRequestModel } from './models/CopyDocumentRequestModel';
export type { CreateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel } from './models/CreateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel';
export type { CreateContentRequestModelBaseMediaValueModelMediaVariantRequestModel } from './models/CreateContentRequestModelBaseMediaValueModelMediaVariantRequestModel';
export type { CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel } from './models/CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel';
export type { CreateDataTypeRequestModel } from './models/CreateDataTypeRequestModel';
export type { CreateDictionaryItemRequestModel } from './models/CreateDictionaryItemRequestModel';
export type { CreateDocumentRequestModel } from './models/CreateDocumentRequestModel';
export type { CreateDocumentTypePropertyTypeContainerRequestModel } from './models/CreateDocumentTypePropertyTypeContainerRequestModel';
export type { CreateDocumentTypePropertyTypeRequestModel } from './models/CreateDocumentTypePropertyTypeRequestModel';
export type { CreateDocumentTypeRequestModel } from './models/CreateDocumentTypeRequestModel';
export type { CreateFolderRequestModel } from './models/CreateFolderRequestModel';
export type { CreateLanguageRequestModel } from './models/CreateLanguageRequestModel';
export type { CreateMediaRequestModel } from './models/CreateMediaRequestModel';
@@ -44,6 +48,7 @@ export type { CreateUserGroupRequestModel } from './models/CreateUserGroupReques
export type { CreateUserRequestModel } from './models/CreateUserRequestModel';
export type { CreateUserResponseModel } from './models/CreateUserResponseModel';
export type { CultureReponseModel } from './models/CultureReponseModel';
export type { CurrentUserResponseModel } from './models/CurrentUserResponseModel';
export type { DatabaseInstallResponseModel } from './models/DatabaseInstallResponseModel';
export type { DatabaseSettingsPresentationModel } from './models/DatabaseSettingsPresentationModel';
export type { DataTypeItemResponseModel } from './models/DataTypeItemResponseModel';
@@ -108,6 +113,8 @@ export type { ItemResponseModelBaseModel } from './models/ItemResponseModelBaseM
export type { LanguageItemResponseModel } from './models/LanguageItemResponseModel';
export type { LanguageModelBaseModel } from './models/LanguageModelBaseModel';
export type { LanguageResponseModel } from './models/LanguageResponseModel';
export type { LinkedLoginModel } from './models/LinkedLoginModel';
export type { LinkedLoginsRequestModel } from './models/LinkedLoginsRequestModel';
export type { LoggerResponseModel } from './models/LoggerResponseModel';
export type { LoginRequestModel } from './models/LoginRequestModel';
export type { LogLevelCountsReponseModel } from './models/LogLevelCountsReponseModel';
@@ -186,8 +193,8 @@ export type { ProblemDetailsModel } from './models/ProblemDetailsModel';
export type { ProfilingStatusRequestModel } from './models/ProfilingStatusRequestModel';
export type { ProfilingStatusResponseModel } from './models/ProfilingStatusResponseModel';
export type { PropertyTypeAppearanceModel } from './models/PropertyTypeAppearanceModel';
export type { PropertyTypeContainerResponseModelBaseModel } from './models/PropertyTypeContainerResponseModelBaseModel';
export type { PropertyTypeResponseModelBaseModel } from './models/PropertyTypeResponseModelBaseModel';
export type { PropertyTypeContainerModelBaseModel } from './models/PropertyTypeContainerModelBaseModel';
export type { PropertyTypeModelBaseModel } from './models/PropertyTypeModelBaseModel';
export type { PropertyTypeValidationModel } from './models/PropertyTypeValidationModel';
export { PublishedStateModel } from './models/PublishedStateModel';
export type { RecycleBinItemResponseModel } from './models/RecycleBinItemResponseModel';
@@ -213,6 +220,7 @@ export type { SearcherResponseModel } from './models/SearcherResponseModel';
export type { SearchResultResponseModel } from './models/SearchResultResponseModel';
export type { ServerStatusResponseModel } from './models/ServerStatusResponseModel';
export type { SetAvatarRequestModel } from './models/SetAvatarRequestModel';
export type { SetTourStatusRequestModel } from './models/SetTourStatusRequestModel';
export type { SnippetItemResponseModel } from './models/SnippetItemResponseModel';
export type { StaticFileItemResponseModel } from './models/StaticFileItemResponseModel';
export { StatusResultTypeModel } from './models/StatusResultTypeModel';
@@ -242,14 +250,19 @@ export type { TemporaryFileResponseModel } from './models/TemporaryFileResponseM
export type { TextFileResponseModelBaseModel } from './models/TextFileResponseModelBaseModel';
export type { TextFileUpdateModel } from './models/TextFileUpdateModel';
export type { TextFileViewModelBaseModel } from './models/TextFileViewModelBaseModel';
export type { TourStatusModel } from './models/TourStatusModel';
export type { TreeItemPresentationModel } from './models/TreeItemPresentationModel';
export type { UnlockUsersRequestModel } from './models/UnlockUsersRequestModel';
export type { UpdateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel } from './models/UpdateContentRequestModelBaseDocumentValueModelDocumentVariantRequestModel';
export type { UpdateContentRequestModelBaseMediaValueModelMediaVariantRequestModel } from './models/UpdateContentRequestModelBaseMediaValueModelMediaVariantRequestModel';
export type { UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel } from './models/UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel';
export type { UpdateDataTypeRequestModel } from './models/UpdateDataTypeRequestModel';
export type { UpdateDictionaryItemRequestModel } from './models/UpdateDictionaryItemRequestModel';
export type { UpdateDocumentNotificationsRequestModel } from './models/UpdateDocumentNotificationsRequestModel';
export type { UpdateDocumentRequestModel } from './models/UpdateDocumentRequestModel';
export type { UpdateDocumentTypePropertyTypeContainerRequestModel } from './models/UpdateDocumentTypePropertyTypeContainerRequestModel';
export type { UpdateDocumentTypePropertyTypeRequestModel } from './models/UpdateDocumentTypePropertyTypeRequestModel';
export type { UpdateDocumentTypeRequestModel } from './models/UpdateDocumentTypeRequestModel';
export type { UpdateDomainsRequestModel } from './models/UpdateDomainsRequestModel';
export type { UpdateFolderReponseModel } from './models/UpdateFolderReponseModel';
export type { UpdateLanguageRequestModel } from './models/UpdateLanguageRequestModel';
@@ -265,16 +278,21 @@ export type { UpdateUserGroupRequestModel } from './models/UpdateUserGroupReques
export type { UpdateUserGroupsOnUserRequestModel } from './models/UpdateUserGroupsOnUserRequestModel';
export type { UpdateUserRequestModel } from './models/UpdateUserRequestModel';
export type { UpgradeSettingsResponseModel } from './models/UpgradeSettingsResponseModel';
export type { UserDataModel } from './models/UserDataModel';
export type { UserDataResponseModel } from './models/UserDataResponseModel';
export type { UserGroupBaseModel } from './models/UserGroupBaseModel';
export type { UserGroupItemResponseModel } from './models/UserGroupItemResponseModel';
export type { UserGroupResponseModel } from './models/UserGroupResponseModel';
export type { UserInstallResponseModel } from './models/UserInstallResponseModel';
export type { UserItemResponseModel } from './models/UserItemResponseModel';
export { UserOrderModel } from './models/UserOrderModel';
export type { UserPermissionModel } from './models/UserPermissionModel';
export type { UserPermissionsResponseModel } from './models/UserPermissionsResponseModel';
export type { UserPresentationBaseModel } from './models/UserPresentationBaseModel';
export type { UserResponseModel } from './models/UserResponseModel';
export type { UserSettingsModel } from './models/UserSettingsModel';
export { UserStateModel } from './models/UserStateModel';
export type { UserTourStatusesResponseModel } from './models/UserTourStatusesResponseModel';
export type { ValueModelBaseModel } from './models/ValueModelBaseModel';
export type { VariantModelBaseModel } from './models/VariantModelBaseModel';
export type { VariantResponseModelBaseModel } from './models/VariantResponseModelBaseModel';
@@ -319,6 +337,7 @@ export { TagResource } from './services/TagResource';
export { TelemetryResource } from './services/TelemetryResource';
export { TemplateResource } from './services/TemplateResource';
export { TemporaryFileResource } from './services/TemporaryFileResource';
export { TourResource } from './services/TourResource';
export { TrackedReferenceResource } from './services/TrackedReferenceResource';
export { UpgradeResource } from './services/UpgradeResource';
export { UserResource } from './services/UserResource';

View File

@@ -8,7 +8,6 @@ import type { DocumentTypePropertyTypeContainerResponseModel } from './DocumentT
import type { DocumentTypePropertyTypeResponseModel } from './DocumentTypePropertyTypeResponseModel';
export type ContentTypeResponseModelBaseDocumentTypePropertyTypeResponseModelDocumentTypePropertyTypeContainerResponseModel = {
id?: string;
alias?: string;
name?: string;
description?: string | null;
@@ -21,5 +20,6 @@ export type ContentTypeResponseModelBaseDocumentTypePropertyTypeResponseModelDoc
containers?: Array<DocumentTypePropertyTypeContainerResponseModel>;
allowedContentTypes?: Array<ContentTypeSortModel>;
compositions?: Array<ContentTypeCompositionModel>;
id?: string;
};

View File

@@ -8,7 +8,6 @@ import type { MediaTypePropertyTypeContainerResponseModel } from './MediaTypePro
import type { MediaTypePropertyTypeResponseModel } from './MediaTypePropertyTypeResponseModel';
export type ContentTypeResponseModelBaseMediaTypePropertyTypeResponseModelMediaTypePropertyTypeContainerResponseModel = {
id?: string;
alias?: string;
name?: string;
description?: string | null;
@@ -21,5 +20,6 @@ export type ContentTypeResponseModelBaseMediaTypePropertyTypeResponseModelMediaT
containers?: Array<MediaTypePropertyTypeContainerResponseModel>;
allowedContentTypes?: Array<ContentTypeSortModel>;
compositions?: Array<ContentTypeCompositionModel>;
id?: string;
};

View File

@@ -0,0 +1,24 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCompositionModel } from './ContentTypeCompositionModel';
import type { ContentTypeSortModel } from './ContentTypeSortModel';
import type { CreateDocumentTypePropertyTypeContainerRequestModel } from './CreateDocumentTypePropertyTypeContainerRequestModel';
import type { CreateDocumentTypePropertyTypeRequestModel } from './CreateDocumentTypePropertyTypeRequestModel';
export type CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel = {
alias?: string;
name?: string;
description?: string | null;
icon?: string;
allowedAsRoot?: boolean;
variesByCulture?: boolean;
variesBySegment?: boolean;
isElement?: boolean;
properties?: Array<CreateDocumentTypePropertyTypeRequestModel>;
containers?: Array<CreateDocumentTypePropertyTypeContainerRequestModel>;
allowedContentTypes?: Array<ContentTypeSortModel>;
compositions?: Array<ContentTypeCompositionModel>;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeContainerModelBaseModel } from './PropertyTypeContainerModelBaseModel';
export type CreateDocumentTypePropertyTypeContainerRequestModel = PropertyTypeContainerModelBaseModel;

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeModelBaseModel } from './PropertyTypeModelBaseModel';
export type CreateDocumentTypePropertyTypeRequestModel = PropertyTypeModelBaseModel;

View File

@@ -0,0 +1,14 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCleanupModel } from './ContentTypeCleanupModel';
import type { CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel } from './CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel';
export type CreateDocumentTypeRequestModel = (CreateContentTypeRequestModelBaseCreateDocumentTypePropertyTypeRequestModelCreateDocumentTypePropertyTypeContainerRequestModel & {
$type: string;
allowedTemplateIds?: Array<string>;
defaultTemplateId?: string | null;
cleanup?: ContentTypeCleanupModel;
});

View File

@@ -0,0 +1,19 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type CurrentUserResponseModel = {
$type: string;
id?: string;
email?: string;
userName?: string;
name?: string;
languageIsoCode?: string | null;
contentStartNodeIds?: Array<string>;
mediaStartNodeIds?: Array<string>;
avatarUrls?: Array<string>;
languages?: Array<string>;
hasAccessToAllLanguages?: boolean;
permissions?: Array<string>;
};

View File

@@ -2,7 +2,7 @@
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeContainerResponseModelBaseModel } from './PropertyTypeContainerResponseModelBaseModel';
import type { PropertyTypeContainerModelBaseModel } from './PropertyTypeContainerModelBaseModel';
export type DocumentTypePropertyTypeContainerResponseModel = PropertyTypeContainerResponseModelBaseModel;
export type DocumentTypePropertyTypeContainerResponseModel = PropertyTypeContainerModelBaseModel;

View File

@@ -2,7 +2,7 @@
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeResponseModelBaseModel } from './PropertyTypeResponseModelBaseModel';
import type { PropertyTypeModelBaseModel } from './PropertyTypeModelBaseModel';
export type DocumentTypePropertyTypeResponseModel = PropertyTypeResponseModelBaseModel;
export type DocumentTypePropertyTypeResponseModel = PropertyTypeModelBaseModel;

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type LinkedLoginModel = {
providerName?: string;
providerKey?: string;
};

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { LinkedLoginModel } from './LinkedLoginModel';
export type LinkedLoginsRequestModel = {
linkedLogins?: Array<LinkedLoginModel>;
};

View File

@@ -2,7 +2,7 @@
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeContainerResponseModelBaseModel } from './PropertyTypeContainerResponseModelBaseModel';
import type { PropertyTypeContainerModelBaseModel } from './PropertyTypeContainerModelBaseModel';
export type MediaTypePropertyTypeContainerResponseModel = PropertyTypeContainerResponseModelBaseModel;
export type MediaTypePropertyTypeContainerResponseModel = PropertyTypeContainerModelBaseModel;

View File

@@ -2,7 +2,7 @@
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeResponseModelBaseModel } from './PropertyTypeResponseModelBaseModel';
import type { PropertyTypeModelBaseModel } from './PropertyTypeModelBaseModel';
export type MediaTypePropertyTypeResponseModel = PropertyTypeResponseModelBaseModel;
export type MediaTypePropertyTypeResponseModel = PropertyTypeModelBaseModel;

View File

@@ -2,7 +2,7 @@
/* tslint:disable */
/* eslint-disable */
export type PropertyTypeContainerResponseModelBaseModel = {
export type PropertyTypeContainerModelBaseModel = {
id?: string;
parentId?: string | null;
name?: string | null;

View File

@@ -5,7 +5,7 @@
import type { PropertyTypeAppearanceModel } from './PropertyTypeAppearanceModel';
import type { PropertyTypeValidationModel } from './PropertyTypeValidationModel';
export type PropertyTypeResponseModelBaseModel = {
export type PropertyTypeModelBaseModel = {
id?: string;
containerId?: string | null;
sortOrder?: number;

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { TourStatusModel } from './TourStatusModel';
export type SetTourStatusRequestModel = TourStatusModel;

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type TourStatusModel = {
alias?: string;
completed?: boolean;
disabled?: boolean;
};

View File

@@ -0,0 +1,24 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCompositionModel } from './ContentTypeCompositionModel';
import type { ContentTypeSortModel } from './ContentTypeSortModel';
import type { UpdateDocumentTypePropertyTypeContainerRequestModel } from './UpdateDocumentTypePropertyTypeContainerRequestModel';
import type { UpdateDocumentTypePropertyTypeRequestModel } from './UpdateDocumentTypePropertyTypeRequestModel';
export type UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel = {
alias?: string;
name?: string;
description?: string | null;
icon?: string;
allowedAsRoot?: boolean;
variesByCulture?: boolean;
variesBySegment?: boolean;
isElement?: boolean;
properties?: Array<UpdateDocumentTypePropertyTypeRequestModel>;
containers?: Array<UpdateDocumentTypePropertyTypeContainerRequestModel>;
allowedContentTypes?: Array<ContentTypeSortModel>;
compositions?: Array<ContentTypeCompositionModel>;
};

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeContainerModelBaseModel } from './PropertyTypeContainerModelBaseModel';
export type UpdateDocumentTypePropertyTypeContainerRequestModel = PropertyTypeContainerModelBaseModel;

View File

@@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { PropertyTypeModelBaseModel } from './PropertyTypeModelBaseModel';
export type UpdateDocumentTypePropertyTypeRequestModel = PropertyTypeModelBaseModel;

View File

@@ -0,0 +1,14 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ContentTypeCleanupModel } from './ContentTypeCleanupModel';
import type { UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel } from './UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel';
export type UpdateDocumentTypeRequestModel = (UpdateContentTypeRequestModelBaseUpdateDocumentTypePropertyTypeRequestModelUpdateDocumentTypePropertyTypeContainerRequestModel & {
$type: string;
allowedTemplateIds?: Array<string>;
defaultTemplateId?: string | null;
cleanup?: ContentTypeCleanupModel;
});

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UserDataModel = {
name?: string;
data?: string;
};

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { UserDataModel } from './UserDataModel';
export type UserDataResponseModel = {
userData?: Array<UserDataModel>;
};

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type UserPermissionModel = {
nodeKey?: string;
permissions?: Array<string>;
};

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { UserPermissionModel } from './UserPermissionModel';
export type UserPermissionsResponseModel = {
permissions?: Array<UserPermissionModel>;
};

View File

@@ -0,0 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { SetTourStatusRequestModel } from './SetTourStatusRequestModel';
import type { TourStatusModel } from './TourStatusModel';
export type UserTourStatusesResponseModel = {
tourStatuses?: Array<(TourStatusModel | SetTourStatusRequestModel)>;
};

View File

@@ -1,9 +1,11 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CreateDocumentTypeRequestModel } from '../models/CreateDocumentTypeRequestModel';
import type { DocumentTypeItemResponseModel } from '../models/DocumentTypeItemResponseModel';
import type { DocumentTypeResponseModel } from '../models/DocumentTypeResponseModel';
import type { PagedDocumentTypeTreeItemResponseModel } from '../models/PagedDocumentTypeTreeItemResponseModel';
import type { UpdateDocumentTypeRequestModel } from '../models/UpdateDocumentTypeRequestModel';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
@@ -11,6 +13,28 @@ import { request as __request } from '../core/request';
export class DocumentTypeResource {
/**
* @returns any Success
* @throws ApiError
*/
public static postDocumentType({
requestBody,
}: {
requestBody?: CreateDocumentTypeRequestModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/document-type',
body: requestBody,
mediaType: 'application/json',
responseHeader: 'location',
errors: {
400: `Bad Request`,
404: `Not Found`,
},
});
}
/**
* @returns any Success
* @throws ApiError
@@ -32,6 +56,53 @@ export class DocumentTypeResource {
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static deleteDocumentTypeById({
id,
}: {
id: string,
}): CancelablePromise<DocumentTypeResponseModel> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/umbraco/management/api/v1/document-type/{id}',
path: {
'id': id,
},
errors: {
404: `Not Found`,
},
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static putDocumentTypeById({
id,
requestBody,
}: {
id: string,
requestBody?: UpdateDocumentTypeRequestModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'PUT',
url: '/umbraco/management/api/v1/document-type/{id}',
path: {
'id': id,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Bad Request`,
404: `Not Found`,
},
});
}
/**
* @returns any Success
* @throws ApiError

View File

@@ -0,0 +1,41 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { SetTourStatusRequestModel } from '../models/SetTourStatusRequestModel';
import type { UserTourStatusesResponseModel } from '../models/UserTourStatusesResponseModel';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class TourResource {
/**
* @returns any Success
* @throws ApiError
*/
public static getTour(): CancelablePromise<UserTourStatusesResponseModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/tour',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static postTour({
requestBody,
}: {
requestBody?: SetTourStatusRequestModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/tour',
body: requestBody,
mediaType: 'application/json',
});
}
}

View File

@@ -4,17 +4,21 @@
import type { ChangePasswordUserRequestModel } from '../models/ChangePasswordUserRequestModel';
import type { CreateUserRequestModel } from '../models/CreateUserRequestModel';
import type { CreateUserResponseModel } from '../models/CreateUserResponseModel';
import type { CurrentUserResponseModel } from '../models/CurrentUserResponseModel';
import type { DirectionModel } from '../models/DirectionModel';
import type { DisableUserRequestModel } from '../models/DisableUserRequestModel';
import type { EnableUserRequestModel } from '../models/EnableUserRequestModel';
import type { InviteUserRequestModel } from '../models/InviteUserRequestModel';
import type { LinkedLoginsRequestModel } from '../models/LinkedLoginsRequestModel';
import type { PagedUserResponseModel } from '../models/PagedUserResponseModel';
import type { SetAvatarRequestModel } from '../models/SetAvatarRequestModel';
import type { UnlockUsersRequestModel } from '../models/UnlockUsersRequestModel';
import type { UpdateUserGroupsOnUserRequestModel } from '../models/UpdateUserGroupsOnUserRequestModel';
import type { UpdateUserRequestModel } from '../models/UpdateUserRequestModel';
import type { UserDataResponseModel } from '../models/UserDataResponseModel';
import type { UserItemResponseModel } from '../models/UserItemResponseModel';
import type { UserOrderModel } from '../models/UserOrderModel';
import type { UserPermissionsResponseModel } from '../models/UserPermissionsResponseModel';
import type { UserResponseModel } from '../models/UserResponseModel';
import type { UserStateModel } from '../models/UserStateModel';
@@ -191,6 +195,127 @@ export class UserResource {
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrent(): CancelablePromise<CurrentUserResponseModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static postUserCurrentAvatar({
requestBody,
}: {
requestBody?: SetAvatarRequestModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/user/current/avatar',
body: requestBody,
mediaType: 'application/json',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static postUserCurrentChangePassword({
requestBody,
}: {
requestBody?: ChangePasswordUserRequestModel,
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/umbraco/management/api/v1/user/current/change-password',
body: requestBody,
mediaType: 'application/json',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrentData(): CancelablePromise<UserDataResponseModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current/data',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrentLogins(): CancelablePromise<LinkedLoginsRequestModel> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current/logins',
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrentPermissions({
id,
}: {
id?: Array<string>,
}): CancelablePromise<Array<UserPermissionsResponseModel>> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current/permissions',
query: {
'id': id,
},
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrentPermissionsDocument({
id,
}: {
id?: Array<string>,
}): CancelablePromise<Array<UserPermissionsResponseModel>> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current/permissions/document',
query: {
'id': id,
},
});
}
/**
* @returns any Success
* @throws ApiError
*/
public static getUserCurrentPermissionsMedia({
id,
}: {
id?: Array<string>,
}): CancelablePromise<Array<UserPermissionsResponseModel>> {
return __request(OpenAPI, {
method: 'GET',
url: '/umbraco/management/api/v1/user/current/permissions/media',
query: {
'id': id,
},
});
}
/**
* @returns any Success
* @throws ApiError

View File

@@ -86,6 +86,6 @@ export class UmbContextProvider<HostType extends EventTarget = EventTarget> {
destroy(): void {
// I want to make sure to call this, but for now it was too overwhelming to require the destroy method on context instances.
(this.#instance as any).destroy?.();
(this.#instance as any)?.destroy?.();
}
}

View File

@@ -1110,26 +1110,46 @@ class UmbDocumentTypeData extends UmbEntityData<DocumentTypeResponseModel> {
super(data);
}
// TODO: Can we do this smarter so we don't need to make this for each mock data:
insert(item: DocumentTypeResponseModel) {
const result = super.insert(item);
this.treeData.push(createDocumentTypeTreeItem(result));
return result;
}
update(item: DocumentTypeResponseModel) {
const result = super.save(item);
this.treeData = this.treeData.map((x) => {
if(x.id === result.id) {
return createDocumentTypeTreeItem(result);
} else {
return x;
}
});
return result;
}
getTreeRoot(): Array<DocumentTypeTreeItemResponseModel> {
const rootItems = this.treeData.filter((item) => item.parentId === null);
return rootItems.map((item) => createDocumentTypeTreeItem(item));
const result = rootItems.map((item) => createDocumentTypeTreeItem(item));
return result;
}
getTreeItemChildren(id: string): Array<DocumentTypeTreeItemResponseModel> {
const childItems = this.treeData.filter((item) => item.parentId === id);
return childItems.map((item) => createDocumentTypeTreeItem(item));
return childItems.map((item) => item);
}
getTreeItem(ids: Array<string>): Array<DocumentTypeTreeItemResponseModel> {
const items = this.treeData.filter((item) => ids.includes(item.id ?? ''));
return items.map((item) => createDocumentTypeTreeItem(item));
return items.map((item) => item);
}
getAllowedTypesOf(id: string): Array<DocumentTypeTreeItemResponseModel> {
const documentType = this.getById(id);
const allowedTypeKeys = documentType?.allowedContentTypes?.map((documentType) => documentType.id) ?? [];
const items = this.treeData.filter((item) => allowedTypeKeys.includes(item.id ?? ''));
return items.map((item) => createDocumentTypeTreeItem(item));
return items.map((item) => item);
}
}

View File

@@ -618,23 +618,42 @@ class UmbDocumentData extends UmbEntityData<DocumentResponseModel> {
super(data);
}
// TODO: Can we do this smarter so we don't need to make this for each mock data:
insert(item: DocumentResponseModel) {
const result = super.insert(item);
this.treeData.push(createDocumentTreeItem(result));
return result;
}
update(item: DocumentResponseModel) {
const result = super.save(item);
this.treeData = this.treeData.map((x) => {
if(x.id === result.id) {
return createDocumentTreeItem(result);
} else {
return x;
}
});
return result;
}
getTreeRoot(): PagedDocumentTreeItemResponseModel {
const items = this.treeData.filter((item) => item.parentId === null);
const treeItems = items.map((item) => createDocumentTreeItem(item));
const treeItems = items.map((item) => item);
const total = items.length;
return { items: treeItems, total };
}
getTreeItemChildren(id: string): PagedDocumentTreeItemResponseModel {
const items = this.treeData.filter((item) => item.parentId === id);
const treeItems = items.map((item) => createDocumentTreeItem(item));
const treeItems = items.map((item) => item);
const total = items.length;
return { items: treeItems, total };
}
getTreeItem(ids: Array<string>): Array<DocumentTreeItemResponseModel> {
const items = this.treeData.filter((item) => ids.includes(item.id ?? ''));
return items.map((item) => createDocumentTreeItem(item));
return items.map((item) => item);
}
}

View File

@@ -1,4 +1,5 @@
import { UmbData } from './data.js';
import { UmbId } from '@umbraco-cms/backoffice/id';
import type { UmbEntityBase } from '@umbraco-cms/backoffice/models';
// Temp mocked database
@@ -23,6 +24,12 @@ export class UmbEntityData<T extends UmbEntityBase> extends UmbData<T> {
}
insert(item: T) {
// TODO: Remove this fix when all types come with an ID them selfs.
if (!item.id) {
item.id = UmbId.new();
}
const exits = this.data.find((i) => i.id === item.id);
if (exits) {
@@ -30,6 +37,8 @@ export class UmbEntityData<T extends UmbEntityBase> extends UmbData<T> {
}
this.data.push(item);
return item;
}
save(saveItem: T) {

View File

@@ -6,6 +6,7 @@ import type {
FolderTreeItemResponseModel,
DocumentTypeResponseModel,
FileSystemTreeItemPresentationModel,
DocumentResponseModel,
} from '@umbraco-cms/backoffice/backend-api';
export const createEntityTreeItem = (item: any): EntityTreeItemResponseModel => {
@@ -17,7 +18,7 @@ export const createEntityTreeItem = (item: any): EntityTreeItemResponseModel =>
hasChildren: item.hasChildren,
id: item.id,
isContainer: item.isContainer,
parentId: item.parentId,
parentId: item.parentId ?? null,
};
};
@@ -31,6 +32,7 @@ export const createFolderTreeItem = (item: any): FolderTreeItemResponseModel =>
// TODO: remove isTrashed type extension when we have found a solution to trashed items
export const createContentTreeItem = (item: any): ContentTreeItemResponseModel & { isTrashed: boolean } => {
// TODO: There we have to adapt to variants as part of the tree model:
return {
...createEntityTreeItem(item),
noAccess: item.noAccess,
@@ -40,23 +42,28 @@ export const createContentTreeItem = (item: any): ContentTreeItemResponseModel &
// TODO: remove isTrashed type extension when we have found a solution to trashed items
export const createDocumentTreeItem = (
item: DocumentTreeItemResponseModel
item: DocumentResponseModel
): DocumentTreeItemResponseModel & { isTrashed: boolean } => {
return {
...createContentTreeItem(item),
/*
noAccess: item.noAccess,
isProtected: item.isProtected,
isPublished: item.isPublished,
isEdited: item.isEdited,
isTrashed: item.isTrashed,
*/
$type: "DocumentTreeItemViewModel",
type: "document",
icon: "document",// TODO: Should get this from document type...
name: item.variants?.[0].name ?? '',
noAccess: false,
isProtected: false,
isPublished: false,
isEdited: false,
isTrashed: false,
hasChildren: false,
isContainer: false,
};
};
export const createDocumentTypeTreeItem = (item: DocumentTypeResponseModel): DocumentTypeTreeItemResponseModel => {
return {
...createFolderTreeItem(item),
...createEntityTreeItem(item),
type: "document-type",
isElement: item.isElement,
};
};

View File

@@ -9,9 +9,13 @@ export const handlers = [
const data = await req.json();
if (!data) return;
umbDocumentTypeData.insert(data);
// TODO: This is something that is missing in the Full model, but which we need to for the tree model. This should be fixed in the Full model.
data.parentId ??= null;
return res(ctx.status(200));
const created = umbDocumentTypeData.insert(data);
// TODO: remove this hack, as we get the right end-point, in that case we will be in control of the Ids. (I choose this URL to make it clear thats its a hack/mocked URL)
return res(ctx.status(200), ctx.set({'location': '/header/location/id/'+created.id}));
}),
rest.put(umbracoPath(`/document-type/:id`), async (req, res, ctx) => {

View File

@@ -6,7 +6,7 @@ import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type {
DataTypeResponseModel,
DataTypePropertyPresentationModel,
PropertyTypeResponseModelBaseModel,
PropertyTypeModelBaseModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@@ -15,10 +15,10 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr
@customElement('umb-property-type-based-property')
export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
@property({ type: Object, attribute: false })
public get property(): PropertyTypeResponseModelBaseModel | undefined {
public get property(): PropertyTypeModelBaseModel | undefined {
return this._property;
}
public set property(value: PropertyTypeResponseModelBaseModel | undefined) {
public set property(value: PropertyTypeModelBaseModel | undefined) {
const oldProperty = this._property;
this._property = value;
if (this._property?.dataTypeId !== oldProperty?.dataTypeId) {
@@ -26,7 +26,7 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
this._observeProperty();
}
}
private _property?: PropertyTypeResponseModelBaseModel;
private _property?: PropertyTypeModelBaseModel;
@state()
private _propertyEditorUiAlias?: string;

View File

@@ -2,7 +2,7 @@ import {
PropertyContainerTypes,
UmbContentTypePropertyStructureManager,
} from './content-type-structure-manager.class.js';
import { PropertyTypeContainerResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbArrayState, UmbBooleanState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@@ -20,12 +20,12 @@ export class UmbContentTypeContainerStructureHelper {
// Containers defined in data might be more than actual containers to display as we merge them by name.
// Direct containers are the containers defining the total of this container(Multiple containers with the same name and type)
private _directContainers: PropertyTypeContainerResponseModelBaseModel[] = [];
private _directContainers: PropertyTypeContainerModelBaseModel[] = [];
// Owner containers are containers owned by the owner Document Type (The specific one up for editing)
private _ownerContainers: PropertyTypeContainerResponseModelBaseModel[] = [];
private _ownerContainers: PropertyTypeContainerModelBaseModel[] = [];
// State containing the merged containers (only one pr. name):
#containers = new UmbArrayState<PropertyTypeContainerResponseModelBaseModel>([], (x) => x.id);
#containers = new UmbArrayState<PropertyTypeContainerModelBaseModel>([], (x) => x.id);
readonly containers = this.#containers.asObservable();
#hasProperties = new UmbBooleanState(false);
@@ -159,7 +159,7 @@ export class UmbContentTypeContainerStructureHelper {
);
}
private _insertGroupContainers = (groupContainers: PropertyTypeContainerResponseModelBaseModel[]) => {
private _insertGroupContainers = (groupContainers: PropertyTypeContainerModelBaseModel[]) => {
groupContainers.forEach((group) => {
if (group.name !== null && group.name !== undefined) {
if (!this.#containers.getValue().find((x) => x.name === group.name)) {
@@ -188,7 +188,7 @@ export class UmbContentTypeContainerStructureHelper {
async partialUpdateContainer(
containerId?: string,
partialUpdate?: Partial<PropertyTypeContainerResponseModelBaseModel>
partialUpdate?: Partial<PropertyTypeContainerModelBaseModel>
) {
await this.#init;
if (!this.#structure || !containerId || !partialUpdate) return;

View File

@@ -4,7 +4,7 @@ import {
} from './content-type-structure-manager.class.js';
import {
DocumentTypePropertyTypeResponseModel,
PropertyTypeResponseModelBaseModel,
PropertyTypeModelBaseModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@@ -116,7 +116,7 @@ export class UmbContentTypePropertyStructureHelper {
return await this.#structure.createProperty(null, ownerId, sortOrder);
}
async insertProperty(property: PropertyTypeResponseModelBaseModel, sortOrder = 0) {
async insertProperty(property: PropertyTypeModelBaseModel, sortOrder = 0) {
await this.#init;
if (!this.#structure) return false;
@@ -139,7 +139,6 @@ export class UmbContentTypePropertyStructureHelper {
async partialUpdateProperty(propertyKey?: string, partialUpdate?: Partial<DocumentTypePropertyTypeResponseModel>) {
await this.#init;
if (!this.#structure || !propertyKey || !partialUpdate) return;
return await this.#structure.updateProperty(null, propertyKey, partialUpdate);
}
}

View File

@@ -2,8 +2,8 @@ import { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import { UmbId } from '@umbraco-cms/backoffice/id';
import {
DocumentTypePropertyTypeResponseModel,
PropertyTypeContainerResponseModelBaseModel,
PropertyTypeResponseModelBaseModel,
PropertyTypeContainerModelBaseModel,
PropertyTypeModelBaseModel,
DocumentTypeResponseModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UmbControllerHostElement, UmbControllerInterface } from '@umbraco-cms/backoffice/controller-api';
@@ -35,7 +35,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
x.flatMap((x) => x.containers ?? [])
);
#containers = new UmbArrayState<PropertyTypeContainerResponseModelBaseModel>([], (x) => x.id);
#containers = new UmbArrayState<PropertyTypeContainerModelBaseModel>([], (x) => x.id);
constructor(host: UmbControllerHostElement, typeRepository: R) {
this.#host = host;
@@ -95,7 +95,12 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
if (!documentType || !documentType.id) return false;
//const value = documentType as CreateDocumentTypeRequestModel & { id: string };
await this.#contentTypeRepository.create(documentType);
const { data } = await this.#contentTypeRepository.create(documentType);
if (!data) return false;
await this.loadType(data.id)
return true;
}
@@ -171,7 +176,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
await this.#init;
contentTypeId = contentTypeId ?? this.#ownerDocumentTypeId!;
const container: PropertyTypeContainerResponseModelBaseModel = {
const container: PropertyTypeContainerModelBaseModel = {
id: UmbId.new(),
parentId: parentId,
name: 'New',
@@ -190,7 +195,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
async updateContainer(
documentTypeId: string | null,
groupKey: string,
partialUpdate: Partial<PropertyTypeContainerResponseModelBaseModel>
partialUpdate: Partial<PropertyTypeContainerModelBaseModel>
) {
await this.#init;
documentTypeId = documentTypeId ?? this.#ownerDocumentTypeId!;
@@ -216,7 +221,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
await this.#init;
documentTypeId = documentTypeId ?? this.#ownerDocumentTypeId!;
const property: PropertyTypeResponseModelBaseModel = {
const property: PropertyTypeModelBaseModel = {
id: UmbId.new(),
containerId: containerId,
alias: '',
@@ -245,7 +250,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
return property;
}
async insertProperty(documentTypeId: string | null, property: PropertyTypeResponseModelBaseModel) {
async insertProperty(documentTypeId: string | null, property: PropertyTypeModelBaseModel) {
await this.#init;
documentTypeId = documentTypeId ?? this.#ownerDocumentTypeId!;
@@ -270,7 +275,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
async updateProperty(
documentTypeId: string | null,
propertyId: string,
partialUpdate: Partial<PropertyTypeResponseModelBaseModel>
partialUpdate: Partial<PropertyTypeModelBaseModel>
) {
await this.#init;
documentTypeId = documentTypeId ?? this.#ownerDocumentTypeId!;
@@ -349,7 +354,7 @@ export class UmbContentTypePropertyStructureManager<R extends UmbDetailRepositor
}
containersOfParentKey(
parentId: PropertyTypeContainerResponseModelBaseModel['parentId'],
parentId: PropertyTypeContainerModelBaseModel['parentId'],
containerType: PropertyContainerTypes
) {
return this.#containers.getObservablePart((data) => {

View File

@@ -11,25 +11,25 @@ import { UmbDetailRepository, UmbItemRepository } from '@umbraco-cms/backoffice/
export class UmbDeleteEntityAction<
T extends UmbDetailRepository & UmbItemRepository<any>
> extends UmbEntityActionBase<T> {
#modalContext?: UmbModalManagerContext;
#modalManager?: UmbModalManagerContext;
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string) {
super(host, repositoryAlias, unique);
new UmbContextConsumerController(this.host, UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
this.#modalContext = instance;
this.#modalManager = instance;
});
}
async execute() {
if (!this.repository || !this.#modalContext) return;
if (!this.repository || !this.#modalManager) return;
const { data } = await this.repository.requestItems([this.unique]);
if (data) {
const item = data[0];
const modalContext = this.#modalContext.open(UMB_CONFIRM_MODAL, {
const modalContext = this.#modalManager.open(UMB_CONFIRM_MODAL, {
headline: `Delete ${item.name}`,
content: 'Are you sure you want to delete this item?',
color: 'danger',

View File

@@ -15,6 +15,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
UmbPropertySettingsModalResult
> {
//TODO: Should these options come from the server?
// TODO: Or should they come from a extension point?
@state() private _customValidationOptions = [
{
name: 'No validation',
@@ -44,15 +45,14 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
@state()
protected _returnData!: UmbPropertySettingsModalResult;
constructor() {
super();
}
connectedCallback(): void {
super.connectedCallback();
this._returnData = JSON.parse(JSON.stringify(this.data));
const regEx = this._returnData.validation?.regEx ?? '';
this._returnData.validation ??= {};
const regEx = this._returnData.validation.regEx ?? '';
const newlySelected = this._customValidationOptions.find((option) => {
option.selected = option.value === regEx;
return option.selected;
@@ -80,10 +80,6 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
const isValid = form.checkValidity();
if (!isValid) return;
const formData = new FormData(form);
this._returnData.validation!.mandatoryMessage = formData.get('mandatory-message')?.toString() || '';
this.modalContext?.submit(this._returnData);
}
@@ -152,11 +148,6 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
#onToggleAliasLock() {
this._aliasLocked = !this._aliasLocked;
if (this._aliasLocked) {
this._returnData.alias = generateAlias(this._returnData.alias ?? '');
this.requestUpdate('_returnData');
}
}
#onValidationRegExChange(event: UUIInputEvent) {
@@ -238,34 +229,32 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
}
#renderAlignLeftIcon() {
return html`<div
return html`<button
type="button"
@click=${this.#setAppearanceNormal}
@keydown=${() => ''}
class="appearance left ${this._returnData.appearance?.labelOnTop ? '' : 'selected'}">
<svg width="260" height="32" viewBox="0 0 260 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="89" height="14" rx="7" fill="currentColor" />
<rect x="121" width="139" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<rect x="121" y="46" width="108" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<rect x="121" y="23" width="139" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<svg width="200" height="48" viewBox="0 0 200 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="14" rx="6" fill="currentColor" />
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
<rect x="106" width="94" height="60" rx="5" fill="currentColor" fill-opacity="0.4" />
</svg>
<label class="appearance-label"> Label on the left </label>
</div>`;
</button>`;
}
#renderAlignTopIcon() {
return html`
<div
<button
type="button"
@click=${this.#setAppearanceTop}
@keydown=${() => ''}
class="appearance top ${this._returnData.appearance?.labelOnTop ? 'selected' : ''}">
<svg width="139" height="48" viewBox="0 0 139 90" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="89" height="14" rx="7" fill="currentColor" />
<rect y="30" width="139" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<rect y="76" width="108" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<rect y="53" width="139" height="10" rx="5" fill="currentColor" fill-opacity="0.4" />
<svg width="140" height="48" viewBox="0 0 140 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="90" height="14" rx="6" fill="currentColor" />
<rect y="22" width="64" height="9" rx="4" fill="currentColor" fill-opacity="0.4" />
<rect y="42" width="140" height="36" rx="5" fill="currentColor" fill-opacity="0.4" />
</svg>
<label class="appearance-label"> Label on top </label>
</div>
</button>
`;
}
@@ -328,10 +317,11 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
.appearance {
position: relative;
display: flex;
border: 2px solid var(--uui-color-border-standalone);
border: 1px solid var(--uui-color-border-standalone);
background-color: transparent;
padding: var(--uui-size-space-4) var(--uui-size-space-5);
align-items: center;
border-radius: 6px;
border-radius: var(--uui-border-radius);
opacity: 0.8;
flex-direction: column;
justify-content: space-between;
@@ -340,26 +330,8 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
.appearance-label {
font-size: 0.8rem;
line-height: 1;
}
.appearance.selected .appearance-label {
font-weight: bold;
}
.appearance:not(.selected):hover {
border-color: var(--uui-color-border-emphasis);
cursor: pointer;
opacity: 1;
}
.appearance.selected {
border-color: var(--uui-color-selected);
opacity: 1;
}
.appearance.selected::after {
content: '';
position: absolute;
inset: 0;
border-radius: 6px;
opacity: 0.1;
background-color: var(--uui-color-selected);
pointer-events: none;
}
.appearance.left {
flex-grow: 1;
@@ -372,6 +344,28 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement<
width: 100%;
color: var(--uui-color-text);
}
.appearance:not(.selected):hover {
border-color: var(--uui-color-border-emphasis);
cursor: pointer;
opacity: 1;
}
.appearance.selected {
background-color: var(--uui-color-surface);
border-color: var(--uui-color-selected);
color: var(--uui-color-selected);
opacity: 1;
}
.appearance.selected svg {
color: var(--uui-color-selected);
}
.appearance.selected::after {
content: '';
position: absolute;
inset: 0;
border-radius: 6px;
opacity: 0.1;
background-color: var(--uui-color-selected);
}
hr {
border: none;
border-top: 1px solid var(--uui-color-divider);

View File

@@ -11,10 +11,10 @@ import { BehaviorSubject } from '@umbraco-cms/backoffice/external/rxjs';
import { ManifestModal, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbRouterSlotElement } from '@umbraco-cms/backoffice/router';
import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import type { UmbControllerHostElement, UmbControllerInterface } from '@umbraco-cms/backoffice/controller-api';
import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextProviderController, UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbContextProvider, UmbContextToken } from '@umbraco-cms/backoffice/context-api';
/**
* Type which omits the real submit method, and replaces it with a submit method which accepts an optional argument depending on the generic type.
@@ -41,15 +41,17 @@ type OptionalSubmitArgumentIfUndefined<T> = T extends undefined
};
// TODO: consider splitting this into two separate handlers
export class UmbModalContextClass<ModalData extends object = object, ModalResult = unknown> {
export class UmbModalContextClass<ModalData extends object = object, ModalResult = unknown> implements UmbControllerInterface {
#host: UmbControllerHostElement;
#submitPromise: Promise<ModalResult>;
#submitResolver?: (value: ModalResult) => void;
#submitRejecter?: () => void;
private _modalExtensionObserver?: UmbObserverController<ManifestModal | undefined>;
public readonly modalElement: UUIModalDialogElement | UUIModalSidebarElement;
#modalRouterElement: UmbRouterSlotElement = document.createElement('umb-router-slot');
#modalContextProvider;
#innerElement = new BehaviorSubject<HTMLElement | undefined>(undefined);
public readonly innerElement = this.#innerElement.asObservable();
@@ -59,6 +61,10 @@ export class UmbModalContextClass<ModalData extends object = object, ModalResult
public readonly type: UmbModalType = 'dialog';
public readonly size: UUIModalSidebarSize = 'small';
public get unique() {
return 'umbModalContext:'+this.key;
}
constructor(
host: UmbControllerHostElement,
router: IRouterSlot | null,
@@ -108,12 +114,23 @@ export class UmbModalContextClass<ModalData extends object = object, ModalResult
this.modalElement.appendChild(this.#modalRouterElement);
this.#observeModal(modalAlias.toString());
// Note, We are doing the Typing dance here because of the way we are correcting the submit method attribute type.
new UmbContextProviderController(
host,
// Not using a controller, cause we want to use the modal as the provider, this is a UUI element. So its a bit of costume implementation:
this.#modalContextProvider = new UmbContextProvider(
this.modalElement,
UMB_MODAL_CONTEXT_TOKEN,
// Note, We are doing the Typing dance here because of the way we are correcting the submit method attribute type.
this as unknown as UmbModalContext<ModalData, ModalResult>
);
this.#host.addController(this);
}
hostConnected(): void {
this.#modalContextProvider.hostConnected();
}
hostDisconnected(): void {
this.#modalContextProvider.hostDisconnected();
}
#createContainerElement() {
@@ -139,7 +156,8 @@ export class UmbModalContextClass<ModalData extends object = object, ModalResult
If we find a better generic solution to communicate between the modal and the implementor, then we can remove the element as part of the modalContext. */
#observeModal(modalAlias: string) {
if (this.#host) {
new UmbObserverController(
this._modalExtensionObserver?.destroy();
this._modalExtensionObserver = new UmbObserverController(
this.#host,
umbExtensionsRegistry.getByTypeAndAlias('modal', modalAlias),
async (manifest) => {
@@ -150,8 +168,7 @@ export class UmbModalContextClass<ModalData extends object = object, ModalResult
this.#appendInnerElement(innerElement);
}
}
},
'_observeModalExtension'
}
);
}
}
@@ -210,6 +227,14 @@ export class UmbModalContextClass<ModalData extends object = object, ModalResult
public onSubmit(): Promise<ModalResult> {
return this.#submitPromise;
}
destroy(): void {
this.#innerElement.complete();
this._modalExtensionObserver?.destroy();
this._modalExtensionObserver = undefined;
}
}
export const UMB_MODAL_CONTEXT_TOKEN = new UmbContextToken<UmbModalContext>('UmbModalContext');

View File

@@ -108,6 +108,9 @@ export class UmbModalRouteRegistrationController<D extends object = object, R =
`Identifier ${identifier} was not registered at the construction of the modal registration controller, it has to be.`
);
}
const oldValue = this.#uniquePaths.get(identifier);
if(oldValue === value) return;
this.#uniquePaths.set(identifier, value);
this.#registerModal();
}

View File

@@ -68,7 +68,7 @@ export class UmbModalRouteRegistration<UmbModalTokenData extends object = object
public open(params: { [key: string]: string | number }, prepend?: string) {
if (this.active) return;
window.history.pushState({}, '', this.#routeBuilder?.(params) + (prepend ? `/${prepend}` : ''));
window.history.pushState({}, '', this.#routeBuilder?.(params) + (prepend ? `${prepend}` : ''));
}
/**

View File

@@ -1,8 +1,8 @@
import { PropertyTypeResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export type UmbPropertySettingsModalData = PropertyTypeResponseModelBaseModel;
export type UmbPropertySettingsModalResult = PropertyTypeResponseModelBaseModel;
export type UmbPropertySettingsModalData = PropertyTypeModelBaseModel;
export type UmbPropertySettingsModalResult = PropertyTypeModelBaseModel;
export const UMB_PROPERTY_SETTINGS_MODAL = new UmbModalToken<
UmbPropertySettingsModalData,

View File

@@ -212,9 +212,11 @@ export class UmbSorterController<T> implements UmbControllerInterface {
hostDisconnected() {
// TODO: Clean up??
this.#observer.disconnect();
(this.#containerElement as any)['__umbBlockGridSorterController'] = undefined;
this.#containerElement.removeEventListener('dragover', preventDragOver);
(this.#containerElement as any) = undefined;
if(this.#containerElement) {
(this.#containerElement as any)['__umbBlockGridSorterController'] = undefined;
this.#containerElement.removeEventListener('dragover', preventDragOver);
(this.#containerElement as any) = undefined;
}
}
setupItem(element: HTMLElement) {

View File

@@ -76,7 +76,9 @@ export class UmbTreeItemBaseElement extends UmbLitElement {
if (!asObservable) return;
this.observe(asObservable(), (childItems) => {
const oldValue = this._childItems;
this._childItems = childItems;
this.requestUpdate('_childItems', oldValue);
});
}

View File

@@ -42,7 +42,7 @@ export class UmbTreeElement extends UmbLitElement {
this.#treeContext.setMultiple(newVal);
}
// TODO: what is the best name for this functionatliy?
// TODO: what is the best name for this functionality?
private _hideTreeRoot = false;
@property({ type: Boolean, attribute: 'hide-tree-root' })
get hideTreeRoot() {
@@ -96,8 +96,9 @@ export class UmbTreeElement extends UmbLitElement {
if (asObservable) {
this.#rootItemsObserver = this.observe(asObservable(), (rootItems) => {
const oldValue = this._items;
this._items = rootItems;
this.requestUpdate();
this.requestUpdate('_items', oldValue);
});
}
}
@@ -116,7 +117,8 @@ export class UmbTreeElement extends UmbLitElement {
return html`
${repeat(
this._items,
(item, index) => index,
// TODO: use unique here:
(item, index) => item.name + '___' + index,
(item) => html`<umb-tree-item .item=${item}></umb-tree-item>`
)}
`;

View File

@@ -7,12 +7,12 @@ export class UmbVariantId {
public readonly segment: string | null = null;
constructor(variantData: { culture?: string | null; segment?: string | null }) {
this.culture = variantData.culture || null;
this.segment = variantData.segment || null;
this.culture = (variantData.culture === 'invariant' ? null : variantData.culture) ?? null;
this.segment = variantData.segment ?? null;
}
public compare(obj: { culture?: string | null; segment?: string | null }): boolean {
return this.culture === (obj.culture || null) && this.segment === (obj.segment || null);
return this.equal(new UmbVariantId(obj));
}
public equal(variantId: UmbVariantId): boolean {

View File

@@ -41,17 +41,18 @@ export class UmbWorkspaceFooterLayoutElement extends UmbLitElement {
@state()
_withinModal = false;
#modalContext?: UmbModalContext;
@state()
_modalContext?: UmbModalContext;
constructor() {
super();
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (context) => {
this.#modalContext = context;
this._modalContext = context;
});
}
private _onClose = () => {
this.#modalContext?.reject();
this._modalContext?.reject();
};
// TODO: Some event/callback from umb-extension-slot that can be utilized to hide the footer, if empty.
@@ -59,10 +60,10 @@ export class UmbWorkspaceFooterLayoutElement extends UmbLitElement {
return html`
<umb-footer-layout>
<slot></slot>
<slot name="actions" slot="actions"></slot>
${this.#modalContext
${this._modalContext
? html`<uui-button slot="actions" label="Close" @click=${this._onClose}></uui-button>`
: ''}
<slot name="actions" slot="actions"></slot>
<umb-extension-slot
slot="actions"
type="workspaceAction"

View File

@@ -2,17 +2,17 @@ import { UMB_WORKSPACE_VARIANT_CONTEXT_TOKEN } from '../workspace-variant.contex
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { css, html , customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { PropertyTypeResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import type { PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@customElement('umb-variantable-property')
export class UmbVariantablePropertyElement extends UmbLitElement {
private _property?: PropertyTypeResponseModelBaseModel | undefined;
private _property?: PropertyTypeModelBaseModel | undefined;
@property({ type: Object, attribute: false })
public get property(): PropertyTypeResponseModelBaseModel | undefined {
public get property(): PropertyTypeModelBaseModel | undefined {
return this._property;
}
public set property(property: PropertyTypeResponseModelBaseModel | undefined) {
public set property(property: PropertyTypeModelBaseModel | undefined) {
this._property = property;
this._updatePropertyVariantId();
}

View File

@@ -53,7 +53,7 @@ export class UmbWorkspaceVariantContext {
this._observeVariant();
});
this.index.subscribe(() => {
new UmbObserverController(host, this.#index, () => {
this._observeVariant();
});
}

View File

@@ -3,7 +3,7 @@ import { manifests as createManifests } from './create/manifests.js';
import {
UmbCopyEntityAction,
UmbMoveEntityAction,
UmbTrashEntityAction,
UmbDeleteEntityAction,
UmbSortChildrenOfEntityAction,
} from '@umbraco-cms/backoffice/entity-action';
import { ManifestEntityAction } from '@umbraco-cms/backoffice/extension-registry';
@@ -13,14 +13,14 @@ const entityType = 'document-type';
const entityActions: Array<ManifestEntityAction> = [
{
type: 'entityAction',
alias: 'Umb.EntityAction.DocumentType.Trash',
name: 'Trash Document-Type Entity Action',
alias: 'Umb.EntityAction.DocumentType.Delete',
name: 'Delete Document-Type Entity Action',
weight: 900,
meta: {
icon: 'umb:trash',
label: 'Trash',
label: 'Delete (TBD)',
repositoryAlias: DOCUMENT_TYPE_REPOSITORY_ALIAS,
api: UmbTrashEntityAction,
api: UmbDeleteEntityAction,
},
conditions: {
entityTypes: [entityType],

View File

@@ -3,7 +3,7 @@ import { html, nothing, customElement, state, ifDefined } from '@umbraco-cms/bac
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UmbAllowedDocumentTypesModalData, UmbAllowedDocumentTypesModalResult } from '@umbraco-cms/backoffice/modal';
import { UmbModalBaseElement } from '@umbraco-cms/internal/modal';
import { DocumentTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { DocumentTypeResponseModel, DocumentTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api';
@customElement('umb-allowed-document-types-modal')
export class UmbAllowedDocumentTypesModalElement extends UmbModalBaseElement<
@@ -21,17 +21,20 @@ export class UmbAllowedDocumentTypesModalElement extends UmbModalBaseElement<
public connectedCallback() {
super.connectedCallback();
const parentId = this.data?.parentId;
const parentName = this.data?.parentName;
if (parentName) {
this._headline = `Create at '${parentName}'`;
} else {
this._headline = `Create`;
}
if (this.data?.parentId) {
if (parentId) {
// TODO: Support root aka. id of null? or maybe its an active prop, like 'atRoot'.
// TODO: show error
this._retrieveAllowedChildrenOf(this.data.parentId);
this._retrieveAllowedChildrenOf(parentId);
} else {
this._retrieveAllowedChildrenOfRoot();
}
}
@@ -43,6 +46,25 @@ export class UmbAllowedDocumentTypesModalElement extends UmbModalBaseElement<
}
}
private async _retrieveAllowedChildrenOfRoot() {
// TODO: This is a hack until we get the right end points (Which will become a Document end point. meaning this modal should have another name, it should be named so its clear that this is for documents, not document types. ex.: 'create-document-modal')
const { data } = await this.#documentTypeRepository.requestRootTreeItems();
if (!data) return;
const allFullModels: Array<DocumentTypeResponseModel & {$type: ''}> = [];
await Promise.all(
data.items.map((item) => {
if (item.id) {
return this.#documentTypeRepository.requestById(item.id).then((result) => {if(result.data) {allFullModels.push({$type: '', ...result.data})}});
}
return Promise.resolve();
})
);
this._allowedDocumentTypes = allFullModels.filter((item) => item.allowedAsRoot) ?? [];
// End of hack...^^
}
private _handleCancel() {
this.modalContext?.reject();
}

View File

@@ -6,16 +6,19 @@ import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import {
CreateDocumentTypeRequestModel,
DocumentTypeResponseModel,
EntityTreeItemResponseModel,
FolderTreeItemResponseModel,
UpdateDocumentTypeRequestModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
type ItemType = DocumentTypeResponseModel;
type ItemType = DocumentTypeResponseModel & {$type: string};
export class UmbDocumentTypeRepository
implements UmbTreeRepository<EntityTreeItemResponseModel>, UmbDetailRepository<ItemType>
implements UmbTreeRepository<EntityTreeItemResponseModel>,
UmbDetailRepository<CreateDocumentTypeRequestModel, any, UpdateDocumentTypeRequestModel, DocumentTypeResponseModel>
{
#init!: Promise<unknown>;
@@ -131,6 +134,7 @@ export class UmbDocumentTypeRepository
if (data) {
this.#detailStore?.append(data);
}
return { data };
}
@@ -163,22 +167,38 @@ export class UmbDocumentTypeRepository
// Could potentially be general methods:
async create(documentType: ItemType) {
if (!documentType || !documentType.id) throw new Error('Template is missing');
await this.#init;
const { error } = await this.#detailDataSource.insert(documentType);
const { error, data } = await this.#detailDataSource.insert(documentType);
if (!error) {
const treeItem = createTreeItem(documentType);
this.#treeStore?.appendItems([treeItem]);
if (!error && data) {
const notification = { data: { message: `Document Type created` } };
this.#notificationContext?.peek('positive', notification);
// TODO: The parts here is a hack, when we can trust the IDs we send, then this should be removed/changed:
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#detailStore?.append(documentType);
// TODO: Update tree store with the new item? or ask tree to request the new item?
const splitResultUrl = data.split("/");
const newId = splitResultUrl[splitResultUrl.length - 1];
// Temporary hack while we are not in control of IDs:
const newDocument = {...(await this.requestById(newId)).data, $type: ''};
if(newDocument) {
const notification = { data: { message: `Document Type created` } };
this.#notificationContext?.peek('positive', notification);
await this.requestRootTreeItems();
// TODO: currently we cannot put this data into our store, cause we don't have the right ID, as the server currently changes it (and other ids of it, container-id and property-id)
//this.#detailStore?.append(newDocument);
//const treeItem = createTreeItem(newDocument);
//this.#treeStore?.appendItems([treeItem]);
return { data: newDocument };
}
}
return { error };

View File

@@ -1,5 +1,5 @@
import type { UmbDataSource } from '@umbraco-cms/backoffice/repository';
import { DocumentTypeResource, DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { CreateDocumentTypeRequestModel, DocumentTypeResource, DocumentTypeResponseModel, UpdateDocumentTypeRequestModel } from '@umbraco-cms/backoffice/backend-api';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
import { UmbId } from '@umbraco-cms/backoffice/id';
@@ -10,7 +10,7 @@ import { UmbId } from '@umbraco-cms/backoffice/id';
* @class UmbDocumentTypeServerDataSource
* @implements {RepositoryDetailDataSource}
*/
export class UmbDocumentTypeServerDataSource implements UmbDataSource<any, any, any, DocumentTypeResponseModel> {
export class UmbDocumentTypeServerDataSource implements UmbDataSource<CreateDocumentTypeRequestModel, any, UpdateDocumentTypeRequestModel, DocumentTypeResponseModel> {
#host: UmbControllerHostElement;
/**
@@ -48,8 +48,12 @@ export class UmbDocumentTypeServerDataSource implements UmbDataSource<any, any,
* @memberof UmbDocumentTypeServerDataSource
*/
async createScaffold(parentId: string | null) {
const data: DocumentTypeResponseModel = {
// TODO: Type hack to append $type and parentId to the DocumentTypeResponseModel.
//, parentId: string | null
const data: DocumentTypeResponseModel & {$type: string} = {
$type: 'string',
id: UmbId.new(),
//parentId: parentId,
name: '',
alias: '',
description: '',
@@ -62,7 +66,11 @@ export class UmbDocumentTypeServerDataSource implements UmbDataSource<any, any,
compositions: [],
allowedTemplateIds: [],
defaultTemplateId: null,
cleanup: undefined,
cleanup: {
preventCleanup: false,
keepAllVersionsNewerThanDays: null,
keepLatestVersionPerDayForDays: null,
},
properties: [],
containers: [],
};
@@ -70,66 +78,58 @@ export class UmbDocumentTypeServerDataSource implements UmbDataSource<any, any,
return { data };
}
/**
* Inserts a new Document on the server
* @param {Document} document
* Inserts a new Document Type on the server
* @param {CreateDocumentTypeRequestModel} documentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async insert(document: DocumentTypeResponseModel) {
if (!document.id) throw new Error('ID is missing');
async insert(documentType: CreateDocumentTypeRequestModel) {
if (!documentType) throw new Error('Document is missing');
//if (!document.id) throw new Error('ID is missing');
let body: string;
documentType = {...documentType};
try {
body = JSON.stringify(document);
} catch (error) {
console.error(error);
return Promise.reject();
}
//return tryExecuteAndNotify(this.#host, DocumentTypeResource.postDocument(payload));
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<string>(
// TODO: Hack to remove some props that ruins the document-type post end-point.
(documentType as any).$type = undefined;
(documentType as any).id = undefined;
// TODO: Investigate if this matters (should go away anyway when we have the end-point accepts us defining the ids.)
documentType.properties = documentType.properties?.map((prop) => {
return {
...prop,
id: undefined,
}
});
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document-type', {
method: 'POST',
body: body,
headers: {
'Content-Type': 'application/json',
},
}) as any
DocumentTypeResource.postDocumentType({
requestBody: documentType,
}),
);
}
/**
* Updates a Document on the server
* @param {Document} Document
* Updates a Document Type on the server
* @param {string} id
* @param {Document} documentType
* @return {*}
* @memberof UmbDocumentTypeServerDataSource
*/
async update(id: string, document: any) {
async update(id: string, documentType: UpdateDocumentTypeRequestModel) {
if (!id) throw new Error('Id is missing');
let body: string;
documentType = {...documentType};
try {
body = JSON.stringify(document);
} catch (error) {
console.error(error);
return Promise.reject();
}
// TODO: Hack to remove some props that ruins the document-type post end-point.
(documentType as any).$type = undefined;
(documentType as any).id = undefined;
// TODO: use resources when end point is ready:
return tryExecuteAndNotify<DocumentTypeResponseModel>(
this.#host,
fetch(`/umbraco/management/api/v1/document-type/${document.id}`, {
method: 'PUT',
body: body,
headers: {
'Content-Type': 'application/json',
},
}) as any
);
return tryExecuteAndNotify(this.#host, DocumentTypeResource.putDocumentTypeById({ id, requestBody: documentType }));
}
/**
@@ -143,16 +143,8 @@ export class UmbDocumentTypeServerDataSource implements UmbDataSource<any, any,
throw new Error('Id is missing');
}
return tryExecuteAndNotify(
this.#host,
fetch('/umbraco/management/api/v1/document-type/trash', {
method: 'POST',
body: JSON.stringify([id]),
headers: {
'Content-Type': 'application/json',
},
}).then((res) => res.json())
);
// TODO: Hack the type to avoid type-error here:
return tryExecuteAndNotify(this.#host, DocumentTypeResource.deleteDocumentTypeById({ id })) as any;
}
/**

View File

@@ -8,6 +8,7 @@ import {
UMB_ICON_PICKER_MODAL,
} from '@umbraco-cms/backoffice/modal';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { generateAlias } from '@umbraco-cms/backoffice/utils';
@customElement('umb-document-type-workspace-editor')
export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
@state()
@@ -25,6 +26,9 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
@state()
private _alias?: string;
@state()
private _aliasLocked = true;
private _modalContext?: UmbModalManagerContext;
constructor() {
@@ -42,24 +46,45 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
#observeDocumentType() {
if (!this.#workspaceContext) return;
this.observe(this.#workspaceContext.name, (name) => (this._name = name));
this.observe(this.#workspaceContext.alias, (alias) => (this._alias = alias));
this.observe(this.#workspaceContext.icon, (icon) => (this._icon = icon));
this.observe(this.#workspaceContext.name, (name) => (this._name = name), '_observeName');
this.observe(this.#workspaceContext.alias, (alias) => (this._alias = alias), '_observeAlias');
this.observe(this.#workspaceContext.icon, (icon) => (this._icon = icon), '_observeIcon');
this.observe(this.#workspaceContext.isNew, (isNew) => {
if(isNew) {
// TODO: Would be good with a more general way to bring focus to the name input.
(this.shadowRoot?.querySelector('#name') as HTMLElement)?.focus();
}
this.removeControllerByUnique('_observeIsNew');
}, '_observeIsNew');
}
// TODO. find a way where we don't have to do this for all workspaces.
private _handleNameInput(event: UUIInputEvent) {
#onNameChange(event: UUIInputEvent) {
if (event instanceof UUIInputEvent) {
const target = event.composedPath()[0] as UUIInputElement;
if (typeof target?.value === 'string') {
const oldName = this._name;
const oldAlias = this._alias;
const newName = event.target.value.toString();
if (this._aliasLocked) {
const expectedOldAlias = generateAlias(oldName ?? '');
// Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.)
if (expectedOldAlias === oldAlias) {
this.#workspaceContext?.setAlias(generateAlias(newName));
}
}
this.#workspaceContext?.setName(target.value);
}
}
}
// TODO. find a way where we don't have to do this for all workspaces.
private _handleAliasInput(event: UUIInputEvent) {
#onAliasChange(event: UUIInputEvent) {
if (event instanceof UUIInputEvent) {
const target = event.composedPath()[0] as UUIInputElement;
@@ -70,6 +95,10 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
event.stopPropagation();
}
#onToggleAliasLock() {
this._aliasLocked = !this._aliasLocked;
}
private async _handleIconClick() {
const modalContext = this._modalContext?.open(UMB_ICON_PICKER_MODAL, {
icon: this._icon,
@@ -90,10 +119,23 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
<uui-icon name="${this._icon}" style="color: ${this._iconColorAlias}"></uui-icon>
</uui-button>
<uui-input id="name" .value=${this._name} @input="${this._handleNameInput}">
<uui-input-lock id="alias" slot="append" .value=${this._alias} @input="${this._handleAliasInput}"></uui-input
></uui-input-lock>
<uui-input id="name" .value=${this._name} @input="${this.#onNameChange}">
<!-- TODO: should use UUI-LOCK-INPUT, but that does not fire an event when its locked/unlocked -->
<uui-input
name="alias"
slot="append"
@input=${this.#onAliasChange}
.value=${this._alias}
placeholder="Enter alias..."
?disabled=${this._aliasLocked}>
<!-- TODO: validation for bad characters -->
<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
<uui-icon name=${this._aliasLocked ? 'umb:lock' : 'umb:unlocked'}></uui-icon>
</div>
</uui-input>
</uui-input>
</div>
<div slot="footer-info">
@@ -133,10 +175,14 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
align-items: center;
}
#alias {
height: calc(100% - 2px);
--uui-input-border-width: 0;
--uui-button-height: calc(100% -2px);
#alias-lock {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
#alias-lock uui-icon {
margin-bottom: 2px;
}
#icon {
@@ -144,6 +190,7 @@ export class UmbDocumentTypeWorkspaceEditorElement extends UmbLitElement {
margin-right: var(--uui-size-space-2);
margin-left: calc(var(--uui-size-space-4) * -1);
}
`,
];
}

View File

@@ -114,7 +114,8 @@ export class UmbDocumentTypeWorkspaceContext
this.structure.updateOwnerDocumentType({ defaultTemplateId });
}
async createScaffold(parentId: string | null) {
async create(parentId: string | null) {
const { data } = await this.structure.createScaffold(parentId);
if (!data) return undefined;

View File

@@ -1,30 +1,46 @@
import { UmbDocumentTypeWorkspaceContext } from './document-type-workspace.context.js';
import { UmbDocumentTypeWorkspaceEditorElement } from './document-type-workspace-editor.element.js';
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { html , customElement, state } from '@umbraco-cms/backoffice/external/lit';
import type { UmbRoute } from '@umbraco-cms/backoffice/router';
import { UmbRoute, UmbRouterSlotInitEvent, generateRoutePathBuilder } from '@umbraco-cms/backoffice/router';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@customElement('umb-document-type-workspace')
export class UmbDocumentTypeWorkspaceElement extends UmbLitElement {
#workspaceContext = new UmbDocumentTypeWorkspaceContext(this);
#element = new UmbDocumentTypeWorkspaceEditorElement();
#routerPath? = '';
@state()
_routes: UmbRoute[] = [
{
path: 'create/:parentId',
component: () => this.#element,
component: import('./document-type-workspace-editor.element.js'),
setup: (_component, info) => {
const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId;
this.#workspaceContext.createScaffold(parentId);
this.#workspaceContext.create(parentId);
// Navigate to edit route when language is created:
this.observe(
this.#workspaceContext.isNew,
(isNew) => {
if (isNew === false) {
const id = this.#workspaceContext.getEntityId();
if (this.#routerPath && id) {
const routeBasePath = this.#routerPath.endsWith('/') ? this.#routerPath : this.#routerPath + '/';
// TODO: Revisit if this is the right way to change URL:
const newPath = generateRoutePathBuilder(routeBasePath + 'edit/:id')({ id });
window.history.pushState({}, '', newPath);
}
}
},
'_observeIsNew'
);
},
},
{
path: 'edit/:id',
component: () => this.#element,
component: import('./document-type-workspace-editor.element.js'),
setup: (_component, info) => {
console.log('Setup for edit/id');
this.removeControllerByUnique('_observeIsNew');
const id = info.match.params.id;
this.#workspaceContext.load(id);
},
@@ -32,7 +48,9 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement {
];
render() {
return html` <umb-router-slot .routes=${this._routes}></umb-router-slot> `;
return html` <umb-router-slot .routes=${this._routes} @init=${(event: UmbRouterSlotInitEvent) => {
this.#routerPath = event.target.absoluteRouterPath;
}}></umb-router-slot> `;
}
static styles = [UUITextStyles];

View File

@@ -6,7 +6,7 @@ import { UmbSorterController, UmbSorterConfig } from '@umbraco-cms/backoffice/so
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import {
DocumentTypePropertyTypeResponseModel,
PropertyTypeResponseModelBaseModel,
PropertyTypeModelBaseModel,
} from '@umbraco-cms/backoffice/backend-api';
import { UMB_MODAL_MANAGER_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
import './document-type-workspace-view-edit-property.element.js';
@@ -78,9 +78,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
_propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this);
@state()
_propertyStructure: Array<PropertyTypeResponseModelBaseModel> = [];
#modalContext?: typeof UMB_MODAL_MANAGER_CONTEXT_TOKEN.TYPE;
_propertyStructure: Array<PropertyTypeModelBaseModel> = [];
constructor() {
super();
@@ -90,7 +88,6 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
(workspaceContext as UmbDocumentTypeWorkspaceContext).structure
);
});
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => (this.#modalContext = instance));
this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => {
this._propertyStructure = propertyStructure;
this.#propertySorter.setModel(this._propertyStructure);
@@ -101,25 +98,16 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
const property = await this._propertyStructureHelper.addProperty(this._containerId);
if (!property) return;
// TODO: Figure out how we from this location can get into the routeable modal..
/*
// Take id and parse to modal:
console.log('property id:', property.id!, property);
// TODO: Figure out how we from this location can get into the route modal, via URL.
// The modal is registered by the document-type-workspace-view-edit-property element, therefor a bit hard to get the URL from here.
// TODO: route modal..
const modalContext = this.#modalContext?.open(UMB_PROPERTY_SETTINGS_MODAL);
modalContext?.onSubmit().then((result) => {
console.log(result);
});
*/
}
render() {
return html`<div id="property-list">
${repeat(
this._propertyStructure,
(property) => property.alias ?? '' + property.containerId ?? '' + (property as any).sortOrder ?? '',
(property) => property.id ?? '' + property.containerId ?? '' + (property as any).sortOrder ?? '',
(property) =>
html`<document-type-workspace-view-edit-property
class="property"

View File

@@ -1,8 +1,9 @@
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { PropertyTypeResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { generateAlias } from '@umbraco-cms/backoffice/utils';
/**
* @element document-type-workspace-view-edit-property
@@ -11,18 +12,18 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
*/
@customElement('document-type-workspace-view-edit-property')
export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
private _property?: PropertyTypeResponseModelBaseModel | undefined;
private _property?: PropertyTypeModelBaseModel | undefined;
/**
* Property, the data object for the property.
* @type {PropertyTypeResponseModelBaseModel}
* @type {PropertyTypeModelBaseModel}
* @attr
* @default undefined
*/
@property({ type: Object })
public get property(): PropertyTypeResponseModelBaseModel | undefined {
public get property(): PropertyTypeModelBaseModel | undefined {
return this._property;
}
public set property(value: PropertyTypeResponseModelBaseModel | undefined) {
public set property(value: PropertyTypeModelBaseModel | undefined) {
const oldValue = this._property;
this._property = value;
this.#modalRegistration.setUniquePathValue('propertyId', value?.id?.toString());
@@ -60,7 +61,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
});
}
_partialUpdate(partialObject: PropertyTypeResponseModelBaseModel) {
_partialUpdate(partialObject: PropertyTypeModelBaseModel) {
this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject }));
}
@@ -84,20 +85,55 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
: '';
}
@state()
private _aliasLocked = true;
#onToggleAliasLock() {
this._aliasLocked = !this._aliasLocked;
}
#onNameChange(event: UUIInputEvent) {
if (event instanceof UUIInputEvent) {
const target = event.composedPath()[0] as UUIInputElement;
if (typeof target?.value === 'string') {
const oldName = this.property?.name ?? '';
const oldAlias = this.property?.alias ?? '';
const newName = event.target.value.toString();
if (this._aliasLocked) {
const expectedOldAlias = generateAlias(oldName ?? '');
// Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.)
if (expectedOldAlias === oldAlias) {
this._singleValueUpdate('alias', generateAlias(newName ?? ''));
}
}
this._singleValueUpdate('name', newName);
}
}
}
renderEditableProperty() {
return this.property
? html`
<div id="header">
<uui-input
.value=${this.property.name}
@input=${(e: CustomEvent) => {
if (e.target) this._singleValueUpdate('name', (e.target as HTMLInputElement).value);
}}></uui-input>
<uui-input-lock
@input=${this.#onNameChange}></uui-input>
<!-- TODO: should use UUI-LOCK-INPUT, but that does not fire an event when its locked/unlocked -->
<uui-input
name="alias"
.value=${this.property.alias}
?disabled=${this._aliasLocked}
@input=${(e: CustomEvent) => {
if (e.target) this._singleValueUpdate('alias', (e.target as HTMLInputElement).value);
}}></uui-input-lock>
}}
>
<!-- TODO: validation for bad characters -->
<div @click=${this.#onToggleAliasLock} @keydown=${() => ''} id="alias-lock" slot="prepend">
<uui-icon name=${this._aliasLocked ? 'umb:lock' : 'umb:unlocked'}></uui-icon>
</div>
</uui-input>
<slot name="property-action-menu"></slot>
<p>
<uui-textarea
@@ -180,6 +216,17 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
#editor {
background-color: var(--uui-color-background);
}
#alias-lock {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
#alias-lock uui-icon {
margin-bottom: 2px;
}
`,
];
}

View File

@@ -3,7 +3,7 @@ import { css, html, customElement, property, state, repeat } from '@umbraco-cms/
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { PropertyTypeContainerResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import './document-type-workspace-view-edit-properties.element.js';
@@ -48,7 +48,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement {
_groupStructureHelper = new UmbContentTypeContainerStructureHelper(this);
@state()
_groups: Array<PropertyTypeContainerResponseModelBaseModel> = [];
_groups: Array<PropertyTypeContainerModelBaseModel> = [];
@state()
_hasProperties = false;

View File

@@ -5,7 +5,7 @@ import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type';
import { encodeFolderName, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { PropertyTypeContainerResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import type { UmbRoute } from '@umbraco-cms/backoffice/router';
import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry';
@@ -22,7 +22,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement
private _routes: UmbRoute[] = [];
@state()
_tabs: Array<PropertyTypeContainerResponseModelBaseModel> = [];
_tabs: Array<PropertyTypeContainerModelBaseModel> = [];
@state()
private _routerPath?: string;

View File

@@ -13,10 +13,9 @@ import {
} from '@umbraco-cms/backoffice/backend-api';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
type ItemType = DocumentResponseModel;
export class UmbDocumentRepository
implements UmbTreeRepository<DocumentTreeItemResponseModel>, UmbDetailRepository<ItemType>
implements UmbTreeRepository<DocumentTreeItemResponseModel>,
UmbDetailRepository<CreateDocumentRequestModel, any, UpdateDocumentRequestModel, DocumentResponseModel>
{
#init!: Promise<unknown>;
@@ -124,10 +123,10 @@ export class UmbDocumentRepository
// DETAILS:
async createScaffold(documentTypeKey: string) {
async createScaffold(documentTypeKey: string, preset?: Partial<CreateDocumentRequestModel>) {
if (!documentTypeKey) throw new Error('Document type id is missing');
await this.#init;
return this.#detailDataSource.createScaffold(documentTypeKey);
return this.#detailDataSource.createScaffold(documentTypeKey, preset);
}
async requestById(id: string) {
@@ -165,14 +164,18 @@ export class UmbDocumentRepository
const { error } = await this.#detailDataSource.insert(item);
if (!error) {
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#store?.append(item);
// TODO: Update tree store with the new item? or ask tree to request the new item?
// TODO: Revisit this call, as we should be able to update tree on client.
await this.requestRootTreeItems();
const notification = { data: { message: `Document created` } };
this.#notificationContext?.peek('positive', notification);
}
// TODO: we currently don't use the detail store for anything.
// Consider to look up the data before fetching from the server
this.#store?.append(item);
// TODO: Update tree store with the new item? or ask tree to request the new item?
return { error };
}
@@ -193,6 +196,9 @@ export class UmbDocumentRepository
//this.#treeStore?.updateItem(item.id, { name: item.name });// Port data to tree store.
// TODO: would be nice to align the stores on methods/methodNames.
// TODO: Revisit this call, as we should be able to update tree on client.
await this.requestRootTreeItems();
const notification = { data: { message: `Document saved` } };
this.#notificationContext?.peek('positive', notification);
}

View File

@@ -55,16 +55,17 @@ export class UmbDocumentServerDataSource
* @return {*}
* @memberof UmbDocumentServerDataSource
*/
async createScaffold(documentTypeId: string) {
async createScaffold(documentTypeId: string, preset?: Partial<CreateDocumentRequestModel>) {
const data: DocumentResponseModel = {
urls: [],
templateId: null,
parentId: null,
id: UmbId.new(),
contentTypeId: documentTypeId,
values: [],
variants: [
{
$type: '',
$type: 'DocumentVariantRequestModel',
state: ContentStateModel.DRAFT,
publishDate: null,
culture: null,
@@ -74,6 +75,7 @@ export class UmbDocumentServerDataSource
updateDate: undefined,
},
],
...preset,
};
return { data };

View File

@@ -68,7 +68,7 @@ export class UmbDocumentTreeServerDataSource implements UmbTreeDataSource {
async getChildrenOf(parentId: string | null) {
if (parentId === undefined) throw new Error('Parent id is missing');
/* TODO: should we make getRootItems() internal
/* TODO: should we make getRootItems() internal
so it only is a server concern that there are two endpoints? */
if (parentId === null) {
return this.getRootItems();

View File

@@ -75,8 +75,8 @@ export class UmbDocumentWorkspaceContext
return data || undefined;
}
async createScaffold(documentTypeKey: string) {
const { data } = await this.repository.createScaffold(documentTypeKey);
async create(documentTypeKey: string, parentId: string | null) {
const { data } = await this.repository.createScaffold(documentTypeKey, {parentId});
if (!data) return undefined;
this.setIsNew(true);

View File

@@ -9,24 +9,23 @@ import './document-workspace-editor.element.js';
@customElement('umb-document-workspace')
export class UmbDocumentWorkspaceElement extends UmbLitElement {
#workspaceContext = new UmbDocumentWorkspaceContext(this);
#element = document.createElement('umb-document-workspace-editor');
@state()
_routes: UmbRoute[] = [
{
path: 'create/:parentId/:documentTypeKey',
component: () => this.#element,
component: import('./document-workspace-editor.element.js'),
setup: async (_component, info) => {
// TODO: use parent id:
// TODO: Notice the perspective of permissions here, we need to check if the user has access to create a document of this type under this parent?
const parentId = info.match.params.parentId;
const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId;
const documentTypeKey = info.match.params.documentTypeKey;
this.#workspaceContext.createScaffold(documentTypeKey);
this.#workspaceContext.create(documentTypeKey, parentId);
},
},
{
path: 'edit/:id',
component: () => this.#element,
component: import('./document-workspace-editor.element.js'),
setup: (_component, info) => {
const id = info.match.params.id;
this.#workspaceContext.load(id);

View File

@@ -3,7 +3,7 @@ import { css, html , customElement, property, state , repeat } from '@umbraco-cm
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UmbContentTypePropertyStructureHelper, PropertyContainerTypes } from '@umbraco-cms/backoffice/content-type';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { PropertyTypeResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
@customElement('umb-document-workspace-view-edit-properties')
export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement {
@@ -26,7 +26,7 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement
_propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this);
@state()
_propertyStructure: Array<PropertyTypeResponseModelBaseModel> = [];
_propertyStructure: Array<PropertyTypeModelBaseModel> = [];
constructor() {
super();

View File

@@ -3,7 +3,7 @@ import { css, html, customElement, property, state, repeat } from '@umbraco-cms/
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { PropertyTypeContainerResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import './document-workspace-view-edit-properties.element.js';
@@ -34,7 +34,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement {
_groupStructureHelper = new UmbContentTypeContainerStructureHelper(this);
@state()
_groups: Array<PropertyTypeContainerResponseModelBaseModel> = [];
_groups: Array<PropertyTypeContainerModelBaseModel> = [];
@state()
_hasProperties = false;

View File

@@ -9,7 +9,7 @@ import {
UmbRouterSlotInitEvent,
} from '@umbraco-cms/backoffice/router';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { PropertyTypeContainerResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api';
import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry';
@@ -25,7 +25,7 @@ export class UmbDocumentWorkspaceViewEditElement
private _routes: UmbRoute[] = [];
@state()
_tabs: Array<PropertyTypeContainerResponseModelBaseModel> = [];
_tabs: Array<PropertyTypeContainerModelBaseModel> = [];
@state()
private _routerPath?: string;

View File

@@ -44,7 +44,7 @@ export class UmbWorkspaceMediaTypeContext
}
}
async createScaffold() {
async create() {
const { data } = await this.repository.createScaffold();
if (!data) return;
this.setIsNew(true);

View File

@@ -53,7 +53,7 @@ export class UmbMediaWorkspaceContext
}
}
async createScaffold(parentId: string | null) {
async create(parentId: string | null) {
const { data } = await this.repository.createScaffold(parentId);
if (!data) return;
this.setIsNew(true);

View File

@@ -46,7 +46,7 @@ export class UmbWorkspaceMemberGroupContext
}
}
async createScaffold() {
async create() {
const { data } = await this.repository.createScaffold();
if (!data) return;
this.setIsNew(true);

View File

@@ -2,7 +2,7 @@ import type { UmbDataTypeModel } from '../../models.js';
import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UUITextStyles, FormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbModalRouteRegistrationController, UMB_DATA_TYPE_PICKER_FLOW_MODAL } from '@umbraco-cms/backoffice/modal';
import { UmbModalRouteRegistrationController, UMB_DATA_TYPE_PICKER_FLOW_MODAL, UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/modal';
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
// Note: Does only support picking a single data type. But this could be developed later into this same component. To follow other picker input components.
@@ -37,8 +37,11 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
this.#itemsManager.setUniques(super.value.split(','));
}
#editDataTypeModal?: UmbModalRouteRegistrationController;
@state()
private _modalRoute?: string;
private _createRoute?: string;
constructor() {
super();
@@ -51,6 +54,12 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
this._items = items;
});
this.#editDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.onSetup(() => {
return { entityType: 'data-type', preset: {} };
})
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_MODAL)
.onSetup(() => {
return {
@@ -64,8 +73,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
this.dispatchEvent(new CustomEvent('change', { composed: true, bubbles: true }));
})
.observeRouteBuilder((routeBuilder) => {
this._modalRoute = routeBuilder(null);
this.requestUpdate('_modalRoute');
this._createRoute = routeBuilder(null);
});
}
@@ -77,13 +85,14 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
property-editor-ui-alias=${this._items[0].propertyEditorAlias}
property-editor-model-alias=${this._items[0].propertyEditorUiAlias}
@open=${() => {
console.warn('TO BE DONE..');
// TODO: Could use something smarter for workspace modals, as I would like to avoid setting the rest of the URL here:
this.#editDataTypeModal?.open({}, 'edit/' + this._items![0].id)
}}
border>
<!-- TODO: Get the icon from property editor UI -->
<uui-icon name="${'document'}" slot="icon"></uui-icon>
<uui-action-bar slot="actions">
<uui-button label="Change" .href=${this._modalRoute}></uui-button>
<uui-button label="Change" .href=${this._createRoute}></uui-button>
</uui-action-bar>
</umb-ref-data-type>
`
@@ -93,7 +102,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
label="Select Property Editor"
look="placeholder"
color="default"
.href=${this._modalRoute}></uui-button>
.href=${this._createRoute}></uui-button>
`;
}

View File

@@ -57,8 +57,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
#propertyEditorUIs: Array<ManifestPropertyEditorUi> = [];
#currentFilterQuery = '';
//UMB_DATA_TYPE_PICKER_FLOW_UI_PICKER_MODAL;
constructor() {
super();
this.#repository = new UmbDataTypeRepository(this);
@@ -73,6 +71,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
.onSubmit((submitData) => {
if (submitData.dataTypeId) {
this._select(submitData.dataTypeId);
this._submit();
} else if (submitData.createNewWithPropertyEditorUiAlias) {
this._createDataType(submitData.createNewWithPropertyEditorUiAlias);
}
@@ -88,7 +87,9 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
return { entityType: 'data-type', preset: { propertyEditorUiAlias: params.uiAlias } };
})
.onSubmit((submitData) => {
console.log('submitData', submitData);
this._select(submitData.id);
this._submit();
});
this.#init();
@@ -120,6 +121,7 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
private _handleDataTypeClick(dataType: EntityTreeItemResponseModel) {
if (dataType.id) {
this._select(dataType.id);
this._submit();
}
}
@@ -170,7 +172,6 @@ export class UmbDataTypePickerFlowModalElement extends UmbLitElement {
<uui-box> ${this._renderFilter()} ${this._renderGrid()} </uui-box>
<div slot="actions">
<uui-button label="Close" @click=${this._close}></uui-button>
<uui-button label="${this._submitLabel}" look="primary" color="positive" @click=${this._submit}></uui-button>
</div>
</umb-body-layout>
`;

View File

@@ -227,7 +227,9 @@ export class UmbDataTypeRepository
// Consider notify a workspace if a template is updated in the store while someone is editing it.
// TODO: would be nice to align the stores on methods/methodNames.
// this.#detailStore?.append(dataType);
// TODO: This is parsing on the full models to the tree and item store. Those should only contain the data they need. I don't know, at this point, if thats a repository or store responsibility.
this.#treeStore?.updateItem(id, updatedDataType);
this.#itemStore?.updateItem(id, updatedDataType);
const notification = { data: { message: `Data Type saved` } };
this.#notificationContext?.peek('positive', notification);
@@ -249,6 +251,7 @@ export class UmbDataTypeRepository
// TODO: would be nice to align the stores on methods/methodNames.
this.#detailStore?.remove([id]);
this.#treeStore?.removeItem(id);
this.#itemStore?.removeItem(id);
const notification = { data: { message: `Data Type deleted` } };
this.#notificationContext?.peek('positive', notification);

View File

@@ -27,7 +27,7 @@ export class UmbDataTypeWorkspaceContext
}
}
async createScaffold(parentId: string | null) {
async create(parentId: string | null) {
let { data } = await this.repository.createScaffold(parentId);
if (this.modalContext) {
data = { ...data, ...this.modalContext.data.preset };
@@ -78,7 +78,7 @@ export class UmbDataTypeWorkspaceContext
if (!this.#data.value) return;
if (!this.#data.value.id) return;
if (this.isNew) {
if (this.getIsNew()) {
await this.repository.create(this.#data.value);
} else {
await this.repository.save(this.#data.value.id, this.#data.value);

View File

@@ -18,7 +18,7 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
component: () => this.#element,
setup: (_component, info) => {
const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId;
this.#workspaceContext.createScaffold(parentId);
this.#workspaceContext.create(parentId);
},
},
{

View File

@@ -1,6 +1,5 @@
import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
import type {
ManifestModal,
ManifestWorkspace,
ManifestWorkspaceAction,
ManifestWorkspaceEditorView,

View File

@@ -27,7 +27,7 @@ export class UmbLanguageWorkspaceContext
}
}
async createScaffold() {
async create() {
const { data } = await this.repository.createScaffold();
if (!data) return;
this.setIsNew(true);

View File

@@ -40,7 +40,7 @@ export class UmbLanguageWorkspaceElement extends UmbLitElement {
path: 'create',
component: this.#getComponentElement,
setup: async () => {
this.#languageWorkspaceContext.createScaffold();
this.#languageWorkspaceContext.create();
// Navigate to edit route when language is created:
this.observe(

View File

@@ -42,7 +42,7 @@ export class UmbTagRepository {
if (data) {
// TODO: allow to append an array of items to the store
// TODO: append culture? "Invariant" if null.
// TODO: Lone: append culture? "Invariant" if null. Niels: Actually, as of my current stand point, I think we should aim for invariant to be the value of ´null´.
data.items.forEach((x) => this.#tagStore?.append(x));
}

View File

@@ -1,10 +1,8 @@
import { UmbPartialViewDetailServerDataSource } from './sources/partial-views.detail.server.data.js';
import { UmbPartialViewsTreeServerDataSource } from './sources/partial-views.tree.server.data.js';
import { UmbPartialViewsStore, UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN } from './partial-views.store.js';
import { UmbPartialViewsTreeStore, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN } from './partial-views.tree.store.js';
import { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
import { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository';
@@ -18,9 +16,6 @@ export class UmbTemplateRepository implements UmbTreeRepository<any>, UmbDetailR
#detailDataSource: UmbPartialViewDetailServerDataSource;
#treeStore?: UmbPartialViewsTreeStore;
#store?: UmbPartialViewsStore;
#notificationContext?: UmbNotificationContext;
constructor(host: UmbControllerHostElement) {
this.#host = host;
@@ -32,19 +27,12 @@ export class UmbTemplateRepository implements UmbTreeRepository<any>, UmbDetailR
new UmbContextConsumerController(this.#host, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN, (instance) => {
this.#treeStore = instance;
}),
new UmbContextConsumerController(this.#host, UMB_PARTIAL_VIEWS_STORE_CONTEXT_TOKEN, (instance) => {
this.#store = instance;
}),
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
this.#notificationContext = instance;
}),
]);
}
requestTreeRoot(): Promise<{ data?: UmbTreeRootEntityModel | undefined; error?: ProblemDetailsModel | undefined }> {
throw new Error('Method not implemented.');
//throw new Error('Method not implemented.');
return {data: undefined, error: undefined} as any;
}
requestItemsLegacy?:
@@ -61,6 +49,9 @@ export class UmbTemplateRepository implements UmbTreeRepository<any>, UmbDetailR
throw new Error('Method not implemented.');
}
// TODO: This method to be done, or able to go away?
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
requestById(id: string): Promise<{ data?: any; error?: ProblemDetailsModel | undefined }> {
throw new Error('Method not implemented.');
}

View File

@@ -46,7 +46,7 @@ export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext<UmbTemp
}
}
async createScaffold(parentKey: string | null) {
async create(parentKey: string | null) {
const { data } = await this.repository.createScaffold(parentKey);
if (!data) return;
this.setIsNew(true);

View File

@@ -22,7 +22,7 @@ export class UmbPartialViewsWorkspaceElement extends UmbLitElement {
component: () => this.#element,
setup: async (component: PageComponent, info: IRoutingInfo) => {
const parentKey = info.match.params.parentKey;
this.#partialViewsWorkspaceContext.createScaffold(parentKey);
this.#partialViewsWorkspaceContext.create(parentKey);
},
},
{

View File

@@ -128,7 +128,7 @@ export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext<UmbTemplate
}
}
async createScaffold(parentId: string | null = null) {
async create(parentId: string | null = null) {
const { data } = await this.repository.createScaffold();
if (!data) return;
this.setIsNew(true);

View File

@@ -24,7 +24,7 @@ export class UmbTemplateWorkspaceElement extends UmbLitElement {
component: () => this.#element,
setup: (component: PageComponent, info: IRoutingInfo) => {
const parentKey = info.match.params.parentKey;
this.#templateWorkspaceContext.createScaffold(parentKey);
this.#templateWorkspaceContext.create(parentKey);
},
},
{

View File

@@ -48,7 +48,7 @@ export default class UmbCreateDictionaryEntityAction extends UmbEntityActionBase
const { name } = await modalContext.onSubmit();
if (!name) return;
const { data } = await this.repository.createScaffold(this.unique, name);
const { data } = await this.repository.createScaffold(this.unique, {name});
// TODO => get location header to route to new item
console.log(data);

View File

@@ -127,10 +127,11 @@ export class UmbDictionaryRepository
// DETAILS
async createScaffold(parentId: string | null, name?: string) {
// TODO: consider if we want to create a specific createScaffoldWithName, to loose the coupling to the model.
async createScaffold(parentId: string | null, preset?: Partial<CreateDictionaryItemRequestModel>) {
if (parentId === undefined) throw new Error('Parent id is missing');
await this.#init;
return this.#detailSource.createScaffold(parentId, name);
return this.#detailSource.createScaffold(parentId, preset);
}
async requestById(id: string) {

View File

@@ -33,12 +33,13 @@ export class UmbDictionaryDetailServerDataSource
* @return {*}
* @memberof UmbDictionaryDetailServerDataSource
*/
async createScaffold(parentId?: string | null, name?: string) {
async createScaffold(parentId?: string | null, preset?: Partial<CreateDictionaryItemRequestModel>) {
const data = {
id: UmbId.new(),
parentId,
name,
name: '',
translations: [],
...preset
};
return { data };

View File

@@ -62,7 +62,7 @@ export class UmbDictionaryWorkspaceContext
}
}
async createScaffold(parentId: string | null) {
async create(parentId: string | null) {
const { data } = await this.repository.createScaffold(parentId);
if (!data) return;
this.setIsNew(true);

View File

@@ -18,8 +18,8 @@ import {
UmbDetailRepository,
UmbItemDataSource,
UmbItemRepository,
UmbRepositoryErrorResponse,
UmbRepositoryResponse,
UmbDataSourceErrorResponse,
DataSourceResponse,
} from '@umbraco-cms/backoffice/repository';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
import { UMB_NOTIFICATION_CONTEXT_TOKEN, UmbNotificationContext } from '@umbraco-cms/backoffice/notification';
@@ -69,7 +69,7 @@ export class UmbUserGroupRepository
}).asPromise(),
]);
}
createScaffold(parentId: string | null): Promise<UmbRepositoryResponse<UserGroupBaseModel>> {
createScaffold(parentId: string | null): Promise<DataSourceResponse<UserGroupBaseModel>> {
return this.#detailSource.createScaffold(parentId);
}
@@ -113,7 +113,7 @@ export class UmbUserGroupRepository
throw new Error('Method not implemented.');
}
async create(userGroupRequestData: any): Promise<UmbRepositoryResponse<any>> {
async create(userGroupRequestData: any): Promise<DataSourceResponse<any>> {
if (!userGroupRequestData) throw new Error('Data is missing');
const { data, error } = await this.#detailSource.insert(userGroupRequestData);
@@ -144,7 +144,7 @@ export class UmbUserGroupRepository
return { data, error };
}
async delete(id: string): Promise<UmbRepositoryErrorResponse> {
async delete(id: string): Promise<UmbDataSourceErrorResponse> {
if (!id) throw new Error('UserGroup id is missing');
const { error } = await this.#detailSource.delete(id);

View File

@@ -23,7 +23,7 @@ export class UmbUserGroupWorkspaceContext
this.#userRepository = new UmbUserRepository(host);
}
async createScaffold() {
async create() {
const { data } = await this.repository.createScaffold(null);
this.setIsNew(true);
// TODO: Should the data be the base model or the presentation model?

View File

@@ -16,7 +16,7 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement {
path: 'create',
component: () => this.#element,
setup: (component, info) => {
this.#workspaceContext.createScaffold();
this.#workspaceContext.create();
},
},
{

View File

@@ -1,5 +1,5 @@
import { UmbRepositoryResponse } from './detail-repository.interface.js';
import type { DataSourceResponse } from "./data-source/index.js";
export interface UmbCopyRepository {
copy(unique: string, targetUnique: string): Promise<UmbRepositoryResponse<string>>;
copy(unique: string, targetUnique: string): Promise<DataSourceResponse<string>>;
}

View File

@@ -1,12 +1,6 @@
import type { DataSourceResponse, UmbDataSourceErrorResponse } from './data-source/index.js';
import { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api';
export interface UmbRepositoryErrorResponse {
error?: ProblemDetailsModel;
}
export interface UmbRepositoryResponse<T> extends UmbRepositoryErrorResponse {
data?: T;
}
export interface UmbDetailRepository<
CreateRequestType = any,
@@ -14,10 +8,10 @@ export interface UmbDetailRepository<
UpdateRequestType = any,
ResponseType = any
> {
createScaffold(parentId: string | null): Promise<UmbRepositoryResponse<CreateRequestType>>;
requestById(id: string): Promise<UmbRepositoryResponse<ResponseType | undefined>>;
createScaffold(parentId: string | null, preset?: Partial<CreateRequestType>): Promise<DataSourceResponse<CreateRequestType>>;
requestById(id: string): Promise<DataSourceResponse<ResponseType | undefined>>;
byId(id: string): Promise<Observable<ResponseType | undefined>>;
create(data: CreateRequestType): Promise<UmbRepositoryResponse<CreateResponseType>>;
save(id: string, data: UpdateRequestType): Promise<UmbRepositoryErrorResponse>;
delete(id: string): Promise<UmbRepositoryErrorResponse>;
create(data: CreateRequestType): Promise<DataSourceResponse<CreateResponseType>>;
save(id: string, data: UpdateRequestType): Promise<UmbDataSourceErrorResponse>;
delete(id: string): Promise<UmbDataSourceErrorResponse>;
}

View File

@@ -1,5 +1,5 @@
import { UmbRepositoryErrorResponse } from './detail-repository.interface.js';
import { UmbDataSourceErrorResponse } from "./data-source/index.js";
export interface UmbMoveRepository {
move(unique: string, targetUnique: string): Promise<UmbRepositoryErrorResponse>;
move(unique: string, targetUnique: string): Promise<UmbDataSourceErrorResponse>;
}

Some files were not shown because too many files have changed in this diff Show More