Merge pull request #13152 from shariquerik/wiki-based-desk

feat: Workspace 2.0
This commit is contained in:
mergify[bot] 2021-08-13 06:44:32 +00:00 committed by GitHub
commit bd6ed2033f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 3191 additions and 501 deletions

View file

@ -0,0 +1,90 @@
context('Workspace 2.0', () => {
before(() => {
cy.visit('/login');
cy.login();
cy.visit('/app/website');
});
it('Navigate to page from sidebar', () => {
cy.visit('/app/build');
cy.get('.codex-editor__redactor .ce-block');
cy.get('.sidebar-item-container[item-name="Settings"]').first().click();
cy.location('pathname').should('eq', '/app/settings');
});
it('Create Private Page', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.custom-actions button[data-label="Create%20Page"]').click();
cy.fill_field('title', 'Test Private Page', 'Data');
cy.fill_field('icon', 'edit', 'Icon');
cy.get_open_dialog().find('.modal-header').click();
cy.get_open_dialog().find('.btn-primary').click();
// check if sidebar item is added in pubic section
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0');
cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
cy.wait(300);
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('have.attr', 'item-public', '0');
cy.wait(500);
cy.get('.codex-editor__redactor .ce-block');
cy.get('.standard-actions .btn-secondary[data-label=Customize]').click();
});
it('Add New Block', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.custom-actions .inner-group-button[data-label="Add%20Block"]').click();
cy.get('.custom-actions .inner-group-button .dropdown-menu .block-menu-item-label').contains('Heading').click();
cy.get(".ce-block:last").find('h2').click({force: true}).type('Header');
cy.get(".ce-block:last").find('.ce-header').should('exist');
cy.get('.custom-actions .inner-group-button[data-label="Add%20Block"]').click();
cy.get('.custom-actions .inner-group-button .dropdown-menu .block-menu-item-label').contains('Text').click();
cy.get(".ce-block:last").find('.ce-paragraph').click({force: true}).type('Paragraph text');
cy.get(".ce-block:last").find('.ce-paragraph').should('exist');
});
it('Delete A Block', () => {
cy.get(".ce-block:last").find('.delete-paragraph').click();
cy.get(".ce-block:last").find('.ce-paragraph').should('not.exist');
});
it('Shrink and Expand A Block', () => {
cy.get(".ce-block:last").find('.tune-btn').click();
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-11');
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-10');
cy.get('.ce-settings--opened .ce-shrink-button').click();
cy.get(".ce-block:last").should('have.class', 'col-9');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-10');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-11');
cy.get('.ce-settings--opened .ce-expand-button').click();
cy.get(".ce-block:last").should('have.class', 'col-12');
});
it('Change Header Text Size', () => {
cy.get('.ce-settings--opened .cdx-settings-button[data-level="3"]').click();
cy.get(".ce-block:last").find('.widget-head h3').should('exist');
cy.get('.ce-settings--opened .cdx-settings-button[data-level="4"]').click();
cy.get(".ce-block:last").find('.widget-head h4').should('exist');
cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
});
it('Delete Private Page', () => {
cy.get('.codex-editor__redactor .ce-block');
cy.get('.standard-actions .btn-secondary[data-label=Customize]').click();
cy.get('.sidebar-item-container[item-name="Test Private Page"]').find('.sidebar-item-control .delete-page').click();
cy.wait(300);
cy.get('.modal-footer > .standard-actions > .btn-modal-primary:visible').first().click();
cy.get('.standard-actions .btn-primary[data-label="Save Customizations"]').click();
cy.get('.codex-editor__redactor .ce-block');
cy.get('.sidebar-item-container[item-name="Test Private Page"]').should('not.exist');
});
});

View file

@ -1,22 +1,27 @@
{ {
"category": "Administration", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"ToDo\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"File\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Assignment Rule\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Auto Repeat\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Email\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Automation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Event Streaming\", \"col\": 4}}]",
"creation": "2020-03-02 14:53:24.980279", "creation": "2020-03-02 14:53:24.980279",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "tool", "icon": "tool",
"idx": 0, "idx": 0,
"is_standard": 1, "is_default": 0,
"is_standard": 0,
"label": "Tools", "label": "Tools",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Tools", "label": "Tools",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -25,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "To Do", "label": "To Do",
"link_count": 0,
"link_to": "ToDo", "link_to": "ToDo",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -35,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Calendar", "label": "Calendar",
"link_count": 0,
"link_to": "Event", "link_to": "Event",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -45,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Note", "label": "Note",
"link_count": 0,
"link_to": "Note", "link_to": "Note",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -55,6 +63,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Files", "label": "Files",
"link_count": 0,
"link_to": "File", "link_to": "File",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -65,6 +74,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Activity", "label": "Activity",
"link_count": 0,
"link_to": "activity", "link_to": "activity",
"link_type": "Page", "link_type": "Page",
"onboard": 0, "onboard": 0,
@ -74,6 +84,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email", "label": "Email",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -82,6 +93,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Newsletter", "label": "Newsletter",
"link_count": 0,
"link_to": "Newsletter", "link_to": "Newsletter",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -92,6 +104,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email Group", "label": "Email Group",
"link_count": 0,
"link_to": "Email Group", "link_to": "Email Group",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -101,6 +114,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Automation", "label": "Automation",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -109,6 +123,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Assignment Rule", "label": "Assignment Rule",
"link_count": 0,
"link_to": "Assignment Rule", "link_to": "Assignment Rule",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -119,6 +134,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Milestone", "label": "Milestone",
"link_count": 0,
"link_to": "Milestone", "link_to": "Milestone",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -129,6 +145,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Auto Repeat", "label": "Auto Repeat",
"link_count": 0,
"link_to": "Auto Repeat", "link_to": "Auto Repeat",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -138,6 +155,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Streaming", "label": "Event Streaming",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -146,6 +164,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Producer", "label": "Event Producer",
"link_count": 0,
"link_to": "Event Producer", "link_to": "Event Producer",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -156,6 +175,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Consumer", "label": "Event Consumer",
"link_count": 0,
"link_to": "Event Consumer", "link_to": "Event Consumer",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -166,6 +186,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Update Log", "label": "Event Update Log",
"link_count": 0,
"link_to": "Event Update Log", "link_to": "Event Update Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -176,6 +197,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Event Sync Log", "label": "Event Sync Log",
"link_count": 0,
"link_to": "Event Sync Log", "link_to": "Event Sync Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -186,19 +208,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Document Type Mapping", "label": "Document Type Mapping",
"link_count": 0,
"link_to": "Document Type Mapping", "link_to": "Document Type Mapping",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2020-12-01 13:38:39.950350", "modified": "2021-08-05 12:16:02.839180",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Automation", "module": "Automation",
"name": "Tools", "name": "Tools",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 26,
"shortcuts": [ "shortcuts": [
{ {
"label": "ToDo", "label": "ToDo",
@ -225,5 +254,6 @@
"link_to": "Auto Repeat", "link_to": "Auto Repeat",
"type": "DocType" "type": "DocType"
} }
] ],
"title": "Tools"
} }

View file

@ -105,8 +105,8 @@ def load_conf_settings(bootinfo):
if key in conf: bootinfo[key] = conf.get(key) if key in conf: bootinfo[key] = conf.get(key)
def load_desktop_data(bootinfo): def load_desktop_data(bootinfo):
from frappe.desk.desktop import get_desk_sidebar_items from frappe.desk.desktop import get_wspace_sidebar_items
bootinfo.allowed_workspaces = get_desk_sidebar_items() bootinfo.allowed_workspaces = get_wspace_sidebar_items().get('pages')
bootinfo.module_page_map = get_controller("Workspace").get_module_page_map() bootinfo.module_page_map = get_controller("Workspace").get_module_page_map()
bootinfo.dashboards = frappe.get_all("Dashboard") bootinfo.dashboards = frappe.get_all("Dashboard")

View file

@ -1,24 +1,28 @@
{ {
"cards_label": "Elements", "cards_label": "Elements",
"category": "Modules", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"DocType\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Workspace\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Report\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Elements\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Modules\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Models\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Views\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Scripting\", \"col\": 4}}]",
"creation": "2021-01-02 10:51:16.579957", "creation": "2021-01-02 10:51:16.579957",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "tool", "icon": "tool",
"idx": 0, "idx": 0,
"is_default": 0, "is_default": 0,
"is_standard": 1, "is_standard": 0,
"label": "Build", "label": "Build",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Modules", "label": "Modules",
"link_count": 0,
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"only_for": "", "only_for": "",
@ -28,6 +32,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Module Def", "label": "Module Def",
"link_count": 0,
"link_to": "Module Def", "link_to": "Module Def",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -38,6 +43,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workspace", "label": "Workspace",
"link_count": 0,
"link_to": "Workspace", "link_to": "Workspace",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -48,6 +54,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Module Onboarding", "label": "Module Onboarding",
"link_count": 0,
"link_to": "Module Onboarding", "link_to": "Module Onboarding",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -58,6 +65,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Block Module", "label": "Block Module",
"link_count": 0,
"link_to": "Block Module", "link_to": "Block Module",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -68,6 +76,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Models", "label": "Models",
"link_count": 0,
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"only_for": "", "only_for": "",
@ -77,6 +86,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "DocType", "label": "DocType",
"link_count": 0,
"link_to": "DocType", "link_to": "DocType",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -87,6 +97,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workflow", "label": "Workflow",
"link_count": 0,
"link_to": "Workflow", "link_to": "Workflow",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -97,6 +108,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Views", "label": "Views",
"link_count": 0,
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"only_for": "", "only_for": "",
@ -106,6 +118,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Report", "label": "Report",
"link_count": 0,
"link_to": "Report", "link_to": "Report",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -116,6 +129,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Print Format", "label": "Print Format",
"link_count": 0,
"link_to": "Print Format", "link_to": "Print Format",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -126,6 +140,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workspace", "label": "Workspace",
"link_count": 0,
"link_to": "Workspace", "link_to": "Workspace",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -136,6 +151,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dashboard", "label": "Dashboard",
"link_count": 0,
"link_to": "Dashboard", "link_to": "Dashboard",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -146,6 +162,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Scripting", "label": "Scripting",
"link_count": 0,
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"only_for": "", "only_for": "",
@ -155,6 +172,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Server Script", "label": "Server Script",
"link_count": 0,
"link_to": "Server Script", "link_to": "Server Script",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -165,6 +183,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Client Script", "label": "Client Script",
"link_count": 0,
"link_to": "Client Script", "link_to": "Client Script",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -175,6 +194,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Scheduled Job Type", "label": "Scheduled Job Type",
"link_count": 0,
"link_to": "Scheduled Job Type", "link_to": "Scheduled Job Type",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -182,13 +202,19 @@
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-02-04 13:48:48.493146", "modified": "2021-08-05 12:15:55.793022",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Build", "name": "Build",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 5,
"shortcuts": [ "shortcuts": [
{ {
"doc_view": "", "doc_view": "",
@ -208,5 +234,6 @@
"link_to": "Report", "link_to": "Report",
"type": "DocType" "type": "DocType"
} }
] ],
"title": "Build"
} }

View file

@ -1,22 +1,27 @@
{ {
"category": "Modules", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Settings\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"System Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Print Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Website Settings\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Data\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Email / Notifications\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Website\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Core\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Printing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Workflow\", \"col\": 4}}]",
"creation": "2020-03-02 15:09:40.527211", "creation": "2020-03-02 15:09:40.527211",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 1, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "setting", "icon": "setting",
"idx": 0, "idx": 0,
"is_standard": 1, "is_default": 0,
"is_standard": 0,
"label": "Settings", "label": "Settings",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Data", "label": "Data",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -25,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Import Data", "label": "Import Data",
"link_count": 0,
"link_to": "Data Import", "link_to": "Data Import",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -35,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Export Data", "label": "Export Data",
"link_count": 0,
"link_to": "Data Export", "link_to": "Data Export",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -45,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Bulk Update", "label": "Bulk Update",
"link_count": 0,
"link_to": "Bulk Update", "link_to": "Bulk Update",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -55,6 +63,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Download Backups", "label": "Download Backups",
"link_count": 0,
"link_to": "backups", "link_to": "backups",
"link_type": "Page", "link_type": "Page",
"onboard": 0, "onboard": 0,
@ -65,6 +74,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Deleted Documents", "label": "Deleted Documents",
"link_count": 0,
"link_to": "Deleted Document", "link_to": "Deleted Document",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -74,6 +84,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email / Notifications", "label": "Email / Notifications",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -82,6 +93,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email Account", "label": "Email Account",
"link_count": 0,
"link_to": "Email Account", "link_to": "Email Account",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -92,6 +104,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email Domain", "label": "Email Domain",
"link_count": 0,
"link_to": "Email Domain", "link_to": "Email Domain",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -102,6 +115,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Notification", "label": "Notification",
"link_count": 0,
"link_to": "Notification", "link_to": "Notification",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -112,6 +126,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Email Template", "label": "Email Template",
"link_count": 0,
"link_to": "Email Template", "link_to": "Email Template",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -122,6 +137,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Auto Email Report", "label": "Auto Email Report",
"link_count": 0,
"link_to": "Auto Email Report", "link_to": "Auto Email Report",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -132,6 +148,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Newsletter", "label": "Newsletter",
"link_count": 0,
"link_to": "Newsletter", "link_to": "Newsletter",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -142,6 +159,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Notification Settings", "label": "Notification Settings",
"link_count": 0,
"link_to": "Notification Settings", "link_to": "Notification Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -151,6 +169,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website", "label": "Website",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -159,6 +178,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Settings", "label": "Website Settings",
"link_count": 0,
"link_to": "Website Settings", "link_to": "Website Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -169,6 +189,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Theme", "label": "Website Theme",
"link_count": 0,
"link_to": "Website Theme", "link_to": "Website Theme",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -179,6 +200,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Script", "label": "Website Script",
"link_count": 0,
"link_to": "Website Script", "link_to": "Website Script",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -189,6 +211,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "About Us Settings", "label": "About Us Settings",
"link_count": 0,
"link_to": "About Us Settings", "link_to": "About Us Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -199,6 +222,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Contact Us Settings", "label": "Contact Us Settings",
"link_count": 0,
"link_to": "Contact Us Settings", "link_to": "Contact Us Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -208,6 +232,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Core", "label": "Core",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -216,6 +241,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "System Settings", "label": "System Settings",
"link_count": 0,
"link_to": "System Settings", "link_to": "System Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -226,6 +252,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Error Log", "label": "Error Log",
"link_count": 0,
"link_to": "Error Log", "link_to": "Error Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -236,6 +263,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Error Snapshot", "label": "Error Snapshot",
"link_count": 0,
"link_to": "Error Snapshot", "link_to": "Error Snapshot",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -246,6 +274,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Domain Settings", "label": "Domain Settings",
"link_count": 0,
"link_to": "Domain Settings", "link_to": "Domain Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -255,6 +284,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Printing", "label": "Printing",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -263,6 +293,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Print Format Builder", "label": "Print Format Builder",
"link_count": 0,
"link_to": "print-format-builder", "link_to": "print-format-builder",
"link_type": "Page", "link_type": "Page",
"onboard": 0, "onboard": 0,
@ -273,6 +304,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Print Settings", "label": "Print Settings",
"link_count": 0,
"link_to": "Print Settings", "link_to": "Print Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -283,6 +315,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Print Format", "label": "Print Format",
"link_count": 0,
"link_to": "Print Format", "link_to": "Print Format",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -293,6 +326,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Print Style", "label": "Print Style",
"link_count": 0,
"link_to": "Print Style", "link_to": "Print Style",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -302,6 +336,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workflow", "label": "Workflow",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -310,6 +345,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workflow", "label": "Workflow",
"link_count": 0,
"link_to": "Workflow", "link_to": "Workflow",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -320,6 +356,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workflow State", "label": "Workflow State",
"link_count": 0,
"link_to": "Workflow State", "link_to": "Workflow State",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -330,19 +367,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Workflow Action", "label": "Workflow Action",
"link_count": 0,
"link_to": "Workflow Action", "link_to": "Workflow Action",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2020-12-01 13:38:40.235323", "modified": "2021-08-05 12:16:03.456173",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Settings", "name": "Settings",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"pin_to_bottom": 1, "parent_page": "",
"pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 29,
"shortcuts": [ "shortcuts": [
{ {
"icon": "setting", "icon": "setting",
@ -363,5 +407,6 @@
"type": "DocType" "type": "DocType"
} }
], ],
"shortcuts_label": "Settings" "shortcuts_label": "Settings",
"title": "Settings"
} }

