From 619a4adfd5c3b353cd1fd73bdd857d06a41592df Mon Sep 17 00:00:00 2001 From: "hasnain2808@gmail.com" Date: Thu, 21 Jan 2021 13:10:06 +0530 Subject: [PATCH 001/102] fix: add patch for List View Settings Rename --- frappe/patches.txt | 1 + ...list_view_setting_to_list_view_settings.py | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py diff --git a/frappe/patches.txt b/frappe/patches.txt index 1a086303ba..c49c70ba64 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -318,3 +318,4 @@ frappe.patches.v13_0.remove_custom_link execute:frappe.delete_doc("DocType", "Footer Item") frappe.patches.v13_0.replace_field_target_with_open_in_new_tab frappe.patches.v13_0.delete_package_publish_tool +frappe.patches.v13_0.rename_list_view_setting_to_list_view_settings \ No newline at end of file diff --git a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py new file mode 100644 index 0000000000..ee752ae085 --- /dev/null +++ b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -0,0 +1,21 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe + + +def execute(): + if frappe.db.table_exists('List View Setting'): + existing_list_view_settings = frappe.get_all('List View Settings', as_list=True) + for list_view_setting in frappe.get_all('List View Setting', fields = ['disable_count', 'disable_sidebar_stats', 'disable_auto_refresh', 'name']): + name = list_view_setting.pop('name') + if name not in [x[0] for x in existing_list_view_settings]: + list_view_setting['doctype'] = 'List View Settings' + list_view_settings = frappe.get_doc(list_view_setting) + # setting name here is necessary because autoname is set as prompt + list_view_settings.name = name + list_view_settings.insert() + frappe.delete_doc("DocType", "List View Setting", force=True) + frappe.db.commit() + frappe.db.sql("DROP TABLE IF EXISTS `tabList View Setting`") From 0c8c76ed1b69f76d5f747de73c27cfea3b024e7f Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Mohsin Rajan Date: Fri, 22 Jan 2021 12:49:23 +0530 Subject: [PATCH 002/102] fix: Do not manually drop the table --- .../v13_0/rename_list_view_setting_to_list_view_settings.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py index ee752ae085..fcf8afc826 100644 --- a/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py +++ b/frappe/patches/v13_0/rename_list_view_setting_to_list_view_settings.py @@ -18,4 +18,3 @@ def execute(): list_view_settings.insert() frappe.delete_doc("DocType", "List View Setting", force=True) frappe.db.commit() - frappe.db.sql("DROP TABLE IF EXISTS `tabList View Setting`") From 79cb3f0596f263c989667864ee905a8df1d8bd9e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 4 Feb 2021 14:06:09 +0530 Subject: [PATCH 003/102] feat: Rename "Custom Script" to "Client Script" * Remove postfix "-Client" from ClientScript.autoname --- frappe/core/doctype/doctype/doctype.json | 2 +- frappe/core/workspace/build/build.json | 7 ++--- .../README.md | 0 .../__init__.py | 0 .../client_script.js} | 2 +- .../client_script.json} | 23 ++++++---------- .../client_script.py} | 4 +-- .../test_client_script.py} | 4 +-- .../customization/customization.json | 17 ++++++++---- frappe/desk/form/meta.py | 2 +- frappe/desk/form/utils.py | 2 +- frappe/model/__init__.py | 2 +- frappe/model/delete_doc.py | 2 +- .../public/js/frappe/form/script_manager.js | 2 +- frappe/public/js/frappe/model/model.js | 2 +- frappe/tests/test_exporter_fixtures.py | 26 +++++++++---------- frappe/utils/fixtures.py | 6 ++--- 17 files changed, 52 insertions(+), 51 deletions(-) rename frappe/custom/doctype/{custom_script => client_script}/README.md (100%) rename frappe/custom/doctype/{custom_script => client_script}/__init__.py (100%) rename frappe/custom/doctype/{custom_script/custom_script.js => client_script/client_script.js} (97%) rename frappe/custom/doctype/{custom_script/custom_script.json => client_script/client_script.json} (86%) rename frappe/custom/doctype/{custom_script/custom_script.py => client_script/client_script.py} (84%) rename frappe/custom/doctype/{custom_script/test_custom_script.py => client_script/test_client_script.py} (65%) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 569414e98b..3ccd1e7e09 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -555,7 +555,7 @@ }, { "group": "Customization", - "link_doctype": "Custom Script", + "link_doctype": "Client Script", "link_fieldname": "dt" }, { diff --git a/frappe/core/workspace/build/build.json b/frappe/core/workspace/build/build.json index c4bde55d7f..aefda698b1 100644 --- a/frappe/core/workspace/build/build.json +++ b/frappe/core/workspace/build/build.json @@ -11,6 +11,7 @@ "hide_custom": 0, "icon": "tool", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Build", "links": [ @@ -163,8 +164,8 @@ { "hidden": 0, "is_query_report": 0, - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", "link_type": "DocType", "onboard": 0, "only_for": "", @@ -181,7 +182,7 @@ "type": "Link" } ], - "modified": "2021-01-02 14:03:15.029699", + "modified": "2021-02-04 13:48:48.493146", "modified_by": "Administrator", "module": "Core", "name": "Build", diff --git a/frappe/custom/doctype/custom_script/README.md b/frappe/custom/doctype/client_script/README.md similarity index 100% rename from frappe/custom/doctype/custom_script/README.md rename to frappe/custom/doctype/client_script/README.md diff --git a/frappe/custom/doctype/custom_script/__init__.py b/frappe/custom/doctype/client_script/__init__.py similarity index 100% rename from frappe/custom/doctype/custom_script/__init__.py rename to frappe/custom/doctype/client_script/__init__.py diff --git a/frappe/custom/doctype/custom_script/custom_script.js b/frappe/custom/doctype/client_script/client_script.js similarity index 97% rename from frappe/custom/doctype/custom_script/custom_script.js rename to frappe/custom/doctype/client_script/client_script.js index 711e7d1796..21e7334b82 100644 --- a/frappe/custom/doctype/custom_script/custom_script.js +++ b/frappe/custom/doctype/client_script/client_script.js @@ -1,7 +1,7 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Custom Script', { +frappe.ui.form.on('Client Script', { refresh(frm) { if (frm.doc.dt && frm.doc.script) { frm.add_custom_button(__('Go to {0}', [frm.doc.dt]), diff --git a/frappe/custom/doctype/custom_script/custom_script.json b/frappe/custom/doctype/client_script/client_script.json similarity index 86% rename from frappe/custom/doctype/custom_script/custom_script.json rename to frappe/custom/doctype/client_script/client_script.json index 328b247c49..57e6c68094 100644 --- a/frappe/custom/doctype/custom_script/custom_script.json +++ b/frappe/custom/doctype/client_script/client_script.json @@ -2,7 +2,7 @@ "actions": [], "allow_import": 1, "creation": "2013-01-10 16:34:01", - "description": "Adds a client custom script to a DocType", + "description": "Adds a custom client script to a DocType", "doctype": "DocType", "document_type": "Document", "engine": "InnoDB", @@ -22,9 +22,7 @@ "oldfieldname": "dt", "oldfieldtype": "Link", "options": "DocType", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "script", @@ -32,35 +30,29 @@ "label": "Script", "oldfieldname": "script", "oldfieldtype": "Code", - "options": "JS", - "show_days": 1, - "show_seconds": 1 + "options": "JS" }, { "fieldname": "sample", "fieldtype": "HTML", "label": "Sample", - "options": "

