diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 6c81d6298a..454cc89694 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -50,6 +50,7 @@ if [ "$TYPE" == "server" ]; then sed -i 's/^socketio:/# socketio:/g' Procfile; f if [ "$TYPE" == "server" ]; then sed -i 's/^redis_socketio:/# redis_socketio:/g' Procfile; fi if [ "$TYPE" == "ui" ]; then bench setup requirements --node; fi +if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi # install node-sass which is required for website theme test cd ./apps/frappe || exit diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000000..df3ae9484a --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,3 @@ +Faker~=8.1.0 +pyngrok~=5.0.5 +unittest-xml-reporting~=3.0.4 diff --git a/frappe/__init__.py b/frappe/__init__.py index 1b4429d55b..c8245b0bf0 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -30,9 +30,6 @@ from .utils.lazy_loader import lazy_import from frappe.query_builder import get_query_builder, patch_query_execute -# Lazy imports -faker = lazy_import('faker') - __version__ = '14.0.0-dev' __title__ = "Frappe Framework" @@ -1838,6 +1835,7 @@ def parse_json(val): return parse_json(val) def mock(type, size=1, locale='en'): + import faker results = [] fake = faker.Faker(locale) if type not in dir(fake): diff --git a/frappe/automation/workspace/tools/tools.json b/frappe/automation/workspace/tools/tools.json index f556be1c07..fa2606dc43 100644 --- a/frappe/automation/workspace/tools/tools.json +++ b/frappe/automation/workspace/tools/tools.json @@ -1,20 +1,13 @@ { - "category": "", "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", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "tool", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Tools", "links": [ { @@ -215,15 +208,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:16:02.839180", + "modified": "2021-08-05 12:16:02.839181", "modified_by": "Administrator", "module": "Automation", "name": "Tools", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/frappe/build.py b/frappe/build.py index 8b32b03d60..6b93b8b93a 100644 --- a/frappe/build.py +++ b/frappe/build.py @@ -16,7 +16,6 @@ from frappe.utils.minify import JavascriptMinify import click import psutil from urllib.parse import urlparse -from simple_chalk import green from semantic_version import Version from requests import head from requests.exceptions import HTTPError @@ -108,7 +107,7 @@ def fetch_assets(url, frappe_head): if not assets_archive: raise AssetsNotDownloadedError(f"Assets could not be retrived from {url}") - print(f"\n{green('✔')} Downloaded Frappe assets from {url}") + click.echo(click.style("✔", fg="green") + f" Downloaded Frappe assets from {url}") return assets_archive @@ -131,7 +130,7 @@ def setup_assets(assets_archive): directories_created.add(asset_directory) tar.makefile(file, dest) - print("{0} Restored {1}".format(green('✔'), show)) + click.echo(click.style("✔", fg="green") + f" Restored {show}") return directories_created @@ -379,7 +378,7 @@ def make_asset_dirs(hard_link=False): except Exception: print(fail_message, end="\r") - print(unstrip(f"{green('✔')} Application Assets Linked") + "\n") + click.echo(unstrip(click.style("✔", fg="green") + " Application Assets Linked") + "\n") def link_assets_dir(source, target, hard_link=False): diff --git a/frappe/client.py b/frappe/client.py index 842e5cf199..fc87880f81 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -282,18 +282,17 @@ def bulk_update(docs): docs = json.loads(docs) failed_docs = [] for doc in docs: + doc.pop("flags", None) try: - ddoc = {key: val for key, val in doc.items() if key not in ['doctype', 'docname']} - doctype = doc['doctype'] - docname = doc['docname'] - doc = frappe.get_doc(doctype, docname) - doc.update(ddoc) - doc.save() - except: + existing_doc = frappe.get_doc(doc.pop("doctype"), doc.pop("docname")) + existing_doc.update(doc) + existing_doc.save() + except Exception: failed_docs.append({ 'doc': doc, 'exc': frappe.utils.get_traceback() }) + return {'failed_docs': failed_docs} @frappe.whitelist() diff --git a/frappe/core/workspace/build/build.json b/frappe/core/workspace/build/build.json index 8536c807d2..aabb4f9d1c 100644 --- a/frappe/core/workspace/build/build.json +++ b/frappe/core/workspace/build/build.json @@ -1,21 +1,13 @@ { - "cards_label": "Elements", - "category": "", "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}},{\"type\":\"card\",\"data\":{\"card_name\":\"Packages\",\"col\":4}}]", "creation": "2021-01-02 10:51:16.579957", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "tool", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Build", "links": [ { @@ -230,15 +222,12 @@ "type": "Link" } ], - "modified": "2021-09-05 21:14:52.384815", + "modified": "2021-09-05 21:14:52.384816", "modified_by": "Administrator", "module": "Core", "name": "Build", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/frappe/core/workspace/settings/settings.json b/frappe/core/workspace/settings/settings.json index 93a6c81c90..917ce2cbdc 100644 --- a/frappe/core/workspace/settings/settings.json +++ b/frappe/core/workspace/settings/settings.json @@ -1,20 +1,13 @@ { - "category": "", "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}}]", + "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", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "setting", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Settings", "links": [ { @@ -374,15 +367,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:16:03.456173", + "modified": "2021-08-05 12:16:03.456174", "modified_by": "Administrator", "module": "Core", "name": "Settings", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], @@ -407,6 +397,5 @@ "type": "DocType" } ], - "shortcuts_label": "Settings", "title": "Settings" } \ No newline at end of file diff --git a/frappe/core/workspace/users/users.json b/frappe/core/workspace/users/users.json index 09a835ea2c..85c110151b 100644 --- a/frappe/core/workspace/users/users.json +++ b/frappe/core/workspace/users/users.json @@ -1,20 +1,13 @@ { - "category": "", "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", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "users", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Users", "links": [ { @@ -152,15 +145,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:16:03.010204", + "modified": "2021-08-05 12:16:03.010205", "modified_by": "Administrator", "module": "Core", "name": "Users", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/frappe/custom/doctype/custom_field/custom_field.py b/frappe/custom/doctype/custom_field/custom_field.py index bf606701da..8c22d3c45c 100644 --- a/frappe/custom/doctype/custom_field/custom_field.py +++ b/frappe/custom/doctype/custom_field/custom_field.py @@ -131,7 +131,7 @@ def create_custom_field(doctype, df, ignore_validate=False): "permlevel": 0, "fieldtype": 'Data', "hidden": 0, - # Looks like we always use this programatically? + # Looks like we always use this programatically? # "is_standard": 1 }) custom_field.update(df) @@ -146,24 +146,29 @@ def create_custom_fields(custom_fields, ignore_validate = False, update=True): if not ignore_validate and frappe.flags.in_setup_wizard: ignore_validate = True - for doctype, fields in custom_fields.items(): + for doctypes, fields in custom_fields.items(): if isinstance(fields, dict): # only one field fields = [fields] - for df in fields: - field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) - if not field: - try: - df["owner"] = "Administrator" - create_custom_field(doctype, df, ignore_validate=ignore_validate) - except frappe.exceptions.DuplicateEntryError: - pass - elif update: - custom_field = frappe.get_doc("Custom Field", field) - custom_field.flags.ignore_validate = ignore_validate - custom_field.update(df) - custom_field.save() + if isinstance(doctypes, str): + # only one doctype + doctypes = (doctypes,) + + for doctype in doctypes: + for df in fields: + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": df["fieldname"]}) + if not field: + try: + df["owner"] = "Administrator" + create_custom_field(doctype, df, ignore_validate=ignore_validate) + except frappe.exceptions.DuplicateEntryError: + pass + elif update: + custom_field = frappe.get_doc("Custom Field", field) + custom_field.flags.ignore_validate = ignore_validate + custom_field.update(df) + custom_field.save() frappe.clear_cache(doctype=doctype) frappe.db.updatedb(doctype) diff --git a/frappe/custom/doctype/custom_field/test_custom_field.py b/frappe/custom/doctype/custom_field/test_custom_field.py index 9633f0eb8a..ad3cf27eea 100644 --- a/frappe/custom/doctype/custom_field/test_custom_field.py +++ b/frappe/custom/doctype/custom_field/test_custom_field.py @@ -6,7 +6,42 @@ import frappe import unittest -test_records = frappe.get_test_records('Custom Field') +test_records = frappe.get_test_records("Custom Field") + class TestCustomField(unittest.TestCase): - pass + def test_create_custom_fields(self): + from .custom_field import create_custom_fields + + create_custom_fields( + { + "Address": [ + { + "fieldname": "_test_custom_field_1", + "label": "_Test Custom Field 1", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + ("Address", "Contact"): [ + { + "fieldname": "_test_custom_field_2", + "label": "_Test Custom Field 2", + "fieldtype": "Data", + "insert_after": "phone", + }, + ], + } + ) + + frappe.db.commit() + + self.assertTrue( + frappe.db.exists("Custom Field", "Address-_test_custom_field_1") + ) + self.assertTrue( + frappe.db.exists("Custom Field", "Address-_test_custom_field_2") + ) + self.assertTrue( + frappe.db.exists("Custom Field", "Contact-_test_custom_field_2") + ) diff --git a/frappe/custom/workspace/customization/customization.json b/frappe/custom/workspace/customization/customization.json index 136b1a57eb..7aec530604 100644 --- a/frappe/custom/workspace/customization/customization.json +++ b/frappe/custom/workspace/customization/customization.json @@ -1,20 +1,13 @@ { - "category": "", "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", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "customization", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Customization", "links": [ { @@ -130,15 +123,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:15:57.486112", + "modified": "2021-08-05 12:15:57.486113", "modified_by": "Administrator", "module": "Custom", "name": "Customization", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index b9b01d0a74..e1789852f1 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -32,9 +32,6 @@ class Workspace: self.page_name = page.get('name') self.page_title = page.get('title') self.public_page = page.get('public') - self.extended_links = [] - self.extended_charts = [] - self.extended_shortcuts = [] self.workspace_manager = "Workspace Manager" in frappe.get_roles() self.user = frappe.get_user() @@ -151,21 +148,6 @@ class Workspace: return doc - def get_pages_to_extend(self): - pages = frappe.get_all("Workspace", filters={ - "extends": self.page_name, - 'restrict_to_domain': ['in', frappe.get_active_domains()], - 'for_user': '', - 'module': ['in', self.allowed_modules] - }) - - pages = [frappe.get_cached_doc("Workspace", page['name']) for page in pages] - - for page in pages: - self.extended_links = self.extended_links + page.get_link_groups() - self.extended_charts = self.extended_charts + page.charts - self.extended_shortcuts = self.extended_shortcuts + page.shortcuts - def is_item_allowed(self, name, item_type): if frappe.session.user == "Administrator": return True @@ -187,17 +169,14 @@ class Workspace: def build_workspace(self): self.cards = { - 'label': _(self.doc.cards_label), 'items': self.get_links() } self.charts = { - 'label': _(self.doc.charts_label), 'items': self.get_charts() } self.shortcuts = { - 'label': _(self.doc.shortcuts_label), 'items': self.get_shortcuts() } @@ -249,9 +228,6 @@ class Workspace: if not self.doc.hide_custom: cards = cards + get_custom_reports_and_doctypes(self.doc.module) - if len(self.extended_links): - cards = merge_cards_based_on_label(cards + self.extended_links) - default_country = frappe.db.get_default("country") new_data = [] @@ -289,8 +265,6 @@ class Workspace: all_charts = [] if frappe.has_permission("Dashboard Chart", throw=False): charts = self.doc.charts - if len(self.extended_charts): - charts = charts + self.extended_charts for chart in charts: if frappe.has_permission('Dashboard Chart', doc=chart.chart_name): @@ -311,8 +285,6 @@ class Workspace: items = [] shortcuts = self.doc.shortcuts - if len(self.extended_shortcuts): - shortcuts = shortcuts + self.extended_shortcuts for item in shortcuts: new_item = item.as_dict().copy() @@ -380,8 +352,7 @@ def get_desktop_page(page): 'charts': wspace.charts, 'shortcuts': wspace.shortcuts, 'cards': wspace.cards, - 'onboardings': wspace.onboardings, - 'allow_customization': not wspace.doc.disable_user_customization + 'onboardings': wspace.onboardings } except DoesNotExistError: frappe.log_error(frappe.get_traceback()) @@ -414,7 +385,7 @@ def get_wspace_sidebar_items(): # Filter Page based on Permission for page in all_pages: try: - wspace = Workspace(page) + wspace = Workspace(page, True) if wspace.is_permitted() and wspace.is_page_allowed() or has_access: if page.public: pages.append(page) @@ -461,7 +432,6 @@ def get_custom_doctype_list(module): return out - def get_custom_report_list(module): """Returns list on new style reports for modules.""" reports = frappe.get_all("Report", fields=["name", "ref_doctype", "report_type"], filters= @@ -482,85 +452,6 @@ def get_custom_report_list(module): return out -def get_custom_workspace_for_user(page): - """Get custom page from workspace if exists or create one - - Args: - page (stirng): Page name - - Returns: - Object: Document object - """ - filters = { - 'extends': page, - 'for_user': frappe.session.user, - } - pages = frappe.get_list("Workspace", filters=filters) - if pages: - return frappe.get_doc("Workspace", pages[0]) - doc = frappe.new_doc("Workspace") - doc.extends = page - doc.for_user = frappe.session.user - return doc - -@frappe.whitelist() -def save_customization(page, config): - """Save customizations as a separate doctype in Workspace per user - - Args: - page (string): Name of the page to be edited - config (dict): Dictionary config of al widgets - - Returns: - Boolean: Customization saving status - """ - original_page = frappe.get_doc("Workspace", page) - page_doc = get_custom_workspace_for_user(page) - - # Update field values - page_doc.update({ - "icon": original_page.icon, - "charts_label": original_page.charts_label, - "cards_label": original_page.cards_label, - "shortcuts_label": original_page.shortcuts_label, - "module": original_page.module, - "onboarding": original_page.onboarding, - "developer_mode_only": original_page.developer_mode_only, - "category": original_page.category - }) - - config = _dict(loads(config)) - if config.charts: - page_doc.charts = prepare_widget(config.charts, "Workspace Chart", "charts") - if config.shortcuts: - page_doc.shortcuts = prepare_widget(config.shortcuts, "Workspace Shortcut", "shortcuts") - if config.cards: - page_doc.build_links_table_from_cards(config.cards) - - # Set label - page_doc.label = page + '-' + frappe.session.user - - try: - if page_doc.is_new(): - page_doc.insert(ignore_permissions=True) - else: - page_doc.save(ignore_permissions=True) - except (ValidationError, TypeError) as e: - # Create a json string to log - json_config = dumps(config, 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 save_new_widget(doc, page, blocks, new_widgets): widgets = _dict(loads(new_widgets)) @@ -593,6 +484,7 @@ def save_new_widget(doc, page, blocks, new_widgets): return False return True + def clean_up(original_page, blocks): page_widgets = {} @@ -670,40 +562,14 @@ def prepare_widget(config, doctype, parentfield): prepare_widget_list.append(doc) return prepare_widget_list - @frappe.whitelist() def update_onboarding_step(name, field, value): """Update status of onboaridng step Args: - name (string): Name of the doc - field (string): field to be updated - value: Value to be updated + name (string): Name of the doc + field (string): field to be updated + value: Value to be updated """ frappe.db.set_value("Onboarding Step", name, field, value) - -@frappe.whitelist() -def reset_customization(page): - """Reset workspace customizations for a user - - Args: - page (string): Name of the page to be reset - """ - page_doc = get_custom_workspace_for_user(page) - page_doc.delete() - -def merge_cards_based_on_label(cards): - """Merge cards with common label.""" - cards_dict = {} - for card in cards: - label = card.get('label') - if label in cards_dict: - links = cards_dict[label].links + card.links - cards_dict[label].update(dict(links=links)) - cards_dict[label] = cards_dict.pop(label) - else: - cards_dict[label] = card - - return list(cards_dict.values()) - diff --git a/frappe/desk/doctype/workspace/workspace.js b/frappe/desk/doctype/workspace/workspace.js index 19d429f9f6..5377470343 100644 --- a/frappe/desk/doctype/workspace/workspace.js +++ b/frappe/desk/doctype/workspace/workspace.js @@ -8,14 +8,9 @@ frappe.ui.form.on('Workspace', { refresh: function(frm) { frm.enable_save(); - frm.get_field("is_standard").toggle(frappe.boot.developer_mode); - frm.get_field("developer_mode_only").toggle(frappe.boot.developer_mode); - if (frm.doc.for_user) { - frm.set_df_property("extends", "read_only", true); - } - - if (frm.doc.for_user || (frm.doc.is_standard && !frappe.boot.developer_mode)) { + if (frm.doc.for_user || (frm.doc.public && !frm.has_perm('write') && + !frappe.user.has_role('Workspace Manager'))) { frm.trigger('disable_form'); } }, diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 756a40da4b..04975c69e3 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -11,32 +11,19 @@ "title", "sequence_id", "for_user", - "extends", "parent_page", "module", - "category", + "column_break_3", "icon", "restrict_to_domain", - "onboarding", - "column_break_3", - "extends_another_page", - "is_default", - "is_standard", - "developer_mode_only", - "disable_user_customization", - "pin_to_top", - "pin_to_bottom", "hide_custom", "public", "content", "section_break_2", - "charts_label", "charts", "section_break_15", - "shortcuts_label", "shortcuts", "section_break_18", - "cards_label", "links", "roles_section", "roles" @@ -63,7 +50,6 @@ "options": "Workspace Chart" }, { - "depends_on": "eval:!doc.extends_another_page || !doc.is_standard || frappe.boot.developer_mode", "fieldname": "shortcuts", "fieldtype": "Table", "label": "Shortcuts", @@ -74,7 +60,6 @@ "fieldtype": "Link", "label": "Restrict to Domain", "options": "Domain", - "read_only_depends_on": "eval:doc.extends_another_page == 0", "search_index": 1 }, { @@ -89,64 +74,6 @@ "fieldname": "column_break_3", "fieldtype": "Column Break" }, - { - "fieldname": "category", - "fieldtype": "Select", - "label": "Category", - "options": "Modules\nDomains\nPlaces\nAdministration", - "read_only_depends_on": "eval:doc.extends_another_page == 1", - "search_index": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.extends_another_page == 0", - "fieldname": "developer_mode_only", - "fieldtype": "Check", - "label": "Developer Mode Only", - "search_index": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.pin_to_bottom!=1 && doc.extends_another_page == 0", - "fieldname": "pin_to_top", - "fieldtype": "Check", - "label": "Pin To Top", - "search_index": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.extends_another_page == 0", - "fieldname": "disable_user_customization", - "fieldtype": "Check", - "label": "Disable User Customization", - "search_index": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.pin_to_top!=1 && doc.extends_another_page == 0", - "fieldname": "pin_to_bottom", - "fieldtype": "Check", - "label": "Pin To Bottom", - "search_index": 1 - }, - { - "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", - "fieldname": "charts_label", - "fieldtype": "Data", - "label": "Label" - }, - { - "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", - "fieldname": "shortcuts_label", - "fieldtype": "Data", - "label": "Label" - }, - { - "depends_on": "eval:!doc.extends_another_page || !doc.is_standard", - "fieldname": "cards_label", - "fieldtype": "Data", - "label": "Label" - }, { "collapsible": 1, "collapsible_depends_on": "shortcuts", @@ -161,40 +88,12 @@ "fieldtype": "Section Break", "label": "Link Cards" }, - { - "default": "0", - "fieldname": "is_standard", - "fieldtype": "Check", - "label": "Is Standard", - "search_index": 1 - }, - { - "default": "0", - "fieldname": "extends_another_page", - "fieldtype": "Check", - "label": "Extends Another Page", - "search_index": 1 - }, - { - "depends_on": "eval:doc.extends_another_page == 1 || doc.for_user", - "fieldname": "extends", - "fieldtype": "Link", - "label": "Extends", - "options": "Workspace", - "search_index": 1 - }, { "fieldname": "for_user", "fieldtype": "Data", "label": "For User", "read_only": 1 }, - { - "fieldname": "onboarding", - "fieldtype": "Link", - "label": "Onboarding", - "options": "Module Onboarding" - }, { "default": "0", "description": "Checking this will hide custom doctypes and reports cards in Links section", @@ -213,21 +112,14 @@ "label": "Links", "options": "Workspace Link" }, - { - "default": "0", - "depends_on": "extends_another_page", - "description": "Sets the current page as default for all users", - "fieldname": "is_default", - "fieldtype": "Check", - "label": "Is Default" - }, { "default": "0", "fieldname": "public", "fieldtype": "Check", "in_list_view": 1, "in_standard_filter": 1, - "label": "Public" + "label": "Public", + "search_index": 1 }, { "fieldname": "title", @@ -266,7 +158,7 @@ ], "in_create": 1, "links": [], - "modified": "2021-09-16 12:01:06.450621", + "modified": "2021-09-16 12:01:06.450622", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index a0a22a43fc..94114e3918 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -13,8 +13,8 @@ from json import loads class Workspace(Document): def validate(self): - if (self.is_standard and not frappe.conf.developer_mode and not disable_saving_as_standard()): - frappe.throw(_("You need to be in developer mode to edit this document")) + if (self.public and not is_workspace_manager() and not disable_saving_as_public()): + frappe.throw(_("You need to be Workspace Manager to edit this document")) validate_route_conflict(self.doctype, self.name) try: @@ -23,15 +23,8 @@ class Workspace(Document): except Exception: frappe.throw(_("Content data shoud be a list")) - duplicate_exists = frappe.db.exists("Workspace", { - "name": ["!=", self.name], 'is_default': 1, 'extends': self.extends - }) - - if self.is_default and self.name and duplicate_exists: - frappe.throw(_("You can only have one default page that extends a particular standard page.")) - def on_update(self): - if disable_saving_as_standard(): + if disable_saving_as_public(): return if frappe.conf.developer_mode and self.module and self.public: @@ -39,12 +32,7 @@ class Workspace(Document): @staticmethod def get_module_page_map(): - filters = { - 'extends_another_page': 0, - 'for_user': '', - } - - pages = frappe.get_all("Workspace", fields=["name", "module"], filters=filters, as_list=1) + pages = frappe.get_all("Workspace", fields=["name", "module"], filters={'for_user': ''}, as_list=1) return { page[1]: page[0] for page in pages if page[1] } @@ -76,35 +64,6 @@ class Workspace(Document): return cards - def build_links_table_from_cards(self, config): - # Empty links table - self.links = [] - order = config.get('order') - widgets = config.get('widgets') - - for idx, name in enumerate(order): - card = widgets[name].copy() - links = loads(card.get('links')) - - self.append('links', { - "label": card.get('label'), - "type": "Card Break", - "icon": card.get('icon'), - "hidden": card.get('hidden') or False - }) - - 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') - }) - def build_links_table_from_card(self, config): for idx, card in enumerate(config): @@ -137,7 +96,7 @@ class Workspace(Document): "idx": self.links[-1].idx + 1 }) -def disable_saving_as_standard(): +def disable_saving_as_public(): return frappe.flags.in_install or \ frappe.flags.in_patch or \ frappe.flags.in_test or \ @@ -212,7 +171,7 @@ def save_page(title, icon, parent, public, sb_public_items, sb_private_items, de def delete_pages(deleted_pages): for page in deleted_pages: - if page.get("public") and "Workspace Manager" not in frappe.get_roles(): + if page.get("public") and not is_workspace_manager(): return {"name": page.get("title"), "public": 1, "label": page.get("label")} if frappe.db.exists("Workspace", page.get("name")): @@ -227,7 +186,7 @@ def sort_pages(sb_public_items, sb_private_items): if sb_private_items: sort_page(wspace_private_pages, sb_private_items) - if sb_public_items and "Workspace Manager" in frappe.get_roles(): + if sb_public_items and is_workspace_manager(): sort_page(wspace_public_pages, sb_public_items) def sort_page(wspace_pages, pages): @@ -242,3 +201,6 @@ def sort_page(wspace_pages, pages): def get_page_list(fields, filters): return frappe.get_list("Workspace", fields=fields, filters=filters, order_by='sequence_id asc') + +def is_workspace_manager(): + return "Workspace Manager" in frappe.get_roles() diff --git a/frappe/integrations/workspace/integrations/integrations.json b/frappe/integrations/workspace/integrations/integrations.json index 4167858db2..b85056e3ef 100644 --- a/frappe/integrations/workspace/integrations/integrations.json +++ b/frappe/integrations/workspace/integrations/integrations.json @@ -1,20 +1,13 @@ { - "category": "", "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", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "integration", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Integrations", "links": [ { @@ -267,15 +260,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:16:00.355267", + "modified": "2021-08-05 12:16:00.355268", "modified_by": "Administrator", "module": "Integrations", "name": "Integrations", - "onboarding": "", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 14f1dbf2b0..de83b24cd8 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -458,7 +458,7 @@ def bulk_rename(doctype, rows=None, via_console = False): """Bulk rename documents :param doctype: DocType to be renamed - :param rows: list of documents as `((oldname, newname), ..)`""" + :param rows: list of documents as `((oldname, newname, merge(optional)), ..)`""" if not rows: frappe.throw(_("Please select a valid csv file with data")) @@ -471,8 +471,9 @@ def bulk_rename(doctype, rows=None, via_console = False): for row in rows: # if row has some content if len(row) > 1 and row[0] and row[1]: + merge = len(row) > 2 and (row[2] == "1" or row[2].lower() == "true") try: - if rename_doc(doctype, row[0], row[1]): + if rename_doc(doctype, row[0], row[1], merge=merge): msg = _("Successful: {0} to {1}").format(row[0], row[1]) frappe.db.commit() else: diff --git a/frappe/patches.txt b/frappe/patches.txt index 41ca1a1724..85df031073 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -182,4 +182,4 @@ frappe.patches.v13_0.jinja_hook frappe.patches.v13_0.update_notification_channel_if_empty frappe.patches.v14_0.drop_data_import_legacy frappe.patches.v14_0.rename_cancelled_documents -frappe.patches.v14_0.update_workspace2 # 25.08.2021 +frappe.patches.v14_0.update_workspace2 # 20.09.2021 diff --git a/frappe/patches/v14_0/update_workspace2.py b/frappe/patches/v14_0/update_workspace2.py index c212faee76..82076c4328 100644 --- a/frappe/patches/v14_0/update_workspace2.py +++ b/frappe/patches/v14_0/update_workspace2.py @@ -4,8 +4,8 @@ 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)): + + for seq, wspace in enumerate(frappe.get_all('Workspace', order_by='name asc')): doc = frappe.get_doc('Workspace', wspace.name) content = create_content(doc) update_wspace(doc, seq, content) @@ -53,7 +53,7 @@ def update_wspace(doc, seq, content): if not doc.title and not doc.content and not doc.is_standard and not doc.public: doc.sequence_id = seq + 1 doc.content = json.dumps(content) - doc.public = 0 + doc.public = 0 if doc.for_user else 1 doc.title = doc.extends or doc.label doc.extends = '' doc.category = '' diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index a4dc1a6709..a53368d67a 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -283,7 +283,7 @@ frappe.Application = class Application { frappe.workspaces = {}; for (let page of frappe.boot.allowed_workspaces || []) { frappe.modules[page.module]=page; - frappe.workspaces[frappe.router.slug(page.title)] = page; + frappe.workspaces[frappe.router.slug(page.name)] = page; } } diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index 128bd355ad..b9ec9a5438 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -302,7 +302,7 @@ class FormTimeline extends BaseTimeline { (this.doc_info.info_logs || []).forEach(info_log => { info_timeline_contents.push({ creation: info_log.creation, - content: `${this.get_user_link(info_log.comment_email)} ${info_log.content}`, + content: `${this.get_user_link(info_log.owner)} ${info_log.content}`, }); }); return info_timeline_contents; diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 864670cb9b..7874ffcdde 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -133,12 +133,14 @@ frappe.router = { // /app/user/user-001 = ["Form", "User", "user-001"] // /app/event/view/calendar/default = ["List", "Event", "Calendar", "Default"] + let private_wspace = route[1] && `${route[1]}-${frappe.user.name.toLowerCase()}`; + if (frappe.workspaces[route[0]]) { // public workspace route = ['Workspaces', frappe.workspaces[route[0]].title]; - } else if (route[0] == 'private' && frappe.workspaces[route[1]]) { + } else if (route[0] == 'private' && frappe.workspaces[private_wspace]) { // private workspace - route = ['Workspaces', 'private', frappe.workspaces[route[1]].title]; + route = ['Workspaces', 'private', frappe.workspaces[private_wspace].title]; } else if (this.routes[route[0]]) { // route route = this.set_doctype_route(route); @@ -363,7 +365,8 @@ frappe.router = { return a; } }).join('/'); - let default_page = frappe.workspaces['home'] ? 'home' : Object.keys(frappe.workspaces)[0]; + let private_home = frappe.workspaces[`home-${frappe.user.name.toLowerCase()}`]; + let default_page = private_home ? 'private/home' : frappe.workspaces['home'] ? 'home' : Object.keys(frappe.workspaces)[0]; return '/app/' + (path_string || default_page); }, diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index 952fd62aa1..6d1d7228e3 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -50,38 +50,31 @@ frappe.search.AwesomeBar = class AwesomeBar { this.awesomplete = awesomplete; - $input.on("input", function(e) { + $input.on("input", frappe.utils.debounce(function(e) { var value = e.target.value; var txt = value.trim().replace(/\s\s+/g, ' '); var last_space = txt.lastIndexOf(' '); me.global_results = []; - // if(txt && txt.length > 1) { - // me.global.get_awesome_bar_options(txt.toLowerCase(), me); - // } - var $this = $(this); - clearTimeout($this.data('timeout')); + me.options = []; - $this.data('timeout', setTimeout(function(){ - me.options = []; - if(txt && txt.length > 1) { - if(last_space !== -1) { - me.set_specifics(txt.slice(0,last_space), txt.slice(last_space+1)); - } - me.add_defaults(txt); - me.options = me.options.concat(me.build_options(txt)); - me.options = me.options.concat(me.global_results); - } else { - me.options = me.options.concat( - me.deduplicate(frappe.search.utils.get_recent_pages(txt || ""))); - me.options = me.options.concat(frappe.search.utils.get_frequent_links()); + if (txt && txt.length > 1) { + if (last_space !== -1) { + me.set_specifics(txt.slice(0, last_space), txt.slice(last_space+1)); } - me.add_help(); + me.add_defaults(txt); + me.options = me.options.concat(me.build_options(txt)); + me.options = me.options.concat(me.global_results); + } else { + me.options = me.options.concat( + me.deduplicate(frappe.search.utils.get_recent_pages(txt || ""))); + me.options = me.options.concat(frappe.search.utils.get_frequent_links()); + } + me.add_help(); - awesomplete.list = me.deduplicate(me.options); - }, 100)); + awesomplete.list = me.deduplicate(me.options); - }); + }, 500)); var open_recent = function() { if (!this.autocomplete_open) { diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index b49dfa0280..2baff996c6 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -23,6 +23,14 @@ if (!Array.prototype.uniqBy) { }); } +// Python's dict.setdefault ported for JS objects +Object.defineProperty(Object.prototype, "setDefault", { + value: function(key, default_value) { + if (!(key in this)) this[key] = default_value; + return this[key]; + } +}); + // Pluralize String.prototype.plural = function(revert) { const plural = { diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index 4a3a953a01..04cc1b9880 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -517,9 +517,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { } else { this.page.show_form(); } - - this.page.body[0].style.setProperty('--report-filter-height', this.page.page_form.css('height')); - this.page.body.parent().css('margin-bottom', 'unset'); } set_filters(filters) { @@ -834,7 +831,6 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { if (this.raw_data.add_total_row) { data = data.slice(); data.splice(-1, 1); - this.$page.find('.layout-main-section')[0].style.setProperty('--report-total-height', '310px'); } this.$report.show(); diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 2a92d93e30..8866a4b2af 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -50,8 +50,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.setup_columns(); super.setup_new_doc_event(); this.page.main.addClass('report-view'); - this.page.body[0].style.setProperty('--report-filter-height', this.page.page_form.css('height')); - this.page.body.parent().css('margin-bottom', 'unset'); } toggle_side_bar() { diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index 15e27fed40..9b4a2ed14f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -30,7 +30,7 @@ export default class Card extends Block { this.new('card', 'links'); if (this.data && this.data.card_name) { - let has_data = this.make('card', this.data.card_name, 'links'); + let has_data = this.make('card', __(this.data.card_name), 'links'); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index e41063e6fc..02e6a66e6f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -30,7 +30,7 @@ export default class Chart extends Block { this.new('chart'); if (this.data && this.data.chart_name) { - let has_data = this.make('chart', this.data.chart_name); + let has_data = this.make('chart', __(this.data.chart_name)); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 356f9c3244..d88bc42af9 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -27,7 +27,7 @@ export default class Header extends Block { data = {}; } - newData.text = data.text || ''; + newData.text = (data.text && __(data.text.replace(/(\n|\t)/gm, ""))) || ''; newData.level = parseInt(data.level) || this.defaultLevel.number; newData.col = parseInt(data.col) || 12; diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 26afa65d51..9e5dfb68ff 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -177,7 +177,7 @@ export default class Paragraph extends Block { set data(data) { this._data = data || {}; - this._element.innerHTML = this._data.text || ''; + this._element.innerHTML = __(this._data.text) || ''; } static get pasteConfig() { diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index f7482a06f3..96b8f47484 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -29,7 +29,7 @@ export default class Shortcut extends Block { this.new('shortcut'); if (this.data && this.data.shortcut_name) { - let has_data = this.make('shortcut', this.data.shortcut_name); + let has_data = this.make('shortcut', __(this.data.shortcut_name)); if (!has_data) return; } diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 8989814349..e6248f66cf 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -51,38 +51,37 @@ frappe.views.Workspace = class Workspace { this.body = this.wrapper.find(".layout-main-section"); } - setup_pages(reload) { - this.get_pages().then(pages => { - this.all_pages = pages.pages; - this.has_access = pages.has_access; + async setup_pages(reload) { + this.sidebar_pages = !this.discard ? await this.get_pages() : this.sidebar_pages; + this.all_pages = this.sidebar_pages.pages; + this.has_access = this.sidebar_pages.has_access; - this.all_pages.forEach(page => { - page.is_editable = !page.public || pages.has_access; - }); - - this.public_pages = this.all_pages.filter(page => page.public); - this.private_pages = this.all_pages.filter(page => !page.public); - - if (this.all_pages) { - frappe.workspaces = {}; - for (let page of this.all_pages) { - frappe.workspaces[frappe.router.slug(page.title)] = {title: page.title}; - } - if (this.new_page && this.new_page.name) { - if (!frappe.workspaces[frappe.router.slug(this.new_page.name)]) { - this.new_page = { name: this.all_pages[0].title, public: this.all_pages[0].public }; - } - if (this.new_page.public) { - frappe.set_route(`${frappe.router.slug(this.new_page.name)}`); - } else { - frappe.set_route(`private/${frappe.router.slug(this.new_page.name)}`); - } - this.new_page = null; - } - this.make_sidebar(); - reload && this.show(); - } + this.all_pages.forEach(page => { + page.is_editable = !page.public || this.has_access; }); + + this.public_pages = this.all_pages.filter(page => page.public); + this.private_pages = this.all_pages.filter(page => !page.public); + + if (this.all_pages) { + frappe.workspaces = {}; + for (let page of this.all_pages) { + frappe.workspaces[frappe.router.slug(page.name)] = {title: page.title}; + } + if (this.new_page && this.new_page.name) { + if (!frappe.workspaces[frappe.router.slug(this.new_page.label)]) { + this.new_page = { name: this.all_pages[0].title, public: this.all_pages[0].public }; + } + if (this.new_page.public) { + frappe.set_route(`${frappe.router.slug(this.new_page.name)}`); + } else { + frappe.set_route(`private/${frappe.router.slug(this.new_page.name)}`); + } + this.new_page = null; + } + this.make_sidebar(); + reload && this.show(); + } } get_pages() { @@ -95,10 +94,10 @@ frappe.views.Workspace = class Workspace {
@@ -152,8 +151,8 @@ frappe.views.Workspace = class Workspace { append_item(item, container) { let is_current_page = frappe.router.slug(item.title) == frappe.router.slug(this.get_page_to_show().name) && item.public == this.get_page_to_show().public; + item.selected = is_current_page; if (is_current_page) { - item.selected = true; this.current_page = { name: item.title, public: item.public }; } @@ -219,14 +218,14 @@ frappe.views.Workspace = class Workspace { if (!this.page_data || Object.keys(this.page_data).length === 0) return; + if (this.page_data.charts && this.page_data.charts.items.length === 0) return; + return frappe.dashboard_utils.get_dashboard_settings().then(settings => { if (settings) { let chart_config = settings.chart_config ? JSON.parse(settings.chart_config) : {}; - if (this.page_data.charts && this.page_data.charts.items) { - this.page_data.charts.items.map(chart => { - chart.chart_settings = chart_config[chart.chart_name] || {}; - }); - } + this.page_data.charts.items.map(chart => { + chart.chart_settings = chart_config[chart.chart_name] || {}; + }); this.pages[page.name] = this.page_data; } }); @@ -272,8 +271,7 @@ frappe.views.Workspace = class Workspace { `).appendTo(this.body); } - this.$page.prepend(frappe.render_template('workspace_loading_skeleton')); - this.$page.find('.codex-editor').addClass('hidden'); + this.create_skeleton(); if (this.all_pages) { let pages = page.public ? this.public_pages : this.private_pages; @@ -293,8 +291,7 @@ frappe.views.Workspace = class Workspace { this.prepare_editorjs(); $('.item-anchor').removeClass('disable-click'); - this.$page.find('.codex-editor').removeClass('hidden'); - this.$page.find('.workspace-skeleton').remove(); + this.remove_skeleton(); } } @@ -336,10 +333,10 @@ frappe.views.Workspace = class Workspace { this.page.clear_secondary_action(); this.page.clear_inner_toolbar(); - current_page.is_editable && this.page.set_secondary_action(__("Edit"), () => { + current_page.is_editable && this.page.set_secondary_action(__("Edit"), async () => { if (!this.editor || !this.editor.readOnly) return; this.is_read_only = false; - this.editor.readOnly.toggle(); + await this.editor.readOnly.toggle(); this.editor.isReady.then(() => { this.initialize_editorjs_undo(); this.setup_customization_buttons(current_page); @@ -383,13 +380,13 @@ frappe.views.Workspace = class Workspace { this.page.set_secondary_action( __("Discard"), - () => { + async () => { + this.discard = true; this.page.clear_primary_action(); this.page.clear_secondary_action(); this.page.clear_inner_toolbar(); - this.editor.readOnly.toggle(); + await this.editor.readOnly.toggle(); this.is_read_only = true; - this.deleted_sidebar_items = []; this.reload(); frappe.show_alert({ message: __("Customizations Discarded"), indicator: "info" }); } @@ -568,10 +565,10 @@ frappe.views.Workspace = class Workspace { } } ] - }).then(() => { + }).then(async () => { if (this.editor.configuration.readOnly) { this.is_read_only = false; - this.editor.readOnly.toggle(); + await this.editor.readOnly.toggle(); } this.add_page_to_sidebar(values); this.show_sidebar_actions(); @@ -646,7 +643,10 @@ frappe.views.Workspace = class Workspace { this.tools = { header: { class: this.blocks['header'], - inlineToolbar: true + inlineToolbar: true, + config: { + defaultLevel: 4 + } }, paragraph: { class: this.blocks['paragraph'], @@ -693,6 +693,7 @@ frappe.views.Workspace = class Workspace { save_page() { frappe.dom.freeze(); + this.create_skeleton(); let save = true; if (!this.title && this.current_page) { let pages = this.current_page.public ? this.public_pages : this.private_pages; @@ -740,13 +741,6 @@ frappe.views.Workspace = class Workspace { if (res.message) { me.new_page = res.message; me.pages[res.message.label] && delete me.pages[res.message.label]; - me.title = ''; - me.icon = ''; - me.parent = ''; - me.public = false; - me.sorted_public_items = []; - me.sorted_private_items = []; - me.deleted_sidebar_items = []; me.reload(); frappe.show_alert({ message: __("Page Saved Successfully"), indicator: "green" }); } @@ -759,9 +753,26 @@ frappe.views.Workspace = class Workspace { } reload() { - this.$page.prepend(frappe.render_template('workspace_loading_skeleton')); - this.$page.find('.codex-editor').addClass('hidden'); + this.title = ''; + this.icon = ''; + this.parent = ''; + this.public = false; + this.sorted_public_items = []; + this.sorted_private_items = []; + this.deleted_sidebar_items = []; + this.create_skeleton(); this.setup_pages(true); + this.discard = false; this.undo.readOnly = true; } + + create_skeleton() { + this.$page.prepend(frappe.render_template('workspace_loading_skeleton')); + this.$page.find('.codex-editor').addClass('hidden'); + } + + remove_skeleton() { + this.$page.find('.codex-editor').removeClass('hidden'); + this.$page.find('.workspace-skeleton').remove(); + } }; diff --git a/frappe/public/js/frappe/widgets/widget_group.js b/frappe/public/js/frappe/widgets/widget_group.js index d8f92edc5d..cb3fc58ea3 100644 --- a/frappe/public/js/frappe/widgets/widget_group.js +++ b/frappe/public/js/frappe/widgets/widget_group.js @@ -191,7 +191,6 @@ export class SingleWidgetGroup { Object.assign(this, opts); this.widgets_list = []; this.widgets_dict = {}; - this.widget_order = []; this.make(); } diff --git a/frappe/public/scss/desk/report.scss b/frappe/public/scss/desk/report.scss index 2389a4f8f6..f8666602ff 100644 --- a/frappe/public/scss/desk/report.scss +++ b/frappe/public/scss/desk/report.scss @@ -84,39 +84,17 @@ margin-bottom: 10px; } -.layout-main-section { - --report-filter-height: 0px; - --report-total-height: 275px; -} - .report-wrapper { overflow: auto; - - .datatable { - height: calc(100vh - var(--report-filter-height) - 205px); - - .dt-scrollable { - height: calc(100vh - var(--report-filter-height) - var(--report-total-height)); - } - } } .report-view { .result { - min-height: 50vh !important; .dt-row:last-child:not(.dt-row-filter) { .dt-cell { border-bottom: 1px solid var(--border-color); } } - - .datatable { - height: calc(100vh - var(--report-filter-height) - 225px); - - .dt-scrollable { - height: calc(100vh - var(--report-filter-height) - 295px); - } - } } } diff --git a/frappe/website/workspace/website/website.json b/frappe/website/workspace/website/website.json index 8d22f84b5e..bd06f0a131 100644 --- a/frappe/website/workspace/website/website.json +++ b/frappe/website/workspace/website/website.json @@ -1,20 +1,13 @@ { - "category": "", "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}}]", "creation": "2020-03-02 14:13:51.089373", - "developer_mode_only": 0, - "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "", - "extends_another_page": 0, "for_user": "", "hide_custom": 0, "icon": "website", "idx": 0, - "is_default": 0, - "is_standard": 0, "label": "Website", "links": [ { @@ -239,15 +232,12 @@ "type": "Link" } ], - "modified": "2021-08-05 12:16:03.154032", + "modified": "2021-08-05 12:16:03.154033", "modified_by": "Administrator", "module": "Website", "name": "Website", - "onboarding": "Website", "owner": "Administrator", "parent_page": "", - "pin_to_bottom": 0, - "pin_to_top": 0, "public": 1, "restrict_to_domain": "", "roles": [], diff --git a/requirements.txt b/requirements.txt index 3890e1aa90..671f6ced11 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,6 @@ croniter~=1.0.11 cryptography~=3.4.7 dropbox~=11.7.0 email-reply-parser~=0.5.12 -Faker~=8.1.0 git-url-parse~=1.2.2 gitdb~=4.0.7 GitPython~=3.1.14 @@ -44,7 +43,6 @@ pyasn1~=0.4.8 pycryptodome~=3.10.1 PyJWT~=2.0.1 PyMySQL~=1.0.2 -pyngrok~=5.0.5 pyOpenSSL~=20.0.1 pyotp~=2.6.0 PyPDF2~=1.26.0 @@ -64,12 +62,10 @@ rq~=1.8.0 rsa>=4.1 # not directly required, pinned by Snyk to avoid a vulnerability schedule~=1.1.0 semantic-version~=2.8.5 -simple-chalk~=0.1.0 six~=1.15.0 sqlparse~=0.4.1 stripe~=2.56.0 terminaltables~=3.1.0 -unittest-xml-reporting~=3.0.4 urllib3~=1.26.4 Werkzeug~=0.16.1 Whoosh~=2.7.4