View file

@ -1,23 +1,27 @@
{ {
"category": "Administration", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"User\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Role\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Permission Manager\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"User Profile\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"User Type\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Users\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Logs\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Permissions\", \"col\": 4}}]",
"creation": "2020-03-02 15:12:16.754449", "creation": "2020-03-02 15:12:16.754449",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "users", "icon": "users",
"idx": 0, "idx": 0,
"is_default": 0, "is_default": 0,
"is_standard": 1, "is_standard": 0,
"label": "Users", "label": "Users",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Users", "label": "Users",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -26,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "User", "label": "User",
"link_count": 0,
"link_to": "User", "link_to": "User",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -36,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Role", "label": "Role",
"link_count": 0,
"link_to": "Role", "link_to": "Role",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -46,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Role Profile", "label": "Role Profile",
"link_count": 0,
"link_to": "Role Profile", "link_to": "Role Profile",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -55,6 +62,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Logs", "label": "Logs",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -63,6 +71,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Activity Log", "label": "Activity Log",
"link_count": 0,
"link_to": "Activity Log", "link_to": "Activity Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -73,6 +82,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Access Log", "label": "Access Log",
"link_count": 0,
"link_to": "Access Log", "link_to": "Access Log",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -82,6 +92,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Permissions", "label": "Permissions",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -90,6 +101,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Role Permissions Manager", "label": "Role Permissions Manager",
"link_count": 0,
"link_to": "permission-manager", "link_to": "permission-manager",
"link_type": "Page", "link_type": "Page",
"onboard": 0, "onboard": 0,
@ -100,6 +112,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "User Permissions", "label": "User Permissions",
"link_count": 0,
"link_to": "User Permission", "link_to": "User Permission",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -110,6 +123,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Role Permission for Page and Report", "label": "Role Permission for Page and Report",
"link_count": 0,
"link_to": "Role Permission for Page and Report", "link_to": "Role Permission for Page and Report",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -120,6 +134,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 1, "is_query_report": 1,
"label": "Permitted Documents For User", "label": "Permitted Documents For User",
"link_count": 0,
"link_to": "Permitted Documents For User", "link_to": "Permitted Documents For User",
"link_type": "Report", "link_type": "Report",
"onboard": 0, "onboard": 0,
@ -130,19 +145,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Document Share Report", "label": "Document Share Report",
"link_count": 0,
"link_to": "Document Share Report", "link_to": "Document Share Report",
"link_type": "Report", "link_type": "Report",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-03-25 23:02:34.582569", "modified": "2021-08-05 12:16:03.010204",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Users", "name": "Users",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 27,
"shortcuts": [ "shortcuts": [
{ {
"label": "User", "label": "User",
@ -170,5 +192,6 @@
"link_to": "User Type", "link_to": "User Type",
"type": "DocType" "type": "DocType"
} }
] ],
"title": "Users"
} }

View file

@ -1,23 +1,27 @@
{ {
"category": "Administration", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customize Form\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Custom Role\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Client Script\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Server Script\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Dashboards\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Form Customization\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other\", \"col\": 4}}]",
"creation": "2020-03-02 15:15:03.839594", "creation": "2020-03-02 15:15:03.839594",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "customization", "icon": "customization",
"idx": 0, "idx": 0,
"is_default": 0, "is_default": 0,
"is_standard": 1, "is_standard": 0,
"label": "Customization", "label": "Customization",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dashboards", "label": "Dashboards",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -26,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dashboard", "label": "Dashboard",
"link_count": 0,
"link_to": "Dashboard", "link_to": "Dashboard",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -36,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dashboard Chart", "label": "Dashboard Chart",
"link_count": 0,
"link_to": "Dashboard Chart", "link_to": "Dashboard Chart",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -46,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dashboard Chart Source", "label": "Dashboard Chart Source",
"link_count": 0,
"link_to": "Dashboard Chart Source", "link_to": "Dashboard Chart Source",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -55,6 +62,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Form Customization", "label": "Form Customization",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -63,6 +71,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Customize Form", "label": "Customize Form",
"link_count": 0,
"link_to": "Customize Form", "link_to": "Customize Form",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -73,6 +82,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Custom Field", "label": "Custom Field",
"link_count": 0,
"link_to": "Custom Field", "link_to": "Custom Field",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -83,6 +93,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Client Script", "label": "Client Script",
"link_count": 0,
"link_to": "Client Script", "link_to": "Client Script",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -93,6 +104,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "DocType", "label": "DocType",
"link_count": 0,
"link_to": "DocType", "link_to": "DocType",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -102,6 +114,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Other", "label": "Other",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -110,19 +123,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Custom Translations", "label": "Custom Translations",
"link_count": 0,
"link_to": "Translation", "link_to": "Translation",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2021-02-04 13:50:35.750463", "modified": "2021-08-05 12:15:57.486112",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Custom", "module": "Custom",
"name": "Customization", "name": "Customization",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 8,
"shortcuts": [ "shortcuts": [
{ {
"label": "Customize Form", "label": "Customize Form",
@ -145,5 +165,6 @@
"link_to": "Server Script", "link_to": "Server Script",
"type": "DocType" "type": "DocType"
} }
] ],
"title": "Customization"
} }

View file

@ -6,6 +6,7 @@ import frappe
from json import loads, dumps from json import loads, dumps
from frappe import _, DoesNotExistError, ValidationError, _dict from frappe import _, DoesNotExistError, ValidationError, _dict
from frappe.boot import get_allowed_pages, get_allowed_reports from frappe.boot import get_allowed_pages, get_allowed_reports
from frappe.core.doctype.custom_role.custom_role import get_custom_allowed_roles
from functools import wraps from functools import wraps
from frappe.cache_manager import ( from frappe.cache_manager import (
build_domain_restriced_doctype_cache, build_domain_restriced_doctype_cache,
@ -27,18 +28,21 @@ def handle_not_exist(fn):
class Workspace: class Workspace:
def __init__(self, page_name, minimal=False): def __init__(self, page, minimal=False):
self.page_name = page_name self.page_name = page.get('name')
self.page_title = page.get('title')
self.public_page = page.get('public')
self.extended_links = [] self.extended_links = []
self.extended_charts = [] self.extended_charts = []
self.extended_shortcuts = [] self.extended_shortcuts = []
self.workspace_manager = "Workspace Manager" in frappe.get_roles()
self.user = frappe.get_user() self.user = frappe.get_user()
self.allowed_modules = self.get_cached('user_allowed_modules', self.get_allowed_modules) self.allowed_modules = self.get_cached('user_allowed_modules', self.get_allowed_modules)
self.doc = self.get_page_for_user() self.doc = frappe.get_cached_doc("Workspace", self.page_name)
if self.doc.module and self.doc.module not in self.allowed_modules: if self.doc and self.doc.module and self.doc.module not in self.allowed_modules and not self.workspace_manager:
raise frappe.PermissionError raise frappe.PermissionError
self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items) self.can_read = self.get_cached('user_perm_can_read', self.get_can_read_items)
@ -47,16 +51,17 @@ class Workspace:
self.allowed_reports = get_allowed_reports(cache=True) self.allowed_reports = get_allowed_reports(cache=True)
if not minimal: if not minimal:
self.onboarding_doc = self.get_onboarding_doc() if self.doc.content:
self.onboarding = None self.onboarding_list = [x['data']['onboarding_name'] for x in loads(self.doc.content) if x['type'] == 'onboarding']
self.onboardings = []
self.table_counts = get_table_with_counts() self.table_counts = get_table_with_counts()
self.restricted_doctypes = frappe.cache().get_value("domain_restricted_doctypes") or build_domain_restriced_doctype_cache() self.restricted_doctypes = frappe.cache().get_value("domain_restricted_doctypes") or build_domain_restriced_doctype_cache()
self.restricted_pages = frappe.cache().get_value("domain_restricted_pages") or build_domain_restriced_page_cache() self.restricted_pages = frappe.cache().get_value("domain_restricted_pages") or build_domain_restriced_page_cache()
def is_page_allowed(self): def is_page_allowed(self):
cards = self.doc.get_link_groups() + get_custom_reports_and_doctypes(self.doc.module) + self.extended_links cards = self.doc.get_link_groups() + get_custom_reports_and_doctypes(self.doc.module)
shortcuts = self.doc.shortcuts + self.extended_shortcuts shortcuts = self.doc.shortcuts
for section in cards: for section in cards:
links = loads(section.get('links')) if isinstance(section.get('links'), str) else section.get('links') links = loads(section.get('links')) if isinstance(section.get('links'), str) else section.get('links')
@ -74,8 +79,28 @@ class Workspace:
if self.is_item_allowed(item.link_to, item.type) and _in_active_domains(item): if self.is_item_allowed(item.link_to, item.type) and _in_active_domains(item):
return True return True
if not shortcuts and not self.doc.links:
return True
return False return False
def is_permitted(self):
"""Returns true if Has Role is not set or the user is allowed."""
from frappe.utils import has_common
allowed = [d.role for d in frappe.get_all("Has Role", fields=["role"], filters={"parent": self.doc.name})]
custom_roles = get_custom_allowed_roles('page', self.doc.name)
allowed.extend(custom_roles)
if not allowed:
return True
roles = frappe.get_roles()
if has_common(roles, allowed):
return True
def get_cached(self, cache_key, fallback_fn): def get_cached(self, cache_key, fallback_fn):
_cache = frappe.cache() _cache = frappe.cache()
@ -101,39 +126,18 @@ class Workspace:
return self.user.allow_modules return self.user.allow_modules
def get_page_for_user(self): def get_onboarding_doc(self, onboarding):
filters = {
'extends': self.page_name,
'for_user': frappe.session.user
}
user_pages = frappe.get_all("Workspace", filters=filters, limit=1)
if user_pages:
return frappe.get_cached_doc("Workspace", user_pages[0])
filters = {
'extends_another_page': 1,
'extends': self.page_name,
'is_default': 1
}
default_page = frappe.get_all("Workspace", filters=filters, limit=1)
if default_page:
return frappe.get_cached_doc("Workspace", default_page[0])
self.get_pages_to_extend()
return frappe.get_cached_doc("Workspace", self.page_name)
def get_onboarding_doc(self):
# Check if onboarding is enabled # Check if onboarding is enabled
if not frappe.get_system_settings("enable_onboarding"): if not frappe.get_system_settings("enable_onboarding"):
return None return None
if not self.doc.onboarding: if not self.onboarding_list:
return None return None
if frappe.db.get_value("Module Onboarding", self.doc.onboarding, "is_complete"): if frappe.db.get_value("Module Onboarding", onboarding, "is_complete"):
return None return None
doc = frappe.get_doc("Module Onboarding", self.doc.onboarding) doc = frappe.get_doc("Module Onboarding", onboarding)
# Check if user is allowed # Check if user is allowed
allowed_roles = set(doc.get_allowed_roles()) allowed_roles = set(doc.get_allowed_roles())
@ -197,14 +201,9 @@ class Workspace:
'items': self.get_shortcuts() 'items': self.get_shortcuts()
} }
if self.onboarding_doc: self.onboardings = {
self.onboarding = { 'items': self.get_onboardings()
'label': _(self.onboarding_doc.title), }
'subtitle': _(self.onboarding_doc.subtitle),
'success': _(self.onboarding_doc.success_message),
'docs_url': self.onboarding_doc.documentation_url,
'items': self.get_onboarding_steps()
}
def _doctype_contains_a_record(self, name): def _doctype_contains_a_record(self, name):
exists = self.table_counts.get(name, False) exists = self.table_counts.get(name, False)
@ -333,9 +332,26 @@ class Workspace:
return items return items
@handle_not_exist @handle_not_exist
def get_onboarding_steps(self): def get_onboardings(self):
if self.onboarding_list:
for onboarding in self.onboarding_list:
onboarding_doc = self.get_onboarding_doc(onboarding)
if onboarding_doc:
item = {
'label': _(onboarding),
'title': _(onboarding_doc.title),
'subtitle': _(onboarding_doc.subtitle),
'success': _(onboarding_doc.success_message),
'docs_url': onboarding_doc.documentation_url,
'items': self.get_onboarding_steps(onboarding_doc)
}
self.onboardings.append(item)
return self.onboardings
@handle_not_exist
def get_onboarding_steps(self, onboarding_doc):
steps = [] steps = []
for doc in self.onboarding_doc.get_steps(): for doc in onboarding_doc.get_steps():
step = doc.as_dict().copy() step = doc.as_dict().copy()
step.label = _(doc.title) step.label = _(doc.title)
if step.action == "Create Entry": if step.action == "Create Entry":
@ -358,13 +374,13 @@ def get_desktop_page(page):
dict: dictionary of cards, charts and shortcuts to be displayed on website dict: dictionary of cards, charts and shortcuts to be displayed on website
""" """
try: try:
wspace = Workspace(page) wspace = Workspace(loads(page))
wspace.build_workspace() wspace.build_workspace()
return { return {
'charts': wspace.charts, 'charts': wspace.charts,
'shortcuts': wspace.shortcuts, 'shortcuts': wspace.shortcuts,
'cards': wspace.cards, 'cards': wspace.cards,
'onboarding': wspace.onboarding, 'onboardings': wspace.onboardings,
'allow_customization': not wspace.doc.disable_user_customization 'allow_customization': not wspace.doc.disable_user_customization
} }
except DoesNotExistError: except DoesNotExistError:
@ -372,39 +388,45 @@ def get_desktop_page(page):
return {} return {}
@frappe.whitelist() @frappe.whitelist()
def get_desk_sidebar_items(): def get_wspace_sidebar_items():
"""Get list of sidebar items for desk""" """Get list of sidebar items for desk"""
has_access = "Workspace Manager" in frappe.get_roles()
# don't get domain restricted pages # don't get domain restricted pages
blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules() blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules()
blocked_modules.append('Dummy Module')
filters = { filters = {
'restrict_to_domain': ['in', frappe.get_active_domains()], 'restrict_to_domain': ['in', frappe.get_active_domains()],
'extends_another_page': 0,
'for_user': '',
'module': ['not in', blocked_modules] 'module': ['not in', blocked_modules]
} }
if not frappe.local.conf.developer_mode: if has_access:
filters['developer_mode_only'] = '0' filters = []
# pages sorted based on pinned to top and then by name # pages sorted based on sequence id
order_by = "pin_to_top desc, pin_to_bottom asc, name asc" order_by = "sequence_id asc"
all_pages = frappe.get_all("Workspace", fields=["name", "category", "icon", "module"], fields = ["name", "title", "for_user", "parent_page", "content", "public", "module", "icon"]
filters=filters, order_by=order_by, ignore_permissions=True) all_pages = frappe.get_all("Workspace", fields=fields, filters=filters, order_by=order_by, ignore_permissions=True)
pages = [] pages = []
private_pages = []
# Filter Page based on Permission # Filter Page based on Permission
for page in all_pages: for page in all_pages:
try: try:
wspace = Workspace(page.get('name'), True) wspace = Workspace(page)
if wspace.is_page_allowed(): if wspace.is_permitted() and wspace.is_page_allowed() or has_access:
pages.append(page) if page.public:
pages.append(page)
elif page.for_user == frappe.session.user:
private_pages.append(page)
page['label'] = _(page.get('name')) page['label'] = _(page.get('name'))
except frappe.PermissionError: except frappe.PermissionError:
pass pass
if private_pages:
pages.extend(private_pages)
return pages return {'pages': pages, 'has_access': has_access}
def get_table_with_counts(): def get_table_with_counts():
counts = frappe.cache().get_value("information_schema:counts") counts = frappe.cache().get_value("information_schema:counts")
@ -471,7 +493,7 @@ def get_custom_workspace_for_user(page):
""" """
filters = { filters = {
'extends': page, 'extends': page,
'for_user': frappe.session.user 'for_user': frappe.session.user,
} }
pages = frappe.get_list("Workspace", filters=filters) pages = frappe.get_list("Workspace", filters=filters)
if pages: if pages:
@ -481,7 +503,6 @@ def get_custom_workspace_for_user(page):
doc.for_user = frappe.session.user doc.for_user = frappe.session.user
return doc return doc
@frappe.whitelist() @frappe.whitelist()
def save_customization(page, config): def save_customization(page, config):
"""Save customizations as a separate doctype in Workspace per user """Save customizations as a separate doctype in Workspace per user
@ -540,6 +561,80 @@ def save_customization(page, config):
return True return True
def save_new_widget(doc, page, blocks, new_widgets):
widgets = _dict(loads(new_widgets))
if widgets.chart:
doc.charts.extend(new_widget(widgets.chart, "Workspace Chart", "charts"))
if widgets.shortcut:
doc.shortcuts.extend(new_widget(widgets.shortcut, "Workspace Shortcut", "shortcuts"))
if widgets.card:
doc.build_links_table_from_card(widgets.card)
# remove duplicate and unwanted widgets
if widgets:
clean_up(doc, blocks)
try:
doc.save(ignore_permissions=True)
except (ValidationError, TypeError) as e:
# Create a json string to log
json_config = dumps(widgets, sort_keys=True, indent=4)
# Error log body
log = \
"""
page: {0}
config: {1}
exception: {2}
""".format(page, json_config, e)
frappe.log_error(log, _("Could not save customization"))
return False
return True
def clean_up(original_page, blocks):
page_widgets = {}
for wid in ['shortcut', 'card', 'chart']:
# get list of widget's name from blocks
page_widgets[wid] = [x['data'][wid + '_name'] for x in loads(blocks) if x['type'] == wid]
# shortcut & chart cleanup
for wid in ['shortcut', 'chart']:
updated_widgets = []
original_page.get(wid+'s').reverse()
for w in original_page.get(wid+'s'):
if w.label in page_widgets[wid] and w.label not in [x.label for x in updated_widgets]:
updated_widgets.append(w)
original_page.set(wid+'s', updated_widgets)
# card cleanup
for i, v in enumerate(original_page.links):
if v.type == 'Card Break' and v.label not in page_widgets['card']:
del original_page.links[i : i+v.link_count+1]
def new_widget(config, doctype, parentfield):
if not config:
return []
prepare_widget_list = []
for idx, widget in enumerate(config):
# Some cleanup
widget.pop("name", None)
# New Doc
doc = frappe.new_doc(doctype)
doc.update(widget)
# Manually Set IDX
doc.idx = idx + 1
# Set Parent Field
doc.parentfield = parentfield
prepare_widget_list.append(doc)
return prepare_widget_list
def prepare_widget(config, doctype, parentfield): def prepare_widget(config, doctype, parentfield):
"""Create widget child table entries with parent details """Create widget child table entries with parent details

