Merge branch 'main' into bugfix/persisting-user-group-start-nodes
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { tryExecute } from '@umbraco-cms/backoffice/resources';
|
||||
import { ServerService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { UmbBasicState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import {
|
||||
type UmbExtensionManifestInitializer,
|
||||
UmbExtensionsManifestInitializer,
|
||||
} from '@umbraco-cms/backoffice/extension-api';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
import { UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { type ManifestSection, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbExtensionManifestInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
|
||||
export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
|
||||
#activeSectionAlias = new UmbStringState(undefined);
|
||||
@@ -16,11 +17,32 @@ export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
|
||||
#allowedSections = new UmbBasicState<Array<UmbExtensionManifestInitializer<ManifestSection>>>([]);
|
||||
public readonly allowedSections = this.#allowedSections.asObservable();
|
||||
|
||||
#verison = new UmbStringState(undefined);
|
||||
public readonly version = this.#verison.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_BACKOFFICE_CONTEXT);
|
||||
new UmbExtensionsManifestInitializer(this, umbExtensionsRegistry, 'section', null, (sections) => {
|
||||
this.#allowedSections.setValue([...sections]);
|
||||
});
|
||||
|
||||
this.#getVersion();
|
||||
}
|
||||
|
||||
async #getVersion() {
|
||||
const { data } = await tryExecute(ServerService.getServerInformation());
|
||||
if (!data) return;
|
||||
|
||||
// A quick semver parser (to remove the unwanted bits) [LK]
|
||||
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [semVer, major, minor, patch, prerelease, buildmetadata] =
|
||||
data.version.match(
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/,
|
||||
) ?? [];
|
||||
|
||||
const version = [major, minor, patch].join('.') + (prerelease ? `-${prerelease}` : '');
|
||||
this.#verison.setValue(version);
|
||||
}
|
||||
|
||||
public setActiveSectionAlias(alias: string) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { UMB_BACKOFFICE_CONTEXT } from '../backoffice.context.js';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
|
||||
@customElement('umb-backoffice-header-logo')
|
||||
export class UmbBackofficeHeaderLogoElement extends UmbLitElement {
|
||||
@state()
|
||||
private _version?: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_BACKOFFICE_CONTEXT, (context) => {
|
||||
this.observe(
|
||||
context.version,
|
||||
(version) => {
|
||||
if (!version) return;
|
||||
this._version = version;
|
||||
},
|
||||
'_observeVersion',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-button id="logo" look="primary" label="Umbraco" compact popovertarget="logo-popover">
|
||||
<img src="/umbraco/backoffice/assets/umbraco_logomark_white.svg" alt="Umbraco" />
|
||||
</uui-button>
|
||||
<uui-popover-container id="logo-popover" placement="bottom-start">
|
||||
<umb-popover-layout>
|
||||
<div id="modal">
|
||||
<img src="/umbraco/backoffice/assets/umbraco_logo_blue.svg" alt="Umbraco" loading="lazy" />
|
||||
<span>${this._version}</span>
|
||||
<a href="https://umbraco.com" target="_blank" rel="noopener">Umbraco.com</a>
|
||||
</div>
|
||||
</umb-popover-layout>
|
||||
</uui-popover-container>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UmbTextStyles,
|
||||
css`
|
||||
#logo {
|
||||
--uui-button-padding-top-factor: 1;
|
||||
--uui-button-padding-bottom-factor: 0.5;
|
||||
margin-right: var(--uui-size-space-2);
|
||||
--uui-button-background-color: transparent;
|
||||
}
|
||||
|
||||
#logo > img {
|
||||
height: var(--uui-size-10);
|
||||
width: var(--uui-size-10);
|
||||
}
|
||||
|
||||
#modal {
|
||||
padding: var(--uui-size-space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-backoffice-header-logo': UmbBackofficeHeaderLogoElement;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@@ -7,17 +6,14 @@ export class UmbBackofficeHeaderElement extends UmbLitElement {
|
||||
render() {
|
||||
return html`
|
||||
<div id="appHeader">
|
||||
<uui-button id="logo" look="primary" label="Umbraco" compact>
|
||||
<img src="/umbraco/backoffice/assets/umbraco_logomark_white.svg" alt="Umbraco" />
|
||||
</uui-button>
|
||||
|
||||
<umb-backoffice-header-logo></umb-backoffice-header-logo>
|
||||
<umb-backoffice-header-sections id="sections"></umb-backoffice-header-sections>
|
||||
<umb-backoffice-header-apps></umb-backoffice-header-apps>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles: CSSResultGroup = [
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
width: 100%;
|
||||
@@ -31,18 +27,6 @@ export class UmbBackofficeHeaderElement extends UmbLitElement {
|
||||
padding: 0 var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
#logo {
|
||||
--uui-button-padding-top-factor: 1;
|
||||
--uui-button-padding-bottom-factor: 0.5;
|
||||
margin-right: var(--uui-size-space-2);
|
||||
--uui-button-background-color: transparent;
|
||||
}
|
||||
|
||||
#logo img {
|
||||
height: var(--uui-size-10);
|
||||
width: var(--uui-size-10);
|
||||
}
|
||||
|
||||
#sections {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './backoffice-header-apps.element.js';
|
||||
export * from './backoffice-header-logo.element.js';
|
||||
export * from './backoffice-header-sections.element.js';
|
||||
export * from './backoffice-header.element.js';
|
||||
export * from './backoffice-main.element.js';
|
||||
|
||||
@@ -543,7 +543,6 @@ export type DatatypeConfigurationResponseModel = {
|
||||
canBeChanged: DataTypeChangeModeModel
|
||||
documentListViewId: string
|
||||
mediaListViewId: string
|
||||
memberListViewId: string
|
||||
};
|
||||
|
||||
export type DefaultReferenceResponseModel = {
|
||||
@@ -689,6 +688,11 @@ documentType: DocumentTypeReferenceResponseModel
|
||||
variants: Array<DocumentVariantItemResponseModel>
|
||||
};
|
||||
|
||||
export type DocumentTypeBlueprintItemResponseModel = {
|
||||
id: string
|
||||
name: string
|
||||
};
|
||||
|
||||
export type DocumentTypeCleanupModel = {
|
||||
preventCleanup: boolean
|
||||
keepAllVersionsNewerThanDays?: number | null
|
||||
@@ -800,6 +804,11 @@ export type DocumentUrlInfoModel = {
|
||||
url: string
|
||||
};
|
||||
|
||||
export type DocumentUrlInfoResponseModel = {
|
||||
id: string
|
||||
urlInfos: Array<DocumentUrlInfoModel>
|
||||
};
|
||||
|
||||
export type DocumentValueModel = {
|
||||
culture?: string | null
|
||||
segment?: string | null
|
||||
@@ -1252,6 +1261,11 @@ export type MediaUrlInfoModel = {
|
||||
url: string
|
||||
};
|
||||
|
||||
export type MediaUrlInfoResponseModel = {
|
||||
id: string
|
||||
urlInfos: Array<MediaUrlInfoModel>
|
||||
};
|
||||
|
||||
export type MediaValueModel = {
|
||||
culture?: string | null
|
||||
segment?: string | null
|
||||
@@ -1579,6 +1593,11 @@ export type PagedDocumentTreeItemResponseModel = {
|
||||
items: Array<DocumentTreeItemResponseModel>
|
||||
};
|
||||
|
||||
export type PagedDocumentTypeBlueprintItemResponseModel = {
|
||||
total: number
|
||||
items: Array<DocumentTypeBlueprintItemResponseModel>
|
||||
};
|
||||
|
||||
export type PagedDocumentTypeTreeItemResponseModel = {
|
||||
total: number
|
||||
items: Array<DocumentTypeTreeItemResponseModel>
|
||||
@@ -2971,6 +2990,10 @@ PostDocumentBlueprintFromDocument: {
|
||||
GetItemDocumentBlueprint: {
|
||||
id?: Array<string>
|
||||
|
||||
};
|
||||
GetTreeDocumentBlueprintAncestors: {
|
||||
descendantId?: string
|
||||
|
||||
};
|
||||
GetTreeDocumentBlueprintChildren: {
|
||||
foldersOnly?: boolean
|
||||
@@ -3000,6 +3023,7 @@ take?: number
|
||||
,PutDocumentBlueprintFolderById: string
|
||||
,PostDocumentBlueprintFromDocument: string
|
||||
,GetItemDocumentBlueprint: Array<DocumentBlueprintItemResponseModel>
|
||||
,GetTreeDocumentBlueprintAncestors: Array<DocumentBlueprintTreeItemResponseModel>
|
||||
,GetTreeDocumentBlueprintChildren: PagedDocumentBlueprintTreeItemResponseModel
|
||||
,GetTreeDocumentBlueprintRoot: PagedDocumentBlueprintTreeItemResponseModel
|
||||
|
||||
@@ -3030,6 +3054,12 @@ requestBody?: UpdateDocumentTypeRequestModel
|
||||
GetDocumentTypeByIdAllowedChildren: {
|
||||
id: string
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
};
|
||||
GetDocumentTypeByIdBlueprint: {
|
||||
id: string
|
||||
skip?: number
|
||||
take?: number
|
||||
|
||||
};
|
||||
@@ -3109,6 +3139,7 @@ take?: number
|
||||
,DeleteDocumentTypeById: string
|
||||
,PutDocumentTypeById: string
|
||||
,GetDocumentTypeByIdAllowedChildren: PagedAllowedDocumentTypeModel
|
||||
,GetDocumentTypeByIdBlueprint: PagedDocumentTypeBlueprintItemResponseModel
|
||||
,GetDocumentTypeByIdCompositionReferences: Array<DocumentTypeCompositionResponseModel>
|
||||
,PostDocumentTypeByIdCopy: string
|
||||
,PutDocumentTypeByIdMove: string
|
||||
@@ -3288,6 +3319,10 @@ take?: number
|
||||
PutDocumentSort: {
|
||||
requestBody?: SortingRequestModel
|
||||
|
||||
};
|
||||
GetDocumentUrls: {
|
||||
id?: Array<string>
|
||||
|
||||
};
|
||||
PostDocumentValidate: {
|
||||
requestBody?: CreateDocumentRequestModel
|
||||
@@ -3373,6 +3408,7 @@ take?: number
|
||||
,GetDocumentAreReferenced: PagedReferenceByIdModel
|
||||
,GetDocumentConfiguration: DocumentConfigurationResponseModel
|
||||
,PutDocumentSort: string
|
||||
,GetDocumentUrls: Array<DocumentUrlInfoResponseModel>
|
||||
,PostDocumentValidate: string
|
||||
,GetItemDocument: Array<DocumentItemResponseModel>
|
||||
,GetItemDocumentSearch: PagedModelDocumentItemResponseModel
|
||||
@@ -3835,6 +3871,10 @@ take?: number
|
||||
PutMediaSort: {
|
||||
requestBody?: SortingRequestModel
|
||||
|
||||
};
|
||||
GetMediaUrls: {
|
||||
id?: Array<string>
|
||||
|
||||
};
|
||||
PostMediaValidate: {
|
||||
requestBody?: CreateMediaRequestModel
|
||||
@@ -3900,6 +3940,7 @@ take?: number
|
||||
,GetMediaAreReferenced: PagedReferenceByIdModel
|
||||
,GetMediaConfiguration: MediaConfigurationResponseModel
|
||||
,PutMediaSort: string
|
||||
,GetMediaUrls: Array<MediaUrlInfoResponseModel>
|
||||
,PostMediaValidate: string
|
||||
,DeleteRecycleBinMedia: string
|
||||
,DeleteRecycleBinMediaById: string
|
||||
|
||||
@@ -1149,6 +1149,28 @@ requestBody
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getTreeDocumentBlueprintAncestors(data: DocumentBlueprintData['payloads']['GetTreeDocumentBlueprintAncestors'] = {}): CancelablePromise<DocumentBlueprintData['responses']['GetTreeDocumentBlueprintAncestors']> {
|
||||
const {
|
||||
|
||||
descendantId
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/tree/document-blueprint/ancestors',
|
||||
query: {
|
||||
descendantId
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -1329,6 +1351,34 @@ take
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getDocumentTypeByIdBlueprint(data: DocumentTypeData['payloads']['GetDocumentTypeByIdBlueprint']): CancelablePromise<DocumentTypeData['responses']['GetDocumentTypeByIdBlueprint']> {
|
||||
const {
|
||||
|
||||
id,
|
||||
skip,
|
||||
take
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/document-type/{id}/blueprint',
|
||||
path: {
|
||||
id
|
||||
},
|
||||
query: {
|
||||
skip, take
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
404: `Not Found`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
@@ -2448,6 +2498,28 @@ take
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getDocumentUrls(data: DocumentData['payloads']['GetDocumentUrls'] = {}): CancelablePromise<DocumentData['responses']['GetDocumentUrls']> {
|
||||
const {
|
||||
|
||||
id
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/document/urls',
|
||||
query: {
|
||||
id
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns string Success
|
||||
* @throws ApiError
|
||||
@@ -4298,6 +4370,28 @@ take
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns unknown Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getMediaUrls(data: MediaData['payloads']['GetMediaUrls'] = {}): CancelablePromise<MediaData['responses']['GetMediaUrls']> {
|
||||
const {
|
||||
|
||||
id
|
||||
} = data;
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/media/urls',
|
||||
query: {
|
||||
id
|
||||
},
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns string Success
|
||||
* @throws ApiError
|
||||
@@ -6587,6 +6681,7 @@ export class SecurityService {
|
||||
url: '/umbraco/management/api/v1/security/configuration',
|
||||
errors: {
|
||||
401: `The resource is protected and requires an authentication token`,
|
||||
403: `The authenticated user do not have access to this resource`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { html, customElement, property, css, state, nothing } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
UMB_DATATYPE_WORKSPACE_MODAL,
|
||||
UMB_DATA_TYPE_ENTITY_TYPE,
|
||||
UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
UMB_DATA_TYPE_PICKER_FLOW_DATA_TYPE_PICKER_MODAL,
|
||||
} from '@umbraco-cms/backoffice/data-type';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDataTypeItemModel } from '@umbraco-cms/backoffice/data-type';
|
||||
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
@customElement('umb-input-collection-configuration')
|
||||
@@ -18,38 +15,24 @@ export class UmbInputCollectionConfigurationElement extends UmbFormControlMixin(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
#itemManager = new UmbRepositoryItemsManager<UmbDataTypeItemModel>(
|
||||
this,
|
||||
UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS,
|
||||
(x) => x.unique,
|
||||
);
|
||||
|
||||
#createDataTypeModal: UmbModalRouteRegistrationController;
|
||||
#dataTypeModal: UmbModalRouteRegistrationController;
|
||||
|
||||
#propertyEditorUiAlias = 'Umb.PropertyEditorUi.CollectionView';
|
||||
|
||||
@state()
|
||||
private _dataTypePickerModalPath?: string;
|
||||
|
||||
@state()
|
||||
private _item?: UmbDataTypeItemModel;
|
||||
|
||||
@property({ attribute: 'default-value' })
|
||||
defaultValue?: string;
|
||||
|
||||
#setValue(value: string) {
|
||||
this.value = value;
|
||||
this.#itemManager.setUniques(value ? [value] : []);
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.observe(this.#itemManager.items, (items) => {
|
||||
this._item = items[0];
|
||||
});
|
||||
|
||||
new UmbModalRouteRegistrationController(this, UMB_DATA_TYPE_PICKER_FLOW_DATA_TYPE_PICKER_MODAL)
|
||||
.addAdditionalPath(':uiAlias')
|
||||
.onSetup((routingInfo) => {
|
||||
@@ -71,7 +54,7 @@ export class UmbInputCollectionConfigurationElement extends UmbFormControlMixin(
|
||||
this._dataTypePickerModalPath = routeBuilder({ uiAlias: this.#propertyEditorUiAlias });
|
||||
});
|
||||
|
||||
this.#createDataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL)
|
||||
this.#dataTypeModal = new UmbModalRouteRegistrationController(this, UMB_DATATYPE_WORKSPACE_MODAL)
|
||||
.addAdditionalPath(':uiAlias')
|
||||
.onSetup((params) => {
|
||||
return { data: { entityType: UMB_DATA_TYPE_ENTITY_TYPE, preset: { editorUiAlias: params.uiAlias } } };
|
||||
@@ -81,51 +64,49 @@ export class UmbInputCollectionConfigurationElement extends UmbFormControlMixin(
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
if (this.value) {
|
||||
this.#itemManager.setUniques([this.value as string]);
|
||||
}
|
||||
}
|
||||
|
||||
#clearDataType() {
|
||||
this.#setValue('');
|
||||
}
|
||||
|
||||
#createDataType() {
|
||||
this.#createDataTypeModal.open(
|
||||
this.#dataTypeModal.open(
|
||||
{ uiAlias: this.#propertyEditorUiAlias },
|
||||
`create/parent/${UMB_DATA_TYPE_ENTITY_TYPE}/null`,
|
||||
);
|
||||
}
|
||||
|
||||
#editDataType() {
|
||||
this.#dataTypeModal?.open({}, `edit/${this.value}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
return !this.value ? this.#renderCreate() : this.#renderConfigured();
|
||||
}
|
||||
|
||||
#renderCreate() {
|
||||
if (!this._dataTypePickerModalPath) return nothing;
|
||||
return html`<uui-button
|
||||
id="create-button"
|
||||
color="default"
|
||||
look="placeholder"
|
||||
label="Configure as a collection"
|
||||
href=${this._dataTypePickerModalPath}></uui-button>`;
|
||||
return html`
|
||||
<uui-button
|
||||
id="create-button"
|
||||
color="default"
|
||||
look="placeholder"
|
||||
label="Configure as a collection"
|
||||
href=${this._dataTypePickerModalPath}></uui-button>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderConfigured() {
|
||||
if (!this._item || !this._dataTypePickerModalPath) return nothing;
|
||||
if (!this.value || !this._dataTypePickerModalPath) return nothing;
|
||||
return html`
|
||||
<uui-ref-list>
|
||||
<uui-ref-node-data-type standalone name=${this._item.name} detail=${this._item.unique}>
|
||||
<umb-ref-data-type standalone data-type-id=${this.value as string} @open=${this.#editDataType}>
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button
|
||||
label=${this.localize.term('general_choose')}
|
||||
href=${this._dataTypePickerModalPath}></uui-button>
|
||||
<uui-button @click=${this.#clearDataType} label=${this.localize.term('general_remove')}></uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node-data-type>
|
||||
</umb-ref-data-type>
|
||||
</uui-ref-list>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@ export class UmbPropertyTypeSettingsModalElement extends UmbModalBaseElement<
|
||||
|
||||
this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (instance) => {
|
||||
if (!this.data?.contentTypeId) return;
|
||||
if (instance.getUnique() !== this.data.contentTypeId) {
|
||||
// We can currently only edit properties that are part of a content type workspace, which has to be present outside of the modal. [NL]
|
||||
throw new Error(
|
||||
'The content type workspace context does not match the content type id of the property type settings modal.',
|
||||
);
|
||||
}
|
||||
|
||||
this.observe(instance.variesByCulture, (variesByCulture) => (this._contentTypeVariesByCulture = variesByCulture));
|
||||
this.observe(instance.variesBySegment, (variesBySegment) => (this._contentTypeVariesBySegment = variesBySegment));
|
||||
|
||||
@@ -178,6 +178,13 @@ export class UmbContentTypePropertyStructureHelper<T extends UmbContentTypeModel
|
||||
return this.#structure.ownerContentTypePart((x) => x?.properties.some((y) => y.id === propertyId));
|
||||
}
|
||||
|
||||
async contentTypeOfProperty(propertyId: UmbPropertyTypeId) {
|
||||
await this.#init;
|
||||
if (!this.#structure) return;
|
||||
|
||||
return this.#structure.contentTypeOfProperty(propertyId);
|
||||
}
|
||||
|
||||
// TODO: consider moving this to another class, to separate 'viewer' from 'manipulator':
|
||||
/** Manipulate methods: */
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
import { incrementString } from '@umbraco-cms/backoffice/utils';
|
||||
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
|
||||
|
||||
type UmbPropertyTypeId = UmbPropertyTypeModel['id'];
|
||||
|
||||
/**
|
||||
* Manages a structure of a Content Type and its properties and containers.
|
||||
* This loads and merges the structures of the Content Type and its inherited and composed Content Types.
|
||||
@@ -192,6 +194,10 @@ export class UmbContentTypeStructureManager<
|
||||
return this.#contentTypes.getValue().find((y) => y.unique === this.#ownerContentTypeUnique);
|
||||
}
|
||||
|
||||
getOwnerContentTypeUnique() {
|
||||
return this.#ownerContentTypeUnique;
|
||||
}
|
||||
|
||||
updateOwnerContentType(entry: Partial<T>) {
|
||||
this.#contentTypes.updateOne(this.#ownerContentTypeUnique, entry);
|
||||
}
|
||||
@@ -670,6 +676,12 @@ export class UmbContentTypeStructureManager<
|
||||
});
|
||||
}
|
||||
|
||||
contentTypeOfProperty(propertyId: UmbPropertyTypeId) {
|
||||
return this.#contentTypes.asObservablePart((contentTypes) =>
|
||||
contentTypes.find((contentType) => contentType.properties.some((p) => p.id === propertyId)),
|
||||
);
|
||||
}
|
||||
|
||||
private _reset() {
|
||||
this.#contentTypeObservers.forEach((observer) => observer.destroy());
|
||||
this.#contentTypeObservers = [];
|
||||
|
||||
@@ -208,10 +208,7 @@ export class UmbContentTypeDesignEditorPropertiesElement extends UmbLitElement {
|
||||
return html`
|
||||
<umb-content-type-design-editor-property
|
||||
data-umb-property-id=${property.id}
|
||||
owner-content-type-id=${ifDefined(this._ownerContentType!.unique)}
|
||||
owner-content-type-name=${ifDefined(this._ownerContentType!.name)}
|
||||
.editContentTypePath=${this._editContentTypePath}
|
||||
?inherited=${property.container?.id !== this.containerId}
|
||||
?sort-mode-active=${this._sortModeActive}
|
||||
.propertyStructureHelper=${this.#propertyStructureHelper}
|
||||
.property=${property}>
|
||||
|
||||
@@ -58,28 +58,21 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
}
|
||||
private _property?: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined;
|
||||
|
||||
/**
|
||||
* Inherited, Determines if the property is part of the main content type thats being edited.
|
||||
* If true, then the property is inherited from another content type, not a part of the main content type.
|
||||
* @type {boolean}
|
||||
* @attr
|
||||
* @default undefined
|
||||
*/
|
||||
@property({ type: Boolean })
|
||||
public inherited?: boolean;
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: 'sort-mode-active' })
|
||||
public sortModeActive = false;
|
||||
|
||||
@property({ type: String, attribute: 'owner-content-type-id' })
|
||||
public ownerContentTypeId?: string;
|
||||
|
||||
@property({ type: String, attribute: 'owner-content-type-name' })
|
||||
public ownerContentTypeName?: string;
|
||||
|
||||
@property({ type: String, attribute: 'edit-content-type-path' })
|
||||
public editContentTypePath?: string;
|
||||
|
||||
@state()
|
||||
public _inherited?: boolean;
|
||||
|
||||
@state()
|
||||
public _inheritedContentTypeId?: string;
|
||||
|
||||
@state()
|
||||
public _inheritedContentTypeName?: string;
|
||||
|
||||
@state()
|
||||
protected _modalRoute?: string;
|
||||
|
||||
@@ -96,7 +89,7 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
this.#settingsModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_TYPE_SETTINGS_MODAL)
|
||||
.addUniquePaths(['propertyId'])
|
||||
.onSetup(() => {
|
||||
const id = this.ownerContentTypeId;
|
||||
const id = this._inheritedContentTypeId;
|
||||
if (id === undefined) return false;
|
||||
const propertyData = this.property;
|
||||
if (propertyData === undefined) return false;
|
||||
@@ -114,9 +107,12 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
if (this._propertyStructureHelper && this._property) {
|
||||
// We can first match with something if we have a name [NL]
|
||||
this.observe(
|
||||
await this._propertyStructureHelper!.isOwnerProperty(this._property.id),
|
||||
(isOwned) => {
|
||||
this.inherited = !isOwned;
|
||||
await this._propertyStructureHelper!.contentTypeOfProperty(this._property.id),
|
||||
(contentType) => {
|
||||
this._inherited =
|
||||
this._propertyStructureHelper?.getStructureManager()?.getOwnerContentTypeUnique() !== contentType?.unique;
|
||||
this._inheritedContentTypeId = contentType?.unique;
|
||||
this._inheritedContentTypeName = contentType?.name;
|
||||
},
|
||||
'observeIsOwnerProperty',
|
||||
);
|
||||
@@ -196,7 +192,7 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
|
||||
render() {
|
||||
// TODO: Only show alias on label if user has access to DocumentType within settings: [NL]
|
||||
return this.inherited ? this.renderInheritedProperty() : this.renderEditableProperty();
|
||||
return this._inherited ? this.renderInheritedProperty() : this.renderEditableProperty();
|
||||
}
|
||||
|
||||
renderInheritedProperty() {
|
||||
@@ -217,8 +213,8 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
<uui-icon name="icon-merge"></uui-icon>
|
||||
<span
|
||||
>${this.localize.term('contentTypeEditor_inheritedFrom')}
|
||||
<a href=${this.editContentTypePath + 'edit/' + this.ownerContentTypeId}>
|
||||
${this.ownerContentTypeName ?? '??'}
|
||||
<a href=${this.editContentTypePath + 'edit/' + this._inheritedContentTypeId}>
|
||||
${this._inheritedContentTypeName ?? '??'}
|
||||
</a>
|
||||
</span>
|
||||
</uui-tag>
|
||||
@@ -275,12 +271,12 @@ export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement {
|
||||
if (!this.property) return;
|
||||
return html`
|
||||
<div class="sortable">
|
||||
<uui-icon name="${this.inherited ? 'icon-merge' : 'icon-navigation'}"></uui-icon>
|
||||
<uui-icon name="${this._inherited ? 'icon-merge' : 'icon-navigation'}"></uui-icon>
|
||||
${this.property.name} <span style="color: var(--uui-color-disabled-contrast)">(${this.property.alias})</span>
|
||||
</div>
|
||||
<uui-input
|
||||
type="number"
|
||||
?readonly=${this.inherited}
|
||||
?readonly=${this._inherited}
|
||||
label="sort order"
|
||||
@change=${(e: UUIInputEvent) =>
|
||||
this.#partialUpdate({ sortOrder: parseInt(e.target.value as string) ?? 0 } as UmbPropertyTypeModel)}
|
||||
|
||||
@@ -41,7 +41,8 @@ export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement impleme
|
||||
#ensureHashPrefix(swatch: UmbSwatchDetails): UmbSwatchDetails {
|
||||
return {
|
||||
label: swatch.label,
|
||||
value: swatch.value.startsWith('#') ? swatch.value : `#${swatch.value}`,
|
||||
// hex color regex adapted from: https://stackoverflow.com/a/9682781/12787
|
||||
value: swatch.value.match(/^(?:[0-9a-f]{3}){1,2}$/i) ? `#${swatch.value}` : swatch.value,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,4 +4,4 @@ export {
|
||||
} from './detail/index.js';
|
||||
export { UmbDocumentBlueprintItemRepository, UMB_DOCUMENT_BLUEPRINT_ITEM_REPOSITORY_ALIAS } from './item/index.js';
|
||||
|
||||
export type { UmbDocumentBlueprintItemModel } from './item/types.js';
|
||||
export type { UmbDocumentBlueprintItemModel, UmbDocumentBlueprintItemBaseModel } from './item/types.js';
|
||||
|
||||
@@ -5,7 +5,13 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbItemRepositoryBase } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export class UmbDocumentBlueprintItemRepository extends UmbItemRepositoryBase<UmbDocumentBlueprintItemModel> {
|
||||
#dataSource = new UmbDocumentBlueprintItemServerDataSource(this);
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UmbDocumentBlueprintItemServerDataSource, UMB_DOCUMENT_BLUEPRINT_ITEM_STORE_CONTEXT);
|
||||
}
|
||||
|
||||
async requestItemsByDocumentType(unique: string) {
|
||||
return this.#dataSource.getItemsByDocumentType(unique);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE } from '../../entity.js';
|
||||
import type { UmbDocumentBlueprintItemModel } from './types.js';
|
||||
import { DocumentBlueprintService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbDocumentBlueprintItemBaseModel, UmbDocumentBlueprintItemModel } from './types.js';
|
||||
import { DocumentBlueprintService, DocumentTypeService } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import { UmbItemServerDataSourceBase } from '@umbraco-cms/backoffice/repository';
|
||||
import type { DocumentBlueprintItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
/**
|
||||
* A data source for Document Blueprint items that fetches data from the server
|
||||
@@ -15,6 +16,7 @@ export class UmbDocumentBlueprintItemServerDataSource extends UmbItemServerDataS
|
||||
DocumentBlueprintItemResponseModel,
|
||||
UmbDocumentBlueprintItemModel
|
||||
> {
|
||||
#host: UmbControllerHost;
|
||||
/**
|
||||
* Creates an instance of UmbDocumentBlueprintItemServerDataSource.
|
||||
* @param {UmbControllerHost} host
|
||||
@@ -25,6 +27,26 @@ export class UmbDocumentBlueprintItemServerDataSource extends UmbItemServerDataS
|
||||
getItems,
|
||||
mapper,
|
||||
});
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
async getItemsByDocumentType(unique: string) {
|
||||
if (!unique) throw new Error('Unique is missing');
|
||||
const { data, error } = await tryExecuteAndNotify(
|
||||
this.#host,
|
||||
DocumentTypeService.getDocumentTypeByIdBlueprint({ id: unique }),
|
||||
);
|
||||
|
||||
if (data) {
|
||||
const items: Array<UmbDocumentBlueprintItemBaseModel> = data.items.map((item) => ({
|
||||
entityType: UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE,
|
||||
unique: item.id,
|
||||
name: item.name,
|
||||
}));
|
||||
return { data: items };
|
||||
}
|
||||
|
||||
return { error };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,7 @@ import type { UmbDocumentBlueprintEntityType } from '../../entity.js';
|
||||
import type { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
|
||||
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
|
||||
|
||||
export interface UmbDocumentBlueprintItemModel {
|
||||
entityType: UmbDocumentBlueprintEntityType;
|
||||
name: string;
|
||||
unique: string;
|
||||
export interface UmbDocumentBlueprintItemModel extends UmbDocumentBlueprintItemBaseModel {
|
||||
documentType: {
|
||||
unique: string;
|
||||
icon: string;
|
||||
@@ -13,6 +10,12 @@ export interface UmbDocumentBlueprintItemModel {
|
||||
};
|
||||
}
|
||||
|
||||
export interface UmbDocumentBlueprintItemBaseModel {
|
||||
entityType: UmbDocumentBlueprintEntityType;
|
||||
name: string;
|
||||
unique: string;
|
||||
}
|
||||
|
||||
export interface UmbDocumentBlueprintItemVariantModel {
|
||||
name: string;
|
||||
culture: string | null;
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
type UmbAllowedDocumentTypeModel,
|
||||
} from '@umbraco-cms/backoffice/document-type';
|
||||
import {
|
||||
type UmbDocumentBlueprintItemModel,
|
||||
UmbDocumentBlueprintItemRepository,
|
||||
type UmbDocumentBlueprintItemBaseModel,
|
||||
} from '@umbraco-cms/backoffice/document-blueprint';
|
||||
|
||||
@customElement('umb-document-create-options-modal')
|
||||
@@ -35,7 +35,7 @@ export class UmbDocumentCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
`${this.localize.term('create_createUnder')} ${this.localize.term('actionCategories_content')}`;
|
||||
|
||||
@state()
|
||||
private _availableBlueprints: Array<UmbDocumentBlueprintItemModel> = [];
|
||||
private _availableBlueprints: Array<UmbDocumentBlueprintItemBaseModel> = [];
|
||||
|
||||
async firstUpdated() {
|
||||
const parentUnique = this.data?.parent.unique;
|
||||
@@ -88,10 +88,9 @@ export class UmbDocumentCreateOptionsModalElement extends UmbModalBaseElement<
|
||||
this.#documentTypeUnique = documentTypeUnique;
|
||||
this.#documentTypeIcon = this._allowedDocumentTypes.find((dt) => dt.unique === documentTypeUnique)?.icon ?? '';
|
||||
|
||||
/** TODO: Fix this to use the correct endpoint when it becomes available */
|
||||
const { data } = await this.#documentBlueprintItemRepository.requestItems([]);
|
||||
const { data } = await this.#documentBlueprintItemRepository.requestItemsByDocumentType(documentTypeUnique);
|
||||
|
||||
this._availableBlueprints = data?.filter((blueprint) => blueprint.documentType.unique === documentTypeUnique) ?? [];
|
||||
this._availableBlueprints = data ?? [];
|
||||
|
||||
if (!this._availableBlueprints.length) {
|
||||
this.#onNavigate(documentTypeUnique);
|
||||
|
||||
@@ -1,46 +1,60 @@
|
||||
/* eslint-disable lit/attribute-value-entities */
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { css, html, customElement } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
|
||||
@customElement('umb-umbraco-news-dashboard')
|
||||
export class UmbUmbracoNewsDashboardElement extends UmbLitElement {
|
||||
#currentUserContext?: typeof UMB_CURRENT_USER_CONTEXT.TYPE;
|
||||
|
||||
@state()
|
||||
private _name = '';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_CURRENT_USER_CONTEXT, (instance) => {
|
||||
this.#currentUserContext = instance;
|
||||
this.#observeCurrentUser();
|
||||
});
|
||||
}
|
||||
|
||||
#observeCurrentUser(): void {
|
||||
if (!this.#currentUserContext) return;
|
||||
this.observe(this.#currentUserContext.currentUser, (user) => {
|
||||
this._name = user?.name ?? '';
|
||||
});
|
||||
}
|
||||
#infoLinks = [
|
||||
{
|
||||
name: 'Documentation',
|
||||
description: 'Find the answers to all your Umbraco questions',
|
||||
href: 'https://docs.umbraco.com/?utm_source=core&utm_medium=dashboard&utm_campaign=docs',
|
||||
},
|
||||
{
|
||||
name: 'Community',
|
||||
description: 'Get support and inspiration from driven Umbraco experts',
|
||||
href: 'https://our.umbraco.com/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=our_forum',
|
||||
},
|
||||
{
|
||||
name: 'Resources',
|
||||
description: 'Free video tutorials to jumpstart your journey with the CMS',
|
||||
href: 'https://umbraco.com/resources/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=resources',
|
||||
},
|
||||
{
|
||||
name: 'Training',
|
||||
description: 'Real-life training and official Umbraco certifications',
|
||||
href: 'https://umbraco.com/training/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=training',
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-box class="uui-text">
|
||||
<h1 class="uui-h2" style="margin-top: var(--uui-size-layout-1);">
|
||||
<umb-localize key="dashboard_welcome">Welcome</umb-localize>, ${this._name}
|
||||
</h1>
|
||||
<p class="uui-lead">
|
||||
This is the beta version of Umbraco 14, where you can have a first-hand look at the new Backoffice.
|
||||
</p>
|
||||
<p>
|
||||
Please refer to the
|
||||
<a target="_blank" href="https://docs.umbraco.com/umbraco-cms/v/14.latest-beta/">documentation</a> to learn
|
||||
more about what is possible. Here you will find excellent tutorials, guides, and references to help you get
|
||||
started extending the Backoffice.
|
||||
</p>
|
||||
</uui-box>
|
||||
<div id="info-links" class="uui-text">
|
||||
<uui-box id="our-umbraco">
|
||||
<div>
|
||||
<h2 class="uui-h3">Our Umbraco - The Friendliest Community</h2>
|
||||
<p>
|
||||
Our Umbraco, the official community site, is your one-stop-shop for everything Umbraco. Whether you need a
|
||||
question answered, cool plugins, or a guide of how to do something in Umbraco, the world's best and
|
||||
friendliest community is just a click away.
|
||||
</p>
|
||||
<uui-button
|
||||
look="primary"
|
||||
target="_blank"
|
||||
href="https://our.umbraco.com/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=our"
|
||||
label="Visit Our Umbraco"></uui-button>
|
||||
</div>
|
||||
</uui-box>
|
||||
${this.#infoLinks.map(
|
||||
(link) => html`
|
||||
<a class="info-link" target="_blank" href=${link.href}>
|
||||
<h3 class="uui-h5">${link.name}</h3>
|
||||
<p>${link.description}</p>
|
||||
</a>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -54,6 +68,34 @@ export class UmbUmbracoNewsDashboardElement extends UmbLitElement {
|
||||
p {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#our-umbraco {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: -1;
|
||||
margin-bottom: var(--uui-size-space-4);
|
||||
}
|
||||
#info-links {
|
||||
display: grid;
|
||||
max-width: 1000px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-gap: var(--uui-size-space-4);
|
||||
}
|
||||
.info-link {
|
||||
border: 1px solid var(--uui-color-border);
|
||||
padding: var(--uui-size-space-4);
|
||||
border-radius: calc(var(--uui-border-radius) * 2);
|
||||
line-height: 1.5;
|
||||
background-color: var(--uui-color-surface);
|
||||
text-decoration: none;
|
||||
}
|
||||
.info-link h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: var(--uui-size-space-1);
|
||||
}
|
||||
.info-link p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user