Feature/installed and created packages (#545)
* multiple selection in modalhandler is ok? * installed view updates * created view updates * workspace for package builder * package builder * all pickers in workspace * use input-picker rather than property editor ui * installed package view * ui updates * packageview * update handlers * update package views with migrations * update backend api * endpoints * migration & language picker * small update * seperate migrations that doesnt belong to a packag * rename NotificationContext * filter out packages that have no name before they go in to the store --------- Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,10 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
||||
export { OpenAPI } from './core/OpenAPI';
|
||||
export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { AuditLogBaseModel } from './models/AuditLogBaseModel';
|
||||
export type { AuditLogResponseModel } from './models/AuditLogResponseModel';
|
||||
export type { AuditLogWithUsernameResponseModel } from './models/AuditLogWithUsernameResponseModel';
|
||||
export { AuditTypeModel } from './models/AuditTypeModel';
|
||||
export type { ConsentLevelModel } from './models/ConsentLevelModel';
|
||||
export { ContentStateModel } from './models/ContentStateModel';
|
||||
export type { ContentTreeItemModel } from './models/ContentTreeItemModel';
|
||||
@@ -74,6 +78,7 @@ export type { LanguageModel } from './models/LanguageModel';
|
||||
export type { LanguageModelBaseModel } from './models/LanguageModelBaseModel';
|
||||
export type { LanguageUpdateModel } from './models/LanguageUpdateModel';
|
||||
export type { LoggerModel } from './models/LoggerModel';
|
||||
export type { LogLevelCountsModel } from './models/LogLevelCountsModel';
|
||||
export { LogLevelModel } from './models/LogLevelModel';
|
||||
export type { LogMessageModel } from './models/LogMessageModel';
|
||||
export type { LogMessagePropertyModel } from './models/LogMessagePropertyModel';
|
||||
@@ -84,7 +89,13 @@ export type { OkResultModel } from './models/OkResultModel';
|
||||
export { OperatorModel } from './models/OperatorModel';
|
||||
export type { OutOfDateStatusModel } from './models/OutOfDateStatusModel';
|
||||
export { OutOfDateTypeModel } from './models/OutOfDateTypeModel';
|
||||
export type { PackageCreateModel } from './models/PackageCreateModel';
|
||||
export type { PackageDefinitionModel } from './models/PackageDefinitionModel';
|
||||
export type { PackageMigrationStatusModel } from './models/PackageMigrationStatusModel';
|
||||
export type { PackageModelBaseModel } from './models/PackageModelBaseModel';
|
||||
export type { PackageUpdateModel } from './models/PackageUpdateModel';
|
||||
export type { PagedAuditLogResponseModel } from './models/PagedAuditLogResponseModel';
|
||||
export type { PagedAuditLogWithUsernameResponseModel } from './models/PagedAuditLogWithUsernameResponseModel';
|
||||
export type { PagedContentTreeItemModel } from './models/PagedContentTreeItemModel';
|
||||
export type { PagedCultureModel } from './models/PagedCultureModel';
|
||||
export type { PagedDictionaryOverviewModel } from './models/PagedDictionaryOverviewModel';
|
||||
@@ -101,6 +112,7 @@ export type { PagedLanguageModel } from './models/PagedLanguageModel';
|
||||
export type { PagedLoggerModel } from './models/PagedLoggerModel';
|
||||
export type { PagedLogMessageModel } from './models/PagedLogMessageModel';
|
||||
export type { PagedLogTemplateModel } from './models/PagedLogTemplateModel';
|
||||
export type { PagedPackageDefinitionModel } from './models/PagedPackageDefinitionModel';
|
||||
export type { PagedPackageMigrationStatusModel } from './models/PagedPackageMigrationStatusModel';
|
||||
export type { PagedRecycleBinItemModel } from './models/PagedRecycleBinItemModel';
|
||||
export type { PagedRedirectUrlModel } from './models/PagedRedirectUrlModel';
|
||||
@@ -157,6 +169,7 @@ export type { ValueViewModelBaseModel } from './models/ValueViewModelBaseModel';
|
||||
export type { VariantViewModelBaseModel } from './models/VariantViewModelBaseModel';
|
||||
export type { VersionModel } from './models/VersionModel';
|
||||
|
||||
export { AuditLogResource } from './services/AuditLogResource';
|
||||
export { CultureResource } from './services/CultureResource';
|
||||
export { DataTypeResource } from './services/DataTypeResource';
|
||||
export { DictionaryResource } from './services/DictionaryResource';
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AuditTypeModel } from './AuditTypeModel';
|
||||
|
||||
export type AuditLogBaseModel = {
|
||||
userKey?: string;
|
||||
entityKey?: string | null;
|
||||
timestamp?: string;
|
||||
logType?: AuditTypeModel;
|
||||
entityType?: string | null;
|
||||
comment?: string | null;
|
||||
parameters?: string | null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AuditLogBaseModel } from './AuditLogBaseModel';
|
||||
|
||||
export type AuditLogResponseModel = AuditLogBaseModel;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AuditLogBaseModel } from './AuditLogBaseModel';
|
||||
|
||||
export type AuditLogWithUsernameResponseModel = (AuditLogBaseModel & {
|
||||
userName?: string | null;
|
||||
userAvatars?: Array<string> | null;
|
||||
});
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export enum AuditTypeModel {
|
||||
NEW = 'New',
|
||||
SAVE = 'Save',
|
||||
SAVE_VARIANT = 'SaveVariant',
|
||||
OPEN = 'Open',
|
||||
DELETE = 'Delete',
|
||||
PUBLISH = 'Publish',
|
||||
PUBLISH_VARIANT = 'PublishVariant',
|
||||
SEND_TO_PUBLISH = 'SendToPublish',
|
||||
SEND_TO_PUBLISH_VARIANT = 'SendToPublishVariant',
|
||||
UNPUBLISH = 'Unpublish',
|
||||
UNPUBLISH_VARIANT = 'UnpublishVariant',
|
||||
MOVE = 'Move',
|
||||
COPY = 'Copy',
|
||||
ASSIGN_DOMAIN = 'AssignDomain',
|
||||
PUBLIC_ACCESS = 'PublicAccess',
|
||||
SORT = 'Sort',
|
||||
NOTIFY = 'Notify',
|
||||
SYSTEM = 'System',
|
||||
ROLL_BACK = 'RollBack',
|
||||
PACKAGER_INSTALL = 'PackagerInstall',
|
||||
PACKAGER_UNINSTALL = 'PackagerUninstall',
|
||||
CUSTOM = 'Custom',
|
||||
CONTENT_VERSION_PREVENT_CLEANUP = 'ContentVersionPreventCleanup',
|
||||
CONTENT_VERSION_ENABLE_CLEANUP = 'ContentVersionEnableCleanup',
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type LogLevelCountsModel = {
|
||||
information?: number;
|
||||
debug?: number;
|
||||
warning?: number;
|
||||
error?: number;
|
||||
fatal?: number;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AuditLogResponseModel } from './AuditLogResponseModel';
|
||||
|
||||
export type PagedAuditLogResponseModel = {
|
||||
total: number;
|
||||
items: Array<AuditLogResponseModel>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { AuditLogWithUsernameResponseModel } from './AuditLogWithUsernameResponseModel';
|
||||
|
||||
export type PagedAuditLogWithUsernameResponseModel = {
|
||||
total: number;
|
||||
items: Array<AuditLogWithUsernameResponseModel>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { AuditTypeModel } from '../models/AuditTypeModel';
|
||||
import type { DirectionModel } from '../models/DirectionModel';
|
||||
import type { PagedAuditLogResponseModel } from '../models/PagedAuditLogResponseModel';
|
||||
import type { PagedAuditLogWithUsernameResponseModel } from '../models/PagedAuditLogWithUsernameResponseModel';
|
||||
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
|
||||
export class AuditLogResource {
|
||||
|
||||
/**
|
||||
* @returns PagedAuditLogWithUsernameResponseModel Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getAuditLog({
|
||||
orderDirection,
|
||||
sinceDate,
|
||||
skip,
|
||||
take = 100,
|
||||
}: {
|
||||
orderDirection?: DirectionModel,
|
||||
sinceDate?: string,
|
||||
skip?: number,
|
||||
take?: number,
|
||||
}): CancelablePromise<PagedAuditLogWithUsernameResponseModel> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/audit-log',
|
||||
query: {
|
||||
'orderDirection': orderDirection,
|
||||
'sinceDate': sinceDate,
|
||||
'skip': skip,
|
||||
'take': take,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedAuditLogResponseModel Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getAuditLogByKey({
|
||||
key,
|
||||
orderDirection,
|
||||
sinceDate,
|
||||
skip,
|
||||
take = 100,
|
||||
}: {
|
||||
key: string,
|
||||
orderDirection?: DirectionModel,
|
||||
sinceDate?: string,
|
||||
skip?: number,
|
||||
take?: number,
|
||||
}): CancelablePromise<PagedAuditLogResponseModel> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/audit-log/{key}',
|
||||
path: {
|
||||
'key': key,
|
||||
},
|
||||
query: {
|
||||
'orderDirection': orderDirection,
|
||||
'sinceDate': sinceDate,
|
||||
'skip': skip,
|
||||
'take': take,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns PagedAuditLogResponseModel Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getAuditLogTypeByLogType({
|
||||
logType,
|
||||
sinceDate,
|
||||
skip,
|
||||
take = 100,
|
||||
}: {
|
||||
logType: AuditTypeModel,
|
||||
sinceDate?: string,
|
||||
skip?: number,
|
||||
take?: number,
|
||||
}): CancelablePromise<PagedAuditLogResponseModel> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/audit-log/type/{logType}',
|
||||
path: {
|
||||
'logType': logType,
|
||||
},
|
||||
query: {
|
||||
'sinceDate': sinceDate,
|
||||
'skip': skip,
|
||||
'take': take,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { DirectionModel } from '../models/DirectionModel';
|
||||
import type { LogLevelCountsModel } from '../models/LogLevelCountsModel';
|
||||
import type { LogLevelModel } from '../models/LogLevelModel';
|
||||
import type { PagedLoggerModel } from '../models/PagedLoggerModel';
|
||||
import type { PagedLogMessageModel } from '../models/PagedLogMessageModel';
|
||||
@@ -46,7 +47,7 @@ export class LogViewerResource {
|
||||
}: {
|
||||
startDate?: string,
|
||||
endDate?: string,
|
||||
}): CancelablePromise<any> {
|
||||
}): CancelablePromise<LogLevelCountsModel> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/umbraco/management/api/v1/log-viewer/level-count',
|
||||
|
||||
@@ -20,4 +20,21 @@ export class ProfilingResource {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putProfilingStatus({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody?: ProfilingStatusModel,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/umbraco/management/api/v1/profiling/status',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class TrackedReferenceResource {
|
||||
parentKey,
|
||||
skip,
|
||||
take,
|
||||
filterMustBeIsDependency,
|
||||
filterMustBeIsDependency = true,
|
||||
}: {
|
||||
parentKey: string,
|
||||
skip?: number,
|
||||
|
||||
@@ -159,3 +159,7 @@ export type UmbPackage = {
|
||||
};
|
||||
|
||||
export type PagedManifestsResponse = UmbPackage[];
|
||||
|
||||
export type UmbPackageWithMigrationStatus = UmbPackage & {
|
||||
hasPendingMigrations: boolean;
|
||||
};
|
||||
|
||||
@@ -45,9 +45,9 @@ import {
|
||||
} from './settings/languages/app-language-select/app-language.context';
|
||||
import { UmbPackageStore } from './packages/repository/package.store';
|
||||
import { UmbServerExtensionController } from './packages/repository/server-extension.controller';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/notification';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
|
||||
|
||||
import '@umbraco-cms/router';
|
||||
|
||||
|
||||
@@ -1,24 +1,295 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { UUIBooleanInputEvent, UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
|
||||
import { css, html, nothing } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { ifDefined } from 'lit-html/directives/if-defined.js';
|
||||
import { UmbInputDocumentPickerElement } from '../../../shared/components/input-document-picker/input-document-picker.element';
|
||||
import { UmbInputMediaPickerElement } from '../../../shared/components/input-media-picker/input-media-picker.element';
|
||||
import { UmbInputLanguagePickerElement } from '../../../shared/components/input-language-picker/input-language-picker.element';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import { PackageDefinitionModel, PackageResource } from '@umbraco-cms/backend-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/notification';
|
||||
|
||||
@customElement('umb-workspace-package-builder')
|
||||
export class UmbWorkspacePackageBuilderElement extends LitElement {
|
||||
export class UmbWorkspacePackageBuilderElement extends UmbLitElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 0 var(--uui-size-layout-1);
|
||||
display: flex;
|
||||
gap: var(--uui-size-space-4);
|
||||
}
|
||||
|
||||
uui-box {
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
|
||||
uui-checkbox {
|
||||
margin-top: var(--uui-size-space-4);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@property()
|
||||
entityKey?: string;
|
||||
|
||||
@state()
|
||||
private _package: PackageDefinitionModel = {};
|
||||
|
||||
@query('#package-name-input')
|
||||
private _packageNameInput!: UUIInputElement;
|
||||
|
||||
private _notificationContext?: UmbNotificationContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
|
||||
this._notificationContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.entityKey) this.#getPackageCreated();
|
||||
}
|
||||
|
||||
async #getPackageCreated() {
|
||||
if (!this.entityKey) return;
|
||||
const { data } = await tryExecuteAndNotify(this, PackageResource.getPackageCreatedByKey({ key: this.entityKey }));
|
||||
if (!data) return;
|
||||
this._package = data as PackageDefinitionModel;
|
||||
}
|
||||
|
||||
async #download() {
|
||||
if (!this._package?.key) return;
|
||||
const response = await tryExecuteAndNotify(
|
||||
this,
|
||||
PackageResource.getPackageCreatedByKeyDownload({ key: this._package.key })
|
||||
);
|
||||
}
|
||||
|
||||
#nameDefined() {
|
||||
const valid = this._packageNameInput.checkValidity();
|
||||
if (!valid) this._notificationContext?.peek('danger', { data: { message: 'Package missing a name' } });
|
||||
return valid;
|
||||
}
|
||||
|
||||
async #save() {
|
||||
if (!this.#nameDefined()) return;
|
||||
const response = await tryExecuteAndNotify(
|
||||
this,
|
||||
PackageResource.postPackageCreated({ requestBody: this._package })
|
||||
);
|
||||
if (!response.data || response.error) return;
|
||||
this._package = response.data as PackageDefinitionModel;
|
||||
this.#navigateBack();
|
||||
}
|
||||
|
||||
async #update() {
|
||||
if (!this.#nameDefined()) return;
|
||||
if (!this._package?.key) return;
|
||||
const response = await tryExecuteAndNotify(
|
||||
this,
|
||||
PackageResource.putPackageCreatedByKey({ key: this._package.key, requestBody: this._package })
|
||||
);
|
||||
|
||||
if (response.error) return;
|
||||
this.#navigateBack();
|
||||
}
|
||||
|
||||
#navigateBack() {
|
||||
window.history.pushState({}, '', '/section/packages/view/created');
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-workspace-layout alias="Umb.Workspace.PackageBuilder"
|
||||
>PACKAGE BUILDER</umb-workspace-layout
|
||||
> `;
|
||||
return html`
|
||||
<umb-workspace-layout alias="Umb.Workspace.PackageBuilder">
|
||||
${this.#renderHeader()}
|
||||
<uui-box class="wrapper" headline="Package Content"> ${this.#renderEditors()} </uui-box>
|
||||
${this.#renderActions()}
|
||||
</umb-workspace-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderHeader() {
|
||||
return html`<div class="header" slot="header">
|
||||
<uui-button compact @click="${this.#navigateBack}" label="Back to created package overview">
|
||||
<uui-icon name="umb:arrow-left"></uui-icon>
|
||||
</uui-button>
|
||||
<uui-input
|
||||
required
|
||||
id="package-name-input"
|
||||
label="Name of the package"
|
||||
placeholder="Enter a name"
|
||||
value="${ifDefined(this._package?.name)}"
|
||||
@change="${(e: UUIInputEvent) => (this._package.name = e.target.value as string)}"></uui-input>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderActions() {
|
||||
return html`<div slot="actions">
|
||||
${this._package?.key
|
||||
? html`<uui-button @click="${this.#download}" color="" look="secondary" label="Download package">
|
||||
Download
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
<uui-button
|
||||
@click="${this._package.key ? this.#update : this.#save}"
|
||||
color="positive"
|
||||
look="primary"
|
||||
label="Save changes to package">
|
||||
Save
|
||||
</uui-button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderEditors() {
|
||||
return html`<umb-workspace-property-layout label="Content" description="">
|
||||
${this.#renderContentSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Media" description=""
|
||||
>${this.#renderMediaSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Document Types" description="">
|
||||
${this.#renderDocumentTypeSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Media Types" description="">
|
||||
${this.#renderMediaTypeSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Languages" description="">
|
||||
${this.#renderLanguageSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Dictionary" description="">
|
||||
${this.#renderDictionarySection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Data Types" description="">
|
||||
${this.#renderDataTypeSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Templates" description="">
|
||||
${this.#renderTemplateSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Stylesheets" description="">
|
||||
${this.#renderStylesheetsSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Scripts" description="">
|
||||
${this.#renderScriptsSection()}
|
||||
</umb-workspace-property-layout>
|
||||
|
||||
<umb-workspace-property-layout label="Partial Views" description="">
|
||||
${this.#renderPartialViewSection()}
|
||||
</umb-workspace-property-layout>`;
|
||||
}
|
||||
|
||||
#renderContentSection() {
|
||||
return html`
|
||||
<div slot="editor">
|
||||
<umb-input-document-picker
|
||||
.value=${this._package.contentNodeId ?? ''}
|
||||
max="1"
|
||||
@change="${(e: CustomEvent) =>
|
||||
(this._package.contentNodeId = (e.target as UmbInputDocumentPickerElement).selectedKeys[0])}">
|
||||
</umb-input-document-picker>
|
||||
<uui-checkbox
|
||||
label="Include child nodes"
|
||||
.checked="${this._package.contentLoadChildNodes ?? false}"
|
||||
@change="${(e: UUIBooleanInputEvent) => (this._package.contentLoadChildNodes = e.target.checked)}">
|
||||
Include child nodes
|
||||
</uui-checkbox>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderMediaSection() {
|
||||
return html`
|
||||
<div slot="editor">
|
||||
<umb-input-media-picker
|
||||
.selectedKeys=${this._package.mediaKeys ?? []}
|
||||
@change="${(e: CustomEvent) =>
|
||||
(this._package.mediaKeys = (
|
||||
e.target as UmbInputMediaPickerElement
|
||||
).selectedKeys)}"></umb-input-media-picker>
|
||||
<uui-checkbox
|
||||
label="Include child nodes"
|
||||
.checked="${this._package.mediaLoadChildNodes ?? false}"
|
||||
@change="${(e: UUIBooleanInputEvent) => (this._package.mediaLoadChildNodes = e.target.checked)}">
|
||||
Include child nodes
|
||||
</uui-checkbox>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
#renderDocumentTypeSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderMediaTypeSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderLanguageSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-language-picker
|
||||
.value="${this._package.languages?.join(',') ?? ''}"
|
||||
@change="${(e: CustomEvent) => {
|
||||
this._package.languages = (e.target as UmbInputLanguagePickerElement).selectedIsoCodes;
|
||||
}}"></umb-input-language-picker>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderDictionarySection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderDataTypeSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderTemplateSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderStylesheetsSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderScriptsSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#renderPartialViewSection() {
|
||||
return html`<div slot="editor">
|
||||
<umb-input-checkbox-list></umb-input-checkbox-list>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,55 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { path, stripSlash } from 'router-slot';
|
||||
|
||||
@customElement('umb-workspace-package')
|
||||
export class UmbWorkspacePackageElement extends LitElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.header {
|
||||
display: flex;
|
||||
font-size: var(--uui-type-h5-size);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@property()
|
||||
entityKey?: string;
|
||||
|
||||
@state()
|
||||
_package?: any;
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this.entityKey) this._getPackageData();
|
||||
}
|
||||
|
||||
private _getPackageData() {
|
||||
//TODO
|
||||
|
||||
this._package = {
|
||||
key: this.entityKey,
|
||||
name: 'A created package',
|
||||
};
|
||||
}
|
||||
|
||||
private _navigateBack() {
|
||||
window.history.pushState({}, '', '/section/packages/view/installed');
|
||||
}
|
||||
|
||||
private _renderHeader() {
|
||||
return html`<div class="header" slot="header">
|
||||
<uui-button compact @click="${this._navigateBack}">
|
||||
<uui-icon name="umb:arrow-left"></uui-icon>
|
||||
${this._package.name ?? 'Package name'}
|
||||
</uui-button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-workspace-layout alias="Umb.Workspace.Package">PACKAGE Workspace</umb-workspace-layout> `;
|
||||
return html`<umb-workspace-layout alias="Umb.Workspace.Package"> ${this._renderHeader()} </umb-workspace-layout> `;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { html } from 'lit';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { IRoute, IRoutingInfo } from 'router-slot';
|
||||
import type { ManifestWorkspace } from '@umbraco-cms/models';
|
||||
import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
|
||||
import type { ManifestTree, ManifestWorkspace } from '@umbraco-cms/models';
|
||||
import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
|
||||
@customElement('umb-created-packages-section-view')
|
||||
@@ -12,9 +12,10 @@ export class UmbCreatedPackagesSectionViewElement extends UmbLitElement {
|
||||
|
||||
private _workspaces: Array<ManifestWorkspace> = [];
|
||||
|
||||
private _trees: Array<ManifestTree> = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.observe(umbExtensionsRegistry?.extensionsOfType('workspace'), (workspaceExtensions) => {
|
||||
this._workspaces = workspaceExtensions;
|
||||
this._createRoutes();
|
||||
@@ -48,7 +49,7 @@ export class UmbCreatedPackagesSectionViewElement extends UmbLitElement {
|
||||
|
||||
routes.push({
|
||||
path: '**',
|
||||
redirectTo: 'section/packages/view/created/overview', //TODO: this should be dynamic
|
||||
redirectTo: 'overview',
|
||||
});
|
||||
this._routes = routes;
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { html, LitElement } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
@customElement('umb-packages-created-item')
|
||||
export class UmbPackagesCreatedItem extends LitElement {
|
||||
@property({ type: Object })
|
||||
package!: any;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-ref-node-package
|
||||
name=${this.package.name}
|
||||
version=${this.package.version}
|
||||
@open=${this._onClick}></uui-ref-node-package>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onClick() {
|
||||
window.history.pushState({}, '', `/section/packages/view/created/packageBuilder/${this.package.key}`);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-packages-created-item': UmbPackagesCreatedItem;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,158 @@
|
||||
import { html, LitElement } from 'lit';
|
||||
import { html, css, nothing } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
|
||||
import './packages-created-item.element';
|
||||
import { UUIPaginationEvent } from '@umbraco-ui/uui';
|
||||
import { PackageDefinitionModel, PackageResource } from '@umbraco-cms/backend-api';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '@umbraco-cms/modal';
|
||||
|
||||
@customElement('umb-packages-created-overview')
|
||||
export class UmbPackagesCreatedOverviewElement extends LitElement {
|
||||
// TODO: implement call to backend
|
||||
// TODO: add correct model for created packages
|
||||
@state()
|
||||
private _createdPackages: any[] = [
|
||||
{
|
||||
alias: 'my.package',
|
||||
key: '2a0181ec-244b-4068-a1d7-2f95ed7e6da6',
|
||||
name: 'A created package',
|
||||
plans: [],
|
||||
version: '1.0.0',
|
||||
},
|
||||
export class UmbPackagesCreatedOverviewElement extends UmbLitElement {
|
||||
static styles = [
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
uui-box {
|
||||
margin: var(--uui-size-space-5) 0;
|
||||
padding-bottom: var(--uui-size-space-1);
|
||||
}
|
||||
|
||||
.no-packages {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
uui-pagination {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pagination,
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
private take = 20;
|
||||
|
||||
@state()
|
||||
private _loading = true;
|
||||
|
||||
@state()
|
||||
private _createdPackages: PackageDefinitionModel[] = [];
|
||||
|
||||
@state()
|
||||
private _currentPage = 1;
|
||||
|
||||
@state()
|
||||
private _total?: number;
|
||||
|
||||
private _modalContext?: UmbModalContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.#getPackages();
|
||||
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
}
|
||||
|
||||
async #getPackages() {
|
||||
const skip = this._currentPage * this.take - this.take;
|
||||
const { data } = await tryExecuteAndNotify(this, PackageResource.getPackageCreated({ skip, take: this.take }));
|
||||
if (data) {
|
||||
this._total = data.total;
|
||||
this._createdPackages = data.items;
|
||||
}
|
||||
this._loading = false;
|
||||
}
|
||||
|
||||
#createNewPackage() {
|
||||
window.history.pushState({}, '', `/section/packages/view/created/package-builder`);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-box headline="Created packages">
|
||||
return html`<uui-button look="primary" @click="${this.#createNewPackage}" label="Create package">
|
||||
Create package
|
||||
</uui-button>
|
||||
${this._loading ? html`<uui-loader class="loading"></uui-loader>` : this.#renderCreatedPackages()}
|
||||
${this.#renderPagination()}`;
|
||||
}
|
||||
|
||||
#renderCreatedPackages() {
|
||||
if (!this._createdPackages.length) return html`<h2 class="no-packages">No packages have been created yet</h2>`;
|
||||
|
||||
return html`<uui-box headline="Created packages" style="--uui-box-default-padding:0;">
|
||||
<uui-ref-list>
|
||||
${repeat(
|
||||
this._createdPackages,
|
||||
(item) => item.key,
|
||||
(item) => html`<umb-packages-created-item .package=${item}></umb-packages-created-item>`
|
||||
(item) => this.#renderPackageItem(item)
|
||||
)}
|
||||
</uui-ref-list>
|
||||
</uui-box>`;
|
||||
}
|
||||
|
||||
#renderPackageItem(p: PackageDefinitionModel) {
|
||||
return html`<uui-ref-node-package name=${ifDefined(p.name)} @open="${() => this.#packageBuilder(p)}">
|
||||
<uui-action-bar slot="actions">
|
||||
<uui-button @click=${() => this.#deletePackage(p)} label="Delete package">
|
||||
<uui-icon name="delete"></uui-icon>
|
||||
</uui-button>
|
||||
</uui-action-bar>
|
||||
</uui-ref-node-package>`;
|
||||
}
|
||||
|
||||
#packageBuilder(p: PackageDefinitionModel) {
|
||||
if (!p.key) return;
|
||||
window.history.pushState({}, '', `/section/packages/view/created/package-builder/${p.key}`);
|
||||
}
|
||||
|
||||
#renderPagination() {
|
||||
if (!this._total) return nothing;
|
||||
const totalPages = Math.ceil(this._total / this.take);
|
||||
if (totalPages <= 1) return nothing;
|
||||
return html`<div class="pagination">
|
||||
<uui-pagination .total="${totalPages}" @change="${this.#onPageChange}"></uui-pagination>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
#onPageChange(event: UUIPaginationEvent) {
|
||||
if (this._currentPage === event.target.current) return;
|
||||
this._currentPage = event.target.current;
|
||||
this.#getPackages();
|
||||
}
|
||||
|
||||
async #deletePackage(p: PackageDefinitionModel) {
|
||||
if (!p.key) return;
|
||||
const modalHandler = this._modalContext?.confirm({
|
||||
color: 'danger',
|
||||
headline: `Remove ${p.name}?`,
|
||||
content: 'Are you sure you want to delete this package',
|
||||
confirmLabel: 'Delete',
|
||||
});
|
||||
|
||||
const deleteConfirmed = await modalHandler?.onClose().then(({ confirmed }: any) => {
|
||||
return confirmed;
|
||||
});
|
||||
|
||||
if (!deleteConfirmed == true) return;
|
||||
|
||||
const { error } = await tryExecuteAndNotify(this, PackageResource.deletePackageCreatedByKey({ key: p.key }));
|
||||
if (error) return;
|
||||
const index = this._createdPackages.findIndex((x) => x.key === p.key);
|
||||
this._createdPackages.splice(index, 1);
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
export default UmbPackagesCreatedOverviewElement;
|
||||
|
||||
@@ -1,27 +1,54 @@
|
||||
import { html, nothing } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { html, css, nothing } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { firstValueFrom, map } from 'rxjs';
|
||||
import { UUIButtonState } from '@umbraco-ui/uui';
|
||||
|
||||
import { UmbModalContext, UMB_MODAL_CONTEXT_TOKEN } from '../../../../../core/modal';
|
||||
import { createExtensionElement, umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
|
||||
|
||||
import type { ManifestPackageView, UmbPackage } from '@umbraco-cms/models';
|
||||
import type { ManifestPackageView } from '@umbraco-cms/models';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
import { PackageResource } from '@umbraco-cms/backend-api';
|
||||
import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/notification';
|
||||
|
||||
@customElement('umb-installed-packages-section-view-item')
|
||||
export class UmbInstalledPackagesSectionViewItemElement extends UmbLitElement {
|
||||
@property({ type: Object })
|
||||
package!: UmbPackage;
|
||||
export class UmbInstalledPackagesSectionViewItem extends UmbLitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
min-height: 47px;
|
||||
}
|
||||
`;
|
||||
|
||||
@property()
|
||||
name?: string;
|
||||
|
||||
@property()
|
||||
version?: string;
|
||||
|
||||
@property()
|
||||
hasPendingMigrations = false;
|
||||
|
||||
@property()
|
||||
customIcon?: string;
|
||||
|
||||
@state()
|
||||
private _migrationButtonState?: UUIButtonState;
|
||||
|
||||
@state()
|
||||
private _packageView?: ManifestPackageView;
|
||||
|
||||
private _notificationContext?: UmbNotificationContext;
|
||||
private _modalContext?: UmbModalContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => {
|
||||
this._notificationContext = instance;
|
||||
});
|
||||
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
@@ -30,8 +57,8 @@ export class UmbInstalledPackagesSectionViewItemElement extends UmbLitElement {
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
if (this.package.name?.length) {
|
||||
this.findPackageView(this.package.name);
|
||||
if (this.name?.length) {
|
||||
this.findPackageView(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,18 +79,51 @@ export class UmbInstalledPackagesSectionViewItemElement extends UmbLitElement {
|
||||
this._packageView = views[0];
|
||||
}
|
||||
|
||||
async _onMigration() {
|
||||
if (!this.name) return;
|
||||
const modalHandler = this._modalContext?.confirm({
|
||||
color: 'positive',
|
||||
headline: `Run migrations for ${this.name}?`,
|
||||
content: `Do you want to start run migrations for ${this.name}`,
|
||||
confirmLabel: 'Run migrations',
|
||||
});
|
||||
|
||||
const migrationConfirmed = await modalHandler?.onClose().then(({ confirmed }: any) => {
|
||||
return confirmed;
|
||||
});
|
||||
|
||||
if (!migrationConfirmed == true) return;
|
||||
this._migrationButtonState = 'waiting';
|
||||
const { error } = await tryExecuteAndNotify(
|
||||
this,
|
||||
PackageResource.postPackageByNameRunMigration({ name: this.name })
|
||||
);
|
||||
if (error) return;
|
||||
this._notificationContext?.peek('positive', { data: { message: 'Migrations completed' } });
|
||||
this._migrationButtonState = 'success';
|
||||
this.hasPendingMigrations = false;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-ref-node-package name=${ifDefined(this.package.name)} version=${ifDefined(this.package.version)}>
|
||||
<uui-action-bar slot="actions">
|
||||
${this._packageView
|
||||
<uui-ref-node-package
|
||||
name=${ifDefined(this.name)}
|
||||
version="${ifDefined(this.version)}"
|
||||
@open=${this._onConfigure}
|
||||
?disabled="${!this._packageView}">
|
||||
${this.customIcon ? html`<uui-icon slot="icon" name="${this.customIcon}"></uui-icon>` : nothing}
|
||||
<div slot="tag">
|
||||
${this.hasPendingMigrations
|
||||
? html`<uui-button
|
||||
@click="${this._onMigration}"
|
||||
.state=${this._migrationButtonState}
|
||||
color="warning"
|
||||
look="primary"
|
||||
color="positive"
|
||||
@click=${this._onConfigure}
|
||||
label="Configure"></uui-button>`
|
||||
label="Run pending package migrations">
|
||||
Run pending migrations
|
||||
</uui-button>`
|
||||
: nothing}
|
||||
</uui-action-bar>
|
||||
</div>
|
||||
</uui-ref-node-package>
|
||||
`;
|
||||
}
|
||||
@@ -81,12 +141,16 @@ export class UmbInstalledPackagesSectionViewItemElement extends UmbLitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
this._modalContext?.open(element, { data: this.package, size: 'small', type: 'sidebar' });
|
||||
this._modalContext?.open(element, {
|
||||
data: { name: this.name, version: this.version },
|
||||
size: 'full',
|
||||
type: 'sidebar',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-installed-packages-section-view-item': UmbInstalledPackagesSectionViewItemElement;
|
||||
'umb-installed-packages-section-view-item': UmbInstalledPackagesSectionViewItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,56 @@
|
||||
import { html } from 'lit';
|
||||
import { html, css, nothing } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { repeat } from 'lit/directives/repeat.js';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { UmbPackageRepository } from '../../../repository/package.repository';
|
||||
import type { UmbPackage } from '@umbraco-cms/models';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
import type { UmbPackageWithMigrationStatus } from '@umbraco-cms/models';
|
||||
|
||||
import './installed-packages-section-view-item.element';
|
||||
|
||||
@customElement('umb-installed-packages-section-view')
|
||||
export class UmbInstalledPackagesSectionView extends UmbLitElement {
|
||||
@state()
|
||||
private _installedPackages: UmbPackage[] = [];
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
margin: var(--uui-size-layout-1);
|
||||
}
|
||||
uui-box {
|
||||
margin-top: var(--uui-size-space-5);
|
||||
padding-bottom: var(--uui-size-space-1);
|
||||
}
|
||||
|
||||
private repository: UmbPackageRepository;
|
||||
umb-installed-packages-section-view-item {
|
||||
padding: var(--uui-size-space-3) 0 var(--uui-size-space-2);
|
||||
}
|
||||
|
||||
umb-installed-packages-section-view-item:not(:first-child) {
|
||||
border-top: 1px solid var(--uui-color-border, #d8d7d9);
|
||||
}
|
||||
|
||||
.no-packages {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
private _installedPackages: UmbPackageWithMigrationStatus[] = [];
|
||||
|
||||
@state()
|
||||
private _migrationPackages: UmbPackageWithMigrationStatus[] = [];
|
||||
|
||||
#packageRepository: UmbPackageRepository;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.repository = new UmbPackageRepository(this);
|
||||
this.#packageRepository = new UmbPackageRepository(this);
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
@@ -28,20 +61,77 @@ export class UmbInstalledPackagesSectionView extends UmbLitElement {
|
||||
* Fetch the installed packages from the server
|
||||
*/
|
||||
private async _loadInstalledPackages() {
|
||||
const package$ = await this.repository.rootItems();
|
||||
package$.subscribe((packages) => {
|
||||
this._installedPackages = packages.filter((p) => !!p.name);
|
||||
const data = await Promise.all([this.#packageRepository.rootItems(), this.#packageRepository.migrations()]);
|
||||
|
||||
const [package$, migration$] = data;
|
||||
|
||||
combineLatest([package$, migration$]).subscribe(([packages, migrations]) => {
|
||||
this._installedPackages = packages.map((p) => {
|
||||
const migration = migrations.find((m) => m.packageName === p.name);
|
||||
if (migration) {
|
||||
// Remove that migration from the list
|
||||
migrations = migrations.filter((m) => m.packageName !== p.name);
|
||||
}
|
||||
|
||||
return {
|
||||
...p,
|
||||
hasPendingMigrations: migration?.hasPendingMigrations ?? false,
|
||||
};
|
||||
});
|
||||
|
||||
this._migrationPackages = [
|
||||
...migrations.map((m) => ({
|
||||
name: m.packageName,
|
||||
hasPendingMigrations: m.hasPendingMigrations ?? false,
|
||||
})),
|
||||
];
|
||||
/*this._installedPackages = [
|
||||
...this._installedPackages,
|
||||
...migrations.map((m) => ({
|
||||
name: m.packageName,
|
||||
hasPendingMigrations: m.hasPendingMigrations ?? false,
|
||||
})),
|
||||
];*/
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<uui-box headline="Installed packages">
|
||||
if (this._installedPackages.length) return html`${this._renderCustomMigrations()} ${this._renderInstalled()} `;
|
||||
return html`<div class="no-packages">
|
||||
<h2><strong>No packages have been installed</strong></h2>
|
||||
<p>
|
||||
Browse through the available packages using the <strong>'Packages'</strong> icon in the top right of your screen
|
||||
</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _renderInstalled() {
|
||||
return html`<uui-box headline="Installed packages" style="--uui-box-default-padding:0">
|
||||
<uui-ref-list>
|
||||
${repeat(
|
||||
this._installedPackages,
|
||||
(item) => item.name,
|
||||
(item) =>
|
||||
html`<umb-installed-packages-section-view-item .package=${item}></umb-installed-packages-section-view-item>`
|
||||
(item) => html`<umb-installed-packages-section-view-item
|
||||
.name=${item.name}
|
||||
.version=${item.version}
|
||||
.hasPendingMigrations=${item.hasPendingMigrations}></umb-installed-packages-section-view-item>`
|
||||
)}
|
||||
</uui-ref-list>
|
||||
</uui-box>`;
|
||||
}
|
||||
|
||||
private _renderCustomMigrations() {
|
||||
if (!this._migrationPackages) return;
|
||||
return html`<uui-box headline="Migrations" style="--uui-box-default-padding:0">
|
||||
<uui-ref-list>
|
||||
${repeat(
|
||||
this._migrationPackages,
|
||||
(item) => item.name,
|
||||
(item) => html`<umb-installed-packages-section-view-item
|
||||
.name=${item.name}
|
||||
.version=${item.version}
|
||||
.customIcon="${'umb:sync'}"
|
||||
.hasPendingMigrations=${item.hasPendingMigrations}></umb-installed-packages-section-view-item>`
|
||||
)}
|
||||
</uui-ref-list>
|
||||
</uui-box>`;
|
||||
|
||||
@@ -39,7 +39,8 @@ export class UmbPackageRepository {
|
||||
const { data: packages } = await this.#packageSource.getRootItems();
|
||||
|
||||
if (packages) {
|
||||
store.appendItems(packages);
|
||||
// Append packages to the store but only if they have a name
|
||||
store.appendItems(packages.filter((p) => p.name?.length));
|
||||
const extensions: ManifestBase[] = [];
|
||||
|
||||
packages.forEach((p) => {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { handlers as templateHandlers } from './domains/template.handlers';
|
||||
import { handlers as languageHandlers } from './domains/language.handlers';
|
||||
import { handlers as cultureHandlers } from './domains/culture.handlers';
|
||||
import { handlers as redirectManagementHandlers } from './domains/redirect-management.handlers';
|
||||
import { handlers as packageHandlers } from './domains/package.handlers';
|
||||
|
||||
const handlers = [
|
||||
serverHandlers.serverVersionHandler,
|
||||
@@ -51,6 +52,7 @@ const handlers = [
|
||||
...languageHandlers,
|
||||
...cultureHandlers,
|
||||
...redirectManagementHandlers,
|
||||
...packageHandlers,
|
||||
];
|
||||
|
||||
switch (import.meta.env.VITE_UMBRACO_INSTALL_STATUS) {
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import { rest } from 'msw';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { umbracoPath } from '@umbraco-cms/utils';
|
||||
import {
|
||||
PackageCreateModel,
|
||||
PackageDefinitionModel,
|
||||
PagedPackageDefinitionModel,
|
||||
PagedPackageMigrationStatusModel,
|
||||
} from '@umbraco-cms/backend-api';
|
||||
|
||||
export const handlers = [
|
||||
rest.get(umbracoPath('/package/migration-status'), (_req, res, ctx) => {
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(200),
|
||||
ctx.json<PagedPackageMigrationStatusModel>({
|
||||
total: 3,
|
||||
items: [
|
||||
{
|
||||
hasPendingMigrations: true,
|
||||
packageName: 'Named Package',
|
||||
},
|
||||
{
|
||||
hasPendingMigrations: true,
|
||||
packageName: 'My Custom Migration',
|
||||
},
|
||||
{
|
||||
hasPendingMigrations: false,
|
||||
packageName: 'Package with a view',
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post(umbracoPath('/package/:name/run-migration'), async (_req, res, ctx) => {
|
||||
const name = _req.params.name as string;
|
||||
if (!name) return res(ctx.status(404));
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
|
||||
rest.get(umbracoPath('/package/created'), async (_req, res, ctx) => {
|
||||
// read all
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json<PagedPackageDefinitionModel>({
|
||||
total: packageArray.length,
|
||||
items: packageArray,
|
||||
})
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post(umbracoPath('/package/created'), async (_req, res, ctx) => {
|
||||
//save
|
||||
const data: PackageCreateModel = await _req.json();
|
||||
const newPackage: PackageDefinitionModel = { ...data, key: uuidv4() };
|
||||
packageArray.push(newPackage);
|
||||
return res(ctx.status(200), ctx.json<PackageDefinitionModel>(newPackage));
|
||||
}),
|
||||
|
||||
rest.get(umbracoPath('/package/created/:key'), (_req, res, ctx) => {
|
||||
//read 1
|
||||
const key = _req.params.key as string;
|
||||
if (!key) return res(ctx.status(404));
|
||||
const found = packageArray.find((p) => p.key == key);
|
||||
if (!found) return res(ctx.status(404));
|
||||
return res(ctx.status(200), ctx.json<PackageDefinitionModel>(found));
|
||||
}),
|
||||
|
||||
rest.put(umbracoPath('/package/created/:key'), async (_req, res, ctx) => {
|
||||
//update
|
||||
const data: PackageDefinitionModel = await _req.json();
|
||||
if (!data.key) return;
|
||||
const index = packageArray.findIndex((x) => x.key === data.key);
|
||||
packageArray[index] = data;
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
|
||||
rest.delete(umbracoPath('/package/created/:key'), (_req, res, ctx) => {
|
||||
//delete
|
||||
const key = _req.params.key as string;
|
||||
if (!key) return res(ctx.status(404));
|
||||
const index = packageArray.findIndex((p) => p.key == key);
|
||||
if (index <= -1) return res(ctx.status(404));
|
||||
packageArray.splice(index, 1);
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
|
||||
rest.get(umbracoPath('/package/created/:key/download'), (_req, res, ctx) => {
|
||||
//download
|
||||
return res(ctx.status(200));
|
||||
}),
|
||||
];
|
||||
|
||||
const packageArray: PackageDefinitionModel[] = [
|
||||
{
|
||||
key: '2a0181ec-244b-4068-a1d7-2f95ed7e6da6',
|
||||
packagePath: undefined,
|
||||
name: 'My Package',
|
||||
//contentNodeId?: string | null;
|
||||
//contentLoadChildNodes?: boolean;
|
||||
//mediaKeys?: Array<string>;
|
||||
//mediaLoadChildNodes?: boolean;
|
||||
//documentTypes?: Array<string>;
|
||||
//mediaTypes?: Array<string>;
|
||||
//dataTypes?: Array<string>;
|
||||
//templates?: Array<string>;
|
||||
//partialViews?: Array<string>;
|
||||
//stylesheets?: Array<string>;
|
||||
//scripts?: Array<string>;
|
||||
//languages?: Array<string>;
|
||||
//dictionaryItems?: Array<string>;
|
||||
},
|
||||
{
|
||||
key: '2a0181ec-244b-4068-a1d7-2f95ed7e6da7',
|
||||
packagePath: undefined,
|
||||
name: 'My Second Package',
|
||||
},
|
||||
|
||||
{
|
||||
key: '2a0181ec-244b-4068-a1d7-2f95ed7e6da8',
|
||||
packagePath: undefined,
|
||||
name: 'My Third Package',
|
||||
},
|
||||
];
|
||||
@@ -13,6 +13,7 @@ import { handlers as profileHandlers } from './domains/performance-profiling.han
|
||||
import { handlers as healthCheckHandlers } from './domains/health-check.handlers';
|
||||
import { handlers as languageHandlers } from './domains/language.handlers';
|
||||
import { handlers as redirectManagementHandlers } from './domains/redirect-management.handlers';
|
||||
import { handlers as packageHandlers } from './domains/package.handlers';
|
||||
|
||||
export const handlers = [
|
||||
serverHandlers.serverRunningHandler,
|
||||
@@ -31,4 +32,5 @@ export const handlers = [
|
||||
...healthCheckHandlers,
|
||||
...languageHandlers,
|
||||
...redirectManagementHandlers,
|
||||
...packageHandlers,
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { UmbModalLayoutElement } from '../modal-layout.element';
|
||||
|
||||
export interface UmbModalContentPickerData {
|
||||
multiple?: boolean;
|
||||
selection: Array<string>;
|
||||
selection?: Array<string>;
|
||||
}
|
||||
|
||||
import { UmbTreeElement } from '../../../../backoffice/shared/components/tree/tree.element';
|
||||
|
||||
Reference in New Issue
Block a user