diff --git a/cypress/integration/workspace.js b/cypress/integration/workspace.js index 97db14d683..f794df4ed7 100644 --- a/cypress/integration/workspace.js +++ b/cypress/integration/workspace.js @@ -7,11 +7,12 @@ context("Workspace 2.0", () => { it("Navigate to page from sidebar", () => { cy.visit("/app/build"); cy.get(".codex-editor__redactor .ce-block"); - cy.get('.sidebar-item-container[item-title="Website"]').first().click(); - cy.location("pathname").should("eq", "/app/website"); + cy.get('.sidebar-item-container[item-title="Page"]').first().click(); + cy.location("pathname").should("eq", "/app/page"); }); it("Create Private Page", () => { + cy.visit("/app/build"); cy.intercept({ method: "POST", url: "api/method/frappe.desk.doctype.workspace.workspace.new_page", @@ -27,61 +28,16 @@ context("Workspace 2.0", () => { cy.get_open_dialog().find(".btn-primary").click(); // check if sidebar item is added in pubic section - cy.get('.sidebar-item-container[item-title="Test Private Page"]').should( - "have.attr", - "item-public", - "0" - ); + cy.get('.sidebar-item-container[item-title="Test Private Page"]'); cy.wait(300); cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); cy.wait(300); - cy.get('.sidebar-item-container[item-title="Test Private Page"]').should( - "have.attr", - "item-public", - "0" - ); - - cy.wait("@new_page"); - }); - - it("Create Child Page", () => { - cy.intercept({ - method: "POST", - url: "api/method/frappe.desk.doctype.workspace.workspace.new_page", - }).as("new_page"); - - cy.get(".codex-editor__redactor .ce-block"); - cy.get(".btn-new-workspace").click(); - cy.fill_field("title", "Test Child Page", "Data"); - cy.fill_field("parent", "Test Private Page", "Select"); - cy.fill_field("type", "Workspace", "Select"); - cy.get_open_dialog().find(".modal-header").click(); - cy.wait(300); - cy.get_open_dialog().find(".btn-primary").click(); - - // check if sidebar item is added in pubic section - cy.get('.sidebar-item-container[item-title="Test Child Page"]').should( - "have.attr", - "item-public", - "0" - ); - cy.wait(300); - cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); - cy.wait(300); - cy.get('.sidebar-item-container[item-title="Test Child Page"]').should( - "have.attr", - "item-public", - "0" - ); + cy.get('.sidebar-item-container[item-title="Test Private Page"]'); cy.wait("@new_page"); }); it("Add New Block", () => { - cy.get('.sidebar-item-container[item-title="Test Private Page"]').as("sidebar-item"); - - cy.get("@sidebar-item").find(".standard-sidebar-item").first().click({ force: true }); - cy.get(".btn-edit-workspace").click({ force: true }); cy.get(".ce-block").click().type("{enter}"); diff --git a/cypress/integration/workspace_blocks.js b/cypress/integration/workspace_blocks.js index 70159150e2..03fbf06db6 100644 --- a/cypress/integration/workspace_blocks.js +++ b/cypress/integration/workspace_blocks.js @@ -25,19 +25,11 @@ context("Workspace Blocks", () => { cy.get_open_dialog().find(".btn-primary").click(); // check if sidebar item is added in private section - cy.get('.sidebar-item-container[item-title="Test Block Page"]').should( - "have.attr", - "item-public", - "0" - ); + cy.get('.sidebar-item-container[item-title="Test Block Page"]'); cy.wait(300); cy.get('.standard-actions .btn-primary[data-label="Save"]').click(); cy.wait(300); - cy.get('.sidebar-item-container[item-title="Test Block Page"]').should( - "have.attr", - "item-public", - "0" - ); + cy.get('.sidebar-item-container[item-title="Test Block Page"]'); cy.wait("@new_page"); }); diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 0a11f1ac84..757a842277 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -6,7 +6,9 @@ from json import loads import frappe from frappe import _ +from frappe.boot import get_sidebar_items from frappe.desk.desktop import get_workspace_sidebar_items, save_new_widget +from frappe.desk.doctype.workspace_sidebar.workspace_sidebar import add_to_my_workspace from frappe.desk.utils import validate_route_conflict from frappe.model.document import Document from frappe.model.rename_doc import rename_doc @@ -294,7 +296,10 @@ def new_page(new_page): doc.sequence_id = last_sequence_id(doc) + 1 doc.save(ignore_permissions=True) - return get_workspace_sidebar_items() + # add to workspace sidebar items + if not doc.public: + add_to_my_workspace(doc) + return {"workspace_pages": get_workspace_sidebar_items(), "sidebar_items": get_sidebar_items()} @frappe.whitelist() diff --git a/frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py b/frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py index 1bc62beae5..d8bf2e0c73 100644 --- a/frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py +++ b/frappe/desk/doctype/workspace_sidebar/workspace_sidebar.py @@ -6,7 +6,6 @@ from json import JSONDecodeError, dumps, loads import frappe from frappe import _ -from frappe.desk.doctype.workspace.workspace import is_workspace_manager from frappe.model.document import Document from frappe.modules.utils import create_directory_on_app_path @@ -52,6 +51,10 @@ class WorkspaceSidebar(Document): frappe.throw(_("You need to be Workspace Manager to delete a public workspace.")) +def is_workspace_manager(): + return "Workspace Manager" in frappe.get_roles() + + def create_workspace_sidebar_for_workspaces(): from frappe.query_builder import DocType @@ -98,3 +101,19 @@ def add_sidebar_items(sidebar_title, sidebar_items): w.items = items w.save() return w + + +def add_to_my_workspace(workspace): + private_sidebar = frappe.get_doc("Workspace Sidebar", "My Workspaces") + + workspace_sidebar = { + "label": workspace.title, + "type": "Link", + "link_to": f"{workspace.title}-{workspace.for_user}", + "link_type": "Workspace", + "icon": workspace.icon, + } + + private_sidebar.append("items", workspace_sidebar) + + private_sidebar.save() diff --git a/frappe/public/js/frappe/ui/sidebar/sidebar.js b/frappe/public/js/frappe/ui/sidebar/sidebar.js index b250ed0d2e..32d28f3dff 100644 --- a/frappe/public/js/frappe/ui/sidebar/sidebar.js +++ b/frappe/public/js/frappe/ui/sidebar/sidebar.js @@ -58,12 +58,18 @@ frappe.ui.Sidebar = class Sidebar { } setup(workspace_title) { this.workspace_title = workspace_title; + this.check_for_private_workspace(workspace_title); this.prepare(); this.$sidebar.attr("data-title", this.workspace_title); this.sidebar_header = new frappe.ui.SidebarHeader(this); this.make_sidebar(); this.setup_complete = true; } + check_for_private_workspace(workspace_title) { + if (workspace_title == "private") { + this.workspace_title = "My Workspaces"; + } + } setup_events() { const me = this; frappe.router.on("change", function (router) { @@ -239,12 +245,6 @@ frappe.ui.Sidebar = class Sidebar { this.set_active_workspace_item(); } - reload() { - return frappe.workspace.get_pages().then((r) => { - frappe.boot.sidebar_pages = r; - this.setup_pages(); - }); - } set_height() { $(".body-sidebar").css("height", window.innerHeight + "px"); $(".overlay").css("height", window.innerHeight + "px"); @@ -274,7 +274,13 @@ frappe.ui.Sidebar = class Sidebar { let route = frappe.get_route(); if (frappe.get_route()[0] == "setup-wizard") return; if (route[0] == "Workspaces") { - let workspace = route[1]; + let workspace; + if (!route[1]) { + workspace = "My Workspaces"; + } else { + workspace = route[1]; + } + frappe.app.sidebar.setup(workspace); } else if (route[0] == "List" || route[0] == "Form") { let doctype = route[1]; diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 1ec4ec3ade..a4d069e3ce 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -550,6 +550,7 @@ frappe.views.Workspace = class Workspace { } create_page(new_page) { + const me = this; return new Promise((resolve) => { frappe.call({ method: "frappe.desk.doctype.workspace.workspace.new_page", @@ -565,11 +566,18 @@ frappe.views.Workspace = class Workspace { indicator: "green", }); } - frappe.boot.sidebar_pages = r.message; - - if (!frappe.boot.app_data_map["private"] && new_page.public === 0) { - this.sidebar.apps_switcher.add_private_app(); + if (r.message) { + frappe.boot.sidebar_pages = r.message.workspace_pages; + frappe.boot.workspaces = r.message.workspace_pages; + me.workspaces = frappe.boot.workspaces.pages; + me.setup_pages(frappe.boot.sidebar_pages.pages); + frappe.boot.workspace_sidebar_item = r.message.sidebar_items; } + + if (new_page.public === 0) { + frappe.app.sidebar.setup("private"); + } + resolve(); } }, @@ -577,6 +585,31 @@ frappe.views.Workspace = class Workspace { }); } + setup_pages(all_pages) { + all_pages.forEach((page) => { + page.is_editable = !page.public || this.has_access; + if (typeof page.content == "string") { + page.content = JSON.parse(page.content); + } + }); + + if (all_pages) { + frappe.workspaces = {}; + frappe.workspace_list = []; + frappe.workspace_map = {}; + for (let page of all_pages) { + frappe.workspaces[frappe.router.slug(page.name)] = { + name: page.name, + public: page.public, + }; + if (!page.app && page.module) { + page.app = frappe.boot.module_app[frappe.slug(page.module)]; + } + frappe.workspace_map[page.name] = page; + frappe.workspace_list.push(page); + } + } + } initialize_editorjs(blocks) { this.tools = { header: { @@ -722,7 +755,7 @@ frappe.views.Workspace = class Workspace { this._page = null; return this.get_pages().then((r) => { frappe.boot.sidebar_pages = r; - this.sidebar.setup_pages(); + this.setup_pages(frappe.boot.workspaces.pages); this.show(); if (this.undo) this.undo.readOnly = true; }); diff --git a/frappe/workspace_sidebar/my_workspaces.json b/frappe/workspace_sidebar/my_workspaces.json new file mode 100644 index 0000000000..d762b716b7 --- /dev/null +++ b/frappe/workspace_sidebar/my_workspaces.json @@ -0,0 +1,14 @@ +{ + "app": "frappe", + "creation": "2025-11-03 03:11:08.482620", + "docstatus": 0, + "doctype": "Workspace Sidebar", + "header_icon": "user", + "idx": 0, + "items": [], + "modified": "2025-11-03 10:44:42.883207", + "modified_by": "Administrator", + "name": "My Workspaces", + "owner": "Administrator", + "title": "My Workspaces" +}