Custom Script Help

\n

Custom Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

\n
\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field,  source_fieldname,  target_fieldname); \ncur_frm.add_fetch('customer',  'local_tax_no',  'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task',  'validate',  function(frm) {\n    if (frm.doc.from_date < get_today()) {\n        msgprint('You can not select past date in From Date');\n        validated = false;\n    } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task',  {\n    refresh: function(frm) {\n        // use the __islocal value of doc,  to check if the doc is saved or not\n        frm.set_df_property('myfield',  'read_only',  frm.doc.__islocal ? 0 : 1);\n    } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task',  {\n    validate: function(frm) {\n        if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n            msgprint('You are only allowed Material Receipt');\n            validated = false;\n        }\n    } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice',  {\n    validate: function(frm) {\n        // calculate incentives for each person on the deal\n        total_incentive = 0\n        $.each(frm.doc.sales_team,  function(i,  d) {\n            // calculate incentive\n            var incentive_percent = 2;\n            if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n            // actual incentive\n            d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n            total_incentive += flt(d.incentives)\n        });\n        frm.doc.total_incentive = total_incentive;\n    } \n})\n\n
", - "show_days": 1, - "show_seconds": 1 + "options": "

Client Script Help

\n

Client Scripts are executed only on the client-side (i.e. in Forms). Here are some examples to get you started

\n
\n\n// fetch local_tax_no on selection of customer \n// cur_frm.add_fetch(link_field,  source_fieldname,  target_fieldname); \ncur_frm.add_fetch('customer',  'local_tax_no',  'local_tax_no');\n\n// additional validation on dates \nfrappe.ui.form.on('Task',  'validate',  function(frm) {\n    if (frm.doc.from_date < get_today()) {\n        msgprint('You can not select past date in From Date');\n        validated = false;\n    } \n});\n\n// make a field read-only after saving \nfrappe.ui.form.on('Task',  {\n    refresh: function(frm) {\n        // use the __islocal value of doc,  to check if the doc is saved or not\n        frm.set_df_property('myfield',  'read_only',  frm.doc.__islocal ? 0 : 1);\n    } \n});\n\n// additional permission check\nfrappe.ui.form.on('Task',  {\n    validate: function(frm) {\n        if(user=='user1@example.com' && frm.doc.purpose!='Material Receipt') {\n            msgprint('You are only allowed Material Receipt');\n            validated = false;\n        }\n    } \n});\n\n// calculate sales incentive\nfrappe.ui.form.on('Sales Invoice',  {\n    validate: function(frm) {\n        // calculate incentives for each person on the deal\n        total_incentive = 0\n        $.each(frm.doc.sales_team,  function(i,  d) {\n            // calculate incentive\n            var incentive_percent = 2;\n            if(frm.doc.base_grand_total > 400) incentive_percent = 4;\n            // actual incentive\n            d.incentives = flt(frm.doc.base_grand_total) * incentive_percent / 100;\n            total_incentive += flt(d.incentives)\n        });\n        frm.doc.total_incentive = total_incentive;\n    } \n})\n\n
" }, { "default": "0", "fieldname": "enabled", "fieldtype": "Check", - "label": "Enabled", - "show_days": 1, - "show_seconds": 1 + "label": "Enabled" } ], "icon": "fa fa-glass", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-08-24 21:56:07.719579", + "modified": "2021-02-04 13:57:56.509437", "modified_by": "Administrator", "module": "Custom", - "name": "Custom Script", + "name": "Client Script", "owner": "Administrator", "permissions": [ { @@ -86,6 +78,7 @@ "write": 1 } ], + "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 } \ No newline at end of file diff --git a/frappe/custom/doctype/custom_script/custom_script.py b/frappe/custom/doctype/client_script/client_script.py similarity index 84% rename from frappe/custom/doctype/custom_script/custom_script.py rename to frappe/custom/doctype/client_script/client_script.py index e15819de65..e252e2a750 100644 --- a/frappe/custom/doctype/custom_script/custom_script.py +++ b/frappe/custom/doctype/client_script/client_script.py @@ -5,9 +5,9 @@ import frappe from frappe.model.document import Document -class CustomScript(Document): +class ClientScript(Document): def autoname(self): - self.name = self.dt + "-Client" + self.name = self.dt def on_update(self): frappe.clear_cache(doctype=self.dt) diff --git a/frappe/custom/doctype/custom_script/test_custom_script.py b/frappe/custom/doctype/client_script/test_client_script.py similarity index 65% rename from frappe/custom/doctype/custom_script/test_custom_script.py rename to frappe/custom/doctype/client_script/test_client_script.py index 6947e6060d..de113c1ce7 100644 --- a/frappe/custom/doctype/custom_script/test_custom_script.py +++ b/frappe/custom/doctype/client_script/test_client_script.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest -# test_records = frappe.get_test_records('Custom Script') +# test_records = frappe.get_test_records('Client Script') -class TestCustomScript(unittest.TestCase): +class TestClientScript(unittest.TestCase): pass diff --git a/frappe/custom/workspace/customization/customization.json b/frappe/custom/workspace/customization/customization.json index 3631914249..cdc3b73366 100644 --- a/frappe/custom/workspace/customization/customization.json +++ b/frappe/custom/workspace/customization/customization.json @@ -10,6 +10,7 @@ "hide_custom": 0, "icon": "customization", "idx": 0, + "is_default": 0, "is_standard": 1, "label": "Customization", "links": [ @@ -81,8 +82,8 @@ "dependencies": "", "hidden": 0, "is_query_report": 0, - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", "link_type": "DocType", "onboard": 0, "type": "Link" @@ -115,7 +116,7 @@ "type": "Link" } ], - "modified": "2020-12-01 13:38:39.843773", + "modified": "2021-02-04 13:50:35.750463", "modified_by": "Administrator", "module": "Custom", "name": "Customization", @@ -134,8 +135,14 @@ "type": "DocType" }, { - "label": "Custom Script", - "link_to": "Custom Script", + "label": "Client Script", + "link_to": "Client Script", + "type": "DocType" + }, + { + "doc_view": "", + "label": "Server Script", + "link_to": "Server Script", "type": "DocType" } ] diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index d5428b1da2..c63da93a33 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -130,7 +130,7 @@ class FormMeta(Meta): def add_custom_script(self): """embed all require files""" # custom script - custom = frappe.db.get_value("Custom Script", {"dt": self.name, "enabled": 1}, "script") or "" + custom = frappe.db.get_value("Client Script", {"dt": self.name, "enabled": 1}, "script") or "" self.set("__custom_js", custom) diff --git a/frappe/desk/form/utils.py b/frappe/desk/form/utils.py index 4c3bab2e23..395d2b9571 100644 --- a/frappe/desk/form/utils.py +++ b/frappe/desk/form/utils.py @@ -47,7 +47,7 @@ def validate_link(): except Exception as e: error_message = str(e).split("Unknown column '") fieldname = None if len(error_message)<=1 else error_message[1].split("'")[0] - frappe.msgprint(_("Wrong fieldname {0} in add_fetch configuration of custom script").format(fieldname)) + frappe.msgprint(_("Wrong fieldname {0} in add_fetch configuration of custom client script").format(fieldname)) frappe.errprint(frappe.get_traceback()) if fetch_value: diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index c39a73ccd7..af06696621 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -118,7 +118,7 @@ core_doctypes_list = ( 'Customize Form Field', 'Property Setter', 'Custom Field', - 'Custom Script' + 'Client Script' ) log_types = ( diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 7b29692ad1..d0e0a6fb1a 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -68,7 +68,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa check_permission_and_not_submitted(doc) frappe.db.sql("delete from `tabCustom Field` where dt = %s", name) - frappe.db.sql("delete from `tabCustom Script` where dt = %s", name) + frappe.db.sql("delete from `tabClient Script` where dt = %s", name) frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", name) frappe.db.sql("delete from `tabReport` where ref_doctype=%s", name) frappe.db.sql("delete from `tabCustom DocPerm` where parent=%s", name) diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js index 7ac3673b08..770319ba53 100644 --- a/frappe/public/js/frappe/form/script_manager.js +++ b/frappe/public/js/frappe/form/script_manager.js @@ -176,7 +176,7 @@ frappe.ui.form.ScriptManager = Class.extend({ eval(doctype.__custom_js); } catch(e) { frappe.msgprint({ - title: __('Error in Custom Script'), + title: __('Error in Client Script'), indicator: 'orange', message: '
' + e.stack  + '
' }); diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index a2e872085e..9ec7b0e931 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -15,7 +15,7 @@ $.extend(frappe.model, { core_doctypes_list: ['DocType', 'DocField', 'DocPerm', 'User', 'Role', 'Has Role', 'Page', 'Module Def', 'Print Format', 'Report', 'Customize Form', - 'Customize Form Field', 'Property Setter', 'Custom Field', 'Custom Script'], + 'Customize Form Field', 'Property Setter', 'Custom Field', 'Client Script'], std_fields: [ {fieldname:'name', fieldtype:'Link', label:__('ID')}, diff --git a/frappe/tests/test_exporter_fixtures.py b/frappe/tests/test_exporter_fixtures.py index a860cc6a96..b8bd94e3e9 100644 --- a/frappe/tests/test_exporter_fixtures.py +++ b/frappe/tests/test_exporter_fixtures.py @@ -12,9 +12,9 @@ class TestDataImportFixtures(unittest.TestCase): def setUp(self): pass - #start test for Custom Script + #start test for Client Script def test_Custom_Script_fixture_simple(self): - fixture = "Custom Script" + fixture = "Client Script" path = frappe.scrub(fixture) + "_original_style.csv" export_csv(fixture, path) @@ -22,7 +22,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_simple_name_equal_default(self): - fixture = ["Custom Script", {"name":["Item-Client"]}] + fixture = ["Client Script", {"name":["Item"]}] path = frappe.scrub(fixture[0]) + "_simple_name_equal_default.csv" export_csv(fixture, path) @@ -30,7 +30,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_simple_name_equal(self): - fixture = ["Custom Script", {"name":["Item-Client"],"op":"="}] + fixture = ["Client Script", {"name":["Item"],"op":"="}] path = frappe.scrub(fixture[0]) + "_simple_name_equal.csv" export_csv(fixture, path) @@ -38,7 +38,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_simple_name_not_equal(self): - fixture = ["Custom Script", {"name":["Item-Client"],"op":"!="}] + fixture = ["Client Script", {"name":["Item"],"op":"!="}] path = frappe.scrub(fixture[0]) + "_simple_name_not_equal.csv" export_csv(fixture, path) @@ -47,7 +47,7 @@ class TestDataImportFixtures(unittest.TestCase): #without [] around the name... def test_Custom_Script_fixture_simple_name_at_least_equal(self): - fixture = ["Custom Script", {"name":"Item-Cli"}] + fixture = ["Client Script", {"name":"Item-Cli"}] path = frappe.scrub(fixture[0]) + "_simple_name_at_least_equal.csv" export_csv(fixture, path) @@ -55,7 +55,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_multi_name_equal(self): - fixture = ["Custom Script", {"name":["Item-Client", "Customer-Client"],"op":"="}] + fixture = ["Client Script", {"name":["Item", "Customer"],"op":"="}] path = frappe.scrub(fixture[0]) + "_multi_name_equal.csv" export_csv(fixture, path) @@ -63,7 +63,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_multi_name_not_equal(self): - fixture = ["Custom Script", {"name":["Item-Client", "Customer-Client"],"op":"!="}] + fixture = ["Client Script", {"name":["Item", "Customer"],"op":"!="}] path = frappe.scrub(fixture[0]) + "_multi_name_not_equal.csv" export_csv(fixture, path) @@ -71,7 +71,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_empty_object(self): - fixture = ["Custom Script", {}] + fixture = ["Client Script", {}] path = frappe.scrub(fixture[0]) + "_empty_object_should_be_all.csv" export_csv(fixture, path) @@ -79,16 +79,16 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_just_list(self): - fixture = ["Custom Script"] + fixture = ["Client Script"] path = frappe.scrub(fixture[0]) + "_just_list_should_be_all.csv" export_csv(fixture, path) self.assertTrue(True) os.remove(path) - # Custom Script regular expression + # Client Script regular expression def test_Custom_Script_fixture_rex_no_flags(self): - fixture = ["Custom Script", {"name":r"^[i|A]"}] + fixture = ["Client Script", {"name":r"^[i|A]"}] path = frappe.scrub(fixture[0]) + "_rex_no_flags.csv" export_csv(fixture, path) @@ -96,7 +96,7 @@ class TestDataImportFixtures(unittest.TestCase): os.remove(path) def test_Custom_Script_fixture_rex_with_flags(self): - fixture = ["Custom Script", {"name":r"^[i|A]", "flags":"L,M"}] + fixture = ["Client Script", {"name":r"^[i|A]", "flags":"L,M"}] path = frappe.scrub(fixture[0]) + "_rex_with_flags.csv" export_csv(fixture, path) diff --git a/frappe/utils/fixtures.py b/frappe/utils/fixtures.py index a1b4eadbf3..7bad6391cc 100644 --- a/frappe/utils/fixtures.py +++ b/frappe/utils/fixtures.py @@ -38,13 +38,13 @@ def import_custom_scripts(app): "custom_scripts") + os.path.sep + fname) as f: doctype = fname.rsplit(".", 1)[0] script = f.read() - if frappe.db.exists("Custom Script", {"dt": doctype}): - custom_script = frappe.get_doc("Custom Script", {"dt": doctype}) + if frappe.db.exists("Client Script", {"dt": doctype}): + custom_script = frappe.get_doc("Client Script", {"dt": doctype}) custom_script.script = script custom_script.save() else: frappe.get_doc({ - "doctype":"Custom Script", + "doctype":"Client Script", "dt": doctype, "script": script }).insert() From da6e183a1f571d45a006f8a410c6ceb15af88e8a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 4 Feb 2021 17:00:16 +0530 Subject: [PATCH 004/102] chore: Bump modified time of DocType --- frappe/core/doctype/doctype/doctype.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index 3ccd1e7e09..1533829b3c 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -609,7 +609,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2020-12-10 15:10:09.227205", + "modified": "2021-02-04 15:10:09.227205", "modified_by": "Administrator", "module": "Core", "name": "DocType", From 5bd7ed85e5f4caf956d5e79f766cfcc1ed05a961 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 4 Feb 2021 17:03:03 +0530 Subject: [PATCH 005/102] fix: Added patch to rename custom script to client script --- frappe/patches.txt | 1 + frappe/patches/v13_0/rename_custom_client_script.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 frappe/patches/v13_0/rename_custom_client_script.py diff --git a/frappe/patches.txt b/frappe/patches.txt index f076d5bd9c..03f7ea71a1 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -329,3 +329,4 @@ frappe.core.doctype.page.patches.drop_unused_pages execute:frappe.get_doc('Role', 'Guest').save() # remove desk access frappe.patches.v13_0.rename_desk_page_to_workspace # 02.02.2021 frappe.patches.v13_0.delete_package_publish_tool +frappe.patches.v13_0.rename_custom_client_script diff --git a/frappe/patches/v13_0/rename_custom_client_script.py b/frappe/patches/v13_0/rename_custom_client_script.py new file mode 100644 index 0000000000..718f1f6a46 --- /dev/null +++ b/frappe/patches/v13_0/rename_custom_client_script.py @@ -0,0 +1,9 @@ +import frappe + + +def execute(): + if frappe.db.exists("DocType", "Client Script"): + return + + frappe.rename_doc("DocType", "Custom Script", "Client Script") + frappe.reload_doctype("Client Script", force=True) From 3589f724af2e66397db1f602cbf38efbd753309e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 4 Feb 2021 17:20:44 +0530 Subject: [PATCH 006/102] style: sort imports --- frappe/utils/__init__.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 5ac4de618d..8577d7cd7c 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -1,23 +1,31 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt -# util __init__.py +from __future__ import print_function, unicode_literals -from __future__ import unicode_literals, print_function -from werkzeug.test import Client -import os, re, sys, json, hashlib, requests, traceback import functools -from .html_utils import sanitize_html -import frappe -from frappe.utils.identicon import Identicon -from email.utils import parseaddr, formataddr +import hashlib +import io +import json +import os +import re +import sys +import traceback from email.header import decode_header, make_header +from email.utils import formataddr, parseaddr +from gzip import GzipFile +from typing import Generator, Iterable + +import requests +from six import string_types, text_type +from six.moves.urllib.parse import quote +from werkzeug.test import Client + +import frappe # utility functions like cint, int, flt, etc. from frappe.utils.data import * -from six.moves.urllib.parse import quote -from six import text_type, string_types -import io -from gzip import GzipFile +from frappe.utils.identicon import Identicon +from frappe.utils.html_utils import sanitize_html default_fields = ['doctype', 'name', 'owner', 'creation', 'modified', 'modified_by', 'parent', 'parentfield', 'parenttype', 'idx', 'docstatus'] @@ -304,8 +312,8 @@ def unesc(s, esc_chars): def execute_in_shell(cmd, verbose=0): # using Popen instead of os.system - as recommended by python docs - from subprocess import Popen import tempfile + from subprocess import Popen with tempfile.TemporaryFile() as stdout: with tempfile.TemporaryFile() as stderr: @@ -471,8 +479,9 @@ def get_request_session(max_retries=3): def watch(path, handler=None, debug=True): import time - from watchdog.observers import Observer + from watchdog.events import FileSystemEventHandler + from watchdog.observers import Observer class Handler(FileSystemEventHandler): def on_any_event(self, event): @@ -571,9 +580,9 @@ def get_installed_apps_info(): return out def get_site_info(): - from frappe.utils.user import get_system_managers from frappe.core.doctype.user.user import STANDARD_USERS from frappe.email.queue import get_emails_sent_this_month + from frappe.utils.user import get_system_managers # only get system users users = frappe.get_all('User', filters={'user_type': 'System User', 'name': ('not in', STANDARD_USERS)}, From 07b8131d71af023823a5cf9b025233677ce2424f Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 9 Feb 2021 17:15:53 +0530 Subject: [PATCH 007/102] fix: kanban style --- frappe/public/scss/desk/kanban.scss | 46 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/frappe/public/scss/desk/kanban.scss b/frappe/public/scss/desk/kanban.scss index 13551c4c17..902164ff10 100644 --- a/frappe/public/scss/desk/kanban.scss +++ b/frappe/public/scss/desk/kanban.scss @@ -15,7 +15,7 @@ .kanban { display: flex; - overflow: auto; + overflow-y: hidden; -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ @@ -32,6 +32,8 @@ border-radius: var(--border-radius); padding: var(--padding-md); min-height: calc(100vh - 250px); + max-height: calc(75vh - 10px); + // overflow: hidden; &.add-new-column { order: 1; @@ -138,7 +140,6 @@ } .kanban-cards { - min-height: 100px; max-height: calc(100vh - 250px); margin: -5px; padding: 5px; @@ -191,10 +192,20 @@ } } - .kanban-card-title { - max-width: 90%; - font-size: $font-size-base; - font-weight: 500; + .kanban-title-area { + margin-bottom: var(--margin-md); + + .kanban-card-title { + max-width: 90%; + font-size: var(--text-md); + font-weight: 500; + } + + .kanban-card-creation { + font-size: var(--text-md); + color: var(--text-muted); + margin-top: var(--margin-xs); + } } .kanban-card-edit { @@ -248,12 +259,35 @@ } .kanban-card-meta { + + .list-comment-count { + width: 30px; + } + + .like-action:not(.liked) { + .icon use { + stroke: var(--text-muted); + } + } + .kanban-assignments { display: flex; float: right; .avatar { cursor: default; + width: 22px; + height: 22px; + } + + .avatar-action { + width: 22px; + height: 22px; + + .icon { + width: 12px; + height: 12px; + } } } } From 0d29d46f7d6c95f67060c70878a874f5a02074ff Mon Sep 17 00:00:00 2001 From: prssanna Date: Tue, 9 Feb 2021 17:16:58 +0530 Subject: [PATCH 008/102] feat: add likes and creation to kanban --- frappe/public/js/frappe/views/kanban/kanban_board.js | 12 +++++++++--- .../public/js/frappe/views/kanban/kanban_card.html | 5 ++++- frappe/public/js/frappe/views/kanban/kanban_view.js | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index 66ae842ae7..e5f28659f4 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -446,6 +446,7 @@ frappe.provide("frappe.views"); group: "cards", animation: 150, dataIdAttr: 'data-name', + forceFallback: true, onStart: function() { wrapper.find('.kanban-card.add-card').fadeOut(200, function() { wrapper.find('.kanban-cards').height('100vh'); @@ -546,14 +547,14 @@ frappe.provide("frappe.views"); var opts = { name: card.name, title: remove_img_tags(card.title), - disable_click: card._disable_click ? 'disable-click' : '' + disable_click: card._disable_click ? 'disable-click' : '', + creation: card.creation, }; self.$card = $(frappe.render_template('kanban_card', opts)) .appendTo(wrapper); } function render_card_meta() { - var html = ""; if (card.comment_count > 0) html += ` @@ -563,7 +564,10 @@ frappe.provide("frappe.views"); const $assignees_group = get_assignees_group(); - html += ``; + html += ` + + ${cur_list.get_like_html(card)} + `; if (card.color && frappe.ui.color.validate_hex(card.color)) { const $div = $('
'); @@ -630,6 +634,8 @@ frappe.provide("frappe.views"); doctype: state.doctype, name: card.name, title: card[state.card_meta.title_field.fieldname], + creation: moment(card.creation).format('MMM DD, YYYY'), + tags: card._user_tags, column: card[state.board.field_name], assigned_list: card.assigned_list || assigned_list, comment_count: card.comment_count || comment_count, diff --git a/frappe/public/js/frappe/views/kanban/kanban_card.html b/frappe/public/js/frappe/views/kanban/kanban_card.html index 108dadce8c..b67488f46f 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_card.html +++ b/frappe/public/js/frappe/views/kanban/kanban_card.html @@ -2,9 +2,12 @@
-
+
{{ title }}
+
+ {{ creation }} +
diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 46348b0d11..096c882d74 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -74,6 +74,7 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { setup_view() { this.setup_realtime_updates(); + this.setup_like(); } set_fields() { From ff27940b8e2848628ba30df093f1a5a2b156fc41 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 4 Feb 2021 17:21:06 +0530 Subject: [PATCH 009/102] refactor(utils): Update create_batch docstring, annotations --- frappe/utils/__init__.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 8577d7cd7c..c8a589c694 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -700,13 +700,19 @@ def get_safe_filters(filters): return filters -def create_batch(iterable, batch_size): - """ - Convert an iterable to multiple batches of constant size of batch_size +def create_batch(iterable: Iterable, size: int) -> Generator[Iterable, None, None]: + """Convert an iterable to multiple batches of constant size of batch_size + + Args: + iterable (Iterable): Iterable object which is subscriptable + size (int): Maximum size of batches to be generated + + Yields: + Generator[List]: Batched iterable of maximum length `size` """ total_count = len(iterable) - for i in range(0, total_count, batch_size): - yield iterable[i:min(i + batch_size, total_count)] + for i in range(0, total_count, size): + yield iterable[i : min(i + size, total_count)] def set_request(**kwargs): from werkzeug.test import EnvironBuilder From 721097e47dc2881b492dcd8b11e8a35e3b9ac167 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 6 Jan 2021 10:41:18 +0530 Subject: [PATCH 010/102] chore: Drop deprecated code --- frappe/utils/__init__.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index c8a589c694..414d3b930f 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -27,24 +27,10 @@ from frappe.utils.data import * from frappe.utils.identicon import Identicon from frappe.utils.html_utils import sanitize_html + default_fields = ['doctype', 'name', 'owner', 'creation', 'modified', 'modified_by', 'parent', 'parentfield', 'parenttype', 'idx', 'docstatus'] -# used in import_docs.py -# TODO: deprecate it -def getCSVelement(v): - """ - Returns the CSV value of `v`, For example: - - * apple becomes "apple" - * hi"there becomes "hi""there" - """ - v = cstr(v) - if not v: return '' - if (',' in v) or ('\n' in v) or ('"' in v): - if '"' in v: v = v.replace('"', '""') - return '"'+v+'"' - else: return v or '' def get_fullname(user=None): """get the full name (first name + last name) of the user from User""" From 0f9e77c401c8bdf47b770d3a322f20da5402e6b4 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 11 Feb 2021 10:24:46 +0530 Subject: [PATCH 011/102] refactor: optimize updating of order for kanban board --- .../desk/doctype/kanban_board/kanban_board.py | 73 +++++++++++++++++-- .../js/frappe/views/kanban/kanban_board.js | 72 +++++++++++++++++- 2 files changed, 135 insertions(+), 10 deletions(-) diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index f1ad41db6c..9152e952ab 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -17,6 +17,10 @@ class KanbanBoard(Document): def on_update(self): frappe.clear_cache(doctype=self.reference_doctype) + def before_insert(self): + for column in self.columns: + column.order = get_order_for_column(self, column.column_name) + def validate_column_name(self): for column in self.columns: if not column.column_name: @@ -125,6 +129,56 @@ def update_order(board_name, order): board.save() return board, updated_cards +@frappe.whitelist() +def update_order_for_single_card(board_name, docname, from_colname, to_colname, old_index, new_index): + '''Save the order of cards in columns''' + board = frappe.get_doc('Kanban Board', board_name) + doctype = board.reference_doctype + fieldname = board.field_name + old_index = frappe.parse_json(old_index) + new_index = frappe.parse_json(new_index) + + # save current order and index of columns to be updated + for i, col in enumerate(board.columns): + from_col_order, from_col_idx = get_kanban_column_order_and_index(board, from_colname) + to_col_order, to_col_idx = get_kanban_column_order_and_index(board, to_colname) + + if from_colname == to_colname: + from_col_order = to_col_order + + to_col_order.insert(new_index, from_col_order.pop((old_index))) + + # save updated order + board.columns[from_col_idx].order = frappe.as_json(from_col_order) + board.columns[to_col_idx].order = frappe.as_json(to_col_order) + board.save() + + # update changed value in doc + frappe.set_value(doctype, docname, fieldname, to_colname) + + return board + +def get_kanban_column_order_and_index(board, colname): + for i, col in enumerate(board.columns): + if col.column_name == colname: + col_order = frappe.parse_json(col.order) + col_idx = i + + return col_order, col_idx + +@frappe.whitelist() +def add_card(board_name, docname, colname): + board = frappe.get_doc('Kanban Board', board_name) + doctype = board.reference_doctype + fieldname = board.field_name + + col_order, col_idx = get_kanban_column_order_and_index(board, colname) + col_order.insert(0, docname) + + board.columns[col_idx].order = frappe.as_json(col_order) + + board.save() + return board @frappe.whitelist() def quick_kanban_board(doctype, board_name, field_name, project=None): @@ -133,6 +187,13 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): doc = frappe.new_doc('Kanban Board') meta = frappe.get_meta(doctype) + doc.kanban_board_name = board_name + doc.reference_doctype = doctype + doc.field_name = field_name + + if project: + doc.filters = '[["Task","project","=","{0}"]]'.format(project) + options = '' for field in meta.fields: if field.fieldname == field_name: @@ -149,12 +210,6 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): column_name=column )) - doc.kanban_board_name = board_name - doc.reference_doctype = doctype - doc.field_name = field_name - - if project: - doc.filters = '[["Task","project","=","{0}"]]'.format(project) if doctype in ['Note', 'ToDo']: doc.private = 1 @@ -162,6 +217,12 @@ def quick_kanban_board(doctype, board_name, field_name, project=None): doc.save() return doc +def get_order_for_column(board, colname): + filters = [[board.reference_doctype, board.field_name, '=', colname]] + if board.filters: + filters.append(frappe.parse_json(board.filters)[0]) + + return frappe.as_json(frappe.get_list(board.reference_doctype, filters=filters, pluck='name')) @frappe.whitelist() def update_column_order(board_name, order): diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index e5f28659f4..495482afd5 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -124,7 +124,12 @@ frappe.provide("frappe.views"); const new_cards = state.cards.slice(); new_cards[index] = card; updater.set({ cards: new_cards }); - fluxify.doAction('update_order'); + const args = { + new: 1, + name: card.name, + colname: updated_doc[state.board.field_name], + } + fluxify.doAction('update_order_for_single_card', args); }); } else { frappe.new_doc(this.doctype, doc); @@ -155,6 +160,53 @@ frappe.provide("frappe.views"); fluxify.doAction('update_card', updated_card); }); }, + update_order_for_single_card: function(updater, card) { + // cache original order + const _cards = this.cards.slice(); + const _columns = this.columns.slice(); + let args = {}; + let method_name = ""; + + if (card.new) { + method_name = "add_card"; + args = { + board_name: this.board.name, + docname: card.name, + colname: card.colname, + }; + } else { + method_name = "update_order_for_single_card"; + args = { + board_name: this.board.name, + docname: card.name, + from_colname: card.from_colname, + to_colname: card.to_colname, + old_index: card.old_index, + new_index: card.new_index, + }; + } + + frappe.call({ + method: method_prefix + method_name, + args: args, + callback: (r) => { + let board = r.message; + let updated_cards = [{'name': card.name, 'column': card.to_colname || card.colname}]; + let cards = update_cards_column(updated_cards); + let columns = prepare_columns(board.columns); + updater.set({ + cards: cards, + columns: columns + }); + } + }).fail(function() { + // revert original order + updater.set({ + cards: _cards, + columns: _columns + }); + }); + }, update_order: function(updater) { // cache original order const _cards = this.cards.slice(); @@ -447,16 +499,26 @@ frappe.provide("frappe.views"); animation: 150, dataIdAttr: 'data-name', forceFallback: true, - onStart: function() { + onStart: function(e) { wrapper.find('.kanban-card.add-card').fadeOut(200, function() { wrapper.find('.kanban-cards').height('100vh'); }); }, - onEnd: function() { + onEnd: function(e) { wrapper.find('.kanban-card.add-card').fadeIn(100); wrapper.find('.kanban-cards').height('auto'); // update order - fluxify.doAction('update_order'); + let from_col = $(e.from).parents('.kanban-column').attr('data-column-value'); + let to_col = $(e.to).parents('.kanban-column').attr('data-column-value'); + let docname = $(e.item).attr('data-name'); + const args = { + name: docname, + from_colname: from_col, + to_colname: to_col, + old_index: e.oldIndex, + new_index: e.newIndex, + }; + fluxify.doAction('update_order_for_single_card', args); }, onAdd: function() { }, @@ -555,6 +617,8 @@ frappe.provide("frappe.views"); } function render_card_meta() { + let html = ''; + if (card.comment_count > 0) html += ` From 8d847ee52ea94b59ae68777930e1c607260144a7 Mon Sep 17 00:00:00 2001 From: prssanna Date: Thu, 11 Feb 2021 11:09:55 +0530 Subject: [PATCH 012/102] feat: add tags to kanban --- frappe/public/js/frappe/list/list_view.js | 16 +++++++++++----- .../js/frappe/views/kanban/kanban_board.js | 10 +++++++++- frappe/public/scss/desk/kanban.scss | 15 ++++++++++++++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 2f1f278ecc..66d3198e17 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -164,7 +164,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { const match_rules_list = frappe.perm.get_match_rules(this.doctype); if (match_rules_list.length) { this.restricted_list = $( - `` ).click(() => this.show_restrictions(match_rules_list)).appendTo(this.page.page_form); @@ -676,7 +676,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { if (col.type === "Tag") { const tags_display_class = !this.tags_shown ? 'hide' : ''; - let tags_html = doc._user_tags ? this.get_tags_html(doc._user_tags) : '
-
'; + let tags_html = doc._user_tags ? this.get_tags_html(doc._user_tags, 2) : '
-
'; return `