View file

@ -2,10 +2,26 @@
# Copyright (c) 2020, Frappe Technologies and contributors # Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt # For license information, please see license.txt
# import frappe import frappe
from frappe import _
import json
from frappe.model.document import Document from frappe.model.document import Document
class OnboardingStep(Document): class OnboardingStep(Document):
def before_export(self, doc): def before_export(self, doc):
doc.is_complete = 0 doc.is_complete = 0
doc.is_skipped = 0 doc.is_skipped = 0
@frappe.whitelist()
def get_onboarding_steps(ob_steps):
steps = []
for s in json.loads(ob_steps):
doc = frappe.get_doc('Onboarding Step', s.get('step'))
step = doc.as_dict().copy()
step.label = _(doc.title)
if step.action == "Create Entry":
step.is_submittable = frappe.db.get_value("DocType", step.reference_document, 'is_submittable', cache=True)
steps.append(step)
return steps

View file

@ -8,8 +8,11 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"label", "label",
"title",
"sequence_id",
"for_user", "for_user",
"extends", "extends",
"parent_page",
"module", "module",
"category", "category",
"icon", "icon",
@ -24,6 +27,9 @@
"pin_to_top", "pin_to_top",
"pin_to_bottom", "pin_to_bottom",
"hide_custom", "hide_custom",
"public",
"content_section",
"content",
"section_break_2", "section_break_2",
"charts_label", "charts_label",
"charts", "charts",
@ -32,7 +38,8 @@
"shortcuts", "shortcuts",
"section_break_18", "section_break_18",
"cards_label", "cards_label",
"links" "links",
"roles"
], ],
"fields": [ "fields": [
{ {
@ -199,7 +206,7 @@
}, },
{ {
"fieldname": "icon", "fieldname": "icon",
"fieldtype": "Data", "fieldtype": "Icon",
"label": "Icon" "label": "Icon"
}, },
{ {
@ -209,16 +216,53 @@
"options": "Workspace Link" "options": "Workspace Link"
}, },
{ {
"default": "0", "default": "0",
"depends_on": "extends_another_page", "depends_on": "extends_another_page",
"description": "Sets the current page as default for all users", "description": "Sets the current page as default for all users",
"fieldname": "is_default", "fieldname": "is_default",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Default" "label": "Is Default"
} },
{
"default": "0",
"fieldname": "public",
"fieldtype": "Check",
"label": "Public"
},
{
"fieldname": "title",
"fieldtype": "Data",
"label": "Title"
},
{
"fieldname": "parent_page",
"fieldtype": "Data",
"label": "Parent Page"
},
{
"fieldname": "content_section",
"fieldtype": "Section Break",
"label": "Content"
},
{
"fieldname": "content",
"fieldtype": "Long Text",
"label": "Content"
},
{
"fieldname": "sequence_id",
"fieldtype": "Int",
"label": "Sequence Id"
},
{
"fieldname": "roles",
"fieldtype": "Table",
"label": "Roles",
"options": "Has Role"
}
], ],
"links": [], "links": [],
"modified": "2021-01-21 12:09:36.156614", "modified": "2021-08-05 11:49:09.028243",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Desk", "module": "Desk",
"name": "Workspace", "name": "Workspace",
@ -232,7 +276,7 @@
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "System Manager", "role": "Workspace Manager",
"share": 1, "share": 1,
"write": 1 "write": 1
}, },
@ -248,4 +292,4 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC"
} }

View file

@ -6,6 +6,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.modules.export_file import export_to_files from frappe.modules.export_file import export_to_files
from frappe.model.document import Document from frappe.model.document import Document
from frappe.desk.desktop import save_new_widget
from frappe.desk.utils import validate_route_conflict from frappe.desk.utils import validate_route_conflict
from json import loads from json import loads
@ -27,7 +28,7 @@ class Workspace(Document):
if disable_saving_as_standard(): if disable_saving_as_standard():
return return
if frappe.conf.developer_mode and self.is_standard: if frappe.conf.developer_mode and self.module and self.public:
export_to_files(record_list=[['Workspace', self.name]], record_module=self.module) export_to_files(record_list=[['Workspace', self.name]], record_module=self.module)
@staticmethod @staticmethod
@ -98,6 +99,37 @@ class Workspace(Document):
"is_query_report": link.get('is_query_report') "is_query_report": link.get('is_query_report')
}) })
def build_links_table_from_card(self, config):
for idx, card in enumerate(config):
links = loads(card.get('links'))
# remove duplicate before adding
for idx, link in enumerate(self.links):
if link.label == card.get('label') and link.type == 'Card Break':
del self.links[idx : idx + link.link_count + 1]
self.append('links', {
"label": card.get('label'),
"type": "Card Break",
"icon": card.get('icon'),
"hidden": card.get('hidden') or False,
"link_count": card.get('link_count'),
"idx": 1 if not self.links else self.links[-1].idx + 1
})
for link in links:
self.append('links', {
"label": link.get('label'),
"type": "Link",
"link_type": link.get('link_type'),
"link_to": link.get('link_to'),
"onboard": link.get('onboard'),
"only_for": link.get('only_for'),
"dependencies": link.get('dependencies'),
"is_query_report": link.get('is_query_report'),
"idx": self.links[-1].idx + 1
})
def disable_saving_as_standard(): def disable_saving_as_standard():
return frappe.flags.in_install or \ return frappe.flags.in_install or \
@ -123,3 +155,84 @@ def get_link_type(key):
def get_report_type(report): def get_report_type(report):
report_type = frappe.get_value("Report", report, "report_type") report_type = frappe.get_value("Report", report, "report_type")
return report_type in ["Query Report", "Script Report", "Custom Report"] return report_type in ["Query Report", "Script Report", "Custom Report"]
@frappe.whitelist()
def save_page(title, icon, parent, public, sb_public_items, sb_private_items, deleted_pages, new_widgets, blocks, save):
save = frappe.parse_json(save)
public = frappe.parse_json(public)
if save:
doc = frappe.new_doc('Workspace')
doc.title = title
doc.icon = icon
doc.content = blocks
doc.parent_page = parent
if public:
doc.label = title
doc.public = 1
else:
doc.label = title + "-" + frappe.session.user
doc.for_user = frappe.session.user
doc.save(ignore_permissions=True)
else:
if public:
filters = {
'public': public,
'label': title
}
else:
filters = {
'for_user': frappe.session.user,
'label': title + "-" + frappe.session.user
}
pages = frappe.get_list("Workspace", filters=filters)
if pages:
doc = frappe.get_doc("Workspace", pages[0])
doc.content = blocks
doc.save(ignore_permissions=True)
if loads(new_widgets):
save_new_widget(doc, title, blocks, new_widgets)
if loads(sb_public_items) or loads(sb_private_items):
sort_pages(loads(sb_public_items), loads(sb_private_items))
if loads(deleted_pages):
return delete_pages(loads(deleted_pages))
return {"name": title, "public": public}
def delete_pages(deleted_pages):
for page in deleted_pages:
if page.get("public") and "Workspace Manager" not in frappe.get_roles():
return {"name": page.get("title"), "public": 1}
if frappe.db.exists("Workspace", page.get("name")):
frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True)
return {"name": "Home", "public": 1}
def sort_pages(sb_public_items, sb_private_items):
wspace_public_pages = get_page_list(['name', 'title'], {'public': 1})
wspace_private_pages = get_page_list(['name', 'title'], {'for_user': frappe.session.user})
if sb_private_items:
sort_page(wspace_private_pages, sb_private_items)
if sb_public_items and "Workspace Manager" in frappe.get_roles():
sort_page(wspace_public_pages, sb_public_items)
def sort_page(wspace_pages, pages):
for seq, d in enumerate(pages):
for page in wspace_pages:
if page.title == d.get('title'):
doc = frappe.get_doc('Workspace', page.name)
doc.sequence_id = seq + 1
doc.parent_page = d.get('parent_page') or ""
doc.save(ignore_permissions=True)
break
def get_page_list(fields, filters):
return frappe.get_list("Workspace", fields=fields, filters=filters, order_by='sequence_id asc')

View file

