add scripts files
This commit is contained in:
committed by
Jacob Overgaard
parent
a0a09a33e1
commit
b8a4cf2062
@@ -0,0 +1,15 @@
|
||||
import { ScriptResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export type ScriptDetails = ScriptResponseModel;
|
||||
|
||||
export const SCRIPTS_ENTITY_TYPE = 'script';
|
||||
export const SCRIPTS_ROOT_ENTITY_TYPE = 'script-root';
|
||||
export const SCRIPTS_FOLDER_ENTITY_TYPE = 'script-folder';
|
||||
export const SCRIPTS_FOLDER_EMPTY_ENTITY_TYPE = 'script-folder-empty';
|
||||
|
||||
export const SCRIPTS_REPOSITORY_ALIAS = 'Umb.Repository.Scripts';
|
||||
|
||||
export const SCRIPTS_TREE_ALIAS = 'Umb.Tree.Scripts';
|
||||
|
||||
export const UMB_SCRIPTS_TREE_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.Scripts.Tree';
|
||||
export const UMB_SCRIPTS_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.Scripts';
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export class UmbCreateScriptAction<T extends { copy(): Promise<void> }> extends UmbEntityActionBase<T> {
|
||||
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string) {
|
||||
super(host, repositoryAlias, unique);
|
||||
}
|
||||
|
||||
async execute() {
|
||||
history.pushState(null, '', `section/settings/workspace/script/create/${this.unique ?? 'null'}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
SCRIPTS_REPOSITORY_ALIAS,
|
||||
SCRIPTS_ENTITY_TYPE,
|
||||
SCRIPTS_FOLDER_ENTITY_TYPE,
|
||||
SCRIPTS_ROOT_ENTITY_TYPE,
|
||||
SCRIPTS_FOLDER_EMPTY_ENTITY_TYPE,
|
||||
} from '../config.js';
|
||||
import { UmbCreateScriptAction } from './create/create-empty.action.js';
|
||||
import {
|
||||
UmbCreateFolderEntityAction,
|
||||
UmbDeleteEntityAction,
|
||||
UmbDeleteFolderEntityAction,
|
||||
} from '@umbraco-cms/backoffice/entity-action';
|
||||
import { ManifestEntityAction } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const scriptsViewActions: Array<ManifestEntityAction> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialView.Delete',
|
||||
name: 'Delete PartialView Entity Action',
|
||||
meta: {
|
||||
icon: 'umb:trash',
|
||||
label: 'Delete',
|
||||
api: UmbDeleteEntityAction,
|
||||
repositoryAlias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
entityTypes: [SCRIPTS_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const scriptsFolderActions: Array<ManifestEntityAction> = [
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialViewFolder.Create.New',
|
||||
name: 'Create PartialView Entity Under Directory Action',
|
||||
meta: {
|
||||
icon: 'umb:article',
|
||||
label: 'New empty partial view',
|
||||
api: UmbCreateScriptAction,
|
||||
repositoryAlias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
entityTypes: [SCRIPTS_FOLDER_ENTITY_TYPE, SCRIPTS_ROOT_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialViewFolder.DeleteFolder',
|
||||
name: 'Remove empty folder',
|
||||
meta: {
|
||||
icon: 'umb:trash',
|
||||
label: 'Remove folder',
|
||||
api: UmbDeleteFolderEntityAction,
|
||||
repositoryAlias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
entityTypes: [SCRIPTS_FOLDER_EMPTY_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'entityAction',
|
||||
alias: 'Umb.EntityAction.PartialViewFolder.CreateFolder',
|
||||
name: 'Create empty folder',
|
||||
meta: {
|
||||
icon: 'umb:add',
|
||||
label: 'Create folder',
|
||||
api: UmbCreateFolderEntityAction,
|
||||
repositoryAlias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
entityTypes: [SCRIPTS_FOLDER_EMPTY_ENTITY_TYPE, SCRIPTS_FOLDER_ENTITY_TYPE, SCRIPTS_ROOT_ENTITY_TYPE],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [...scriptsViewActions, ...scriptsFolderActions];
|
||||
@@ -0,0 +1 @@
|
||||
export * from './repository/index.js';
|
||||
@@ -0,0 +1,13 @@
|
||||
import { manifests as repositoryManifests } from './repository/manifests.js';
|
||||
import { manifests as menuItemManifests } from './menu-item/manifests.js';
|
||||
import { manifests as treeManifests } from './tree/manifests.js';
|
||||
import { manifests as entityActionsManifests } from './entity-actions/manifests.js';
|
||||
import { manifests as workspaceManifests } from './workspace/manifests.js';
|
||||
|
||||
export const manifests = [
|
||||
...repositoryManifests,
|
||||
...menuItemManifests,
|
||||
...treeManifests,
|
||||
...entityActionsManifests,
|
||||
...workspaceManifests,
|
||||
];
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SCRIPTS_ENTITY_TYPE, SCRIPTS_TREE_ALIAS } from '../config.js';
|
||||
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const menuItem: ManifestTypes = {
|
||||
type: 'menuItem',
|
||||
kind: 'tree',
|
||||
alias: 'Umb.MenuItem.Scripts',
|
||||
name: 'Scripts Menu Item',
|
||||
weight: 40,
|
||||
meta: {
|
||||
label: 'Scripts',
|
||||
icon: 'umb:folder',
|
||||
entityType: SCRIPTS_ENTITY_TYPE,
|
||||
treeAlias: SCRIPTS_TREE_ALIAS,
|
||||
menus: ['Umb.Menu.Templating'],
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [menuItem];
|
||||
@@ -0,0 +1 @@
|
||||
export * from './scripts.repository.js';
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ManifestRepository, ManifestStore, ManifestTreeStore } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { SCRIPTS_REPOSITORY_ALIAS } from '../config.js';
|
||||
|
||||
const repository: ManifestRepository = {
|
||||
type: 'repository',
|
||||
alias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
name: 'Partial Views Repository',
|
||||
class: UmbScriptsRepository,
|
||||
};
|
||||
|
||||
export const SCRIPTS_STORE_ALIAS = 'Umb.Store.PartialViews';
|
||||
export const SCRIPTS_TREE_STORE_ALIAS = 'Umb.Store.PartialViewsTree';
|
||||
|
||||
const store: ManifestStore = {
|
||||
type: 'store',
|
||||
alias: SCRIPTS_STORE_ALIAS,
|
||||
name: 'Partial Views Store',
|
||||
class: UmbScriptsStore,
|
||||
};
|
||||
|
||||
const treeStore: ManifestTreeStore = {
|
||||
type: 'treeStore',
|
||||
alias: SCRIPTS_TREE_STORE_ALIAS,
|
||||
name: 'Partial Views Tree Store',
|
||||
class: UmbScriptsTreeStore,
|
||||
};
|
||||
|
||||
export const manifests = [repository, store, treeStore];
|
||||
@@ -0,0 +1,218 @@
|
||||
import { PARTIAL_VIEW_ROOT_ENTITY_TYPE } from '../../partial-views/config.js';
|
||||
import { UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN } from '../../partial-views/repository/partial-views.tree.store.js';
|
||||
import { PartialViewGetFolderResponse } from '../../partial-views/repository/sources/partial-views.folder.server.data.js';
|
||||
import { UmbScriptsTreeServerDataSource } from './sources/scripts.tree.server.data.js';
|
||||
import { UmbScriptsServerDataSource } from './sources/scripts.detail.server.data.js';
|
||||
import { UmbScriptsFolderServerDataSource } from './sources/scripts.folder.server.data.js';
|
||||
import { UmbScriptsTreeStore } from './scripts.tree.store.js';
|
||||
import {
|
||||
DataSourceResponse,
|
||||
UmbDataSourceErrorResponse,
|
||||
UmbDetailRepository,
|
||||
UmbFolderRepository,
|
||||
UmbTreeRepository,
|
||||
} from '@umbraco-cms/backoffice/repository';
|
||||
import {
|
||||
CreateFolderRequestModel,
|
||||
CreateScriptRequestModel,
|
||||
FileItemResponseModelBaseModel,
|
||||
FileSystemTreeItemPresentationModel,
|
||||
FolderModelBaseModel,
|
||||
FolderResponseModel,
|
||||
ProblemDetails,
|
||||
ScriptResponseModel,
|
||||
TextFileResponseModelBaseModel,
|
||||
UpdateScriptRequestModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
|
||||
export class UmbScriptsRepository
|
||||
implements
|
||||
UmbTreeRepository<FileSystemTreeItemPresentationModel>,
|
||||
UmbDetailRepository<CreateScriptRequestModel, string, UpdateScriptRequestModel, ScriptResponseModel, string>,
|
||||
UmbFolderRepository
|
||||
{
|
||||
#init;
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
#treeDataSource: UmbScriptsTreeServerDataSource;
|
||||
#detailDataSource: UmbScriptsServerDataSource;
|
||||
#folderDataSource: UmbScriptsFolderServerDataSource;
|
||||
|
||||
#treeStore?: UmbScriptsTreeStore;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
|
||||
this.#treeDataSource = new UmbScriptsTreeServerDataSource(this.#host);
|
||||
this.#detailDataSource = new UmbScriptsServerDataSource(this.#host);
|
||||
this.#folderDataSource = new UmbScriptsFolderServerDataSource(this.#host);
|
||||
|
||||
this.#init = Promise.all([
|
||||
new UmbContextConsumerController(this.#host, UMB_PARTIAL_VIEW_TREE_STORE_CONTEXT_TOKEN, (instance) => {
|
||||
this.#treeStore = instance;
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
//#region FOLDER
|
||||
createFolderScaffold(
|
||||
parentId: string | null,
|
||||
): Promise<{ data?: FolderResponseModel | undefined; error?: ProblemDetails | undefined }> {
|
||||
const data: FolderResponseModel = {
|
||||
name: '',
|
||||
parentId,
|
||||
};
|
||||
return Promise.resolve({ data, error: undefined });
|
||||
}
|
||||
async createFolder(
|
||||
requestBody: CreateFolderRequestModel,
|
||||
): Promise<{ data?: string | undefined; error?: ProblemDetails | undefined }> {
|
||||
await this.#init;
|
||||
const req = {
|
||||
parentPath: requestBody.parentId,
|
||||
name: requestBody.name,
|
||||
};
|
||||
const promise = this.#folderDataSource.insert(req);
|
||||
await promise;
|
||||
this.requestTreeItemsOf(requestBody.parentId ? requestBody.parentId : null);
|
||||
return promise;
|
||||
}
|
||||
async requestFolder(
|
||||
unique: string,
|
||||
): Promise<{ data?: PartialViewGetFolderResponse | undefined; error?: ProblemDetails | undefined }> {
|
||||
await this.#init;
|
||||
return this.#folderDataSource.get(unique);
|
||||
}
|
||||
updateFolder(
|
||||
unique: string,
|
||||
folder: FolderModelBaseModel,
|
||||
): Promise<{ data?: FolderModelBaseModel | undefined; error?: ProblemDetails | undefined }> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
async deleteFolder(path: string): Promise<{ error?: ProblemDetails | undefined }> {
|
||||
await this.#init;
|
||||
const { data } = await this.requestFolder(path);
|
||||
const promise = this.#folderDataSource.delete(path);
|
||||
await promise;
|
||||
this.requestTreeItemsOf(data?.parentPath ? data?.parentPath : null);
|
||||
return promise;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region TREE
|
||||
|
||||
async requestTreeRoot() {
|
||||
await this.#init;
|
||||
|
||||
const data = {
|
||||
id: null,
|
||||
path: null,
|
||||
type: PARTIAL_VIEW_ROOT_ENTITY_TYPE,
|
||||
name: 'Partial Views',
|
||||
icon: 'umb:folder',
|
||||
hasChildren: true,
|
||||
};
|
||||
return { data };
|
||||
}
|
||||
|
||||
async requestRootTreeItems() {
|
||||
await this.#init;
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getRootItems();
|
||||
|
||||
if (data) {
|
||||
this.#treeStore?.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.rootItems };
|
||||
}
|
||||
|
||||
async requestTreeItemsOf(path: string | null) {
|
||||
if (path === null) {
|
||||
return this.requestRootTreeItems();
|
||||
}
|
||||
|
||||
await this.#init;
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getChildrenOf({ path, skip: 0, take: 100 });
|
||||
if (data) {
|
||||
this.#treeStore!.appendItems(data.items);
|
||||
}
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.childrenOf(path) };
|
||||
}
|
||||
|
||||
async requestTreeItems(keys: Array<string>) {
|
||||
await this.#init;
|
||||
|
||||
if (!keys) {
|
||||
const error: ProblemDetails = { title: 'Keys are missing' };
|
||||
return { data: undefined, error };
|
||||
}
|
||||
|
||||
const { data, error } = await this.#treeDataSource.getItem(keys);
|
||||
|
||||
return { data, error, asObservable: () => this.#treeStore!.items(keys) };
|
||||
}
|
||||
|
||||
async rootTreeItems() {
|
||||
await this.#init;
|
||||
return this.#treeStore!.rootItems;
|
||||
}
|
||||
|
||||
async treeItemsOf(parentPath: string | null) {
|
||||
if (!parentPath) throw new Error('Parent Path is missing');
|
||||
await this.#init;
|
||||
return this.#treeStore!.childrenOf(parentPath);
|
||||
}
|
||||
|
||||
async treeItems(paths: Array<string>) {
|
||||
if (!paths) throw new Error('Paths are missing');
|
||||
await this.#init;
|
||||
return this.#treeStore!.items(paths);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region DETAILS
|
||||
async requestByKey(path: string) {
|
||||
if (!path) throw new Error('Path is missing');
|
||||
await this.#init;
|
||||
const { data, error } = await this.#detailDataSource.get(path);
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
requestById(id: string): Promise<DataSourceResponse<any>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
byId(id: string): Promise<Observable<any>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
createScaffold(parentId: string | null, preset: string): Promise<DataSourceResponse<TextFileResponseModelBaseModel>> {
|
||||
return this.#detailDataSource.createScaffold(parentId, preset);
|
||||
}
|
||||
async create(data: CreateScriptRequestModel): Promise<DataSourceResponse<any>> {
|
||||
const promise = this.#detailDataSource.insert(data);
|
||||
await promise;
|
||||
this.requestTreeItemsOf(data.parentPath ? data.parentPath : null);
|
||||
return promise;
|
||||
}
|
||||
save(id: string, requestBody: UpdateScriptRequestModel): Promise<UmbDataSourceErrorResponse> {
|
||||
return this.#detailDataSource.update(id, requestBody);
|
||||
}
|
||||
async delete(id: string): Promise<UmbDataSourceErrorResponse> {
|
||||
const promise = this.#detailDataSource.delete(id);
|
||||
const parentPath = id.substring(0, id.lastIndexOf('/'));
|
||||
this.requestTreeItemsOf(parentPath ? parentPath : null);
|
||||
return promise;
|
||||
}
|
||||
|
||||
requestItems(keys: Array<string>): Promise<DataSourceResponse<FileItemResponseModelBaseModel[]>> {
|
||||
return this.#detailDataSource.getItems(keys);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { UMB_SCRIPTS_STORE_CONTEXT_TOKEN_ALIAS } from '../config.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbStoreBase } from '@umbraco-cms/backoffice/store';
|
||||
import type { TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
/**
|
||||
* @export
|
||||
* @class UmbPartialViewsStore
|
||||
* @extends {UmbStoreBase}
|
||||
* @description - Data Store for partial views
|
||||
*/
|
||||
export class UmbScriptsStore extends UmbStoreBase {
|
||||
/**
|
||||
* Creates an instance of UmbPartialViewsStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(host, UMB_SCRIPTS_STORE_CONTEXT_TOKEN.toString(), new UmbArrayState<TemplateResponseModel>([], (x) => x.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a partial view to the store
|
||||
* @param {Template} template
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
append(template: TemplateResponseModel) {
|
||||
this._data.append([template]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes partial views in the store with the given uniques
|
||||
* @param {string[]} uniques
|
||||
* @memberof UmbPartialViewsStore
|
||||
*/
|
||||
remove(uniques: string[]) {
|
||||
this._data.remove(uniques);
|
||||
}
|
||||
}
|
||||
|
||||
export const UMB_SCRIPTS_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbScriptsStore>(
|
||||
UMB_SCRIPTS_STORE_CONTEXT_TOKEN_ALIAS,
|
||||
);
|
||||
@@ -0,0 +1,26 @@
|
||||
import { UMB_SCRIPTS_TREE_STORE_CONTEXT_TOKEN_ALIAS } from '../config.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbFileSystemTreeStore } from '@umbraco-cms/backoffice/store';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
|
||||
export const UMB_SCRIPTS_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken<UmbPartialViewsTreeStore>(
|
||||
UMB_SCRIPTS_TREE_STORE_CONTEXT_TOKEN_ALIAS,
|
||||
);
|
||||
|
||||
/**
|
||||
* Tree Store for partial views
|
||||
*
|
||||
* @export
|
||||
* @class UmbPartialViewsTreeStore
|
||||
* @extends {UmbEntityTreeStore}
|
||||
*/
|
||||
export class UmbPartialViewsTreeStore extends UmbFileSystemTreeStore {
|
||||
/**
|
||||
* Creates an instance of UmbPartialViewsTreeStore.
|
||||
* @param {UmbControllerHostInterface} host
|
||||
* @memberof UmbPartialViewsTreeStore
|
||||
*/
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(host, UMB_SCRIPTS_TREE_STORE_CONTEXT_TOKEN.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {
|
||||
FileSystemTreeItemPresentationModel,
|
||||
PagedFileSystemTreeItemPresentationModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export interface ScriptsTreeDataSource {
|
||||
getRootItems(): Promise<DataSourceResponse<PagedFileSystemTreeItemPresentationModel>>;
|
||||
getChildrenOf({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
}: {
|
||||
path?: string | undefined;
|
||||
skip?: number | undefined;
|
||||
take?: number | undefined;
|
||||
}): Promise<DataSourceResponse<PagedFileSystemTreeItemPresentationModel>>;
|
||||
getItem(ids: Array<string>): Promise<DataSourceResponse<FileSystemTreeItemPresentationModel[]>>;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
CreatePartialViewRequestModel,
|
||||
CreateScriptRequestModel,
|
||||
CreateTextFileViewModelBaseModel,
|
||||
PartialViewItemResponseModel,
|
||||
ScriptResource,
|
||||
ScriptResponseModel,
|
||||
UpdatePartialViewRequestModel,
|
||||
UpdateScriptRequestModel,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { DataSourceResponse, UmbDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
export class UmbScriptsServerDataSource
|
||||
implements UmbDataSource<CreateScriptRequestModel, string, UpdateScriptRequestModel, ScriptResponseModel, string>
|
||||
{
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
}
|
||||
createScaffold(
|
||||
parentId: string | null,
|
||||
preset?: string | Partial<CreateTextFileViewModelBaseModel> | undefined,
|
||||
): Promise<DataSourceResponse<CreateTextFileViewModelBaseModel>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a partial view with the given path from the server
|
||||
* @param {string} path
|
||||
* @return {*}
|
||||
* @memberof UmbStylesheetServerDataSource
|
||||
*/
|
||||
get(path: string): Promise<DataSourceResponse<ScriptResponseModel>> {
|
||||
if (!path) throw new Error('Path is missing');
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.getScript({ path }));
|
||||
}
|
||||
/**
|
||||
* Creates a new partial view
|
||||
*
|
||||
* @param {CreatePartialViewRequestModel} requestBody
|
||||
* @return {*} {Promise<DataSourceResponse<string>>}
|
||||
* @memberof UmbPartialViewDetailServerDataSource
|
||||
*/
|
||||
insert(requestBody: CreatePartialViewRequestModel): Promise<DataSourceResponse<string>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.postScript({ requestBody }));
|
||||
}
|
||||
|
||||
//TODO the parameters here are bit ugly, since unique is already in the request body parameter, but it has to be done to marry the UmbDataSource interface an backend API together... maybe come up with some nicer solution
|
||||
/**
|
||||
* Updates a partial view
|
||||
*
|
||||
* @param {string} [unique='']
|
||||
* @param {UpdatePartialViewRequestModel} requestBody
|
||||
* @return {*} {Promise<DataSourceResponse<any>>}
|
||||
* @memberof UmbPartialViewDetailServerDataSource
|
||||
*/
|
||||
update(unique = '', requestBody: UpdatePartialViewRequestModel): Promise<DataSourceResponse<any>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.putScript({ requestBody }));
|
||||
}
|
||||
/**
|
||||
* Deletes a partial view
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {*} {Promise<DataSourceResponse>}
|
||||
* @memberof UmbPartialViewDetailServerDataSource
|
||||
*/
|
||||
delete(path: string): Promise<DataSourceResponse> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.deleteScript({ path }));
|
||||
}
|
||||
|
||||
getItems(keys: Array<string>): Promise<DataSourceResponse<PartialViewItemResponseModel[]>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.getScriptItem({ path: keys }));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
CreateFolderRequestModel,
|
||||
FolderModelBaseModel,
|
||||
FolderResponseModel,
|
||||
ScriptResource,
|
||||
} from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { DataSourceResponse, UmbFolderDataSource } from '@umbraco-cms/backoffice/repository';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
//! this is of any type in the backend-api
|
||||
export type ScriptsGetFolderResponse = { path: string; parentPath: string; name: string };
|
||||
|
||||
export class UmbScriptsFolderServerDataSource implements UmbFolderDataSource {
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
}
|
||||
createScaffold(parentId: string | null): Promise<DataSourceResponse<FolderResponseModel>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
get(unique: string): Promise<DataSourceResponse<ScriptsGetFolderResponse>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.getScriptFolder({ path: unique }));
|
||||
}
|
||||
insert(requestBody: CreateFolderRequestModel): Promise<DataSourceResponse<string>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.postScriptFolder({ requestBody }));
|
||||
}
|
||||
update(unique: string, data: CreateFolderRequestModel): Promise<DataSourceResponse<FolderModelBaseModel>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
delete(path: string): Promise<DataSourceResponse<unknown>> {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.deleteScriptFolder({ path }));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ScriptsTreeDataSource } from './index.js';
|
||||
import { ScriptResource, ProblemDetails } from '@umbraco-cms/backoffice/backend-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
|
||||
|
||||
export class UmbScriptsTreeServerDataSource implements ScriptsTreeDataSource {
|
||||
#host: UmbControllerHostElement;
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
this.#host = host;
|
||||
}
|
||||
|
||||
async getRootItems() {
|
||||
return tryExecuteAndNotify(this.#host, ScriptResource.getTreeScriptRoot({}));
|
||||
}
|
||||
|
||||
async getChildrenOf({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
}: {
|
||||
path?: string | undefined;
|
||||
skip?: number | undefined;
|
||||
take?: number | undefined;
|
||||
}) {
|
||||
if (!path) {
|
||||
const error: ProblemDetails = { title: 'Path is missing' };
|
||||
return error;
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
ScriptResource.getTreeScriptChildren({
|
||||
path,
|
||||
skip,
|
||||
take,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async getItem(path: Array<string>) {
|
||||
if (!path) {
|
||||
const error: ProblemDetails = { title: 'Paths are missing' };
|
||||
return error;
|
||||
}
|
||||
|
||||
return tryExecuteAndNotify(
|
||||
this.#host,
|
||||
ScriptResource.getScriptItem({
|
||||
path,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
SCRIPTS_ENTITY_TYPE,
|
||||
SCRIPTS_REPOSITORY_ALIAS,
|
||||
SCRIPTS_ROOT_ENTITY_TYPE,
|
||||
SCRIPTS_TREE_ALIAS,
|
||||
} from '../config.js';
|
||||
import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const tree: ManifestTree = {
|
||||
type: 'tree',
|
||||
alias: SCRIPTS_TREE_ALIAS,
|
||||
name: 'Scripts Tree',
|
||||
weight: 30,
|
||||
meta: {
|
||||
repositoryAlias: SCRIPTS_REPOSITORY_ALIAS,
|
||||
},
|
||||
};
|
||||
|
||||
const treeItem: ManifestTreeItem = {
|
||||
type: 'treeItem',
|
||||
kind: 'fileSystem',
|
||||
alias: 'Umb.TreeItem.Scripts',
|
||||
name: 'Scripts Tree Item',
|
||||
meta: {
|
||||
entityTypes: [SCRIPTS_ROOT_ENTITY_TYPE, SCRIPTS_ENTITY_TYPE],
|
||||
},
|
||||
};
|
||||
|
||||
export const manifests = [tree, treeItem];
|
||||
@@ -0,0 +1,35 @@
|
||||
import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
|
||||
import type { ManifestWorkspace, ManifestWorkspaceAction } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
const workspace: ManifestWorkspace = {
|
||||
type: 'workspace',
|
||||
alias: 'Umb.Workspace.Scripts',
|
||||
name: 'Partial View Workspace',
|
||||
loader: () => import('./scripts-workspace.element.js'),
|
||||
meta: {
|
||||
entityType: 'partial-view',
|
||||
},
|
||||
};
|
||||
|
||||
const workspaceActions: Array<ManifestWorkspaceAction> = [
|
||||
{
|
||||
type: 'workspaceAction',
|
||||
alias: 'Umb.WorkspaceAction.Scripts.Save',
|
||||
name: 'Save Partial View',
|
||||
weight: 70,
|
||||
meta: {
|
||||
look: 'primary',
|
||||
color: 'positive',
|
||||
label: 'Save',
|
||||
api: UmbSaveWorkspaceAction,
|
||||
},
|
||||
conditions: [
|
||||
{
|
||||
alias: 'Umb.Condition.WorkspaceAlias',
|
||||
match: workspace.alias,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const manifests = [workspace, ...workspaceActions];
|
||||
@@ -0,0 +1,171 @@
|
||||
import { UmbScriptsWorkspaceContext } from './scripts-workspace.context.js';
|
||||
import type { UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor';
|
||||
import { UUITextStyles, UUIInputElement } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { css, html, customElement, query, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
|
||||
import { Subject, debounceTime } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
@customElement('umb-scripts-workspace-edit')
|
||||
export class UmbScriptsWorkspaceEditElement extends UmbLitElement {
|
||||
#name: string | undefined = '';
|
||||
@state()
|
||||
private get _name() {
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
private set _name(value) {
|
||||
this.#name = value?.replace('.js', '');
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
@state()
|
||||
private _content?: string | null = '';
|
||||
|
||||
@state()
|
||||
private _path?: string | null = '';
|
||||
|
||||
@state()
|
||||
private _ready?: boolean = false;
|
||||
|
||||
@query('umb-code-editor')
|
||||
private _codeEditor?: UmbCodeEditorElement;
|
||||
|
||||
#scriptsWorkspaceContext?: UmbScriptsWorkspaceContext;
|
||||
private _modalContext?: UmbModalManagerContext;
|
||||
|
||||
#isNew = false;
|
||||
|
||||
private inputQuery$ = new Subject<string>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => {
|
||||
this._modalContext = instance;
|
||||
});
|
||||
|
||||
this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => {
|
||||
this.#scriptsWorkspaceContext = workspaceContext as UmbScriptsWorkspaceContext;
|
||||
this.observe(this.#scriptsWorkspaceContext.name, (name) => {
|
||||
this._name = name;
|
||||
});
|
||||
|
||||
this.observe(this.#scriptsWorkspaceContext.content, (content) => {
|
||||
this._content = content;
|
||||
});
|
||||
|
||||
this.observe(this.#scriptsWorkspaceContext.path, (path) => {
|
||||
this._path = path;
|
||||
});
|
||||
|
||||
this.observe(this.#scriptsWorkspaceContext.isNew, (isNew) => {
|
||||
this.#isNew = !!isNew;
|
||||
});
|
||||
|
||||
this.observe(this.#scriptsWorkspaceContext.isCodeEditorReady, (isReady) => {
|
||||
this._ready = isReady;
|
||||
});
|
||||
|
||||
this.inputQuery$.pipe(debounceTime(250)).subscribe((nameInputValue: string) => {
|
||||
this.#scriptsWorkspaceContext?.setName(`${nameInputValue}.cshtml`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#onNameInput(event: Event) {
|
||||
const target = event.target as UUIInputElement;
|
||||
const value = target.value as string;
|
||||
this.inputQuery$.next(value);
|
||||
}
|
||||
|
||||
#onCodeEditorInput(event: Event) {
|
||||
const target = event.target as UmbCodeEditorElement;
|
||||
const value = target.code as string;
|
||||
this.#scriptsWorkspaceContext?.setContent(value);
|
||||
}
|
||||
|
||||
#renderCodeEditor() {
|
||||
return html`<umb-code-editor
|
||||
language="razor"
|
||||
id="content"
|
||||
.code=${this._content ?? ''}
|
||||
@input=${this.#onCodeEditorInput}></umb-code-editor>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<umb-workspace-editor alias="Umb.Workspace.Template">
|
||||
<div id="workspace-header" slot="header">
|
||||
<uui-input
|
||||
placeholder="Enter name..."
|
||||
.value=${this._name}
|
||||
@input=${this.#onNameInput}
|
||||
label="template name"></uui-input>
|
||||
<small>Scripts/${this._path}</small>
|
||||
</div>
|
||||
<uui-box>
|
||||
${this._ready
|
||||
? this.#renderCodeEditor()
|
||||
: html`<div id="loader-container">
|
||||
<uui-loader></uui-loader>
|
||||
</div>`}
|
||||
</uui-box>
|
||||
</umb-workspace-editor>`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#loader-container {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-height: calc(100dvh - 360px);
|
||||
}
|
||||
|
||||
umb-code-editor {
|
||||
--editor-height: calc(100dvh - 300px);
|
||||
}
|
||||
|
||||
uui-box {
|
||||
min-height: calc(100dvh - 300px);
|
||||
margin: var(--uui-size-layout-1);
|
||||
--uui-box-default-padding: 0;
|
||||
/* remove header border bottom as code editor looks better in this box */
|
||||
--uui-color-divider-standalone: transparent;
|
||||
}
|
||||
|
||||
#workspace-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
uui-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#code-editor-menu-container uui-icon:not([name='umb:delete']) {
|
||||
margin-right: var(--uui-size-space-3);
|
||||
}
|
||||
|
||||
#code-editor-menu-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--uui-size-space-3);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
export default UmbScriptsWorkspaceEditElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-scripts-workspace-edit': UmbScriptsWorkspaceEditElement;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { ScriptDetails } from '../config.js';
|
||||
import { UmbScriptsRepository } from '../repository/scripts.repository.js';
|
||||
import { createObservablePart, UmbBooleanState, UmbDeepState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||
import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor';
|
||||
import { UpdatePartialViewRequestModel } from '@umbraco-cms/backoffice/backend-api';
|
||||
|
||||
export class UmbScriptsWorkspaceContext extends UmbWorkspaceContext<UmbScriptsRepository, ScriptDetails> {
|
||||
getEntityId(): string | undefined {
|
||||
return this.getData()?.path;
|
||||
}
|
||||
getEntityType(): string {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
save(): Promise<void> {
|
||||
const script = this.getData();
|
||||
|
||||
if (!script) return Promise.reject('Something went wrong, there is no data for partial view you want to save...');
|
||||
if (this.getIsNew()) {
|
||||
const createRequestBody = {
|
||||
name: script.name,
|
||||
content: script.content,
|
||||
parentPath: script.path + '/',
|
||||
};
|
||||
|
||||
this.repository.create(createRequestBody);
|
||||
console.log('create');
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!script.path) return Promise.reject('There is no path');
|
||||
const updateRequestBody: UpdatePartialViewRequestModel = {
|
||||
name: script.name,
|
||||
existingPath: script.path,
|
||||
content: script.content,
|
||||
};
|
||||
this.repository.save(script.path, updateRequestBody);
|
||||
return Promise.resolve();
|
||||
}
|
||||
destroy(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
#data = new UmbDeepState<ScriptDetails | undefined>(undefined);
|
||||
data = this.#data.asObservable();
|
||||
name = createObservablePart(this.#data, (data) => data?.name);
|
||||
content = createObservablePart(this.#data, (data) => data?.content);
|
||||
path = createObservablePart(this.#data, (data) => data?.path);
|
||||
|
||||
#isCodeEditorReady = new UmbBooleanState(false);
|
||||
isCodeEditorReady = this.#isCodeEditorReady.asObservable();
|
||||
|
||||
constructor(host: UmbControllerHostElement) {
|
||||
super(host, 'Umb.Workspace.PartialViews', new UmbScriptsRepository(host));
|
||||
this.#loadCodeEditor();
|
||||
}
|
||||
|
||||
async #loadCodeEditor() {
|
||||
try {
|
||||
await loadCodeEditor();
|
||||
this.#isCodeEditorReady.next(true);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data.getValue();
|
||||
}
|
||||
|
||||
setName(value: string) {
|
||||
this.#data.next({ ...this.#data.value, name: value });
|
||||
}
|
||||
|
||||
setContent(value: string) {
|
||||
this.#data.next({ ...this.#data.value, content: value });
|
||||
}
|
||||
|
||||
async load(entityKey: string) {
|
||||
const { data } = await this.repository.requestByKey(entityKey);
|
||||
if (data) {
|
||||
this.setIsNew(false);
|
||||
this.#data.next(data);
|
||||
}
|
||||
}
|
||||
|
||||
async create(parentKey: string | null, name = 'Empty') {
|
||||
const { data } = await this.repository.createScaffold(parentKey, name);
|
||||
const newPartial = {
|
||||
...data,
|
||||
name: '',
|
||||
path: parentKey ?? '',
|
||||
};
|
||||
if (!data) return;
|
||||
this.setIsNew(true);
|
||||
this.#data.next(newPartial);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { UmbScriptsWorkspaceContext } from './scripts-workspace.context.js';
|
||||
import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui';
|
||||
import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
|
||||
import { UmbRoute, IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
import './scripts-workspace-edit.element.js';
|
||||
|
||||
import { UmbWorkspaceIsNewRedirectController } from '@umbraco-cms/backoffice/workspace';
|
||||
|
||||
@customElement('umb-scripts-workspace')
|
||||
export class UmbScriptsWorkspaceElement extends UmbLitElement {
|
||||
#scriptsWorkspaceContext = new UmbScriptsWorkspaceContext(this);
|
||||
|
||||
#element = document.createElement('umb-scripts-workspace-edit');
|
||||
|
||||
@state()
|
||||
_routes: UmbRoute[] = [
|
||||
{
|
||||
path: 'create/:parentKey',
|
||||
component: () => this.#element,
|
||||
setup: async (component: PageComponent, info: IRoutingInfo) => {
|
||||
const parentKey = info.match.params.parentKey;
|
||||
const decodePath = decodeURIComponent(parentKey);
|
||||
this.#scriptsWorkspaceContext.create(decodePath === 'null' ? null : parentKey);
|
||||
|
||||
new UmbWorkspaceIsNewRedirectController(
|
||||
this,
|
||||
this.#scriptsWorkspaceContext,
|
||||
this.shadowRoot!.querySelector('umb-router-slot')!,
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'edit/:key',
|
||||
component: () => this.#element,
|
||||
setup: (component: PageComponent, info: IRoutingInfo) => {
|
||||
const key = info.match.params.key;
|
||||
const decodePath = decodeURIComponent(key).replace('-js', '.js');
|
||||
this.#scriptsWorkspaceContext.load(decodePath);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
return html`<umb-router-slot .routes=${this._routes}></umb-router-slot>`;
|
||||
}
|
||||
|
||||
static styles = [UUITextStyles, css``];
|
||||
}
|
||||
|
||||
export default UmbScriptsWorkspaceElement;
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-scripts-workspace': UmbScriptsWorkspaceElement;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user