Merge branch 'main' into feature/debug-component
This commit is contained in:
@@ -17,8 +17,8 @@ import {
|
||||
} from './shared/components/backoffice-frame/backoffice.context';
|
||||
import { UmbDocumentTypeStore } from './documents/document-types/repository/document-type.store';
|
||||
import { UmbDocumentTypeTreeStore } from './documents/document-types/repository/document-type.tree.store';
|
||||
import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store';
|
||||
import { UmbMediaTypeTreeStore } from './media/media-types/media-type.tree.store';
|
||||
import { UmbMediaTypeDetailStore } from './media/media-types/repository/media-type.detail.store';
|
||||
import { UmbMediaTypeTreeStore } from './media/media-types/repository/media-type.tree.store';
|
||||
import { UmbDocumentStore } from './documents/documents/repository/document.store';
|
||||
import { UmbDocumentTreeStore } from './documents/documents/repository/document.tree.store';
|
||||
import { UmbMediaDetailStore } from './media/media/repository/media.detail.store';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { UmbMediaTypeRepository } from '../repository/media-type.repository';
|
||||
import { UmbEntityActionBase } from '../../../shared/entity-actions';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
|
||||
export class UmbCreateMediaTypeEntityAction extends UmbEntityActionBase<UmbMediaTypeRepository> {
|
||||
constructor(host: UmbControllerHostInterface, repositoryAlias: string, unique: string) {
|
||||
super(host, repositoryAlias, unique);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
console.log(`execute for: ${this.unique}`);
|
||||
alert('open create dialog');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { UmbDeleteEntityAction } from '../../../../backoffice/shared/entity-actions/delete/delete.action';
|
||||
import { UmbMoveEntityAction } from '../../../../backoffice/shared/entity-actions/move/move.action';
|
||||
import { MEDIA_TYPE_REPOSITORY_ALIAS } from '../repository/manifests';
|
||||
import { UmbCopyEntityAction } from '../../../../backoffice/shared/entity-actions/copy/copy.action';
|
||||
import { UmbCreateMediaTypeEntityAction } from './create.action';
|
||||
import UmbReloadMediaTypeEntityAction from './reload.action';
|
||||
import type { ManifestEntityAction } from '@umbraco-cms/models';
|
||||
|
||||
const entityType = 'media-type';
|
||||
const repositoryAlias = MEDIA_TYPE_REPOSITORY_ALIAS;
|
||||
|
||||
const entityActions: Array<ManifestEntityAction> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.MediaType.Create',
|
||||
name: 'Create Media Type Entity Action',
|
||||
weight: 500,
|
||||
meta: {
|
||||
entityType,
|
||||
icon: 'umb:add',
|
||||
label: 'Create',
|
||||
repositoryAlias,
|
||||
api: UmbCreateMediaTypeEntityAction,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.MediaType.Move',
|
||||
name: 'Move Media Type Entity Action',
|
||||
weight: 400,
|
||||
meta: {
|
||||
entityType,
|
||||
icon: 'umb:enter',
|
||||
label: 'Move',
|
||||
repositoryAlias,
|
||||
api: UmbMoveEntityAction,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.MediaType.Copy',
|
||||
name: 'Copy Media Type Entity Action',
|
||||
weight: 300,
|
||||
meta: {
|
||||
entityType,
|
||||
icon: 'umb:documents',
|
||||
label: 'Copy',
|
||||
repositoryAlias,
|
||||
api: UmbCopyEntityAction,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.MediaType.Delete',
|
||||
name: 'Delete Media Type Entity Action',
|
||||
weight: 200,
|
||||
meta: {
|
||||
entityType,
|
||||
icon: 'umb:trash',
|
||||
label: 'Delete',
|
||||
repositoryAlias,
|
||||
api: UmbDeleteEntityAction,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.MediaType.Reload',
|
||||
name: 'Reload Media Type Entity Action',
|
||||
weight: 100,
|
||||
meta: {
|
||||
entityType,
|
||||
icon: 'umb:refresh',
|
||||
label: 'Reload',
|
||||
repositoryAlias,
|
||||
api: UmbReloadMediaTypeEntityAction,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...entityActions];
|
||||
@@ -0,0 +1,16 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { UmbEntityActionBase } from '../../../shared/entity-actions';
|
||||
import { UmbMediaTypeRepository } from '../repository/media-type.repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
|
||||
export default class UmbReloadMediaTypeEntityAction extends UmbEntityActionBase<UmbMediaTypeRepository> {
|
||||
static styles = [UUITextStyles];
|
||||
|
||||
constructor(host: UmbControllerHostInterface, repositoryAlias: string, unique: string) {
|
||||
super(host, repositoryAlias, unique);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
alert('refresh')
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,13 @@
|
||||
import { manifests as sidebarMenuItemManifests } from './sidebar-menu-item/manifests';
|
||||
import { manifests as treeManifests } from './tree/manifests';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests';
|
||||
import { manifests as repositoryManifests } from './repository/manifests';
|
||||
import { manifests as entityActionManifests } from './entity-actions/manifests';
|
||||
|
||||
export const manifests = [...sidebarMenuItemManifests, ...treeManifests, ...workspaceManifests];
|
||||
export const manifests = [
|
||||
...sidebarMenuItemManifests,
|
||||
...treeManifests,
|
||||
...repositoryManifests,
|
||||
...workspaceManifests,
|
||||
...entityActionManifests,
|
||||
];
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import type { MediaTypeDetails } from '@umbraco-cms/models';
|
||||
import { UmbContextToken } from '@umbraco-cms/context-api';
|
||||
import { ArrayState } from '@umbraco-cms/observable-api';
|
||||
import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
|
||||
export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeDetailStore>(
|
||||
'UmbMediaTypeDetailStore'
|
||||
);
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTypeDetailStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Details Data Store for Media Types
|
||||
*/
|
||||
export class UmbMediaTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore<MediaTypeDetails> {
|
||||
private _data = new ArrayState<MediaTypeDetails>([], (x) => x.key);
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
|
||||
getScaffold(entityType: string, parentKey: string | null) {
|
||||
return {} as MediaTypeDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable.
|
||||
* @param {string} key
|
||||
* @return {*} {(Observable<DataTypeModel | undefined>)}
|
||||
* @memberof UmbMediaTypesStore
|
||||
*/
|
||||
getByKey(key: string) {
|
||||
return null as any;
|
||||
}
|
||||
|
||||
// TODO: make sure UI somehow can follow the status of this action.
|
||||
/**
|
||||
* @description - Save a Media Type.
|
||||
* @param {Array<MediaTypeDetails>} mediaTypes
|
||||
* @memberof UmbMediaTypesStore
|
||||
* @return {*} {Promise<void>}
|
||||
*/
|
||||
save(data: MediaTypeDetails[]) {
|
||||
return null as any;
|
||||
}
|
||||
|
||||
// TODO: How can we avoid having this in both stores?
|
||||
/**
|
||||
* @description - Delete a Media Type.
|
||||
* @param {string[]} keys
|
||||
* @memberof UmbMediaTypesStore
|
||||
* @return {*} {Promise<void>}
|
||||
*/
|
||||
async delete(keys: string[]) {
|
||||
// TODO: use backend cli when available.
|
||||
this._data.remove(keys);
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import { FolderTreeItemModel, MediaTypeResource } from '@umbraco-cms/backend-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
import { UmbContextToken } from '@umbraco-cms/context-api';
|
||||
import { ArrayState } from '@umbraco-cms/observable-api';
|
||||
import { UmbStoreBase } from '@umbraco-cms/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
|
||||
export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeTreeStore>(
|
||||
'UmbMediaTypeTreeStore'
|
||||
);
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTypeTreeStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Tree Data Store for Media Types
|
||||
*/
|
||||
export class UmbMediaTypeTreeStore extends UmbStoreBase {
|
||||
#data = new ArrayState<FolderTreeItemModel>([], (x) => x.key);
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
|
||||
getTreeRoot() {
|
||||
tryExecuteAndNotify(this._host, MediaTypeResource.getTreeMediaTypeRoot({})).then(({ data }) => {
|
||||
if (data) {
|
||||
this.#data.append(data.items);
|
||||
}
|
||||
});
|
||||
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === null));
|
||||
}
|
||||
|
||||
getTreeItemChildren(key: string) {
|
||||
tryExecuteAndNotify(
|
||||
this._host,
|
||||
MediaTypeResource.getTreeMediaTypeChildren({
|
||||
parentKey: key,
|
||||
})
|
||||
).then(({ data }) => {
|
||||
if (data) {
|
||||
this.#data.append(data.items);
|
||||
}
|
||||
});
|
||||
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => item.parentKey === key));
|
||||
}
|
||||
|
||||
getTreeItems(keys: Array<string>) {
|
||||
if (keys?.length > 0) {
|
||||
tryExecuteAndNotify(
|
||||
this._host,
|
||||
MediaTypeResource.getTreeMediaTypeItem({
|
||||
key: keys,
|
||||
})
|
||||
).then(({ data }) => {
|
||||
if (data) {
|
||||
// TODO: how do we handle if an item has been removed during this session(like in another tab or by another user)?
|
||||
this.#data.append(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? '')));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { UmbMediaTypeRepository } from './media-type.repository';
|
||||
import { ManifestRepository } from 'libs/extensions-registry/repository.models';
|
||||
|
||||
export const MEDIA_TYPE_REPOSITORY_ALIAS = 'Umb.Repository.MediaTypes';
|
||||
|
||||
const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: MEDIA_TYPE_REPOSITORY_ALIAS,
|
||||
name: 'Media Types Repository',
|
||||
class: UmbMediaTypeRepository,
|
||||
};
|
||||
|
||||
export const manifests = [repository];
|
||||
@@ -0,0 +1,33 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/context-api';
|
||||
import { UmbStoreBase } from '@umbraco-cms/store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
import { ArrayState } from '@umbraco-cms/observable-api';
|
||||
import type { MediaTypeDetails } from '@umbraco-cms/models';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTypeDetailStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Details Data Store for Media Types
|
||||
*/
|
||||
export class UmbMediaTypeDetailStore
|
||||
extends UmbStoreBase
|
||||
{
|
||||
#data = new ArrayState<MediaTypeDetails>([], (x) => x.key);
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UmbMediaTypeDetailStore.name);
|
||||
}
|
||||
|
||||
append(mediaType: MediaTypeDetails) {
|
||||
this.#data.append([mediaType]);
|
||||
}
|
||||
|
||||
remove(uniques: string[]) {
|
||||
this.#data.remove(uniques);
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeDetailStore>(
|
||||
UmbMediaTypeDetailStore.name
|
||||
);
|
||||
@@ -0,0 +1,185 @@
|
||||
import { UmbMediaTypeTreeStore, UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN } from "./media-type.tree.store";
|
||||
import { UmbMediaTypeDetailServerDataSource } from "./sources/media-type.detail.server.data";
|
||||
import { UmbMediaTypeDetailStore, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from "./media-type.detail.store";
|
||||
import { MediaTypeTreeServerDataSource } from "./sources/media-type.tree.server.data";
|
||||
import { ProblemDetailsModel } from "@umbraco-cms/backend-api";
|
||||
import { UmbContextConsumerController } from "@umbraco-cms/context-api";
|
||||
import { UmbControllerHostInterface } from "@umbraco-cms/controller";
|
||||
import type { MediaTypeDetails } from "@umbraco-cms/models";
|
||||
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from "@umbraco-cms/notification";
|
||||
import { UmbTreeRepository, RepositoryTreeDataSource } from "@umbraco-cms/repository";
|
||||
|
||||
export class UmbMediaTypeRepository implements UmbTreeRepository {
|
||||
#init!: Promise<unknown>;
|
||||
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
#treeSource: RepositoryTreeDataSource;
|
||||
#treeStore?: UmbMediaTypeTreeStore;
|
||||
|
||||
#detailSource: UmbMediaTypeDetailServerDataSource;
|
||||
#detailStore?: UmbMediaTypeDetailStore;
|
||||
|
||||
#notificationService?: UmbNotificationService;
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
this.#host = host;
|
||||
|
||||
// TODO: figure out how spin up get the correct data source
|
||||
this.#treeSource = new MediaTypeTreeServerDataSource(this.#host);
|
||||
this.#detailSource = new UmbMediaTypeDetailServerDataSource(this.#host);
|
||||
|
||||
this.#init = Promise.all([
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#detailStore = instance;
|
||||
}),
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#treeStore = instance;
|
||||
}),
|
||||
|
||||
new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#notificationService = instance;
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
async requestRootTreeItems() {
|
||||
await this.#init;
|
||||
|
||||
const { data, error } = await this.#treeSource.getRootItems();
|
||||
|
||||
if (data) {
|
||||
this.#treeStore?.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.rootItems };
|
||||
}
|
||||
|
||||
async requestTreeItemsOf(parentKey: string | null) {
|
||||
await this.#init;
|
||||
|
||||
if (!parentKey) {
|
||||
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
|
||||
return { data: undefined, error };
|
||||
}
|
||||
|
||||
const { data, error } = await this.#treeSource.getChildrenOf(parentKey);
|
||||
|
||||
if (data) {
|
||||
this.#treeStore?.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.childrenOf(parentKey) };
|
||||
}
|
||||
|
||||
async requestTreeItems(keys: Array<string>) {
|
||||
await this.#init;
|
||||
|
||||
if (!keys) {
|
||||
const error: ProblemDetailsModel = { title: 'Keys are missing' };
|
||||
return { data: undefined, error };
|
||||
}
|
||||
|
||||
const { data, error } = await this.#treeSource.getItems(keys);
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.items(keys) };
|
||||
}
|
||||
|
||||
async rootTreeItems() {
|
||||
await this.#init;
|
||||
return this.#treeStore!.rootItems;
|
||||
}
|
||||
|
||||
async treeItemsOf(parentKey: string | null) {
|
||||
await this.#init;
|
||||
return this.#treeStore!.childrenOf(parentKey);
|
||||
}
|
||||
|
||||
async treeItems(keys: Array<string>) {
|
||||
await this.#init;
|
||||
return this.#treeStore!.items(keys);
|
||||
}
|
||||
|
||||
// DETAILS
|
||||
|
||||
async createDetailsScaffold() {
|
||||
await this.#init;
|
||||
return this.#detailSource.createScaffold();
|
||||
}
|
||||
|
||||
async requestDetails(key: string) {
|
||||
await this.#init;
|
||||
|
||||
// TODO: should we show a notification if the key is missing?
|
||||
// Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
|
||||
if (!key) {
|
||||
const error: ProblemDetailsModel = { title: 'Key is missing' };
|
||||
return { error };
|
||||
}
|
||||
const { data, error } = await this.#detailSource.get(key);
|
||||
|
||||
if (data) {
|
||||
this.#detailStore?.append(data);
|
||||
}
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
async delete(key: string) {
|
||||
await this.#init;
|
||||
return this.#detailSource.delete(key);
|
||||
}
|
||||
|
||||
async saveDetail(mediaType: MediaTypeDetails) {
|
||||
await this.#init;
|
||||
|
||||
// TODO: should we show a notification if the media type is missing?
|
||||
// Investigate what is best for Acceptance testing, cause in that perspective a thrown error might be the best choice?
|
||||
if (!mediaType || !mediaType.key) {
|
||||
const error: ProblemDetailsModel = { title: 'Media Type is missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
const { error } = await this.#detailSource.update(mediaType);
|
||||
|
||||
if (!error) {
|
||||
const notification = { data: { message: `Media type '${mediaType.name}' saved` } };
|
||||
this.#notificationService?.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
|
||||
// Consider notify a workspace if a media type is updated in the store while someone is editing it.
|
||||
this.#detailStore?.append(mediaType);
|
||||
this.#treeStore?.updateItem(mediaType.key, { name: mediaType.name });
|
||||
// TODO: would be nice to align the stores on methods/methodNames.
|
||||
|
||||
return { error };
|
||||
}
|
||||
|
||||
async createDetail(mediaType: MediaTypeDetails) {
|
||||
await this.#init;
|
||||
|
||||
if (!mediaType.name) {
|
||||
const error: ProblemDetailsModel = { title: 'Name is missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
const { data, error } = await this.#detailSource.insert(mediaType);
|
||||
|
||||
if (!error) {
|
||||
const notification = { data: { message: `Media type '${mediaType.name}' created` } };
|
||||
this.#notificationService?.peek('positive', notification);
|
||||
}
|
||||
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
async move() {
|
||||
alert('move me!');
|
||||
}
|
||||
|
||||
async copy() {
|
||||
alert('copy me');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { UmbContextToken } from '@umbraco-cms/context-api';
|
||||
import { UmbTreeStoreBase } from '@umbraco-cms/store';
|
||||
import type { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbMediaTypeTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @description - Tree Data Store for Media Types
|
||||
*/
|
||||
export class UmbMediaTypeTreeStore extends UmbTreeStoreBase {
|
||||
|
||||
/**
|
||||
* Creates an instance of UmbMediaTypeTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbMediaTypeTreeStore
|
||||
*/
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host, UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbMediaTypeTreeStore>(
|
||||
UmbMediaTypeTreeStore.name
|
||||
);
|
||||
@@ -0,0 +1,115 @@
|
||||
import { MediaTypeDetailDataSource } from './media-type.details.server.data.interface';
|
||||
import { ProblemDetailsModel } from '@umbraco-cms/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
import type { MediaTypeDetails } from '@umbraco-cms/models';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
|
||||
/**
|
||||
* @description - A data source for the Media Type detail that fetches data from the server
|
||||
* @export
|
||||
* @class UmbMediaTypeDetailServerDataSource
|
||||
* @implements {MediaTypeDetailDataSource}
|
||||
*/
|
||||
export class UmbMediaTypeDetailServerDataSource implements MediaTypeDetailDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Creates a new MediaType scaffold
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeDetailServerDataSource
|
||||
*/
|
||||
async createScaffold() {
|
||||
const data: MediaTypeDetails = {
|
||||
name: '',
|
||||
} as MediaTypeDetails;
|
||||
|
||||
return { data };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Fetches a MediaType with the given key from the server
|
||||
* @param {string} key
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeDetailServerDataSource
|
||||
*/
|
||||
get(key: string) {
|
||||
//return tryExecuteAndNotify(this.#host, MediaTypeResource.getMediaTypeByKey({ key })) as any;
|
||||
// TODO: use backend cli when available.
|
||||
return tryExecuteAndNotify(this.#host, fetch(`/umbraco/management/api/v1/media-type/${key}`)) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Updates a MediaType on the server
|
||||
* @param {MediaTypeDetails} MediaType
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeDetailServerDataSource
|
||||
*/
|
||||
async update(mediaType: MediaTypeDetails) {
|
||||
if (!mediaType.key) {
|
||||
const error: ProblemDetailsModel = { title: 'MediaType key is missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
const payload = { key: mediaType.key, requestBody: mediaType };
|
||||
//return tryExecuteAndNotify(this.#host, MediaTypeResource.putMediaTypeByKey(payload));
|
||||
|
||||
// TODO: use backend cli when available.
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
fetch(`/umbraco/management/api/v1/media-type/${mediaType.key}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Inserts a new MediaType on the server
|
||||
* @param {MediaTypeDetails} data
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeDetailServerDataSource
|
||||
*/
|
||||
async insert(data: MediaTypeDetails) {
|
||||
//return tryExecuteAndNotify(this.#host, MediaTypeResource.postMediaType({ requestBody: data }));
|
||||
// TODO: use backend cli when available.
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
fetch(`/umbraco/management/api/v1/media-type/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description - Deletes a MediaType on the server
|
||||
* @param {string} key
|
||||
* @return {*}
|
||||
* @memberof UmbMediaTypeDetailServerDataSource
|
||||
*/
|
||||
async delete(key: string) {
|
||||
if (!key) {
|
||||
const error: ProblemDetailsModel = { title: 'Key is missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
//return await tryExecuteAndNotify(this.#host, MediaTypeResource.deleteMediaTypeByKey({ key }));
|
||||
// TODO: use backend cli when available.
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
fetch(`/umbraco/management/api/v1/media-type/${key}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
) as any;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { DataSourceResponse, MediaTypeDetails } from '@umbraco-cms/models';
|
||||
|
||||
// TODO => Use models when they exist
|
||||
export interface MediaTypeDetailDataSource {
|
||||
createScaffold(parentKey: string): Promise<DataSourceResponse<MediaTypeDetails>>;
|
||||
get(key: string): Promise<DataSourceResponse<MediaTypeDetails>>;
|
||||
insert(data: any): Promise<DataSourceResponse>;
|
||||
update(data: any): Promise<DataSourceResponse>;
|
||||
delete(key: string): Promise<DataSourceResponse>;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { MediaTypeResource, ProblemDetailsModel } from '@umbraco-cms/backend-api';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
import { RepositoryTreeDataSource } from '@umbraco-cms/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/resources';
|
||||
|
||||
/**
|
||||
* A data source for the MediaType tree that fetches data from the server
|
||||
* @export
|
||||
* @class MediaTypeTreeServerDataSource
|
||||
* @implements {MediaTypeTreeDataSource}
|
||||
*/
|
||||
export class MediaTypeTreeServerDataSource implements RepositoryTreeDataSource {
|
||||
#host: UmbControllerHostInterface;
|
||||
|
||||
/**
|
||||
* Creates an instance of MediaTypeTreeDataSource.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof MediaTypeTreeDataSource
|
||||
*/
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the root items for the tree from the server
|
||||
* @return {*}
|
||||
* @memberof MediaTypeTreeServerDataSource
|
||||
*/
|
||||
async getRootItems() {
|
||||
return tryExecuteAndNotify(this.#host, MediaTypeResource.getTreeMediaTypeRoot({}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the children of a given parent key from the server
|
||||
* @param {(string | null)} parentKey
|
||||
* @return {*}
|
||||
* @memberof MediaTypeTreeServerDataSource
|
||||
*/
|
||||
async getChildrenOf(parentKey: string | null) {
|
||||
if (!parentKey) {
|
||||
const error: ProblemDetailsModel = { title: 'Parent key is missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MediaTypeResource.getTreeMediaTypeChildren({
|
||||
parentKey,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the items for the given keys from the server
|
||||
* @param {Array<string>} keys
|
||||
* @return {*}
|
||||
* @memberof MediaTypeTreeServerDataSource
|
||||
*/
|
||||
async getItems(keys: Array<string>) {
|
||||
if (!keys || keys.length === 0) {
|
||||
const error: ProblemDetailsModel = { title: 'Keys are missing' };
|
||||
return { error };
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
MediaTypeResource.getTreeMediaTypeItem({
|
||||
key: keys,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN } from '../media-type.tree.store';
|
||||
import { UmbMediaTypeRepository } from '../repository/media-type.repository';
|
||||
import type { ManifestTree, ManifestTreeItemAction } from '@umbraco-cms/models';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
@@ -6,7 +6,7 @@ const tree: ManifestTree = {
|
||||
alias: 'Umb.Tree.MediaTypes',
|
||||
name: 'Media Types Tree',
|
||||
meta: {
|
||||
storeAlias: UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN.toString(),
|
||||
repository: UmbMediaTypeRepository
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context';
|
||||
import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface';
|
||||
import { UmbMediaTypeRepository } from '../repository/media-type.repository';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
import { ObjectState } from '@umbraco-cms/observable-api';
|
||||
import type { MediaTypeDetails } from '@umbraco-cms/models';
|
||||
|
||||
type EntityType = MediaTypeDetails;
|
||||
export class UmbWorkspaceMediaTypeContext
|
||||
extends UmbWorkspaceContext
|
||||
implements UmbWorkspaceEntityContextInterface<EntityType | undefined>
|
||||
{
|
||||
#host: UmbControllerHostInterface;
|
||||
#repo: UmbMediaTypeRepository;
|
||||
|
||||
#data = new ObjectState<MediaTypeDetails | undefined>(undefined);
|
||||
data = this.#data.asObservable();
|
||||
name = this.#data.getObservablePart((data) => data?.name);
|
||||
|
||||
constructor(host: UmbControllerHostInterface) {
|
||||
super(host);
|
||||
this.#host = host;
|
||||
this.#repo = new UmbMediaTypeRepository(this.#host);
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data.getValue();
|
||||
}
|
||||
|
||||
getEntityKey() {
|
||||
return this.getData()?.key || '';
|
||||
}
|
||||
|
||||
getEntityType() {
|
||||
return 'media-type';
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.#data.update({ name });
|
||||
}
|
||||
|
||||
setPropertyValue(alias: string, value: string) {
|
||||
// TODO => Implement setPropertyValue
|
||||
}
|
||||
|
||||
async load(entityKey: string) {
|
||||
const { data } = await this.#repo.requestDetails(entityKey);
|
||||
if (data) {
|
||||
this.#data.next(data);
|
||||
}
|
||||
}
|
||||
|
||||
async createScaffold() {
|
||||
const { data } = await this.#repo.createDetailsScaffold();
|
||||
if (!data) return;
|
||||
this.#data.next(data);
|
||||
}
|
||||
|
||||
async save() {
|
||||
if (!this.#data.value) return;
|
||||
this.#repo.saveDetail(this.#data.value);
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
this.#data.complete();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,71 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import '../../../shared/components/workspace/workspace-layout/workspace-layout.element';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
|
||||
import { UmbWorkspaceEntityElement } from '../../../../backoffice/shared/components/workspace/workspace-entity-element.interface';
|
||||
import { UmbWorkspaceMediaTypeContext } from './media-type-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
|
||||
@customElement('umb-media-type-workspace')
|
||||
export class UmbMediaTypeWorkspaceElement extends LitElement {
|
||||
export class UmbMediaTypeWorkspaceElement extends UmbLitElement implements UmbWorkspaceEntityElement {
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
#header {
|
||||
display: flex;
|
||||
padding: 0 var(--uui-size-space-6);
|
||||
gap: var(--uui-size-space-4);
|
||||
width: 100%;
|
||||
}
|
||||
uui-input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
private _unique?: string;
|
||||
|
||||
@state()
|
||||
private _mediaTypeName?: string | null = '';
|
||||
|
||||
@property()
|
||||
id!: string;
|
||||
|
||||
#workspaceContext = new UmbWorkspaceMediaTypeContext(this);
|
||||
|
||||
public load(entityKey: string) {
|
||||
this.#workspaceContext.load(entityKey);
|
||||
this._unique = entityKey;
|
||||
}
|
||||
|
||||
public create() {
|
||||
this.#workspaceContext.createScaffold();
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.observe(this.#workspaceContext.name, (name) => {
|
||||
this._mediaTypeName = name;
|
||||
});
|
||||
}
|
||||
|
||||
// TODO. find a way where we don't have to do this for all Workspaces.
|
||||
#handleInput(event: UUIInputEvent) {
|
||||
if (event instanceof UUIInputEvent) {
|
||||
const target = event.composedPath()[0] as UUIInputElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
this.#workspaceContext.setName(target.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-workspace-layout alias="Umb.Workspace.MediaType">Media Type Workspace</umb-workspace-layout>`;
|
||||
return html`<umb-workspace-layout alias="Umb.Workspace.MediaType">
|
||||
<uui-input id="header" slot="header" .value=${this._unique} @input="${this.#handleInput}"></uui-input>
|
||||
</umb-workspace-layout>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { distinctUntilChanged } from 'rxjs';
|
||||
import { UmbWorkspaceDataTypeContext } from './data-type-workspace.context';
|
||||
import { UmbLitElement } from '@umbraco-cms/element';
|
||||
|
||||
@@ -29,23 +28,23 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
|
||||
`,
|
||||
];
|
||||
|
||||
private _workspaceContext: UmbWorkspaceDataTypeContext = new UmbWorkspaceDataTypeContext(this);
|
||||
|
||||
public load(value: string) {
|
||||
this._workspaceContext?.load(value);
|
||||
//this._unique = entityKey;
|
||||
}
|
||||
|
||||
public create(parentKey: string | null) {
|
||||
this._workspaceContext.createScaffold(parentKey);
|
||||
}
|
||||
#workspaceContext: UmbWorkspaceDataTypeContext = new UmbWorkspaceDataTypeContext(this);
|
||||
|
||||
@state()
|
||||
private _dataTypeName = '';
|
||||
|
||||
public load(value: string) {
|
||||
this.#workspaceContext?.load(value);
|
||||
//this._unique = entityKey;
|
||||
}
|
||||
|
||||
public create(parentKey: string | null) {
|
||||
this.#workspaceContext.createScaffold(parentKey);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.observe(this._workspaceContext.name, (dataTypeName) => {
|
||||
this.observe(this.#workspaceContext.name, (dataTypeName) => {
|
||||
if (dataTypeName !== this._dataTypeName) {
|
||||
this._dataTypeName = dataTypeName ?? '';
|
||||
}
|
||||
@@ -53,12 +52,12 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
// TODO. find a way where we don't have to do this for all Workspaces.
|
||||
private _handleInput(event: UUIInputEvent) {
|
||||
#handleInput(event: UUIInputEvent) {
|
||||
if (event instanceof UUIInputEvent) {
|
||||
const target = event.composedPath()[0] as UUIInputElement;
|
||||
|
||||
if (typeof target?.value === 'string') {
|
||||
this._workspaceContext.setName(target.value);
|
||||
this.#workspaceContext.setName(target.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +65,7 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement {
|
||||
render() {
|
||||
return html`
|
||||
<umb-workspace-layout alias="Umb.Workspace.DataType">
|
||||
<uui-input id="header" slot="header" .value=${this._dataTypeName} @input="${this._handleInput}"></uui-input>
|
||||
<uui-input id="header" slot="header" .value=${this._dataTypeName} @input="${this.#handleInput}"></uui-input>
|
||||
</umb-workspace-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { DictionaryDetails } from '@umbraco-cms/models';
|
||||
* @export
|
||||
* @class UmbDictionaryDetailStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Details Data Store for Data Types
|
||||
* @description - Details Data Store for Dictionary
|
||||
*/
|
||||
export class UmbDictionaryDetailStore
|
||||
extends UmbStoreBase
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DictionaryTreeServerDataSource } from './sources/dictionary.tree.server.data';
|
||||
import { UmbDictionaryTreeStore, UMB_DICTIONARY_TREE_STORE_CONTEXT_TOKEN } from './dictionary.tree.store';
|
||||
import { UmbDictionaryDetailStore, UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN } from './dictionary.detail.store';
|
||||
import { UmbDictionaryDetailServerDataSource } from './sources/dictionary.detail.server.data';
|
||||
import { UmbDictionaryDetailStore, UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN } from './dictionary.detail.store';
|
||||
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/context-api';
|
||||
import { RepositoryTreeDataSource, UmbTreeRepository } from '@umbraco-cms/repository';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller';
|
||||
* @export
|
||||
* @class UmbDictionaryTreeStore
|
||||
* @extends {UmbTreeStoreBase}
|
||||
* @description - Tree Data Store for Data Types
|
||||
* @description - Tree Data Store for Dictionary
|
||||
*/
|
||||
export class UmbDictionaryTreeStore extends UmbTreeStoreBase {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user