@ -8,15 +8,16 @@
"type", "type",
"label", "label",
"icon", "icon",
"only_for",
"hidden", "hidden",
"link_details_section", "link_details_section",
"link_type", "link_type",
"link_to", "link_to",
"column_break_7", "column_break_7",
"dependencies", "dependencies",
"only_for",
"onboard", "onboard",
"is_query_report" "is_query_report",
"link_count"
], ],
"fields": [ "fields": [
{ {
@ -99,12 +100,19 @@
"fieldname": "is_query_report", "fieldname": "is_query_report",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Query Report" "label": "Is Query Report"
},
{
"depends_on": "eval:doc.type == \"Card Break\"",
"fieldname": "link_count",
"fieldtype": "Int",
"hidden": 1,
"label": "Link Count"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2021-05-13 13:10:18.128512", "modified": "2021-06-01 11:23:28.990593",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Desk", "module": "Desk",
"name": "Workspace Link", "name": "Workspace Link",

View file

@ -1,22 +1,27 @@
{ {
"category": "Administration", "category": "",
"charts": [], "charts": [],
"content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Backup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Google Services\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Authentication\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payments\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
"creation": "2020-03-02 15:16:18.714190", "creation": "2020-03-02 15:16:18.714190",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 1, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "integration", "icon": "integration",
"idx": 0, "idx": 0,
"is_standard": 1, "is_default": 0,
"is_standard": 0,
"label": "Integrations", "label": "Integrations",
"links": [ "links": [
{ {
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Backup", "label": "Backup",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -25,6 +30,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Dropbox Settings", "label": "Dropbox Settings",
"link_count": 0,
"link_to": "Dropbox Settings", "link_to": "Dropbox Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -35,6 +41,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "S3 Backup Settings", "label": "S3 Backup Settings",
"link_count": 0,
"link_to": "S3 Backup Settings", "link_to": "S3 Backup Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -45,6 +52,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Drive", "label": "Google Drive",
"link_count": 0,
"link_to": "Google Drive", "link_to": "Google Drive",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -54,6 +62,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Services", "label": "Google Services",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -62,6 +71,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Settings", "label": "Google Settings",
"link_count": 0,
"link_to": "Google Settings", "link_to": "Google Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -72,6 +82,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Contacts", "label": "Google Contacts",
"link_count": 0,
"link_to": "Google Contacts", "link_to": "Google Contacts",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -82,6 +93,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Calendar", "label": "Google Calendar",
"link_count": 0,
"link_to": "Google Calendar", "link_to": "Google Calendar",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -92,6 +104,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Google Drive", "label": "Google Drive",
"link_count": 0,
"link_to": "Google Drive", "link_to": "Google Drive",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -101,6 +114,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Authentication", "label": "Authentication",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -109,6 +123,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Social Login Key", "label": "Social Login Key",
"link_count": 0,
"link_to": "Social Login Key", "link_to": "Social Login Key",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -119,6 +134,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "LDAP Settings", "label": "LDAP Settings",
"link_count": 0,
"link_to": "LDAP Settings", "link_to": "LDAP Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -129,6 +145,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "OAuth Client", "label": "OAuth Client",
"link_count": 0,
"link_to": "OAuth Client", "link_to": "OAuth Client",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -139,6 +156,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "OAuth Provider Settings", "label": "OAuth Provider Settings",
"link_count": 0,
"link_to": "OAuth Provider Settings", "link_to": "OAuth Provider Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -148,6 +166,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Payments", "label": "Payments",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -156,6 +175,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Braintree Settings", "label": "Braintree Settings",
"link_count": 0,
"link_to": "Braintree Settings", "link_to": "Braintree Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -166,6 +186,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "PayPal Settings", "label": "PayPal Settings",
"link_count": 0,
"link_to": "PayPal Settings", "link_to": "PayPal Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -176,6 +197,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Razorpay Settings", "label": "Razorpay Settings",
"link_count": 0,
"link_to": "Razorpay Settings", "link_to": "Razorpay Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -186,6 +208,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Stripe Settings", "label": "Stripe Settings",
"link_count": 0,
"link_to": "Stripe Settings", "link_to": "Stripe Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -196,6 +219,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Paytm Settings", "label": "Paytm Settings",
"link_count": 0,
"link_to": "Paytm Settings", "link_to": "Paytm Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -205,6 +229,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Settings", "label": "Settings",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -213,6 +238,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Webhook", "label": "Webhook",
"link_count": 0,
"link_to": "Webhook", "link_to": "Webhook",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -223,38 +249,37 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Slack Webhook URL", "label": "Slack Webhook URL",
"link_count": 0,
"link_to": "Slack Webhook URL", "link_to": "Slack Webhook URL",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
}, },
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Twilio Settings",
"link_to": "Twilio Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{ {
"dependencies": "", "dependencies": "",
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "SMS Settings", "label": "SMS Settings",
"link_count": 0,
"link_to": "SMS Settings", "link_to": "SMS Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2020-12-01 13:38:39.706680", "modified": "2021-08-05 12:16:00.355267",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Integrations", "module": "Integrations",
"name": "Integrations", "name": "Integrations",
"onboarding": "",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"shortcuts": [] "public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 15,
"shortcuts": [],
"title": "Integrations"
} }

View file

@ -182,3 +182,4 @@ frappe.patches.v13_0.jinja_hook
frappe.patches.v13_0.update_notification_channel_if_empty frappe.patches.v13_0.update_notification_channel_if_empty
frappe.patches.v14_0.drop_data_import_legacy frappe.patches.v14_0.drop_data_import_legacy
frappe.patches.v14_0.rename_cancelled_documents frappe.patches.v14_0.rename_cancelled_documents
frappe.patches.v14_0.update_workspace2

View file

@ -0,0 +1,69 @@
import frappe
import json
from frappe import _
def execute():
frappe.reload_doc('desk', 'doctype', 'workspace', force=True)
order_by = "pin_to_top desc, pin_to_bottom asc, name asc"
for seq, wspace in enumerate(frappe.get_all('Workspace', order_by=order_by)):
doc = frappe.get_doc('Workspace', wspace.name)
content = create_content(doc)
update_wspace(doc, seq, content)
frappe.db.commit()
def create_content(doc):
content = []
if doc.onboarding:
content.append({"type":"onboarding","data":{"onboarding_name":doc.onboarding,"col":12}})
if doc.charts:
invalid_links = []
for c in doc.charts:
if c.get_invalid_links()[0]:
invalid_links.append(c)
else:
content.append({"type":"chart","data":{"chart_name":c.label,"col":12}})
for l in invalid_links:
del doc.charts[doc.charts.index(l)]
if doc.shortcuts:
invalid_links = []
if doc.charts:
content.append({"type":"spacer","data":{"col":12}})
content.append({"type":"header","data":{"text":doc.shortcuts_label or _("Your Shortcuts"),"level":4,"col":12}})
for s in doc.shortcuts:
if s.get_invalid_links()[0]:
invalid_links.append(s)
else:
content.append({"type":"shortcut","data":{"shortcut_name":s.label,"col":4}})
for l in invalid_links:
del doc.shortcuts[doc.shortcuts.index(l)]
if doc.links:
invalid_links = []
content.append({"type":"spacer","data":{"col":12}})
content.append({"type":"header","data":{"text":doc.cards_label or _("Reports & Masters"),"level":4,"col":12}})
for l in doc.links:
if l.type == 'Card Break':
content.append({"type":"card","data":{"card_name":l.label,"col":4}})
if l.get_invalid_links()[0]:
invalid_links.append(l)
for l in invalid_links:
del doc.links[doc.links.index(l)]
return content
def update_wspace(doc, seq, content):
if not doc.is_standard and not doc.public:
doc.sequence_id = seq + 1
doc.content = json.dumps(content)
doc.public = 0
doc.title = doc.extends
doc.extends = ''
doc.category = ''
doc.onboarding = ''
doc.extends_another_page = 0
doc.is_default = 0
doc.is_standard = 0
doc.developer_mode_only = 0
doc.disable_user_customization = 0
doc.pin_to_top = 0
doc.pin_to_bottom = 0
doc.hide_custom = 0
doc.save(ignore_permissions=True)

View file

@ -57,6 +57,9 @@
<symbol viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" id="icon-small-down"> <symbol viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" id="icon-small-down">
<path d="M2.625 4.375L6 7.75l3.375-3.375" stroke="var(--icon-stroke)" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M2.625 4.375L6 7.75l3.375-3.375" stroke="var(--icon-stroke)" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path>
</symbol> </symbol>
<symbol viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" id="icon-small-up">
<path d="M9.5 7.75L6 4.25L2.5 7.75" stroke="var(--icon-stroke)" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
<symbol viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" id="icon-small-add"> <symbol viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" id="icon-small-add">
<path d="M8 4v8M4 8h8" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M8 4v8M4 8h8" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"></path>
</symbol> </symbol>

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View file

@ -82,6 +82,7 @@ import "./frappe/ui/toolbar/toolbar.js";
import "./frappe/views/communication.js"; import "./frappe/views/communication.js";
import "./frappe/views/translation_manager.js"; import "./frappe/views/translation_manager.js";
import "./frappe/views/workspace/workspace.js"; import "./frappe/views/workspace/workspace.js";
import "./frappe/views/workspace/blocks/index.js";
import "./frappe/widgets/widget_group.js"; import "./frappe/widgets/widget_group.js";

View file

@ -283,7 +283,7 @@ frappe.Application = class Application {
frappe.workspaces = {}; frappe.workspaces = {};
for (let page of frappe.boot.allowed_workspaces || []) { for (let page of frappe.boot.allowed_workspaces || []) {
frappe.modules[page.module]=page; frappe.modules[page.module]=page;
frappe.workspaces[frappe.router.slug(page.name)] = page; frappe.workspaces[frappe.router.slug(page.title)] = page;
} }
if (!frappe.workspaces['home']) { if (!frappe.workspaces['home']) {
// default workspace is settings for Frappe // default workspace is settings for Frappe

View file

@ -118,6 +118,7 @@ frappe.router = {
convert_to_standard_route(route) { convert_to_standard_route(route) {
// /app/settings = ["Workspaces", "Settings"] // /app/settings = ["Workspaces", "Settings"]
// /app/private/settings = ["Workspaces", "private", "Settings"]
// /app/user = ["List", "User"] // /app/user = ["List", "User"]
// /app/user/view/report = ["List", "User", "Report"] // /app/user/view/report = ["List", "User", "Report"]
// /app/user/view/tree = ["Tree", "User"] // /app/user/view/tree = ["Tree", "User"]
@ -126,8 +127,11 @@ frappe.router = {
// /app/event/view/calendar/default = ["List", "Event", "Calendar", "Default"] // /app/event/view/calendar/default = ["List", "Event", "Calendar", "Default"]
if (frappe.workspaces[route[0]]) { if (frappe.workspaces[route[0]]) {
// workspace // public workspace
route = ['Workspaces', frappe.workspaces[route[0]].name]; route = ['Workspaces', frappe.workspaces[route[0]].title];
} else if (frappe.workspaces[route[1]]) {
// private workspace
route = ['Workspaces', 'private', frappe.workspaces[route[1]].title];
} else if (this.routes[route[0]]) { } else if (this.routes[route[0]]) {
// route // route
route = this.set_doctype_route(route); route = this.set_doctype_route(route);
@ -136,6 +140,11 @@ frappe.router = {
return route; return route;
}, },
doctype_route_exist(route) {
route = this.get_sub_path_string(route).split('/');
return this.routes[route[0]];
},
set_doctype_route(route) { set_doctype_route(route) {
let doctype_route = this.routes[route[0]]; let doctype_route = this.routes[route[0]];
// doctype route // doctype route

View file

@ -1325,6 +1325,19 @@ Object.assign(frappe.utils, {
return clipboard_data.getData('Text'); return clipboard_data.getData('Text');
}, },
add_custom_button(html, action, class_name = "", title="", btn_type, wrapper, prepend) {
if (!btn_type) btn_type = 'btn-secondary';
let button = $(
`<button class="btn ${btn_type} btn-xs ${class_name}" title="${title}">${html}</button>`
);
button.click(event => {
event.stopPropagation();
action && action(event);
});
!prepend && button.appendTo(wrapper);
prepend && wrapper.prepend(button);
},
sleep(time) { sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time)); return new Promise((resolve) => setTimeout(resolve, time));
} }

View file

@ -0,0 +1,115 @@
import get_dialog_constructor from "../../../widgets/widget_dialog.js";
export default class Block {
constructor(opts) {
Object.assign(this, opts);
}
make(block, block_name, widget_type = block) {
let block_data = this.config.page_data[block+'s'].items.find(obj => {
return obj.label == block_name;
});
if (!block_data) return false;
this.wrapper.innerHTML = '';
block_data.in_customize_mode = !this.readOnly;
this.block_widget = new frappe.widget.SingleWidgetGroup({
container: this.wrapper,
type: widget_type,
class_name: block == 'chart' ? 'widget-charts' : '',
options: this.options,
widgets: block_data,
api: this.api,
block: this.block
});
this.wrapper.setAttribute(block+'_name', block_name);
if (!this.readOnly) {
this.block_widget.customize();
}
return true;
}
rendered() {
var e = this.wrapper.closest('.ce-block');
e.classList.add("col-" + this.get_col());
}
new(block, widget_type = block) {
const dialog_class = get_dialog_constructor(widget_type);
let block_name = block+'_name';
this.dialog = new dialog_class({
label: this.label,
type: widget_type,
primary_action: (widget) => {
widget.in_customize_mode = 1;
this.block_widget = frappe.widget.make_widget({
...widget,
widget_type: widget_type,
container: this.wrapper,
options: {
...this.options,
on_delete: () => this.api.blocks.delete(),
on_edit: () => this.on_edit(this.block_widget)
}
});
this.block_widget.customize(this.options);
this.wrapper.setAttribute(block_name, this.block_widget.label);
this.new_block_widget = this.block_widget.get_config();
this.add_tune_button();
},
});
if (!this.readOnly && this.data && !this.data[block_name]) {
this.dialog.make();
}
}
on_edit(block_obj) {
let block_name = block_obj.edit_dialog.type+'_name';
if (block_obj.edit_dialog.type == 'links') {
block_name = 'card_name';
}
let block = block_obj.get_config();
this.block_widget.widgets = block;
this.wrapper.setAttribute(block_name, block.label);
this.new_block_widget = block_obj.get_config();
}
add_tune_button() {
let $widget_control = $(this.wrapper).find('.widget-control');
frappe.utils.add_custom_button(
frappe.utils.icon('dot-horizontal', 'xs'),
(event) => {
let evn = event;
!$('.ce-settings.ce-settings--opened').length &&
setTimeout(() => {
this.api.toolbar.toggleBlockSettings();
var position = $(evn.target).offset();
$('.ce-settings.ce-settings--opened').offset({
top: position.top + 25,
left: position.left - 77
});
}, 50);
},
"tune-btn",
`${__('Tune')}`,
null,
$widget_control,
true
);
}
get_col() {
let col = this.col || 12;
let class_name = "col-12";
let wrapper = this.wrapper.closest('.ce-block');
const col_class = new RegExp(/\bcol-.+?\b/, "g");
if (wrapper && wrapper.className.match(col_class)) {
wrapper.classList.forEach(function (cn) {
cn.match(col_class) && (class_name = cn);
});
let parts = class_name.split("-");
col = parseInt(parts[1]);
}
return col;
}
}

View file

@ -0,0 +1,59 @@
import Block from "./block.js";
export default class Card extends Block {
static get toolbox() {
return {
title: 'Card',
icon: '<svg height="20" width="20" viewBox="2 2 20 20"><path d="M7 15h3a1 1 0 000-2H7a1 1 0 000 2zM19 5H5a3 3 0 00-3 3v9a3 3 0 003 3h14a3 3 0 003-3V8a3 3 0 00-3-3zm1 12a1 1 0 01-1 1H5a1 1 0 01-1-1v-6h16zm0-8H4V8a1 1 0 011-1h14a1 1 0 011 1z"/></svg>'
};
}
static get isReadOnlySupported() {
return true;
}
constructor({ data, api, config, readOnly, block }) {
super({ data, api, config, readOnly, block });
this.sections = {};
this.col = this.data.col ? this.data.col : "12";
this.allow_customization = !this.readOnly;
this.options = {
allow_sorting: this.allow_customization,
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true,
};
}
render() {
this.wrapper = document.createElement('div');
this.new('card', 'links');
if (this.data && this.data.card_name) {
let has_data = this.make('card', this.data.card_name, 'links');
if (!has_data) return;
}
if (!this.readOnly) {
this.add_tune_button();
}
return this.wrapper;
}
validate(savedData) {
if (!savedData.card_name) {
return false;
}
return true;
}
save(blockContent) {
return {
card_name: blockContent.getAttribute('card_name'),
col: this.get_col(),
new: this.new_block_widget
};
}
}

View file

@ -0,0 +1,59 @@
import Block from "./block.js";
export default class Chart extends Block {
static get toolbox() {
return {
title: 'Chart',
icon: '<svg height="18" width="18" viewBox="0 0 512 512"><path d="M117.547 234.667H10.88c-5.888 0-10.667 4.779-10.667 10.667v256C.213 507.221 4.992 512 10.88 512h106.667c5.888 0 10.667-4.779 10.667-10.667v-256a10.657 10.657 0 00-10.667-10.666zM309.12 0H202.453c-5.888 0-10.667 4.779-10.667 10.667v490.667c0 5.888 4.779 10.667 10.667 10.667H309.12c5.888 0 10.667-4.779 10.667-10.667V10.667C319.787 4.779 315.008 0 309.12 0zM501.12 106.667H394.453c-5.888 0-10.667 4.779-10.667 10.667v384c0 5.888 4.779 10.667 10.667 10.667H501.12c5.888 0 10.667-4.779 10.667-10.667v-384c0-5.889-4.779-10.667-10.667-10.667z"/></svg>'
};
}
static get isReadOnlySupported() {
return true;
}
constructor({ data, api, config, readOnly, block }) {
super({ data, api, config, readOnly, block });
this.col = this.data.col ? this.data.col : "12";
this.allow_customization = !this.readOnly;
this.options = {
allow_sorting: this.allow_customization,
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true,
max_widget_count: 2,
};
}
render() {
this.wrapper = document.createElement('div');
this.new('chart');
if (this.data && this.data.chart_name) {
let has_data = this.make('chart', this.data.chart_name);
if (!has_data) return;
}
if (!this.readOnly) {
this.add_tune_button();
}
return this.wrapper;
}
validate(savedData) {
if (!savedData.chart_name) {
return false;
}
return true;
}
save(blockContent) {
return {
chart_name: blockContent.getAttribute('chart_name'),
col: this.get_col(),
new: this.new_block_widget
};
}
}

View file

@ -0,0 +1,339 @@
import Block from "./block.js";
export default class Header extends Block {
constructor({ data, config, api, readOnly }) {
super({ config, api, readOnly });
this._CSS = {
block: this.api.styles.block,
settingsButton: this.api.styles.settingsButton,
settingsButtonActive: this.api.styles.settingsButtonActive,
wrapper: 'ce-header',
};
this._settings = this.config;
this._data = this.normalizeData(data);
this.settingsButtons = [];
this._element = this.getTag();
this.data = data;
this.col = this.data.col ? this.data.col : "12";
}
normalizeData(data) {
const newData = {};
if (typeof data !== 'object') {
data = {};
}
newData.text = data.text || '';
newData.level = parseInt(data.level) || this.defaultLevel.number;
newData.col = parseInt(data.col) || 12;
return newData;
}
render() {
this.wrapper = document.createElement('div');
this.wrapper.contentEditable = this.readOnly ? 'false' : 'true';
if (!this.readOnly) {
let $widget_head = $(`<div class="widget-head"></div>`);
let $widget_control = $(`<div class="widget-control"></div>`);
$widget_head[0].appendChild(this._element);
$widget_control.appendTo($widget_head);
$widget_head.appendTo(this.wrapper);
this.wrapper.classList.add('widget', 'header');
frappe.utils.add_custom_button(
frappe.utils.icon('dot-horizontal', 'xs'),
(event) => {
let evn = event;
!$('.ce-settings.ce-settings--opened').length &&
setTimeout(() => {
this.api.toolbar.toggleBlockSettings();
var position = $(evn.target).offset();
$('.ce-settings.ce-settings--opened').offset({
top: position.top + 25,
left: position.left - 77
});
}, 50);
},
"tune-btn",
`${__('Tune')}`,
null,
$widget_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('drag', 'xs'),
null,
"drag-handle",
`${__('Drag')}`,
null,
$widget_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('delete', 'xs'),
() => this.api.blocks.delete(),
"delete-header",
`${__('Delete')}`,
null,
$widget_control
);
return this.wrapper;
}
return this._element;
}
renderSettings() {
const holder = document.createElement('DIV');
if (this.levels.length <= 1) {
return holder;
}
this.levels.forEach(level => {
const selectTypeButton = document.createElement('SPAN');
selectTypeButton.classList.add(this._CSS.settingsButton);
if (this.currentLevel.number === level.number) {
selectTypeButton.classList.add(this._CSS.settingsButtonActive);
}
selectTypeButton.innerHTML = level.svg;
selectTypeButton.dataset.level = level.number;
selectTypeButton.addEventListener('click', () => {
this.setLevel(level.number);
});
holder.appendChild(selectTypeButton);
this.settingsButtons.push(selectTypeButton);
});
return holder;
}
setLevel(level) {
this.data = {
level: level,
text: this.data.text,
};
this.settingsButtons.forEach(button => {
button.classList.toggle(this._CSS.settingsButtonActive, parseInt(button.dataset.level) === level);
});
}
merge(data) {
const newData = {
text: this.data.text + data.text,
level: this.data.level,
};
this.data = newData;
}
validate(blockData) {
return blockData.text.trim() !== '';
}
save(toolsContent) {
this.wrapper = this._element;
return {
text: toolsContent.innerText,
level: this.currentLevel.number,
col: this.get_col()
};
}
rendered() {
var e = this._element.closest('.ce-block');
e.classList.add("col-" + this.get_col());
}
static get conversionConfig() {
return {
export: 'text', // use 'text' property for other blocks
import: 'text', // fill 'text' property from other block's export string
};
}
static get sanitize() {
return {
level: false,
text: {},
};
}
static get isReadOnlySupported() {
return true;
}
get data() {
this._data.text = this._element.innerHTML;
this._data.level = this.currentLevel.number;
return this._data;
}
set data(data) {
this._data = this.normalizeData(data);
if (data.level !== undefined && this._element.parentNode) {
const newHeader = this.getTag();
newHeader.innerHTML = this._element.innerHTML;
this._element.parentNode.replaceChild(newHeader, this._element);
this._element = newHeader;
}
if (data.text !== undefined) {
this._element.innerHTML = this._data.text || '';
}
if (!this.readOnly && this.wrapper) {
this.wrapper.classList.add('widget', 'header');
}
}
getTag() {
const tag = document.createElement(this.currentLevel.tag);
tag.innerHTML = this._data.text || '';
tag.classList.add(this._CSS.wrapper);
if (!this.readOnly) {
tag.contentEditable = true;
}
tag.dataset.placeholder = this.api.i18n.t(this._settings.placeholder || '');
return tag;
}
get currentLevel() {
let level = this.levels.find(levelItem => levelItem.number === this._data.level);
if (!level) {
level = this.defaultLevel;
}
return level;
}
get defaultLevel() {
if (this._settings.defaultLevel) {
const userSpecified = this.levels.find(levelItem => {
return levelItem.number === this._settings.defaultLevel;
});
if (userSpecified) {
return userSpecified;
} else {
// console.warn('(ง\'̀-\'́)ง Heading Tool: the default level specified was not found in available levels');
}
}
return this.levels[1];
}
get levels() {
const availableLevels = [
{
number: 1,
tag: 'H1',
svg: '<svg width="16" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.14 1.494V4.98h4.62V1.494c0-.498.098-.871.293-1.12A.927.927 0 0 1 7.82 0c.322 0 .583.123.782.37.2.246.3.62.3 1.124v9.588c0 .503-.101.88-.303 1.128a.957.957 0 0 1-.779.374.921.921 0 0 1-.77-.378c-.193-.251-.29-.626-.29-1.124V6.989H2.14v4.093c0 .503-.1.88-.302 1.128a.957.957 0 0 1-.778.374.921.921 0 0 1-.772-.378C.096 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.285.374A.922.922 0 0 1 1.06 0c.321 0 .582.123.782.37.199.246.299.62.299 1.124zm11.653 9.985V5.27c-1.279.887-2.14 1.33-2.583 1.33a.802.802 0 0 1-.563-.228.703.703 0 0 1-.245-.529c0-.232.08-.402.241-.511.161-.11.446-.25.854-.424.61-.259 1.096-.532 1.462-.818a5.84 5.84 0 0 0 .97-.962c.282-.355.466-.573.552-.655.085-.082.246-.123.483-.123.267 0 .481.093.642.28.161.186.242.443.242.77v7.813c0 .914-.345 1.371-1.035 1.371-.307 0-.554-.093-.74-.28-.187-.186-.28-.461-.28-.825z"/></svg>',
},
{
number: 2,
tag: 'H2',
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm10.99 9.288h3.527c.351 0 .62.072.804.216.185.144.277.34.277.588 0 .22-.073.408-.22.56-.146.154-.368.23-.665.23h-4.972c-.338 0-.601-.093-.79-.28a.896.896 0 0 1-.284-.659c0-.162.06-.377.182-.645s.255-.478.399-.631a38.617 38.617 0 0 1 1.621-1.598c.482-.444.827-.735 1.034-.875.369-.261.676-.523.922-.787.245-.263.432-.534.56-.81.129-.278.193-.549.193-.815 0-.288-.069-.546-.206-.773a1.428 1.428 0 0 0-.56-.53 1.618 1.618 0 0 0-.774-.19c-.59 0-1.054.26-1.392.777-.045.068-.12.252-.226.554-.106.302-.225.534-.358.696-.133.162-.328.243-.585.243a.76.76 0 0 1-.56-.223c-.149-.148-.223-.351-.223-.608 0-.31.07-.635.21-.972.139-.338.347-.645.624-.92a3.093 3.093 0 0 1 1.054-.665c.426-.169.924-.253 1.496-.253.69 0 1.277.108 1.764.324.315.144.592.343.83.595.24.252.425.544.558.875.133.33.2.674.2 1.03 0 .558-.14 1.066-.416 1.523-.277.457-.56.815-.848 1.074-.288.26-.771.666-1.45 1.22-.677.554-1.142.984-1.394 1.29a3.836 3.836 0 0 0-.331.44z"/></svg>',
},
{
number: 3,
tag: 'H3',
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm11.61 4.919c.418 0 .778-.123 1.08-.368.301-.245.452-.597.452-1.055 0-.35-.12-.65-.36-.902-.241-.252-.566-.378-.974-.378-.277 0-.505.038-.684.116a1.1 1.1 0 0 0-.426.306 2.31 2.31 0 0 0-.296.49c-.093.2-.178.388-.255.565a.479.479 0 0 1-.245.225.965.965 0 0 1-.409.081.706.706 0 0 1-.5-.22c-.152-.148-.228-.345-.228-.59 0-.236.071-.484.214-.745a2.72 2.72 0 0 1 .627-.746 3.149 3.149 0 0 1 1.024-.568 4.122 4.122 0 0 1 1.368-.214c.44 0 .842.06 1.205.18.364.12.679.294.947.52.267.228.47.49.606.79.136.3.204.622.204.967 0 .454-.099.843-.296 1.168-.198.324-.48.64-.848.95.354.19.653.408.895.653.243.245.426.516.548.813.123.298.184.619.184.964 0 .413-.083.812-.248 1.198-.166.386-.41.73-.732 1.031a3.49 3.49 0 0 1-1.147.708c-.443.17-.932.256-1.467.256a3.512 3.512 0 0 1-1.464-.293 3.332 3.332 0 0 1-1.699-1.64c-.142-.314-.214-.573-.214-.777 0-.263.085-.475.255-.636a.89.89 0 0 1 .637-.242c.127 0 .25.037.367.112a.53.53 0 0 1 .232.27c.236.63.489 1.099.759 1.405.27.306.65.46 1.14.46a1.714 1.714 0 0 0 1.46-.824c.17-.273.256-.588.256-.947 0-.53-.145-.947-.436-1.249-.29-.302-.694-.453-1.212-.453-.09 0-.231.01-.422.028-.19.018-.313.027-.367.027-.25 0-.443-.062-.579-.187-.136-.125-.204-.299-.204-.521 0-.218.081-.394.245-.528.163-.134.406-.2.728-.2h.28z"/></svg>',
},
{
number: 4,
tag: 'H4',
svg: '<svg width="20" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm13.003 10.09v-1.252h-3.38c-.427 0-.746-.097-.96-.29-.213-.193-.32-.456-.32-.788 0-.085.016-.171.048-.259.031-.088.078-.18.141-.276.063-.097.128-.19.195-.28.068-.09.15-.2.25-.33l3.568-4.774a5.44 5.44 0 0 1 .576-.683.763.763 0 0 1 .542-.212c.682 0 1.023.39 1.023 1.171v5.212h.29c.346 0 .623.047.832.142.208.094.313.3.313.62 0 .26-.086.45-.256.568-.17.12-.427.179-.768.179h-.41v1.252c0 .346-.077.603-.23.771-.152.168-.356.253-.612.253a.78.78 0 0 1-.61-.26c-.154-.173-.232-.427-.232-.764zm-2.895-2.76h2.895V4.91L12.26 8.823z"/></svg>',
},
{
number: 5,
tag: 'H5',
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm14.16 2.645h-3.234l-.388 2.205c.644-.344 1.239-.517 1.783-.517.436 0 .843.082 1.222.245.38.164.712.39.998.677.286.289.51.63.674 1.025.163.395.245.82.245 1.273 0 .658-.148 1.257-.443 1.797-.295.54-.72.97-1.276 1.287-.556.318-1.197.477-1.923.477-.813 0-1.472-.15-1.978-.45-.506-.3-.865-.643-1.076-1.031-.21-.388-.316-.727-.316-1.018 0-.177.073-.345.22-.504a.725.725 0 0 1 .556-.238c.381 0 .665.22.85.66.182.404.427.719.736.943.309.225.654.337 1.035.337.35 0 .656-.09.919-.272.263-.182.466-.431.61-.749.142-.318.214-.678.214-1.082 0-.436-.078-.808-.232-1.117a1.607 1.607 0 0 0-.62-.69 1.674 1.674 0 0 0-.864-.229c-.39 0-.67.048-.837.143-.168.095-.41.262-.725.5-.316.239-.576.358-.78.358a.843.843 0 0 1-.592-.242c-.173-.16-.259-.344-.259-.548 0-.022.025-.177.075-.463l.572-3.26c.063-.39.181-.675.354-.852.172-.177.454-.265.844-.265h3.595c.708 0 1.062.27 1.062.81a.711.711 0 0 1-.26.572c-.172.145-.426.218-.762.218z"/></svg>',
},
{
number: 6,
tag: 'H6',
svg: '<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zM12.53 7.058a3.093 3.093 0 0 1 1.004-.814 2.734 2.734 0 0 1 1.214-.264c.43 0 .827.08 1.19.24.365.161.684.39.957.686.274.296.485.645.635 1.048a3.6 3.6 0 0 1 .223 1.262c0 .637-.145 1.216-.437 1.736-.292.52-.699.926-1.221 1.218-.522.292-1.114.438-1.774.438-.76 0-1.416-.186-1.967-.557-.552-.37-.974-.919-1.265-1.645-.292-.726-.438-1.613-.438-2.662 0-.855.088-1.62.265-2.293.176-.674.43-1.233.76-1.676.33-.443.73-.778 1.2-1.004.47-.226 1.006-.339 1.608-.339.579 0 1.089.113 1.53.34.44.225.773.506.997.84.224.335.335.656.335.964 0 .185-.07.354-.21.505a.698.698 0 0 1-.536.227.874.874 0 0 1-.529-.18 1.039 1.039 0 0 1-.36-.498 1.42 1.42 0 0 0-.495-.655 1.3 1.3 0 0 0-.786-.247c-.24 0-.479.069-.716.207a1.863 1.863 0 0 0-.6.56c-.33.479-.525 1.333-.584 2.563zm1.832 4.213c.456 0 .834-.186 1.133-.56.298-.373.447-.862.447-1.468 0-.412-.07-.766-.21-1.062a1.584 1.584 0 0 0-.577-.678 1.47 1.47 0 0 0-.807-.234c-.28 0-.548.074-.804.224-.255.149-.461.365-.617.647a2.024 2.024 0 0 0-.234.994c0 .61.158 1.12.475 1.527.316.407.714.61 1.194.61z"/></svg>',
},
];
return this._settings.levels ? availableLevels.filter(
l => this._settings.levels.includes(l.number)
) : availableLevels;
}
onPaste(event) {
const content = event.detail.data;
let level = this.defaultLevel.number;
switch (content.tagName) {
case 'H1':
level = 1;
break;
case 'H2':
level = 2;
break;
case 'H3':
level = 3;
break;
case 'H4':
level = 4;
break;
case 'H5':
level = 5;
break;
case 'H6':
level = 6;
break;
}
if (this._settings.levels) {
// Fallback to nearest level when specified not available
level = this._settings.levels.reduce((prevLevel, currLevel) => {
return Math.abs(currLevel - level) < Math.abs(prevLevel - level) ? currLevel : prevLevel;
});
}
this.data = {
level,
text: content.innerHTML,
};
}
static get pasteConfig() {
return {
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'],
};
}
static get toolbox() {
return {
icon: '<svg width="10" height="14" viewBox="0 0 10 14"><path d="M7.6 8.15H2.25v4.525a1.125 1.125 0 0 1-2.25 0V1.125a1.125 1.125 0 1 1 2.25 0V5.9H7.6V1.125a1.125 1.125 0 0 1 2.25 0v11.55a1.125 1.125 0 0 1-2.25 0V8.15z"></path></svg>',
title: 'Heading',
};
}
}

View file

@ -0,0 +1,27 @@
// import blocks
import Header from "./header";
import Paragraph from "./paragraph";
import Card from "./card";
import Chart from "./chart";
import Shortcut from "./shortcut";
import Spacer from "./spacer";
import Onboarding from "./onboarding";
// import tunes
import SpacingTune from "./spacing_tune";
frappe.provide("frappe.wspace_block");
frappe.wspace_block.blocks = {
header: Header,
paragraph: Paragraph,
card: Card,
chart: Chart,
shortcut: Shortcut,
spacer: Spacer,
onboarding: Onboarding,
};
frappe.wspace_block.tunes = {
spacing_tune: SpacingTune
};

View file

@ -0,0 +1,129 @@
import get_dialog_constructor from "../../../widgets/widget_dialog.js";
import Block from "./block.js";
export default class Onboarding extends Block {
static get toolbox() {
return {
title: 'Onboarding',
icon: '<svg width="24" height="24" viewBox="2 0 20 24" fill="none"><path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10zM12 11.09v5.455" stroke="#1F272E" fill="none"/><path d="M12.41 7.455a.41.41 0 11-.82 0 .41.41 0 01.82 0z" stroke="#1F272E"/></svg>'
};
}
static get isReadOnlySupported() {
return true;
}
constructor({ data, api, config, readOnly, block }) {
super({ data, api, config, readOnly, block });
this.col = this.data.col ? this.data.col : "12";
this.allow_customization = !this.readOnly;
this.options = {
allow_sorting: this.allow_customization,
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true
};
}
rendered() {
var e = this.wrapper.closest('.ce-block');
if (this.readOnly && !$(this.wrapper).find('.onboarding-widget-box').is(':visible')) {
$(e).hide();
}
e.classList.add("col-" + this.get_col());
}
new(block, widget_type = block) {
const dialog_class = get_dialog_constructor(widget_type);
let block_name = block+'_name';
this.dialog = new dialog_class({
label: this.label,
type: widget_type,
primary_action: (widget) => {
widget.in_customize_mode = 1;
this.block_widget = frappe.widget.make_widget({
...widget,
widget_type: widget_type,
container: this.wrapper,
options: {
...this.options,
on_delete: () => this.api.blocks.delete(),
on_edit: () => this.on_edit(this.block_widget)
},
new: true
});
this.block_widget.customize(this.options);
this.wrapper.setAttribute(block_name, this.block_widget.label || this.block_widget.onboarding_name);
this.new_block_widget = this.block_widget.get_config();
this.add_tune_button();
},
});
if (!this.readOnly && this.data && !this.data[block_name]) {
this.dialog.make();
}
}
make(block, block_name) {
let block_data = this.config.page_data['onboardings'].items.find(obj => {
return obj.label == block_name;
});
if (!block_data) return false;
this.wrapper.innerHTML = '';
block_data.in_customize_mode = !this.readOnly;
this.block_widget = frappe.widget.make_widget({
container: this.wrapper,
widget_type: 'onboarding',
in_customize_mode: block_data.in_customize_mode,
options: {
...this.options,
on_delete: () => this.api.blocks.delete(),
on_edit: () => this.on_edit(this.block_widget)
},
label: block_data.label,
title: block_data.title || __("Let's Get Started"),
subtitle: block_data.subtitle,
steps: block_data.items,
success: block_data.success,
docs_url: block_data.docs_url,
user_can_dismiss: block_data.user_can_dismiss,
});
this.wrapper.setAttribute(block+'_name', block_name);
if (!this.readOnly) {
this.block_widget.customize(this.options);
}
return true;
}
render() {
this.wrapper = document.createElement('div');
this.new('onboarding');
if (this.data && this.data.onboarding_name) {
let has_data = this.make('onboarding', this.data.onboarding_name);
if (!has_data) return;
}
if (!this.readOnly) {
this.add_tune_button();
}
$(this.wrapper).css("padding-bottom", "20px");
return this.wrapper;
}
validate(savedData) {
if (!savedData.onboarding_name) {
return false;
}
return true;
}
save(blockContent) {
return {
onboarding_name: blockContent.getAttribute('onboarding_name'),
col: this.get_col(),
new: this.new_block_widget
};
}
}

View file

@ -0,0 +1,192 @@
import Block from "./block.js";
export default class Paragraph extends Block {
static get DEFAULT_PLACEHOLDER() {
return '';
}
constructor({ data, config, api, readOnly }) {
super({ config, api, readOnly });
this._CSS = {
block: this.api.styles.block,
wrapper: 'ce-paragraph'
};
if (!this.readOnly) {
this.onKeyUp = this.onKeyUp.bind(this);
}
this._placeholder = this.config.placeholder ? this.config.placeholder : Paragraph.DEFAULT_PLACEHOLDER;
this._data = {};
this._element = this.drawView();
this._preserveBlank = this.config.preserveBlank !== undefined ? this.config.preserveBlank : false;
this.data = data;
this.col = this.data.col ? this.data.col : "12";
}
onKeyUp(e) {
if (e.code !== 'Backspace' && e.code !== 'Delete') {
return;
}
const {textContent} = this._element;
if (textContent === '') {
this._element.innerHTML = '';
}
}
drawView() {
let div = document.createElement('DIV');
div.classList.add(this._CSS.wrapper, this._CSS.block, 'widget');
div.contentEditable = false;
div.dataset.placeholder = this.api.i18n.t(this._placeholder);
if (!this.readOnly) {
div.contentEditable = true;
div.addEventListener('keyup', this.onKeyUp);
}
return div;
}
render() {
this.wrapper = document.createElement('div');
this.wrapper.contentEditable = this.readOnly ? 'false' : 'true';
if (!this.readOnly) {
let $para_control = $(`<div class="paragraph-control"></div>`);
this.wrapper.appendChild(this._element);
this._element.classList.remove('widget');
$para_control.appendTo(this.wrapper);
this.wrapper.classList.add('widget');
frappe.utils.add_custom_button(
frappe.utils.icon('dot-horizontal', 'xs'),
(event) => {
let evn = event;
!$('.ce-settings.ce-settings--opened').length &&
setTimeout(() => {
this.api.toolbar.toggleBlockSettings();
var position = $(evn.target).offset();
$('.ce-settings.ce-settings--opened').offset({
top: position.top + 25,
left: position.left - 77
});
}, 50);
},
"tune-btn",
`${__('Tune')}`,
null,
$para_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('drag', 'xs'),
null,
"drag-handle",
`${__('Drag')}`,
null,
$para_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('delete', 'xs'),
() => this.api.blocks.delete(),
"delete-paragraph",
`${__('Delete')}`,
null,
$para_control
);
return this.wrapper;
}
return this._element;
}
merge(data) {
let newData = {
text: this.data.text + data.text
};
this.data = newData;
}
validate(savedData) {
if (savedData.text.trim() === '' && !this._preserveBlank) {
return false;
}
return true;
}
save(toolsContent) {
this.wrapper = this._element;
return {
text: toolsContent.innerText,
col: this.get_col(),
};
}
rendered() {
var e = this._element.closest('.ce-block');
e.classList.add("col-" + this.get_col());
}
onPaste(event) {
const data = {
text: event.detail.data.innerHTML
};
this.data = data;
}
static get conversionConfig() {
return {
export: 'text', // to convert Paragraph to other block, use 'text' property of saved data
import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data
};
}
static get sanitize() {
return {
text: {
br: true,
}
};
}
static get isReadOnlySupported() {
return true;
}
get data() {
let text = this._element.innerHTML;
this._data.text = text;
return this._data;
}
set data(data) {
this._data = data || {};
this._element.innerHTML = this._data.text || '';
}
static get pasteConfig() {
return {
tags: [ 'P' ]
};
}
static get toolbox() {
return {
icon: '<svg viewBox="0.2 -0.3 9 11.4" width="12" height="14"><path d="M0 2.77V.92A1 1 0 01.2.28C.35.1.56 0 .83 0h7.66c.28.01.48.1.63.28.14.17.21.38.21.64v1.85c0 .26-.08.48-.23.66-.15.17-.37.26-.66.26-.28 0-.5-.09-.64-.26a1 1 0 01-.21-.66V1.69H5.6v7.58h.5c.25 0 .45.08.6.23.17.16.25.35.25.6s-.08.45-.24.6a.87.87 0 01-.62.22H3.21a.87.87 0 01-.61-.22.78.78 0 01-.24-.6c0-.25.08-.44.24-.6a.85.85 0 01.61-.23h.5V1.7H1.73v1.08c0 .26-.08.48-.23.66-.15.17-.37.26-.66.26-.28 0-.5-.09-.64-.26A1 1 0 010 2.77z"/></svg>',
title: 'Text'
};
}
}

View file

@ -0,0 +1,57 @@
import Block from "./block.js";
export default class Shortcut extends Block {
static get toolbox() {
return {
title: 'Shortcut',
icon: '<svg height="18" width="18" viewBox="0 0 122.88 115.71"><path d="M116.56 3.69l-3.84 53.76-17.69-15c-19.5 8.72-29.96 23.99-30.51 43.77-17.95-26.98-7.46-50.4 12.46-65.97L64.96 3l51.6.69zM28.3 0h14.56v19.67H32.67c-4.17 0-7.96 1.71-10.72 4.47-2.75 2.75-4.46 6.55-4.46 10.72l-.03 46c.03 4.16 1.75 7.95 4.5 10.71 2.76 2.76 6.56 4.48 10.71 4.48h58.02c4.15 0 7.95-1.72 10.71-4.48 2.76-2.76 4.48-6.55 4.48-10.71V73.9h17.01v11.33c0 7.77-3.2 17.04-8.32 22.16-5.12 5.12-12.21 8.32-19.98 8.32H28.3c-7.77 0-14.86-3.2-19.98-8.32C3.19 102.26 0 95.18 0 87.41l.03-59.1c-.03-7.79 3.16-14.88 8.28-20C13.43 3.19 20.51 0 28.3 0z" fill-rule="evenodd" clip-rule="evenodd"/></svg>'
};
}
static get isReadOnlySupported() {
return true;
}
constructor({ data, api, config, readOnly, block }) {
super({ data, api, config, readOnly, block });
this.col = this.data.col ? this.data.col : "12";
this.allow_customization = !this.readOnly;
this.options = {
allow_sorting: this.allow_customization,
allow_create: this.allow_customization,
allow_delete: this.allow_customization,
allow_hiding: false,
allow_edit: true
};
}
render() {
this.wrapper = document.createElement('div');
this.new('shortcut');
if (this.data && this.data.shortcut_name) {
let has_data = this.make('shortcut', this.data.shortcut_name);
if (!has_data) return;
}
if (!this.readOnly) {
this.add_tune_button();
}
return this.wrapper;
}
validate(savedData) {
if (!savedData.shortcut_name) {
return false;
}
return true;
}
save(blockContent) {
return {
shortcut_name: blockContent.getAttribute('shortcut_name'),
col: this.get_col(),
new: this.new_block_widget
};
}
}

View file

@ -0,0 +1,82 @@
import Block from './block.js';
export default class Spacer extends Block {
static get toolbox() {
return {
title: 'Spacer',
icon: '<svg width="18" height="18" viewBox="0 0 400 400"><path d="M377.87 24.126C361.786 8.042 342.417 0 319.769 0H82.227C59.579 0 40.211 8.042 24.125 24.126 8.044 40.212.002 59.576.002 82.228v237.543c0 22.647 8.042 42.014 24.123 58.101 16.086 16.085 35.454 24.127 58.102 24.127h237.542c22.648 0 42.011-8.042 58.102-24.127 16.085-16.087 24.126-35.453 24.126-58.101V82.228c-.004-22.648-8.046-42.016-24.127-58.102zm-12.422 295.645c0 12.559-4.47 23.314-13.415 32.264-8.945 8.945-19.698 13.411-32.265 13.411H82.227c-12.563 0-23.317-4.466-32.264-13.411-8.945-8.949-13.418-19.705-13.418-32.264V82.228c0-12.562 4.473-23.316 13.418-32.264 8.947-8.946 19.701-13.418 32.264-13.418h237.542c12.566 0 23.319 4.473 32.265 13.418 8.945 8.947 13.415 19.701 13.415 32.264v237.543h-.001z"/></svg>'
};
}
static get isReadOnlySupported() {
return true;
}
constructor({ data, api, config, readOnly }) {
super({ data, api, config, readOnly });
this.col = this.data.col ? this.data.col : "12";
}
render() {
this.wrapper = document.createElement('div');
if (!this.readOnly) {
let $spacer = $(`
<div class="widget-head">
<div></div>
<div>Spacer</div>
<div class="widget-control"></div>
</div>
`);
$spacer.appendTo(this.wrapper);
this.wrapper.classList.add('widget', 'new-widget');
this.wrapper.style.minHeight = 50 + 'px';
let $widget_control = $spacer.find('.widget-control');
frappe.utils.add_custom_button(
frappe.utils.icon('dot-horizontal', 'xs'),
(event) => {
let evn = event;
!$('.ce-settings.ce-settings--opened').length &&
setTimeout(() => {
this.api.toolbar.toggleBlockSettings();
var position = $(evn.target).offset();
$('.ce-settings.ce-settings--opened').offset({
top: position.top + 25,
left: position.left - 77
});
}, 50);
},
"tune-btn",
`${__('Tune')}`,
null,
$widget_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('drag', 'xs'),
null,
"drag-handle",
`${__('Drag')}`,
null,
$widget_control
);
frappe.utils.add_custom_button(
frappe.utils.icon('delete', 'xs'),
() => this.api.blocks.delete(),
"delete-spacer",
`${__('Delete')}`,
null,
$widget_control
);
}
return this.wrapper;
}
save() {
return {
col: this.get_col()
};
}
}

View file

@ -0,0 +1,123 @@
export default class SpacingTune {
static get isTune() {
return true;
}
constructor({api, settings}) {
this.api = api;
this.settings = settings;
this.CSS = {
button: 'ce-settings__button',
wrapper: 'ce-tune-layout',
sidebar: 'cdx-settings-sidebar',
animation: 'wobble',
};
this.data = { colWidth: 12 };
this.wrapper = undefined;
this.sidebar = undefined;
}
render() {
let me = this;
let layoutWrapper = document.createElement('div');
layoutWrapper.classList.add(this.CSS.wrapper);
let decreaseWidthButton = document.createElement('div');
decreaseWidthButton.classList.add(this.CSS.button, 'ce-shrink-button');
let increaseWidthButton = document.createElement('div');
increaseWidthButton.classList.add(this.CSS.button, 'ce-expand-button');
layoutWrapper.appendChild(decreaseWidthButton);
layoutWrapper.appendChild(increaseWidthButton);
decreaseWidthButton.innerHTML = `<svg version="1.1" height="10" x="0px" y="0px" viewBox="-674 380 17 10" style="enable-background:new -674 380 17 10;" xml:space="preserve"><path d="M-674,383.9h3.6l-1.7-1.7c-0.4-0.4-0.4-1.2,0-1.6c0.4-0.4,1.1-0.4,1.6,0l3.2,3.2c0.6,0.2,0.8,0.8,0.6,1.4 c-0.1,0.1-0.1,0.3-0.2,0.4l-3.8,3.8c-0.4,0.4-1.1,0.4-1.5,0c-0.4-0.4-0.4-1.1,0-1.5l1.8-1.8h-3.6V383.9z"/><path d="M-657,386.1h-3.6l1.7,1.7c0.4,0.4,0.4,1.2,0,1.6c-0.4,0.4-1.1,0.4-1.6,0l-3.2-3.2c-0.6-0.2-0.8-0.8-0.6-1.4 c0.1-0.1,0.1-0.3,0.2-0.4l3.8-3.8c0.4-0.4,1.1-0.4,1.5,0c0.4,0.4,0.4,1.1,0,1.5l-1.8,1.8h3.6V386.1z"/></svg>`;
this.api.tooltip.onHover(decreaseWidthButton, 'Shrink', {
placement: 'top',
hidingDelay: 500,
});
this.api.listeners.on(
decreaseWidthButton,
'click',
() => me.decreaseWidth(),
false
);
increaseWidthButton.innerHTML = `<svg width="17" height="10" viewBox="0 0 17 10"><path d="M13.568 5.925H4.056l1.703 1.703a1.125 1.125 0 0 1-1.59 1.591L.962 6.014A1.069 1.069 0 0 1 .588 4.26L4.38.469a1.069 1.069 0 0 1 1.512 1.511L4.084 3.787h9.606l-1.85-1.85a1.069 1.069 0 1 1 1.512-1.51l3.792 3.791a1.069 1.069 0 0 1-.475 1.788L13.514 9.16a1.125 1.125 0 0 1-1.59-1.591l1.644-1.644z"/></svg>`;
this.api.tooltip.onHover(increaseWidthButton, 'Expand', {
placement: 'top',
hidingDelay: 500,
});
this.api.listeners.on(
increaseWidthButton,
'click',
() => me.increaseWidth(),
false
);
this.wrapper = layoutWrapper;
return layoutWrapper;
}
decreaseWidth() {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
if (currentBlockIndex < 0) {
return;
}
let currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
if (!currentBlock) {
return;
}
let currentBlockElement = currentBlock.holder;
let className = 'col-12';
let colClass = new RegExp(/\bcol-.+?\b/, 'g');
if (currentBlockElement.className.match(colClass)) {
currentBlockElement.classList.forEach( cn => {
if (cn.match(colClass)) {
className = cn;
}
});
let parts = className.split('-');
let width = parseInt(parts[1]);
if (width >= 4) {
currentBlockElement.classList.remove('col-'+width);
width = width - 1;
currentBlockElement.classList.add('col-'+width);
}
}
}
increaseWidth() {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
if (currentBlockIndex < 0) {
return;
}
const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
if (!currentBlock) {
return;
}
const currentBlockElement = currentBlock.holder;
let className = 'col-12';
const colClass = new RegExp(/\bcol-.+?\b/, 'g');
if (currentBlockElement.className.match(colClass)) {
currentBlockElement.classList.forEach( cn => {
if (cn.match(colClass)) {
className = cn;
}
});
let parts = className.split('-');
let width = parseInt(parts[1]);
if (width <= 11) {
currentBlockElement.classList.remove('col-'+width);
width = width + 1;
currentBlockElement.classList.add('col-'+width);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -25,18 +25,23 @@ export default class Widget {
this.action_area.empty(); this.action_area.empty();
options.allow_sorting && options.allow_sorting &&
this.add_custom_button( frappe.utils.add_custom_button(
frappe.utils.icon('drag', 'xs'), frappe.utils.icon('drag', 'xs'),
null, null,
"drag-handle", "drag-handle",
`${__('Drag')}`,
null,
this.action_area
); );
options.allow_delete && options.allow_delete &&
this.add_custom_button( frappe.utils.add_custom_button(
frappe.utils.icon('delete', 'xs'), frappe.utils.icon('delete', 'xs'),
() => this.delete(), () => this.delete(),
"", "",
`${__('Delete')}` `${__('Delete')}`,
null,
this.action_area
); );
if (options.allow_hiding) { if (options.allow_hiding) {
@ -48,11 +53,13 @@ export default class Widget {
} }
const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash'; const classname = this.hidden ? 'fa fa-eye' : 'fa fa-eye-slash';
const title = this.hidden ? `${__('Show')}` : `${__('Hide')}`; const title = this.hidden ? `${__('Show')}` : `${__('Hide')}`;
this.add_custom_button( frappe.utils.add_custom_button(
`<i class="${classname}" aria-hidden="true"></i>`, `<i class="${classname}" aria-hidden="true"></i>`,
() => this.hide_or_show(), () => this.hide_or_show(),
"show-or-hide-button", "show-or-hide-button",
title title,
null,
this.action_area
); );
this.show_or_hide_button = this.action_area.find( this.show_or_hide_button = this.action_area.find(
@ -61,18 +68,24 @@ export default class Widget {
} }
options.allow_edit && options.allow_edit &&
this.add_custom_button( frappe.utils.add_custom_button(
frappe.utils.icon("edit", "xs"), frappe.utils.icon("edit", "xs"),
() => this.edit() () => this.edit(),
null,
`${__('Edit')}`,
null,
this.action_area
); );
if (options.allow_resize) { if (options.allow_resize) {
const title = this.width == 'Full'? `${__('Collapse')}` : `${__('Expand')}`; const title = this.width == 'Full'? `${__('Collapse')}` : `${__('Expand')}`;
this.add_custom_button( frappe.utils.add_custom_button(
'<i class="fa fa-expand" aria-hidden="true"></i>', '<i class="fa fa-expand" aria-hidden="true"></i>',
() => this.toggle_width(), () => this.toggle_width(),
"resize-button", "resize-button",
title title,
null,
this.action_area
); );
this.resize_button = this.action_area.find( this.resize_button = this.action_area.find(
@ -88,12 +101,11 @@ export default class Widget {
make_widget() { make_widget() {
this.widget = $(`<div class="widget this.widget = $(`<div class="widget
${ this.hidden ? "hidden" : " " }
${ this.shadow ? "widget-shadow" : " " } ${ this.shadow ? "widget-shadow" : " " }
" data-widget-name="${this.name ? this.name : ''}"> " data-widget-name="${this.name ? this.name : ''}">
<div class="widget-head"> <div class="widget-head">
<div> <div class="widget-label">
<div class="widget-title ellipsis"></div> <div class="widget-title"></div>
<div class="widget-subtitle"></div> <div class="widget-subtitle"></div>
</div> </div>
<div class="widget-control"></div> <div class="widget-control"></div>
@ -114,37 +126,25 @@ export default class Widget {
} }
set_title(max_chars) { set_title(max_chars) {
let base = this.label || this.name; let base = this.title || this.label || this.name;
let title = max_chars ? frappe.ellipsis(base, max_chars) : base; let title = max_chars ? frappe.ellipsis(base, max_chars) : base;
if (this.icon) { if (this.icon) {
let icon = frappe.utils.icon(this.icon); let icon = frappe.utils.icon(this.icon);
this.title_field[0].innerHTML = `${icon} <span>${title}</span>`; this.title_field[0].innerHTML = `${icon} <span class="ellipsis" title="${title}">${title}</span>`;
} else { } else {
this.title_field[0].innerHTML = title; this.title_field[0].innerHTML = `<span class="ellipsis" title="${title}">${title}</span>`;
if (max_chars) { if (max_chars) {
this.title_field[0].setAttribute('title', this.label); this.title_field[0].setAttribute('title', this.title || this.label);
} }
} }
this.subtitle && this.subtitle_field.html(this.subtitle); this.subtitle && this.subtitle_field.html(this.subtitle);
} }
add_custom_button(html, action, class_name = "", title="", btn_type) { delete(animate=true, dismissed=false) {
if (!btn_type) btn_type = 'btn-secondary';
let button = $(
`<button class="btn ${btn_type} btn-xs ${class_name}" title="${title}">${html}</button>`
);
button.click(event => {
event.stopPropagation();
action && action();
});
button.appendTo(this.action_area);
}
delete(animate=true) {
let remove_widget = (setup_new) => { let remove_widget = (setup_new) => {
this.widget.remove(); this.widget.remove();
this.options.on_delete && this.options.on_delete(this.name, setup_new); !dismissed && this.options.on_delete && this.options.on_delete(this.name, setup_new);
}; };
if (animate) { if (animate) {
@ -168,8 +168,9 @@ export default class Widget {
primary_action: (data) => { primary_action: (data) => {
Object.assign(this, data); Object.assign(this, data);
data.name = this.name; data.name = this.name;
this.new = true;
this.refresh(); this.refresh();
this.options.on_edit && this.options.on_edit(data);
}, },
primary_action_label: __("Save") primary_action_label: __("Save")
}); });

View file

@ -349,6 +349,8 @@ export default class ChartWidget extends Widget {
} }
setup_filter_button() { setup_filter_button() {
if (this.in_customize_mode) return;
this.is_document_type = this.is_document_type =
this.chart_doc.chart_type !== "Report" && this.chart_doc.chart_type !== "Report" &&
this.chart_doc.chart_type !== "Custom"; this.chart_doc.chart_type !== "Custom";

View file

@ -12,14 +12,18 @@ export default class LinksWidget extends Widget {
return { return {
name: this.name, name: this.name,
links: JSON.stringify(this.links), links: JSON.stringify(this.links),
link_count: this.links.length,
label: this.label, label: this.label,
hidden: this.hidden, hidden: this.hidden,
}; };
} }
set_body() { set_body() {
this.options = {};
this.options.links = this.links; if (!this.options) {
this.options = {};
this.options.links = this.links;
}
this.widget.addClass("links-widget-box"); this.widget.addClass("links-widget-box");
const is_link_disabled = item => { const is_link_disabled = item => {
return item.dependencies && item.incomplete_dependencies; return item.dependencies && item.incomplete_dependencies;
@ -81,7 +85,9 @@ export default class LinksWidget extends Widget {
${get_link_for_item(item)} ${get_link_for_item(item)}
</a>`); </a>`);
}); });
if (this.in_customize_mode) {
this.body.empty();
}
this.link_list.forEach(link => link.appendTo(this.body)); this.link_list.forEach(link => link.appendTo(this.body));
} }

View file

@ -3,7 +3,23 @@ import Widget from "./base_widget.js";
frappe.provide("frappe.utils"); frappe.provide("frappe.utils");
export default class OnboardingWidget extends Widget { export default class OnboardingWidget extends Widget {
async refresh() {
this.new && await this.get_onboarding_data();
this.set_title();
this.set_actions();
this.set_body();
this.setup_events();
}
get_config() {
return {
label: this.onboarding_name
};
}
make_body() { make_body() {
this.body.empty();
this.steps_wrapper = $(`<div class="onboarding-steps-wrapper"></div>`).appendTo(this.body); this.steps_wrapper = $(`<div class="onboarding-steps-wrapper"></div>`).appendTo(this.body);
this.step_preview = $(`<div class="onboarding-step-preview"> this.step_preview = $(`<div class="onboarding-step-preview">
<div class="onboarding-step-body"></div> <div class="onboarding-step-body"></div>
@ -477,11 +493,13 @@ export default class OnboardingWidget extends Widget {
} }
is_dismissed() { is_dismissed() {
if (this.in_customize_mode) return false;
let dismissed = JSON.parse( let dismissed = JSON.parse(
localStorage.getItem("dismissed-onboarding") || "{}" localStorage.getItem("dismissed-onboarding") || "{}"
); );
if (Object.keys(dismissed).includes(this.label)) { if (Object.keys(dismissed).includes(this.title)) {
let last_hidden = new Date(dismissed[this.label]); let last_hidden = new Date(dismissed[this.title]);
let today = new Date(); let today = new Date();
let diff = frappe.datetime.get_hour_diff(today, last_hidden); let diff = frappe.datetime.get_hour_diff(today, last_hidden);
return diff < 24; return diff < 24;
@ -490,6 +508,8 @@ export default class OnboardingWidget extends Widget {
} }
set_actions() { set_actions() {
if (this.in_customize_mode) return;
this.action_area.empty(); this.action_area.empty();
const dismiss = $( const dismiss = $(
`<div class="small" style="cursor:pointer;">${__('Dismiss', null, 'Stop showing the onboarding widget.')}</div>` `<div class="small" style="cursor:pointer;">${__('Dismiss', null, 'Stop showing the onboarding widget.')}</div>`
@ -498,14 +518,38 @@ export default class OnboardingWidget extends Widget {
let dismissed = JSON.parse( let dismissed = JSON.parse(
localStorage.getItem("dismissed-onboarding") || "{}" localStorage.getItem("dismissed-onboarding") || "{}"
); );
dismissed[this.label] = frappe.datetime.now_datetime(); dismissed[this.title] = frappe.datetime.now_datetime();
localStorage.setItem( localStorage.setItem(
"dismissed-onboarding", "dismissed-onboarding",
JSON.stringify(dismissed) JSON.stringify(dismissed)
); );
this.delete(); this.delete(true, true);
this.widget.closest('.ce-block').hide();
}); });
dismiss.appendTo(this.action_area); dismiss.appendTo(this.action_area);
} }
get_onboarding_data() {
return frappe.model
.with_doc("Module Onboarding", this.onboarding_name)
.then(onboarding_doc => {
if (onboarding_doc) {
this.onboarding_doc = onboarding_doc;
this.label = onboarding_doc.label;
this.title = onboarding_doc.title || __("Let's Get Started");
this.subtitle = onboarding_doc.subtitle;
this.success = onboarding_doc.success;
this.docs_url = onboarding_doc.docs_url;
this.user_can_dismiss = onboarding_doc.user_can_dismiss;
const method =
"frappe.desk.doctype.onboarding_step.onboarding_step.get_onboarding_steps";
return frappe
.xcall(method, { ob_steps: onboarding_doc.steps })
.then(steps => {
this.steps = steps;
});
}
});
}
} }

View file

@ -124,6 +124,116 @@ class ChartDialog extends WidgetDialog {
} }
} }
class OnboardingDialog extends WidgetDialog {
constructor(opts) {
super(opts);
}
get_fields() {
return [
{
fieldtype: "Link",
fieldname: "onboarding_name",
label: "Onboarding Name",
options: "Module Onboarding",
reqd: 1,
}
];
}
}
class CardDialog extends WidgetDialog {
constructor(opts) {
super(opts);
}
get_fields() {
let me = this;
return [
{
fieldtype: "Data",
fieldname: "label",
label: "Label",
},
{
fieldname: 'links',
fieldtype: 'Table',
label: __('Card Links'),
editable_grid: 1,
data: me.values ? JSON.parse(me.values.links) : [],
get_data: () => {
return me.values ? JSON.parse(me.values.links) : [];
},
fields: [
{
fieldname: "label",
fieldtype: "Data",
in_list_view: 1,
label: "Label"
},
{
fieldname: "icon",
fieldtype: "Data",
label: "Icon"
},
{
fieldname: "link_type",
fieldtype: "Select",
in_list_view: 1,
label: "Link Type",
options: ["DocType", "Page", "Report"],
onchange: (e) => {
me.link_to = e.currentTarget.value;
}
},
{
fieldname: "link_to",
fieldtype: "Dynamic Link",
in_list_view: 1,
label: "Link To",
options: "link_type",
get_options: () => {
return me.link_to;
}
},
{
fieldname: "column_break_7",
fieldtype: "Column Break"
},
{
fieldname: "dependencies",
fieldtype: "Data",
label: "Dependencies"
},
{
fieldname: "only_for",
fieldtype: "Link",
label: "Only for ",
options: "Country"
},
{
default: "0",
fieldname: "onboard",
fieldtype: "Check",
label: "Onboard"
},
{
default: "0",
fieldname: "is_query_report",
fieldtype: "Check",
label: "Is Query Report"
}
],
},
];
}
process_data(data) {
data.label = data.label ? data.label : data.chart_name;
return data;
}
}
class ShortcutDialog extends WidgetDialog { class ShortcutDialog extends WidgetDialog {
constructor(opts) { constructor(opts) {
super(opts); super(opts);
@ -438,6 +548,8 @@ export default function get_dialog_constructor(type) {
chart: ChartDialog, chart: ChartDialog,
shortcut: ShortcutDialog, shortcut: ShortcutDialog,
number_card: NumberCardDialog, number_card: NumberCardDialog,
links: CardDialog,
onboarding: OnboardingDialog
}; };
return widget_map[type] || WidgetDialog; return widget_map[type] || WidgetDialog;

View file

@ -186,4 +186,51 @@ export default class WidgetGroup {
} }
} }
export class SingleWidgetGroup {
constructor(opts) {
Object.assign(this, opts);
this.widgets_list = [];
this.widgets_dict = {};
this.widget_order = [];
this.make();
}
make() {
this.add_widget(this.widgets);
}
add_widget(widget) {
let widget_object = frappe.widget.make_widget({
...widget,
widget_type: this.type,
container: this.container,
height: this.height || null,
options: {
...this.options,
on_delete: () => this.on_delete(),
on_edit: () => this.on_edit(widget_object)
}
});
this.widgets_list.push(widget_object);
this.widgets_dict[widget.name] = widget_object;
return widget_object;
}
on_delete() {
this.api.blocks.delete();
}
on_edit(widget_object) {
this.block.call("on_edit", widget_object);
}
customize() {
this.widgets_list.forEach((wid) => {
wid.customize(this.options);
});
}
}
frappe.widget.WidgetGroup = WidgetGroup; frappe.widget.WidgetGroup = WidgetGroup;
frappe.widget.SingleWidgetGroup = SingleWidgetGroup;

View file

@ -9,6 +9,11 @@ body {
.standard-sidebar-label { .standard-sidebar-label {
font-size: var(--text-xs); font-size: var(--text-xs);
text-transform: uppercase; text-transform: uppercase;
cursor: pointer;
span {
pointer-events: none;
}
} }
.standard-sidebar-section { .standard-sidebar-section {
@ -128,19 +133,25 @@ body {
.widget-head { .widget-head {
@include flex(flex, space-between, center, null); @include flex(flex, space-between, center, null);
.widget-title { .widget-label {
@include flex(flex, null, center, null); min-width: 0px;
font-size: var(--text-lg);
font-family: inherit;
font-weight: 500;
line-height: 1.3em;
color: var(--heading-color);
svg { .widget-title {
margin-right: 6px; @include flex(flex, null, center, null);
box-shadow: none; font-size: var(--text-lg);
font-family: inherit;
font-weight: 500;
line-height: 1.3em;
color: var(--heading-color);
svg {
flex: none;
margin-right: 6px;
box-shadow: none;
}
} }
} }
.widget-control { .widget-control {
@include flex(flex, null, center, row-reverse); @include flex(flex, null, center, row-reverse);
@ -781,4 +792,263 @@ body {
} }
} }
} }
.block-menu-item-icon svg{
width: 12px;
height: 12px;
margin-right: 5px;
}
.standard-sidebar-item {
justify-content: space-between;
padding: 0px;
.sidebar-item-control {
> * {
align-self: center;
margin-left: 3px;
box-shadow: none;
}
.drag-handle {
cursor: all-scroll;
cursor: -webkit-grabbing;
display: none;
}
.delete-page {
display: none;
}
.drop-icon {
padding: 10px 12px 10px 2px;
}
.sidebar-info {
display: none;
}
svg {
margin-right: 0;
}
}
.sidebar-item-label {
flex: 1;
}
.item-anchor {
display: flex;
overflow: hidden;
padding: 8px 0px 8px 12px;
flex: 1;
}
}
.sidebar-item-container {
.sidebar-item-container{
margin-left: 10px;
.standard-sidebar-item {
justify-content: start;
}
}
}
.standard-sidebar-section.show-control {
.desk-sidebar-item.standard-sidebar-item {
&:hover, &.selected {
.drag-handle {
display: inline-block;
}
.delete-page {
display: inline-block;
margin-right: 8px;
}
.sidebar-info {
display: inline-block;
margin-right: 8px;
}
.drop-icon {
padding: 10px 8px 10px 2px;
margin-left: -4px;
}
}
.block-click {
pointer-events:none;
}
}
}
.codex-editor {
min-height: 630px;
.codex-editor__redactor{
display: flex;
flex-wrap: wrap;
flex-direction: row;
margin: 0px -7px;
padding-bottom: 20px !important;
.ce-block{
width: 100%;
padding-left: 0;
padding-right: 0;
&.ce-block--selected {
.ce-block__content {
background-color: inherit;
}
}
.ce-block__content {
max-width: 100%;
height: 100%;
padding: 7px;
&> div {
height: 100%;
}
.tune-btn > * {
pointer-events: none;
}
.ce-header {
padding: 0 !important;
margin-bottom: 0 !important;
flex: 1;
}
.widget{
&.header {
display: flex;
justify-content: center;
flex: 1;
padding-left: 15px !important;
padding-right: 15px !important;
min-height: 50px;
box-shadow: none;
background-color: var(--control-bg);
color: var(--text-muted);
}
&:focus {
outline: none;
}
&.new-widget {
align-items: inherit;
}
.paragraph-control {
display: flex;
flex-direction: row-reverse;
position: absolute;
right: 20px;
gap: 5px;
background-color: var(--card-bg);
padding-left: 5px;
.drag-handle {
cursor: all-scroll;
cursor: -webkit-grabbing;
}
}
}
}
}
}
svg {
fill: none;
}
.ce-toolbar {
svg {
fill: currentColor;
}
.icon {
stroke: none;
width: fit-content;
height: fit-content;
}
.ce-settings {
width: fit-content;
.ce-settings__button, .cdx-settings-button {
color: #707684;
}
.cdx-settings-button--active {
color: #388ae5;
}
.cdx-settings-button.disabled{
pointer-events: none;
opacity: .5
}
.cdx-settings-sidebar{
position: absolute;
right: 100%;
top:0;
background: #fff;
width: 108px;
height: 145px;
box-shadow: 0 3px 15px -3px rgba(13,20,33,.13);
border-radius: 0 4px 4px 0;z-index: 0;
}
}
.ce-toolbar__settings-btn {
display: none;
}
}
.ce-inline-tool, .ce-inline-toolbar__dropdown {
.icon {
fill: currentColor;
}
}
@media (min-width: 1199px) {
.ce-toolbar__content {
max-width: 930px;
}
}
@media (max-width: 995px) {
.ce-toolbar__content {
max-width: 760px;
}
}
@media (max-width: 1199px) {
.ce-block.col-4 {
flex: 0 0 50%;
max-width: 50%;
}
}
@media (max-width: 750px) {
.ce-block.col-4 {
flex: 0 0 100%;
max-width: 100%;
}
}
@media (max-width: 750px) {
.ce-block.col-6 {
flex: 0 0 100%;
max-width: 100%;
}
}
}
} }

View file

@ -1,20 +1,20 @@
{ {
"category": "Modules", "category": "",
"charts": [ "charts": [],
{ "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Website\", \"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Blog Post\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Blogger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Web Page\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Web Form\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Website Settings\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Blog\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Web Site\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Portal\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Knowledge Base\", \"col\": 4}}]",
"chart_name": "Website Analytics"
}
],
"creation": "2020-03-02 14:13:51.089373", "creation": "2020-03-02 14:13:51.089373",
"developer_mode_only": 0, "developer_mode_only": 0,
"disable_user_customization": 0, "disable_user_customization": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
"extends": "",
"extends_another_page": 0, "extends_another_page": 0,
"for_user": "",
"hide_custom": 0, "hide_custom": 0,
"icon": "website", "icon": "website",
"idx": 0, "idx": 0,
"is_standard": 1, "is_default": 0,
"is_standard": 0,
"label": "Website", "label": "Website",
"links": [ "links": [
{ {
@ -22,6 +22,7 @@
"icon": "setting", "icon": "setting",
"is_query_report": 0, "is_query_report": 0,
"label": "Setup", "label": "Setup",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -30,6 +31,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Settings", "label": "Website Settings",
"link_count": 0,
"link_to": "Website Settings", "link_to": "Website Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -40,6 +42,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Theme", "label": "Website Theme",
"link_count": 0,
"link_to": "Website Theme", "link_to": "Website Theme",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -50,6 +53,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Script", "label": "Website Script",
"link_count": 0,
"link_to": "Website Script", "link_to": "Website Script",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -60,6 +64,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "About Us Settings", "label": "About Us Settings",
"link_count": 0,
"link_to": "About Us Settings", "link_to": "About Us Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -70,6 +75,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Contact Us Settings", "label": "Contact Us Settings",
"link_count": 0,
"link_to": "Contact Us Settings", "link_to": "Contact Us Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -80,6 +86,7 @@
"icon": "", "icon": "",
"is_query_report": 0, "is_query_report": 0,
"label": "Blog", "label": "Blog",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -88,6 +95,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Blog Post", "label": "Blog Post",
"link_count": 0,
"link_to": "Blog Post", "link_to": "Blog Post",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -98,6 +106,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Blogger", "label": "Blogger",
"link_count": 0,
"link_to": "Blogger", "link_to": "Blogger",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -108,6 +117,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Blog Category", "label": "Blog Category",
"link_count": 0,
"link_to": "Blog Category", "link_to": "Blog Category",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -118,6 +128,7 @@
"icon": "website", "icon": "website",
"is_query_report": 0, "is_query_report": 0,
"label": "Web Site", "label": "Web Site",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -126,6 +137,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Web Page", "label": "Web Page",
"link_count": 0,
"link_to": "Web Page", "link_to": "Web Page",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -136,6 +148,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Web Form", "label": "Web Form",
"link_count": 0,
"link_to": "Web Form", "link_to": "Web Form",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -146,6 +159,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Sidebar", "label": "Website Sidebar",
"link_count": 0,
"link_to": "Website Sidebar", "link_to": "Website Sidebar",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -156,6 +170,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Slideshow", "label": "Website Slideshow",
"link_count": 0,
"link_to": "Website Slideshow", "link_to": "Website Slideshow",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -166,6 +181,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Website Route Meta", "label": "Website Route Meta",
"link_count": 0,
"link_to": "Website Route Meta", "link_to": "Website Route Meta",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -176,6 +192,7 @@
"icon": "website", "icon": "website",
"is_query_report": 0, "is_query_report": 0,
"label": "Portal", "label": "Portal",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -184,6 +201,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Portal Settings", "label": "Portal Settings",
"link_count": 0,
"link_to": "Portal Settings", "link_to": "Portal Settings",
"link_type": "DocType", "link_type": "DocType",
"onboard": 1, "onboard": 1,
@ -194,6 +212,7 @@
"icon": "project", "icon": "project",
"is_query_report": 0, "is_query_report": 0,
"label": "Knowledge Base", "label": "Knowledge Base",
"link_count": 0,
"onboard": 0, "onboard": 0,
"type": "Card Break" "type": "Card Break"
}, },
@ -202,6 +221,7 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Help Category", "label": "Help Category",
"link_count": 0,
"link_to": "Help Category", "link_to": "Help Category",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
@ -212,20 +232,26 @@
"hidden": 0, "hidden": 0,
"is_query_report": 0, "is_query_report": 0,
"label": "Help Article", "label": "Help Article",
"link_count": 0,
"link_to": "Help Article", "link_to": "Help Article",
"link_type": "DocType", "link_type": "DocType",
"onboard": 0, "onboard": 0,
"type": "Link" "type": "Link"
} }
], ],
"modified": "2020-12-01 13:38:39.556588", "modified": "2021-08-05 12:16:03.154032",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Website", "name": "Website",
"onboarding": "Website", "onboarding": "Website",
"owner": "Administrator", "owner": "Administrator",
"parent_page": "",
"pin_to_bottom": 0, "pin_to_bottom": 0,
"pin_to_top": 0, "pin_to_top": 0,
"public": 1,
"restrict_to_domain": "",
"roles": [],
"sequence_id": 28,
"shortcuts": [ "shortcuts": [
{ {
"color": "Green", "color": "Green",
@ -261,5 +287,6 @@
"link_to": "Website Settings", "link_to": "Website Settings",
"type": "DocType" "type": "DocType"
} }
] ],
"title": "Website"
} }

View file

@ -20,6 +20,7 @@
}, },
"homepage": "https://frappeframework.com", "homepage": "https://frappeframework.com",
"dependencies": { "dependencies": {
"@editorjs/editorjs": "2.20.0",
"ace-builds": "^1.4.8", "ace-builds": "^1.4.8",
"air-datepicker": "github:frappe/air-datepicker", "air-datepicker": "github:frappe/air-datepicker",
"autoprefixer": "^9.8.6", "autoprefixer": "^9.8.6",
@ -30,6 +31,7 @@
"cropperjs": "^1.5.12", "cropperjs": "^1.5.12",
"cssnano": "^5.0.0", "cssnano": "^5.0.0",
"driver.js": "^0.9.8", "driver.js": "^0.9.8",
"editorjs-undo": "0.1.6",
"express": "^4.17.1", "express": "^4.17.1",
"fast-deep-equal": "^2.0.1", "fast-deep-equal": "^2.0.1",
"frappe-charts": "^2.0.0-rc13", "frappe-charts": "^2.0.0-rc13",

View file

@ -40,6 +40,14 @@
resolved "https://registry.yarnpkg.com/@deepcode/dcignore/-/dcignore-1.0.2.tgz#39e4a3df7dde8811925330506e4bb3fbf3c288d8" resolved "https://registry.yarnpkg.com/@deepcode/dcignore/-/dcignore-1.0.2.tgz#39e4a3df7dde8811925330506e4bb3fbf3c288d8"
integrity sha512-DPgxtHuJwBORpqRkPXzzOT+uoPRVJmaN7LR+pmeL6DQM90kj6G6GFUH1i/YpRH8NbML8ZGEDwB9f9u4UwD2pzg== integrity sha512-DPgxtHuJwBORpqRkPXzzOT+uoPRVJmaN7LR+pmeL6DQM90kj6G6GFUH1i/YpRH8NbML8ZGEDwB9f9u4UwD2pzg==
"@editorjs/editorjs@2.20.0":
version "2.20.0"
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.20.0.tgz#1e0dc7e6c1433c34c9d2d3e42153313c2dbb3514"
integrity sha512-e6DWi8bMypFhovq9R6cefaDWVfrlVU++Q7ABp79+MxZIuC/SKAW5EtxBbKPL22H/Mc3bJIhZCxOqEl70HBh2yw==
dependencies:
codex-notifier "^1.1.2"
codex-tooltip "^1.0.1"
"@nodelib/fs.scandir@2.1.4": "@nodelib/fs.scandir@2.1.4":
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
@ -1510,6 +1518,16 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codex-notifier@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/codex-notifier/-/codex-notifier-1.1.2.tgz#a733079185f4c927fa296f1d71eb8753fe080895"
integrity sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==
codex-tooltip@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/codex-tooltip/-/codex-tooltip-1.0.2.tgz#81a9d3e2937658c6e5312106b47b9f094ff7be63"
integrity sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w==
color-convert@^1.9.0: color-convert@^1.9.0:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@ -2168,6 +2186,11 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0" jsbn "~0.1.0"
safer-buffer "^2.1.0" safer-buffer "^2.1.0"
editorjs-undo@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/editorjs-undo/-/editorjs-undo-0.1.6.tgz#823349a1e9a78d8bc68ba8570a2b854063bc804a"
integrity sha512-zVHPnBf2mcI8hWT9Eu8H3bGDEcMj4gppXbQjJW11Aa8Kdy2SVBGhM6fS59OUlBsm8iHWLxuoG2NUIfy9Rd30sw==
ee-first@1.1.1: ee-first@1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"