diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts index 26f845a4b1..f79da572c9 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet.data.ts @@ -87,7 +87,7 @@ h1 { path: 'Folder 1/Stylesheet File 3.css', name: 'Stylesheet File 3.css', type: 'stylesheet', - hasChildren: true, + hasChildren: false, isFolder: false, content: `h1 { color: pink; @@ -225,9 +225,10 @@ ${rule.selector} { } insertStyleSheet(item: CreateTextFileViewModelBaseModel) { + const parentPath = item.parentPath ? `${item.parentPath}/` : ''; const newItem: StylesheetDBItem = { ...item, - path: `${item.parentPath}/${item.name}.css`, + path: `${parentPath}${item.name}`, isFolder: false, hasChildren: false, type: 'stylesheet', @@ -238,7 +239,6 @@ ${rule.selector} { return newItem; } - insert(item: StylesheetDBItem) { const exits = this.data.find((i) => i.path === item.path); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts index 3ae10d1f33..dba7083fa2 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/stylesheet.handlers.ts @@ -39,8 +39,8 @@ const detailHandlers = [ const response = umbStylesheetData.getStylesheet(path); return res(ctx.status(200), ctx.json(response)); }), - rest.post(umbracoPath('/stylesheet'), (req, res, ctx) => { - const requestBody = req.json() as CreateTextFileViewModelBaseModel; + rest.post(umbracoPath('/stylesheet'), async (req, res, ctx) => { + const requestBody = (await req.json()) as CreateTextFileViewModelBaseModel; if (!requestBody) return res(ctx.status(400, 'no body found')); const response = umbStylesheetData.insertStyleSheet(requestBody); return res(ctx.status(200), ctx.json(response)); @@ -53,7 +53,7 @@ const detailHandlers = [ return res(ctx.status(200), ctx.json(response)); }), rest.put(umbracoPath('/stylesheet'), async (req, res, ctx) => { - const requestBody = await req.json() as UpdateStylesheetRequestModel; + const requestBody = (await req.json()) as UpdateStylesheetRequestModel; if (!requestBody) return res(ctx.status(400, 'no body found')); umbStylesheetData.updateData(requestBody); return res(ctx.status(200)); @@ -75,7 +75,6 @@ const detailHandlers = [ }), ]; - const rulesHandlers = [ rest.post(umbracoPath('/stylesheet/rich-text/extract-rules'), async (req, res, ctx) => { const requestBody = req.json() as ExtractRichTextStylesheetRulesRequestModel; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/tree/data-type-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/tree/data-type-tree.repository.ts index 4c0ed2bdde..e3686a7fdf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/data-type/tree/data-type-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/data-type/tree/data-type-tree.repository.ts @@ -1,4 +1,4 @@ -import { UmbEntityTreeRepositoryBase } from '../../tree/entity-tree.repository.js'; +import { UmbTreeRepositoryBase } from '../../tree/tree-repository-base.js'; import { DATA_TYPE_ROOT_ENTITY_TYPE } from '../entities.js'; import { UmbDataTypeTreeServerDataSource } from './data-type.tree.server.data.js'; import { UMB_DATA_TYPE_TREE_STORE_CONTEXT } from './data-type.tree.store.js'; @@ -7,7 +7,7 @@ import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export class UmbDataTypeTreeRepository - extends UmbEntityTreeRepositoryBase + extends UmbTreeRepositoryBase implements UmbApi { constructor(host: UmbControllerHost) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts index ee94cf6c95..d9323dd5be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-main-views/section-main-views.element.ts @@ -1,4 +1,4 @@ -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbRoute, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; import { @@ -110,8 +110,8 @@ export class UmbSectionMainViewElement extends UmbLitElement { const dashboardPath = this.#constructDashboardPath(dashboard); return html` `; })} @@ -129,8 +129,8 @@ export class UmbSectionMainViewElement extends UmbLitElement { const viewPath = this.#constructViewPath(view); return html` ${viewName} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/store/file-system-item.store.ts b/src/Umbraco.Web.UI.Client/src/packages/core/store/file-system-item.store.ts new file mode 100644 index 0000000000..c766960bbf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/store/file-system-item.store.ts @@ -0,0 +1,31 @@ +import { UmbStoreBase } from './store-base.js'; +import { UmbItemStore } from './item-store.interface.js'; +import type { FileItemResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +/** + * @export + * @class UmbFileSystemItemStore + * @extends {UmbStoreBase} + * @description - Data Store for File system items + */ + +export class UmbFileSystemItemStore + extends UmbStoreBase + implements UmbItemStore +{ + constructor(host: UmbControllerHost, storeAlias: string) { + super(host, storeAlias, new UmbArrayState([], (x) => x.path)); + } + + /** + * Return an observable to observe file system items + * @param {Array} paths + * @return {*} + * @memberof UmbFileSystemItemStore + */ + items(paths: Array) { + return this._data.asObservablePart((items) => items.filter((item) => paths.includes(item.path ?? ''))); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts index adc28974bc..54e9c0c197 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/store/index.ts @@ -3,3 +3,4 @@ export * from './store-base.js'; export * from './store.interface.js'; export * from './store.js'; export * from './entity-item.store.js'; +export * from './file-system-item.store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/file-system-tree.store.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/file-system-tree.store.ts index 64be709a21..db22d8e8a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/file-system-tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/file-system-tree.store.ts @@ -31,9 +31,11 @@ export class UmbFileSystemTreeStore * @memberof UmbFileSystemTreeStore */ childrenOf(parentPath: string | null) { - return this._data.asObservablePart((items) => - items.filter((item) => item.path?.startsWith(parentPath + '/') || parentPath === null), - ); + if (parentPath === null) { + return this.rootItems; + } + + return this._data.asObservablePart((items) => items.filter((item) => item.path?.startsWith(parentPath + '/'))); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts index a4dffc04f6..5f775743d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/index.ts @@ -13,4 +13,4 @@ export * from './tree-store.interface.js'; export * from './entity-tree-store.js'; export * from './file-system-tree.store.js'; -export { UmbEntityTreeRepositoryBase } from './entity-tree.repository.js'; +export { UmbTreeRepositoryBase } from './tree-repository-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-tree.repository.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts index fd7e6146c9..28741c462f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository-base.ts @@ -7,7 +7,7 @@ import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -export class UmbEntityTreeRepositoryBase< +export class UmbTreeRepositoryBase< TreeItemType extends UmbEntityTreeItemModel, TreeRootType extends UmbEntityTreeRootModel, > @@ -34,7 +34,7 @@ export class UmbEntityTreeRepositoryBase< /** * Request the tree root item * @return {*} - * @memberof UmbEntityTreeRepositoryBase + * @memberof UmbTreeRepositoryBase */ async requestTreeRoot() { if (!this.#treeSource.getTreeRoot?.()) { @@ -47,7 +47,7 @@ export class UmbEntityTreeRepositoryBase< /** * Requests root items of a tree * @return {*} - * @memberof UmbEntityTreeRepositoryBase + * @memberof UmbTreeRepositoryBase */ async requestRootTreeItems() { await this._init; @@ -63,27 +63,27 @@ export class UmbEntityTreeRepositoryBase< /** * Requests tree items of a given parent - * @param {(string | null)} parentId + * @param {(string | null)} parentUnique * @return {*} - * @memberof UmbEntityTreeRepositoryBase + * @memberof UmbTreeRepositoryBase */ - async requestTreeItemsOf(parentId: string | null) { - if (parentId === undefined) throw new Error('Parent id is missing'); + async requestTreeItemsOf(parentUnique: string | null) { + if (parentUnique === undefined) throw new Error('Parent unique is missing'); await this._init; - const { data, error } = await this.#treeSource.getChildrenOf(parentId); + const { data, error } = await this.#treeSource.getChildrenOf(parentUnique); if (data) { this._treeStore!.appendItems(data.items); } - return { data, error, asObservable: () => this._treeStore!.childrenOf(parentId) }; + return { data, error, asObservable: () => this._treeStore!.childrenOf(parentUnique) }; } /** * Returns a promise with an observable of tree root items * @return {*} - * @memberof UmbEntityTreeRepositoryBase + * @memberof UmbTreeRepositoryBase */ async rootTreeItems() { await this._init; @@ -92,13 +92,13 @@ export class UmbEntityTreeRepositoryBase< /** * Returns a promise with an observable of children items of a given parent - * @param {(string | null)} parentId + * @param {(string | null)} parentUnique * @return {*} - * @memberof UmbEntityTreeRepositoryBase + * @memberof UmbTreeRepositoryBase */ - async treeItemsOf(parentId: string | null) { - if (parentId === undefined) throw new Error('Parent id is missing'); + async treeItemsOf(parentUnique: string | null) { + if (parentUnique === undefined) throw new Error('Parent unique is missing'); await this._init; - return this._treeStore!.childrenOf(parentId); + return this._treeStore!.childrenOf(parentUnique); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts index 9ab0f433d0..db89e96c87 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-repository.interface.ts @@ -13,17 +13,26 @@ export interface UmbTreeRepository< error?: ProblemDetails; }>; + requestTreeItemsOf: (parentUnique: string | null) => Promise<{ + data?: UmbPagedData; + error?: ProblemDetails; + asObservable?: () => Observable; + }>; + + treeItemsOf: (parentUnique: string | null) => Promise>; + + /* TODO: remove this. It is not used client side. + Logic to call the root endpoint should be in the data source + because it is a server decision to split them + */ requestRootTreeItems: () => Promise<{ data?: UmbPagedData; error?: ProblemDetails; asObservable?: () => Observable; }>; - requestTreeItemsOf: (parentUnique: string | null) => Promise<{ - data?: UmbPagedData; - error?: ProblemDetails; - asObservable?: () => Observable; - }>; + // TODO: remove + rootTreeItems: () => Promise>; // TODO: remove this when all repositories are migrated to the new interface items interface requestItemsLegacy?: (uniques: string[]) => Promise<{ @@ -32,10 +41,6 @@ export interface UmbTreeRepository< asObservable?: () => Observable; }>; - rootTreeItems: () => Promise>; - - treeItemsOf: (parentUnique: string | null) => Promise>; - // TODO: remove this when all repositories are migrated to the new items interface itemsLegacy?: (uniques: string[]) => Promise>; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/create/create.action.ts index a6adcf55ac..7eed91c078 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/entity-actions/create/create.action.ts @@ -1,54 +1,16 @@ import { UmbDictionaryRepository } from '../../repository/dictionary.repository.js'; -import { UmbTextStyles } from "@umbraco-cms/backoffice/style"; -import { UmbSectionSidebarContext, UMB_SECTION_SIDEBAR_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/section'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import { - UmbModalManagerContext, - UMB_MODAL_MANAGER_CONTEXT_TOKEN, - UMB_CREATE_DICTIONARY_MODAL, -} from '@umbraco-cms/backoffice/modal'; export default class UmbCreateDictionaryEntityAction extends UmbEntityActionBase { static styles = [UmbTextStyles]; - #modalContext?: UmbModalManagerContext; - - #sectionSidebarContext!: UmbSectionSidebarContext; - constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string) { super(host, repositoryAlias, unique); - - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (instance) => { - this.#modalContext = instance; - }); - - this.consumeContext(UMB_SECTION_SIDEBAR_CONTEXT_TOKEN, (instance) => { - this.#sectionSidebarContext = instance; - }); } async execute() { - // TODO: what to do if modal service is not available? - if (!this.#modalContext) return; - if (!this.repository) return; - - // TODO: how can we get the current entity detail in the modal? Passing the observable - // feels a bit hacky. Works, but hacky. - const modalContext = this.#modalContext?.open(UMB_CREATE_DICTIONARY_MODAL, { - parentId: this.unique, - parentName: this.#sectionSidebarContext.headline, - }); - - const { name, parentId } = await modalContext.onSubmit(); - if (!name || parentId === undefined) return; - - const { data: url } = await this.repository.create({ name, parentId }); - if (!url) return; - - //TODO: Why do we need to extract the id like this? - const id = url.substring(url.lastIndexOf('/') + 1); - - history.pushState({}, '', `/section/dictionary/workspace/dictionary-item/edit/${id}`); + history.pushState({}, '', `/section/dictionary/workspace/dictionary-item/create/${this.unique ?? 'null'}`); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts index ce1810c9e2..b38dbedb43 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/repository/dictionary.repository.ts @@ -111,6 +111,20 @@ export class UmbDictionaryRepository return { data, error, asObservable: () => this.#treeStore!.items(ids) }; } + async requestItems(ids: Array) { + // TODO: There is a bug where the item gets removed from the tree before we confirm the delete via the modal. It doesn't delete the item unless we confirm the delete. + if (!ids) throw new Error('Dictionary Ids are missing'); + await this.#init; + + const { data, error } = await this.#treeSource.getItems(ids); + + if (data) { + this.#treeStore?.appendItems(data); + } + + return { data, error, asObservable: () => this.#treeStore!.items(ids) }; + } + async rootTreeItems() { await this.#init; return this.#treeStore!.rootItems; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts index 1a14c0fd4a..0bb29045ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts @@ -68,9 +68,8 @@ export class UmbDictionaryWorkspaceContext const { data } = await this.repository.createScaffold(parentId); if (!data) return; this.setIsNew(true); - // TODO: This is a hack to get around the fact that the data is not typed correctly. - // Create and response models are different. We need to look into this. - this.#data.next(data as unknown as DictionaryItemResponseModel); + + this.#data.next(data as DictionaryItemResponseModel); } async save() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.element.ts index d4246170bf..be1cbe2647 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.element.ts @@ -26,7 +26,6 @@ export class UmbWorkspaceDictionaryElement extends UmbLitElement { component: () => this.#element, setup: async (_component, info) => { const parentId = info.match.params.parentId === 'null' ? null : info.match.params.parentId; - await this.#workspaceContext.create(parentId); new UmbWorkspaceIsNewRedirectController( diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts index cc29803cbc..8fa85338e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/tree/document-type-tree.repository.ts @@ -2,12 +2,12 @@ import { DOCUMENT_TYPE_ROOT_ENTITY_TYPE } from '../index.js'; import { UmbDocumentTypeTreeServerDataSource } from './document-type.tree.server.data-source.js'; import { UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT } from './document-type.tree.store.js'; import { UmbDocumentTypeTreeItemModel, UmbDocumentTypeTreeRootModel } from './types.js'; -import { UmbEntityTreeRepositoryBase } from '@umbraco-cms/backoffice/tree'; +import { UmbTreeRepositoryBase } from '@umbraco-cms/backoffice/tree'; import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export class UmbDocumentTypeTreeRepository - extends UmbEntityTreeRepositoryBase + extends UmbTreeRepositoryBase implements UmbApi { constructor(host: UmbControllerHost) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/config.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/config.ts deleted file mode 100644 index cb6e44810e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const STYLESHEET_ENTITY_TYPE = 'stylesheet'; - -export const STYLESHEET_ROOT_ENTITY_TYPE = 'stylesheet-root'; -export const STYLESHEET_FOLDER_ENTITY_TYPE = 'stylesheet-folder'; -export const STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE = 'stylesheet-folder-empty'; - -export const STYLESHEET_REPOSITORY_ALIAS = 'Umb.Repository.Stylesheet'; - -export const STYLESHEET_TREE_ALIAS = 'Umb.Tree.Stylesheet'; - -export const UMB_STYLESHEET_TREE_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.Stylesheet.Tree'; -export const UMB_STYLESHEET_STORE_CONTEXT_TOKEN_ALIAS = 'Umb.Store.Stylesheet'; - -export const STYLESHEET_STORE_ALIAS = 'Umb.Store.Stylesheet'; -export const STYLESHEET_TREE_STORE_ALIAS = 'Umb.Store.StylesheetTree'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create-rtf.action.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create-rtf.action.ts index 9bc1e2ec28..de93d19cc1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create-rtf.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create-rtf.action.ts @@ -7,6 +7,11 @@ export class UmbCreateRTFStylesheetAction }> e } async execute() { + if (this.unique !== null) { + // Note: %2F is a slash (/) + this.unique = this.unique.replace(/\//g, '%2F'); + } + history.pushState( null, '', diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts index b5b4e71368..a8ed2d1327 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts @@ -7,6 +7,11 @@ export class UmbCreateStylesheetAction }> exte } async execute() { + if (this.unique !== null) { + // Note: %2F is a slash (/) + this.unique = this.unique.replace(/\//g, '%2F'); + } + history.pushState(null, '', `section/settings/workspace/stylesheet/create/${this.unique ?? 'null'}/view/code`); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/manifests.ts index d21cd9a82b..4c1da9ff30 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/manifests.ts @@ -1,10 +1,11 @@ import { - STYLESHEET_ENTITY_TYPE, - STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, - STYLESHEET_FOLDER_ENTITY_TYPE, - STYLESHEET_REPOSITORY_ALIAS, - STYLESHEET_ROOT_ENTITY_TYPE, -} from '../config.js'; + UMB_STYLESHEET_ENTITY_TYPE, + UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, + UMB_STYLESHEET_FOLDER_ENTITY_TYPE, + UMB_STYLESHEET_ROOT_ENTITY_TYPE, +} from '../entity-type.js'; + +import { UMB_STYLESHEET_REPOSITORY_ALIAS } from '../repository/index.js'; import { UmbCreateRTFStylesheetAction } from './create/create-rtf.action.js'; import { UmbCreateStylesheetAction } from './create/create.action.js'; import { @@ -26,8 +27,8 @@ const stylesheetActions: Array = [ meta: { icon: 'icon-trash', label: 'Delete', - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, - entityTypes: [STYLESHEET_ENTITY_TYPE], + repositoryAlias: UMB_STYLESHEET_REPOSITORY_ALIAS, + entityTypes: [UMB_STYLESHEET_ENTITY_TYPE], }, }, ]; @@ -43,8 +44,12 @@ const stylesheetFolderActions: Array = [ meta: { icon: 'icon-script', label: 'New stylesheet file', - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, - entityTypes: [STYLESHEET_FOLDER_ENTITY_TYPE, STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, STYLESHEET_ROOT_ENTITY_TYPE], + repositoryAlias: UMB_STYLESHEET_REPOSITORY_ALIAS, + entityTypes: [ + UMB_STYLESHEET_FOLDER_ENTITY_TYPE, + UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, + UMB_STYLESHEET_ROOT_ENTITY_TYPE, + ], }, }, { @@ -55,8 +60,12 @@ const stylesheetFolderActions: Array = [ meta: { icon: 'icon-script', label: 'New Rich Text Editor style sheet file', - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, - entityTypes: [STYLESHEET_FOLDER_ENTITY_TYPE, STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, STYLESHEET_ROOT_ENTITY_TYPE], + repositoryAlias: UMB_STYLESHEET_REPOSITORY_ALIAS, + entityTypes: [ + UMB_STYLESHEET_FOLDER_ENTITY_TYPE, + UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, + UMB_STYLESHEET_ROOT_ENTITY_TYPE, + ], }, }, { @@ -67,8 +76,8 @@ const stylesheetFolderActions: Array = [ meta: { icon: 'icon-trash', label: 'Remove folder', - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, - entityTypes: [STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE], + repositoryAlias: UMB_STYLESHEET_REPOSITORY_ALIAS, + entityTypes: [UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE], }, }, { @@ -79,8 +88,12 @@ const stylesheetFolderActions: Array = [ meta: { icon: 'icon-add', label: 'Create folder', - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, - entityTypes: [STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, STYLESHEET_FOLDER_ENTITY_TYPE, STYLESHEET_ROOT_ENTITY_TYPE], + repositoryAlias: UMB_STYLESHEET_REPOSITORY_ALIAS, + entityTypes: [ + UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE, + UMB_STYLESHEET_FOLDER_ENTITY_TYPE, + UMB_STYLESHEET_ROOT_ENTITY_TYPE, + ], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-type.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-type.ts new file mode 100644 index 0000000000..8dbd996a3e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-type.ts @@ -0,0 +1,5 @@ +export const UMB_STYLESHEET_ENTITY_TYPE = 'stylesheet'; +export const UMB_STYLESHEET_ROOT_ENTITY_TYPE = 'stylesheet-root'; + +export const UMB_STYLESHEET_FOLDER_ENTITY_TYPE = 'stylesheet-folder'; +export const UMB_STYLESHEET_FOLDER_EMPTY_ENTITY_TYPE = 'stylesheet-folder-empty'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/index.ts index ec91672f86..c0657c8bf8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/index.ts @@ -3,3 +3,4 @@ import { StylesheetResponseModel } from '@umbraco-cms/backoffice/backend-api'; export type StylesheetDetails = StylesheetResponseModel; export * from './repository/index.js'; +export { UmbStylesheetTreeRepository } from './tree/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu-item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu-item/manifests.ts index 287fa5a01b..4109f5071c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu-item/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/menu-item/manifests.ts @@ -1,4 +1,4 @@ -import { STYLESHEET_TREE_ALIAS } from '../tree/manifests.js'; +import { UMB_STYLESHEET_TREE_ALIAS } from '../tree/manifests.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; const menuItem: ManifestTypes = { @@ -10,7 +10,7 @@ const menuItem: ManifestTypes = { meta: { label: 'Stylesheets', icon: 'icon-folder', - treeAlias: STYLESHEET_TREE_ALIAS, + treeAlias: UMB_STYLESHEET_TREE_ALIAS, menus: ['Umb.Menu.Templating'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/index.ts index c95f93399b..3ee1d14e58 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/index.ts @@ -1 +1,3 @@ export * from './stylesheet.repository.js'; +export * from './item/index.js'; +export { UMB_STYLESHEET_REPOSITORY_ALIAS } from './manifests.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/index.ts new file mode 100644 index 0000000000..929e43c0f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/index.ts @@ -0,0 +1,3 @@ +export { UmbStylesheetItemRepository } from './stylesheet-item.repository.js'; +export { UMB_STYLESHEET_ITEM_REPOSITORY_ALIAS, UMB_STYLESHEET_ITEM_STORE_ALIAS } from './manifests.js'; +export { UMB_STYLESHEET_ITEM_STORE_CONTEXT } from './stylesheet-item.store.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/manifests.ts new file mode 100644 index 0000000000..97aaa9753e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/manifests.ts @@ -0,0 +1,22 @@ +import { UmbStylesheetItemStore } from './stylesheet-item.store.js'; +import { UmbStylesheetItemRepository } from './stylesheet-item.repository.js'; +import type { ManifestRepository, ManifestItemStore } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_STYLESHEET_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.Stylesheet.Item'; +export const UMB_STYLESHEET_ITEM_STORE_ALIAS = 'Umb.ItemStore.Stylesheet'; + +const repository: ManifestRepository = { + type: 'repository', + alias: UMB_STYLESHEET_ITEM_REPOSITORY_ALIAS, + name: 'Stylesheet Item Repository', + api: UmbStylesheetItemRepository, +}; + +const itemStore: ManifestItemStore = { + type: 'itemStore', + alias: 'Umb.ItemStore.Stylesheet', + name: 'Stylesheet Item Store', + api: UmbStylesheetItemStore, +}; + +export const manifests = [repository, itemStore]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.repository.ts new file mode 100644 index 0000000000..9cc0cc8198 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.repository.ts @@ -0,0 +1,11 @@ +import { UmbStylesheetItemServerDataSource } from './stylesheet-item.server.data-source.js'; +import { UMB_STYLESHEET_ITEM_STORE_CONTEXT } from './stylesheet-item.store.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { StylesheetItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbItemRepositoryBase } from '@umbraco-cms/backoffice/repository'; + +export class UmbStylesheetItemRepository extends UmbItemRepositoryBase { + constructor(host: UmbControllerHost) { + super(host, UmbStylesheetItemServerDataSource, UMB_STYLESHEET_ITEM_STORE_CONTEXT); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.server.data-source.ts new file mode 100644 index 0000000000..2c65c2a56c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.server.data-source.ts @@ -0,0 +1,40 @@ +import type { UmbItemDataSource } from '@umbraco-cms/backoffice/repository'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { StylesheetItemResponseModel, StylesheetResource } from '@umbraco-cms/backoffice/backend-api'; + +/** + * A data source for stylesheet items that fetches data from the server + * @export + * @class UmbStylesheetItemServerDataSource + * @implements {UmbItemDataSource} + */ +export class UmbStylesheetItemServerDataSource implements UmbItemDataSource { + #host: UmbControllerHost; + + /** + * Creates an instance of UmbStylesheetItemServerDataSource. + * @param {UmbControllerHost} host + * @memberof UmbStylesheetItemServerDataSource + */ + constructor(host: UmbControllerHost) { + this.#host = host; + } + + /** + * Fetches the items for the given paths from the server + * @param {Array} paths + * @return {*} + * @memberof UmbStylesheetItemServerDataSource + */ + async getItems(paths: Array) { + if (!paths) throw new Error('Paths are missing'); + + return tryExecuteAndNotify( + this.#host, + StylesheetResource.getStylesheetItem({ + path: paths, + }), + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.store.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.store.ts new file mode 100644 index 0000000000..661cc27988 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/item/stylesheet-item.store.ts @@ -0,0 +1,24 @@ +import type { StylesheetItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbFileSystemItemStore } from '@umbraco-cms/backoffice/store'; + +/** + * @export + * @class UmbStylesheetItemStore + * @extends {UmbFileSystemItemStore} + * @description - Data Store for Stylesheet items + */ + +export class UmbStylesheetItemStore extends UmbFileSystemItemStore { + /** + * Creates an instance of UmbStylesheetItemStore. + * @param {UmbControllerHostElement} host + * @memberof UmbStylesheetItemStore + */ + constructor(host: UmbControllerHostElement) { + super(host, UMB_STYLESHEET_ITEM_STORE_CONTEXT.toString()); + } +} + +export const UMB_STYLESHEET_ITEM_STORE_CONTEXT = new UmbContextToken('UmbStylesheetItemStore'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/manifests.ts index e1ace39c01..184a4de42b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/manifests.ts @@ -1,22 +1,14 @@ -import { STYLESHEET_REPOSITORY_ALIAS, STYLESHEET_TREE_STORE_ALIAS } from '../config.js'; import { UmbStylesheetRepository } from './stylesheet.repository.js'; -import { UmbStylesheetTreeStore } from './stylesheet.tree.store.js'; -import { ManifestRepository, ManifestTreeStore } from '@umbraco-cms/backoffice/extension-registry'; +import { manifests as itemManifests } from './item/manifests.js'; +import { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry'; +export const UMB_STYLESHEET_REPOSITORY_ALIAS = 'Umb.Repository.Stylesheet'; const repository: ManifestRepository = { type: 'repository', - alias: STYLESHEET_REPOSITORY_ALIAS, + alias: UMB_STYLESHEET_REPOSITORY_ALIAS, name: 'Stylesheet Repository', api: UmbStylesheetRepository, }; - -const treeStore: ManifestTreeStore = { - type: 'treeStore', - alias: STYLESHEET_TREE_STORE_ALIAS, - name: 'Stylesheet Tree Store', - api: UmbStylesheetTreeStore, -}; - -export const manifests = [treeStore, repository]; +export const manifests = [repository, ...itemManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.repository.ts index b34b5a4605..9c5ec4ee0b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.repository.ts @@ -1,6 +1,4 @@ import { StylesheetDetails } from '../index.js'; -import { UmbStylesheetTreeStore, UMB_STYLESHEET_TREE_STORE_CONTEXT_TOKEN } from './stylesheet.tree.store.js'; -import { UmbStylesheetTreeServerDataSource } from './sources/stylesheet.tree.server.data.js'; import { UmbStylesheetServerDataSource } from './sources/stylesheet.server.data.js'; import { StylesheetGetFolderResponse, @@ -33,34 +31,24 @@ import { UpdateStylesheetRequestModel, UpdateTextFileViewModelBaseModel, } from '@umbraco-cms/backoffice/backend-api'; -import type { UmbFileSystemTreeRootModel } from '@umbraco-cms/backoffice/tree'; import { UmbApi } from '@umbraco-cms/backoffice/extension-api'; export class UmbStylesheetRepository extends UmbBaseController implements - UmbTreeRepository, UmbDetailRepository, UmbFolderRepository, UmbApi { #dataSource; - #treeDataSource; - #treeStore?: UmbStylesheetTreeStore; #folderDataSource; - #init; constructor(host: UmbControllerHostElement) { super(host); // TODO: figure out how spin up get the correct data source this.#dataSource = new UmbStylesheetServerDataSource(this); - this.#treeDataSource = new UmbStylesheetTreeServerDataSource(this); this.#folderDataSource = new UmbStylesheetFolderServerDataSource(this); - - this.#init = this.consumeContext(UMB_STYLESHEET_TREE_STORE_CONTEXT_TOKEN, (instance) => { - this.#treeStore = instance; - }).asPromise(); } //#region FOLDER: @@ -78,34 +66,34 @@ export class UmbStylesheetRepository async createFolder( folderRequest: CreateFolderRequestModel, ): Promise<{ data?: string | undefined; error?: ProblemDetails | undefined }> { - await this.#init; const req = { parentPath: folderRequest.parentId, name: folderRequest.name, }; const promise = this.#folderDataSource.insert(req); await promise; - this.requestTreeItemsOf(folderRequest.parentId ? folderRequest.parentId : null); + //this.requestTreeItemsOf(folderRequest.parentId ? folderRequest.parentId : null); return promise; } + async requestFolder( unique: string, ): Promise<{ data?: StylesheetGetFolderResponse | 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); + //this.requestTreeItemsOf(data?.parentPath ? data?.parentPath : null); return promise; } @@ -122,26 +110,29 @@ export class UmbStylesheetRepository async requestById(id: string): Promise> { if (!id) throw new Error('id is missing'); - await this.#init; const { data, error } = await this.#dataSource.get(id); return { data, error }; } + byId(id: string): Promise> { throw new Error('Method not implemented.'); } + async create(data: CreateTextFileViewModelBaseModel): Promise> { const promise = this.#dataSource.insert(data); await promise; - this.requestTreeItemsOf(data.parentPath ? data.parentPath : null); + //this.requestTreeItemsOf(data.parentPath ? data.parentPath : null); return promise; } + save(id: string, data: UpdateTextFileViewModelBaseModel): Promise { return this.#dataSource.update(id, data); } + delete(id: string): Promise { const promise = this.#dataSource.delete(id); const parentPath = id.substring(0, id.lastIndexOf('/')); - this.requestTreeItemsOf(parentPath ? parentPath : null); + //this.requestTreeItemsOf(parentPath ? parentPath : null); return promise; } @@ -180,71 +171,4 @@ export class UmbStylesheetRepository } //#endregion - - //#region TREE: - async requestTreeRoot() { - await this.#init; - - const data = { - path: null, - type: 'stylesheet-root', - name: 'Stylesheets', - icon: 'icon-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 }; - } - - async requestTreeItemsOf(path: string | null) { - if (path === undefined) throw new Error('Cannot request tree item with missing path'); - - await this.#init; - - const { data, error } = await this.#treeDataSource.getChildrenOf(path); - - if (data) { - this.#treeStore!.appendItems(data.items); - } - - return { data, error, asObservable: () => this.#treeStore!.childrenOf(path) }; - } - - async requestItems(paths: Array) { - if (!paths) throw new Error('Paths are missing'); - await this.#init; - const { data, error } = await this.#treeDataSource.getItems(paths); - return { data, error }; - } - - 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 itemsLegacy(paths: Array) { - if (!paths) throw new Error('Paths are missing'); - await this.#init; - return this.#treeStore!.items(paths); - } - - //#endregion } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/index.ts new file mode 100644 index 0000000000..de16c2e3c3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/index.ts @@ -0,0 +1 @@ +export { UmbStylesheetTreeRepository } from './stylesheet-tree.repository.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/manifests.ts index 108889cea8..1ee6d92c47 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/manifests.ts @@ -1,15 +1,38 @@ -import { STYLESHEET_ENTITY_TYPE, STYLESHEET_REPOSITORY_ALIAS } from '../config.js'; -import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry'; +import { UMB_STYLESHEET_ENTITY_TYPE } from '../entity-type.js'; +import { UmbStylesheetTreeRepository } from './stylesheet-tree.repository.js'; +import { UmbStylesheetTreeStore } from './stylesheet-tree.store.js'; +import type { + ManifestRepository, + ManifestTree, + ManifestTreeItem, + ManifestTreeStore, +} from '@umbraco-cms/backoffice/extension-registry'; -export const STYLESHEET_TREE_ALIAS = 'Umb.Tree.Stylesheet'; +export const UMB_STYLESHEET_TREE_ALIAS = 'Umb.Tree.Stylesheet'; +export const UMB_STYLESHEET_TREE_REPOSITORY_ALIAS = 'Umb.Repository.StylesheetTree'; +export const UMB_STYLESHEET_TREE_STORE_ALIAS = 'Umb.Store.StylesheetTree'; + +const treeRepository: ManifestRepository = { + type: 'repository', + alias: UMB_STYLESHEET_TREE_REPOSITORY_ALIAS, + name: 'Stylesheet Tree Repository', + api: UmbStylesheetTreeRepository, +}; + +const treeStore: ManifestTreeStore = { + type: 'treeStore', + alias: UMB_STYLESHEET_TREE_STORE_ALIAS, + name: 'Stylesheet Tree Store', + api: UmbStylesheetTreeStore, +}; const tree: ManifestTree = { type: 'tree', - alias: STYLESHEET_TREE_ALIAS, + alias: UMB_STYLESHEET_TREE_ALIAS, name: 'Stylesheet Tree', weight: 10, meta: { - repositoryAlias: STYLESHEET_REPOSITORY_ALIAS, + repositoryAlias: UMB_STYLESHEET_TREE_REPOSITORY_ALIAS, }, }; @@ -19,8 +42,8 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.Stylesheet', name: 'Stylesheet Tree Item', meta: { - entityTypes: ['stylesheet-root', STYLESHEET_ENTITY_TYPE], + entityTypes: ['stylesheet-root', UMB_STYLESHEET_ENTITY_TYPE], }, }; -export const manifests = [tree, treeItem]; +export const manifests = [treeRepository, treeStore, tree, treeItem]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.repository.ts new file mode 100644 index 0000000000..d080059d08 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.repository.ts @@ -0,0 +1,22 @@ +import { UmbStylesheetTreeServerDataSource } from './stylesheet-tree.server.data-source.js'; +import { UMB_STYLESHEET_TREE_STORE_CONTEXT_TOKEN } from './stylesheet-tree.store.js'; +import { UmbTreeRepositoryBase } from '@umbraco-cms/backoffice/tree'; +import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbStylesheetTreeRepository extends UmbTreeRepositoryBase { + constructor(host: UmbControllerHost) { + super(host, UmbStylesheetTreeServerDataSource, UMB_STYLESHEET_TREE_STORE_CONTEXT_TOKEN); + } + + async requestTreeRoot() { + const data = { + path: null, + type: 'stylesheet-root', + name: 'Stylesheets', + icon: 'icon-folder', + hasChildren: true, + }; + + return { data }; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.server.data-source.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts rename to src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.server.data-source.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.tree.store.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.store.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/repository/stylesheet.tree.store.ts rename to src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/tree/stylesheet-tree.store.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts index d37300a880..2028478b43 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts @@ -48,7 +48,11 @@ export class UmbStylesheetWorkspaceEditorElement extends UmbLitElement { this.#workspaceContext.path, (path) => { this._path = path; - this._dirName = this._path?.substring(0, this._path?.lastIndexOf('\\') + 1)?.replace(/\\/g, '/'); + if (this._path?.includes('.css')) { + this._dirName = this._path?.substring(0, this._path?.lastIndexOf('\\') + 1)?.replace(/\\/g, '/'); + } else { + this._dirName = path + '/'; + } }, '_observeStylesheetPath', ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts index ca05d25fe0..480c6be472 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts @@ -2,11 +2,7 @@ import { UmbStylesheetRepository } from '../repository/stylesheet.repository.js' import { StylesheetDetails } from '../index.js'; import { UmbSaveableWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import { - UmbArrayState, - UmbBooleanState, - UmbObjectState -} from '@umbraco-cms/backoffice/observable-api'; +import { UmbArrayState, UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; import { RichTextRuleModel, UpdateStylesheetRequestModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; @@ -48,10 +44,10 @@ export class UmbStylesheetWorkspaceContext } getEntityId() { - const path = this.getData()?.path; + const path = this.getData()?.path?.replace(/\//g, '%2F'); const name = this.getData()?.name; - // TODO: %2F is a slash (/). Should we make it an actual slash in the URL? (%2F for now so that the server can find the correct stylesheet via URL) + // Note: %2F is a slash (/) return path && name ? `${path}%2F${name}` : name || ''; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.element.ts index 76485ab439..c4f74d092e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.element.ts @@ -19,6 +19,7 @@ export class UmbStylesheetWorkspaceElement extends UmbLitElement { const path = info.match.params.path === 'null' ? null : info.match.params.path; const serverPath = path === null ? null : serverFilePathFromUrlFriendlyPath(path); await this.#workspaceContext.create(serverPath); + await this.#workspaceContext.setRules([]); new UmbWorkspaceIsNewRedirectController( this, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts index 2a9cf401ff..b5f5fe9eda 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts @@ -47,8 +47,12 @@ export class UmbCurrentUserHeaderAppElement extends UmbLitElement { render() { return html` - - + + `; }